diff --git a/AUTHORS b/AUTHORS
index 7523e48..1618fddc6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -891,6 +891,7 @@
 Sreerenj Balachandran <sreerenj.balachandran@intel.com>
 Srirama Chandra Sekhar Mogali <srirama.m@samsung.com>
 Staphany Park <stapark008@gmail.com>
+Stephan Hartmann <stha09@googlemail.com>
 Stephen Searles <stephen.searles@gmail.com>
 Steve Sanders <steve@zanderz.com>
 Steven Pennington <spenn@engr.uvic.ca>
diff --git a/DEPS b/DEPS
index 19a8868..096dc60b 100644
--- a/DEPS
+++ b/DEPS
@@ -181,11 +181,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': '435adfe71a0a02d0338586e9d58b6be0202f7fb5',
+  'skia_revision': '4cf1874981c345533de5aee0baaf06484d0dc09d',
   # 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': 'cb92763d5c8a9ac41d15252209ecb3edc06656e5',
+  'v8_revision': 'cd34145326def51cb6dcf87aed7d0caf9f62bb4f',
   # 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.
@@ -193,11 +193,11 @@
   # 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': '86f730971751eb18460f2e13e3be1b97a68abacc',
+  'angle_revision': 'ffdd58f5c0bfc403c1850b50106a2e8926ecc51b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '63468081e52e4f482adf1c4152b7196dc261144b',
+  'swiftshader_revision': '5349f3c9d90450e0b1c7aba89072975db5dd9662',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -244,7 +244,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '6a138fbe7ca9a5b29dee5225c2a27e40efd723af',
+  'catapult_revision': 'aea3d43d0b6a7c95369c8416ddb1fd6914f452f7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -252,7 +252,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': 'e6a9868686ae207226061cb797232606d985f747',
+  'devtools_frontend_revision': 'cc93308cee322f28b02be92ac410d6651938de37',
   # 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.
@@ -312,7 +312,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '2b24c3d92df5a473b752a0c145b79096441f5d50',
+  'dawn_revision': '0c66bcd13a5426a56635d0e17ce85ff6e7595158',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -448,7 +448,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/chromedriver/cipd',
-        'version': 'tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC',
+        'version': 'JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C',
       }
     ],
     'condition': 'checkout_android',
@@ -1297,7 +1297,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f7650d0b86f496745dd2860f97c466c84ee5d97c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '47bc3d60ef2d608eb8a48c13ca1b6c6e135f7462',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1568,7 +1568,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5dd59b4e1cf6dd78a7d4d992061964e8f3b54d90',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8d5e7c35c508faabe224a59587e29e25f3bf2143',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 713bb64..22f63b2 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2301,14 +2301,12 @@
     'chromeos_attestation': ['dkrahn+watch@chromium.org'],
     'chromeos_bluetooth': ['hansberry+watch-bluetooth@chromium.org',
                            'jhawkins+watch-bluetooth@chromium.org',
-                           'khorimoto+watch-bluetooth@chromium.org',
                            'vecore+watch-bluetooth@google.com'],
     'chromeos_calculator': ['dharcourt@chromium.org'],
     'chromeos_cellular': ['azeemarshad+watch-cellular@chromium.org',
                           'benchan+watch-cellular@chromium.org',
                           'ejcaruso+watch-cellular@chromium.org',
                           'jhawkins+watch-cellular@chromium.org',
-                          'khorimoto+watch-cellular@chromium.org',
                           'vecore+watch-cellular@google.com'],
     'chromeos_geolocation': ['alemate+watch@chromium.org'],
     'chromeos_lkgm': ['achuith+watch@chromium.org',
@@ -2324,14 +2322,12 @@
                      'ejcaruso+watch-network@chromium.org',
                      'jhawkins+watch-network@chromium.org',
                      'jonmann+watch-network@chromium.org',
-                     'khorimoto+watch-network@chromium.org',
                      'stevenjb+watch-network@chromium.org',
                      'vecore+watch-network@google.com'],
     'chromeos_timezone': ['alemate+watch@chromium.org'],
     'chromeos_webui': ['alemate+watch@chromium.org'],
     'chromeos_wifi_sync': ['jhawkins+watch@chromium.org',
                            'jonmann+watch@chromium.org',
-                           'khorimoto+watch@chromium.org',
                            'stevenjb+watch@chromium.org',
                            'vecore+watch@google.com'],
     'chromevox': ['anastasi+watch@google.com',
@@ -2519,10 +2515,7 @@
     'midi': ['toyoshim+midi@chromium.org'],
     'mojo': ['darin+watch@chromium.org'],
     'multidevice': ['hansberry+watch-multidevice@chromium.org',
-                    'hsuregan+watch-multidevice@chromium.org',
                     'jhawkins+watch-multidevice@chromium.org',
-                    'jordynass+watch-multidevice@chromium.org',
-                    'khorimoto+watch-multidevice@chromium.org',
                     'nohle+watch-multidevice@chromium.org',
                     'themaxli+watch-multidevice@chromium.org',
                     'vecore+watch-multidevice@google.com'],
@@ -2636,8 +2629,7 @@
                        'nhiroki@chromium.org',
                        'serviceworker-reviews@chromium.org',
                        'shimazu+serviceworker@chromium.org'],
-    'settings': ['dpapad+watch-settings@chromium.org',
-                 'michaelpg+watch-settings@chromium.org'],
+    'settings': ['michaelpg+watch-settings@chromium.org'],
     'settings_os_settings': ['hsuregan+watch-os-settings@chromium.org',
                              'jamescook+watch-os-settings@chromium.org',
                              'jhawkins+watch-os-settings@chromium.org',
@@ -2651,10 +2643,7 @@
                       'creis+watch@chromium.org',
                       'nasko+codewatch@chromium.org'],
     'smartlock': ['hansberry+watch-smartlock@chromium.org',
-                  'hsuregan+watch-multidevice@chromium.org',
                   'jhawkins+watch-smartlock@chromium.org',
-                  'jordynass+watch-smartlock@chromium.org',
-                  'khorimoto+watch-smartlock@chromium.org',
                   'nohle+watch-smartlock@chromium.org',
                   'vecore+watch-smartlock@google.com'],
     'smb': ['cros-enterprise-lax+smbwatch@chromium.org'],
@@ -2702,10 +2691,7 @@
                     'einbinder+watch-test-runner@chromium.org'],
     'tests': [],
     'tether': ['hansberry+watch-tether@chromium.org',
-               'hsuregan+watch-multidevice@chromium.org',
                'jhawkins+watch-tether@chromium.org',
-               'jordynass+watch-tether@chromium.org',
-               'khorimoto+watch-tether@chromium.org',
                'nohle+watch-tether@chromium.org',
                'vecore+watch-tether@google.com'],
     'textinput': ['keithlee+watch@chromium.org',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 2849ac45..e7e6d49 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1454,7 +1454,6 @@
     "//components/user_manager",
     "//components/vector_icons",
     "//components/viz/host",
-    "//components/viz/service",
     "//dbus",
     "//device/bluetooth",
     "//extensions/common:common_constants",
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index 1b0c73f..36105796 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -784,10 +784,10 @@
   if (accelerator.key_code() == ui::VKEY_LWIN)
     base::RecordAction(UserMetricsAction("Accel_Search_LWin"));
 
-  Shelf::ForWindow(Shell::GetRootWindowForNewWindows())
-      ->shelf_widget()
-      ->GetHomeButton()
-      ->OnPressed(show_source, accelerator.time_stamp());
+  aura::Window* const root_window = Shell::GetRootWindowForNewWindows();
+  Shell::Get()->app_list_controller()->ToggleAppList(
+      display::Screen::GetScreen()->GetDisplayNearestWindow(root_window).id(),
+      show_source, accelerator.time_stamp());
 }
 
 void HandleToggleFullscreen(const ui::Accelerator& accelerator) {
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 4706199..faa1891 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -619,6 +619,19 @@
     int64_t display_id,
     AppListShowSource show_source,
     base::TimeTicks event_time_stamp) {
+  if (IsTabletMode()) {
+    bool handled = Shell::Get()->home_screen_controller()->GoHome(display_id);
+
+    // Perform the "back" action for the app list.
+    if (!handled) {
+      Back();
+      return SHELF_ACTION_APP_LIST_BACK;
+    }
+
+    LogAppListShowSource(show_source);
+    return SHELF_ACTION_APP_LIST_SHOWN;
+  }
+
   ShelfAction action =
       presenter_.ToggleAppList(display_id, show_source, event_time_stamp);
   UpdateExpandArrowVisibility();
@@ -976,25 +989,6 @@
     focused_view->SchedulePaint();
 }
 
-ShelfAction AppListControllerImpl::OnHomeButtonPressed(
-    int64_t display_id,
-    AppListShowSource show_source,
-    base::TimeTicks event_time_stamp) {
-  if (!IsTabletMode())
-    return ToggleAppList(display_id, show_source, event_time_stamp);
-
-  bool handled = Shell::Get()->home_screen_controller()->GoHome(display_id);
-
-  // Perform the "back" action for the app list.
-  if (!handled) {
-    Back();
-    return SHELF_ACTION_APP_LIST_BACK;
-  }
-
-  LogAppListShowSource(show_source);
-  return SHELF_ACTION_APP_LIST_SHOWN;
-}
-
 bool AppListControllerImpl::IsShowingEmbeddedAssistantUI() const {
   return presenter_.IsShowingEmbeddedAssistantUI();
 }
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index ae0d4f8..04dc2c72 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -153,6 +153,11 @@
                                  float background_opacity);
   void EndDragFromShelf(AppListViewState app_list_state);
   void ProcessMouseWheelEvent(const ui::MouseWheelEvent& event);
+  // Toggles app list visibility. In tablet mode, this can only show the app
+  // list (by hiding any windows that might be shown over the homde launcher).
+  // |display_id| is the id of display where app list should toggle.
+  // |show_source| is the source of the event. |event_time_stamp| records the
+  // event timestamp.
   ShelfAction ToggleAppList(int64_t display_id,
                             AppListShowSource show_source,
                             base::TimeTicks event_time_stamp);
@@ -307,15 +312,6 @@
 
   void SetKeyboardTraversalMode(bool engaged);
 
-  // Handles home button press event. (Search key should trigger the same
-  // behavior.) All three parameters are only used in clamshell mode.
-  // |display_id| is the id of display where app list should toggle.
-  // |show_source| is the source of the event. |event_time_stamp| records the
-  // event timestamp.
-  ShelfAction OnHomeButtonPressed(int64_t display_id,
-                                  AppListShowSource show_source,
-                                  base::TimeTicks event_time_stamp);
-
   // Returns current visibility of the Assistant page.
   bool IsShowingEmbeddedAssistantUI() const;
 
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index b2ba957..3e51b9f 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -2528,7 +2528,7 @@
   }
 
   void PressHomeButton() {
-    Shell::Get()->app_list_controller()->OnHomeButtonPressed(
+    Shell::Get()->app_list_controller()->ToggleAppList(
         GetPrimaryDisplayId(), AppListShowSource::kShelfButton,
         base::TimeTicks());
     GetAppListTestHelper()->WaitUntilIdle();
diff --git a/ash/app_list/views/app_list_main_view.cc b/ash/app_list/views/app_list_main_view.cc
index 4d18d8d..89ccf656 100644
--- a/ash/app_list/views/app_list_main_view.cc
+++ b/ash/app_list/views/app_list_main_view.cc
@@ -49,6 +49,8 @@
     : delegate_(delegate),
       model_(delegate->GetModel()),
       search_model_(delegate->GetSearchModel()),
+      search_box_view_(nullptr),
+      contents_view_(nullptr),
       app_list_view_(app_list_view) {
   // We need a layer to apply transform to in small display so that the apps
   // grid fits in the display.
@@ -75,11 +77,11 @@
 
 void AppListMainView::AddContentsViews() {
   DCHECK(search_box_view_);
-  auto contents_view = std::make_unique<ContentsView>(app_list_view_);
-  contents_view->Init(model_);
-  contents_view->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
-  contents_view->layer()->SetMasksToBounds(true);
-  contents_view_ = AddChildView(std::move(contents_view));
+  contents_view_ = new ContentsView(app_list_view_);
+  contents_view_->Init(model_);
+  contents_view_->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
+  contents_view_->layer()->SetMasksToBounds(true);
+  AddChildView(contents_view_);
 
   search_box_view_->set_contents_view(contents_view_);
 }
diff --git a/ash/app_list/views/app_list_main_view.h b/ash/app_list/views/app_list_main_view.h
index 5ae92d0..64a133f8 100644
--- a/ash/app_list/views/app_list_main_view.h
+++ b/ash/app_list/views/app_list_main_view.h
@@ -99,10 +99,10 @@
   SearchModel* search_model_;  // Unowned; ownership is handled by |delegate_|.
 
   // Created by AppListView. Owned by views hierarchy.
-  SearchBoxView* search_box_view_ = nullptr;
+  SearchBoxView* search_box_view_;
 
-  ContentsView* contents_view_ = nullptr;  // Owned by views hierarchy.
-  AppListView* const app_list_view_;       // Owned by views hierarchy.
+  ContentsView* contents_view_;       // Owned by views hierarchy.
+  AppListView* const app_list_view_;  // Owned by views hierarchy.
 
   DISALLOW_COPY_AND_ASSIGN(AppListMainView);
 };
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index bd9963cf..1ea87c4 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -611,24 +611,20 @@
   DCHECK(!search_box_view_);
   DCHECK(!announcement_view_);
 
-  auto app_list_background_shield =
-      std::make_unique<AppListBackgroundShieldView>(delegate_->GetShelfSize() /
-                                                    2);
-  app_list_background_shield->UpdateBackground(/*use_blur*/ !is_tablet_mode &&
-                                               is_background_blur_enabled_);
   app_list_background_shield_ =
-      AddChildView(std::move(app_list_background_shield));
+      new AppListBackgroundShieldView(delegate_->GetShelfSize() / 2);
+  app_list_background_shield_->UpdateBackground(/*use_blur*/ !is_tablet_mode &&
+                                                is_background_blur_enabled_);
+  AddChildView(app_list_background_shield_);
 
-  auto app_list_main_view = std::make_unique<AppListMainView>(delegate_, this);
-  search_box_view_ =
-      new SearchBoxView(app_list_main_view.get(), delegate_, this);
+  app_list_main_view_ = new AppListMainView(delegate_, this);
+  search_box_view_ = new SearchBoxView(app_list_main_view_, delegate_, this);
   search_box_view_->Init(is_tablet_mode);
 
-  // Assign |app_list_main_view_| here since it is accessed during Init().
-  app_list_main_view_ = app_list_main_view.get();
-  app_list_main_view->Init(0, search_box_view_);
-  AddChildView(std::move(app_list_main_view));
-  announcement_view_ = AddChildView(std::make_unique<views::View>());
+  app_list_main_view_->Init(0, search_box_view_);
+  AddChildView(app_list_main_view_);
+  announcement_view_ = new views::View();
+  AddChildView(announcement_view_);
 }
 
 void AppListView::InitWidget(gfx::NativeView parent) {
diff --git a/ash/app_list/views/apps_container_view.cc b/ash/app_list/views/apps_container_view.cc
index e4ad174..e83dc67b 100644
--- a/ash/app_list/views/apps_container_view.cc
+++ b/ash/app_list/views/apps_container_view.cc
@@ -62,26 +62,25 @@
     : contents_view_(contents_view) {
   SetPaintToLayer(ui::LAYER_NOT_DRAWN);
 
-  suggestion_chip_container_view_ = AddChildView(
-      std::make_unique<SuggestionChipContainerView>(contents_view));
+  suggestion_chip_container_view_ =
+      new SuggestionChipContainerView(contents_view);
+  AddChildView(suggestion_chip_container_view_);
 
-  apps_grid_view_ =
-      AddChildView(std::make_unique<AppsGridView>(contents_view_, nullptr));
+  apps_grid_view_ = new AppsGridView(contents_view_, nullptr);
+  AddChildView(apps_grid_view_);
 
   // Page switcher should be initialized after AppsGridView.
-  auto page_switcher = std::make_unique<PageSwitcher>(
-      apps_grid_view_->pagination_model(), true /* vertical */,
-      contents_view_->app_list_view()->is_tablet_mode());
-  page_switcher_ = AddChildView(std::move(page_switcher));
+  page_switcher_ =
+      new PageSwitcher(apps_grid_view_->pagination_model(), true /* vertical */,
+                       contents_view_->app_list_view()->is_tablet_mode());
+  AddChildView(page_switcher_);
 
-  auto app_list_folder_view =
-      std::make_unique<AppListFolderView>(this, model, contents_view_);
+  app_list_folder_view_ = new AppListFolderView(this, model, contents_view_);
   // The folder view is initially hidden.
-  app_list_folder_view->SetVisible(false);
-  auto folder_background_view =
-      std::make_unique<FolderBackgroundView>(app_list_folder_view.get());
-  folder_background_view_ = AddChildView(std::move(folder_background_view));
-  app_list_folder_view_ = AddChildView(std::move(app_list_folder_view));
+  app_list_folder_view_->SetVisible(false);
+  folder_background_view_ = new FolderBackgroundView(app_list_folder_view_);
+  AddChildView(folder_background_view_);
+  AddChildView(app_list_folder_view_);
 
   apps_grid_view_->SetModel(model);
   apps_grid_view_->SetItemList(model->top_level_item_list());
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 456bef6..3a8365d5 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -845,15 +845,15 @@
 
   // Create a new AppListItemView to duplicate the original_drag_view in the
   // folder's grid view.
-  auto view = std::make_unique<AppListItemView>(
-      this, original_drag_view->item(),
-      contents_view_->GetAppListMainView()->view_delegate(),
-      false /* is_in_folder */);
-  auto* view_ptr = items_container_->AddChildView(std::move(view));
+  AppListItemView* view =
+      new AppListItemView(this, original_drag_view->item(),
+                          contents_view_->GetAppListMainView()->view_delegate(),
+                          false /* is_in_folder */);
+  items_container_->AddChildView(view);
   for (const auto& entry : view_model_.entries())
     static_cast<AppListItemView*>(entry.view)->EnsureLayer();
-  view_ptr->EnsureLayer();
-  drag_view_ = view_ptr;
+  view->EnsureLayer();
+  drag_view_ = view;
 
   // Dragged view should have focus. This also fixed the issue
   // https://crbug.com/834682.
@@ -1012,24 +1012,6 @@
 
 void AppsGridView::UpdateControlVisibility(AppListViewState app_list_state,
                                            bool is_in_drag) {
-  if (!folder_delegate_ && features::IsBackgroundBlurEnabled()) {
-    if (is_in_drag) {
-      layer()->SetMaskLayer(nullptr);
-    } else {
-      // TODO(newcomer): Improve implementation of the mask layer so we can
-      // enable it on all devices https://crbug.com/765292.
-      if (!layer()->layer_mask_layer()) {
-        // Always create a new layer. The layer may be recreated by animation,
-        // and using the mask layer used by the detached layer can lead to
-        // crash. b/118822974.
-        fadeout_layer_delegate_ = std::make_unique<FadeoutLayerDelegate>(
-            GetAppListConfig().grid_fadeout_mask_height());
-        layer()->SetMaskLayer(fadeout_layer_delegate_->layer());
-        fadeout_layer_delegate_->layer()->SetBounds(layer()->bounds());
-      }
-    }
-  }
-
   const bool fullscreen_or_in_drag =
       is_in_drag || app_list_state == AppListViewState::kFullscreenAllApps ||
       (app_list_features::IsScalableAppListEnabled() &&
@@ -1225,9 +1207,9 @@
     // Skip "page break" items.
     if (item_list_->item_at(i)->is_page_break())
       continue;
-    std::unique_ptr<AppListItemView> view = CreateViewForItemAtIndex(i);
-    view_model_.Add(view.get(), view_model_.view_size());
-    items_container_->AddChildView(std::move(view));
+    AppListItemView* view = CreateViewForItemAtIndex(i);
+    view_model_.Add(view, view_model_.view_size());
+    items_container_->AddChildView(view);
   }
   if (!folder_delegate_)
     view_structure_.LoadFromMetadata();
@@ -1289,18 +1271,17 @@
   }
 
   while (pulsing_blocks_model_.view_size() < desired) {
-    auto view = std::make_unique<PulsingBlockView>(GetTotalTileSize(), true);
-    pulsing_blocks_model_.Add(view.get(), 0);
-    items_container_->AddChildView(std::move(view));
+    PulsingBlockView* view = new PulsingBlockView(GetTotalTileSize(), true);
+    pulsing_blocks_model_.Add(view, 0);
+    items_container_->AddChildView(view);
   }
 }
 
-std::unique_ptr<AppListItemView> AppsGridView::CreateViewForItemAtIndex(
-    size_t index) {
+AppListItemView* AppsGridView::CreateViewForItemAtIndex(size_t index) {
   // The |drag_view_| might be pending for deletion, therefore |view_model_|
   // may have one more item than |item_list_|.
   DCHECK_LE(index, item_list_->item_count());
-  std::unique_ptr<AppListItemView> view = std::make_unique<AppListItemView>(
+  AppListItemView* view = new AppListItemView(
       this, item_list_->item_at(index),
       contents_view_->GetAppListMainView()->view_delegate());
   return view;
@@ -2078,15 +2059,14 @@
   DCHECK(!folder_delegate_);
   DCHECK(activated_folder_item_view_);
 
-  auto reparented_view_in_root_grid = std::make_unique<AppListItemView>(
-      this, reparented_view->item(),
-      contents_view_->GetAppListMainView()->view_delegate(),
-      false /* is_in_folder */);
+  AppListItemView* reparented_view_in_root_grid =
+      new AppListItemView(this, reparented_view->item(),
+                          contents_view_->GetAppListMainView()->view_delegate(),
+                          false /* is_in_folder */);
 
-  auto* reparented_view_in_root_grid_ptr =
-      items_container_->AddChildView(std::move(reparented_view_in_root_grid));
-  view_model_.Add(reparented_view_in_root_grid_ptr, view_model_.view_size());
-  view_structure_.Add(reparented_view_in_root_grid_ptr, GetLastTargetIndex());
+  items_container_->AddChildView(reparented_view_in_root_grid);
+  view_model_.Add(reparented_view_in_root_grid, view_model_.view_size());
+  view_structure_.Add(reparented_view_in_root_grid, GetLastTargetIndex());
 
   // Set |activated_folder_item_view_| selected so |target_index| will be
   // computed relative to the open folder.
@@ -2094,7 +2074,7 @@
   const GridIndex target_index =
       GetTargetGridIndexForKeyboardReparent(key_code);
   AnnounceReorder(target_index);
-  ReparentItemForReorder(reparented_view_in_root_grid_ptr, target_index);
+  ReparentItemForReorder(reparented_view_in_root_grid, target_index);
 
   GetViewAtIndex(target_index)->RequestFocus();
   Layout();
@@ -2311,12 +2291,11 @@
       GridIndex target_index = GetIndexOfView(target_view);
       gfx::Rect target_view_bounds = target_view->bounds();
       DeleteItemViewAtIndex(target_model_index, false /* sanitize */);
-      std::unique_ptr<AppListItemView> new_target_view =
-          CreateViewForItemAtIndex(folder_item_index);
-      new_target_view->SetBoundsRect(target_view_bounds);
-      view_model_.Add(new_target_view.get(), target_model_index);
+      target_view = CreateViewForItemAtIndex(folder_item_index);
+      target_view->SetBoundsRect(target_view_bounds);
+      view_model_.Add(target_view, target_model_index);
       if (!folder_delegate_)
-        view_structure_.Add(new_target_view.get(), target_index);
+        view_structure_.Add(target_view, target_index);
 
       // If drag view is in front of the position where it will be moved to, we
       // should skip it.
@@ -2324,7 +2303,7 @@
                                             target_model_index)
                              ? 1
                              : 0;
-      items_container_->AddChildViewAt(std::move(new_target_view),
+      items_container_->AddChildViewAt(target_view,
                                        target_model_index - offset);
     } else {
       LOG(ERROR) << "Folder no longer in item_list: " << folder_item_id;
@@ -2467,14 +2446,13 @@
       int target_model_index = view_model_.GetIndexOfView(target_view);
       GridIndex target_index = GetIndexOfView(target_view);
       DeleteItemViewAtIndex(target_model_index, false /* sanitize */);
-      std::unique_ptr<AppListItemView> new_folder_view =
+      AppListItemView* new_folder_view =
           CreateViewForItemAtIndex(new_folder_index);
       new_folder_view->SetBoundsRect(target_rect);
-      view_model_.Add(new_folder_view.get(), target_model_index);
+      view_model_.Add(new_folder_view, target_model_index);
       if (!folder_delegate_)
-        view_structure_.Add(new_folder_view.get(), target_index);
-      items_container_->AddChildViewAt(std::move(new_folder_view),
-                                       target_model_index);
+        view_structure_.Add(new_folder_view, target_index);
+      items_container_->AddChildViewAt(new_folder_view, target_model_index);
     } else {
       LOG(ERROR) << "Folder no longer in item_list: " << new_folder_id;
     }
@@ -2539,14 +2517,12 @@
     NOTREACHED();
     return;
   }
-  std::unique_ptr<AppListItemView> last_item_view =
-      CreateViewForItemAtIndex(last_item_index);
+  AppListItemView* last_item_view = CreateViewForItemAtIndex(last_item_index);
   last_item_view->SetBoundsRect(folder_rect);
-  view_model_.Add(last_item_view.get(), target_model_index);
+  view_model_.Add(last_item_view, target_model_index);
   if (!folder_delegate_)
-    view_structure_.Add(last_item_view.get(), target_index);
-  items_container_->AddChildViewAt(std::move(last_item_view),
-                                   target_model_index);
+    view_structure_.Add(last_item_view, target_index);
+  items_container_->AddChildViewAt(last_item_view, target_model_index);
 }
 
 void AppsGridView::CancelContextMenusOnCurrentPage() {
@@ -2634,10 +2610,10 @@
   EndDrag(true);
 
   if (!item->is_page_break()) {
-    std::unique_ptr<AppListItemView> view = CreateViewForItemAtIndex(index);
+    AppListItemView* view = CreateViewForItemAtIndex(index);
     int model_index = GetTargetModelIndexFromItemIndex(index);
-    view_model_.Add(view.get(), model_index);
-    items_container_->AddChildViewAt(std::move(view), model_index);
+    view_model_.Add(view, model_index);
+    items_container_->AddChildViewAt(view, model_index);
   }
 
   if (!folder_delegate_)
@@ -2766,6 +2742,7 @@
   // Drag ends and animation starts.
   presentation_time_recorder_.reset();
 
+  MaybeCreateGradientMask();
   CancelContextMenusOnCurrentPage();
   pagination_animation_start_frame_number_ =
       GetCompositorActivatedFrameCount(layer()->GetCompositor());
@@ -2831,11 +2808,14 @@
         end_frame_number - pagination_animation_start_frame_number_, duration,
         compositor->refresh_rate(), IsTabletMode());
   }
+  // Gradient mask is no longer necessary once transition is finished.
+  layer()->SetMaskLayer(nullptr);
 }
 
 void AppsGridView::ScrollStarted() {
   DCHECK(!presentation_time_recorder_);
 
+  MaybeCreateGradientMask();
   if (IsTabletMode()) {
     presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
         GetWidget()->GetCompositor(), kPageDragScrollInTabletHistogram,
@@ -2850,6 +2830,8 @@
 void AppsGridView::ScrollEnded() {
   // Scroll can end without triggering state animation.
   presentation_time_recorder_.reset();
+  // No need to reset the mask because transition will happen in almost all
+  // cases.
 }
 
 void AppsGridView::OnAppListModelStatusChanged() {
@@ -3390,15 +3372,14 @@
   folder_item->NotifyOfDraggedItem(drag_item);
 
   // Start animation.
-  auto animation_view = std::make_unique<TopIconAnimationView>(
+  TopIconAnimationView* animation_view = new TopIconAnimationView(
       this, drag_item->GetIcon(GetAppListConfig().type()), base::string16(),
       target_bounds, false, true);
-  auto* animation_view_ptr =
-      items_container_->AddChildView(std::move(animation_view));
-  animation_view_ptr->SetBoundsRect(source_bounds);
-  animation_view_ptr->AddObserver(
+  items_container_->AddChildView(animation_view);
+  animation_view->SetBoundsRect(source_bounds);
+  animation_view->AddObserver(
       new FolderDroppingAnimationObserver(model_, folder_item->id()));
-  animation_view_ptr->TransformView();
+  animation_view->TransformView();
 }
 
 void AppsGridView::MaybeCreateFolderDroppingAccessibilityEvent() {
@@ -3519,15 +3500,14 @@
   // GhostImageView that will fade in.
   last_ghost_view_ = current_ghost_view_;
 
-  auto current_ghost_view = std::make_unique<GhostImageView>(
-      IsFolderItem(drag_view_->item()) /* is_folder */, folder_delegate_,
-      reorder_placeholder_.page);
+  current_ghost_view_ =
+      new GhostImageView(IsFolderItem(drag_view_->item()) /* is_folder */,
+                         folder_delegate_, reorder_placeholder_.page);
   gfx::Rect ghost_view_bounds = GetExpectedTileBounds(reorder_placeholder_);
   ghost_view_bounds.Offset(
       CalculateTransitionOffset(reorder_placeholder_.page));
-  current_ghost_view->Init(drag_view_, ghost_view_bounds);
-  current_ghost_view_ =
-      items_container_->AddChildView(std::move(current_ghost_view));
+  current_ghost_view_->Init(drag_view_, ghost_view_bounds);
+  items_container_->AddChildView(current_ghost_view_);
   current_ghost_view_->FadeIn();
 }
 
@@ -3568,4 +3548,22 @@
   return true;
 }
 
+void AppsGridView::MaybeCreateGradientMask() {
+  if (!folder_delegate_ && features::IsBackgroundBlurEnabled()) {
+    // TODO(newcomer): Improve implementation of the mask layer so we can
+    // enable it on all devices https://crbug.com/765292.
+    if (!layer()->layer_mask_layer()) {
+      // Always create a new layer. The layer may be recreated by animation,
+      // and using the mask layer used by the detached layer can lead to
+      // crash. b/118822974.
+      if (!fadeout_layer_delegate_) {
+        fadeout_layer_delegate_ = std::make_unique<FadeoutLayerDelegate>(
+            GetAppListConfig().grid_fadeout_mask_height());
+        fadeout_layer_delegate_->layer()->SetBounds(layer()->bounds());
+      }
+      layer()->SetMaskLayer(fadeout_layer_delegate_->layer());
+    }
+  }
+}
+
 }  // namespace ash
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index eee3331..d5e3c2ab6 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -347,7 +347,7 @@
   // number of apps.
   void UpdatePulsingBlockViews();
 
-  std::unique_ptr<AppListItemView> CreateViewForItemAtIndex(size_t index);
+  AppListItemView* CreateViewForItemAtIndex(size_t index);
 
   // Returns true if the event was handled by the pagination controller.
   bool HandleScroll(const gfx::Vector2d& offset, ui::EventType type);
@@ -700,6 +700,9 @@
   // handled by AppsGridView.
   bool ShouldHandleDragEvent(const ui::LocatedEvent& event);
 
+  // Create a layer mask for graident alpha when the feature is enabled.
+  void MaybeCreateGradientMask();
+
   AppListModel* model_ = nullptr;         // Owned by AppListView.
   AppListItemList* item_list_ = nullptr;  // Not owned.
 
diff --git a/ash/app_list/views/search_result_suggestion_chip_view.cc b/ash/app_list/views/search_result_suggestion_chip_view.cc
index 77c28fc..0337908 100644
--- a/ash/app_list/views/search_result_suggestion_chip_view.cc
+++ b/ash/app_list/views/search_result_suggestion_chip_view.cc
@@ -250,6 +250,7 @@
     int corner_radius) {
   layer()->SetRoundedCornerRadius(
       {corner_radius, corner_radius, corner_radius, corner_radius});
+  layer()->SetIsFastRoundedCorner(true);
 }
 
 }  // namespace ash
diff --git a/ash/assistant/test/assistant_ash_test_base.cc b/ash/assistant/test/assistant_ash_test_base.cc
index 1368d48..92f719a 100644
--- a/ash/assistant/test/assistant_ash_test_base.cc
+++ b/ash/assistant/test/assistant_ash_test_base.cc
@@ -55,7 +55,7 @@
 }
 
 void PressHomeButton() {
-  Shell::Get()->app_list_controller()->OnHomeButtonPressed(
+  Shell::Get()->app_list_controller()->ToggleAppList(
       display::Screen::GetScreen()->GetPrimaryDisplay().id(),
       AppListShowSource::kShelfButton, base::TimeTicks::Now());
 }
diff --git a/ash/components/fast_ink/BUILD.gn b/ash/components/fast_ink/BUILD.gn
index 8b4e8781..2e6ae84 100644
--- a/ash/components/fast_ink/BUILD.gn
+++ b/ash/components/fast_ink/BUILD.gn
@@ -15,7 +15,7 @@
   deps = [
     "//base",
     "//cc",
-    "//components/viz/service",
+    "//components/viz/common",
     "//gpu",
     "//gpu/command_buffer/client",
     "//gpu/command_buffer/common",
diff --git a/ash/shelf/home_button.cc b/ash/shelf/home_button.cc
index 4d1a736..fae336e 100644
--- a/ash/shelf/home_button.cc
+++ b/ash/shelf/home_button.cc
@@ -24,7 +24,6 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
-#include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/controls/button/button_controller.h"
 
 namespace ash {
@@ -98,7 +97,8 @@
 
   const AppListShowSource show_source =
       event.IsShiftDown() ? kShelfButtonFullscreen : kShelfButton;
-  OnPressed(show_source, event.time_stamp());
+  Shell::Get()->app_list_controller()->ToggleAppList(
+      GetDisplayId(), show_source, event.time_stamp());
 }
 
 void HomeButton::OnAssistantAvailabilityChanged() {
@@ -109,17 +109,6 @@
   return controller_.is_showing_app_list();
 }
 
-void HomeButton::OnPressed(AppListShowSource show_source,
-                           base::TimeTicks time_stamp) {
-  ShelfAction shelf_action =
-      Shell::Get()->app_list_controller()->OnHomeButtonPressed(
-          GetDisplayId(), show_source, time_stamp);
-  if (shelf_action == SHELF_ACTION_APP_LIST_DISMISSED) {
-    GetInkDrop()->SnapToActivated();
-    GetInkDrop()->AnimateToState(views::InkDropState::HIDDEN);
-  }
-}
-
 int64_t HomeButton::GetDisplayId() const {
   aura::Window* window = GetWidget()->GetNativeWindow();
   return display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
diff --git a/ash/shelf/home_button.h b/ash/shelf/home_button.h
index 7d1117d..1ad35f3 100644
--- a/ash/shelf/home_button.h
+++ b/ash/shelf/home_button.h
@@ -54,8 +54,6 @@
   // True if the app list is shown for the display containing this button.
   bool IsShowingAppList() const;
 
-  virtual void OnPressed(AppListShowSource show_source,
-                         base::TimeTicks time_stamp);
 
   // Returns the display which contains this view.
   int64_t GetDisplayId() const;
diff --git a/ash/shelf/home_button_controller.cc b/ash/shelf/home_button_controller.cc
index c13af8f..c6f4b0c 100644
--- a/ash/shelf/home_button_controller.cc
+++ b/ash/shelf/home_button_controller.cc
@@ -23,6 +23,7 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "components/account_id/account_id.h"
 #include "ui/display/screen.h"
+#include "ui/views/animation/ink_drop.h"
 #include "ui/views/animation/ink_drop_state.h"
 #include "ui/views/widget/widget.h"
 
@@ -147,8 +148,8 @@
              ->visibility() == AssistantVisibility::kVisible;
 }
 
-void HomeButtonController::OnAppListVisibilityChanged(bool shown,
-                                                      int64_t display_id) {
+void HomeButtonController::OnAppListVisibilityWillChange(bool shown,
+                                                         int64_t display_id) {
   if (button_->GetDisplayId() != display_id)
     return;
   if (shown)
@@ -204,7 +205,14 @@
 }
 
 void HomeButtonController::OnAppListDismissed() {
+  // If ink drop is not hidden already, snap it to active state, so animation to
+  // DEACTIVATED state starts immediately (the animation would otherwise wait
+  // for the current animation to finish).
+  views::InkDrop* const ink_drop = button_->GetInkDrop();
+  if (ink_drop->GetTargetInkDropState() != views::InkDropState::HIDDEN)
+    ink_drop->SnapToActivated();
   button_->AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
+
   is_showing_app_list_ = false;
   RootWindowController::ForWindow(button_->GetWidget()->GetNativeWindow())
       ->UpdateShelfVisibility();
diff --git a/ash/shelf/home_button_controller.h b/ash/shelf/home_button_controller.h
index 23c8028..ef38065 100644
--- a/ash/shelf/home_button_controller.h
+++ b/ash/shelf/home_button_controller.h
@@ -50,7 +50,7 @@
 
  private:
   // AppListControllerObserver:
-  void OnAppListVisibilityChanged(bool shown, int64_t display_id) override;
+  void OnAppListVisibilityWillChange(bool shown, int64_t display_id) override;
 
   // SessionObserver:
   void OnActiveUserSessionChanged(const AccountId& account_id) override;
diff --git a/ash/shelf/shelf_control_button.cc b/ash/shelf/shelf_control_button.cc
index 7b28f0ab..b633bdd 100644
--- a/ash/shelf/shelf_control_button.cc
+++ b/ash/shelf/shelf_control_button.cc
@@ -77,6 +77,29 @@
   return GetLocalBounds().CenterPoint();
 }
 
+void ShelfControlButton::AddInkDropLayer(ui::Layer* ink_drop_layer) {
+  int radius = ShelfConfig::Get()->control_border_radius();
+
+  ink_drop_layer->SetRoundedCornerRadius(gfx::RoundedCornersF(radius));
+  ink_drop_layer->SetIsFastRoundedCorner(true);
+
+  if (chromeos::switches::ShouldShowShelfHotseat() &&
+      Shell::Get()->tablet_mode_controller()->InTabletMode() &&
+      ShelfConfig::Get()->is_in_app()) {
+    // Control button highlights are oval in in-app, so adjust the insets.
+    gfx::Rect clip(size());
+    clip.Inset(0, ShelfConfig::Get()->in_app_control_button_height_inset());
+    ink_drop_layer->SetClipRect(clip);
+  } else {
+    gfx::Point center = GetCenterPoint();
+    gfx::Rect clip(center.x(), center.y(), 0, 0);
+    clip.Inset(-radius, -radius);
+    ink_drop_layer->SetClipRect(clip);
+  }
+
+  ShelfButton::AddInkDropLayer(ink_drop_layer);
+}
+
 std::unique_ptr<views::InkDropRipple> ShelfControlButton::CreateInkDropRipple()
     const {
   return std::make_unique<views::FloodFillInkDropRipple>(
@@ -86,19 +109,9 @@
 
 std::unique_ptr<views::InkDropMask> ShelfControlButton::CreateInkDropMask()
     const {
-  if (chromeos::switches::ShouldShowShelfHotseat() &&
-      Shell::Get()->tablet_mode_controller()->InTabletMode() &&
-      ShelfConfig::Get()->is_in_app()) {
-    // Control button highlights are oval in in-app, so adjust the insets.
-    return std::make_unique<views::RoundRectInkDropMask>(
-        size(),
-        gfx::Insets(ShelfConfig::Get()->in_app_control_button_height_inset(),
-                    0),
-        ShelfConfig::Get()->control_border_radius());
-  }
-
-  return std::make_unique<views::CircleInkDropMask>(
-      size(), GetCenterPoint(), ShelfConfig::Get()->control_border_radius());
+  // The mask is either cicle or rounded rects. Just use layer's RoundedCorner
+  // api which is faster.
+  return nullptr;
 }
 
 const char* ShelfControlButton::GetClassName() const {
diff --git a/ash/shelf/shelf_control_button.h b/ash/shelf/shelf_control_button.h
index 97effd4..d6106ee 100644
--- a/ash/shelf/shelf_control_button.h
+++ b/ash/shelf/shelf_control_button.h
@@ -31,6 +31,7 @@
   void set_ideal_bounds(const gfx::Rect& bounds) { ideal_bounds_ = bounds; }
 
   // ShelfButton:
+  void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
   std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
   const char* GetClassName() const override;
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 13923b9..371f9738 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -461,8 +461,10 @@
 }
 
 gfx::Rect ShelfLayoutManager::GetIdealBoundsForWorkAreaCalculation() const {
-  if (!IsTabletModeEnabled() || !chromeos::switches::ShouldShowShelfHotseat())
+  if (!IsTabletModeEnabled() || !chromeos::switches::ShouldShowShelfHotseat() ||
+      state_.session_state != session_manager::SessionState::ACTIVE) {
     return GetIdealBounds();
+  }
 
   // For the work-area calculation in tablet mode, always use in-app shelf
   // bounds, because when the shelf is not in-app the UI is either showing
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 27b3aa1..ae864736 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -99,7 +99,7 @@
 namespace {
 
 void PressHomeButton() {
-  Shell::Get()->app_list_controller()->OnHomeButtonPressed(
+  Shell::Get()->app_list_controller()->ToggleAppList(
       display::Screen::GetScreen()->GetPrimaryDisplay().id(),
       AppListShowSource::kShelfButton, base::TimeTicks());
 }
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 2c0c593..71a165d 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -3233,8 +3233,7 @@
             home_button_ink_drop_->GetTargetInkDropState());
   EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
               ElementsAre(views::InkDropState::ACTION_PENDING,
-                          views::InkDropState::DEACTIVATED,
-                          views::InkDropState::HIDDEN));
+                          views::InkDropState::DEACTIVATED));
 
   // Dragging mouse out and back and releasing the button should not change the
   // ink drop state.
@@ -3295,8 +3294,7 @@
   EXPECT_EQ(views::InkDropState::HIDDEN,
             home_button_ink_drop_->GetTargetInkDropState());
   EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
-              ElementsAre(views::InkDropState::DEACTIVATED,
-                          views::InkDropState::HIDDEN));
+              ElementsAre(views::InkDropState::DEACTIVATED));
 }
 
 // Tests that when the app list is hidden, tapping down on the home button
diff --git a/ash/system/night_light/night_light_controller_impl.cc b/ash/system/night_light/night_light_controller_impl.cc
index 8477cc1f..a689c63 100644
--- a/ash/system/night_light/night_light_controller_impl.cc
+++ b/ash/system/night_light/night_light_controller_impl.cc
@@ -461,9 +461,9 @@
   constexpr struct {
     int32_t input_temperature;
     int32_t output_temperature;
-  } kTable[] = {{2700, 5700}, {3100, 6000}, {3700, 6050},
-                {4200, 6300}, {4800, 6300}, {5300, 6300},
-                {6000, 6400}, {7000, 6850}, {8000, 7450}};
+  } kTable[] = {{2700, 4500}, {3100, 5000}, {3700, 5300},
+                {4200, 5500}, {4800, 5800}, {5300, 6000},
+                {6000, 6400}, {7000, 6800}, {8000, 7500}};
 
   constexpr size_t kTableSize = base::size(kTable);
   // We clamp to a range defined by the minimum possible input value and the
diff --git a/ash/system/night_light/night_light_controller_unittest.cc b/ash/system/night_light/night_light_controller_unittest.cc
index aa45ed72..06d8880 100644
--- a/ash/system/night_light/night_light_controller_unittest.cc
+++ b/ash/system/night_light/night_light_controller_unittest.cc
@@ -1031,8 +1031,8 @@
             controller->ambient_temperature());
 
   // Simulate powerd sending multiple times an ambient temperature of 8000.
-  // The remapped ambient temperature should grow and eventually reach ~7350.
-  float ambient_temperature = SimulateAmbientColorFromPowerd(8000, 7350.0f);
+  // The remapped ambient temperature should grow and eventually reach ~7400.
+  float ambient_temperature = SimulateAmbientColorFromPowerd(8000, 7400.0f);
 
   // If powerd sends the same temperature, the remapped temperature should not
   // change.
@@ -1040,8 +1040,8 @@
   EXPECT_EQ(ambient_temperature, controller->ambient_temperature());
 
   // Simulate powerd sending multiple times an ambient temperature of 2700.
-  // The remapped ambient temperature should grow and eventually reach 5700.
-  ambient_temperature = SimulateAmbientColorFromPowerd(2700, 5800.0f);
+  // The remapped ambient temperature should grow and eventually reach 4500.
+  ambient_temperature = SimulateAmbientColorFromPowerd(2700, 4500.0f);
 
   // Disabling ambient color should not affect the returned temperature.
   controller->SetAmbientColorEnabled(false);
@@ -1460,13 +1460,13 @@
 
   // Warm color temperature
   temperature = NightLightControllerImpl::RemapAmbientColorTemperature(3000);
-  EXPECT_GT(temperature, 5700);
-  EXPECT_LT(temperature, 6000);
+  EXPECT_GT(temperature, 4500);
+  EXPECT_LT(temperature, 5000);
 
   // Daylight color temperature
   temperature = NightLightControllerImpl::RemapAmbientColorTemperature(7500);
-  EXPECT_GT(temperature, 6850);
-  EXPECT_LT(temperature, 7450);
+  EXPECT_GT(temperature, 6800);
+  EXPECT_LT(temperature, 7500);
 
   // Extremely high color temperature
   temperature = NightLightControllerImpl::RemapAmbientColorTemperature(20000);
diff --git a/ash/wm/gestures/back_gesture/back_gesture_metrics.h b/ash/wm/gestures/back_gesture/back_gesture_metrics.h
index f663972..f04dd6c6f 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_metrics.h
+++ b/ash/wm/gestures/back_gesture/back_gesture_metrics.h
@@ -51,7 +51,8 @@
   kBottomSnappedWindowMinimize,
   kOverviewAbort,
   kOverviewGoBack,
-  kMaxValue = kOverviewGoBack,
+  kExitFullscreen,
+  kMaxValue = kExitFullscreen,
 };
 
 // The end type of back gesture. Used to get the end scenario type.
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index 9ccf0c61..ed1b4d8 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -1016,8 +1016,18 @@
            event->details().velocity_x() >= kFlingVelocityForGoingBack)) {
         ActivateUnderneathWindowInSplitViewMode(
             back_start_location_, dragged_from_splitview_divider_);
+        auto* top_window_state =
+            WindowState::Get(TabletModeWindowManager::GetTopWindow());
+        if (top_window_state && top_window_state->IsFullscreen() &&
+            !Shell::Get()->overview_controller()->InOverviewSession()) {
+          const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
+          top_window_state->OnWMEvent(&event);
+          RecordEndScenarioType(BackGestureEndScenarioType::kExitFullscreen);
+          return true;
+        }
+
         if (TabletModeWindowManager::ShouldMinimizeTopWindowOnBack()) {
-          WindowState::Get(TabletModeWindowManager::GetTopWindow())->Minimize();
+          top_window_state->Minimize();
           end_type = BackGestureEndType::kMinimize;
         } else {
           aura::Window* root_window =
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index c489f359..243ddcc3 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -1458,6 +1458,35 @@
   EXPECT_EQ(8, target_back_release.accelerator_count());
 }
 
+// Tests the back gesture behavior on a fullscreen'ed window.
+TEST_F(ToplevelWindowEventHandlerBackGestureTest, FullscreenedWindow) {
+  ui::TestAcceleratorTarget target_back_press, target_back_release;
+  RegisterBackPressAndRelease(&target_back_press, &target_back_release);
+
+  WindowState* window_state = WindowState::Get(top_window());
+  const WMEvent fullscreen_event(WM_EVENT_TOGGLE_FULLSCREEN);
+  window_state->OnWMEvent(&fullscreen_event);
+  EXPECT_TRUE(window_state->IsFullscreen());
+
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  const gfx::Point start(0, 100);
+  generator->GestureScrollSequence(
+      start, gfx::Point(kSwipingDistanceForGoingBack + 10, 100),
+      base::TimeDelta::FromMilliseconds(100), 3);
+  // First back gesture should let the window exit fullscreen mode instead of
+  // triggering go back.
+  EXPECT_FALSE(window_state->IsFullscreen());
+  EXPECT_EQ(0, target_back_press.accelerator_count());
+  EXPECT_EQ(0, target_back_release.accelerator_count());
+
+  generator->GestureScrollSequence(
+      start, gfx::Point(kSwipingDistanceForGoingBack + 10, 100),
+      base::TimeDelta::FromMilliseconds(100), 3);
+  // Second back gesture should trigger go back.
+  EXPECT_EQ(1, target_back_press.accelerator_count());
+  EXPECT_EQ(1, target_back_release.accelerator_count());
+}
+
 namespace {
 
 void SendMouseReleaseAndReleaseCapture(ui::test::EventGenerator* generator,
diff --git a/base/android/java/src/org/chromium/base/PathUtils.java b/base/android/java/src/org/chromium/base/PathUtils.java
index 1e787877..eb46b29 100644
--- a/base/android/java/src/org/chromium/base/PathUtils.java
+++ b/base/android/java/src/org/chromium/base/PathUtils.java
@@ -12,6 +12,8 @@
 import android.system.Os;
 import android.text.TextUtils;
 
+import androidx.annotation.NonNull;
+
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.MainDex;
 import org.chromium.base.task.AsyncTask;
@@ -185,11 +187,14 @@
     }
 
     /**
-     * @return the public downloads directory.
+     * Returns the downloads directory. Before Android Q, this returns the public download directory
+     * for Chrome app. On Q+, this returns the first private download directory for the app, since Q
+     * will block public directory access. May return null when there is no external storage volumes
+     * mounted.
      */
     @SuppressWarnings("unused")
     @CalledByNative
-    private static String getDownloadsDirectory() {
+    private static @NonNull String getDownloadsDirectory() {
         // TODO(crbug.com/508615): Temporarily allowing disk access until more permanent fix is in.
         try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
             if (BuildInfo.isAtLeastQ()) {
@@ -198,7 +203,8 @@
                 // permission to write to Environment.getExternalStoragePublicDirectory(). Instead
                 // using Context.getExternalFilesDir() will return a path to sandboxed external
                 // storage for which no additional permissions are required.
-                return getAllPrivateDownloadsDirectories()[0];
+                String[] dirs = getAllPrivateDownloadsDirectories();
+                return getAllPrivateDownloadsDirectories().length == 0 ? "" : dirs[0];
             }
             return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
                     .getPath();
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 2ab499ca..51cd8ac 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8891215718560157712
\ No newline at end of file
+8891189608246083616
\ No newline at end of file
diff --git a/build/linux/unbundle/icu.gn b/build/linux/unbundle/icu.gn
index 923bd7f..e77bc43d 100644
--- a/build/linux/unbundle/icu.gn
+++ b/build/linux/unbundle/icu.gn
@@ -92,6 +92,7 @@
     "unicode/fpositer.h",
     "unicode/gender.h",
     "unicode/gregocal.h",
+    "unicode/listformatter.h",
     "unicode/measfmt.h",
     "unicode/measunit.h",
     "unicode/measure.h",
@@ -174,7 +175,6 @@
     "unicode/icudataver.h",
     "unicode/icuplug.h",
     "unicode/idna.h",
-    "unicode/listformatter.h",
     "unicode/localpointer.h",
     "unicode/locdspnm.h",
     "unicode/locid.h",
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 4e416e64..95eadcfab 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -782,7 +782,10 @@
     "//ui/gl",
     "//ui/gl:test_support",
   ]
-  data_deps = [ "//third_party/mesa_headers" ]
+  data_deps = [
+    "//testing/buildbot/filters:cc_unittests_filters",
+    "//third_party/mesa_headers",
+  ]
 
   if (enable_vulkan) {
     deps += [
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 94ab9d6..8035baf 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1535,6 +1535,12 @@
     ]
   }
 
+  java_cpp_enum("cookie_controls_mode_javagen") {
+    sources = [
+      "../components/content_settings/core/browser/cookie_settings.h",
+    ]
+  }
+
   java_cpp_enum("credit_card_javagen") {
     sources = [ "../components/autofill/core/browser/data_model/credit_card.h" ]
   }
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index d1a2311..4325738 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -391,6 +391,7 @@
     "//chrome:assist_ranker_prediction_enum_javagen",
     "//chrome:content_setting_javagen",
     "//chrome:content_settings_type_javagen",
+    "//chrome:cookie_controls_mode_javagen",
     "//chrome:credit_card_javagen",
     "//chrome:download_enum_javagen",
     "//chrome:instant_apps_reasons_enum_javagen",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index a5765e3..8e87099a 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1541,7 +1541,6 @@
   "java/src/org/chromium/chrome/browser/signin/UnifiedConsentServiceBridge.java",
   "java/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBar.java",
   "java/src/org/chromium/chrome/browser/sms/SmsReceiverUma.java",
-  "java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java",
   "java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java",
   "java/src/org/chromium/chrome/browser/ssl/SecurityStateModel.java",
   "java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java",
@@ -1763,6 +1762,7 @@
   "java/src/org/chromium/chrome/browser/ui/tablet/emptybackground/EmptyBackgroundViewWrapper.java",
   "java/src/org/chromium/chrome/browser/ui/tablet/emptybackground/incognitotoggle/IncognitoToggleButton.java",
   "java/src/org/chromium/chrome/browser/ui/tablet/emptybackground/incognitotoggle/IncognitoToggleButtonTablet.java",
+  "java/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarController.java",
   "java/src/org/chromium/chrome/browser/upgrade/PackageReplacedBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/usage_stats/DigitalWellbeingClient.java",
   "java/src/org/chromium/chrome/browser/usage_stats/EventTracker.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index d575736..5ab65cf 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -441,7 +441,6 @@
   "javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java",
   "javatests/src/org/chromium/chrome/browser/signin/SigninTest.java",
   "javatests/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBarTest.java",
-  "javatests/src/org/chromium/chrome/browser/snackbar/undo/UndoBarControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/ssl/CaptivePortalTest.java",
   "javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java",
   "javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java",
@@ -500,6 +499,7 @@
   "javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/translate/TranslateCompactInfoBarTest.java",
   "javatests/src/org/chromium/chrome/browser/translate/TranslateOptionsTest.java",
+  "javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/usage_stats/TabSuspensionTest.java",
   "javatests/src/org/chromium/chrome/browser/video/FullscreenVideoTest.java",
   "javatests/src/org/chromium/chrome/browser/video/VideoTest.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 43ebe700..51da646 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -261,6 +261,7 @@
     "//chrome/browser/ui/android/widget:java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/autofill_assistant/browser:proto_java",
+    "//components/browser_ui/widget/android:java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_header.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_header.xml
index 5e06cf15..0133d64 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_header.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_header.xml
@@ -45,7 +45,7 @@
         app:srcCompat="@drawable/logo_avatar_anonymous"/>
   </LinearLayout>
 
-  <org.chromium.chrome.browser.ui.widget.MaterialProgressBar
+  <org.chromium.components.browser_ui.widget.MaterialProgressBar
       android:id="@+id/progress_bar"
       android:layout_width="match_parent"
       android:layout_height="2dp"
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
index 12bd6c3..b89b2e9 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
@@ -9,7 +9,7 @@
 import android.animation.ValueAnimator;
 import android.view.View;
 
-import org.chromium.chrome.browser.ui.widget.MaterialProgressBar;
+import org.chromium.components.browser_ui.widget.MaterialProgressBar;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 
 import java.util.ArrayDeque;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java
index 0d6d115c..345f60f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java
@@ -25,7 +25,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.ui.widget.ChromeImageView;
 
 import java.util.ArrayList;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
index 18e5e20..75c49b0 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
@@ -17,7 +17,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 /**
  * This class provides a vertical expander widget, which allows to toggle between a collapsed and an
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
index 86baa1fd..19515a1e 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
@@ -47,8 +47,8 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
-import org.chromium.chrome.browser.ui.widget.MaterialProgressBar;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.widget.MaterialProgressBar;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 /**
diff --git a/chrome/android/features/media_router/java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java b/chrome/android/features/media_router/java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java
index c988ddb..e476e876 100644
--- a/chrome/android/features/media_router/java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java
+++ b/chrome/android/features/media_router/java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java
@@ -24,6 +24,7 @@
     private final String mMediaUrl;
     private MediaStatusObserver mMediaStatusObserver;
     private boolean mLoaded;
+    private boolean mHasEverReceivedValidMediaSession;
 
     FlingingControllerAdapter(RemotingSessionController sessionController, String mediaUrl) {
         mSessionController = sessionController;
@@ -140,6 +141,7 @@
 
         MediaStatus mediaStatus = remoteMediaClient.getMediaStatus();
         if (mediaStatus != null) {
+            mHasEverReceivedValidMediaSession = true;
             if (mediaStatus.getPlayerState() == MediaStatus.PLAYER_STATE_IDLE
                     && mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) {
                 mLoaded = false;
@@ -152,7 +154,11 @@
 
             mMediaStatusObserver.onMediaStatusUpdate(new MediaStatusBridge(mediaStatus));
 
-        } else {
+        } else if (mHasEverReceivedValidMediaSession) {
+            // We can receive a null |mediaStatus| while we are in the process of loading the video.
+            // We should wait until we receive one valid media status before considering the video
+            // unloaded. Otherwise, the first call to seek or play will reload the video.
+            // See b/144325733.
             mLoaded = false;
             mStreamPositionExtrapolator.clear();
         }
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
index d5e6c81c..239c1af 100644
--- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
@@ -51,7 +51,7 @@
                 android:ellipsize="none"
                 android:singleLine="true"
                 android:textAppearance="@style/TextAppearance.BlackTitle2"/>
-            <org.chromium.chrome.browser.ui.widget.RoundedCornerImageView
+            <org.chromium.components.browser_ui.widget.RoundedCornerImageView
                 android:id="@+id/tab_thumbnail"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorToolbar.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorToolbar.java
index de935ac..fbbf62e9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorToolbar.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorToolbar.java
@@ -11,10 +11,10 @@
 import android.util.AttributeSet;
 import android.widget.Button;
 
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.widget.NumberRollView;
 import org.chromium.chrome.browser.widget.selection.SelectableListToolbar;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 import java.util.Collections;
 import java.util.List;
diff --git a/chrome/android/java/res/layout/confirm_import_sync_data.xml b/chrome/android/java/res/layout/confirm_import_sync_data.xml
index e65f6a6..8f9020db 100644
--- a/chrome/android/java/res/layout/confirm_import_sync_data.xml
+++ b/chrome/android/java/res/layout/confirm_import_sync_data.xml
@@ -24,14 +24,14 @@
             android:layout_height="wrap_content"
             android:textAppearance="@style/TextAppearance.BlackTitle1" />
 
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
             android:id="@+id/sync_confirm_import_choice"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:background="?android:attr/selectableItemBackground"
             app:primaryText="@string/sync_import_existing_data" />
 
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
             android:id="@+id/sync_keep_separate_choice"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml b/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
index a8f4b4f..de2d509 100644
--- a/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
+++ b/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
@@ -47,7 +47,7 @@
 
                 <View style="@style/HorizontalDivider" />
 
-                <org.chromium.chrome.browser.ui.widget.RadioButtonLayout
+                <org.chromium.components.browser_ui.widget.RadioButtonLayout
                     android:id="@+id/default_search_engine_dialog_options"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/layout/explore_sites_loading_from_net_view.xml b/chrome/android/java/res/layout/explore_sites_loading_from_net_view.xml
index ef0582d4..6755040 100644
--- a/chrome/android/java/res/layout/explore_sites_loading_from_net_view.xml
+++ b/chrome/android/java/res/layout/explore_sites_loading_from_net_view.xml
@@ -10,7 +10,7 @@
     android:layout_height="match_parent"
     android:gravity="center"
     android:orientation="vertical" >
-    <org.chromium.chrome.browser.ui.widget.LoadingView
+    <org.chromium.components.browser_ui.widget.LoadingView
         android:id="@+id/loading"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/layout/passwords_progress_dialog.xml b/chrome/android/java/res/layout/passwords_progress_dialog.xml
index e1e03393..ba1d408 100644
--- a/chrome/android/java/res/layout/passwords_progress_dialog.xml
+++ b/chrome/android/java/res/layout/passwords_progress_dialog.xml
@@ -11,7 +11,7 @@
     android:orientation="vertical"
     style="@style/AlertDialogContent">
 
-    <org.chromium.chrome.browser.ui.widget.MaterialProgressBar
+    <org.chromium.components.browser_ui.widget.MaterialProgressBar
         android:id="@+id/passwords_progress_bar"
         android:layout_marginTop="14dp"
         android:layout_width="match_parent"
diff --git a/chrome/android/java/res/layout/radio_button_group_theme_preference.xml b/chrome/android/java/res/layout/radio_button_group_theme_preference.xml
index c246700..c1ef5e79 100644
--- a/chrome/android/java/res/layout/radio_button_group_theme_preference.xml
+++ b/chrome/android/java/res/layout/radio_button_group_theme_preference.xml
@@ -10,12 +10,12 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:focusable="false">
-    <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout
+    <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
         android:id="@+id/radio_button_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-      <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+      <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
           android:id="@+id/system_default"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
@@ -24,7 +24,7 @@
           app:descriptionText="@string/themes_system_default_summary" />
 
       <!-- override default padding top and bottom -->
-      <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+      <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
           android:id="@+id/light"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
@@ -34,7 +34,7 @@
           android:background="?attr/selectableItemBackground"
           app:primaryText="@string/light_mode" />
 
-      <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+      <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
           android:id="@+id/dark"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
@@ -44,7 +44,7 @@
           android:background="?attr/selectableItemBackground"
           app:primaryText="@string/dark_mode" />
 
-    </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
+    </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
 
     <!-- Make the row clickable -->
     <LinearLayout
diff --git a/chrome/android/java/res/layout/selectable_list_layout.xml b/chrome/android/java/res/layout/selectable_list_layout.xml
index 4404da24..2d9b64fc 100644
--- a/chrome/android/java/res/layout/selectable_list_layout.xml
+++ b/chrome/android/java/res/layout/selectable_list_layout.xml
@@ -59,14 +59,14 @@
 
         </FrameLayout>
 
-        <org.chromium.chrome.browser.ui.widget.LoadingView
+        <org.chromium.components.browser_ui.widget.LoadingView
             android:id="@+id/loading_view"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center" />
     </org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout>
 
-    <org.chromium.chrome.browser.ui.widget.FadingShadowView
+    <org.chromium.components.browser_ui.widget.FadingShadowView
         android:id="@+id/shadow"
         android:layout_width="match_parent"
         android:layout_height="@dimen/action_bar_shadow_height"
diff --git a/chrome/android/java/res/layout/sheet_tab_toolbar.xml b/chrome/android/java/res/layout/sheet_tab_toolbar.xml
index 5c7c391..a692e78 100644
--- a/chrome/android/java/res/layout/sheet_tab_toolbar.xml
+++ b/chrome/android/java/res/layout/sheet_tab_toolbar.xml
@@ -122,7 +122,7 @@
             android:progressTint="@color/modern_blue_600" />
     </LinearLayout>
 
-    <org.chromium.chrome.browser.ui.widget.FadingShadowView
+    <org.chromium.components.browser_ui.widget.FadingShadowView
         android:id="@+id/shadow"
         android:layout_width="match_parent"
         android:layout_height="@dimen/action_bar_shadow_height"
diff --git a/chrome/android/java/res/layout/status_indicator_container.xml b/chrome/android/java/res/layout/status_indicator_container.xml
index 7707c817..d004aa3 100644
--- a/chrome/android/java/res/layout/status_indicator_container.xml
+++ b/chrome/android/java/res/layout/status_indicator_container.xml
@@ -3,7 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout
+<org.chromium.components.browser_ui.widget.ViewResourceFrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/status_indicator"
     android:layout_width="match_parent"
@@ -18,4 +18,4 @@
         android:minHeight="20dp"
         android:textAlignment="center"
         android:drawablePadding="8dp" />
-</org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout>
+</org.chromium.components.browser_ui.widget.ViewResourceFrameLayout>
diff --git a/chrome/android/java/res/layout/top_view.xml b/chrome/android/java/res/layout/top_view.xml
index 231f9a88..a6aabd9 100644
--- a/chrome/android/java/res/layout/top_view.xml
+++ b/chrome/android/java/res/layout/top_view.xml
@@ -19,7 +19,7 @@
         android:layout_marginEnd="16dp"
         android:textAppearance="@style/TextAppearance.BlackBody" />
 
-    <org.chromium.chrome.browser.ui.widget.WrappingLayout
+    <org.chromium.components.browser_ui.widget.WrappingLayout
         android:id="@+id/chip_container"
         android:layout_below="@id/explanation"
         android:layout_width="match_parent"
@@ -62,7 +62,7 @@
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             style="@style/SuggestionChipContacts" />
-    </org.chromium.chrome.browser.ui.widget.WrappingLayout>
+    </org.chromium.components.browser_ui.widget.WrappingLayout>
 
     <LinearLayout
         android:id="@+id/content"
diff --git a/chrome/android/java/res/layout/tri_state_site_settings_preference.xml b/chrome/android/java/res/layout/tri_state_site_settings_preference.xml
index 9b0a653..0288182 100644
--- a/chrome/android/java/res/layout/tri_state_site_settings_preference.xml
+++ b/chrome/android/java/res/layout/tri_state_site_settings_preference.xml
@@ -13,13 +13,13 @@
     android:focusable="false"
     android:orientation="vertical">
 
-    <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout
+    <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
         android:id="@+id/radio_button_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
 
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
             android:id="@+id/allowed"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -27,7 +27,7 @@
             android:background="?android:attr/selectableItemBackground"
             app:primaryText="@string/website_settings_category_allowed" />
 
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
             android:id="@+id/ask"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -35,7 +35,7 @@
             android:background="?android:attr/selectableItemBackground"
             app:primaryText="@string/website_settings_category_ask" />
 
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
             android:id="@+id/blocked"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -43,5 +43,5 @@
             android:background="?android:attr/selectableItemBackground"
             app:primaryText="@string/website_settings_category_blocked" />
 
-    </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
+    </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
 </LinearLayout>
diff --git a/chrome/android/java/res_download/layout/download_home_toolbar.xml b/chrome/android/java/res_download/layout/download_home_toolbar.xml
index 5d4b42fb20..7d69974 100644
--- a/chrome/android/java/res_download/layout/download_home_toolbar.xml
+++ b/chrome/android/java/res_download/layout/download_home_toolbar.xml
@@ -21,7 +21,7 @@
         android:layout_height="@dimen/toolbar_height_no_shadow"
         style="@style/ModernToolbar"/>
 
-    <org.chromium.chrome.browser.ui.widget.FadingShadowView
+    <org.chromium.components.browser_ui.widget.FadingShadowView
         android:id="@+id/shadow"
         android:layout_width="match_parent"
         android:layout_height="@dimen/action_bar_shadow_height"
diff --git a/chrome/android/java/res_download/layout/downloads_empty_view.xml b/chrome/android/java/res_download/layout/downloads_empty_view.xml
index f4eefa1..e66323ca 100644
--- a/chrome/android/java/res_download/layout/downloads_empty_view.xml
+++ b/chrome/android/java/res_download/layout/downloads_empty_view.xml
@@ -28,7 +28,7 @@
 
     </FrameLayout>
 
-    <org.chromium.chrome.browser.ui.widget.LoadingView
+    <org.chromium.components.browser_ui.widget.LoadingView
         android:id="@+id/loading"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index e661189..d14ecc7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -265,6 +265,7 @@
     public static final String INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE =
             "IntentBlockExternalFormRedirectsNoGesture";
     public static final String INTEREST_FEED_CONTENT_SUGGESTIONS = "InterestFeedContentSuggestions";
+    public static final String INTEREST_FEED_FEEDBACK = "InterestFeedFeedback";
     public static final String KITKAT_SUPPORTED = "KitKatSupported";
     public static final String LOOKALIKE_NAVIGATION_URL_SUGGESTIONS_UI =
             "LookalikeUrlNavigationSuggestionsUI";
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 56133c9..320055c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -111,7 +111,6 @@
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.SearchEngineChoiceNotification;
-import org.chromium.chrome.browser.snackbar.undo.UndoBarController;
 import org.chromium.chrome.browser.suggestions.SuggestionsEventReporterBridge;
 import org.chromium.chrome.browser.suggestions.SuggestionsMetrics;
 import org.chromium.chrome.browser.survey.ChromeSurveyController;
@@ -145,6 +144,7 @@
 import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer;
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate;
+import org.chromium.chrome.browser.undo_tab_close_snackbar.UndoBarController;
 import org.chromium.chrome.browser.usage_stats.UsageStatsService;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.chrome.browser.util.IntentUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
index f2e9c08..eff95c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
@@ -46,9 +46,9 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.settings.autofill.CreditCardNumberFormattingTextWatcher;
-import org.chromium.chrome.browser.ui.widget.AlwaysDismissedDialog;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.AlwaysDismissedDialog;
 import org.chromium.components.browser_ui.widget.FadingEdgeScrollView;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorTextField.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorTextField.java
index 7655d9bb..b68cbb1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorTextField.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorTextField.java
@@ -28,7 +28,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.text.ChromeTextInputLayout;
 
 /** Handles validation and display of one field from the {@link EditorFieldModel}. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/HintedDropDownAdapterWithPlusIcon.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/HintedDropDownAdapterWithPlusIcon.java
index 986b8547..349cf22f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/HintedDropDownAdapterWithPlusIcon.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/HintedDropDownAdapterWithPlusIcon.java
@@ -13,7 +13,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.ui.UiUtils;
 
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java
index 87c2566..0a0334d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java
@@ -20,8 +20,8 @@
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
index 84476fc..7c84799 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
@@ -17,8 +17,8 @@
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.url_formatter.UrlFormatter;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
index d5e5f1a59..ef6f4ffa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
@@ -32,11 +32,11 @@
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkType;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.PageTransition;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TwaFinishHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TwaFinishHandler.java
index 1cc85b4d..23e0a34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TwaFinishHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TwaFinishHandler.java
@@ -4,11 +4,15 @@
 
 package org.chromium.chrome.browser.browserservices.trustedwebactivityui;
 
+import android.os.Build;
 import android.os.Bundle;
 
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
+import org.chromium.chrome.browser.webapps.WebApkExtras;
+import org.chromium.chrome.browser.webapps.WebApkServiceClient;
 
 import javax.inject.Inject;
 
@@ -20,14 +24,16 @@
     private static final String FINISH_TASK_COMMAND_NAME = "finishAndRemoveTask";
     private static final String SUCCESS_KEY = "success";
 
+    private final ChromeActivity mActivity;
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
     private final CustomTabsConnection mConnection;
 
     private boolean mShouldAttemptFinishingTask;
 
     @Inject
-    public TwaFinishHandler(
+    public TwaFinishHandler(ChromeActivity activity,
             BrowserServicesIntentDataProvider intentDataProvider, CustomTabsConnection connection) {
+        mActivity = activity;
         mIntentDataProvider = intentDataProvider;
         mConnection = connection;
     }
@@ -54,6 +60,12 @@
     }
 
     private boolean finishAndRemoveTask() {
+        WebApkExtras webApkExtras = mIntentDataProvider.getWebApkExtras();
+        if (webApkExtras != null && Build.VERSION.SDK_INT >= 23) {
+            WebApkServiceClient.getInstance().finishAndRemoveTaskSdk23(mActivity, webApkExtras);
+            return true;
+        }
+
         // This is the analogue to IWebApkApi#finishAndRemoveTaskSdk23().
         // Currently we don't make this API public, because there could potentially be a way of
         // avoiding it altogether in the two use cases WebAPKs currently have.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
index fa0bbf0b..8638a24b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
@@ -24,7 +24,6 @@
 import androidx.browser.trusted.splashscreens.SplashScreenParamKey;
 
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
-import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TwaFinishHandler;
 import org.chromium.chrome.browser.customtabs.TranslucentCustomTabActivity;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
@@ -79,14 +78,13 @@
     private final ScreenOrientationProvider mScreenOrientationProvider;
     private final SplashImageHolder mSplashImageCache;
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
-    private final TwaFinishHandler mFinishHandler;
 
     @Inject
     public TwaSplashController(SplashController splashController, Activity activity,
             ActivityWindowAndroid activityWindowAndroid,
             ActivityLifecycleDispatcher lifecycleDispatcher,
             ScreenOrientationProvider screenOrientationProvider, SplashImageHolder splashImageCache,
-            BrowserServicesIntentDataProvider intentDataProvider, TwaFinishHandler finishHandler) {
+            BrowserServicesIntentDataProvider intentDataProvider) {
         mSplashController = splashController;
         mActivity = activity;
         mActivityWindowAndroid = activityWindowAndroid;
@@ -94,7 +92,6 @@
         mScreenOrientationProvider = screenOrientationProvider;
         mSplashImageCache = splashImageCache;
         mIntentDataProvider = intentDataProvider;
-        mFinishHandler = finishHandler;
 
         long splashHideAnimationDurationMs = IntentUtils.safeGetInt(
                 getSplashScreenParamsFromIntent(), SplashScreenParamKey.KEY_FADE_OUT_DURATION_MS,
@@ -111,11 +108,6 @@
         if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
             mScreenOrientationProvider.delayOrientationRequests(mActivityWindowAndroid);
         }
-
-        // If the client's activity is opaque, finishing the activities one after another may lead
-        // to bottom activity showing itself in a short flash. The problem can be solved by bottom
-        // activity killing the whole task.
-        mFinishHandler.setShouldAttemptFinishingTask(true);
     }
 
     @Override
@@ -135,7 +127,6 @@
     @Override
     public void onSplashHidden(Tab tab, @SplashController.SplashHidesReason int reason,
             long startTimestamp, long endTimestamp) {
-        mFinishHandler.setShouldAttemptFinishingTask(false);
         mLifecycleDispatcher.unregister(this); // Unregister to get gc-ed
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
index 6aa7305..97736b8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
@@ -22,10 +22,10 @@
 import org.chromium.chrome.browser.thinwebview.ThinWebView;
 import org.chromium.chrome.browser.thinwebview.ThinWebViewConstraints;
 import org.chromium.chrome.browser.thinwebview.ThinWebViewFactory;
-import org.chromium.chrome.browser.ui.widget.FadingShadow;
-import org.chromium.chrome.browser.ui.widget.FadingShadowView;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.widget.FadingShadow;
+import org.chromium.components.browser_ui.widget.FadingShadowView;
 import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.RenderCoordinates;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
index 61419c0..8ee239bb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
@@ -13,7 +13,7 @@
 import org.chromium.chrome.browser.compositor.layouts.components.VirtualView;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
 import org.chromium.chrome.browser.compositor.overlays.SceneOverlay;
-import org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout;
+import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
 import org.chromium.ui.resources.ResourceManager;
 
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
index de10a6f..e6ec2d42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
@@ -21,7 +21,7 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.toolbar.ControlContainer;
 import org.chromium.chrome.browser.toolbar.ToolbarColors;
-import org.chromium.chrome.browser.ui.widget.ClipDrawableProgressBar.DrawingInfo;
+import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar.DrawingInfo;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.resources.ResourceManager;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
index 3b5a86a16..ee7dee97 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java
@@ -19,7 +19,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.share.ShareParams;
-import org.chromium.chrome.browser.ui.widget.ContextMenuDialog;
+import org.chromium.components.browser_ui.widget.ContextMenuDialog;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
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 8d5edc7..a5f89c5 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
@@ -4,12 +4,14 @@
 
 package org.chromium.chrome.browser.customtabs;
 
+import android.content.Intent;
 import android.util.Pair;
 import android.view.KeyEvent;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.KeyboardShortcuts;
@@ -21,7 +23,6 @@
 import org.chromium.chrome.browser.customtabs.dependency_injection.BaseCustomTabActivityComponent;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarColorController;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarCoordinator;
-import org.chromium.chrome.browser.dependency_injection.ChromeActivityComponent;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabState;
 import org.chromium.chrome.browser.tabmodel.ChromeTabCreator;
@@ -35,7 +36,7 @@
  * and {@link CustomTabActivity}.
  * @param <C> - type of associated Dagger component.
  */
-public abstract class BaseCustomTabActivity<C extends ChromeActivityComponent>
+public abstract class BaseCustomTabActivity<C extends BaseCustomTabActivityComponent>
         extends ChromeActivity<C> {
     protected CustomTabToolbarCoordinator mToolbarCoordinator;
     protected CustomTabActivityNavigationController mNavigationController;
@@ -77,6 +78,17 @@
         component.resolveTaskDescriptionHelper();
     }
 
+    /**
+     * Return true when the activity has been launched in a separate task. The default behavior is
+     * to reuse the same task and put the activity on top of the previous one (i.e hiding it). A
+     * separate task creates a new entry in the Android recent screen.
+     */
+    protected boolean useSeparateTask() {
+        final int separateTaskFlags =
+                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+        return (getIntent().getFlags() & separateTaskFlags) != 0;
+    }
+
     @Override
     protected TabModelSelector createTabModelSelector() {
         return mTabFactory.createTabModelSelector();
@@ -138,6 +150,46 @@
     }
 
     @Override
+    public void finish() {
+        super.finish();
+        BrowserServicesIntentDataProvider intentDataProvider = getIntentDataProvider();
+        if (intentDataProvider != null && intentDataProvider.shouldAnimateOnFinish()) {
+            mShouldOverridePackage = true;
+            overridePendingTransition(intentDataProvider.getAnimationEnterRes(),
+                    intentDataProvider.getAnimationExitRes());
+            mShouldOverridePackage = false;
+        } else if (intentDataProvider != null && intentDataProvider.isOpenedByChrome()) {
+            overridePendingTransition(R.anim.no_anim, R.anim.activity_close_exit);
+        }
+    }
+
+    /**
+     * Internal implementation that finishes the activity and removes the references from Android
+     * recents.
+     */
+    protected void handleFinishAndClose() {
+        Runnable defaultBehavior = () -> {
+            if (useSeparateTask()) {
+                ApiCompatibilityUtils.finishAndRemoveTask(this);
+            } else {
+                finish();
+            }
+        };
+        BrowserServicesIntentDataProvider intentDataProvider = getIntentDataProvider();
+        if (intentDataProvider.isTrustedWebActivity()
+                || intentDataProvider.getWebappExtras() != null) {
+            // TODO(pshmakov): extract all finishing logic from BaseCustomTabActivity.
+            // In addition to TwaFinishHandler, create DefaultFinishHandler, PaymentsFinishHandler,
+            // and SeparateTaskActivityFinishHandler, all implementing
+            // CustomTabActivityNavigationController#FinishHandler. Pass the mode enum into
+            // CustomTabActivityModule, so that it can provide the correct implementation.
+            getComponent().resolveTwaFinishHandler().onFinish(defaultBehavior);
+        } else {
+            defaultBehavior.run();
+        }
+    }
+
+    @Override
     public boolean canShowAppMenu() {
         if (getActivityTab() == null || !mToolbarCoordinator.toolbarIsInitialized()) return false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
index b08727f01..3c2f7cf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
@@ -27,8 +27,8 @@
 
 import org.chromium.base.Log;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.IntentUtils;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.ui.widget.Toast;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 2ef57e85..c9917cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -78,17 +78,6 @@
 
     private CustomTabNightModeStateController mNightModeStateController;
 
-    /**
-     * Return true when the activity has been launched in a separate task. The default behavior is
-     * to reuse the same task and put the activity on top of the previous one (i.e hiding it). A
-     * separate task creates a new entry in the Android recent screen.
-     **/
-    private boolean useSeparateTask() {
-        final int separateTaskFlags =
-                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-        return (getIntent().getFlags() & separateTaskFlags) != 0;
-    }
-
     private CustomTabActivityTabProvider.Observer mTabChangeObserver =
             new CustomTabActivityTabProvider.Observer() {
         @Override
@@ -274,43 +263,6 @@
     }
 
     @Override
-    public void finish() {
-        super.finish();
-        if (mIntentDataProvider != null && mIntentDataProvider.shouldAnimateOnFinish()) {
-            mShouldOverridePackage = true;
-            overridePendingTransition(mIntentDataProvider.getAnimationEnterRes(),
-                    mIntentDataProvider.getAnimationExitRes());
-            mShouldOverridePackage = false;
-        } else if (mIntentDataProvider != null && mIntentDataProvider.isOpenedByChrome()) {
-            overridePendingTransition(R.anim.no_anim, R.anim.activity_close_exit);
-        }
-    }
-
-    /**
-     * Internal implementation that finishes the activity and removes the references from Android
-     * recents.
-     */
-    protected void handleFinishAndClose() {
-        Runnable defaultBehavior = () -> {
-            if (useSeparateTask()) {
-                ApiCompatibilityUtils.finishAndRemoveTask(this);
-            } else {
-                finish();
-            }
-        };
-        if (mIntentDataProvider.isTrustedWebActivity()) {
-            // TODO(pshmakov): extract all finishing logic from CustomTabActivity.
-            // In addition to TwaFinishHandler, create DefaultFinishHandler, PaymentsFinishHandler,
-            // and SeparateTaskActivityFinishHandler, all implementing
-            // CustomTabActivityNavigationController#FinishHandler. Pass the mode enum into
-            // CustomTabActivityModule, so that it can provide the correct implementation.
-            getComponent().resolveTwaFinishHandler().onFinish(defaultBehavior);
-        } else {
-            defaultBehavior.run();
-        }
-    }
-
-    @Override
     public boolean onOptionsItemSelected(int itemId, @Nullable Bundle menuItemData) {
         int menuIndex =
                 CustomTabAppMenuPropertiesDelegate.getIndexOfMenuItemFromBundle(menuItemData);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index 6dc9387..df6809ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -48,11 +48,11 @@
 import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleMetrics;
 import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java
index 9863357..6156c4b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.customtabs.dependency_injection;
 
+import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TwaFinishHandler;
 import org.chromium.chrome.browser.customtabs.CustomTabCompositorContentInitializer;
 import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory;
 import org.chromium.chrome.browser.customtabs.CustomTabStatusBarColorProvider;
@@ -33,4 +34,5 @@
     CustomTabToolbarCoordinator resolveToolbarCoordinator();
     SplashController resolveSplashController();
     TabObserverRegistrar resolveTabObserverRegistrar();
+    TwaFinishHandler resolveTwaFinishHandler();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
index c4e9657..2c17611 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.customtabs.dependency_injection;
 
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityCoordinator;
-import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TwaFinishHandler;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityClientConnectionKeeper;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityLifecycleUmaTracker;
 import org.chromium.chrome.browser.customtabs.CustomTabBottomBarDelegate;
@@ -43,7 +42,6 @@
     CustomTabUmaRecorder resolveCustomTabUmaRecorder();
     CustomTabSessionHandler resolveSessionHandler();
     CustomTabActivityClientConnectionKeeper resolveConnectionKeeper();
-    TwaFinishHandler resolveTwaFinishHandler();
     ImmersiveModeController resolveImmersiveModeController();
 
     // For testing
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/DataReductionPromoScreen.java b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/DataReductionPromoScreen.java
index a8cd098..ce630a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/DataReductionPromoScreen.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/DataReductionPromoScreen.java
@@ -13,7 +13,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
-import org.chromium.chrome.browser.ui.widget.PromoDialog;
+import org.chromium.components.browser_ui.widget.PromoDialog;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyView.java
index a0d5886..1cf168a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyView.java
@@ -13,8 +13,8 @@
 import androidx.annotation.StringRes;
 
 import org.chromium.chrome.browser.download.home.empty.EmptyProperties.State;
-import org.chromium.chrome.browser.ui.widget.LoadingView;
 import org.chromium.chrome.download.R;
+import org.chromium.components.browser_ui.widget.LoadingView;
 
 /** A view that represents the visuals required for the empty state of the download home list. */
 class EmptyView {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/view/ForegroundRoundedCornerImageView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/view/ForegroundRoundedCornerImageView.java
index 66e1a4c..fd5b5d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/view/ForegroundRoundedCornerImageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/view/ForegroundRoundedCornerImageView.java
@@ -12,8 +12,8 @@
 import android.view.View;
 import android.widget.ImageView;
 
-import org.chromium.chrome.browser.ui.widget.RoundedCornerImageView;
 import org.chromium.chrome.download.R;
+import org.chromium.components.browser_ui.widget.RoundedCornerImageView;
 
 /** Helper class that adds foreground drawable support to {@code RoundedCornerImageView}. */
 public class ForegroundRoundedCornerImageView extends RoundedCornerImageView {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
index 0a2355f..93be5f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
@@ -13,12 +13,12 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.browser.download.home.list.ListItem;
 import org.chromium.chrome.browser.download.home.metrics.UmaUtils;
-import org.chromium.chrome.browser.ui.widget.FadingShadow;
-import org.chromium.chrome.browser.ui.widget.FadingShadowView;
 import org.chromium.chrome.browser.widget.selection.SelectableListToolbar;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
 import org.chromium.chrome.download.R;
+import org.chromium.components.browser_ui.widget.FadingShadow;
+import org.chromium.components.browser_ui.widget.FadingShadowView;
 import org.chromium.components.feature_engagement.Tracker;
 
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
index cf2f894..46126cae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.ui.widget.LoadingView;
+import org.chromium.components.browser_ui.widget.LoadingView;
 import org.chromium.ui.modelutil.ForwardingListObservable;
 import org.chromium.ui.modelutil.ListObservable.ListObserver;
 import org.chromium.ui.modelutil.PropertyKey;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
index 84c9420d..4acd412d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
@@ -18,7 +18,7 @@
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
-import org.chromium.chrome.browser.ui.widget.RadioButtonLayout;
+import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 /** A {@link Fragment} that presents a set of search engines for the user to choose from. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
index 75f379d..f4791be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
@@ -19,8 +19,8 @@
 import org.chromium.chrome.browser.favicon.RoundedIconGenerator;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.UrlConstants;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.NavigationHistory;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
index 6cb628b..0870804 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
@@ -24,13 +24,13 @@
 import org.chromium.chrome.browser.history.HistoryProvider.BrowsingHistoryObserver;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.ui.widget.MoreProgressButton;
-import org.chromium.chrome.browser.ui.widget.MoreProgressButton.State;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.chrome.browser.widget.DateDividedAdapter;
 import org.chromium.chrome.browser.widget.selection.SelectableItemViewHolder;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
+import org.chromium.components.browser_ui.widget.MoreProgressButton;
+import org.chromium.components.browser_ui.widget.MoreProgressButton.State;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarControlLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarControlLayout.java
index 34e9b80..2e20599c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarControlLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarControlLayout.java
@@ -25,8 +25,8 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.RadioButtonLayout;
 import org.chromium.components.browser_ui.widget.DualControlLayout;
+import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 
 import java.util.List;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
index a87f7ff8..7841732e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
@@ -13,7 +13,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
-import org.chromium.chrome.browser.ui.widget.RadioButtonLayout;
+import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 import org.chromium.components.search_engines.TemplateUrl;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java
index 6ef6f58..d02c9c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java
@@ -17,8 +17,8 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
-import org.chromium.chrome.browser.ui.widget.PromoDialog;
-import org.chromium.chrome.browser.ui.widget.RadioButtonLayout;
+import org.chromium.components.browser_ui.widget.PromoDialog;
+import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 
 /** A dialog that forces the user to choose a default search engine. */
 public class DefaultSearchEnginePromoDialog extends PromoDialog {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index 7c172a3..ffbc8be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -32,9 +32,9 @@
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController;
-import org.chromium.chrome.browser.ui.widget.PromoDialog;
 import org.chromium.chrome.browser.vr.OnExitVrRequestListener;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
+import org.chromium.components.browser_ui.widget.PromoDialog;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.ui.base.PageTransition;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
index 8da2451..3b46588 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
@@ -24,7 +24,7 @@
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.settings.search_engine.SearchEngineSettings;
-import org.chromium.chrome.browser.ui.widget.PromoDialog;
+import org.chromium.components.browser_ui.widget.PromoDialog;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoView.java
index 9201d73..213cef2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoView.java
@@ -31,7 +31,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.LogoBridge.Logo;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
-import org.chromium.chrome.browser.ui.widget.LoadingView;
+import org.chromium.components.browser_ui.widget.LoadingView;
 
 import java.lang.ref.WeakReference;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsGroupView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsGroupView.java
index 3a214bbd..928d08b6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsGroupView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsGroupView.java
@@ -15,7 +15,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.ForeignSessionHelper.ForeignSession;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 /**
  * Header view shown above each group of items on the Recent Tabs page. Shows the name of the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
index 08be86b1..dbafc31c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
@@ -18,10 +18,12 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+
 import androidx.annotation.ColorRes;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
+
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
index 3db4769c..e5a8499f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
@@ -19,8 +19,8 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
-import org.chromium.chrome.browser.ui.widget.RoundedCornerImageView;
 import org.chromium.chrome.browser.util.KeyNavigationUtil;
+import org.chromium.components.browser_ui.widget.RoundedCornerImageView;
 
 /**
  * Base layout for common suggestion types. Includes support for a configurable suggestion content
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
index 33f243af..223d6b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -16,8 +16,8 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
-import org.chromium.chrome.browser.ui.widget.RoundedCornerImageView;
 import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.components.browser_ui.widget.RoundedCornerImageView;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
index 99c551d..42e4ff3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
@@ -12,7 +12,7 @@
 import android.widget.ImageView;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.RoundedCornerImageView;
+import org.chromium.components.browser_ui.widget.RoundedCornerImageView;
 
 /**
  * Container view for omnibox suggestions supplying icon decoration.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentPreferencesUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentPreferencesUtil.java
index 782e441..a1070e2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentPreferencesUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentPreferencesUtil.java
@@ -6,37 +6,28 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.ContextUtils;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
 /** Place to define and control payment preferences. */
 public class PaymentPreferencesUtil {
     // Avoid instantiation by accident.
     private PaymentPreferencesUtil() {}
 
-    /** Preference to indicate whether payment request has been completed successfully once.*/
-    private static final String PAYMENT_COMPLETE_ONCE = "payment_complete_once";
-
-    /** Prefix of the preferences to persist use count of the payment instruments. */
-    public static final String PAYMENT_INSTRUMENT_USE_COUNT_ = "payment_instrument_use_count_";
-
-    /** Prefix of the preferences to persist last use date of the payment instruments. */
-    public static final String PAYMENT_INSTRUMENT_USE_DATE_ = "payment_instrument_use_date_";
-
     /**
      * Checks whehter the payment request has been successfully completed once.
      *
      * @return True If payment request has been successfully completed once.
      */
     public static boolean isPaymentCompleteOnce() {
-        return ContextUtils.getAppSharedPreferences().getBoolean(PAYMENT_COMPLETE_ONCE, false);
+        return SharedPreferencesManager.getInstance().readBoolean(
+                ChromePreferenceKeys.PAYMENTS_PAYMENT_COMPLETE_ONCE, false);
     }
 
     /** Sets the payment request has been successfully completed once. */
     public static void setPaymentCompleteOnce() {
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .putBoolean(PAYMENT_COMPLETE_ONCE, true)
-                .apply();
+        SharedPreferencesManager.getInstance().writeBoolean(
+                ChromePreferenceKeys.PAYMENTS_PAYMENT_COMPLETE_ONCE, true);
     }
 
     /**
@@ -46,7 +37,8 @@
      * @return The use count.
      */
     public static int getPaymentInstrumentUseCount(String id) {
-        return ContextUtils.getAppSharedPreferences().getInt(PAYMENT_INSTRUMENT_USE_COUNT_ + id, 0);
+        return SharedPreferencesManager.getInstance().readInt(
+                ChromePreferenceKeys.PAYMENTS_PAYMENT_INSTRUMENT_USE_COUNT.createKey(id));
     }
 
     /**
@@ -55,10 +47,8 @@
      * @param id The instrument identifier.
      */
     public static void increasePaymentInstrumentUseCount(String id) {
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .putInt(PAYMENT_INSTRUMENT_USE_COUNT_ + id, getPaymentInstrumentUseCount(id) + 1)
-                .apply();
+        SharedPreferencesManager.getInstance().incrementInt(
+                ChromePreferenceKeys.PAYMENTS_PAYMENT_INSTRUMENT_USE_COUNT.createKey(id));
     }
 
     /**
@@ -69,10 +59,8 @@
      */
     @VisibleForTesting
     public static void setPaymentInstrumentUseCountForTest(String id, int count) {
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .putInt(PAYMENT_INSTRUMENT_USE_COUNT_ + id, count)
-                .apply();
+        SharedPreferencesManager.getInstance().writeInt(
+                ChromePreferenceKeys.PAYMENTS_PAYMENT_INSTRUMENT_USE_COUNT.createKey(id), count);
     }
 
     /**
@@ -80,10 +68,11 @@
      *
      * @param id The instrument identifier.
      * @return The time difference between the last use date and 'midnight, January 1, 1970 UTC' in
-     *         millieseconds.
+     *         milliseconds.
      */
     public static long getPaymentInstrumentLastUseDate(String id) {
-        return ContextUtils.getAppSharedPreferences().getLong(PAYMENT_INSTRUMENT_USE_DATE_ + id, 0);
+        return SharedPreferencesManager.getInstance().readLong(
+                ChromePreferenceKeys.PAYMENTS_PAYMENT_INSTRUMENT_USE_DATE.createKey(id));
     }
 
     /**
@@ -91,12 +80,10 @@
      *
      * @param id   The instrument identifier.
      * @param date The time difference between the last use date and 'midnight, January 1, 1970 UTC'
-     *             in millieseconds.
+     *             in milliseconds.
      */
     public static void setPaymentInstrumentLastUseDate(String id, long date) {
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .putLong(PAYMENT_INSTRUMENT_USE_DATE_ + id, date)
-                .apply();
+        SharedPreferencesManager.getInstance().writeLong(
+                ChromePreferenceKeys.PAYMENTS_PAYMENT_INSTRUMENT_USE_DATE.createKey(id), date);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarView.java
index 0e3f299..e5c9f92 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/toolbar/PaymentHandlerToolbarView.java
@@ -15,8 +15,8 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.payments.handler.toolbar.PaymentHandlerToolbarCoordinator.PaymentHandlerToolbarObserver;
-import org.chromium.chrome.browser.ui.widget.FadingShadow;
-import org.chromium.chrome.browser.ui.widget.FadingShadowView;
+import org.chromium.components.browser_ui.widget.FadingShadow;
+import org.chromium.components.browser_ui.widget.FadingShadowView;
 
 /** PaymentHandlerToolbar UI. */
 /* package */ class PaymentHandlerToolbarView {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
index 5ec5ba27..af768c5f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
@@ -27,8 +27,8 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.AlwaysDismissedDialog;
 import org.chromium.chrome.browser.util.ColorUtils;
+import org.chromium.components.browser_ui.widget.AlwaysDismissedDialog;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java
index 5de1787..3095171a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java
@@ -19,9 +19,9 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.UrlConstants;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 /** This class represents a bar to display at the top of the payment request UI. */
 public class PaymentRequestHeader extends FrameLayout {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
index d9a0944..ef5ac51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
@@ -37,8 +37,8 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.prefeditor.EditableOption;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.DualControlLayout;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 import org.chromium.ui.HorizontalListDividerDrawable;
 import org.chromium.ui.UiUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/rlz/RevenueStats.java b/chrome/android/java/src/org/chromium/chrome/browser/rlz/RevenueStats.java
index 6cbb8e9..7409f55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/rlz/RevenueStats.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/rlz/RevenueStats.java
@@ -46,15 +46,6 @@
     }
 
     /**
-     * Stores whether the RLZ provider has been notified that the first search has occurred as
-     * shared preference.
-     */
-    protected static void setRlzNotified(boolean notified) {
-        SharedPreferencesManager.getInstance().writeBoolean(
-                ChromePreferenceKeys.RLZ_NOTIFIED, notified);
-    }
-
-    /**
      * Stores that the RLZ provider has been notified that the first search has occurred.
      */
     protected static void markRlzNotified() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListPreference.java
index 1138d32..b5b2b9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListPreference.java
@@ -21,10 +21,10 @@
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.translate.TranslateBridge;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.ui.widget.listmenu.BasicListMenu;
 import org.chromium.chrome.browser.ui.widget.listmenu.ListMenu;
 import org.chromium.chrome.browser.ui.widget.listmenu.ListMenuItemProperties;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/password/ProgressBarDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/password/ProgressBarDialogFragment.java
index feb42aa4..ff41079 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/password/ProgressBarDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/password/ProgressBarDialogFragment.java
@@ -12,7 +12,7 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.MaterialProgressBar;
+import org.chromium.components.browser_ui.widget.MaterialProgressBar;
 
 /**
  * Shows the dialog that informs the user about the progress of preparing passwords for export and
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/themes/RadioButtonGroupThemePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/themes/RadioButtonGroupThemePreference.java
index f9e0840..f5863b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/themes/RadioButtonGroupThemePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/themes/RadioButtonGroupThemePreference.java
@@ -19,8 +19,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.night_mode.NightModeMetrics;
-import org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription;
-import org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout;
+import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
+import org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/TriStateSiteSettingsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/TriStateSiteSettingsPreference.java
index af3c6fce..17b784b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/TriStateSiteSettingsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/TriStateSiteSettingsPreference.java
@@ -11,7 +11,7 @@
 import android.widget.RadioGroup;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription;
+import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
 
 /**
  * A 3-state Allowed/Ask/Blocked radio group Preference used for SiteSettings.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
index 141ddff..7a8e959 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
@@ -16,7 +16,7 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
-import org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription;
+import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
 
 import java.util.Arrays;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/OWNERS
deleted file mode 100644
index 700893f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-twellington@chromium.org
-
-# COMPONENT: UI>Browser>Mobile
-# OS: Android
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java
index b2bb78e..14dad511 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java
@@ -10,7 +10,7 @@
 import android.view.ViewStub;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout;
+import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.resources.ResourceManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java
index 5d740c6..8e3d5d44 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java
@@ -14,7 +14,7 @@
 import org.chromium.chrome.browser.compositor.overlays.SceneOverlay;
 import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
 import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer;
-import org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout;
+import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
 import org.chromium.ui.resources.ResourceManager;
 
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinder.java
index 7523f37..4c6be1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinder.java
@@ -8,7 +8,7 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout;
+import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ControlContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ControlContainer.java
index 03d9798..936f117 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ControlContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ControlContainer.java
@@ -7,7 +7,7 @@
 import android.view.View;
 
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandler;
-import org.chromium.chrome.browser.ui.widget.ClipDrawableProgressBar;
+import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java
index 117bd33e..0ff3ea7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java
@@ -17,7 +17,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 import java.util.Locale;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java
index 1d9280a6..30c91ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java
@@ -26,9 +26,9 @@
 import org.chromium.base.MathUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.ClipDrawableProgressBar;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
+import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java
index 227c6d5..1be79e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java
@@ -14,7 +14,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandler;
 import org.chromium.chrome.browser.contextualsearch.SwipeRecognizer;
-import org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout;
+import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
index ad07b87..8e19082 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
@@ -65,9 +65,9 @@
 import org.chromium.chrome.browser.toolbar.ToolbarColors;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarTabController;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.common.ContentUrlConstants;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
index 60906e9..d4ce739 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
@@ -21,9 +21,9 @@
 import org.chromium.chrome.browser.contextualsearch.SwipeRecognizer;
 import org.chromium.chrome.browser.toolbar.ControlContainer;
 import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
-import org.chromium.chrome.browser.ui.widget.ClipDrawableProgressBar.DrawingInfo;
-import org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout;
 import org.chromium.chrome.browser.util.ViewUtils;
+import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar.DrawingInfo;
+import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
 import org.chromium.ui.widget.OptimizedFrameLayout;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java b/chrome/android/java/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarController.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java
rename to chrome/android/java/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarController.java
index 6f6b4e9..84db1415 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarController.java
@@ -2,7 +2,7 @@
 // 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.snackbar.undo;
+package org.chromium.chrome.browser.undo_tab_close_snackbar;
 
 import android.content.Context;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashController.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashController.java
index 402d76d..43cc7a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashController.java
@@ -22,6 +22,7 @@
 import org.chromium.base.TraceEvent;
 import org.chromium.base.metrics.CachedMetrics;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TwaFinishHandler;
 import org.chromium.chrome.browser.compositor.CompositorView;
 import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar;
 import org.chromium.chrome.browser.flags.FeatureUtilities;
@@ -91,6 +92,7 @@
     private final Activity mActivity;
     private final ActivityLifecycleDispatcher mLifecycleDispatcher;
     private final TabObserverRegistrar mTabObserverRegistrar;
+    private final TwaFinishHandler mFinishHandler;
 
     private SplashDelegate mDelegate;
 
@@ -124,12 +126,13 @@
 
     @Inject
     public SplashController(Activity activity, ActivityLifecycleDispatcher lifecycleDispatcher,
-            TabObserverRegistrar tabObserverRegistrar) {
+            TabObserverRegistrar tabObserverRegistrar, TwaFinishHandler finishHandler) {
         mActivity = activity;
         mLifecycleDispatcher = lifecycleDispatcher;
         mTabObserverRegistrar = tabObserverRegistrar;
         mObservers = new ObserverList<>();
         mTranslucencyRemovalStrategy = TranslucencyRemoval.NONE;
+        mFinishHandler = finishHandler;
 
         mLifecycleDispatcher.register(this);
         mTabObserverRegistrar.registerTabObserver(this);
@@ -246,6 +249,11 @@
             // SurfaceView is attached.
             removeTranslucency();
         }
+
+        // If the client's activity is opaque, finishing the activities one after another may lead
+        // to bottom activity showing itself in a short flash. The problem can be solved by bottom
+        // activity killing the whole task.
+        mFinishHandler.setShouldAttemptFinishingTask(true);
     }
 
     private static @TranslucencyRemoval int computeTranslucencyRemovalStrategy(
@@ -356,6 +364,8 @@
         mDelegate.onSplashHidden(tab, reason, mSplashShownTimestamp, splashHiddenTimestamp);
         notifySplashscreenHidden(mSplashShownTimestamp, splashHiddenTimestamp);
 
+        mFinishHandler.setShouldAttemptFinishingTask(false);
+
         mLifecycleDispatcher.unregister(this);
 
         mDelegate = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
index cdcb47c..0498538 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
@@ -189,21 +189,6 @@
     }
 
     @Override
-    protected void handleFinishAndClose() {
-        if (getWebApkInfo().isSplashProvidedByWebApk() && isSplashShowing()) {
-            // When the WebAPK provides the splash screen, the splash screen activity is stacked
-            // underneath the WebAPK. The splash screen finishes itself in
-            // {@link Activity#onResume()}. When finishing the WebApkActivity, there is sometimes a
-            // frame of the splash screen drawn prior to the splash screen activity finishing
-            // itself. There are no glitches when the activity stack is finished via
-            // {@link ActivityManager.AppTask#finishAndRemoveTask()}.
-            WebApkServiceClient.getInstance().finishAndRemoveTaskSdk23(this);
-            return;
-        }
-        finish();
-    }
-
-    @Override
     @ActivityType
     public int getActivityType() {
         return ActivityType.WEB_APK;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java
index 77bd730..8d3cb7be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java
@@ -18,6 +18,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.metrics.WebApkUma;
 import org.chromium.chrome.browser.notifications.NotificationBuilderBase;
 import org.chromium.chrome.browser.notifications.NotificationMetadata;
@@ -142,23 +143,22 @@
 
     /** Finishes and removes the WebAPK's task. */
     @TargetApi(Build.VERSION_CODES.M)
-    public void finishAndRemoveTaskSdk23(final WebApkActivity webApkActivity) {
+    public void finishAndRemoveTaskSdk23(final ChromeActivity activity, WebApkExtras webApkExtras) {
         final ApiUseCallback connectionCallback = new ApiUseCallback() {
             @Override
             public void useApi(IWebApkApi api) throws RemoteException {
-                if (webApkActivity.isActivityFinishingOrDestroyed()) return;
+                if (activity.isActivityFinishingOrDestroyed()) return;
 
                 if (!api.finishAndRemoveTaskSdk23()) {
-                    // If |webApkActivity| is not the root of the task, hopefully the activities
-                    // below this one will close themselves.
-                    webApkActivity.finish();
+                    // If |activity| is not the root of the task, hopefully the activities below
+                    // this one will close themselves.
+                    activity.finish();
                 }
             }
         };
 
-        String webApkPackage = webApkActivity.getWebApkPackageName();
-        mConnectionManager.connect(
-                ContextUtils.getApplicationContext(), webApkPackage, connectionCallback);
+        mConnectionManager.connect(ContextUtils.getApplicationContext(),
+                webApkExtras.webApkPackageName, connectionCallback);
     }
 
     /** Returns whether there are any WebAPK service API calls in progress. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 4511acd..859342f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -561,16 +561,7 @@
                         if (getActivityTab().canGoBack()) {
                             getActivityTab().goBack();
                         } else {
-                            if (mWebappInfo.isSplashProvidedByWebApk()) {
-                                // We need to call into WebAPK to finish activity stack because:
-                                // 1) WebApkActivity is not the root of the task.
-                                // 2) The activity stack no longer has focus and thus cannot rely on
-                                //    the client's Activity#onResume() behaviour.
-                                WebApkServiceClient.getInstance().finishAndRemoveTaskSdk23(
-                                        (WebApkActivity) WebappActivity.this);
-                            } else {
-                                ApiCompatibilityUtils.finishAndRemoveTask(WebappActivity.this);
-                            }
+                            handleFinishAndClose();
                         }
                     }
                 }, MS_BEFORE_NAVIGATING_BACK_FROM_INTERSTITIAL);
@@ -699,14 +690,6 @@
                 getWindowAndroid(), (byte) mWebappInfo.orientation());
     }
 
-    /**
-     * Handles finishing activity on behalf of {@link CustomTabNavigationController}.
-     * Overridden by {@link WebApkActivity}.
-     */
-    protected void handleFinishAndClose() {
-        finish();
-    }
-
     protected boolean isSplashShowing() {
         return mSplashController.isSplashShowing();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
index aa97690..f2d2cf3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
@@ -13,7 +13,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 /**
  * Stores info about a web app.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
index e0bb801..30af80fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
@@ -18,7 +18,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 /**
  * Default implementation of SelectableItemViewBase.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
index c4c3a62..d84b410 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -28,10 +28,10 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
-import org.chromium.chrome.browser.ui.widget.FadingShadow;
-import org.chromium.chrome.browser.ui.widget.FadingShadowView;
-import org.chromium.chrome.browser.ui.widget.LoadingView;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
+import org.chromium.components.browser_ui.widget.FadingShadow;
+import org.chromium.components.browser_ui.widget.FadingShadowView;
+import org.chromium.components.browser_ui.widget.LoadingView;
 import org.chromium.components.browser_ui.widget.displaystyle.DisplayStyleObserver;
 import org.chromium.components.browser_ui.widget.displaystyle.HorizontalDisplayStyle;
 import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
index aa2c637..48d178f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
@@ -40,12 +40,12 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.toolbar.top.ActionModeController;
 import org.chromium.chrome.browser.toolbar.top.ToolbarActionModeCallback;
-import org.chromium.chrome.browser.ui.widget.TintedDrawable;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.vr.VrModeObserver;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.chrome.browser.widget.NumberRollView;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.displaystyle.DisplayStyleObserver;
 import org.chromium.components.browser_ui.widget.displaystyle.HorizontalDisplayStyle;
 import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityScrollingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityScrollingTest.java
index 1bf21e74..aba1da0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityScrollingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityScrollingTest.java
@@ -27,13 +27,13 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.history.HistoryTestUtils.TestObserver;
-import org.chromium.chrome.browser.ui.widget.MoreProgressButton;
-import org.chromium.chrome.browser.ui.widget.MoreProgressButton.State;
 import org.chromium.chrome.browser.widget.DateDividedAdapter.FooterItem;
 import org.chromium.chrome.browser.widget.DateDividedAdapter.TimedItem;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils;
 import org.chromium.chrome.test.util.browser.signin.SigninTestUtil;
+import org.chromium.components.browser_ui.widget.MoreProgressButton;
+import org.chromium.components.browser_ui.widget.MoreProgressButton.State;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.UiRestriction;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
index 8f8635ef..72ecdfdb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
@@ -21,8 +21,8 @@
 
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
-import org.chromium.chrome.browser.ui.widget.RadioButtonLayout;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 import org.chromium.components.search_engines.TemplateUrl;
 
 import java.util.ArrayList;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageAutoFetchTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageAutoFetchTest.java
index fcbcb7b..18a0693 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageAutoFetchTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageAutoFetchTest.java
@@ -185,7 +185,6 @@
     @Test
     @MediumTest
     @Feature({"OfflineAutoFetch"})
-    @DisabledTest(message = "https://crbug.com/1041822")
     public void testAutoFetchTriggersOnDNSErrorWhenOffline() {
         attemptLoadPage("http://does.not.resolve.com");
         waitForRequestCount(1);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/themes/ThemeSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/themes/ThemeSettingsFragmentTest.java
index 9bbaa4a..7fd2470 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/themes/ThemeSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/themes/ThemeSettingsFragmentTest.java
@@ -29,10 +29,10 @@
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsActivityTest;
-import org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription;
-import org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
+import org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java
index f8f3519..1e6fcbc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java
@@ -19,6 +19,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -27,6 +28,7 @@
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.UiRestriction;
 
 /**
  * Integration tests for status indicator covering related code in
@@ -36,6 +38,8 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Features.EnableFeatures({ChromeFeatureList.OFFLINE_INDICATOR_V2})
+// TODO(crbug.com/1035584): Enable for tablets once we support them.
+@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
 public class StatusIndicatorTest {
     // clang-format on
     @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java
index 6331ec23..5dba1b06 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java
@@ -21,8 +21,8 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.ViewResourceFrameLayout;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
index 6aa35e6..9d32f51 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
@@ -33,7 +33,6 @@
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelper;
 import org.chromium.chrome.browser.flags.ActivityType;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.snackbar.undo.UndoBarController;
 import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarTest.java
index dd30558..b7ef843 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarTest.java
@@ -26,8 +26,8 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.ClipDrawableProgressBar.ProgressBarObserver;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar.ProgressBarObserver;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
 import org.chromium.ui.test.util.UiRestriction;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/snackbar/undo/UndoBarControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
similarity index 95%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/snackbar/undo/UndoBarControllerTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
index 2dbde015..57b6501 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/snackbar/undo/UndoBarControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
@@ -2,7 +2,7 @@
 // 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.snackbar.undo;
+package org.chromium.chrome.browser.undo_tab_close_snackbar;
 
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -137,13 +137,15 @@
 
     private void clickSnackbar() {
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> mSnackbarManager.onClick(mActivityTestRule.getActivity().findViewById(
+                ()
+                        -> mSnackbarManager.onClick(mActivityTestRule.getActivity().findViewById(
                                 R.id.snackbar_button)));
     }
 
     private void dismissSnackbars() {
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> mSnackbarManager.dismissSnackbars(
+                ()
+                        -> mSnackbarManager.dismissSnackbars(
                                 mSnackbarManager.getCurrentSnackbarForTesting().getController()));
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
index 39b0c18b..0858a62 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
@@ -16,9 +16,9 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.ui.widget.MoreProgressButton;
-import org.chromium.chrome.browser.ui.widget.MoreProgressButton.State;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
+import org.chromium.components.browser_ui.widget.MoreProgressButton;
+import org.chromium.components.browser_ui.widget.MoreProgressButton.State;
 
 import java.util.Date;
 import java.util.concurrent.TimeUnit;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
index d35a3b59..f8701328 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
@@ -17,8 +17,8 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.MoreProgressButton;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
+import org.chromium.components.browser_ui.widget.MoreProgressButton;
 
 import java.util.Date;
 import java.util.concurrent.TimeUnit;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
index 14a5aa3..63408f5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
@@ -25,7 +25,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ui.widget.RoundedCornerImageView;
+import org.chromium.components.browser_ui.widget.RoundedCornerImageView;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index c657e24..4d2ac71c 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1526,6 +1526,9 @@
     Manage Cloud Print devices
   </message>
   <if expr="chromeos">
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER" desc="Label for button that allows the user to access the add print server dialog.">
+      Print server
+    </message>
     <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTERS" desc="In Printing Settings, the title of the CUPS printers setting section.">
       Printers
     </message>
@@ -1592,6 +1595,9 @@
     <message name="IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTERS_MANUALLY_TITLE" desc="Text for the title of the dialog that is used to manually add a printer instead of automatically finding ones nearby.">
       Add a printer manually
     </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE" desc="Text for the title of the dialog that is used to manually add a print server.">
+      Add a print server
+    </message>
     <message name="IDS_SETTINGS_PRINTING_CUPS_SELECT_MANUFACTURER_AND_MODEL_TITLE" desc="Text for the title of the dialog that is used to select a manufacturer and model from the drop down list.">
       Advanced printer configuration
     </message>
@@ -1607,6 +1613,9 @@
     <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_ADDRESS" desc="Label for the CUPS printer address in the Advanced section in the printer details page.">
       Address
     </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS" desc="Label for the CUPS print server address in the in the print server details page.">
+      Address
+    </message>
     <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_PROTOCOL" desc="Label for the CUPS printer protocol in the Advanced section in the printer details page.">
       Protocol
     </message>
@@ -1748,7 +1757,24 @@
     <message name="IDS_SETTINGS_PRINTING_CUPS_DIALOG_GENERAL_ERROR" desc="General message shown on the top of the add/edit printer dialogs when adding/editting a printer fails.">
       Unable to set up printer. Please check configuration and try again.
     </message>
-
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS" desc="Message shown on a toast to indicate no printers were found from a print server.">
+      Did not find any printers from the print server.
+    </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER" desc="Message shown on a toast to indicate that only one printer was found from a print server.">
+      Found 1 printer from the print server.
+    </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS" desc="Message shown on a toast to indicate the number of printers found on a print server.">
+      Found <ph name="NUM_PRINTERS">$1<ex>5</ex></ph> printers from the print server.
+    </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS" desc="Message shown on the address field of the print server dialog when attempting to connect to a print server with an invalid URL.">
+      Invalid address. Please check the address and try again.
+    </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR" desc="Message shown on the print server dialog when the user has provided a valid URL but the server is unreachable.">
+      Couldn't detect the print server. Please check the address and try again.
+    </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD" desc="Message shown on the print server dialog when we are able to reach the print server but still ran into some error with adding the print server.">
+      Couldn't add the print server. Please check the server's configuration and try again.
+    </message>
 
   </if>
   <if expr="not chromeos">
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE.png.sha1
new file mode 100644
index 0000000..5615661
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE.png.sha1
@@ -0,0 +1 @@
+7847e1793a6a9b708a9ef22c668c766352db41ac
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER.png.sha1
new file mode 100644
index 0000000..30d8d1f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER.png.sha1
@@ -0,0 +1 @@
+7fa7d140537ac31ec90c53b8f9fb99b9fba8fa0e
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS.png.sha1
new file mode 100644
index 0000000..5615661
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS.png.sha1
@@ -0,0 +1 @@
+7847e1793a6a9b708a9ef22c668c766352db41ac
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR.png.sha1
new file mode 100644
index 0000000..639a99e
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR.png.sha1
@@ -0,0 +1 @@
+3729b512a1a19b17bace1208ca7ee76e0bbc9b95
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS.png.sha1
new file mode 100644
index 0000000..b12282af
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS.png.sha1
@@ -0,0 +1 @@
+48fe45369f7a25a24b720d22fd4ddccfd0dde2d4
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER.png.sha1
new file mode 100644
index 0000000..82616ae
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER.png.sha1
@@ -0,0 +1 @@
+e2f8d804e0495c0596e33c66cf6def0105580796
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS.png.sha1
new file mode 100644
index 0000000..cfb77cc4
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS.png.sha1
@@ -0,0 +1 @@
+2a34cd31e804697c3f069adff4e4b963b2b9a868
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS.png.sha1
new file mode 100644
index 0000000..0ff6976b
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS.png.sha1
@@ -0,0 +1 @@
+8426afdf54f9740b15a7195138074074a730591a
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD.png.sha1
new file mode 100644
index 0000000..1cf3942
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD.png.sha1
@@ -0,0 +1 @@
+31774716c13e3463df1340c7c3b0e6996be80e50
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 08acdcf..5706b337 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2459,6 +2459,9 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(feed::kInterestFeedContentSuggestions,
                                     kInterestFeedFeatureVariations,
                                     "InterestFeedContentSuggestions")},
+    {"interest-feed-feedback", flag_descriptions::kInterestFeedFeedbackName,
+     flag_descriptions::kInterestFeedFeedbackDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(feed::kInterestFeedFeedback)},
     {"interest-feed-notifications",
      flag_descriptions::kInterestFeedNotificationsName,
      flag_descriptions::kInterestFeedNotificationsDescription, kOsAndroid,
@@ -2580,6 +2583,13 @@
      flag_descriptions::kKernelnextVMsName,
      flag_descriptions::kKernelnextVMsDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kKernelnextVMs)},
+    {"enable-experimental-accessibility-chromevox-search-menus",
+     flag_descriptions::kExperimentalAccessibilityChromeVoxSearchMenusName,
+     flag_descriptions::
+         kExperimentalAccessibilityChromeVoxSearchMenusDescription,
+     kOsCrOS,
+     SINGLE_VALUE_TYPE(
+         ::switches::kEnableExperimentalAccessibilityChromeVoxSearchMenus)},
 #endif  // OS_CHROMEOS
 #if !defined(OS_ANDROID) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
     {"enable-google-branded-context-menu",
@@ -4319,8 +4329,7 @@
 
     {"privacy-settings-redesign",
      flag_descriptions::kPrivacySettingsRedesignName,
-     flag_descriptions::kPrivacySettingsRedesignDescription,
-     kOsWin | kOsMac | kOsLinux,
+     flag_descriptions::kPrivacySettingsRedesignDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kPrivacySettingsRedesign)},
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 44ffb34f..f77c935 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -92,6 +92,7 @@
     &feature_engagement::kIPHChromeDuetSearchFeature,
     &feature_engagement::kIPHChromeDuetTabSwitcherFeature,
     &feed::kInterestFeedContentSuggestions,
+    &feed::kInterestFeedFeedback,
     &kAdjustWebApkInstallationSpace,
     &kAllowNewIncognitoTabIntents,
     &kAllowRemoteContextForNotifications,
diff --git a/chrome/browser/android/preferences/prefs.h b/chrome/browser/android/preferences/prefs.h
index 899fe61..0a3a9fe8 100644
--- a/chrome/browser/android/preferences/prefs.h
+++ b/chrome/browser/android/preferences/prefs.h
@@ -57,6 +57,7 @@
   CLICKED_UPDATE_MENU_ITEM,
   LATEST_VERSION_WHEN_CLICKED_UPDATE_MENU_ITEM,
   BLOCK_THIRD_PARTY_COOKIES,
+  COOKIE_CONTROLS_MODE,
   ENABLE_DO_NOT_TRACK,
   PRINTING_ENABLED,
   OFFER_TRANSLATE_ENABLED,
@@ -109,6 +110,7 @@
     prefs::kClickedUpdateMenuItem,
     prefs::kLatestVersionWhenClickedUpdateMenuItem,
     prefs::kBlockThirdPartyCookies,
+    prefs::kCookieControlsMode,
     prefs::kEnableDoNotTrack,
     prefs::kPrintingEnabled,
     prefs::kOfferTranslateEnabled,
diff --git a/chrome/browser/android/preferences/prefs_unittest.cc b/chrome/browser/android/preferences/prefs_unittest.cc
index 979c83d7..a7b4620 100644
--- a/chrome/browser/android/preferences/prefs_unittest.cc
+++ b/chrome/browser/android/preferences/prefs_unittest.cc
@@ -91,6 +91,7 @@
             GetPrefName(LATEST_VERSION_WHEN_CLICKED_UPDATE_MENU_ITEM));
   EXPECT_EQ(prefs::kBlockThirdPartyCookies,
             GetPrefName(BLOCK_THIRD_PARTY_COOKIES));
+  EXPECT_EQ(prefs::kCookieControlsMode, GetPrefName(COOKIE_CONTROLS_MODE));
   EXPECT_EQ(prefs::kEnableDoNotTrack, GetPrefName(ENABLE_DO_NOT_TRACK));
   EXPECT_EQ(prefs::kPrintingEnabled, GetPrefName(PRINTING_ENABLED));
   EXPECT_EQ(prefs::kOfferTranslateEnabled,
diff --git a/chrome/browser/android/webapk/webapk_icon_hasher.cc b/chrome/browser/android/webapk/webapk_icon_hasher.cc
index c9c09cf..e0b4096b 100644
--- a/chrome/browser/android/webapk/webapk_icon_hasher.cc
+++ b/chrome/browser/android/webapk/webapk_icon_hasher.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/data_url.h"
+#include "net/base/network_isolation_key.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -40,10 +41,10 @@
     network::mojom::URLLoaderFactory* url_loader_factory,
     const url::Origin& request_initiator,
     const GURL& icon_url,
-    const Murmur2HashCallback& callback) {
+    Murmur2HashCallback callback) {
   DownloadAndComputeMurmur2HashWithTimeout(
       url_loader_factory, request_initiator, icon_url,
-      kDownloadTimeoutInMilliseconds, callback);
+      kDownloadTimeoutInMilliseconds, std::move(callback));
 }
 
 // static
@@ -52,10 +53,10 @@
     const url::Origin& request_initiator,
     const GURL& icon_url,
     int timeout_ms,
-    const Murmur2HashCallback& callback) {
+    Murmur2HashCallback callback) {
   if (!icon_url.is_valid()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                  base::BindOnce(callback, ""));
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), ""));
     return;
   }
 
@@ -67,13 +68,13 @@
       hash = ComputeMurmur2Hash(data);
     }
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(callback, hash));
+        FROM_HERE, base::BindOnce(std::move(callback), hash));
     return;
   }
 
   // The icon hasher will delete itself when it is done.
   new WebApkIconHasher(url_loader_factory, request_initiator, icon_url,
-                       timeout_ms, callback);
+                       timeout_ms, std::move(callback));
 }
 
 WebApkIconHasher::WebApkIconHasher(
@@ -81,8 +82,8 @@
     const url::Origin& request_initiator,
     const GURL& icon_url,
     int timeout_ms,
-    const Murmur2HashCallback& callback)
-    : callback_(callback) {
+    Murmur2HashCallback callback)
+    : callback_(std::move(callback)) {
   DCHECK(url_loader_factory);
 
   download_timeout_timer_.Start(
@@ -91,6 +92,9 @@
                  base::Unretained(this)));
 
   auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->trusted_params = network::ResourceRequest::TrustedParams();
+  resource_request->trusted_params->network_isolation_key =
+      net::NetworkIsolationKey(request_initiator, request_initiator);
   resource_request->request_initiator = request_initiator;
   resource_request->url = icon_url;
   simple_url_loader_ = network::SimpleURLLoader::Create(
@@ -128,6 +132,6 @@
 }
 
 void WebApkIconHasher::RunCallback(const std::string& icon_murmur2_hash) {
-  callback_.Run(icon_murmur2_hash);
+  std::move(callback_).Run(icon_murmur2_hash);
   delete this;
 }
diff --git a/chrome/browser/android/webapk/webapk_icon_hasher.h b/chrome/browser/android/webapk/webapk_icon_hasher.h
index ffe3213..c135a1c 100644
--- a/chrome/browser/android/webapk/webapk_icon_hasher.h
+++ b/chrome/browser/android/webapk/webapk_icon_hasher.h
@@ -25,7 +25,7 @@
 class WebApkIconHasher {
  public:
   using Murmur2HashCallback =
-      base::Callback<void(const std::string& /* icon_murmur2_hash */)>;
+      base::OnceCallback<void(const std::string& /* icon_murmur2_hash */)>;
 
   // Creates a self-owned WebApkIconHasher instance. The instance downloads
   // |icon_url| and calls |callback| with the Murmur2 hash of the downloaded
@@ -36,21 +36,21 @@
       network::mojom::URLLoaderFactory* url_loader_factory,
       const url::Origin& request_initiator,
       const GURL& icon_url,
-      const Murmur2HashCallback& callback);
+      Murmur2HashCallback callback);
 
   static void DownloadAndComputeMurmur2HashWithTimeout(
       network::mojom::URLLoaderFactory* url_loader_factory,
       const url::Origin& request_initiator,
       const GURL& icon_url,
       int timeout_ms,
-      const Murmur2HashCallback& callback);
+      Murmur2HashCallback callback);
 
  private:
   WebApkIconHasher(network::mojom::URLLoaderFactory* url_loader_factory,
                    const url::Origin& request_initiator,
                    const GURL& icon_url,
                    int timeout_ms,
-                   const Murmur2HashCallback& callback);
+                   Murmur2HashCallback callback);
   ~WebApkIconHasher();
 
   void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
diff --git a/chrome/browser/android/webapk/webapk_icon_hasher_browsertest.cc b/chrome/browser/android/webapk/webapk_icon_hasher_browsertest.cc
new file mode 100644
index 0000000..8e06ad1
--- /dev/null
+++ b/chrome/browser/android/webapk/webapk_icon_hasher_browsertest.cc
@@ -0,0 +1,119 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/webapk/webapk_icon_hasher.h"
+
+#include <set>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/manifest_icon_downloader.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/base/features.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/public/cpp/shared_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+#include "url/origin.h"
+
+// Browser tests for WebApkIconHasher.
+class WebApkIconHasherBrowserTest : public PlatformBrowserTest {
+ public:
+  WebApkIconHasherBrowserTest()
+      : http_server_(net::EmbeddedTestServer::TYPE_HTTP) {
+    scoped_feature_list_.InitWithFeatures(
+        {net::features::kSplitCacheByNetworkIsolationKey,
+         net::features::kAppendFrameOriginToNetworkIsolationKey},
+        {});
+  }
+  ~WebApkIconHasherBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    http_server_.RegisterRequestHandler(base::BindRepeating(
+        &WebApkIconHasherBrowserTest::OnResourceLoad, base::Unretained(this)));
+    http_server_.ServeFilesFromSourceDirectory("chrome/test/data/banners");
+    ASSERT_TRUE(http_server_.Start());
+
+    ASSERT_TRUE(content::NavigateToURL(
+        GetActiveWebContents(),
+        http_server_.GetURL("/no_manifest_test_page.html")));
+
+    PlatformBrowserTest::SetUpOnMainThread();
+  }
+
+  content::WebContents* GetActiveWebContents() {
+    return chrome_test_utils::GetActiveWebContents(this);
+  }
+
+  std::unique_ptr<net::test_server::HttpResponse> OnResourceLoad(
+      const net::test_server::HttpRequest& request) {
+    network_requests_.insert(request.GetURL());
+    return nullptr;
+  }
+
+  net::EmbeddedTestServer http_server_;
+  std::set<GURL> network_requests_;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebApkIconHasherBrowserTest);
+};
+
+namespace {
+
+void OnDownloadedManifestIcon(base::OnceClosure callback,
+                              const SkBitmap& unused_icon) {
+  std::move(callback).Run();
+}
+
+void OnGotMurmur2Hash(base::OnceClosure callback,
+                      const std::string& unused_icon_murmur2_hash) {
+  std::move(callback).Run();
+}
+
+}  // anonymous namespace
+
+// Checks that WebApkIconHasher fetches the icon cached in the HTTP cache by
+// ManifestIconDownloader.
+IN_PROC_BROWSER_TEST_F(WebApkIconHasherBrowserTest, HasherUsesIconFromCache) {
+  const GURL kIconUrl = http_server_.GetURL("/launcher-icon-max-age.png");
+
+  content::WebContents* web_contents = GetActiveWebContents();
+
+  {
+    base::RunLoop run_loop;
+    content::ManifestIconDownloader::Download(
+        web_contents, kIconUrl, 0, 0,
+        base::BindOnce(&OnDownloadedManifestIcon, run_loop.QuitClosure()),
+        false /* square_only */);
+    run_loop.Run();
+  }
+
+  ASSERT_TRUE(network_requests_.count(kIconUrl));
+  network_requests_.clear();
+
+  {
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
+        content::BrowserContext::GetDefaultStoragePartition(
+            web_contents->GetBrowserContext())
+            ->GetURLLoaderFactoryForBrowserProcess();
+
+    base::RunLoop run_loop;
+    WebApkIconHasher::DownloadAndComputeMurmur2Hash(
+        url_loader_factory.get(), url::Origin::Create(kIconUrl), kIconUrl,
+        base::BindOnce(&OnGotMurmur2Hash, run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  EXPECT_FALSE(network_requests_.count(kIconUrl));
+}
diff --git a/chrome/browser/android/webapk/webapk_icon_hasher_unittest.cc b/chrome/browser/android/webapk/webapk_icon_hasher_unittest.cc
index 2fa12c0..1f5a965e 100644
--- a/chrome/browser/android/webapk/webapk_icon_hasher_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_icon_hasher_unittest.cc
@@ -40,8 +40,8 @@
            const GURL& icon_url) {
     WebApkIconHasher::DownloadAndComputeMurmur2HashWithTimeout(
         url_loader_factory, url::Origin::Create(icon_url), icon_url, 300,
-        base::Bind(&WebApkIconHasherRunner::OnCompleted,
-                   base::Unretained(this)));
+        base::BindOnce(&WebApkIconHasherRunner::OnCompleted,
+                       base::Unretained(this)));
 
     base::RunLoop run_loop;
     on_completed_callback_ = run_loop.QuitClosure();
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index 8cadd16d..980ce2b 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -635,8 +635,8 @@
       GetURLLoaderFactory(browser_context_),
       url::Origin::Create(install_shortcut_info_->url),
       install_shortcut_info_->best_primary_icon_url,
-      base::Bind(&WebApkInstaller::OnGotPrimaryIconMurmur2Hash,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&WebApkInstaller::OnGotPrimaryIconMurmur2Hash,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WebApkInstaller::OnGotPrimaryIconMurmur2Hash(
@@ -654,8 +654,9 @@
         GetURLLoaderFactory(browser_context_),
         url::Origin::Create(install_shortcut_info_->url),
         install_shortcut_info_->best_badge_icon_url,
-        base::Bind(&WebApkInstaller::OnGotBadgeIconMurmur2Hash,
-                   weak_ptr_factory_.GetWeakPtr(), true, primary_icon_hash));
+        base::BindOnce(&WebApkInstaller::OnGotBadgeIconMurmur2Hash,
+                       weak_ptr_factory_.GetWeakPtr(), true,
+                       primary_icon_hash));
   } else {
     OnGotBadgeIconMurmur2Hash(false, primary_icon_hash, "");
   }
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
index 8d069c38..90af7148 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
@@ -590,9 +590,13 @@
   remote_device_cache_->SetRemoteDevices(remote_devices);
 
   base::Optional<multidevice::RemoteDeviceRef> unlock_key_device =
-      remote_device_cache_->GetRemoteDevice(unlock_key_id);
+      remote_device_cache_->GetRemoteDevice(
+          base::nullopt /* instance_id */,
+          unlock_key_id /* legacy_device_id */);
   base::Optional<multidevice::RemoteDeviceRef> local_device =
-      remote_device_cache_->GetRemoteDevice(local_device_id);
+      remote_device_cache_->GetRemoteDevice(
+          base::nullopt /* instance_id */,
+          local_device_id /* legacy_device_id */);
 
   // TODO(hansberry): It is possible that there may not be an unlock key by this
   // point. If this occurs, it is due to a bug in how device metadata is
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
index 55b7186..e271967b 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -1067,13 +1067,7 @@
 }
 
 // TODO(https://crbug.com/990844): Re-enable once flakiness is fixed.
-// TODO(crbug.com/998330): The test is flaky (timeout) on Chromium OS MSAN.
-#if defined(MEMORY_SANITIZER)
-#define MAYBE_XHRNotThrottled DISABLED_XHRNotThrottled
-#else
-#define MAYBE_XHRNotThrottled XHRNotThrottled
-#endif
-IN_PROC_BROWSER_TEST_P(MergeSessionTest, MAYBE_XHRNotThrottled) {
+IN_PROC_BROWSER_TEST_P(MergeSessionTest, DISABLED_XHRNotThrottled) {
   StartNewUserSession(/*wait_for_merge=*/false,
                       /*is_under_advanced_protection=*/false);
 
diff --git a/chrome/browser/chromeos/policy/status_collector/child_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/child_status_collector.cc
index 0f9b2f37..ea799f3a 100644
--- a/chrome/browser/chromeos/policy/status_collector/child_status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/child_status_collector.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/chromeos/policy/status_collector/child_activity_storage.h"
-#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h"
 #include "chrome/browser/chromeos/policy/status_collector/status_collector_state.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
@@ -102,8 +101,8 @@
       const StatusCollectorCallback& response)
       : StatusCollectorState(task_runner, response) {}
 
-  bool FetchAndroidStatus(const DeviceStatusCollector::AndroidStatusFetcher&
-                              android_status_fetcher) {
+  bool FetchAndroidStatus(
+      const StatusCollector::AndroidStatusFetcher& android_status_fetcher) {
     return android_status_fetcher.Run(base::BindRepeating(
         &ChildStatusCollectorState::OnAndroidInfoReceived, this));
   }
@@ -113,13 +112,6 @@
 
   void OnAndroidInfoReceived(const std::string& status,
                              const std::string& droid_guard_info) {
-    // TODO(crbug.com/827386): remove after migration.
-    em::AndroidStatus* const session_android_status =
-        response_params_.session_status->mutable_android_status();
-    session_android_status->set_status_payload(status);
-    session_android_status->set_droid_guard_info(droid_guard_info);
-    // END.
-
     em::AndroidStatus* const child_android_status =
         response_params_.child_status->mutable_android_status();
     child_android_status->set_status_payload(status);
@@ -201,9 +193,6 @@
     return;
   }
 
-  // Activity times.
-  report_activity_times_ = true;
-
   // Settings related.
   report_version_info_ = true;
   cros_settings_->GetBoolean(chromeos::kReportDeviceVersionInfo,
@@ -222,10 +211,6 @@
 }
 
 void ChildStatusCollector::UpdateChildUsageTime() {
-  if (!report_activity_times_) {
-    return;
-  }
-
   Time now = GetCurrentTime();
   Time reset_time = activity_storage_->GetBeginningOfDay(now);
   if (reset_time > now)
@@ -258,38 +243,6 @@
 }
 
 bool ChildStatusCollector::GetActivityTimes(
-    em::DeviceStatusReportRequest* status) {
-  UpdateChildUsageTime();
-
-  // Signed-in user is reported in child reporting.
-  std::vector<ActivityStorage::ActivityPeriod> activity_times =
-      activity_storage_->GetStoredActivityPeriods();
-
-  bool anything_reported = false;
-  for (const auto& activity_period : activity_times) {
-    // This is correct even when there are leap seconds, because when a leap
-    // second occurs, two consecutive seconds have the same timestamp.
-    int64_t end_timestamp =
-        activity_period.start_timestamp + Time::kMillisecondsPerDay;
-
-    em::ActiveTimePeriod* active_period = status->add_active_periods();
-    em::TimePeriod* period = active_period->mutable_time_period();
-    period->set_start_timestamp(activity_period.start_timestamp);
-    period->set_end_timestamp(end_timestamp);
-    active_period->set_active_duration(activity_period.activity_milliseconds);
-    // Report user email only if users reporting is turned on.
-    if (!activity_period.user_email.empty())
-      active_period->set_user_email(activity_period.user_email);
-    if (activity_period.start_timestamp >= last_reported_day_) {
-      last_reported_day_ = activity_period.start_timestamp;
-      duration_for_last_reported_day_ = activity_period.activity_milliseconds;
-    }
-    anything_reported = true;
-  }
-  return anything_reported;
-}
-
-bool ChildStatusCollector::GetActivityTimes(
     em::ChildStatusReportRequest* status) {
   UpdateChildUsageTime();
 
@@ -320,12 +273,6 @@
 }
 
 bool ChildStatusCollector::GetVersionInfo(
-    em::DeviceStatusReportRequest* status) {
-  status->set_os_version(os_version_);
-  return true;
-}
-
-bool ChildStatusCollector::GetVersionInfo(
     em::ChildStatusReportRequest* status) {
   status->set_os_version(os_version_);
   return true;
@@ -342,9 +289,7 @@
   scoped_refptr<ChildStatusCollectorState> state(
       new ChildStatusCollectorState(task_runner_, response));
 
-  // Gather status data. The following calls might queue some async queries.
-  GetDeviceStatus(state);
-  GetSessionStatus(state);
+  // Gather status data might queue some async queries.
   FillChildStatusReportRequest(state);
 
   // If there are no outstanding async queries, the destructor of |state| calls
@@ -353,62 +298,6 @@
   // finished.
 }
 
-void ChildStatusCollector::GetDeviceStatus(
-    scoped_refptr<ChildStatusCollectorState> state) {
-  em::DeviceStatusReportRequest* status =
-      state->response_params().device_status.get();
-  bool anything_reported = false;
-
-  if (report_version_info_)
-    anything_reported |= GetVersionInfo(status);
-
-  if (report_activity_times_)
-    anything_reported |= GetActivityTimes(status);
-
-  if (report_boot_mode_) {
-    base::Optional<std::string> boot_mode =
-        StatusCollector::GetBootMode(statistics_provider_);
-    if (boot_mode) {
-      status->set_boot_mode(*boot_mode);
-      anything_reported = true;
-    }
-  }
-
-  // Wipe pointer if we didn't actually add any data.
-  if (!anything_reported)
-    state->response_params().device_status.reset();
-}
-
-bool ChildStatusCollector::GetSessionStatusForUser(
-    scoped_refptr<ChildStatusCollectorState> state,
-    em::SessionStatusReportRequest* status,
-    const user_manager::User* user) {
-  // Child accounts are not local accounts.
-  DCHECK(!user->IsDeviceLocalAccount());
-
-  Profile* const profile =
-      chromeos::ProfileHelper::Get()->GetProfileByUser(user);
-  if (!profile)
-    return false;
-
-  // Time zone.
-  const std::string current_timezone =
-      base::UTF16ToUTF8(chromeos::system::TimezoneSettings::GetInstance()
-                            ->GetCurrentTimezoneID());
-  status->set_time_zone(current_timezone);
-
-  // Android status.
-  const bool report_android_status =
-      profile->GetPrefs()->GetBoolean(prefs::kReportArcStatusEnabled);
-  if (report_android_status)
-    GetAndroidStatus(state);
-
-  status->set_user_dm_token(GetDMTokenForProfile(profile));
-
-  // At least time zone is always reported.
-  return true;
-}
-
 bool ChildStatusCollector::FillUserSpecificFields(
     scoped_refptr<ChildStatusCollectorState> state,
     em::ChildStatusReportRequest* status,
@@ -437,23 +326,6 @@
   return true;
 }
 
-void ChildStatusCollector::GetSessionStatus(
-    scoped_refptr<ChildStatusCollectorState> state) {
-  em::SessionStatusReportRequest* status =
-      state->response_params().session_status.get();
-  bool anything_reported = false;
-
-  user_manager::UserManager* user_manager = user_manager::UserManager::Get();
-  const user_manager::User* const primary_user = user_manager->GetPrimaryUser();
-  DCHECK(primary_user != nullptr);
-
-  anything_reported |= GetSessionStatusForUser(state, status, primary_user);
-
-  // Wipe pointer if we didn't actually add any data.
-  if (!anything_reported)
-    state->response_params().session_status.reset();
-}
-
 bool ChildStatusCollector::GetAndroidStatus(
     const scoped_refptr<ChildStatusCollectorState>& state) {
   return state->FetchAndroidStatus(android_status_fetcher_);
@@ -474,8 +346,7 @@
   if (report_version_info_)
     anything_reported |= GetVersionInfo(status);
 
-  if (report_activity_times_)
-    anything_reported |= GetActivityTimes(status);
+  anything_reported |= GetActivityTimes(status);
 
   if (report_boot_mode_) {
     base::Optional<std::string> boot_mode =
@@ -498,7 +369,7 @@
 }
 
 bool ChildStatusCollector::ShouldReportActivityTimes() const {
-  return report_activity_times_;
+  return true;
 }
 
 bool ChildStatusCollector::ShouldReportNetworkInterfaces() const {
diff --git a/chrome/browser/chromeos/policy/status_collector/child_status_collector.h b/chrome/browser/chromeos/policy/status_collector/child_status_collector.h
index b28d4ebd..acf60270 100644
--- a/chrome/browser/chromeos/policy/status_collector/child_status_collector.h
+++ b/chrome/browser/chromeos/policy/status_collector/child_status_collector.h
@@ -79,11 +79,6 @@
   bool ShouldReportUsers() const override;
   bool ShouldReportHardwareStatus() const override;
 
-  // How often, in seconds, to poll to see if the user is idle.
-  // Note: This in only used in tests and not referenced in .cc. It should
-  // probably be moved.
-  static const unsigned int kIdlePollIntervalSeconds = 30;
-
   // Returns the amount of time the child has used so far today. If there is no
   // user logged in, it returns 0.
   base::TimeDelta GetActiveChildScreenTime();
@@ -120,18 +115,6 @@
   // Queues async queries!
   bool GetAndroidStatus(const scoped_refptr<ChildStatusCollectorState>& state);
 
-  // TODO(crbug.com/827386): remove after migration.
-  void GetDeviceStatus(scoped_refptr<ChildStatusCollectorState> state);
-  void GetSessionStatus(scoped_refptr<ChildStatusCollectorState> state);
-  bool GetSessionStatusForUser(
-      scoped_refptr<ChildStatusCollectorState> state,
-      enterprise_management::SessionStatusReportRequest* status,
-      const user_manager::User* user);
-  bool GetActivityTimes(
-      enterprise_management::DeviceStatusReportRequest* status);
-  bool GetVersionInfo(enterprise_management::DeviceStatusReportRequest* status);
-  // END.
-
   // Update the cached values of the reporting settings.
   void UpdateReportingSettings();
 
diff --git a/chrome/browser/chromeos/policy/status_collector/child_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/child_status_collector_browsertest.cc
index d2525e9..e8f1bc79 100644
--- a/chrome/browser/chromeos/policy/status_collector/child_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/child_status_collector_browsertest.cc
@@ -73,6 +73,8 @@
 
 constexpr int64_t kMillisecondsPerDay = Time::kMicrosecondsPerDay / 1000;
 
+constexpr int kIdlePollIntervalSeconds = 30;
+
 const char kArcStatus[] = R"(
 {
    "applications":[
@@ -93,7 +95,7 @@
   TestingChildStatusCollector(
       PrefService* pref_service,
       chromeos::system::StatisticsProvider* provider,
-      const policy::ChildStatusCollector::AndroidStatusFetcher&
+      const policy::StatusCollector::AndroidStatusFetcher&
           android_status_fetcher,
       TimeDelta activity_day_start)
       : policy::ChildStatusCollector(pref_service,
@@ -122,9 +124,8 @@
   // Each time this is called, returns a time that is a fixed increment
   // later than the previous time.
   Time GetCurrentTime() override {
-    int poll_interval = policy::ChildStatusCollector::kIdlePollIntervalSeconds;
-    return baseline_time_ +
-           TimeDelta::FromSeconds(poll_interval * baseline_offset_periods_++);
+    return baseline_time_ + TimeDelta::FromSeconds(kIdlePollIntervalSeconds *
+                                                   baseline_offset_periods_++);
   }
 
  private:
@@ -135,16 +136,6 @@
   int baseline_offset_periods_;
 };
 
-// Return the total number of active milliseconds contained in a device
-// status report.
-int64_t GetActiveMilliseconds(const em::DeviceStatusReportRequest& status) {
-  int64_t active_milliseconds = 0;
-  for (int i = 0; i < status.active_periods_size(); i++) {
-    active_milliseconds += status.active_periods(i).active_duration();
-  }
-  return active_milliseconds;
-}
-
 // Overloads |GetActiveMilliseconds| for child status report.
 int64_t GetActiveMilliseconds(const em::ChildStatusReportRequest& status) {
   int64_t active_milliseconds = 0;
@@ -162,7 +153,7 @@
 }
 
 bool GetEmptyAndroidStatus(
-    const policy::ChildStatusCollector::AndroidStatusReceiver& receiver) {
+    const policy::StatusCollector::AndroidStatusReceiver& receiver) {
   // Post it to the thread because this call is expected to be asynchronous.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&CallAndroidStatusReceiver, receiver, "", ""));
@@ -172,7 +163,7 @@
 bool GetFakeAndroidStatus(
     const std::string& status,
     const std::string& droid_guard_info,
-    const policy::ChildStatusCollector::AndroidStatusReceiver& receiver) {
+    const policy::StatusCollector::AndroidStatusReceiver& receiver) {
   // Post it to the thread because this call is expected to be asynchronous.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&CallAndroidStatusReceiver, receiver, status,
@@ -285,8 +276,7 @@
           break;
         case DeviceStateTransitions::kLeaveSleep:
           chromeos::FakePowerManagerClient::Get()->SendSuspendDone(
-              base::TimeDelta::FromSeconds(
-                  policy::ChildStatusCollector::kIdlePollIntervalSeconds));
+              base::TimeDelta::FromSeconds(kIdlePollIntervalSeconds));
           break;
         case DeviceStateTransitions::kEnterSessionActive:
           session_manager::SessionManager::Get()->SetSessionState(
@@ -304,7 +294,7 @@
   }
 
   virtual void RestartStatusCollector(
-      const policy::ChildStatusCollector::AndroidStatusFetcher&
+      const policy::StatusCollector::AndroidStatusFetcher&
           android_status_fetcher,
       const TimeDelta activity_day_start = kMidnight) {
     status_collector_ = std::make_unique<TestingChildStatusCollector>(
@@ -313,8 +303,6 @@
   }
 
   void GetStatus() {
-    device_status_.Clear();
-    session_status_.Clear();
     run_loop_.reset(new base::RunLoop());
     status_collector_->GetStatusAsync(base::BindRepeating(
         &ChildStatusCollectorTest::OnStatusReceived, base::Unretained(this)));
@@ -323,10 +311,6 @@
   }
 
   void OnStatusReceived(StatusCollectorParams callback_params) {
-    if (callback_params.device_status)
-      device_status_ = *callback_params.device_status;
-    if (callback_params.session_status)
-      session_status_ = *callback_params.session_status;
     if (callback_params.child_status)
       child_status_ = *callback_params.child_status;
     EXPECT_TRUE(run_loop_);
@@ -369,9 +353,7 @@
   }
 
   // Convenience method.
-  int64_t ActivePeriodMilliseconds() {
-    return policy::ChildStatusCollector::kIdlePollIntervalSeconds * 1000;
-  }
+  int64_t ActivePeriodMilliseconds() { return kIdlePollIntervalSeconds * 1000; }
 
   void ExpectChildScreenTimeMilliseconds(int64_t duration) {
     profile_pref_service_.CommitPendingWrite(
@@ -411,8 +393,6 @@
   std::unique_ptr<TestingProfile> testing_profile_;
   chromeos::MockUserManager* const user_manager_;
   user_manager::ScopedUserManager user_manager_enabler_;
-  em::DeviceStatusReportRequest device_status_;
-  em::SessionStatusReportRequest session_status_;
   em::ChildStatusReportRequest child_status_;
   TestingPrefServiceSimple local_state_;
   TestingPrefServiceSimple profile_pref_service_;
@@ -436,27 +416,10 @@
 
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  EXPECT_TRUE(device_status_.has_boot_mode());
-  EXPECT_EQ("Verified", device_status_.boot_mode());
-  // END.
-
   EXPECT_TRUE(child_status_.has_boot_mode());
   EXPECT_EQ("Verified", child_status_.boot_mode());
 }
 
-// TODO(crbug.com/827386): remove after migration.
-TEST_F(ChildStatusCollectorTest, NotReportingWriteProtectSwitch) {
-  fake_statistics_provider_.SetMachineStatistic(
-      chromeos::system::kFirmwareWriteProtectBootKey,
-      chromeos::system::kFirmwareWriteProtectBootValueOn);
-
-  GetStatus();
-
-  EXPECT_FALSE(device_status_.has_write_protect_switch());
-}
-// END.
-
 TEST_F(ChildStatusCollectorTest, ReportingArcStatus) {
   RestartStatusCollector(
       base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo));
@@ -465,13 +428,6 @@
 
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  EXPECT_EQ(kArcStatus, session_status_.android_status().status_payload());
-  EXPECT_EQ(kDroidGuardInfo,
-            session_status_.android_status().droid_guard_info());
-  EXPECT_EQ(kFakeDmToken, session_status_.user_dm_token());
-  // END.
-
   EXPECT_EQ(kArcStatus, child_status_.android_status().status_payload());
   EXPECT_EQ(kDroidGuardInfo, child_status_.android_status().droid_guard_info());
   EXPECT_EQ(kFakeDmToken, child_status_.user_dm_token());
@@ -480,58 +436,9 @@
 TEST_F(ChildStatusCollectorTest, ReportingPartialVersionInfo) {
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  // Should only report OS version.
-  EXPECT_TRUE(device_status_.has_os_version());
-  EXPECT_FALSE(device_status_.has_browser_version());
-  EXPECT_FALSE(device_status_.has_channel());
-  EXPECT_FALSE(device_status_.has_firmware_version());
-  EXPECT_FALSE(device_status_.has_tpm_version_info());
-  // END.
-
   EXPECT_TRUE(child_status_.has_os_version());
 }
 
-// TODO(crbug.com/827386): remove after migration.
-TEST_F(ChildStatusCollectorTest, NotReportingVolumeInfo) {
-  RestartStatusCollector(base::BindRepeating(&GetEmptyAndroidStatus));
-  content::RunAllTasksUntilIdle();
-
-  GetStatus();
-
-  EXPECT_EQ(0, device_status_.volume_infos_size());
-}
-
-TEST_F(ChildStatusCollectorTest, NotReportingUsers) {
-  const AccountId account_id0(AccountId::FromUserEmail("user0@gmail.com"));
-  const AccountId account_id1(AccountId::FromUserEmail("user1@gmail.com"));
-  user_manager_->AddUserWithAffiliationAndType(account_id0, true,
-                                               user_manager::USER_TYPE_REGULAR);
-  user_manager_->AddUserWithAffiliationAndType(account_id1, true,
-                                               user_manager::USER_TYPE_CHILD);
-
-  GetStatus();
-
-  EXPECT_EQ(0, device_status_.users_size());
-}
-
-TEST_F(ChildStatusCollectorTest, NotReportingOSUpdateStatus) {
-  MockPlatformVersion("1234.0.0");
-
-  GetStatus();
-
-  EXPECT_FALSE(device_status_.has_os_update_status());
-}
-
-TEST_F(ChildStatusCollectorTest, NotReportingDeviceHardwareStatus) {
-  EXPECT_FALSE(device_status_.has_sound_volume());
-  EXPECT_EQ(0, device_status_.cpu_utilization_pct_samples().size());
-  EXPECT_EQ(0, device_status_.cpu_temp_infos_size());
-  EXPECT_EQ(0, device_status_.system_ram_free_samples().size());
-  EXPECT_FALSE(device_status_.has_system_ram_total());
-  EXPECT_FALSE(device_status_.has_tpm_status_info());
-}
-
 TEST_F(ChildStatusCollectorTest, TimeZoneReporting) {
   const std::string timezone =
       base::UTF16ToUTF8(chromeos::system::TimezoneSettings::GetInstance()
@@ -539,11 +446,6 @@
 
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  EXPECT_TRUE(session_status_.has_time_zone());
-  EXPECT_EQ(timezone, session_status_.time_zone());
-  // END.
-
   EXPECT_TRUE(child_status_.has_time_zone());
   EXPECT_EQ(timezone, child_status_.time_zone());
 }
@@ -563,14 +465,6 @@
 
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  ASSERT_EQ(1, device_status_.active_periods_size());
-  EXPECT_EQ(5 * ActivePeriodMilliseconds(),
-            GetActiveMilliseconds(device_status_));
-  EXPECT_EQ("",  // No email should be saved for child account.
-            device_status_.active_periods(0).user_email());
-  // END.
-
   ASSERT_EQ(1, child_status_.screen_time_span_size());
   EXPECT_EQ(5 * ActivePeriodMilliseconds(),
             GetActiveMilliseconds(child_status_));
@@ -594,14 +488,6 @@
 
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  ASSERT_EQ(1, device_status_.active_periods_size());
-  EXPECT_EQ(4 * ActivePeriodMilliseconds(),
-            GetActiveMilliseconds(device_status_));
-  EXPECT_EQ("",  // No email should be saved for child account.
-            device_status_.active_periods(0).user_email());
-  // END.
-
   ASSERT_EQ(1, child_status_.screen_time_span_size());
   EXPECT_EQ(4 * ActivePeriodMilliseconds(),
             GetActiveMilliseconds(child_status_));
@@ -624,14 +510,6 @@
 
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  ASSERT_EQ(1, device_status_.active_periods_size());
-  EXPECT_EQ(5 * ActivePeriodMilliseconds(),
-            GetActiveMilliseconds(device_status_));
-  EXPECT_EQ("",  // No email should be saved for child account.
-            device_status_.active_periods(0).user_email());
-  // END.
-
   ASSERT_EQ(1, child_status_.screen_time_span_size());
   EXPECT_EQ(5 * ActivePeriodMilliseconds(),
             GetActiveMilliseconds(child_status_));
@@ -670,11 +548,6 @@
 
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  EXPECT_EQ(12 * ActivePeriodMilliseconds(),
-            GetActiveMilliseconds(device_status_));
-  // END.
-
   ExpectChildScreenTimeMilliseconds(12 * ActivePeriodMilliseconds());
   EXPECT_EQ(12 * ActivePeriodMilliseconds(),
             GetActiveMilliseconds(child_status_));
@@ -697,12 +570,6 @@
                        sizeof(test_states) / sizeof(DeviceStateTransitions));
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  EXPECT_EQ(1, device_status_.active_periods_size());
-  EXPECT_EQ(5 * ActivePeriodMilliseconds(),
-            GetActiveMilliseconds(device_status_));
-  // END.
-
   EXPECT_EQ(1, child_status_.screen_time_span_size());
   EXPECT_EQ(5 * ActivePeriodMilliseconds(),
             GetActiveMilliseconds(child_status_));
@@ -731,10 +598,6 @@
   GetStatus();
   // 4 is the number of states yielding an active period with duration of
   // ActivePeriodMilliseconds
-  // TODO(crbug.com/827386): remove after migration.
-  EXPECT_EQ(4 * ActivePeriodMilliseconds(),
-            GetActiveMilliseconds(device_status_));
-  // END.
   EXPECT_EQ(4 * ActivePeriodMilliseconds(),
             GetActiveMilliseconds(child_status_));
   ExpectChildScreenTimeMilliseconds(4 * ActivePeriodMilliseconds());
@@ -754,26 +617,6 @@
                        sizeof(test_states) / sizeof(DeviceStateTransitions));
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  ASSERT_EQ(2, device_status_.active_periods_size());
-
-  em::ActiveTimePeriod period0 = device_status_.active_periods(0);
-  em::ActiveTimePeriod period1 = device_status_.active_periods(1);
-  EXPECT_EQ(ActivePeriodMilliseconds() - 15000, period0.active_duration());
-  EXPECT_EQ(15000, period1.active_duration());
-
-  em::TimePeriod time_period0 = period0.time_period();
-  em::TimePeriod time_period1 = period1.time_period();
-
-  EXPECT_EQ(time_period0.end_timestamp(), time_period1.start_timestamp());
-
-  // Ensure that the start and end times for the period are a day apart.
-  EXPECT_EQ(time_period0.end_timestamp() - time_period0.start_timestamp(),
-            kMillisecondsPerDay);
-  EXPECT_EQ(time_period1.end_timestamp() - time_period1.start_timestamp(),
-            kMillisecondsPerDay);
-  // END.
-
   ASSERT_EQ(2, child_status_.screen_time_span_size());
 
   em::ScreenTimeSpan timespan0 = child_status_.screen_time_span(0);
@@ -809,9 +652,6 @@
 
   GetStatus();
 
-  // TODO(crbug.com/827386): remove after migration.
-  ASSERT_EQ(1, device_status_.active_periods_size());
-  // END.
   ASSERT_EQ(1, child_status_.screen_time_span_size());
   ExpectChildScreenTimeMilliseconds(ActivePeriodMilliseconds());
 }
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
index e28e3e8..2940b1e 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
@@ -397,7 +397,7 @@
 }
 
 bool ReadAndroidStatus(
-    const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver) {
+    const policy::StatusCollector::AndroidStatusReceiver& receiver) {
   auto* const arc_service_manager = arc::ArcServiceManager::Get();
   if (!arc_service_manager)
     return false;
@@ -622,8 +622,8 @@
         base::Bind(&DeviceStatusCollectorState::OnCPUTempInfoReceived, this));
   }
 
-  bool FetchAndroidStatus(const DeviceStatusCollector::AndroidStatusFetcher&
-                              android_status_fetcher) {
+  bool FetchAndroidStatus(
+      const StatusCollector::AndroidStatusFetcher& android_status_fetcher) {
     return android_status_fetcher.Run(
         base::Bind(&DeviceStatusCollectorState::OnAndroidInfoReceived, this));
   }
@@ -1023,7 +1023,7 @@
           DeviceStatusCollector::VolumeInfoFetcher(),
           DeviceStatusCollector::CPUStatisticsFetcher(),
           DeviceStatusCollector::CPUTempFetcher(),
-          DeviceStatusCollector::AndroidStatusFetcher(),
+          StatusCollector::AndroidStatusFetcher(),
           DeviceStatusCollector::TpmStatusFetcher(),
           DeviceStatusCollector::EMMCLifetimeFetcher(),
           DeviceStatusCollector::StatefulPartitionInfoFetcher(),
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.h b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
index a54b737..5da9f038 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
@@ -130,17 +130,6 @@
   using CPUTempFetcher =
       base::Callback<std::vector<enterprise_management::CPUTempInfo>()>;
 
-  // Passed into asynchronous mojo interface for communicating with Android.
-  using AndroidStatusReceiver =
-      base::Callback<void(const std::string&, const std::string&)>;
-  // Calls the enterprise reporting mojo interface, passing over the
-  // AndroidStatusReceiver. Returns false if the mojo interface isn't available,
-  // in which case no asynchronous query is emitted and the android status query
-  // fails synchronously. The |AndroidStatusReceiver| is not called in this
-  // case.
-  using AndroidStatusFetcher =
-      base::Callback<bool(const AndroidStatusReceiver&)>;
-
   // Format of the function that asynchronously receives TpmStatusInfo.
   using TpmStatusReceiver = base::OnceCallback<void(const TpmStatusInfo&)>;
   // Gets the TpmStatusInfo and passes it to TpmStatusReceiver.
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index 9295ac8..1a98345 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -154,7 +154,7 @@
   policy::DeviceStatusCollector::VolumeInfoFetcher volume_info_fetcher;
   policy::DeviceStatusCollector::CPUStatisticsFetcher cpu_fetcher;
   policy::DeviceStatusCollector::CPUTempFetcher cpu_temp_fetcher;
-  policy::DeviceStatusCollector::AndroidStatusFetcher android_status_fetcher;
+  policy::StatusCollector::AndroidStatusFetcher android_status_fetcher;
   policy::DeviceStatusCollector::TpmStatusFetcher tpm_status_fetcher;
   policy::DeviceStatusCollector::EMMCLifetimeFetcher emmc_lifetime_fetcher;
   policy::DeviceStatusCollector::StatefulPartitionInfoFetcher
@@ -313,14 +313,14 @@
 }
 
 void CallAndroidStatusReceiver(
-    const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver,
+    const policy::StatusCollector::AndroidStatusReceiver& receiver,
     const std::string& status,
     const std::string& droid_guard_info) {
   receiver.Run(status, droid_guard_info);
 }
 
 bool GetEmptyAndroidStatus(
-    const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver) {
+    const policy::StatusCollector::AndroidStatusReceiver& receiver) {
   // Post it to the thread because this call is expected to be asynchronous.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&CallAndroidStatusReceiver, receiver, "", ""));
@@ -330,7 +330,7 @@
 bool GetFakeAndroidStatus(
     const std::string& status,
     const std::string& droid_guard_info,
-    const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver) {
+    const policy::StatusCollector::AndroidStatusReceiver& receiver) {
   // Post it to the thread because this call is expected to be asynchronous.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&CallAndroidStatusReceiver, receiver, status,
diff --git a/chrome/browser/chromeos/policy/status_collector/status_collector.h b/chrome/browser/chromeos/policy/status_collector/status_collector.h
index 5f62618..0b03951 100644
--- a/chrome/browser/chromeos/policy/status_collector/status_collector.h
+++ b/chrome/browser/chromeos/policy/status_collector/status_collector.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_STATUS_COLLECTOR_H_
 
 #include <memory>
+#include <string>
 
 #include "base/callback.h"
 #include "base/optional.h"
@@ -64,6 +65,17 @@
 // Defines the API for a status collector.
 class StatusCollector {
  public:
+  // Passed into asynchronous mojo interface for communicating with Android.
+  using AndroidStatusReceiver =
+      base::Callback<void(const std::string&, const std::string&)>;
+  // Calls the enterprise reporting mojo interface, passing over the
+  // AndroidStatusReceiver. Returns false if the mojo interface isn't available,
+  // in which case no asynchronous query is emitted and the android status query
+  // fails synchronously. The |AndroidStatusReceiver| is not called in this
+  // case.
+  using AndroidStatusFetcher =
+      base::Callback<bool(const AndroidStatusReceiver&)>;
+
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
   // Simplifies filling the boot mode for any of the relevant status report
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 45c7b0d..c445df3 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1315,6 +1315,11 @@
     "expiry_milestone": 82
   },
   {
+    "name": "enable-experimental-accessibility-chromevox-search-menus",
+    "owners": ["akihiroota"],
+    "expiry_milestone": 82
+  },
+  {
     "name": "enable-experimental-accessibility-chromevox-sub-node-language-switching",
     "owners": [ "akihiroota", "dmazzoni", "dtseng", "//ui/accessibility/OWNERS"],
     "expiry_milestone": 82
@@ -2506,6 +2511,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "interest-feed-feedback",
+    "owners": [ "petewil", "//chrome/android/feed/OWNERS" ],
+    "expiry_milestone": 90
+  },
+  {
     "name": "interest-feed-notifications",
     "owners": [ "iwells", "//chrome/android/feed/OWNERS" ],
     "expiry_milestone": 80
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index f2d7ab9..47be87a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2648,6 +2648,10 @@
 const char kInterestFeedContentSuggestionsName[] =
     "Interest Feed Content Suggestions";
 
+const char kInterestFeedFeedbackDescription[] =
+    "Allow the user to provide feedback from a feed card.";
+const char kInterestFeedFeedbackName[] = "Interest Feed Feedback";
+
 const char kManualPasswordGenerationAndroidName[] =
     "Manual password generation";
 const char kManualPasswordGenerationAndroidDescription[] =
@@ -3632,8 +3636,14 @@
     "Enable ChromeVox language switching, which changes ChromeVox's "
     "output language upon detection of new language.";
 
+const char kExperimentalAccessibilityChromeVoxSearchMenusName[] =
+    "Enable experimental ChromeVox search menus feature.";
+const char kExperimentalAccessibilityChromeVoxSearchMenusDescription[] =
+    "Allows users to search for items in the ChromeVox menu.";
+
 const char kExperimentalAccessibilityChromeVoxSubNodeLanguageSwitchingName[] =
-    "Enable experimental ChromeVox sub-node (word-level) language switching.";
+    "Enable experimental ChromeVox sub-node (word-level) language "
+    "switching.";
 const char
     kExperimentalAccessibilityChromeVoxSubNodeLanguageSwitchingDescription[] =
         "Enable ChromeVox language switching at the sub-node level, "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e00d974..ffdb1182 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1573,6 +1573,9 @@
 extern const char kInterestFeedContentSuggestionsName[];
 extern const char kInterestFeedContentSuggestionsDescription[];
 
+extern const char kInterestFeedFeedbackName[];
+extern const char kInterestFeedFeedbackDescription[];
+
 extern const char kManualPasswordGenerationAndroidName[];
 extern const char kManualPasswordGenerationAndroidDescription[];
 
@@ -2164,6 +2167,9 @@
 extern const char
     kExperimentalAccessibilityChromeVoxLanguageSwitchingDescription[];
 
+extern const char kExperimentalAccessibilityChromeVoxSearchMenusName[];
+extern const char kExperimentalAccessibilityChromeVoxSearchMenusDescription[];
+
 extern const char
     kExperimentalAccessibilityChromeVoxSubNodeLanguageSwitchingName[];
 extern const char
diff --git a/chrome/browser/metrics/first_web_contents_profiler.cc b/chrome/browser/metrics/first_web_contents_profiler.cc
index cabdd71..a077ca1 100644
--- a/chrome/browser/metrics/first_web_contents_profiler.cc
+++ b/chrome/browser/metrics/first_web_contents_profiler.cc
@@ -188,6 +188,15 @@
     // The active WebContents may be hidden when the window height is small.
     content::WebContents* contents =
         browser->tab_strip_model()->GetActiveWebContents();
+
+#if defined(OS_MACOSX)
+    // TODO(https://crbug.com/1032348): It is incorrect to have a visible
+    // browser window with no active WebContents, but reports on Mac show that
+    // it happens.
+    if (!contents)
+      continue;
+#endif  // defined(OS_MACOSX)
+
     if (contents->GetVisibility() != content::Visibility::VISIBLE)
       continue;
 
diff --git a/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc b/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
index 953e054..2ee5612 100644
--- a/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
+++ b/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
@@ -346,10 +346,10 @@
     return search_results_page_url_;
   }
 
-  void SetExpectedHintsRequestForHosts(
-      const base::flat_set<std::string>& hosts) {
+  void SetExpectedHintsRequestForHostsAndUrls(
+      const base::flat_set<std::string>& hosts_or_urls) {
     base::AutoLock lock(lock_);
-    expect_hints_request_for_hosts_ = hosts;
+    expect_hints_request_for_hosts_and_urls_ = hosts_or_urls;
   }
 
   size_t count_hints_requests_received() {
@@ -420,12 +420,14 @@
 
     optimization_guide::proto::GetHintsRequest hints_request;
     EXPECT_TRUE(hints_request.ParseFromString(request.content));
-    EXPECT_FALSE(hints_request.hosts().empty());
+    EXPECT_FALSE(hints_request.hosts().empty() && hints_request.urls().empty());
     EXPECT_GE(optimization_guide::features::
                   MaxHostsForOptimizationGuideServiceHintsFetch(),
               static_cast<size_t>(hints_request.hosts().size()));
 
-    VerifyHintsMatchExpectedHosts(hints_request);
+    // Only verify the hints if there are hosts in the request.
+    if (!hints_request.hosts().empty())
+      VerifyHintsMatchExpectedHostsAndUrls(hints_request);
 
     if (response_type_ == HintsFetcherRemoteResponseType::kSuccessful) {
       response->set_code(net::HTTP_OK);
@@ -461,21 +463,24 @@
   // Verifies that the hosts present in |hints_request| match the expected set
   // of hosts present in |expect_hints_request_for_hosts_|. The ordering of the
   // hosts in not matched.
-  void VerifyHintsMatchExpectedHosts(
+  void VerifyHintsMatchExpectedHostsAndUrls(
       const optimization_guide::proto::GetHintsRequest& hints_request) const {
-    if (!expect_hints_request_for_hosts_)
+    if (!expect_hints_request_for_hosts_and_urls_)
       return;
 
-    base::flat_set<std::string> hosts_requested;
+    base::flat_set<std::string> hosts_and_urls_requested;
     for (const auto& host : hints_request.hosts())
-      hosts_requested.insert(host.host());
+      hosts_and_urls_requested.insert(host.host());
+    for (const auto& url : hints_request.urls())
+      hosts_and_urls_requested.insert(url.url());
 
-    EXPECT_EQ(expect_hints_request_for_hosts_.value().size(),
-              hosts_requested.size());
-    for (const auto& host : expect_hints_request_for_hosts_.value()) {
-      hosts_requested.erase(host);
+    EXPECT_EQ(expect_hints_request_for_hosts_and_urls_.value().size(),
+              hosts_and_urls_requested.size());
+    for (const auto& host_or_url :
+         expect_hints_request_for_hosts_and_urls_.value()) {
+      hosts_and_urls_requested.erase(host_or_url);
     }
-    EXPECT_EQ(0u, hosts_requested.size());
+    EXPECT_EQ(0u, hosts_and_urls_requested.size());
   }
 
   void TearDownOnMainThread() override {
@@ -502,11 +507,12 @@
   // Count of hints requests received so far by |hints_server_|.
   size_t count_hints_requests_received_ = 0;
 
-  // Guarded by |lock_|.
-  // Set of hosts for which a hints request is expected to arrive. This set is
-  // verified to match with the set of hosts present in the hints request. If
-  // null, then the verification is not done.
-  base::Optional<base::flat_set<std::string>> expect_hints_request_for_hosts_;
+  // Guarded by |lock_|. Set of hosts and URLs for which a hints request is
+  // expected to arrive. This set is verified to match with the set of hosts and
+  // URLs present in the hints request. If null, then the verification is not
+  // done.
+  base::Optional<base::flat_set<std::string>>
+      expect_hints_request_for_hosts_and_urls_;
 
   std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
 
@@ -1070,10 +1076,11 @@
 
     // Navigate to a host not in the seeded site engagement service; it
     // should be recorded as covered by the hints fetcher due to the race.
-    base::flat_set<std::string> expected_hosts_2g;
+    base::flat_set<std::string> expected_request_2g;
     std::string host_2g("https://unseenhost_2g.com/");
-    expected_hosts_2g.insert(GURL(host_2g).host());
-    SetExpectedHintsRequestForHosts(expected_hosts_2g);
+    expected_request_2g.insert(GURL(host_2g).host());
+    expected_request_2g.insert(GURL(host_2g).spec());
+    SetExpectedHintsRequestForHostsAndUrls(expected_request_2g);
     ui_test_utils::NavigateToURL(browser(), GURL(host_2g));
 
     EXPECT_EQ(2u, count_hints_requests_received());
@@ -1102,10 +1109,11 @@
         ->ReportEffectiveConnectionTypeForTesting(
             net::EFFECTIVE_CONNECTION_TYPE_4G);
 
-    base::flat_set<std::string> expected_hosts_4g;
+    base::flat_set<std::string> expected_request_4g;
     std::string host_4g("https://unseenhost_4g.com/");
-    expected_hosts_4g.insert((GURL(host_4g).host()));
-    SetExpectedHintsRequestForHosts(expected_hosts_4g);
+    expected_request_4g.insert((GURL(host_4g).host()));
+    expected_request_4g.insert((GURL(host_4g).spec()));
+    SetExpectedHintsRequestForHostsAndUrls(expected_request_4g);
     ui_test_utils::NavigateToURL(browser(), GURL(host_4g));
 
     EXPECT_EQ(2u, count_hints_requests_received());
@@ -1140,10 +1148,11 @@
 
     // Navigate to a host not in the seeded site engagement service; it
     // should be recorded as not covered by the hints fetcher.
-    base::flat_set<std::string> expected_hosts_3g;
+    base::flat_set<std::string> expected_request_3g;
     std::string host_3g("https://unseenhost_3g.com/");
-    expected_hosts_3g.insert(GURL(host_3g).host());
-    SetExpectedHintsRequestForHosts(expected_hosts_3g);
+    expected_request_3g.insert(GURL(host_3g).host());
+    expected_request_3g.insert(GURL(host_3g).spec());
+    SetExpectedHintsRequestForHostsAndUrls(expected_request_3g);
     ui_test_utils::NavigateToURL(browser(), GURL(host_3g));
 
     EXPECT_EQ(3u, count_hints_requests_received());
@@ -1176,14 +1185,17 @@
   {
     // Navigate to a host that was recently fetched. It
     // should be recorded as covered by the hints fetcher.
-    base::flat_set<std::string> expected_hosts_3g;
+    base::flat_set<std::string> expected_request_3g;
     std::string host_3g("https://unseenhost_3g.com");
-    expected_hosts_3g.insert(GURL(host_3g).host());
-    SetExpectedHintsRequestForHosts(expected_hosts_3g);
+    expected_request_3g.insert(GURL(host_3g).host());
+    expected_request_3g.insert(GURL(host_3g).spec());
+    SetExpectedHintsRequestForHostsAndUrls(expected_request_3g);
     ui_test_utils::NavigateToURL(browser(),
                                  GURL("https://unseenhost_3g.com/test1.html"));
-    // Hints should not be fetched for the same host again.
-    EXPECT_EQ(3u, count_hints_requests_received());
+
+    // With URL-keyed Hints, every unique URL navigated to will result in a
+    // hints fetch if racing is enabled and allowed.
+    EXPECT_EQ(4u, count_hints_requests_received());
     RetryForHistogramUntilCountReached(
         histogram_tester, optimization_guide::kLoadedHintLocalHistogramString,
         4);
@@ -1341,7 +1353,7 @@
   expected_hosts.insert(GURL("https://foo.com").host());
   expected_hosts.insert(GURL("https://example.com").host());
   expected_hosts.insert(GURL("https://example3.com").host());
-  SetExpectedHintsRequestForHosts(expected_hosts);
+  SetExpectedHintsRequestForHostsAndUrls(expected_hosts);
 
   histogram_tester->ExpectTotalCount(
       optimization_guide::kLoadedHintLocalHistogramString, 0);
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index 421e0b82..b9464c4b 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/histogram_macros_local.h"
 #include "base/rand_util.h"
@@ -140,6 +141,33 @@
   return matched_page_hint;
 }
 
+// Util class for recording whether a hints fetch race against the current
+// navigation was attempted. The result is recorded when it goes out of scope
+// and its destructor is called.
+class ScopedHintsManagerRaceNavigationHintsFetchAttemptRecorder {
+ public:
+  ScopedHintsManagerRaceNavigationHintsFetchAttemptRecorder()
+      : race_attempt_status_(
+            optimization_guide::RaceNavigationFetchAttemptStatus::kUnknown) {}
+
+  ~ScopedHintsManagerRaceNavigationHintsFetchAttemptRecorder() {
+    DCHECK_NE(race_attempt_status_,
+              optimization_guide::RaceNavigationFetchAttemptStatus::kUnknown);
+    base::UmaHistogramEnumeration(
+        "OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
+        race_attempt_status_);
+  }
+
+  void set_race_attempt_status(
+      optimization_guide::RaceNavigationFetchAttemptStatus
+          race_attempt_status) {
+    race_attempt_status_ = race_attempt_status;
+  }
+
+ private:
+  optimization_guide::RaceNavigationFetchAttemptStatus race_attempt_status_;
+};
+
 }  // namespace
 
 OptimizationGuideHintsManager::OptimizationGuideHintsManager(
@@ -475,8 +503,10 @@
         optimization_guide::features::GetOptimizationGuideServiceGetHintsURL(),
         pref_service_);
   }
+
   hints_fetcher_->FetchOptimizationGuideServiceHints(
-      top_hosts, optimization_guide::proto::CONTEXT_BATCH_UPDATE,
+      top_hosts, std::vector<GURL>{}, registered_optimization_types_,
+      optimization_guide::proto::CONTEXT_BATCH_UPDATE,
       base::BindOnce(&OptimizationGuideHintsManager::OnHintsFetched,
                      ui_weak_ptr_factory_.GetWeakPtr()));
 }
@@ -666,8 +696,11 @@
         pref_service_);
   }
 
+  // TODO(crbug/1041693): Add support for URL-keyed hints fetch from the
+  // search results page.
   hints_fetcher_->FetchOptimizationGuideServiceHints(
-      target_hosts_serialized,
+      target_hosts_serialized, std::vector<GURL>{},
+      registered_optimization_types_,
       optimization_guide::proto::CONTEXT_PAGE_NAVIGATION,
       base::BindOnce(&OptimizationGuideHintsManager::OnHintsFetched,
                      ui_weak_ptr_factory_.GetWeakPtr()));
@@ -934,31 +967,71 @@
     return;
   }
 
-  if (IsAllowedToFetchNavigationHints(navigation_handle->GetURL()) &&
-      !hint_cache_->HasHint(navigation_handle->GetURL().host())) {
-    std::vector<std::string> hosts{navigation_handle->GetURL().host()};
+  MaybeFetchHintsForNavigation(navigation_handle);
+
+  LoadHintForNavigation(navigation_handle, std::move(callback));
+}
+
+void OptimizationGuideHintsManager::MaybeFetchHintsForNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (registered_optimization_types_.size() == 0)
+    return;
+
+  if (!IsAllowedToFetchNavigationHints(navigation_handle->GetURL()))
+    return;
+
+  ScopedHintsManagerRaceNavigationHintsFetchAttemptRecorder
+      race_navigation_recorder;
+
+  std::vector<std::string> hosts;
+  std::vector<GURL> urls;
+  if (!hint_cache_->HasHint(navigation_handle->GetURL().host())) {
+    hosts.push_back(navigation_handle->GetURL().host());
     page_navigation_hosts_being_fetched_.clear();
     page_navigation_hosts_being_fetched_.push_back(
         navigation_handle->GetURL().host());
 
-    if (!hints_fetcher_) {
-      hints_fetcher_ = std::make_unique<optimization_guide::HintsFetcher>(
-          url_loader_factory_,
-          optimization_guide::features::
-              GetOptimizationGuideServiceGetHintsURL(),
-          pref_service_);
-    }
-    hints_fetcher_->FetchOptimizationGuideServiceHints(
-        hosts, optimization_guide::proto::CONTEXT_PAGE_NAVIGATION,
-        base::BindOnce(&OptimizationGuideHintsManager::OnHintsFetched,
-                       ui_weak_ptr_factory_.GetWeakPtr()));
-
     OptimizationGuideNavigationData* navigation_data =
         OptimizationGuideNavigationData::GetFromNavigationHandle(
             navigation_handle);
     navigation_data->set_was_hint_for_host_attempted_to_be_fetched(true);
+    race_navigation_recorder.set_race_attempt_status(
+        optimization_guide::RaceNavigationFetchAttemptStatus::
+            kRaceNavigationFetchHost);
   }
-  LoadHintForNavigation(navigation_handle, std::move(callback));
+
+  if (!hint_cache_->GetURLKeyedHint(navigation_handle->GetURL())) {
+    urls.push_back(navigation_handle->GetURL());
+    race_navigation_recorder.set_race_attempt_status(
+        optimization_guide::RaceNavigationFetchAttemptStatus::
+            kRaceNavigationFetchURL);
+  }
+
+  if (hosts.empty() && urls.empty()) {
+    race_navigation_recorder.set_race_attempt_status(
+        optimization_guide::RaceNavigationFetchAttemptStatus::
+            kRaceNavigationFetchNotAttempted);
+    return;
+  }
+
+  if (!hints_fetcher_) {
+    hints_fetcher_ = std::make_unique<optimization_guide::HintsFetcher>(
+        url_loader_factory_,
+        optimization_guide::features::GetOptimizationGuideServiceGetHintsURL(),
+        pref_service_);
+  }
+
+  hints_fetcher_->FetchOptimizationGuideServiceHints(
+      hosts, urls, registered_optimization_types_,
+      optimization_guide::proto::CONTEXT_PAGE_NAVIGATION,
+      base::BindOnce(&OptimizationGuideHintsManager::OnHintsFetched,
+                     ui_weak_ptr_factory_.GetWeakPtr()));
+
+  if (!hosts.empty() && !urls.empty()) {
+    race_navigation_recorder.set_race_attempt_status(
+        optimization_guide::RaceNavigationFetchAttemptStatus::
+            kRaceNavigationFetchHostAndURL);
+  }
 }
 
 void OptimizationGuideHintsManager::ClearFetchedHints() {
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
index 838b3f3c..04b45c1 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
@@ -281,6 +281,12 @@
   // remote Optimization Guide Service.
   bool IsHintBeingFetched(const std::string& host) const;
 
+  // Creates a hints fetch for |navigation_handle| if it is allowed. The
+  // fetch will include the host and URL of the |navigation_handle| if the
+  // associated hints for each are not already in the cache.
+  void MaybeFetchHintsForNavigation(
+      content::NavigationHandle* navigation_handle);
+
   // The OptimizationGuideService that this guide is listening to. Not owned.
   optimization_guide::OptimizationGuideService* const
       optimization_guide_service_;
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index 6ab06494..810fed84 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -80,7 +80,8 @@
 }
 
 std::unique_ptr<optimization_guide::proto::GetHintsResponse> BuildHintsResponse(
-    std::vector<std::string> hosts) {
+    std::vector<std::string> hosts,
+    std::vector<std::string> urls) {
   std::unique_ptr<optimization_guide::proto::GetHintsResponse>
       get_hints_response =
           std::make_unique<optimization_guide::proto::GetHintsResponse>();
@@ -92,6 +93,14 @@
     optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
     page_hint->set_page_pattern("page pattern");
   }
+  for (const auto& url : urls) {
+    optimization_guide::proto::Hint* hint = get_hints_response->add_hints();
+    hint->set_key_representation(optimization_guide::proto::FULL_URL);
+    hint->set_key(url);
+    optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
+    page_hint->set_page_pattern("page pattern");
+  }
+
   return get_hints_response;
 }
 
@@ -145,8 +154,9 @@
 
 enum class HintsFetcherEndState {
   kFetchFailed = 0,
-  kFetchSuccessWithHints = 1,
+  kFetchSuccessWithHostHints = 1,
   kFetchSuccessWithNoHints = 2,
+  kFetchSuccessWithURLHints = 3,
 };
 
 // A mock class implementation of HintsFetcher.
@@ -166,6 +176,9 @@
 
   bool FetchOptimizationGuideServiceHints(
       const std::vector<std::string>& hosts,
+      const std::vector<GURL>& urls,
+      const base::flat_set<optimization_guide::proto::OptimizationType>&
+          optimization_types,
       optimization_guide::proto::RequestContext request_context,
       optimization_guide::HintsFetchedCallback hints_fetched_callback)
       override {
@@ -176,19 +189,27 @@
                  optimization_guide::HintsFetcherRequestStatus::kResponseError,
                  base::nullopt);
         return false;
-      case HintsFetcherEndState::kFetchSuccessWithHints:
+      case HintsFetcherEndState::kFetchSuccessWithHostHints:
         hints_fetched_ = true;
         std::move(hints_fetched_callback)
             .Run(request_context,
                  optimization_guide::HintsFetcherRequestStatus::kSuccess,
-                 BuildHintsResponse({"host.com"}));
+                 BuildHintsResponse({"host.com"}, {}));
+        return true;
+      case HintsFetcherEndState::kFetchSuccessWithURLHints:
+        hints_fetched_ = true;
+        std::move(hints_fetched_callback)
+            .Run(request_context,
+                 optimization_guide::HintsFetcherRequestStatus::kSuccess,
+                 BuildHintsResponse({},
+                                    {"https://somedomain.org/news/whatever"}));
         return true;
       case HintsFetcherEndState::kFetchSuccessWithNoHints:
         hints_fetched_ = true;
         std::move(hints_fetched_callback)
             .Run(request_context,
                  optimization_guide::HintsFetcherRequestStatus::kSuccess,
-                 BuildHintsResponse({}));
+                 BuildHintsResponse({}, {}));
         return true;
     }
     return true;
@@ -380,7 +401,6 @@
 
   TestingPrefServiceSimple* pref_service() const { return pref_service_.get(); }
 
- protected:
   void RunUntilIdle() {
     task_environment_.RunUntilIdle();
     base::RunLoop().RunUntilIdle();
@@ -1845,7 +1865,7 @@
   CreateServiceAndHintsManager(/*optimization_types_at_initialization=*/{},
                                /*top_host_provider=*/nullptr);
   hints_manager()->SetHintsFetcherForTesting(
-      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHostHints));
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch.
@@ -1863,7 +1883,7 @@
   CreateServiceAndHintsManager(/*optimization_types_at_initialization=*/{},
                                top_host_provider.get());
   hints_manager()->SetHintsFetcherForTesting(
-      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHostHints));
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch.
@@ -1882,7 +1902,7 @@
   CreateServiceAndHintsManager(/*optimization_types_at_initialization=*/{},
                                top_host_provider.get());
   hints_manager()->SetHintsFetcherForTesting(
-      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHostHints));
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch.
@@ -1902,7 +1922,7 @@
   CreateServiceAndHintsManager(/*optimization_types_at_initialization=*/{},
                                top_host_provider.get());
   hints_manager()->SetHintsFetcherForTesting(
-      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHostHints));
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch.
@@ -1919,7 +1939,7 @@
   CreateServiceAndHintsManager(/*optimization_types_at_initialization=*/{},
                                top_host_provider.get());
   hints_manager()->SetHintsFetcherForTesting(
-      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHostHints));
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch.
@@ -1973,7 +1993,7 @@
   // Force speculative timer to expire after fetch fails first time, update
   // hints fetcher so it succeeds this time.
   hints_manager()->SetHintsFetcherForTesting(
-      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHostHints));
   MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
   EXPECT_EQ(2, top_host_provider->get_num_top_hosts_called());
   EXPECT_TRUE(hints_fetcher()->hints_fetched());
@@ -1990,7 +2010,7 @@
   CreateServiceAndHintsManager(/*optimization_types_at_initialization=*/{},
                                top_host_provider.get());
   hints_manager()->SetHintsFetcherForTesting(
-      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHostHints));
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch that succeeds.
@@ -2000,7 +2020,7 @@
   // TODO(mcrouse): Make sure timer is triggered by metadata entry,
   // |hint_cache| control needed.
   hints_manager()->SetHintsFetcherForTesting(
-      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHostHints));
 
   MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
   EXPECT_FALSE(hints_fetcher()->hints_fetched());
@@ -2162,6 +2182,11 @@
       navigation_data->was_hint_for_host_attempted_to_be_fetched().has_value());
   EXPECT_TRUE(
       navigation_data->was_hint_for_host_attempted_to_be_fetched().value());
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
+      optimization_guide::RaceNavigationFetchAttemptStatus::
+          kRaceNavigationFetchHostAndURL,
+      1);
 }
 
 TEST_F(OptimizationGuideHintsManagerFetchingTest,
@@ -2169,6 +2194,8 @@
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   InitializeWithDefaultConfig("1.0.0.0");
+  hints_manager()->SetHintsFetcherForTesting(
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithURLHints));
 
   // Set ECT estimate so hint is activated.
   hints_manager()->OnEffectiveConnectionTypeChanged(
@@ -2190,6 +2217,72 @@
           navigation_handle.get());
   EXPECT_FALSE(
       navigation_data->was_hint_for_host_attempted_to_be_fetched().has_value());
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
+      optimization_guide::RaceNavigationFetchAttemptStatus::
+          kRaceNavigationFetchURL,
+      1);
+}
+
+TEST_F(OptimizationGuideHintsManagerFetchingTest,
+       URLHintsNotFetchedAtNavigationTime) {
+  hints_manager()->RegisterOptimizationTypes(
+      {optimization_guide::proto::DEFER_ALL_SCRIPT});
+  InitializeWithDefaultConfig("1.0.0.0");
+  hints_manager()->SetHintsFetcherForTesting(
+      BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithURLHints));
+
+  // Set ECT estimate so hint is activated.
+  hints_manager()->OnEffectiveConnectionTypeChanged(
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+
+  base::HistogramTester histogram_tester;
+  {
+    std::unique_ptr<content::MockNavigationHandle> navigation_handle =
+        CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+            url_with_hints());
+
+    base::RunLoop run_loop;
+    hints_manager()->OnNavigationStartOrRedirect(navigation_handle.get(),
+                                                 run_loop.QuitClosure());
+    run_loop.Run();
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 0);
+
+    // Make sure navigation data is populated correctly.
+    OptimizationGuideNavigationData* navigation_data =
+        OptimizationGuideNavigationData::GetFromNavigationHandle(
+            navigation_handle.get());
+    EXPECT_FALSE(navigation_data->was_hint_for_host_attempted_to_be_fetched()
+                     .has_value());
+
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
+        optimization_guide::RaceNavigationFetchAttemptStatus::
+            kRaceNavigationFetchURL,
+        1);
+    RunUntilIdle();
+  }
+
+  {
+    std::unique_ptr<content::MockNavigationHandle> navigation_handle =
+        CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+            url_with_hints());
+    base::RunLoop run_loop;
+    navigation_handle =
+        CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+            url_with_hints());
+    hints_manager()->OnNavigationStartOrRedirect(navigation_handle.get(),
+                                                 run_loop.QuitClosure());
+    run_loop.Run();
+
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
+        optimization_guide::RaceNavigationFetchAttemptStatus::
+            kRaceNavigationFetchNotAttempted,
+        1);
+  }
 }
 
 TEST_F(OptimizationGuideHintsManagerFetchingTest,
@@ -2217,6 +2310,9 @@
           navigation_handle.get());
   EXPECT_FALSE(
       navigation_data->was_hint_for_host_attempted_to_be_fetched().has_value());
+
+  histogram_tester.ExpectTotalCount(
+      "OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus", 0);
 }
 
 TEST_F(OptimizationGuideHintsManagerFetchingTest,
diff --git a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.cc
index c7b8d044..76a02284 100644
--- a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.cc
@@ -180,7 +180,7 @@
       (GetDelegate().GetNavigationStart() - base::TimeTicks()).InMicroseconds();
   const page_load_metrics::ContentfulPaintTimingInfo& largest_contentful_paint =
       largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
-  if (!largest_contentful_paint.IsEmpty()) {
+  if (largest_contentful_paint.ContainsValidTime()) {
     Java_PageLoadMetrics_onLargestContentfulPaint(
         env, java_web_contents, static_cast<jlong>(navigation_id_),
         static_cast<jlong>(navigation_start_tick),
diff --git a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
index b8cb856..9ec3d68 100644
--- a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
@@ -687,7 +687,7 @@
   const page_load_metrics::ContentfulPaintTimingInfo&
       all_frames_largest_contentful_paint =
           largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
-  if (!all_frames_largest_contentful_paint.IsEmpty() &&
+  if (all_frames_largest_contentful_paint.ContainsValidTime() &&
       WasStartedInForegroundOptionalEventInForeground(
           all_frames_largest_contentful_paint.Time(), delegate)) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSLargestContentfulPaint,
diff --git a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc
index 63db9a0..55dcd35 100644
--- a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc
@@ -151,6 +151,7 @@
       base::TimeDelta::FromMilliseconds(40);
   timing.paint_timing->largest_text_paint =
       base::TimeDelta::FromMilliseconds(50);
+  timing.paint_timing->largest_text_paint_size = 20u;
   timing.paint_timing->first_image_paint =
       base::TimeDelta::FromMilliseconds(160);
   timing.parse_timing->parse_stop = base::TimeDelta::FromMilliseconds(320);
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
index 8d8ba10..35a851e 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -294,7 +294,7 @@
   const page_load_metrics::ContentfulPaintTimingInfo&
       main_frame_largest_contentful_paint =
           largest_contentful_paint_handler_.MainFrameLargestContentfulPaint();
-  if (!main_frame_largest_contentful_paint.IsEmpty() &&
+  if (main_frame_largest_contentful_paint.ContainsValidTime() &&
       WasStartedInForegroundOptionalEventInForeground(
           main_frame_largest_contentful_paint.Time(), GetDelegate())) {
     builder.SetPaintTiming_NavigationToLargestContentfulPaint_MainFrame(
@@ -303,7 +303,7 @@
   const page_load_metrics::ContentfulPaintTimingInfo&
       all_frames_largest_contentful_paint =
           largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
-  if (!all_frames_largest_contentful_paint.IsEmpty() &&
+  if (all_frames_largest_contentful_paint.ContainsValidTime() &&
       WasStartedInForegroundOptionalEventInForeground(
           all_frames_largest_contentful_paint.Time(), GetDelegate())) {
     builder.SetPaintTiming_NavigationToLargestContentfulPaint(
@@ -610,7 +610,7 @@
   const page_load_metrics::ContentfulPaintTimingInfo& paint =
       largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
 
-  if (!paint.IsEmpty()) {
+  if (paint.ContainsValidTime()) {
     TRACE_EVENT_INSTANT2(
         "loading",
         "NavStartToLargestContentfulPaint::Candidate::AllFrames::UKM",
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
index 5a8755f..7f3cf603 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -287,6 +287,75 @@
   }
 }
 
+TEST_F(UkmPageLoadMetricsObserverTest, LargestImageLoading) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  // The largest image is loading so its paint time is set to TimeDelta().
+  timing.paint_timing->largest_image_paint = base::TimeDelta();
+  timing.paint_timing->largest_image_paint_size = 50u;
+  // There is a text paint, but it must be ignored because it is smaller than
+  // the image paint.
+  timing.paint_timing->largest_text_paint =
+      base::TimeDelta::FromMilliseconds(600);
+  timing.paint_timing->largest_text_paint_size = 25u;
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kTestUrl1));
+  tester()->SimulateTimingUpdate(timing);
+
+  // Simulate closing the tab.
+  DeleteContents();
+
+  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
+  for (const auto& kv : merged_entries) {
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
+        kv.second.get(),
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName));
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
+        kv.second.get(),
+        PageLoad::
+            kPaintTiming_NavigationToLargestContentfulPaint_MainFrameName));
+  }
+}
+
+TEST_F(UkmPageLoadMetricsObserverTest, LargestImageLoadingSmallerThanText) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  // Largest image is loading.
+  timing.paint_timing->largest_image_paint = base::TimeDelta();
+  timing.paint_timing->largest_image_paint_size = 50u;
+  // Largest text is larger than image.
+  timing.paint_timing->largest_text_paint =
+      base::TimeDelta::FromMilliseconds(600);
+  timing.paint_timing->largest_text_paint_size = 80u;
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kTestUrl1));
+  tester()->SimulateTimingUpdate(timing);
+
+  // Simulate closing the tab.
+  DeleteContents();
+
+  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
+  EXPECT_EQ(1ul, merged_entries.size());
+
+  for (const auto& kv : merged_entries) {
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
+        kv.second.get(),
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 600);
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
+        kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
+  }
+}
+
 TEST_F(UkmPageLoadMetricsObserverTest,
        LargestImagePaint_DiscardBackgroundResult) {
   std::unique_ptr<base::SimpleTestClock> mock_clock(
@@ -652,6 +721,54 @@
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest,
+       LargestContentfulPaintAllFrames_SubframeImageLoading) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  PopulateRequiredTimingFields(&timing);
+
+  page_load_metrics::mojom::PageLoadTiming subframe_timing;
+  page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
+  subframe_timing.navigation_start = base::Time::FromDoubleT(2);
+  // Subframe's largest image is still loading.
+  subframe_timing.paint_timing->largest_image_paint = base::TimeDelta();
+  subframe_timing.paint_timing->largest_image_paint_size = 100u;
+  // Subframe has text but it should be ignored as it's smaller than image.
+  subframe_timing.paint_timing->largest_text_paint =
+      base::TimeDelta::FromMilliseconds(3000);
+  subframe_timing.paint_timing->largest_text_paint_size = 80u;
+  PopulateRequiredTimingFields(&subframe_timing);
+
+  // Commit the main frame and a subframe.
+  NavigateAndCommit(GURL(kTestUrl1));
+  RenderFrameHost* subframe =
+      NavigationSimulator::NavigateAndCommitFromDocument(
+          GURL(kSubframeTestUrl),
+          RenderFrameHostTester::For(web_contents()->GetMainFrame())
+              ->AppendChild("subframe"));
+
+  // Simulate timing updates in the main frame and the subframe.
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
+
+  // Simulate closing the tab.
+  DeleteContents();
+
+  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
+  for (const auto& kv : merged_entries) {
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
+        kv.second.get(),
+        PageLoad::
+            kPaintTiming_NavigationToLargestContentfulPaint_MainFrameName));
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
+        kv.second.get(),
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName));
+  }
+}
+
+TEST_F(UkmPageLoadMetricsObserverTest,
        LargestContentfulPaintAllFrames_OnlyMainFrame) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
diff --git a/chrome/browser/performance_manager/graph/page_node_impl_browsertest.cc b/chrome/browser/performance_manager/graph/page_node_impl_browsertest.cc
index 0b818d2..55b7add 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl_browsertest.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "base/files/file_util.h"
 #include "base/run_loop.h"
+#include "base/sequence_checker.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -14,6 +15,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/performance_manager/graph/page_node_impl.h"
 #include "components/performance_manager/performance_manager_impl.h"
+#include "components/performance_manager/public/graph/frame_node.h"
+#include "components/performance_manager/public/performance_manager.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -94,6 +97,48 @@
 </html>
 )";
 
+// Class used to count how many frame level OrigianTrial freeze policy changed
+// events have been received, used to ensure that all the frame policies have
+// been loaded before checking if the page level policy is the expected one.
+// Created on the UI thread and then lives on the PM sequence.
+class FrameNodeOriginTrialFreezePolicyChangedCounter
+    : public FrameNode::ObserverDefaultImpl,
+      public GraphOwnedDefaultImpl {
+ public:
+  FrameNodeOriginTrialFreezePolicyChangedCounter() {
+    DETACH_FROM_SEQUENCE(sequence_checker_);
+  }
+  ~FrameNodeOriginTrialFreezePolicyChangedCounter() override = default;
+
+  uint32_t GetCount() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return ot_freeze_policy_changed_counter_;
+  }
+
+ private:
+  // GraphOwned implementation:
+  void OnPassedToGraph(Graph* graph) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    graph->AddFrameNodeObserver(this);
+  }
+  void OnTakenFromGraph(Graph* graph) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    graph->RemoveFrameNodeObserver(this);
+  }
+
+  // FrameNode::ObserverDefaultImpl implementation:
+  void OnOriginTrialFreezePolicyChanged(
+      const FrameNode* frame_node,
+      const InterventionPolicy& previous_value) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    ++ot_freeze_policy_changed_counter_;
+  }
+
+  uint32_t ot_freeze_policy_changed_counter_ = 0;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
 // Generate a test response for the PageFreeze Origin Trial tests, based on
 // kOriginTrialTestResponseTemplate.
 std::string GetPageFreezeOriginTrialPageContent(const std::string& page) {
@@ -156,22 +201,40 @@
 }
 
 void RunOriginTrialTestOnPMSequence(
-    const mojom::InterventionPolicy expected_policy) {
+    const mojom::InterventionPolicy expected_policy,
+    FrameNodeOriginTrialFreezePolicyChangedCounter* ot_change_counter,
+    uint32_t expected_ot_change_count) {
   auto* perf_manager = PerformanceManagerImpl::GetInstance();
   ASSERT_TRUE(perf_manager);
+  for (;;) {
+    bool load_complete = false;
+    base::RunLoop run_loop;
+    auto quit_closure = run_loop.QuitClosure();
+    perf_manager->CallOnGraphImpl(
+        FROM_HERE,
+        base::BindLambdaForTesting([&](performance_manager::GraphImpl* graph) {
+          auto ot_change_count = ot_change_counter->GetCount();
+          EXPECT_GE(expected_ot_change_count, ot_change_count);
+          if (ot_change_count == expected_ot_change_count)
+            load_complete = true;
+          std::move(quit_closure).Run();
+        }));
+    run_loop.Run();
+    if (load_complete)
+      break;
+    base::RunLoop().RunUntilIdle();
+  }
+
   base::RunLoop run_loop;
+  auto quit_closure = run_loop.QuitClosure();
   perf_manager->CallOnGraphImpl(
-      FROM_HERE, base::BindOnce(
-                     [](base::OnceClosure quit_closure,
-                        const mojom::InterventionPolicy expected_policy,
-                        performance_manager::GraphImpl* graph) {
-                       auto page_nodes = graph->GetAllPageNodeImpls();
-                       EXPECT_EQ(1U, page_nodes.size());
-                       EXPECT_EQ(expected_policy,
-                                 page_nodes[0]->origin_trial_freeze_policy());
-                       std::move(quit_closure).Run();
-                     },
-                     run_loop.QuitClosure(), expected_policy));
+      FROM_HERE,
+      base::BindLambdaForTesting([&](performance_manager::GraphImpl* graph) {
+        auto page_nodes = graph->GetAllPageNodeImpls();
+        EXPECT_EQ(1U, page_nodes.size());
+        EXPECT_EQ(expected_policy, page_nodes[0]->origin_trial_freeze_policy());
+        std::move(quit_closure).Run();
+      }));
   run_loop.Run();
 }
 
@@ -205,15 +268,26 @@
     // origin, whereas EmbeddedTestServer serves content on a random port.
     url_loader_interceptor_ = std::make_unique<content::URLLoaderInterceptor>(
         base::BindRepeating(&URLLoaderInterceptorCallback));
+
+    auto ot_change_counter =
+        std::make_unique<FrameNodeOriginTrialFreezePolicyChangedCounter>();
+    ot_change_counter_ = ot_change_counter.get();
+    PerformanceManager::PassToGraph(FROM_HERE, std::move(ot_change_counter));
   }
 
   void TearDownOnMainThread() override {
     url_loader_interceptor_.reset();
+    ot_change_counter_ = nullptr;
     InProcessBrowserTest::TearDownOnMainThread();
   }
 
+  FrameNodeOriginTrialFreezePolicyChangedCounter* ot_change_counter() {
+    return ot_change_counter_;
+  }
+
  private:
   std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_;
+  FrameNodeOriginTrialFreezePolicyChangedCounter* ot_change_counter_;
 
   DISALLOW_COPY_AND_ASSIGN(PageNodeImplBrowserTest);
 };
@@ -224,7 +298,8 @@
                      {kOriginTrialTestHostname,
                       kOriginTrialFreezePolicyTestPath, kOriginTrialOptInPage},
                      "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptIn);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptIn,
+                                 ot_change_counter(), 1);
 }
 
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest, PageFreezeOriginTrialOptOut) {
@@ -233,7 +308,8 @@
                      {kOriginTrialTestHostname,
                       kOriginTrialFreezePolicyTestPath, kOriginTrialOptOutPage},
                      "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut,
+                                 ot_change_counter(), 1);
 }
 
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest, PageFreezeOriginTrialDefault) {
@@ -242,7 +318,8 @@
                                         kOriginTrialFreezePolicyTestPath,
                                         kOriginTrialDefaultPage},
                                        "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kDefault);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kDefault,
+                                 ot_change_counter(), 0);
 }
 
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest,
@@ -251,17 +328,18 @@
       browser(), GURL(base::JoinString({kOriginTrialTestHostname, k2iFramesPath,
                                         kOriginTrialOptInOptOut},
                                        "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut,
+                                 ot_change_counter(), 2);
 }
 
-// Flaky. https://crbug.com/1014282
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest,
-                       DISABLED_PageFreezeOriginTrialOptOutOptIn) {
+                       PageFreezeOriginTrialOptOutOptIn) {
   ui_test_utils::NavigateToURL(
       browser(), GURL(base::JoinString({kOriginTrialTestHostname, k2iFramesPath,
                                         kOriginTrialOptOutOptIn},
                                        "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut,
+                                 ot_change_counter(), 2);
 }
 
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest,
@@ -270,7 +348,8 @@
       browser(), GURL(base::JoinString({kOriginTrialTestHostname, k2iFramesPath,
                                         kOriginTrialDefaultOptIn},
                                        "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptIn);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptIn,
+                                 ot_change_counter(), 1);
 }
 
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest,
@@ -279,7 +358,8 @@
       browser(), GURL(base::JoinString({kOriginTrialTestHostname, k2iFramesPath,
                                         kOriginTrialDefaultOptOut},
                                        "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut,
+                                 ot_change_counter(), 1);
 }
 
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest,
@@ -288,7 +368,8 @@
       browser(), GURL(base::JoinString({kOriginTrialTestHostname, k2iFramesPath,
                                         kOriginTrialOptInOptIn},
                                        "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptIn);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptIn,
+                                 ot_change_counter(), 2);
 }
 
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest,
@@ -297,7 +378,8 @@
       browser(), GURL(base::JoinString({kOriginTrialTestHostname, k2iFramesPath,
                                         kOriginTrialOptOutOptOut},
                                        "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kOptOut,
+                                 ot_change_counter(), 2);
 }
 
 IN_PROC_BROWSER_TEST_F(PageNodeImplBrowserTest,
@@ -306,7 +388,8 @@
       browser(), GURL(base::JoinString({kOriginTrialTestHostname, k2iFramesPath,
                                         kOriginTrialDefaultDefault},
                                        "/")));
-  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kDefault);
+  RunOriginTrialTestOnPMSequence(mojom::InterventionPolicy::kDefault,
+                                 ot_change_counter(), 0);
 }
 
 // TODO(sebmarchand): Add more tests, e.g. a test where the main frame and a
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 243c4f2..01de928 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -430,6 +430,17 @@
      */
     public static final String OFFLINE_INDICATOR_V2_ENABLED = "offline_indicator_v2_enabled";
 
+    /** Prefix of the preferences to persist use count of the payment instruments. */
+    public static final KeyPrefix PAYMENTS_PAYMENT_INSTRUMENT_USE_COUNT =
+            new KeyPrefix("payment_instrument_use_count_*");
+
+    /** Prefix of the preferences to persist last use date of the payment instruments. */
+    public static final KeyPrefix PAYMENTS_PAYMENT_INSTRUMENT_USE_DATE =
+            new KeyPrefix("payment_instrument_use_date_*");
+
+    /** Preference to indicate whether payment request has been completed successfully once.*/
+    public static final String PAYMENTS_PAYMENT_COMPLETE_ONCE = "payment_complete_once";
+
     public static final String PRIVACY_METRICS_REPORTING = "metrics_reporting";
     public static final String PRIVACY_METRICS_IN_SAMPLE = "in_metrics_sample";
     public static final String PRIVACY_NETWORK_PREDICTIONS = "network_predictions";
@@ -733,6 +744,7 @@
                 LOCALE_MANAGER_SEARCH_ENGINE_PROMO_SHOW_STATE,
                 LOCALE_MANAGER_WAS_IN_SPECIAL_LOCALE,
                 OFFLINE_INDICATOR_V2_ENABLED,
+                PAYMENTS_PAYMENT_COMPLETE_ONCE,
                 PRIVACY_ALLOW_PRERENDER_OLD,
                 PRIVACY_BANDWIDTH_NO_CELLULAR_OLD,
                 PRIVACY_BANDWIDTH_OLD,
@@ -776,7 +788,12 @@
 
     @CheckDiscard("Validation is performed in tests and in debug builds.")
     static List<KeyPrefix> createGrandfatheredPrefixesInUse() {
-        return Collections.EMPTY_LIST;
+        // clang-format off
+        return Arrays.asList(
+                PAYMENTS_PAYMENT_INSTRUMENT_USE_COUNT,
+                PAYMENTS_PAYMENT_INSTRUMENT_USE_DATE
+        );
+        // clang-format on
     }
 
     @CheckDiscard("Validation is performed in tests and in debug builds.")
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel.js
index 3bea1ab..09818875 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel.js
@@ -157,6 +157,18 @@
 
   /** @type {Window} */
   Panel.ownerWindow = window;
+
+  /**
+   * @type {boolean}
+   * @private
+   */
+  Panel.menuSearchEnabled_ = false;
+
+  chrome.commandLinePrivate.hasSwitch(
+      'enable-experimental-accessibility-chromevox-search-menus',
+      function(enabled) {
+        Panel.menuSearchEnabled_ = enabled;
+      });
 };
 
 /**
@@ -302,8 +314,9 @@
   Panel.clearMenus();
   Panel.pendingCallback_ = null;
 
-  // Build the top-level menus.
-  var searchMenu = Panel.addSearchMenu('panel_search_menu');
+  var searchMenu = (Panel.menuSearchEnabled_) ?
+      Panel.addSearchMenu('panel_search_menu') :
+      null;
   var jumpMenu = Panel.addMenu('panel_menu_jump');
   var speechMenu = Panel.addMenu('panel_menu_speech');
   var tabsMenu = Panel.addMenu('panel_menu_tabs');
@@ -488,7 +501,8 @@
   }
 
   // Activate either the specified menu or the search menu.
-  var selectedMenu = Panel.searchMenu;
+  // Search menu can be null, since it is hidden behind a flag.
+  var selectedMenu = Panel.searchMenu || Panel.menus_[0];
   for (var i = 0; i < Panel.menus_.length; i++) {
     if (Panel.menus_[i].menuMsg == opt_activateMenuTitle) {
       selectedMenu = Panel.menus_[i];
@@ -868,7 +882,7 @@
   // If left/right arrow are pressed, we should adjust the search bar's cursor.
   // We only want to advance the active menu if we are at the beginning/end of
   // the search bar's contents.
-  if (event.target == Panel.searchMenu.searchBar) {
+  if (Panel.searchMenu && event.target == Panel.searchMenu.searchBar) {
     switch (event.key) {
       case 'ArrowLeft':
       case 'ArrowRight':
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js
index 87241d8b..933fa46 100644
--- a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js
+++ b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js
@@ -2,32 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @fileoverview
- * This defines some types for settings-date-time-page.
- */
+cr.define('settings', function() {
+  /**
+   * Describes the effective policy restriction on time zone automatic
+   * detection.
+   * @enum {number}
+   */
+  const TimeZoneAutoDetectPolicyRestriction = {
+    NONE: 0,
+    FORCED_ON: 1,
+    FORCED_OFF: 2,
+  };
 
-cr.exportPath('settings');
+  /**
+   * Describes values of
+   * prefs.generated.resolve_timezone_by_geolocation_method_short. Must be kept
+   * in sync with TimeZoneResolverManager::TimeZoneResolveMethod enum.
+   * @enum {number}
+   */
+  const TimeZoneAutoDetectMethod = {
+    DISABLED: 0,
+    IP_ONLY: 1,
+    SEND_WIFI_ACCESS_POINTS: 2,
+    SEND_ALL_LOCATION_INFO: 3
+  };
 
-/**
- * Describes the effective policy restriction on time zone automatic detection.
- * @enum {number}
- */
-settings.TimeZoneAutoDetectPolicyRestriction = {
-  NONE: 0,
-  FORCED_ON: 1,
-  FORCED_OFF: 2,
-};
-
-/**
- * Describes values of
- * prefs.generated.resolve_timezone_by_geolocation_method_short. Must be kept in
- * sync with TimeZoneResolverManager::TimeZoneResolveMethod enum.
- * @enum {number}
- */
-settings.TimeZoneAutoDetectMethod = {
-  DISABLED: 0,
-  IP_ONLY: 1,
-  SEND_WIFI_ACCESS_POINTS: 2,
-  SEND_ALL_LOCATION_INFO: 3
-};
+  return {TimeZoneAutoDetectPolicyRestriction, TimeZoneAutoDetectMethod};
+});
diff --git a/chrome/browser/resources/settings/chromeos/device_page/keyboard.js b/chrome/browser/resources/settings/chromeos/device_page/keyboard.js
index 36fc0e4..aade607 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/keyboard.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/keyboard.js
@@ -2,27 +2,25 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @fileoverview
- * 'settings-keyboard' is the settings subpage with keyboard settings.
- */
-cr.exportPath('settings');
+cr.define('settings', function() {
+  /**
+   * Modifier key IDs corresponding to the ModifierKey enumerators in
+   * /ui/base/ime/chromeos/ime_keyboard.h.
+   * @enum {number}
+   */
+  const ModifierKey = {
+    SEARCH_KEY: 0,
+    CONTROL_KEY: 1,
+    ALT_KEY: 2,
+    VOID_KEY: 3,  // Represents a disabled key.
+    CAPS_LOCK_KEY: 4,
+    ESCAPE_KEY: 5,
+    BACKSPACE_KEY: 6,
+    ASSISTANT_KEY: 7,
+  };
 
-/**
- * Modifier key IDs corresponding to the ModifierKey enumerators in
- * /ui/base/ime/chromeos/ime_keyboard.h.
- * @enum {number}
- */
-settings.ModifierKey = {
-  SEARCH_KEY: 0,
-  CONTROL_KEY: 1,
-  ALT_KEY: 2,
-  VOID_KEY: 3,  // Represents a disabled key.
-  CAPS_LOCK_KEY: 4,
-  ESCAPE_KEY: 5,
-  BACKSPACE_KEY: 6,
-  ASSISTANT_KEY: 7,
-};
+  return {ModifierKey};
+});
 
 Polymer({
   is: 'settings-keyboard',
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_page_browser_proxy.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_page_browser_proxy.js
index f39bf914..2f0cfcf 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_page_browser_proxy.js
@@ -3,8 +3,6 @@
 // found in the LICENSE file.
 
 /** @fileoverview A helper object used for Internet page. */
-cr.exportPath('settings');
-
 cr.define('settings', function() {
   /** @interface */
   class InternetPageBrowserProxy {
@@ -79,8 +77,5 @@
 
   cr.addSingletonGetter(InternetPageBrowserProxyImpl);
 
-  return {
-    InternetPageBrowserProxy: InternetPageBrowserProxy,
-    InternetPageBrowserProxyImpl: InternetPageBrowserProxyImpl,
-  };
+  return {InternetPageBrowserProxy, InternetPageBrowserProxyImpl};
 });
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
index af5d325d..127c148 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
@@ -7,7 +7,6 @@
  * intended to facilitate passing data between elements in the MultiDevice page
  * cleanly and concisely. It includes some constants and utility methods.
  */
-cr.exportPath('settings');
 
 /** @polymerBehavior */
 const MultiDeviceFeatureBehaviorImpl = {
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
index ee7e8fc..edfab67 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
@@ -10,8 +10,6 @@
  * information relevant to the individual feature, such as a route to the
  * feature's autonomous page if there is one.
  */
-cr.exportPath('settings');
-
 Polymer({
   is: 'settings-multidevice-feature-item',
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
index ef3b0df..61413f9 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
@@ -2,12 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @fileoverview
- * Settings page for managing MultiDevice features.
- */
-cr.exportPath('settings');
-
 Polymer({
   is: 'settings-multidevice-page',
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_smartlock_subpage.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_smartlock_subpage.js
index 7d93d87..7c96b0e 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_smartlock_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_smartlock_subpage.js
@@ -2,12 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @fileoverview
- * Subpage of settings-multidevice-feature for managing the Smart Lock feature.
- */
-cr.exportPath('settings');
-
 cr.define('settings', function() {
   /**
    * The state of the preference controlling Smart Lock's ability to sign-in the
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
index d0c88fb..4cefcbf 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
@@ -7,8 +7,6 @@
  * Subpage of settings-multidevice-page for managing multidevice features
  * individually and for forgetting a host.
  */
-cr.exportPath('settings');
-
 Polymer({
   is: 'settings-multidevice-subpage',
 
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.js b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.js
index f7e5135..adfc829a 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.js
@@ -6,13 +6,15 @@
  * @fileoverview 'os-settings-languages-page' is the settings sub-page
  * for language and input method settings.
  */
-cr.exportPath('settings');
+cr.define('settings', function() {
+  /**
+   * @type {number} Millisecond delay that can be used when closing an action
+   *      menu to keep it briefly on-screen.
+   */
+  const kMenuCloseDelay = 100;
 
-/**
- * @type {number} Millisecond delay that can be used when closing an action
- *      menu to keep it briefly on-screen.
- */
-settings.kMenuCloseDelay = 100;
+  return {kMenuCloseDelay};
+});
 
 (function() {
 'use strict';
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html
index bdd8c736..36008b5c 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html
@@ -64,21 +64,47 @@
           </cr-input>
         </div>
       </div>
-      <div slot="dialog-buttons">
-        <div>
-          <cr-button class="cancel-button" on-click="onCancelTap_">
-            $i18n{cancel}
-          </cr-button>
+      <template is="dom-if" if="[[printServersUiEnabled]]">
+        <div slot="dialog-buttons">
+          <!-- Left Group -->
+          <div>
+            <cr-button id="print-server-button" on-click="onPrintServerTap_">
+              $i18n{printServerButtonText}
+            </cr-button>
+          </div>
+          <!-- Right Group -->
+          <div>
+            <cr-button class="cancel-button" on-click="onCancelTap_">
+              $i18n{cancel}
+            </cr-button>
+            <cr-button id="addPrinterButton" class="action-button"
+                on-click="addPressed_"
+                disabled="[[!canAddPrinter_(newPrinter.*,
+                                            addPrinterInProgress_)]]">
+              $i18n{addPrinterButtonText}
+            </cr-button>
+          </div>
         </div>
-        <div>
-          <cr-button id="addPrinterButton" class="action-button"
-              on-click="addPressed_"
-              disabled="[[!canAddPrinter_(newPrinter.*,
-                                          addPrinterInProgress_)]]">
-            $i18n{addPrinterButtonText}
-          </cr-button>
+      </template>
+      <template is="dom-if" if="[[!printServersUiEnabled]]">
+        <div slot="dialog-buttons">
+          <!-- Left Group -->
+          <div>
+            <cr-button class="cancel-button" on-click="onCancelTap_">
+              $i18n{cancel}
+            </cr-button>
+          </div>
+          <!-- Right Group -->
+          <div>
+            <cr-button id="addPrinterButton" class="action-button"
+                on-click="addPressed_"
+                disabled="[[!canAddPrinter_(newPrinter.*,
+                                            addPrinterInProgress_)]]">
+              $i18n{addPrinterButtonText}
+            </cr-button>
+          </div>
         </div>
-      </div>
+      </template>
     </add-printer-dialog>
   </template>
 </dom-module>
@@ -164,13 +190,56 @@
   </template>
 </dom-module>
 
+<dom-module id="add-print-server-dialog">
+  <template>
+    <style include="cups-printer-shared">
+      [slot=dialog-body] {
+        height: 90px;
+      }
+      [slot=dialog-buttons] {
+        width: auto;
+      }
+    </style>
+    <add-printer-dialog id="printServerDialog">
+      <div slot="dialog-title">
+        $i18n{addPrintServerTitle}
+        <printer-dialog-error id="server-dialog-error"
+            error-text="[[errorText_]]">
+        </printer-dialog-error>
+      </div>
+      <div slot="dialog-body">
+        <div class="settings-box first two-line">
+          <cr-input id="printServerAddressInput"
+              label="$i18n{printServerAddress}"
+              value="{{printServerAddress_}}"
+              error-message="$i18n{printServerInvalidUrlAddress}"
+              on-keypress="onKeypress_"
+              maxlength=63 autofocus>
+          </cr-input>
+        </div>
+      </div>
+      <div slot="dialog-buttons">
+        <cr-button class="cancel-button" on-click="onCancelTap_">
+          $i18n{cancel}
+        </cr-button>
+        <cr-button class="action-button"
+            on-click="onAddPrintServerTap_"
+            disabled="[[inProgress_]]">
+          $i18n{addPrinterButtonText}
+        </cr-button>
+      </div>
+    </add-printer-dialog>
+  </template>
+</dom-module>
+
 <dom-module id="settings-cups-add-printer-dialog">
   <template>
     <style include="settings-shared"></style>
 
     <!-- Manually Add Printer Dialog -->
     <template is="dom-if" if="[[showManuallyAddDialog_]]" restamp>
-      <add-printer-manually-dialog new-printer="{{newPrinter}}">
+      <add-printer-manually-dialog new-printer="{{newPrinter}}"
+          print-servers-ui-enabled="[[printServersUiEnabled_]]">
       </add-printer-manually-dialog>
     </template>
 
@@ -180,6 +249,13 @@
       </add-printer-manufacturer-model-dialog>
     </template>
 
+    <!-- Add Print Server Dialog -->
+    <template is="dom-if" if="[[printServersUiEnabled_]]">
+      <template is="dom-if" if="[[showAddPrintServerDialog_]]" restamp>
+        <add-print-server-dialog></add-print-server-dialog>
+      </template>
+    </template>
+
   </template>
   <script src="cups_add_printer_dialog.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
index 10bcc64..7af4551 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
@@ -19,6 +19,7 @@
 const AddPrinterDialogs = {
   MANUALLY: 'add-printer-manually-dialog',
   MANUFACTURER: 'add-printer-manufacturer-model-dialog',
+  PRINTSERVER: 'add-print-server-dialog',
 };
 
 /**
@@ -69,6 +70,9 @@
       type: String,
       value: '',
     },
+
+    /** @type {boolean} */
+    printServersUiEnabled: Boolean,
   },
 
   observers: [
@@ -168,6 +172,12 @@
     }
   },
 
+  /** @private */
+  onPrintServerTap_: function() {
+    this.$$('add-printer-dialog').close();
+    this.fire('open-add-print-server-dialog');
+  },
+
   /**
    * @param {!Event} event
    * @private
@@ -441,6 +451,84 @@
 });
 
 Polymer({
+  is: 'add-print-server-dialog',
+
+  properties: {
+    /** @private {string} */
+    printServerAddress_: {
+      type: String,
+      value: '',
+    },
+
+    /** @private {string} */
+    errorText_: {
+      type: String,
+      value: '',
+    },
+
+    /** @private {boolean} */
+    inProgress_: {
+      type: Boolean,
+      value: false,
+    },
+  },
+
+  /** @private */
+  onCancelTap_: function() {
+    this.$$('add-printer-dialog').close();
+  },
+
+  /** @private */
+  onAddPrintServerTap_: function() {
+    this.inProgress_ = true;
+    this.$$('#printServerAddressInput').invalid = false;
+    settings.CupsPrintersBrowserProxyImpl.getInstance()
+        .queryPrintServer(this.printServerAddress_)
+        .then(
+            this.onPrintServerAddedSucceeded_.bind(this),
+            this.onPrintServerAddedFailed_.bind(this));
+  },
+
+  /**
+   * @param {!CupsPrintersList} printers
+   * @private
+   */
+  onPrintServerAddedSucceeded_: function(printers) {
+    this.inProgress_ = false;
+    this.fire('show-cups-print-server-toast', {printers: printers});
+    this.$$('add-printer-dialog').close();
+  },
+
+  /**
+   * @param {*} addPrintServerError
+   * @private
+   */
+  onPrintServerAddedFailed_: function(addPrintServerError) {
+    this.inProgress_ = false;
+    if (addPrintServerError == PrintServerResult.INCORRECT_URL) {
+      this.$$('#printServerAddressInput').invalid = true;
+      return;
+    }
+    this.errorText_ = settings.printing.getPrintServerErrorText(
+        /** @type {PrintServerResult} */ (addPrintServerError));
+  },
+
+  /**
+   * Keypress event handler. If enter is pressed, trigger the add event.
+   * @param {!Event} event
+   * @private
+   */
+  onKeypress_: function(event) {
+    if (event.key != 'Enter') {
+      return;
+    }
+    event.stopPropagation();
+
+    this.onAddPrintServerTap_();
+  },
+});
+
+Polymer({
   is: 'settings-cups-add-printer-dialog',
 
   properties: {
@@ -466,12 +554,27 @@
       type: Boolean,
       value: false,
     },
+
+    /** @private {boolean} */
+    showAddPrintServerDialog_: {
+      type: Boolean,
+      value: false,
+    },
+
+    /** @private {boolean} */
+    printServersUiEnabled_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('consumerPrintServerUiEnabled');
+      }
+    },
   },
 
   listeners: {
     'open-manually-add-printer-dialog': 'openManuallyAddPrinterDialog_',
     'open-manufacturer-model-dialog':
         'openManufacturerModelDialogForCurrentPrinter_',
+    'open-add-print-server-dialog': 'openPrintServerDialog_',
     'no-detected-printer': 'onNoDetectedPrinter_',
   },
 
@@ -513,6 +616,13 @@
         '', AddPrinterDialogs.MANUFACTURER, 'showManufacturerDialog_');
   },
 
+  /** @private */
+  openPrintServerDialog_: function() {
+    this.switchDialog_(
+        this.currentDialog_, AddPrinterDialogs.PRINTSERVER,
+        'showAddPrintServerDialog_');
+  },
+
   /**
    * Switch dialog from |fromDialog| to |toDialog|.
    * @param {string} fromDialog
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_util.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_util.js
index 46b1be62..336c9b9 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_util.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_util.js
@@ -142,6 +142,23 @@
   }
 
   /**
+   * Return the error string corresponding to the result code for print servers.
+   * @param {!PrintServerResult} result
+   * @return {string}
+   */
+  function getPrintServerErrorText(result) {
+    switch (result) {
+      case PrintServerResult.CONNECTION_ERROR:
+        return loadTimeData.getString('printServerConnectionError');
+      case PrintServerResult.CANNOT_PARSE_IPP_RESPONSE:
+      case PrintServerResult.HTTP_ERROR:
+        return loadTimeData.getString('printServerConfigurationErrorMessage');
+      default:
+        assertNotReached();
+    }
+  }
+
+  /**
    * We sort by printer type, which is based off of a maintained list in
    * cups_printers_types.js. If the types are the same, we sort alphabetically.
    * @param {!PrinterListEntry} first
@@ -182,6 +199,7 @@
     getBaseName: getBaseName,
     alphabeticalSort: alphabeticalSort,
     getErrorText: getErrorText,
+    getPrintServerErrorText: getPrintServerErrorText,
     sortPrinters: sortPrinters,
     matchesSearchTerm: matchesSearchTerm,
     arePrinterIdsEqual: arePrinterIdsEqual,
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.html b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.html
index f6f808a..509c42b 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.html
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.html
@@ -170,6 +170,12 @@
         [[addPrinterResultText_]]
       </div>
     </cr-toast>
+
+    <cr-toast id="printServerErrorToast" duration="3000" role="alert">
+      <div class="error-message" id="addPrintServerDoneMessage">
+        [[addPrintServerResultText_]]
+      </div>
+    </cr-toast>
   </template>
   <script src="cups_printers.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
index 21afac5..4233385 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
@@ -84,6 +84,7 @@
   listeners: {
     'edit-cups-printer-details': 'onShowCupsEditPrinterDialog_',
     'show-cups-printer-toast': 'openResultToast_',
+    'show-cups-print-server-toast': 'openPrintServerResultToast_',
     'open-manufacturer-model-dialog-for-specified-printer':
         'openManufacturerModelDialogForSpecifiedPrinter_',
   },
@@ -184,6 +185,27 @@
   },
 
   /**
+   * @param {!CustomEvent<!{
+   *      printers: !CupsPrintersList
+   * }>} event
+   * @private
+   */
+  openPrintServerResultToast_: function(event) {
+    const length = event.detail.printers.printerList.length;
+    if (length === 0) {
+      this.addPrintServerResultText_ =
+          loadTimeData.getString('printServerFoundZeroPrinters');
+    } else if (length === 1) {
+      this.addPrintServerResultText_ =
+          loadTimeData.getString('printServerFoundOnePrinter');
+    } else {
+      this.addPrintServerResultText_ =
+          loadTimeData.getStringF('printServerFoundManyPrinters', length);
+    }
+    this.$.printServerErrorToast.show();
+  },
+
+  /**
    * @param {!CustomEvent<{item: !CupsPrinterInfo}>} e
    * @private
    */
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_browser_proxy.js
index 0bb822d0..87c88ca 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_browser_proxy.js
@@ -102,6 +102,19 @@
 };
 
 /**
+ *  @enum {number}
+ *  These values must be kept in sync with the PrintServerQueryResult enum in
+ *  /chrome/browser/chromeos/printing/server_printers_fetcher.h
+ */
+const PrintServerResult = {
+  NO_ERRORS: 0,
+  INCORRECT_URL: 1,
+  CONNECTION_ERROR: 2,
+  HTTP_ERROR: 3,
+  CANNOT_PARSE_IPP_RESPONSE: 4,
+};
+
+/**
  * @typedef {{
  *   message: string
  * }}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
index ec6c0a4..c5935d7e2 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
@@ -10,12 +10,16 @@
  *
  *    <settings-ui prefs="{{prefs}}"></settings-ui>
  */
-cr.exportPath('settings');
-assert(
-    !settings.defaultResourceLoaded,
-    'settings_ui.js run twice. You probably have an invalid import.');
-/** Global defined when the main Settings script runs. */
-settings.defaultResourceLoaded = true;
+cr.define('settings', function() {
+  /** Global defined when the main Settings script runs. */
+  let defaultResourceLoaded = true;  // eslint-disable-line prefer-const
+
+  assert(
+      !window.settings || !settings.defaultResourceLoaded,
+      'settings_ui.js run twice. You probably have an invalid import.');
+
+  return {defaultResourceLoaded};
+});
 
 Polymer({
   is: 'os-settings-ui',
diff --git a/chrome/browser/resources/settings/languages_page/languages.js b/chrome/browser/resources/settings/languages_page/languages.js
index d72be5f..f54aeba 100644
--- a/chrome/browser/resources/settings/languages_page/languages.js
+++ b/chrome/browser/resources/settings/languages_page/languages.js
@@ -13,8 +13,6 @@
 (function() {
 'use strict';
 
-cr.exportPath('settings');
-
 const MoveType = chrome.languageSettingsPrivate.MoveType;
 
 // Translate server treats some language codes the same.
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.js b/chrome/browser/resources/settings/languages_page/languages_page.js
index d857837..59ac7cb 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.js
+++ b/chrome/browser/resources/settings/languages_page/languages_page.js
@@ -6,13 +6,15 @@
  * @fileoverview 'settings-languages-page' is the settings page
  * for language and input method settings.
  */
-cr.exportPath('settings');
+cr.define('settings', function() {
+  /**
+   * @type {number} Millisecond delay that can be used when closing an action
+   *      menu to keep it briefly on-screen.
+   */
+  const kMenuCloseDelay = 100;
 
-/**
- * @type {number} Millisecond delay that can be used when closing an action
- *      menu to keep it briefly on-screen.
- */
-settings.kMenuCloseDelay = 100;
+  return {kMenuCloseDelay};
+});
 
 /**
  * Name of the language setting is shown uma histogram.
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js b/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js
index 5d8c41e..4d9bd998 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js
+++ b/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js
@@ -8,14 +8,16 @@
  * the user to edit/remove the entry.
  */
 
-cr.exportPath('settings');
+cr.define('settings', function() {
+  /**
+   * The name of the event fired from this element when the "Edit" option is
+   * clicked.
+   * @type {string}
+   */
+  const EDIT_STARTUP_URL_EVENT = 'edit-startup-url';
 
-/**
- * The name of the event fired from this element when the "Edit" option is
- * clicked.
- * @type {string}
- */
-settings.EDIT_STARTUP_URL_EVENT = 'edit-startup-url';
+  return {EDIT_STARTUP_URL_EVENT};
+});
 
 Polymer({
   is: 'settings-startup-url-entry',
diff --git a/chrome/browser/resources/settings/open_window_proxy.js b/chrome/browser/resources/settings/open_window_proxy.js
index 5aab78c..1c75d94 100644
--- a/chrome/browser/resources/settings/open_window_proxy.js
+++ b/chrome/browser/resources/settings/open_window_proxy.js
@@ -7,8 +7,6 @@
  * the browser.
  */
 
-cr.exportPath('settings');
-
 cr.define('settings', function() {
   /** @interface */
   class OpenWindowProxy {
@@ -29,8 +27,5 @@
 
   cr.addSingletonGetter(OpenWindowProxyImpl);
 
-  return {
-    OpenWindowProxy: OpenWindowProxy,
-    OpenWindowProxyImpl: OpenWindowProxyImpl,
-  };
+  return {OpenWindowProxy, OpenWindowProxyImpl};
 });
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.js b/chrome/browser/resources/settings/people_page/sync_account_control.js
index c0b03ef..6b5f854 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.js
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.js
@@ -6,10 +6,12 @@
  * 'settings-sync-account-section' is the settings page containing sign-in
  * settings.
  */
-cr.exportPath('settings');
+cr.define('settings', function() {
+  /** @const {number} */
+  const MAX_SIGNIN_PROMO_IMPRESSION = 10;
 
-/** @const {number} */
-settings.MAX_SIGNIN_PROMO_IMPRESSION = 10;
+  return {MAX_SIGNIN_PROMO_IMPRESSION};
+});
 
 Polymer({
   is: 'settings-sync-account-control',
diff --git a/chrome/browser/resources/settings/settings_page/main_page_behavior.js b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
index 8b153417..ca63507 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
@@ -2,29 +2,25 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.exportPath('settings');
-
-/**
- * @enum {string}
- * A categorization of every possible Settings URL, necessary for implementing
- * a finite state machine.
- */
-settings.RouteState = {
-  // Initial state before anything has loaded yet.
-  INITIAL: 'initial',
-  // A dialog that has a dedicated URL (e.g. /importData).
-  DIALOG: 'dialog',
-  // A section (basically a scroll position within the top level page, e.g,
-  // /appearance.
-  SECTION: 'section',
-  // A subpage, or sub-subpage e.g, /searchEngins.
-  SUBPAGE: 'subpage',
-  // The top level Settings page, '/'.
-  TOP_LEVEL: 'top-level',
-};
-
 cr.define('settings', function() {
-  const RouteState = settings.RouteState;
+  /**
+   * @enum {string}
+   * A categorization of every possible Settings URL, necessary for implementing
+   * a finite state machine.
+   */
+  const RouteState = {
+    // Initial state before anything has loaded yet.
+    INITIAL: 'initial',
+    // A dialog that has a dedicated URL (e.g. /importData).
+    DIALOG: 'dialog',
+    // A section (basically a scroll position within the top level page, e.g,
+    // /appearance.
+    SECTION: 'section',
+    // A subpage, or sub-subpage e.g, /searchEngins.
+    SUBPAGE: 'subpage',
+    // The top level Settings page, '/'.
+    TOP_LEVEL: 'top-level',
+  };
 
   /**
    * @param {?settings.Route} route
@@ -394,5 +390,5 @@
     },
   };
 
-  return {MainPageBehavior: MainPageBehavior};
+  return {MainPageBehavior, RouteState};
 });
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 9fdf003..ef7d569 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -10,12 +10,16 @@
  *
  *    <settings-ui prefs="{{prefs}}"></settings-ui>
  */
-cr.exportPath('settings');
-assert(
-    !settings.defaultResourceLoaded,
-    'settings_ui.js run twice. You probably have an invalid import.');
-/** Global defined when the main Settings script runs. */
-settings.defaultResourceLoaded = true;
+cr.define('settings', function() {
+  /** Defined when the main Settings script runs. */
+  let defaultResourceLoaded = true;  // eslint-disable-line prefer-const
+
+  assert(
+      !window.settings || !window.settings.defaultResourceLoaded,
+      'settings_ui.js run twice. You probably have an invalid import.');
+
+  return {defaultResourceLoaded};
+});
 
 Polymer({
   is: 'settings-ui',
diff --git a/chrome/browser/resources/settings/site_settings/constants.js b/chrome/browser/resources/settings/site_settings/constants.js
index 09988539..5f15cfa 100644
--- a/chrome/browser/resources/settings/site_settings/constants.js
+++ b/chrome/browser/resources/settings/site_settings/constants.js
@@ -2,134 +2,146 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.exportPath('settings');
+cr.define('settings', function() {
+  /**
+   * All possible contentSettingsTypes that we currently support configuring in
+   * the UI. Both top-level categories and content settings that represent
+   * individual permissions under Site Details should appear here.
+   * This should be kept in sync with the |kContentSettingsTypeGroupNames| array
+   * in chrome/browser/ui/webui/site_settings_helper.cc
+   * @enum {string}
+   */
+  const ContentSettingsTypes = {
+    COOKIES: 'cookies',
+    IMAGES: 'images',
+    JAVASCRIPT: 'javascript',
+    SOUND: 'sound',
+    PLUGINS: 'plugins',  // AKA Flash.
+    POPUPS: 'popups',
+    GEOLOCATION: 'location',
+    NOTIFICATIONS: 'notifications',
+    MIC: 'media-stream-mic',  // AKA Microphone.
+    CAMERA: 'media-stream-camera',
+    PROTOCOL_HANDLERS: 'register-protocol-handler',
+    UNSANDBOXED_PLUGINS: 'ppapi-broker',
+    AUTOMATIC_DOWNLOADS: 'multiple-automatic-downloads',
+    BACKGROUND_SYNC: 'background-sync',
+    MIDI_DEVICES: 'midi-sysex',
+    USB_DEVICES: 'usb-devices',
+    SERIAL_PORTS: 'serial-ports',
+    ZOOM_LEVELS: 'zoom-levels',
+    PROTECTED_CONTENT: 'protected-content',
+    ADS: 'ads',
+    CLIPBOARD: 'clipboard',
+    SENSORS: 'sensors',
+    PAYMENT_HANDLER: 'payment-handler',
+    MIXEDSCRIPT: 'mixed-script',
+    BLUETOOTH_SCANNING: 'bluetooth-scanning',
+    NATIVE_FILE_SYSTEM_WRITE: 'native-file-system-write',
+    HID_DEVICES: 'hid-devices',
+    AR: 'ar',
+    VR: 'vr',
+  };
 
-/**
- * All possible contentSettingsTypes that we currently support configuring in
- * the UI. Both top-level categories and content settings that represent
- * individual permissions under Site Details should appear here.
- * This should be kept in sync with the |kContentSettingsTypeGroupNames| array
- * in chrome/browser/ui/webui/site_settings_helper.cc
- * @enum {string}
- */
-settings.ContentSettingsTypes = {
-  COOKIES: 'cookies',
-  IMAGES: 'images',
-  JAVASCRIPT: 'javascript',
-  SOUND: 'sound',
-  PLUGINS: 'plugins',  // AKA Flash.
-  POPUPS: 'popups',
-  GEOLOCATION: 'location',
-  NOTIFICATIONS: 'notifications',
-  MIC: 'media-stream-mic',  // AKA Microphone.
-  CAMERA: 'media-stream-camera',
-  PROTOCOL_HANDLERS: 'register-protocol-handler',
-  UNSANDBOXED_PLUGINS: 'ppapi-broker',
-  AUTOMATIC_DOWNLOADS: 'multiple-automatic-downloads',
-  BACKGROUND_SYNC: 'background-sync',
-  MIDI_DEVICES: 'midi-sysex',
-  USB_DEVICES: 'usb-devices',
-  SERIAL_PORTS: 'serial-ports',
-  ZOOM_LEVELS: 'zoom-levels',
-  PROTECTED_CONTENT: 'protected-content',
-  ADS: 'ads',
-  CLIPBOARD: 'clipboard',
-  SENSORS: 'sensors',
-  PAYMENT_HANDLER: 'payment-handler',
-  MIXEDSCRIPT: 'mixed-script',
-  BLUETOOTH_SCANNING: 'bluetooth-scanning',
-  NATIVE_FILE_SYSTEM_WRITE: 'native-file-system-write',
-  HID_DEVICES: 'hid-devices',
-  AR: 'ar',
-  VR: 'vr',
-};
+  /**
+   * Contains the possible string values for a given ContentSettingsTypes.
+   * This should be kept in sync with the |ContentSetting| enum in
+   * components/content_settings/core/common/content_settings.h
+   * @enum {string}
+   */
+  const ContentSetting = {
+    DEFAULT: 'default',
+    ALLOW: 'allow',
+    BLOCK: 'block',
+    ASK: 'ask',
+    SESSION_ONLY: 'session_only',
+    IMPORTANT_CONTENT: 'detect_important_content',
+  };
 
-/**
- * Contains the possible string values for a given ContentSettingsTypes.
- * This should be kept in sync with the |ContentSetting| enum in
- * components/content_settings/core/common/content_settings.h
- * @enum {string}
- */
-settings.ContentSetting = {
-  DEFAULT: 'default',
-  ALLOW: 'allow',
-  BLOCK: 'block',
-  ASK: 'ask',
-  SESSION_ONLY: 'session_only',
-  IMPORTANT_CONTENT: 'detect_important_content',
-};
+  /**
+   * All possible ChooserTypes that we currently support configuring in the UI.
+   * This should be kept in sync with the |kChooserTypeGroupNames| array in
+   * chrome/browser/ui/webui/site_settings_helper.cc
+   * @enum {string}
+   */
+  const ChooserType = {
+    NONE: '',
+    USB_DEVICES: 'usb-devices-data',
+    SERIAL_PORTS: 'serial-ports-data',
+    HID_DEVICES: 'hid-devices-data',
+  };
 
-/**
- * All possible ChooserTypes that we currently support configuring in the UI.
- * This should be kept in sync with the |kChooserTypeGroupNames| array in
- * chrome/browser/ui/webui/site_settings_helper.cc
- * @enum {string}
- */
-settings.ChooserType = {
-  NONE: '',
-  USB_DEVICES: 'usb-devices-data',
-  SERIAL_PORTS: 'serial-ports-data',
-  HID_DEVICES: 'hid-devices-data',
-};
+  /**
+   * Contains the possible sources of a ContentSetting.
+   * This should be kept in sync with the |SiteSettingSource| enum in
+   * chrome/browser/ui/webui/site_settings_helper.h
+   * @enum {string}
+   */
+  const SiteSettingSource = {
+    ADS_FILTER_BLACKLIST: 'ads-filter-blacklist',
+    DEFAULT: 'default',
+    // This source is for the Protected Media Identifier / Protected Content
+    // content setting only, which is only available on ChromeOS.
+    DRM_DISABLED: 'drm-disabled',
+    EMBARGO: 'embargo',
+    EXTENSION: 'extension',
+    INSECURE_ORIGIN: 'insecure-origin',
+    KILL_SWITCH: 'kill-switch',
+    POLICY: 'policy',
+    PREFERENCE: 'preference',
+  };
 
-/**
- * Contains the possible sources of a ContentSetting.
- * This should be kept in sync with the |SiteSettingSource| enum in
- * chrome/browser/ui/webui/site_settings_helper.h
- * @enum {string}
- */
-settings.SiteSettingSource = {
-  ADS_FILTER_BLACKLIST: 'ads-filter-blacklist',
-  DEFAULT: 'default',
-  // This source is for the Protected Media Identifier / Protected Content
-  // content setting only, which is only available on ChromeOS.
-  DRM_DISABLED: 'drm-disabled',
-  EMBARGO: 'embargo',
-  EXTENSION: 'extension',
-  INSECURE_ORIGIN: 'insecure-origin',
-  KILL_SWITCH: 'kill-switch',
-  POLICY: 'policy',
-  PREFERENCE: 'preference',
-};
+  /**
+   * A category value to use for the All Sites list.
+   * @type {string}
+   */
+  const ALL_SITES = 'all-sites';
 
-/**
- * A category value to use for the All Sites list.
- * @type {string}
- */
-settings.ALL_SITES = 'all-sites';
+  /**
+   * An invalid subtype value.
+   * @type {string}
+   */
+  const INVALID_CATEGORY_SUBTYPE = '';
 
-/**
- * An invalid subtype value.
- * @type {string}
- */
-settings.INVALID_CATEGORY_SUBTYPE = '';
+  /**
+   * Contains the possible record action types.
+   * This should be kept in sync with the |AllSitesAction| enum in
+   * chrome/browser/ui/webui/settings/site_settings_handler.cc
+   * @enum {number}
+   */
+  const AllSitesAction = {
+    LOAD_PAGE: 0,
+    RESET_PERMISSIONS: 1,
+    CLEAR_DATA: 2,
+    ENTER_SITE_DETAILS: 3,
+  };
 
-/**
- * Contains the possible record action types.
- * This should be kept in sync with the |AllSitesAction| enum in
- * chrome/browser/ui/webui/settings/site_settings_handler.cc
- * @enum {number}
- */
-settings.AllSitesAction = {
-  LOAD_PAGE: 0,
-  RESET_PERMISSIONS: 1,
-  CLEAR_DATA: 2,
-  ENTER_SITE_DETAILS: 3,
-};
+  /**
+   * Contains the possible sort methods.
+   * @enum {string}
+   */
+  const SortMethod = {
+    NAME: 'name',
+    MOST_VISITED: 'most-visited',
+    STORAGE: 'data-stored',
+  };
 
-/**
- * Contains the possible sort methods.
- * @enum {string}
- */
-settings.SortMethod = {
-  NAME: 'name',
-  MOST_VISITED: 'most-visited',
-  STORAGE: 'data-stored',
-};
+  /**
+   * String representation of the wildcard used for universal
+   * match for SiteExceptions.
+   * @type {string}
+   */
+  const SITE_EXCEPTION_WILDCARD = '*';
 
-/**
- * String representation of the wildcard used for universal
- * match for SiteExceptions.
- * @type {string}
- */
-settings.SITE_EXCEPTION_WILDCARD = '*';
+  return {
+    ALL_SITES,
+    AllSitesAction,
+    ChooserType,
+    ContentSetting,
+    ContentSettingsTypes,
+    INVALID_CATEGORY_SUBTYPE,
+    SITE_EXCEPTION_WILDCARD,
+    SiteSettingSource,
+    SortMethod,
+  };
+});
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
index 0ff221d4..ca6c2587 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/task/post_task.h"
+#include "cc/paint/paint_flags.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/constrained_window/constrained_window_views.h"
@@ -14,8 +15,15 @@
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/controls/throbber.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/fill_layout.h"
 
 namespace safe_browsing {
 
@@ -33,12 +41,37 @@
 constexpr base::TimeDelta kResizeAnimationDuration =
     base::TimeDelta::FromMilliseconds(100);
 
-// TODO(domfc): Update these colors.
-constexpr SkColor kScanPendingColor = 0xff4285f4;
-constexpr SkColor kScanSuccessColor = 0xff34a853;
-constexpr SkColor kScanFailureColor = 0xffea4335;
+constexpr SkColor kScanPendingColor = gfx::kGoogleBlue500;
+constexpr SkColor kScanSuccessColor = gfx::kGoogleGreen500;
+constexpr SkColor kScanFailureColor = gfx::kGoogleRed500;
 
-constexpr int kImageSize = 100;
+constexpr SkColor kScanPendingSideImageColor = gfx::kGoogleBlue400;
+constexpr SkColor kScanDoneSideImageColor = SkColorSetRGB(0xFF, 0xFF, 0xFF);
+
+constexpr int kSideImageSize = 35;
+constexpr int kTopImageSize = 100;
+
+constexpr gfx::Insets kSideImageInsets = gfx::Insets(8, 8, 8, 8);
+constexpr gfx::Insets KSideIconLayoutInsets = gfx::Insets(0, 10);
+constexpr int kSideIconBetweenChildSpacing = 20;
+
+// A simple background class to show a colored circle behind the side icon once
+// the scanning is done.
+class CircleBackground : public views::Background {
+ public:
+  explicit CircleBackground(SkColor color) { SetNativeControlColor(color); }
+  ~CircleBackground() override = default;
+
+  void Paint(gfx::Canvas* canvas, views::View* view) const override {
+    int radius = view->bounds().width() / 2;
+    gfx::PointF center(radius, radius);
+    cc::PaintFlags flags;
+    flags.setAntiAlias(true);
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    flags.setColor(get_color());
+    canvas->DrawCircle(center, radius, flags);
+  }
+};
 
 }  // namespace
 
@@ -129,19 +162,31 @@
   // Update the buttons.
   SetupButtons();
 
-  // Update the image. Currently only the color changes.
+  // Update the top image. Currently only the color changes.
   image_->SetImage(gfx::CreateVectorIcon(vector_icons::kBusinessIcon,
-                                         kImageSize, GetImageColor()));
+                                         kTopImageSize, GetImageColor()));
 
-  // Update the message.
-  int old_text_lines = message_->GetRequiredLines();
+  // Update the side icon by changing it's image color, adding a background and
+  // removing the spinner.
+  side_icon_image_->SetImage(gfx::CreateVectorIcon(
+      vector_icons::kBusinessIcon, kSideImageSize, kScanDoneSideImageColor));
+  side_icon_image_->SetBackground(
+      std::make_unique<CircleBackground>(GetSideImageBackgroundColor()));
+  side_icon_spinner_->parent()->RemoveChildView(side_icon_spinner_);
+  delete side_icon_spinner_;
+
+  // Update the message. Change the text color only if the scan was negative.
+  if (!scan_success_.value())
+    message_->SetEnabledColor(kScanFailureColor);
   message_->SetText(GetDialogMessage());
 
   // Resize the dialog's height. This is needed since the button might be
   // removed (in the success case) and the text might take fewer or more lines.
-  int new_text_lines = message_->GetRequiredLines();
-  if (scan_success_.value() || (old_text_lines != new_text_lines))
-    Resize(new_text_lines - old_text_lines);
+  int text_height = message_->GetRequiredLines() * message_->GetLineHeight();
+  int row_height = message_->parent()->height();
+  int height_to_add = std::max(text_height - row_height, 0);
+  if (scan_success_.value() || (height_to_add > 0))
+    Resize(height_to_add);
 
   // Update the dialog.
   DialogDelegate::DialogModelChanged();
@@ -156,7 +201,7 @@
   }
 }
 
-void DeepScanningDialogViews::Resize(int lines_delta) {
+void DeepScanningDialogViews::Resize(int height_to_add) {
   DCHECK(scan_success_.has_value());
 
   gfx::Rect dialog_rect = widget_->GetContentsView()->GetContentsBounds();
@@ -173,7 +218,7 @@
   }
 
   // Apply the message lines delta.
-  new_height += (lines_delta * message_->GetLineHeight());
+  new_height += height_to_add;
   dialog_rect.set_height(new_height);
 
   // Setup the animation.
@@ -243,25 +288,41 @@
   views::GridLayout* layout =
       contents_view_->SetLayoutManager(std::make_unique<views::GridLayout>());
   views::ColumnSet* columns = layout->AddColumnSet(0);
-  columns->AddColumn(/*h_align=*/views::GridLayout::CENTER,
+  columns->AddColumn(/*h_align=*/views::GridLayout::FILL,
                      /*v_align=*/views::GridLayout::FILL,
                      /*resize_percent=*/1.0,
                      /*size_type=*/views::GridLayout::SizeType::USE_PREF,
                      /*fixed_width=*/0,
                      /*min_width=*/0);
 
-  // Add the image.
+  // Add the top image.
   layout->StartRow(views::GridLayout::kFixedSize, 0);
   auto image = std::make_unique<views::ImageView>();
-  image->SetImage(gfx::CreateVectorIcon(vector_icons::kBusinessIcon, kImageSize,
-                                        GetImageColor()));
+  image->SetImage(gfx::CreateVectorIcon(vector_icons::kBusinessIcon,
+                                        kTopImageSize, GetImageColor()));
   image_ = layout->AddView(std::move(image));
 
-  // Add the message.
+  // Add the side icon and message row.
   layout->StartRow(views::GridLayout::kFixedSize, 0);
+  auto icon_and_message_row = std::make_unique<views::View>();
+  auto* row_layout =
+      icon_and_message_row->SetLayoutManager(std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kHorizontal, KSideIconLayoutInsets,
+          kSideIconBetweenChildSpacing));
+  row_layout->set_main_axis_alignment(
+      views::BoxLayout::MainAxisAlignment::kStart);
+  row_layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kStart);
+
+  // Add the side icon.
+  icon_and_message_row->AddChildView(CreateSideIcon());
+
+  // Add the message.
   auto label = std::make_unique<views::Label>(GetDialogMessage());
   label->SetMultiLine(true);
-  message_ = layout->AddView(std::move(label));
+  message_ = icon_and_message_row->AddChildView(std::move(label));
+
+  layout->AddView(std::move(icon_and_message_row));
 
   // Add padding to distance the message from the button(s).
   layout->AddPaddingRow(views::GridLayout::kFixedSize, 10);
@@ -269,4 +330,44 @@
   widget_ = constrained_window::ShowWebModalDialogViews(this, web_contents_);
 }
 
+std::unique_ptr<views::View> DeepScanningDialogViews::CreateSideIcon() {
+  // The side icon is created either:
+  // - When the pending dialog is shown
+  // - When the response was fast enough that the failure dialog is shown first
+  DCHECK(!scan_success_.has_value() || !scan_success_.value());
+
+  // The icon left of the text has the appearance of a blue "Enterprise" logo
+  // with a spinner when the scan is pending.
+  auto icon = std::make_unique<views::View>();
+  icon->SetLayoutManager(std::make_unique<views::FillLayout>());
+
+  auto side_image = std::make_unique<views::ImageView>();
+  side_image->SetImage(gfx::CreateVectorIcon(
+      vector_icons::kBusinessIcon, kSideImageSize,
+      scan_success_.has_value() ? kScanDoneSideImageColor
+                                : kScanPendingSideImageColor));
+  side_image->SetBorder(views::CreateEmptyBorder(kSideImageInsets));
+  side_icon_image_ = icon->AddChildView(std::move(side_image));
+
+  // Add a spinner if the scan result is pending, otherwise add a background.
+  if (!scan_success_.has_value()) {
+    auto spinner = std::make_unique<views::Throbber>();
+    spinner->Start();
+    side_icon_spinner_ = icon->AddChildView(std::move(spinner));
+  } else {
+    side_icon_image_->SetBackground(
+        std::make_unique<CircleBackground>(GetSideImageBackgroundColor()));
+  }
+
+  return icon;
+}
+
+SkColor DeepScanningDialogViews::GetSideImageBackgroundColor() const {
+  DCHECK(scan_success_.has_value());
+  if (scan_success_.value())
+    return kScanSuccessColor;
+  else
+    return kScanFailureColor;
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h
index bc52199c..bd40f41 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h
@@ -20,6 +20,7 @@
 
 namespace views {
 class ImageView;
+class Throbber;
 class Widget;
 }  // namespace views
 
@@ -55,17 +56,23 @@
   void UpdateDialog();
 
   // Resizes the already shown dialog to accommodate changes in its content.
-  void Resize(int lines_delta);
+  void Resize(int height_to_add);
 
   // Setup the appropriate buttons depending on |scan_success_|.
   void SetupButtons();
 
+  // Returns a newly created side icon.
+  std::unique_ptr<views::View> CreateSideIcon();
+
   // Returns the appropriate dialog message depending on |scan_success_|.
   base::string16 GetDialogMessage() const;
 
   // Returns the image's color depending on |scan_success_|.
   SkColor GetImageColor() const;
 
+  // Returns the side image's background circle color.
+  SkColor GetSideImageBackgroundColor() const;
+
   // Returns the appropriate dialog message depending on |scan_success_|.
   base::string16 GetCancelButtonText() const;
 
@@ -76,9 +83,11 @@
 
   content::WebContents* web_contents_;
 
-  // Views above the buttons. |contents_view_| owns |image_| and |message_|.
+  // Views above the buttons. |contents_view_| owns every other view.
   std::unique_ptr<views::View> contents_view_;
   views::ImageView* image_;
+  views::ImageView* side_icon_image_;
+  views::Throbber* side_icon_spinner_;
   views::Label* message_;
 
   views::Widget* widget_;
diff --git a/chrome/browser/sharing/sharing_fcm_handler_unittest.cc b/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
index 1e4d225..f2db507 100644
--- a/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
+++ b/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
@@ -72,7 +72,7 @@
                          /*sync_preference=*/nullptr,
                          /*vapid_key_manager=*/nullptr,
                          /*local_device_info_provider=*/nullptr) {}
-  ~MockSharingFCMSender() override {}
+  ~MockSharingFCMSender() override = default;
 
   MOCK_METHOD4(SendMessageToTargetInfo,
                void(syncer::DeviceInfo::SharingTargetInfo target,
diff --git a/chrome/browser/sharing/sharing_message_sender.cc b/chrome/browser/sharing/sharing_message_sender.cc
index eaf0dce..16eab0f 100644
--- a/chrome/browser/sharing/sharing_message_sender.cc
+++ b/chrome/browser/sharing/sharing_message_sender.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/sharing/sharing_message_sender.h"
 
 #include "base/guid.h"
+#include "base/stl_util.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_metrics.h"
@@ -33,7 +34,6 @@
          chrome_browser_sharing::SharingMessage::kAckMessage);
 
   std::string message_guid = base::GenerateGUID();
-  send_message_callbacks_.emplace(message_guid, std::move(callback));
   chrome_browser_sharing::MessageType message_type =
       SharingPayloadCaseToMessageType(message.payload_case());
   SharingDevicePlatform receiver_device_platform =
@@ -41,10 +41,16 @@
   base::TimeDelta last_updated_age =
       base::Time::Now() - device.last_updated_timestamp();
 
+  auto inserted = base::InsertOrAssign(
+      message_metadata_, message_guid,
+      SentMessageMetadata(std::move(callback), base::TimeTicks::Now(),
+                          message_type, receiver_device_platform,
+                          last_updated_age));
+  DCHECK(inserted.second);
+
   auto delegate_iter = send_delegates_.find(delegate_type);
   if (delegate_iter == send_delegates_.end()) {
-    InvokeSendMessageCallback(message_guid, message_type,
-                              receiver_device_platform, last_updated_age,
+    InvokeSendMessageCallback(message_guid,
                               SharingSendMessageResult::kInternalError,
                               /*response=*/nullptr);
     return;
@@ -58,8 +64,7 @@
   const syncer::DeviceInfo* local_device_info =
       local_device_info_provider_->GetLocalDeviceInfo();
   if (!local_device_info) {
-    InvokeSendMessageCallback(message_guid, message_type,
-                              receiver_device_platform, last_updated_age,
+    InvokeSendMessageCallback(message_guid,
                               SharingSendMessageResult::kInternalError,
                               /*response=*/nullptr);
     return;
@@ -68,8 +73,7 @@
   base::PostDelayedTask(
       FROM_HERE, {base::TaskPriority::USER_VISIBLE, content::BrowserThread::UI},
       base::BindOnce(&SharingMessageSender::InvokeSendMessageCallback,
-                     weak_ptr_factory_.GetWeakPtr(), message_guid, message_type,
-                     receiver_device_platform, last_updated_age,
+                     weak_ptr_factory_.GetWeakPtr(), message_guid,
                      SharingSendMessageResult::kAckTimeout,
                      /*response=*/nullptr),
       response_timeout);
@@ -84,72 +88,54 @@
   delegate->DoSendMessageToDevice(
       device, response_timeout - kAckTimeToLive, std::move(message),
       base::BindOnce(&SharingMessageSender::OnMessageSent,
-                     weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
-                     message_guid, message_type, receiver_device_platform,
-                     last_updated_age));
+                     weak_ptr_factory_.GetWeakPtr(), message_guid));
 }
 
 void SharingMessageSender::OnMessageSent(
-    base::TimeTicks start_time,
     const std::string& message_guid,
-    chrome_browser_sharing::MessageType message_type,
-    SharingDevicePlatform receiver_device_platform,
-    base::TimeDelta last_updated_age,
     SharingSendMessageResult result,
     base::Optional<std::string> message_id) {
   if (result != SharingSendMessageResult::kSuccessful) {
-    InvokeSendMessageCallback(message_guid, message_type,
-                              receiver_device_platform, last_updated_age,
-                              result,
+    InvokeSendMessageCallback(message_guid, result,
                               /*response=*/nullptr);
     return;
   }
 
-  send_message_times_.emplace(*message_id, start_time);
+  // Got a new message id, store it for later.
   message_guids_.emplace(*message_id, message_guid);
-  receiver_device_platform_.emplace(*message_id, receiver_device_platform);
-  receiver_last_updated_age_.emplace(*message_id, last_updated_age);
-  message_types_.emplace(*message_id, message_type);
+
+  // Check if we got the ack while waiting for the FCM response.
+  auto cached_iter = cached_ack_response_messages_.find(*message_id);
+  if (cached_iter != cached_ack_response_messages_.end()) {
+    OnAckReceived(*message_id, std::move(cached_iter->second));
+    cached_ack_response_messages_.erase(cached_iter);
+  }
 }
 
 void SharingMessageSender::OnAckReceived(
     const std::string& message_id,
     std::unique_ptr<chrome_browser_sharing::ResponseMessage> response) {
   auto guid_iter = message_guids_.find(message_id);
-  if (guid_iter == message_guids_.end())
+  if (guid_iter == message_guids_.end()) {
+    // We don't have the guid yet, store the response until we receive it.
+    cached_ack_response_messages_.emplace(message_id, std::move(response));
     return;
+  }
 
   std::string message_guid = std::move(guid_iter->second);
   message_guids_.erase(guid_iter);
 
-  auto message_types_iter = message_types_.find(message_id);
-  DCHECK(message_types_iter != message_types_.end());
+  auto metadata_iter = message_metadata_.find(message_guid);
+  DCHECK(metadata_iter != message_metadata_.end());
+  const SentMessageMetadata& metadata = metadata_iter->second;
 
-  chrome_browser_sharing::MessageType message_type = message_types_iter->second;
-  message_types_.erase(message_types_iter);
+  LogSharingMessageAckTime(metadata.type, metadata.receiver_device_platform,
+                           base::TimeTicks::Now() - metadata.timestamp);
 
-  auto times_iter = send_message_times_.find(message_id);
-  DCHECK(times_iter != send_message_times_.end());
+  InvokeSendMessageCallback(message_guid, SharingSendMessageResult::kSuccessful,
+                            std::move(response));
 
-  auto device_platform_iter = receiver_device_platform_.find(message_id);
-  DCHECK(device_platform_iter != receiver_device_platform_.end());
-
-  SharingDevicePlatform receiver_device_platform = device_platform_iter->second;
-  receiver_device_platform_.erase(device_platform_iter);
-
-  LogSharingMessageAckTime(message_type, receiver_device_platform,
-                           base::TimeTicks::Now() - times_iter->second);
-  send_message_times_.erase(times_iter);
-
-  auto last_updated_age_iter = receiver_last_updated_age_.find(message_id);
-  DCHECK(last_updated_age_iter != receiver_last_updated_age_.end());
-
-  base::TimeDelta last_updated_age = last_updated_age_iter->second;
-  receiver_last_updated_age_.erase(last_updated_age_iter);
-
-  InvokeSendMessageCallback(
-      message_guid, message_type, receiver_device_platform, last_updated_age,
-      SharingSendMessageResult::kSuccessful, std::move(response));
+  message_metadata_.erase(metadata_iter);
 }
 
 void SharingMessageSender::RegisterSendDelegate(
@@ -161,19 +147,38 @@
 
 void SharingMessageSender::InvokeSendMessageCallback(
     const std::string& message_guid,
-    chrome_browser_sharing::MessageType message_type,
-    SharingDevicePlatform receiver_device_platform,
-    base::TimeDelta last_updated_age,
     SharingSendMessageResult result,
     std::unique_ptr<chrome_browser_sharing::ResponseMessage> response) {
-  auto iter = send_message_callbacks_.find(message_guid);
-  if (iter == send_message_callbacks_.end())
+  auto iter = message_metadata_.find(message_guid);
+  if (iter == message_metadata_.end() || !iter->second.callback)
     return;
 
-  ResponseCallback callback = std::move(iter->second);
-  send_message_callbacks_.erase(iter);
-  std::move(callback).Run(result, std::move(response));
+  SentMessageMetadata& metadata = iter->second;
 
-  LogSendSharingMessageResult(message_type, receiver_device_platform, result);
-  LogSharingDeviceLastUpdatedAgeWithResult(result, last_updated_age);
+  std::move(metadata.callback).Run(result, std::move(response));
+
+  LogSendSharingMessageResult(metadata.type, metadata.receiver_device_platform,
+                              result);
+  LogSharingDeviceLastUpdatedAgeWithResult(result, metadata.last_updated_age);
 }
+
+SharingMessageSender::SentMessageMetadata::SentMessageMetadata(
+    ResponseCallback callback,
+    base::TimeTicks timestamp,
+    chrome_browser_sharing::MessageType type,
+    SharingDevicePlatform receiver_device_platform,
+    base::TimeDelta last_updated_age)
+    : callback(std::move(callback)),
+      timestamp(timestamp),
+      type(type),
+      receiver_device_platform(receiver_device_platform),
+      last_updated_age(last_updated_age) {}
+
+SharingMessageSender::SentMessageMetadata::SentMessageMetadata(
+    SentMessageMetadata&& other) = default;
+
+SharingMessageSender::SentMessageMetadata&
+SharingMessageSender::SentMessageMetadata::operator=(
+    SentMessageMetadata&& other) = default;
+
+SharingMessageSender::SentMessageMetadata::~SentMessageMetadata() = default;
diff --git a/chrome/browser/sharing/sharing_message_sender.h b/chrome/browser/sharing/sharing_message_sender.h
index a70719a4..999c4d0 100644
--- a/chrome/browser/sharing/sharing_message_sender.h
+++ b/chrome/browser/sharing/sharing_message_sender.h
@@ -79,37 +79,43 @@
                             std::unique_ptr<SendMessageDelegate> delegate);
 
  private:
-  void OnMessageSent(base::TimeTicks start_time,
-                     const std::string& message_guid,
-                     chrome_browser_sharing::MessageType message_type,
-                     SharingDevicePlatform receiver_device_platform,
-                     base::TimeDelta last_updated_age,
+  struct SentMessageMetadata {
+    SentMessageMetadata(ResponseCallback callback,
+                        base::TimeTicks timestamp,
+                        chrome_browser_sharing::MessageType type,
+                        SharingDevicePlatform receiver_device_platform,
+                        base::TimeDelta last_updated_age);
+    SentMessageMetadata(SentMessageMetadata&& other);
+    SentMessageMetadata& operator=(SentMessageMetadata&& other);
+    ~SentMessageMetadata();
+
+    ResponseCallback callback;
+    base::TimeTicks timestamp;
+    chrome_browser_sharing::MessageType type;
+    SharingDevicePlatform receiver_device_platform;
+    base::TimeDelta last_updated_age;
+  };
+
+  void OnMessageSent(const std::string& message_guid,
                      SharingSendMessageResult result,
                      base::Optional<std::string> message_id);
 
   void InvokeSendMessageCallback(
       const std::string& message_guid,
-      chrome_browser_sharing::MessageType message_type,
-      SharingDevicePlatform receiver_device_platform,
-      base::TimeDelta last_updated_age,
       SharingSendMessageResult result,
       std::unique_ptr<chrome_browser_sharing::ResponseMessage> response);
 
   SharingSyncPreference* sync_prefs_;
   syncer::LocalDeviceInfoProvider* local_device_info_provider_;
 
-  // Map of random GUID to SendMessageCallback.
-  std::map<std::string, ResponseCallback> send_message_callbacks_;
-  // Map of FCM message_id to time at start of send message request to FCM.
-  std::map<std::string, base::TimeTicks> send_message_times_;
+  // Map of random GUID to SentMessageMetadata.
+  std::map<std::string, SentMessageMetadata> message_metadata_;
   // Map of FCM message_id to random GUID.
   std::map<std::string, std::string> message_guids_;
-  // Map of FCM message_id to platform of receiver device for metrics.
-  std::map<std::string, SharingDevicePlatform> receiver_device_platform_;
-  // Map of FCM message_id to age of last updated timestamp of receiver device.
-  std::map<std::string, base::TimeDelta> receiver_last_updated_age_;
-  // Map of FCM message_id to message type for metrics.
-  std::map<std::string, chrome_browser_sharing::MessageType> message_types_;
+  // Map of FCM message_id to received ACK response messages.
+  std::map<std::string,
+           std::unique_ptr<chrome_browser_sharing::ResponseMessage>>
+      cached_ack_response_messages_;
 
   // Registered delegates to send messages.
   std::map<DelegateType, std::unique_ptr<SendMessageDelegate>> send_delegates_;
diff --git a/chrome/browser/sharing/sharing_message_sender_unittest.cc b/chrome/browser/sharing/sharing_message_sender_unittest.cc
index e10e48b..bfc1878a 100644
--- a/chrome/browser/sharing/sharing_message_sender_unittest.cc
+++ b/chrome/browser/sharing/sharing_message_sender_unittest.cc
@@ -72,6 +72,23 @@
                     SendMessageCallback callback));
 };
 
+// static
+syncer::DeviceInfo::SharingInfo CreateLocalSharingInfo() {
+  return syncer::DeviceInfo::SharingInfo(
+      {kSenderVapidFcmToken, kSenderP256dh, kSenderAuthSecret},
+      {"sender_id_fcm_token", "sender_id_p256dh", "sender_id_auth_secret"},
+      std::set<sync_pb::SharingSpecificFields::EnabledFeatures>());
+}
+
+// static
+syncer::DeviceInfo::SharingInfo CreateSharingInfo() {
+  return syncer::DeviceInfo::SharingInfo(
+      {kFCMToken, kP256dh, kAuthSecret},
+      {"sender_id_fcm_token", "sender_id_p256dh", "sender_id_auth_secret"},
+      std::set<sync_pb::SharingSpecificFields::EnabledFeatures>{
+          sync_pb::SharingSpecificFields::CLICK_TO_CALL});
+}
+
 }  // namespace
 
 class SharingMessageSenderTest : public testing::Test {
@@ -88,6 +105,20 @@
   }
   ~SharingMessageSenderTest() override = default;
 
+  std::unique_ptr<syncer::DeviceInfo> SetupDevice() {
+    std::unique_ptr<syncer::DeviceInfo> device_info = CreateFakeDeviceInfo(
+        kReceiverGUID, kReceiverDeviceName, CreateSharingInfo());
+    fake_device_info_sync_service_.GetDeviceInfoTracker()->Add(
+        device_info.get());
+    fake_device_info_sync_service_.GetLocalDeviceInfoProvider()
+        ->GetMutableDeviceInfo()
+        ->set_sharing_info(CreateLocalSharingInfo());
+    sharing_sync_preference_.SetFCMRegistration(
+        SharingSyncPreference::FCMRegistration(kAuthorizedEntity,
+                                               base::Time::Now()));
+    return device_info;
+  }
+
  protected:
   content::BrowserTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -104,21 +135,6 @@
   DISALLOW_COPY_AND_ASSIGN(SharingMessageSenderTest);
 };
 
-static syncer::DeviceInfo::SharingInfo CreateLocalSharingInfo() {
-  return syncer::DeviceInfo::SharingInfo(
-      {kSenderVapidFcmToken, kSenderP256dh, kSenderAuthSecret},
-      {"sender_id_fcm_token", "sender_id_p256dh", "sender_id_auth_secret"},
-      std::set<sync_pb::SharingSpecificFields::EnabledFeatures>());
-}
-
-static syncer::DeviceInfo::SharingInfo CreateSharingInfo() {
-  return syncer::DeviceInfo::SharingInfo(
-      {kFCMToken, kP256dh, kAuthSecret},
-      {"sender_id_fcm_token", "sender_id_p256dh", "sender_id_auth_secret"},
-      std::set<sync_pb::SharingSpecificFields::EnabledFeatures>{
-          sync_pb::SharingSpecificFields::CLICK_TO_CALL});
-}
-
 MATCHER_P(ProtoEquals, message, "") {
   if (!arg)
     return false;
@@ -132,15 +148,7 @@
 }  // namespace
 
 TEST_F(SharingMessageSenderTest, MessageSent_AckTimedout) {
-  std::unique_ptr<syncer::DeviceInfo> device_info = CreateFakeDeviceInfo(
-      kReceiverGUID, kReceiverDeviceName, CreateSharingInfo());
-  fake_device_info_sync_service_.GetDeviceInfoTracker()->Add(device_info.get());
-  fake_device_info_sync_service_.GetLocalDeviceInfoProvider()
-      ->GetMutableDeviceInfo()
-      ->set_sharing_info(CreateLocalSharingInfo());
-  sharing_sync_preference_.SetFCMRegistration(
-      SharingSyncPreference::FCMRegistration(kAuthorizedEntity,
-                                             base::Time::Now()));
+  std::unique_ptr<syncer::DeviceInfo> device_info = SetupDevice();
 
   base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
   EXPECT_CALL(mock_callback,
@@ -173,15 +181,7 @@
 }
 
 TEST_F(SharingMessageSenderTest, SendMessageToDevice_InternalError) {
-  std::unique_ptr<syncer::DeviceInfo> device_info = CreateFakeDeviceInfo(
-      kReceiverGUID, kReceiverDeviceName, CreateSharingInfo());
-  fake_device_info_sync_service_.GetDeviceInfoTracker()->Add(device_info.get());
-  fake_device_info_sync_service_.GetLocalDeviceInfoProvider()
-      ->GetMutableDeviceInfo()
-      ->set_sharing_info(CreateLocalSharingInfo());
-  sharing_sync_preference_.SetFCMRegistration(
-      SharingSyncPreference::FCMRegistration(kAuthorizedEntity,
-                                             base::Time::Now()));
+  std::unique_ptr<syncer::DeviceInfo> device_info = SetupDevice();
 
   base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
   EXPECT_CALL(mock_callback,
@@ -193,7 +193,7 @@
           base::TimeDelta time_to_live,
           chrome_browser_sharing::SharingMessage message,
           SharingFCMSender::SendMessageCallback callback) {
-        // FCM message not sent succesfully.
+        // FCM message not sent successfully.
         std::move(callback).Run(SharingSendMessageResult::kInternalError,
                                 base::nullopt);
 
@@ -214,15 +214,7 @@
 }
 
 TEST_F(SharingMessageSenderTest, MessageSent_AckReceived) {
-  std::unique_ptr<syncer::DeviceInfo> device_info = CreateFakeDeviceInfo(
-      kReceiverGUID, kReceiverDeviceName, CreateSharingInfo());
-  fake_device_info_sync_service_.GetDeviceInfoTracker()->Add(device_info.get());
-  fake_device_info_sync_service_.GetLocalDeviceInfoProvider()
-      ->GetMutableDeviceInfo()
-      ->set_sharing_info(CreateLocalSharingInfo());
-  sharing_sync_preference_.SetFCMRegistration(
-      SharingSyncPreference::FCMRegistration(kAuthorizedEntity,
-                                             base::Time::Now()));
+  std::unique_ptr<syncer::DeviceInfo> device_info = SetupDevice();
 
   chrome_browser_sharing::SharingMessage sent_message;
   sent_message.mutable_click_to_call_message()->set_phone_number("999999");
@@ -275,6 +267,47 @@
       SharingMessageSender::DelegateType::kFCM, mock_callback.Get());
 }
 
+TEST_F(SharingMessageSenderTest, MessageSent_AckReceivedBeforeMessageId) {
+  std::unique_ptr<syncer::DeviceInfo> device_info = SetupDevice();
+
+  chrome_browser_sharing::SharingMessage sent_message;
+  sent_message.mutable_click_to_call_message()->set_phone_number("999999");
+
+  chrome_browser_sharing::ResponseMessage expected_response_message;
+  base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
+  EXPECT_CALL(mock_callback,
+              Run(testing::Eq(SharingSendMessageResult::kSuccessful),
+                  ProtoEquals(expected_response_message)));
+
+  auto simulate_expected_ack_message_received =
+      [&](syncer::DeviceInfo::SharingTargetInfo target,
+          base::TimeDelta time_to_live,
+          chrome_browser_sharing::SharingMessage message,
+          SharingFCMSender::SendMessageCallback callback) {
+        // Simulate ack message received.
+        std::unique_ptr<chrome_browser_sharing::ResponseMessage>
+            response_message =
+                std::make_unique<chrome_browser_sharing::ResponseMessage>();
+        response_message->CopyFrom(expected_response_message);
+
+        sharing_message_sender_.OnAckReceived(kSenderMessageID,
+                                              std::move(response_message));
+
+        // Call FCM send success after receiving the ACK.
+        std::move(callback).Run(SharingSendMessageResult::kSuccessful,
+                                kSenderMessageID);
+      };
+
+  EXPECT_CALL(
+      *mock_sharing_fcm_sender_,
+      SendMessageToTargetInfo(testing::_, testing::_, testing::_, testing::_))
+      .WillOnce(testing::Invoke(simulate_expected_ack_message_received));
+
+  sharing_message_sender_.SendMessageToDevice(
+      *device_info.get(), kTimeToLive, std::move(sent_message),
+      SharingMessageSender::DelegateType::kFCM, mock_callback.Get());
+}
+
 TEST_F(SharingMessageSenderTest, NonExistingDelegate) {
   SharingMessageSender sharing_message_sender{
       &sharing_sync_preference_,
diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc
index 22ca56c..02cfe55 100644
--- a/chrome/browser/ssl/security_state_tab_helper.cc
+++ b/chrome/browser/ssl/security_state_tab_helper.cc
@@ -110,7 +110,10 @@
   net::SSLVersion ssl_version =
       net::SSLConnectionStatusToVersion(connection_status);
 
-  return ssl_version < ssl_version_min;
+  // Signed Exchanges do not have connection status set. Exclude unknown TLS
+  // versions from legacy TLS treatment. See https://crbug.com/1041773.
+  return ssl_version != net::SSL_CONNECTION_VERSION_UNKNOWN &&
+         ssl_version < ssl_version_min;
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc b/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
index 60ffca4..852a32a 100644
--- a/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc
@@ -57,12 +57,6 @@
   TwoClientAutofillProfileSyncTest() : SyncTest(TWO_CLIENT) {}
   ~TwoClientAutofillProfileSyncTest() override {}
 
-  // Tests that check Sync.ModelTypeEntityChange* histograms require
-  // self-notifications. The reason is that every commit will eventually trigger
-  // an incoming update on the same client, and without self-notifications we
-  // have no good way to reliably trigger these updates.
-  bool TestUsesSelfNotifications() override { return true; }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(TwoClientAutofillProfileSyncTest);
 };
@@ -642,6 +636,7 @@
 
 IN_PROC_BROWSER_TEST_F(TwoClientAutofillProfileSyncTest,
                        E2E_ONLY(TwoClientsAddAutofillProfiles)) {
+  ResetSyncForPrimaryAccount();
   ASSERT_TRUE(SetupSync());
   base::HistogramTester histograms;
 
diff --git a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
index dcda01c9..cd9fa13 100644
--- a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
@@ -25,9 +25,6 @@
 
   ~TwoClientThemesSyncTest() override {}
 
-  // Needed for AwaitQuiescence().
-  bool TestUsesSelfNotifications() override { return true; }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(TwoClientThemesSyncTest);
 };
@@ -39,8 +36,6 @@
                        E2E_ENABLED(DefaultThenSyncCustom)) {
   ResetSyncForPrimaryAccount();
   ASSERT_TRUE(SetupSync());
-  // Wait until sync settles before we override the theme below.
-  AwaitQuiescence();
 
   ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
   ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
@@ -69,8 +64,6 @@
   SetCustomTheme(GetProfile(1));
 
   ASSERT_TRUE(SetupSync());
-  // Wait until sync settles before we override the theme below.
-  AwaitQuiescence();
 
   UseSystemTheme(GetProfile(0));
   ASSERT_TRUE(UsingSystemTheme(GetProfile(0)));
@@ -92,8 +85,6 @@
   SetCustomTheme(GetProfile(1));
 
   ASSERT_TRUE(SetupSync());
-  // Wait until sync settles before we override the theme below.
-  AwaitQuiescence();
 
   UseDefaultTheme(GetProfile(0));
   EXPECT_TRUE(UsingDefaultTheme(GetProfile(0)));
@@ -110,8 +101,6 @@
 IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, E2E_ENABLED(CycleOptions)) {
   ResetSyncForPrimaryAccount();
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
-  // Wait until sync settles before we override the theme below.
-  AwaitQuiescence();
 
   SetCustomTheme(GetProfile(0));
 
diff --git a/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc b/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
index 7e83603..05754e7 100644
--- a/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
@@ -424,7 +424,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
-                       E2E_ENABLED(DisableEnableSync)) {
+                       DisableEnableSync) {
   ResetSyncForPrimaryAccount();
   const base::string16 kUrl1(ASCIIToUTF16("http://history1.google.com/"));
   const base::string16 kUrl2(ASCIIToUTF16("http://history2.google.com/"));
diff --git a/chrome/browser/ui/android/widget/BUILD.gn b/chrome/browser/ui/android/widget/BUILD.gn
index 5585371..a936883 100644
--- a/chrome/browser/ui/android/widget/BUILD.gn
+++ b/chrome/browser/ui/android/widget/BUILD.gn
@@ -7,26 +7,7 @@
 
 android_library("java") {
   sources = [
-    "java/src/org/chromium/chrome/browser/ui/widget/AlwaysDismissedDialog.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/ClipDrawableProgressBar.java",
     "java/src/org/chromium/chrome/browser/ui/widget/CompositeTouchDelegate.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/ContextMenuDialog.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/FadingShadow.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/FadingShadowView.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/LoadingView.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/MaterialProgressBar.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/MoreProgressButton.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/PaddedFrameLayout.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/PromoDialog.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/PromoDialogLayout.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayout.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditText.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/ViewResourceFrameLayout.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/WrappingLayout.java",
     "java/src/org/chromium/chrome/browser/ui/widget/dragreorder/DragReorderableListAdapter.java",
     "java/src/org/chromium/chrome/browser/ui/widget/dragreorder/DragStateDelegate.java",
     "java/src/org/chromium/chrome/browser/ui/widget/highlight/PulseDrawable.java",
@@ -84,38 +65,22 @@
 android_library("ui_widget_java_tests") {
   testonly = true
 
-  sources = [
-    "java/src/org/chromium/chrome/browser/ui/widget/MoreProgressButtonTest.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/PromoDialogTest.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayoutTest.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonRenderTest.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditTextTest.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/WrappingLayoutTest.java",
-    "java/src/org/chromium/chrome/browser/ui/widget/highlight/ViewHighlighterTest.java",
-  ]
+  sources = [ "java/src/org/chromium/chrome/browser/ui/widget/highlight/ViewHighlighterTest.java" ]
+
   deps = [
     ":java",
     ":test_support_java",
     ":ui_widget_java_resources",
-    ":ui_widget_java_test_resources",
+    ":ui_widget_java_resources",
     "//base:base_java_test_support",
+    "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:com_android_support_support_compat_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit",
-    "//ui/android:ui_java_test_support",
-  ]
-}
-
-android_resources("ui_widget_java_test_resources") {
-  custom_package = "org.chromium.chrome.browser.ui.widget.test"
-  resource_dirs = [ "test/java/res" ]
-  deps = [
-    ":ui_widget_java_resources",
-    "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//ui/android:ui_java_resources",
+    "//ui/android:ui_java_test_support",
   ]
 }
 
diff --git a/chrome/browser/ui/android/widget/java/res/values/attrs.xml b/chrome/browser/ui/android/widget/java/res/values/attrs.xml
index 221d285b..ea7a11bc 100644
--- a/chrome/browser/ui/android/widget/java/res/values/attrs.xml
+++ b/chrome/browser/ui/android/widget/java/res/values/attrs.xml
@@ -10,44 +10,4 @@
         <attr name="menuHorizontalOverlapAnchor" format="boolean" />
         <attr name="menuBackground" format="reference" />
     </declare-styleable>
-
-    <declare-styleable name="MaterialProgressBar">
-        <attr name="colorBackground" format="reference|color" />
-        <attr name="colorProgress" format="reference|color" />
-        <attr name="colorSecondaryProgress" format="reference|color" />
-    </declare-styleable>
-
-    <declare-styleable name="PaddedFrameLayout">
-        <attr name="maxChildWidth" format="dimension" />
-        <attr name="maxChildHeight" format="dimension" />
-    </declare-styleable>
-
-    <declare-styleable name="RadioButtonWithDescription">
-        <attr name="primaryText" format="string" />
-        <attr name="descriptionText" format="string" />
-    </declare-styleable>
-
-    <declare-styleable name="RadioButtonWithEditText">
-        <attr name="android:hint"/>
-        <attr name="android:inputType" />
-    </declare-styleable>
-
-    <declare-styleable name="RoundedCornerImageView">
-        <attr name="cornerRadiusTopStart" format="dimension" />
-        <attr name="cornerRadiusTopEnd" format="dimension" />
-        <attr name="cornerRadiusBottomStart" format="dimension" />
-        <attr name="cornerRadiusBottomEnd" format="dimension" />
-        <attr name="roundedfillColor" format="reference|color" />
-    </declare-styleable>
-
-    <declare-styleable name="WrappingLayout">
-        <attr name="horizontalSpacing" format="dimension" />
-        <attr name="verticalSpacing" format="dimension" />
-    </declare-styleable>
-
-    <declare-styleable name="ChromeTextInputLayout">
-        <attr name="hintTextAppearance" format="reference" />
-        <attr name="hint" format="reference|string" />
-        <attr name="errorTextAppearance" format="reference" />
-    </declare-styleable>
 </resources>
diff --git a/chrome/browser/ui/android/widget/java/res/values/colors.xml b/chrome/browser/ui/android/widget/java/res/values/colors.xml
deleted file mode 100644
index 48094d3..0000000
--- a/chrome/browser/ui/android/widget/java/res/values/colors.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<resources>
-    <color name="black_alpha_65">#A6000000</color>
-
-    <color name="modal_dialog_scrim_color">@color/black_alpha_65</color>
-
-    <!-- Progress Bar colors -->
-    <color name="progress_bar_foreground">@color/light_active_color</color>
-    <color name="progress_bar_secondary">@color/modern_grey_600</color>
-    <color name="progress_bar_background">#3D4386F7</color>
-</resources>
diff --git a/chrome/browser/ui/android/widget/java/res/values/dimens.xml b/chrome/browser/ui/android/widget/java/res/values/dimens.xml
index 447791c6..bd74ba1f 100644
--- a/chrome/browser/ui/android/widget/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/widget/java/res/values/dimens.xml
@@ -12,26 +12,9 @@
     <dimen name="menu_drawable_padding">16dp</dimen>
     <dimen name="menu_padding_start">16dp</dimen>
 
-    <!-- RadioButton(WithDescription)Layout dimensions -->
-    <dimen name="default_vertical_margin_between_items">8dp</dimen>
-
-    <!-- RadioButtonWithDescription dimensions -->
-    <dimen name="radio_button_with_description_lateral_padding">16dp</dimen>
-    <dimen name="radio_button_with_description_vertical_padding">10dp</dimen>
-
     <!-- Drag-Reorderable List dimensions -->
     <dimen name="list_item_dragged_elevation">6dp</dimen>
 
-    <!-- Dialogs -->
-    <dimen name="dialog_max_width">600dp</dimen>
-    <dimen name="dialog_header_margin">14dp</dimen>
-    <dimen name="promo_dialog_illustration_margin">24dp</dimen>
-    <dimen name="promo_dialog_illustration_width">150dp</dimen>
-    <dimen name="promo_dialog_padding">16dp</dimen>
-    <dimen name="promo_dialog_stacked_margin">16dp</dimen>
-    <dimen name="promo_dialog_max_content_width">320dp</dimen>
-    <dimen name="promo_dialog_min_scrollable_height">100dp</dimen>
-
     <!-- TextBubble dimensions -->
     <dimen name="text_bubble_margin">4dp</dimen>
     <dimen name="text_bubble_corner_radius">8dp</dimen>
diff --git a/chrome/browser/ui/android/widget/java/res/values/styles.xml b/chrome/browser/ui/android/widget/java/res/values/styles.xml
index 28b0763..feddf28 100644
--- a/chrome/browser/ui/android/widget/java/res/values/styles.xml
+++ b/chrome/browser/ui/android/widget/java/res/values/styles.xml
@@ -14,9 +14,6 @@
         <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
         <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item>
     </style>
-    <style name="ListActionButton" parent="@style/TextButton">
-        <item name="android:minHeight">48dp</item>
-    </style>
 
     <style name="OverflowMenuAnim">
         <item name="android:windowEnterAnimation">@anim/menu_enter</item>
@@ -27,23 +24,6 @@
         <item name="android:windowExitAnimation">@anim/menu_exit_from_bottom</item>
     </style>
 
-    <!-- Promo dialogs -->
-    <style name="PromoDialog" >
-        <item name="android:background">@android:color/transparent</item>
-        <item name="android:backgroundDimEnabled">false</item>
-        <item name="android:clipChildren">false</item>
-        <item name="android:clipToPadding">false</item>
-        <item name="android:windowAnimationStyle">@null</item>
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:windowCloseOnTouchOutside">false</item>
-        <item name="android:windowContentOverlay">@null</item>
-        <item name="android:windowFrame">@null</item>
-        <item name="android:windowFullscreen">false</item>
-        <item name="android:windowIsFloating">true</item>
-        <item name="android:windowIsTranslucent">true</item>
-        <item name="android:windowNoTitle">true</item>
-    </style>
-
     <!-- Bubble styles -->
     <style name="TextBubbleAnimation">
         <item name="android:windowEnterAnimation">@anim/textbubble_in</item>
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index 81738ed..23ca3ea1 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -838,7 +838,6 @@
   bool IsEnabledByPolicy() const {
     switch (GetParam()) {
       case ArcState::ARC_PLAY_STORE_MANAGED_AND_ENABLED:
-        return true;
       case ArcState::ARC_PLAY_STORE_MANAGED_AND_DISABLED:
       case ArcState::ARC_WITHOUT_PLAY_STORE:
         return false;
@@ -2947,20 +2946,8 @@
   // There is no default app for managed users except Play Store
   for (const auto& app : fake_default_apps()) {
     const std::string app_id = ArcAppTest::GetAppId(app);
-    if (IsEnabledByPolicy()) {
-      // Only system apps are allowed. App from package test.app2 is declared as
-      // a system app.
-      auto app = prefs->GetApp(app_id);
-      if (app) {
-        EXPECT_EQ("test.app2", app->package_name);
-        EXPECT_TRUE(prefs->IsRegistered(app_id));
-      } else {
-        EXPECT_FALSE(prefs->IsRegistered(app_id));
-      }
-    } else {
-      EXPECT_FALSE(prefs->IsRegistered(app_id));
-      EXPECT_FALSE(prefs->GetApp(app_id));
-    }
+    EXPECT_FALSE(prefs->IsRegistered(app_id));
+    EXPECT_FALSE(prefs->GetApp(app_id));
   }
 
   // PlayStor exists for managed and enabled state.
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
index 0eaeb0581..784b1712 100644
--- a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
+++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
@@ -28,7 +28,6 @@
 constexpr char kName[] = "name";
 constexpr char kOem[] = "oem";
 constexpr char kPackageName[] = "package_name";
-constexpr char kSystem[] = "system";
 
 constexpr char kDefaultApps[] = "arc.apps.default";
 constexpr char kHidden[] = "hidden";
@@ -87,14 +86,12 @@
     std::string activity;
     std::string app_path;
     bool oem = false;
-    bool system = false;
 
     app_info_dictionary->GetString(kName, &name);
     app_info_dictionary->GetString(kPackageName, &package_name);
     app_info_dictionary->GetString(kActivity, &activity);
     app_info_dictionary->GetString(kAppPath, &app_path);
     app_info_dictionary->GetBoolean(kOem, &oem);
-    app_info_dictionary->GetBoolean(kSystem, &system);
 
     if (name.empty() || package_name.empty() || activity.empty() ||
         app_path.empty()) {
@@ -106,9 +103,8 @@
     const std::string app_id =
         ArcAppListPrefs::GetAppId(package_name, activity);
     std::unique_ptr<ArcDefaultAppList::AppInfo> app =
-        std::make_unique<ArcDefaultAppList::AppInfo>(name, package_name,
-                                                     activity, oem, system,
-                                                     root_dir.Append(app_path));
+        std::make_unique<ArcDefaultAppList::AppInfo>(
+            name, package_name, activity, oem, root_dir.Append(app_path));
     apps.get()->insert(
         std::pair<std::string, std::unique_ptr<ArcDefaultAppList::AppInfo>>(
             app_id, std::move(app)));
@@ -210,10 +206,11 @@
       registry->GetInstalledExtension(arc::kPlayStoreAppId);
   if (arc_host && arc::IsPlayStoreAvailable()) {
     std::unique_ptr<ArcDefaultAppList::AppInfo> play_store_app =
-        std::make_unique<ArcDefaultAppList::AppInfo>(
-            arc_host->name(), arc::kPlayStorePackage, arc::kPlayStoreActivity,
-            false /* oem */, true /* system */,
-            base::FilePath() /* app_path */);
+        std::make_unique<ArcDefaultAppList::AppInfo>(arc_host->name(),
+                                       arc::kPlayStorePackage,
+                                       arc::kPlayStoreActivity,
+                                       false /* oem */,
+                                       base::FilePath() /* app_path */);
     AppInfoMap& app_map =
         IsAppHidden(prefs, arc::kPlayStoreAppId) ? hidden_apps_ : visible_apps_;
     app_map.insert(
@@ -226,16 +223,14 @@
 
 const ArcDefaultAppList::AppInfo* ArcDefaultAppList::GetApp(
     const std::string& app_id) const {
-  if (filter_level_ == FilterLevel::ALL)
+  if ((filter_level_ == FilterLevel::ALL) ||
+      (filter_level_ == FilterLevel::OPTIONAL_APPS &&
+       app_id != arc::kPlayStoreAppId)) {
     return nullptr;
-
+  }
   const auto it = visible_apps_.find(app_id);
   if (it == visible_apps_.end())
     return nullptr;
-
-  if (filter_level_ == FilterLevel::OPTIONAL_APPS && !it->second->system)
-    return nullptr;
-
   return it->second.get();
 }
 
@@ -308,13 +303,11 @@
                                     const std::string& package_name,
                                     const std::string& activity,
                                     bool oem,
-                                    bool system,
                                     const base::FilePath app_path)
     : name(name),
       package_name(package_name),
       activity(activity),
       oem(oem),
-      system(system),
       app_path(app_path) {}
 
 ArcDefaultAppList::AppInfo::~AppInfo() {}
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.h b/chrome/browser/ui/app_list/arc/arc_default_app_list.h
index 92c9eaf1..a0d3356 100644
--- a/chrome/browser/ui/app_list/arc/arc_default_app_list.h
+++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.h
@@ -29,26 +29,24 @@
  public:
   struct AppInfo {
     AppInfo(const std::string& name,
-            const std::string& package_name,
-            const std::string& activity,
-            bool oem,
-            bool system,
-            const base::FilePath app_path);
+                   const std::string& package_name,
+                   const std::string& activity,
+                   bool oem,
+                   const base::FilePath app_path);
     ~AppInfo();
 
     std::string name;
     std::string package_name;
     std::string activity;
     bool oem;
-    bool system;
     base::FilePath app_path;  // App folder that contains pre-installed icons.
   };
 
   enum class FilterLevel {
     // Filter nothing.
     NOTHING,
-    // Filter out only optional apps, leaving system apps available, Play Store
-    // is also system app.
+    // Filter out only optional apps, excluding Play Store for example. Used in
+    // case when Play Store is managed and enabled.
     OPTIONAL_APPS,
     // Filter out everything. Used in case when Play Store is managed and
     // disabled.
diff --git a/chrome/browser/ui/views/extensions/extension_popup.cc b/chrome/browser/ui/views/extensions/extension_popup.cc
index cdeb2a13..d15414b6 100644
--- a/chrome/browser/ui/views/extensions/extension_popup.cc
+++ b/chrome/browser/ui/views/extensions/extension_popup.cc
@@ -114,7 +114,7 @@
   // DesktopNativeWidgetAura does not trigger the expected browser widget
   // [de]activation events when activating widgets in its own root window.
   // This additional check handles those cases. See https://crbug.com/320889 .
-  if (gained_active == anchor_widget()->GetNativeWindow())
+  if (anchor_widget() && gained_active == anchor_widget()->GetNativeWindow())
     CloseUnlessUnderInspection();
 }
 #endif  // defined(USE_AURA)
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
index 92bef765..fbb87a90 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
@@ -145,17 +145,6 @@
   return "ContentSettingsImageView";
 }
 
-void ContentSettingImageView::OnBoundsChanged(
-    const gfx::Rect& previous_bounds) {
-  if (indicator_promo_)
-    indicator_promo_->OnAnchorBoundsChanged();
-
-  if (bubble_view_)
-    bubble_view_->OnAnchorBoundsChanged();
-
-  IconLabelBubbleView::OnBoundsChanged(previous_bounds);
-}
-
 bool ContentSettingImageView::OnMousePressed(const ui::MouseEvent& event) {
   // Pause animation so that the icon does not shrink and deselect while the
   // user is attempting to press it.
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.h b/chrome/browser/ui/views/location_bar/content_setting_image_view.h
index 6d4627bb..07f8a31 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.h
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.h
@@ -72,7 +72,6 @@
 
   // IconLabelBubbleView:
   const char* GetClassName() const override;
-  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   void OnThemeChanged() override;
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index 8ea5a17..846d35de 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -195,6 +195,11 @@
   return delegate_->GetIconLabelBubbleSurroundingForegroundColor();
 }
 
+void IconLabelBubbleView::UpdateLabelColors() {
+  SetEnabledTextColors(GetForegroundColor());
+  label()->SetBackgroundColor(delegate_->GetIconLabelBubbleBackgroundColor());
+}
+
 bool IconLabelBubbleView::ShouldShowSeparator() const {
   return ShouldShowLabel();
 }
@@ -304,9 +309,7 @@
   // under certain conditions. We don't want that, so unset the background.
   label()->SetBackground(nullptr);
 
-  SetEnabledTextColors(GetForegroundColor());
-  label()->SetBackgroundColor(delegate_->GetIconLabelBubbleBackgroundColor());
-  SchedulePaint();
+  UpdateLabelColors();
 }
 
 std::unique_ptr<views::InkDrop> IconLabelBubbleView::CreateInkDrop() {
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
index 46857fd..8565a71 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -101,6 +101,9 @@
   // Gets the color for displaying text and/or icons.
   virtual SkColor GetForegroundColor() const;
 
+  // Sets the label text and background colors.
+  void UpdateLabelColors();
+
   // Returns true when the separator should be visible.
   virtual bool ShouldShowSeparator() const;
 
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view.cc b/chrome/browser/ui/views/location_bar/location_icon_view.cc
index 6e30018..77d5314 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_icon_view.cc
@@ -217,6 +217,10 @@
   UpdateTextVisibility(suppress_animations);
   UpdateIcon();
 
+  // The label text color may have changed in response to changes in security
+  // level.
+  UpdateLabelColors();
+
   bool is_editing_or_empty = delegate_->IsEditingOrEmpty();
   // The tooltip should be shown if we are not editing or empty.
   SetTooltipText(is_editing_or_empty
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
index 938682a..12a3a2b 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -171,14 +171,6 @@
   return gfx::kNoneIcon;
 }
 
-void PageActionIconView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
-  views::BubbleDialogDelegateView* bubble = GetBubble();
-  // TODO(crbug.com/1016968): Remove OnAnchorBoundsChanged after fixing.
-  if (bubble && bubble->GetAnchorView())
-    bubble->OnAnchorBoundsChanged();
-  IconLabelBubbleView::OnBoundsChanged(previous_bounds);
-}
-
 void PageActionIconView::OnTouchUiChanged() {
   icon_size_ = GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
   UpdateIconImage();
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.h b/chrome/browser/ui/views/page_action/page_action_icon_view.h
index 65ab854e..1fc7cdd8 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.h
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.h
@@ -135,7 +135,6 @@
   virtual const gfx::VectorIcon& GetVectorIconBadge() const;
 
   // IconLabelBubbleView:
-  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   void OnTouchUiChanged() override;
   const char* GetClassName() const override;
 
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.cc b/chrome/browser/ui/views/passwords/password_pending_view.cc
index 403fae8..e947581 100644
--- a/chrome/browser/ui/views/passwords/password_pending_view.cc
+++ b/chrome/browser/ui/views/passwords/password_pending_view.cc
@@ -306,6 +306,7 @@
   }
 
   DialogDelegate::SetFootnoteView(CreateFooterView());
+  UpdateDialogButtons();
 }
 
 views::View* PasswordPendingView::GetUsernameTextfieldForTest() const {
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index fbb385d4..912017f 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2541,6 +2541,21 @@
     {"ippPrinterUnreachable", IDS_SETTINGS_PRINTING_CUPS_IPP_URI_UNREACHABLE},
     {"generalPrinterDialogError",
      IDS_SETTINGS_PRINTING_CUPS_DIALOG_GENERAL_ERROR},
+    {"printServerButtonText", IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER},
+    {"addPrintServerTitle", IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE},
+    {"printServerAddress", IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS},
+    {"printServerFoundZeroPrinters",
+     IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS},
+    {"printServerFoundOnePrinter",
+     IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER},
+    {"printServerFoundManyPrinters",
+     IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS},
+    {"printServerInvalidUrlAddress",
+     IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS},
+    {"printServerConnectionError",
+     IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR},
+    {"printServerConfigurationErrorMessage",
+     IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD},
 #else
     {"localPrintersTitle", IDS_SETTINGS_PRINTING_LOCAL_PRINTERS_TITLE},
 #endif
@@ -2557,6 +2572,9 @@
   html_source->AddString(
       "printingCUPSPrintPpdLearnMoreUrl",
       GetHelpUrlWithBoard(chrome::kCupsPrintPPDLearnMoreURL));
+  html_source->AddBoolean(
+      "consumerPrintServerUiEnabled",
+      base::FeatureList::IsEnabled(features::kPrintServerUi));
 #endif
 }
 
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn
index cde9257..bf276534 100644
--- a/chrome/browser/web_applications/components/BUILD.gn
+++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -131,6 +131,7 @@
     "//components/user_manager:user_manager",
     "//content/public/browser",
     "//extensions/common:common_constants",
+    "//net",
     "//services/preferences/public/cpp",
     "//skia",
   ]
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
index 3dd6ebae..8964580 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
+#include "base/win/windows_version.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/web_app_file_handler_registration.h"
 #include "chrome/browser/web_applications/components/web_app_shortcut.h"
@@ -24,6 +25,39 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/install_static/install_util.h"
 #include "chrome/installer/util/shell_util.h"
+#include "net/base/filename_util.h"
+
+namespace {
+
+// Returns the app-specific-launcher filename to be used for |app_name|.
+base::FilePath GetAppSpecificLauncherFilename(const base::string16& app_name) {
+  // Remove any characters that are illegal in Windows filenames.
+  base::FilePath sanitized_app_name =
+      web_app::internals::GetSanitizedFileName(app_name);
+
+  // If |sanitized_app_name| is a reserved filename, append '_' to allow its use
+  // as the launcher filename (e.g. "nul" => "nul_"). If |sanitized_app_name|
+  // starts with a reserved filename followed by '.', replace all '.' characters
+  // with '_' (e.g. "nul.l" => "nul_l"). This is needed because anything after
+  // '.' is interpreted as part of a file extension for the purpose of
+  // identifying reserved filenames, so appending '_' fails to legitimize a
+  // reserved filename when a '.' is present (e.g. "nul.l_" is rejected).
+  if (net::IsReservedNameOnWindows(sanitized_app_name.value())) {
+    base::string16 allowed_filename = sanitized_app_name.value();
+    if (!base::ReplaceChars(allowed_filename, L".", L"_", &allowed_filename))
+      allowed_filename.append(L"_");
+    sanitized_app_name = base::FilePath(allowed_filename);
+  }
+
+  // On Windows 8+, add .exe extension. On Windows 7, where the launcher
+  // filename is used as the app's display name in the Open With menu, omit the
+  // extension.
+  if (base::win::GetVersion() > base::win::Version::WIN7)
+    return sanitized_app_name.AddExtension(L"exe");
+  return sanitized_app_name;
+}
+
+}  // namespace
 
 namespace web_app {
 
@@ -56,31 +90,27 @@
     const base::string16& app_prog_id,
     const std::set<std::string>& file_extensions) {
   base::FilePath web_app_path =
-      web_app::GetWebAppDataDirectory(profile_path, app_id, GURL());
+      GetWebAppDataDirectory(profile_path, app_id, GURL());
   base::string16 utf16_app_name = base::UTF8ToUTF16(app_name);
   base::FilePath icon_path =
-      web_app::internals::GetIconFilePath(web_app_path, utf16_app_name);
+      internals::GetIconFilePath(web_app_path, utf16_app_name);
   base::FilePath pwa_launcher_path = GetChromePwaLauncherPath();
-  base::FilePath sanitized_app_name = internals::GetSanitizedFileName(
-      utf16_app_name + STRING16_LITERAL(".exe"));
-  // TODO(jessemckenna): Do we need to do anything differently for Win7, e.g.,
-  // not append .exe to the name? If so, then we should check for reserved
-  // file names like "CON" using net::IsReservedNameOnWindows.
   base::FilePath app_specific_launcher_path =
-      web_app_path.DirName().Append(sanitized_app_name);
+      web_app_path.Append(GetAppSpecificLauncherFilename(utf16_app_name));
+
   // Create a hard link to the chrome pwa launcher app. Delete any pre-existing
   // version of the file first.
   base::DeleteFile(app_specific_launcher_path, /*recursive=*/false);
   if (!base::CreateWinHardLink(app_specific_launcher_path, pwa_launcher_path) &&
       !base::CopyFile(pwa_launcher_path, app_specific_launcher_path)) {
-    DPLOG(ERROR) << "Unable to copy the generic shim";
+    DPLOG(ERROR) << "Unable to copy the generic PWA launcher";
     return;
   }
-  base::CommandLine app_shim_command(app_specific_launcher_path);
-  app_shim_command.AppendArg("%1");
-  app_shim_command.AppendSwitchPath(switches::kProfileDirectory,
-                                    profile_path.BaseName());
-  app_shim_command.AppendSwitchASCII(switches::kAppId, app_id);
+  base::CommandLine app_specific_launcher_command(app_specific_launcher_path);
+  app_specific_launcher_command.AppendArg("%1");
+  app_specific_launcher_command.AppendSwitchPath(switches::kProfileDirectory,
+                                                 profile_path.BaseName());
+  app_specific_launcher_command.AppendSwitchASCII(switches::kAppId, app_id);
   std::set<base::string16> file_exts;
   // Copy |file_extensions| to a string16 set in O(n) time by hinting that
   // the appended elements should go at the end of the set.
@@ -88,9 +118,9 @@
                  std::inserter(file_exts, file_exts.end()),
                  [](const std::string& ext) { return base::UTF8ToUTF16(ext); });
 
-  ShellUtil::AddFileAssociations(app_prog_id, app_shim_command, utf16_app_name,
-                                 utf16_app_name + STRING16_LITERAL(" File"),
-                                 icon_path, file_exts);
+  ShellUtil::AddFileAssociations(
+      app_prog_id, app_specific_launcher_command, utf16_app_name,
+      utf16_app_name + STRING16_LITERAL(" File"), icon_path, file_exts);
 }
 
 void RegisterFileHandlersWithOs(const AppId& app_id,
@@ -108,24 +138,26 @@
 }
 
 void UnregisterFileHandlersWithOs(const AppId& app_id, Profile* profile) {
-  // Need to delete the shim app file, since uninstall may not remove the
-  // web application directory. This must be done before cleaning up the
-  // registry, since the shim app path is retrieved from the registry.
+  // Need to delete the app-specific-launcher file, since uninstall may not
+  // remove the web application directory. This must be done before cleaning up
+  // the registry, since the app-specific-launcher path is retrieved from the
+  // registry.
 
   base::string16 prog_id = GetProgIdForApp(profile, app_id);
-  base::FilePath shim_app_path =
+  base::FilePath app_specific_launcher_path =
       ShellUtil::GetApplicationPathForProgId(prog_id);
 
   ShellUtil::DeleteFileAssociations(prog_id);
 
   // Need to delete the hardlink file as well, since extension uninstall
   // by default doesn't remove the web application directory.
-  if (!shim_app_path.empty()) {
-    base::PostTask(FROM_HERE,
-                   {base::ThreadPool(), base::MayBlock(),
-                    base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-                   base::BindOnce(IgnoreResult(&base::DeleteFile),
-                                  shim_app_path, /*recursively=*/false));
+  if (!app_specific_launcher_path.empty()) {
+    base::PostTask(
+        FROM_HERE,
+        {base::ThreadPool(), base::MayBlock(),
+         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+        base::BindOnce(IgnoreResult(&base::DeleteFile),
+                       app_specific_launcher_path, /*recursively=*/false));
   }
 }
 
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
index 608fcd6..97d3b9a 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/scoped_path_override.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/test/test_timeouts.h"
+#include "base/win/windows_version.h"
 #include "chrome/browser/web_applications/components/web_app_shortcut_win.h"
 #include "chrome/browser/web_applications/test/test_file_handler_manager.h"
 #include "chrome/common/chrome_paths.h"
@@ -76,9 +77,10 @@
         registry_override_.OverrideRegistry(HKEY_LOCAL_MACHINE));
     ASSERT_NO_FATAL_FAILURE(
         registry_override_.OverrideRegistry(HKEY_CURRENT_USER));
-    // Until the CL to create the shim app is submitted, create it by
+    // Until the CL to create the PWA launcher is submitted, create it by
     // hand. TODO(davidbienvenu): Remove this once cl/1815220 lands.
-    base::File app_shim(GetChromePwaLauncherPath(), base::File::FLAG_CREATE);
+    base::File pwa_launcher(GetChromePwaLauncherPath(),
+                            base::File::FLAG_CREATE);
   }
 
   Profile* profile() { return &profile_; }
@@ -102,17 +104,27 @@
            value == L"";
   }
 
-  base::FilePath GetShimAppPathForApp(Profile* profile,
-                                      const AppId app_id,
-                                      const std::string& sanitized_app_name) {
-    base::FilePath sanitized_path(
-        base::ASCIIToUTF16(sanitized_app_name + ".exe"));
+  // Creates a "Web Applications" directory containing a subdirectory for
+  // |app_id| inside |profile|'s data directory, then returns the expected app-
+  // launcher path inside the subdirectory for |app_id|.
+  base::FilePath CreateDataDirectoryAndGetLauncherPathForApp(
+      Profile* profile,
+      const AppId app_id,
+      const std::string& sanitized_app_name) {
     base::FilePath web_app_dir(
-        GetWebAppDataDirectory(profile->GetPath(), app_id, GURL()).DirName());
+        GetWebAppDataDirectory(profile->GetPath(), app_id, GURL()));
     // Make sure web app dir exists. Normally installing an extension would
     // handle this.
     EXPECT_TRUE(base::CreateDirectory(web_app_dir));
-    return web_app_dir.Append(sanitized_path);
+
+    base::FilePath app_specific_launcher_filename(
+        base::ASCIIToUTF16(sanitized_app_name));
+    if (base::win::GetVersion() > base::win::Version::WIN7) {
+      app_specific_launcher_filename =
+          app_specific_launcher_filename.AddExtension(L"exe");
+    }
+
+    return web_app_dir.Append(app_specific_launcher_filename);
   }
 
  private:
@@ -150,15 +162,18 @@
   std::set<std::string> file_extensions({"txt", "doc"});
   AppId app_id("app_id");
   std::string app_name("app name");
+  base::FilePath app_specific_launcher_path =
+      CreateDataDirectoryAndGetLauncherPathForApp(profile(), app_id, app_name);
+
   RegisterFileHandlersWithOs(app_id, app_name, profile(), file_extensions,
                              /*mime_types=*/{});
-  base::FilePath shim_app_path =
-      GetShimAppPathForApp(profile(), app_id, app_name);
-  EXPECT_TRUE(!shim_app_path.empty());
   base::ThreadPoolInstance::Get()->FlushForTesting();
-  EXPECT_TRUE(base::PathExists(shim_app_path));
-  EXPECT_EQ(shim_app_path, ShellUtil::GetApplicationPathForProgId(
-                               GetProgIdForApp(profile(), app_id)));
+  base::FilePath registered_app_path = ShellUtil::GetApplicationPathForProgId(
+      GetProgIdForApp(profile(), app_id));
+  EXPECT_FALSE(registered_app_path.empty());
+  EXPECT_TRUE(base::PathExists(app_specific_launcher_path));
+  EXPECT_EQ(app_specific_launcher_path, registered_app_path);
+
   // .txt should have |app_name| in its Open With list.
   EXPECT_TRUE(ProgIdRegisteredForFileExtension(".txt", app_id));
   EXPECT_TRUE(ProgIdRegisteredForFileExtension(".doc", app_id));
@@ -166,48 +181,97 @@
 
 TEST_F(WebAppFileHandlerRegistrationWinTest, UnregisterFileHandlersForWebApp) {
   // Register file handlers, and then verify that unregistering removes
-  // the registry settings and the shim app.
+  // the registry settings and the app-specific launcher.
   std::set<std::string> file_extensions({"txt", "doc"});
   AppId app_id("app_id");
   std::string app_name("app name");
+  base::FilePath app_specific_launcher_path =
+      CreateDataDirectoryAndGetLauncherPathForApp(profile(), app_id, app_name);
+
   RegisterFileHandlersWithOs(app_id, app_name, profile(), file_extensions,
                              /*mime_types=*/{});
-  base::FilePath shim_app_path =
-      GetShimAppPathForApp(profile(), app_id, app_name);
   base::ThreadPoolInstance::Get()->FlushForTesting();
-  EXPECT_TRUE(base::PathExists(shim_app_path));
+  EXPECT_TRUE(base::PathExists(app_specific_launcher_path));
   EXPECT_TRUE(ProgIdRegisteredForFileExtension(".txt", app_id));
   EXPECT_TRUE(ProgIdRegisteredForFileExtension(".doc", app_id));
 
   UnregisterFileHandlersWithOs(app_id, profile());
   base::ThreadPoolInstance::Get()->FlushForTesting();
-  EXPECT_FALSE(base::PathExists(shim_app_path));
+  EXPECT_FALSE(base::PathExists(app_specific_launcher_path));
 
   EXPECT_FALSE(ProgIdRegisteredForFileExtension(".txt", app_id));
   EXPECT_FALSE(ProgIdRegisteredForFileExtension(".doc", app_id));
 }
 
-// Test that invalid file name characters in app_name are replaced with '_',
-// and that ".exe" is appended to the shim app name, when file handlers
-// are registered for a Web App.
+// Test that invalid file name characters in app_name are replaced with '_'.
 TEST_F(WebAppFileHandlerRegistrationWinTest, AppNameWithInvalidChars) {
   std::set<std::string> file_extensions({"txt"});
   AppId app_id("app_id");
+
   // '*' is an invalid char in Windows file names, so it should be replaced
   // with '_'.
   std::string app_name("app*name");
-  std::string sanitized_app_name("app_name");
+  base::FilePath app_specific_launcher_path =
+      CreateDataDirectoryAndGetLauncherPathForApp(profile(), app_id,
+                                                  "app_name");
+
   RegisterFileHandlersWithOs(app_id, app_name, profile(),
                              /*file_extensions*/ {"txt"}, /*mime_types=*/{});
-  base::FilePath shim_app_path =
-      GetShimAppPathForApp(profile(), app_id, sanitized_app_name);
+
   base::ThreadPoolInstance::Get()->FlushForTesting();
   base::FilePath registered_app_path = ShellUtil::GetApplicationPathForProgId(
       GetProgIdForApp(profile(), app_id));
   EXPECT_FALSE(registered_app_path.empty());
-  EXPECT_TRUE(base::PathExists(shim_app_path));
-  EXPECT_EQ(shim_app_path.BaseName().value(),
-            base::UTF8ToUTF16(sanitized_app_name + ".exe"));
+  EXPECT_TRUE(base::PathExists(app_specific_launcher_path));
+  EXPECT_EQ(app_specific_launcher_path, registered_app_path);
+}
+
+// Test that an app name that is a reserved filename on Windows has '_' appended
+// to it when used as a filename for its launcher.
+TEST_F(WebAppFileHandlerRegistrationWinTest, AppNameIsReservedFilename) {
+  std::set<std::string> file_extensions({"txt"});
+  AppId app_id("app_id");
+
+  // "con" is a reserved filename on Windows, so it should have '_' appended.
+  std::string app_name("con");
+  base::FilePath app_specific_launcher_path =
+      CreateDataDirectoryAndGetLauncherPathForApp(profile(), app_id, "con_");
+
+  RegisterFileHandlersWithOs(app_id, app_name, profile(),
+                             /*file_extensions*/ {"txt"}, /*mime_types=*/{});
+
+  base::ThreadPoolInstance::Get()->FlushForTesting();
+  base::FilePath registered_app_path = ShellUtil::GetApplicationPathForProgId(
+      GetProgIdForApp(profile(), app_id));
+  EXPECT_FALSE(registered_app_path.empty());
+  EXPECT_TRUE(base::PathExists(app_specific_launcher_path));
+  EXPECT_EQ(app_specific_launcher_path, registered_app_path);
+}
+
+// Test that an app name that consists of a reserved filename on Windows plus
+// '.' has all its '.' characters replaced by '_' when used as a filename for
+// its launcher.
+TEST_F(WebAppFileHandlerRegistrationWinTest, AppNameIsReservedFilenameWithDot) {
+  std::set<std::string> file_extensions({"txt"});
+  AppId app_id("app_id");
+
+  // "con" is a reserved filename on Windows, and appending '_' won't make it
+  // legitimate (because any text after '.' is interpreted as part of a file
+  // extension), so all '.' characters should be replaced with '_'.
+  std::string app_name("con.nect");
+  base::FilePath app_specific_launcher_path =
+      CreateDataDirectoryAndGetLauncherPathForApp(profile(), app_id,
+                                                  "con_nect");
+
+  RegisterFileHandlersWithOs(app_id, app_name, profile(),
+                             /*file_extensions*/ {"txt"}, /*mime_types=*/{});
+
+  base::ThreadPoolInstance::Get()->FlushForTesting();
+  base::FilePath registered_app_path = ShellUtil::GetApplicationPathForProgId(
+      GetProgIdForApp(profile(), app_id));
+  EXPECT_FALSE(registered_app_path.empty());
+  EXPECT_TRUE(base::PathExists(app_specific_launcher_path));
+  EXPECT_EQ(app_specific_launcher_path, registered_app_path);
 }
 
 }  // namespace web_app
diff --git a/chrome/services/keymaster/public/mojom/cert_store.mojom b/chrome/services/keymaster/public/mojom/cert_store.mojom
index 976d21e9..9a705c1 100644
--- a/chrome/services/keymaster/public/mojom/cert_store.mojom
+++ b/chrome/services/keymaster/public/mojom/cert_store.mojom
@@ -30,8 +30,6 @@
   kOk,
   // Failed with error.
   kFailed,
-  // Inspect Chrome's logs for the exact net::Error.
-  kNetError,
   kUnsupportedAlgorithm,
 };
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 9c81d1c..9b3891c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -519,6 +519,7 @@
     data_deps = [ "//testing/buildbot/filters:android_browsertests_filters" ]
 
     sources = [
+      "../browser/android/webapk/webapk_icon_hasher_browsertest.cc",
       "../browser/engagement/important_sites_util_browsertest.cc",
       "../browser/games/games_service_browsertest.cc",
       "../browser/payments/empty_parameters_browsertest.cc",
@@ -546,6 +547,7 @@
       "$root_gen_dir/components/dev_ui_components_resources.pak",
       "$root_out_dir/browser_tests.pak",
       "//components/test/data/payments/",
+      "//chrome/test/data/banners/",
       "//chrome/test/data/ssl/",
     ]
   }
diff --git a/chrome/test/chromedriver/BUILD.gn b/chrome/test/chromedriver/BUILD.gn
index 4993ce8..4553686d 100644
--- a/chrome/test/chromedriver/BUILD.gn
+++ b/chrome/test/chromedriver/BUILD.gn
@@ -317,9 +317,7 @@
 
 executable("chromedriver") {
   testonly = true
-  sources = [
-    "server/chromedriver_server.cc",
-  ]
+  sources = [ "server/chromedriver_server.cc" ]
 
   # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
@@ -350,9 +348,7 @@
     "//testing/xvfb.py",
   ]
 
-  data_deps = [
-    "//chrome/test/chromedriver",
-  ]
+  data_deps = [ "//chrome/test/chromedriver" ]
   if (is_component_build && is_mac) {
     data_deps += [ "//chrome:chrome_framework" ]
   }
@@ -365,9 +361,7 @@
 
 python_library("chromedriver_replay_unittests") {
   testonly = true
-  deps = [
-    ":chromedriver",
-  ]
+  deps = [ ":chromedriver" ]
 
   pydeps_file = "log_replay/client_replay_unittest.pydeps"
 }
diff --git a/chrome/test/chromedriver/test/webview_shell/BUILD.gn b/chrome/test/chromedriver/test/webview_shell/BUILD.gn
index 6dadb55..d6f1e70 100644
--- a/chrome/test/chromedriver/test/webview_shell/BUILD.gn
+++ b/chrome/test/chromedriver/test/webview_shell/BUILD.gn
@@ -10,11 +10,7 @@
 }
 android_apk("chromedriver_webview_shell_apk") {
   apk_name = "ChromeDriverWebViewShell"
-  deps = [
-    ":chromedriver_webview_shell_resources",
-  ]
-  sources = [
-    "java/src/org/chromium/chromedriver_webview_shell/Main.java",
-  ]
+  deps = [ ":chromedriver_webview_shell_resources" ]
+  sources = [ "java/src/org/chromium/chromedriver_webview_shell/Main.java" ]
   android_manifest = "java/AndroidManifest.xml"
 }
diff --git a/chrome/test/data/arc_default_apps/test_app2.json b/chrome/test/data/arc_default_apps/test_app2.json
deleted file mode 100644
index 8fe777d..0000000
--- a/chrome/test/data/arc_default_apps/test_app2.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "name": "TestApp2",
-  "package_name": "test.app2",
-  "activity": "test.app2.activity",
-  "system": true,
-  "app_path": "test_app2"
-}
diff --git a/chrome/test/data/banners/launcher-icon-max-age.png b/chrome/test/data/banners/launcher-icon-max-age.png
new file mode 100644
index 0000000..54a4733
--- /dev/null
+++ b/chrome/test/data/banners/launcher-icon-max-age.png
Binary files differ
diff --git a/chrome/test/data/banners/launcher-icon-max-age.png.mock-http-headers b/chrome/test/data/banners/launcher-icon-max-age.png.mock-http-headers
new file mode 100644
index 0000000..fc909d61
--- /dev/null
+++ b/chrome/test/data/banners/launcher-icon-max-age.png.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Cache-control: max-age=60000
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
index 2afa1f39..9007f1f 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
@@ -1250,3 +1250,188 @@
         });
   });
 });
+
+suite('PrintServerTests', function() {
+  let page = null;
+  let dialog = null;
+
+  /** @type {?settings.TestCupsPrintersBrowserProxy} */
+  let cupsPrintersBrowserProxy = null;
+
+  /** @type {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
+  let mojoApi_;
+
+  suiteSetup(function() {
+    mojoApi_ = new FakeNetworkConfig();
+    network_config.MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_;
+  });
+
+  setup(function() {
+    const mojom = chromeos.networkConfig.mojom;
+    cupsPrintersBrowserProxy =
+        new printerBrowserProxy.TestCupsPrintersBrowserProxy;
+
+    settings.CupsPrintersBrowserProxyImpl.instance_ = cupsPrintersBrowserProxy;
+
+    // Simulate internet connection.
+    mojoApi_.resetForTest();
+    const wifi1 =
+        OncMojo.getDefaultNetworkState(mojom.NetworkType.kWiFi, 'wifi1');
+    wifi1.connectionState = mojom.ConnectionStateType.kOnline;
+    mojoApi_.addNetworksForTest([wifi1]);
+
+    PolymerTest.clearBody();
+    settings.navigateTo(settings.routes.CUPS_PRINTERS);
+
+    page = document.createElement('settings-cups-printers');
+    document.body.appendChild(page);
+    assertTrue(!!page);
+    dialog = page.$$('settings-cups-add-printer-dialog');
+    assertTrue(!!dialog);
+
+    Polymer.dom.flush();
+  });
+
+  teardown(function() {
+    mojoApi_.resetForTest();
+    cupsPrintersBrowserProxy.reset();
+    page.remove();
+    dialog = null;
+    page = null;
+  });
+
+  /**
+   * @param {!HTMLElement} page
+   * @return {?HTMLElement} Returns the print server dialog if it is available.
+   * @private
+   */
+  function getPrintServerDialog(page) {
+    assertTrue(!!page);
+    dialog = page.$$('settings-cups-add-printer-dialog');
+    return dialog.$$('add-print-server-dialog');
+  }
+
+  /**
+   * Opens the add print server dialog, inputs |address| with the specified
+   * |error|. Adds the print server and returns a promise for handling the add
+   * event.
+   * @param {string} address
+   * @param {number} error
+   * @return {!Promise} The promise returned when queryPrintServer is called.
+   * @private
+   */
+  function addPrintServer(address, error) {
+    // Open the add manual printe dialog.
+    assertTrue(!!page);
+    dialog.open();
+    Polymer.dom.flush();
+
+    const addPrinterDialog = dialog.$$('add-printer-manually-dialog');
+    // Switch to Add print server dialog.
+    addPrinterDialog.$$('#print-server-button').click();
+    Polymer.dom.flush();
+    const printServerDialog = dialog.$$('add-print-server-dialog');
+    assertTrue(!!printServerDialog);
+
+    Polymer.dom.flush();
+    cupsPrintersBrowserProxy.setQueryPrintServerResult(error);
+    return test_util.flushTasks().then(() => {
+      // Fill dialog with the server address.
+      const address = printServerDialog.$$('#printServerAddressInput');
+      assertTrue(!!address);
+      address.value = address;
+
+      // Add the print server.
+      const button = printServerDialog.$$('.action-button');
+      // Button should not be disabled before clicking on it.
+      assertTrue(!button.disabled);
+      button.click();
+
+      // Clicking on the button should disable it.
+      assertTrue(button.disabled);
+      return cupsPrintersBrowserProxy.whenCalled('queryPrintServer');
+    });
+  }
+
+  /**
+   * @param {string} expectedError
+   * @private
+   */
+  function verifyErrorMessage(expectedError) {
+    // Assert that the dialog did not close on errors.
+    const printServerDialog = getPrintServerDialog(page);
+    const dialogError = printServerDialog.$$('#server-dialog-error');
+    // Assert that the dialog error is displayed.
+    assertTrue(!dialogError.hidden);
+    assertEquals(loadTimeData.getString(expectedError), dialogError.errorText);
+  }
+
+  test('AddPrintServerIsSuccessful', function() {
+    // Initialize the return result from adding a print server.
+    cupsPrintersBrowserProxy.printServerPrinters =
+        /** @type{} CupsPrintServerPrintersInfo*/ ({
+          printerList: [
+            cups_printer_test_util.createCupsPrinterInfo(
+                'nameA', 'addressA', 'idA'),
+            cups_printer_test_util.createCupsPrinterInfo(
+                'nameB', 'addressB', 'idB')
+          ]
+        });
+    return addPrintServer('serverAddressA', PrintServerResult.NO_ERRORS)
+        .then(() => {
+          Polymer.dom.flush();
+          const toast = page.$$('#printServerErrorToast');
+          assertTrue(toast.open);
+          assertEquals(
+              loadTimeData.getStringF(
+                  'printServerFoundManyPrinters',
+                  /*expectedPrintersLength=*/ 2),
+              toast.textContent.trim());
+        });
+  });
+
+  test('AddPrintServerAddressError', function() {
+    cupsPrintersBrowserProxy.printServerPrinters =
+        /** @type{} CupsPrintServerPrintersInfo*/ ({printerList: []});
+    return addPrintServer('serverAddressA', PrintServerResult.INCORRECT_URL)
+        .then(() => {
+          Polymer.dom.flush();
+          const printServerDialog = getPrintServerDialog(page);
+          // Assert that the dialog did not close on errors.
+          assertTrue(!!printServerDialog);
+          // Assert that the address input field is invalid.
+          assertTrue(printServerDialog.$$('#printServerAddressInput').invalid);
+        });
+  });
+
+  test('AddPrintServerConnectionError', function() {
+    cupsPrintersBrowserProxy.printServerPrinters =
+        /** @type{} CupsPrintServerPrintersInfo*/ ({printerList: []});
+    return addPrintServer('serverAddressA', PrintServerResult.CONNECTION_ERROR)
+        .then(() => {
+          Polymer.dom.flush();
+          verifyErrorMessage('printServerConnectionError');
+        });
+  });
+
+  test('AddPrintServerReachableServerButIppResponseError', function() {
+    cupsPrintersBrowserProxy.printServerPrinters =
+        /** @type{} CupsPrintServerPrintersInfo*/ ({printerList: []});
+    return addPrintServer(
+               'serverAddressA', PrintServerResult.CANNOT_PARSE_IPP_RESPONSE)
+        .then(() => {
+          Polymer.dom.flush();
+          verifyErrorMessage('printServerConfigurationErrorMessage');
+        });
+  });
+
+  test('AddPrintServerReachableServerButHttpResponseError', function() {
+    cupsPrintersBrowserProxy.printServerPrinters =
+        /** @type{} CupsPrintServerPrintersInfo*/ ({printerList: []});
+    return addPrintServer('serverAddressA', PrintServerResult.HTTP_ERROR)
+        .then(() => {
+          Polymer.dom.flush();
+          verifyErrorMessage('printServerConfigurationErrorMessage');
+        });
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 89b23c0c..22dd2b9 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -1030,11 +1030,18 @@
   }
 
   /** @override */
+  get featureList() {
+    return {enabled: ['features::kPrintServerUi']};
+  }
+
+  /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/assert.js',
       BROWSER_SETTINGS_PATH + '../test_util.js',
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      BROWSER_SETTINGS_PATH + '../chromeos/fake_network_config_mojom.js',
+      'cups_printer_test_utils.js',
       'test_cups_printers_browser_proxy.js',
       'cups_printer_test_utils.js',
       'cups_printer_page_tests.js',
diff --git a/chrome/test/data/webui/settings/chromeos/test_cups_printers_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_cups_printers_browser_proxy.js
index b5ecb01..4c5a2f51 100644
--- a/chrome/test/data/webui/settings/chromeos/test_cups_printers_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_cups_printers_browser_proxy.js
@@ -47,6 +47,12 @@
       this.getPrinterInfoResult_ = null;
 
       /**
+       * Contains the result code from querying a print server.
+       * @private {PrintServerResult}
+       */
+      this.queryPrintServerResult_ = null;
+
+      /**
        * If set, 'addDiscoveredPrinter' will fail and the promise will be
        * rejected with this printer.
        * @private {CupsPrinterInfo}
@@ -143,6 +149,9 @@
     /** @override */
     queryPrintServer(serverUrl) {
       this.methodCalled('queryPrintServer', serverUrl);
+      if (this.queryPrintServerResult_ != PrintServerResult.NO_ERRORS) {
+        return Promise.reject(this.queryPrintServerResult_);
+      }
       return Promise.resolve(this.printServerPrinters);
     }
 
@@ -157,6 +166,11 @@
       this.getPrinterInfoResult_ = result;
     }
 
+    /** @param {PrintServerResult} result */
+    setQueryPrintServerResult(result) {
+      this.queryPrintServerResult_ = result;
+    }
+
     /** @param {!CupsPrinterInfo} printer */
     setAddDiscoveredPrinterFailure(printer) {
       this.addDiscoveredFailedPrinter_ = printer;
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 5cea2c2d9..5630aca 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -25,9 +25,7 @@
 # Depends on all non-test targets that should be built by the Chromecast
 # internal build infrastructure.
 group("all") {
-  deps = [
-    "//chromecast/build:archive",
-  ]
+  deps = [ "//chromecast/build:archive" ]
   if (is_android && chromecast_branding == "public") {
     deps += [ ":cast_shell_apk" ]
   }
@@ -147,6 +145,7 @@
         "--no-sandbox",
         "--enable-local-file-accesses",
         "--test-launcher-jobs=1",
+        "--enable-hardware-overlays=\"\"",
       ]
 
       gtest_excludes = []
@@ -446,15 +445,11 @@
     "//chromecast/browser:browsertests",
   ]
 
-  data_deps = [
-    ":chromecast_locales_pak",
-  ]
+  data_deps = [ ":chromecast_locales_pak" ]
 }
 
 group("cast_shell_lib") {
-  data_deps = [
-    ":cast_shell_pak",
-  ]
+  data_deps = [ ":cast_shell_pak" ]
 
   deps = [
     "//chromecast/app",
@@ -470,9 +465,7 @@
 }
 
 cast_executable("cast_shell") {
-  sources = [
-    "app/cast_main.cc",
-  ]
+  sources = [ "app/cast_main.cc" ]
 
   deps = [
     ":cast_shell_lib",
@@ -545,12 +538,9 @@
   repack("_cast_repack_${locale}") {
     visibility = [ ":chromecast_locales_pak" ]
     output = "$root_out_dir/chromecast_locales/${locale}.pak"
-    sources = [
-      "$root_gen_dir/chromecast/app/chromecast_settings_${locale}.pak",
-    ]
-    deps = [
-      "//chromecast/app:chromecast_settings",
-    ]
+    sources =
+        [ "$root_gen_dir/chromecast/app/chromecast_settings_${locale}.pak" ]
+    deps = [ "//chromecast/app:chromecast_settings" ]
 
     if (chromecast_branding != "public") {
       sources += [
@@ -608,9 +598,7 @@
   android_assets("cast_shell_apk_assets") {
     assert(v8_use_external_startup_data)
 
-    sources = [
-      "$root_out_dir/assets/cast_shell.pak",
-    ]
+    sources = [ "$root_out_dir/assets/cast_shell.pak" ]
 
     deps = [
       ":cast_shell_apk_locale_assets",
@@ -630,9 +618,7 @@
       renaming_destinations += [ "stored-locales/${locale}.pak" ]
     }
 
-    deps = [
-      ":chromecast_locales_pak",
-    ]
+    deps = [ ":chromecast_locales_pak" ]
 
     treat_as_locale_paks = true
     disable_compression = true
diff --git a/chromecast/activity/BUILD.gn b/chromecast/activity/BUILD.gn
index 69b7534..4247e40f 100644
--- a/chromecast/activity/BUILD.gn
+++ b/chromecast/activity/BUILD.gn
@@ -5,11 +5,7 @@
 import("//chromecast/chromecast.gni")
 
 cast_source_set("activity") {
-  sources = [
-    "queryable_data_host.h",
-  ]
+  sources = [ "queryable_data_host.h" ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
diff --git a/chromecast/app/BUILD.gn b/chromecast/app/BUILD.gn
index d3cddc4b..fdcca6a 100644
--- a/chromecast/app/BUILD.gn
+++ b/chromecast/app/BUILD.gn
@@ -72,9 +72,7 @@
 
 cast_source_set("test_support") {
   testonly = true
-  sources = [
-    "cast_test_launcher.cc",
-  ]
+  sources = [ "cast_test_launcher.cc" ]
 
   deps = [
     ":app",
@@ -89,9 +87,7 @@
 
 cast_source_set("unittests") {
   testonly = true
-  sources = [
-    "linux/cast_crash_reporter_client_unittest.cc",
-  ]
+  sources = [ "linux/cast_crash_reporter_client_unittest.cc" ]
 
   deps = [
     "//base",
@@ -125,16 +121,12 @@
   source = "//chromecast/app/resources/chromecast_settings.grd"
   resource_ids = "//chromecast/app/resources/resource_ids"
 
-  outputs = [
-    "grit/chromecast_settings.h",
-  ]
+  outputs = [ "grit/chromecast_settings.h" ]
   foreach(locale, cast_locales) {
     outputs += [ "chromecast_settings_${locale}.pak" ]
   }
 
-  deps = [
-    ":verify_cast_locales",
-  ]
+  deps = [ ":verify_cast_locales" ]
 }
 
 # This target ensures that Chromecast developers are notified when locales
@@ -154,7 +146,5 @@
 
   args += cast_locales
 
-  outputs = [
-    stamp_file,
-  ]
+  outputs = [ stamp_file ]
 }
diff --git a/chromecast/base/BUILD.gn b/chromecast/base/BUILD.gn
index cd5a126..3143b34b 100644
--- a/chromecast/base/BUILD.gn
+++ b/chromecast/base/BUILD.gn
@@ -130,9 +130,7 @@
     "chromecast_switches.h",
   ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
 
 # command line initialization code for shared libs
@@ -193,9 +191,7 @@
 
 # Declares CreateSysInfo() without defining the implementation (see below).
 cast_source_set("cast_sys_info_util") {
-  public = [
-    "cast_sys_info_util.h",
-  ]
+  public = [ "cast_sys_info_util.h" ]
 }
 
 # Defines a CastSysInfoDummy suitable for use in tests, and public builds.
@@ -212,15 +208,9 @@
 
 # Links a CreateSysInfo() implementation that returns CastSysInfoDummy.
 cast_source_set("dummy_create_sys_info") {
-  sources = [
-    "cast_sys_info_util_simple.cc",
-  ]
-  deps = [
-    ":dummy_cast_sys_info",
-  ]
-  public_deps = [
-    ":cast_sys_info_util",
-  ]
+  sources = [ "cast_sys_info_util_simple.cc" ]
+  deps = [ ":dummy_cast_sys_info" ]
+  public_deps = [ ":cast_sys_info_util" ]
 }
 
 # Links the default CreateSysInfo() implementation for Android.
@@ -233,9 +223,7 @@
       "//chromecast/browser:jni_headers",
       "//chromecast/public",
     ]
-    public_deps = [
-      ":cast_sys_info_util",
-    ]
+    public_deps = [ ":cast_sys_info_util" ]
     sources = [
       "cast_sys_info_android.cc",
       "cast_sys_info_android.h",
@@ -252,9 +240,7 @@
   # Target for OEM partners to override sys_info shared library, i.e.
   # libcast_sys_info_1.0.so.
   cast_shared_library("libcast_sys_info_1.0") {
-    sources = [
-      "cast_sys_info_shlib.cc",
-    ]
+    sources = [ "cast_sys_info_shlib.cc" ]
 
     deps = [
       ":cast_sys_info_util",
@@ -265,9 +251,7 @@
 
   # Links a CreateSysInfo() that loads libcast_sys_info_1.0.so.
   cast_source_set("shlib_create_sys_info") {
-    sources = [
-      "cast_sys_info_util_shlib.cc",
-    ]
+    sources = [ "cast_sys_info_util_shlib.cc" ]
 
     deps = [
       ":libcast_sys_info_1.0",
@@ -275,26 +259,18 @@
       "//chromecast/public",
     ]
 
-    public_deps = [
-      ":cast_sys_info_util",
-    ]
+    public_deps = [ ":cast_sys_info_util" ]
   }
 }  # is_android
 
 # Links the default CreateSysInfo() for this build configuration.
 group("default_create_sys_info") {
   if (is_android) {
-    public_deps = [
-      ":android_create_sys_info",
-    ]
+    public_deps = [ ":android_create_sys_info" ]
   } else if (chromecast_branding == "public") {
-    public_deps = [
-      ":dummy_create_sys_info",
-    ]
+    public_deps = [ ":dummy_create_sys_info" ]
   } else {
-    public_deps = [
-      ":shlib_create_sys_info",
-    ]
+    public_deps = [ ":shlib_create_sys_info" ]
   }
 }
 
@@ -313,9 +289,7 @@
   template_file = "version.h.in"
   output = "$target_gen_dir/version.h"
 
-  sources = [
-    "//chrome/VERSION",
-  ]
+  sources = [ "//chrome/VERSION" ]
   extra_args = [
     "-e",
     "VERSION_FULL=\"%s.%s.%s.%s\"%(MAJOR,MINOR,BUILD,PATCH)",
diff --git a/chromecast/base/component/BUILD.gn b/chromecast/base/component/BUILD.gn
index 9f81f18..151da70f 100644
--- a/chromecast/base/component/BUILD.gn
+++ b/chromecast/base/component/BUILD.gn
@@ -12,15 +12,11 @@
     "component_internal.h",
   ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
 
 test("cast_component_unittests") {
-  sources = [
-    "component_unittest.cc",
-  ]
+  sources = [ "component_unittest.cc" ]
 
   deps = [
     ":component",
diff --git a/chromecast/base/metrics/BUILD.gn b/chromecast/base/metrics/BUILD.gn
index f1bde85..3de5438e 100644
--- a/chromecast/base/metrics/BUILD.gn
+++ b/chromecast/base/metrics/BUILD.gn
@@ -13,9 +13,7 @@
     "grouped_histogram.h",
   ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
 
 cast_source_set("test_support") {
@@ -26,9 +24,7 @@
     "mock_cast_metrics_helper.h",
   ]
 
-  public_deps = [
-    ":metrics",
-  ]
+  public_deps = [ ":metrics" ]
 
   deps = [
     "//base",
diff --git a/chromecast/bindings/BUILD.gn b/chromecast/bindings/BUILD.gn
index a0cebfc..5e488a7 100644
--- a/chromecast/bindings/BUILD.gn
+++ b/chromecast/bindings/BUILD.gn
@@ -9,9 +9,7 @@
 }
 
 source_set("named_message_port_connector_resources") {
-  data = [
-    "resources/named_message_port_connector.js",
-  ]
+  data = [ "resources/named_message_port_connector.js" ]
 }
 
 grit("bindings_resources") {
@@ -28,12 +26,8 @@
     "bindings_manager.cc",
     "bindings_manager.h",
   ]
-  deps = [
-    "//base",
-  ]
-  public_deps = [
-    "//mojo/public/cpp/bindings",
-  ]
+  deps = [ "//base" ]
+  public_deps = [ "//mojo/public/cpp/bindings" ]
 }
 
 if (is_fuchsia) {
@@ -50,9 +44,7 @@
       "//fuchsia/base:message_port",
     ]
 
-    public_deps = [
-      ":bindings_manager",
-    ]
+    public_deps = [ ":bindings_manager" ]
   }
 }
 
@@ -71,26 +63,18 @@
       "//third_party/blink/public/common",
     ]
 
-    data = [
-      "named_message_port_connector.js",
-    ]
+    data = [ "named_message_port_connector.js" ]
 
-    public_deps = [
-      ":bindings_manager",
-    ]
+    public_deps = [ ":bindings_manager" ]
   }
 
   source_set("browsertests_cast") {
     testonly = true
-    sources = [
-      "bindings_manager_cast_browsertest.cc",
-    ]
+    sources = [ "bindings_manager_cast_browsertest.cc" ]
 
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
-    data = [
-      "//chromecast/bindings/testdata",
-    ]
+    data = [ "//chromecast/bindings/testdata" ]
 
     deps = [
       "//base",
diff --git a/chromecast/build/BUILD.gn b/chromecast/build/BUILD.gn
index f0730131..b696a9f 100644
--- a/chromecast/build/BUILD.gn
+++ b/chromecast/build/BUILD.gn
@@ -8,26 +8,16 @@
   artifact_type_suffix = "$cast_build_incremental-$target_cpu"
 
   copy("archive_public_cast_apk") {
-    sources = [
-      "$root_out_dir/apks/CastShell.apk",
-    ]
-    outputs = [
-      "$root_out_dir/archive/CastShell-$artifact_type_suffix.apk",
-    ]
-    deps = [
-      "//chromecast:cast_shell_apk",
-    ]
+    sources = [ "$root_out_dir/apks/CastShell.apk" ]
+    outputs = [ "$root_out_dir/archive/CastShell-$artifact_type_suffix.apk" ]
+    deps = [ "//chromecast:cast_shell_apk" ]
   }
 }
 
 group("archive") {
   if (chromecast_branding == "internal") {
-    deps = [
-      "//chromecast/internal/build:archive",
-    ]
+    deps = [ "//chromecast/internal/build:archive" ]
   } else if (is_android) {
-    deps = [
-      ":archive_public_cast_apk",
-    ]
+    deps = [ ":archive_public_cast_apk" ]
   }
 }
diff --git a/chromecast/build/tests/cast_test.gni b/chromecast/build/tests/cast_test.gni
index dfead10f..1b716a7 100644
--- a/chromecast/build/tests/cast_test.gni
+++ b/chromecast/build/tests/cast_test.gni
@@ -216,9 +216,7 @@
     _test_name = get_label_info(_test, "name")
     group(_test_name + "_cast_runtime_deps") {
       testonly = true
-      data_deps = [
-        _test,
-      ]
+      data_deps = [ _test ]
       write_runtime_deps =
           "${_shared_dir}/runtime_deps/${_test_name}_runtime_deps.txt"
     }
@@ -230,9 +228,7 @@
   action(target_name + "_create_list") {
     script = "//chromecast/tools/build/generate_test_lists.py"
 
-    outputs = [
-      "$_output_prefix.tests",
-    ]
+    outputs = [ "$_output_prefix.tests" ]
 
     args = [
       "-o",
@@ -256,9 +252,7 @@
   action(target_name + "_filters") {
     script = "//chromecast/tools/build/generate_test_lists.py"
 
-    outputs = [
-      "$_output_prefix.filters",
-    ]
+    outputs = [ "$_output_prefix.filters" ]
 
     args = [
       "-o",
@@ -389,9 +383,7 @@
   action(_pack_build_action) {
     script = "//chromecast/tools/build/generate_test_lists.py"
 
-    outputs = [
-      invoker.build_list_path,
-    ]
+    outputs = [ invoker.build_list_path ]
 
     args = [
       "-o",
@@ -437,9 +429,7 @@
     }
 
     # Depend first on the "pack_build" step, so that the build lists are created.
-    deps = [
-      ":$_pack_build_action",
-    ]
+    deps = [ ":$_pack_build_action" ]
 
     # Next, depend on the filter steps, such that they are created before this
     # script executes.
@@ -512,9 +502,7 @@
     testonly = true
 
     script = "//chromecast/tools/build/package_test_deps.py"
-    outputs = [
-      invoker.output,
-    ]
+    outputs = [ invoker.output ]
 
     # Generate a comma separated list of filters.
     _exclude_deps = ""
diff --git a/chromecast/crash/BUILD.gn b/chromecast/crash/BUILD.gn
index b4dcd4c..a4bee04 100644
--- a/chromecast/crash/BUILD.gn
+++ b/chromecast/crash/BUILD.gn
@@ -47,9 +47,7 @@
 }
 
 cast_executable("crash_uploader") {
-  sources = [
-    "linux/crash_uploader.cc",
-  ]
+  sources = [ "linux/crash_uploader.cc" ]
 
   deps = [
     ":crash",
diff --git a/chromecast/crypto/BUILD.gn b/chromecast/crypto/BUILD.gn
index f6cda841..6af8878 100644
--- a/chromecast/crypto/BUILD.gn
+++ b/chromecast/crypto/BUILD.gn
@@ -11,17 +11,13 @@
     "signature_cache.h",
   ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
 
 # Note: this doesn't correspond to a GYP unit test and should be merged with
 # others. (b/22952424)
 test("cast_crypto_unittests") {
-  sources = [
-    "signature_cache_unittest.cc",
-  ]
+  sources = [ "signature_cache_unittest.cc" ]
 
   deps = [
     ":crypto",
diff --git a/chromecast/device/bluetooth/BUILD.gn b/chromecast/device/bluetooth/BUILD.gn
index 31fc3d6..40c7608 100644
--- a/chromecast/device/bluetooth/BUILD.gn
+++ b/chromecast/device/bluetooth/BUILD.gn
@@ -18,9 +18,7 @@
 }
 
 test("cast_bluetooth_unittests") {
-  sources = [
-    "bluetooth_util_test.cc",
-  ]
+  sources = [ "bluetooth_util_test.cc" ]
 
   deps = [
     ":util",
diff --git a/chromecast/device/bluetooth/le/BUILD.gn b/chromecast/device/bluetooth/le/BUILD.gn
index 5c138aa..6ea1081 100644
--- a/chromecast/device/bluetooth/le/BUILD.gn
+++ b/chromecast/device/bluetooth/le/BUILD.gn
@@ -34,9 +34,7 @@
     "scan_filter.h",
   ]
 
-  public_deps = [
-    "//chromecast/public",
-  ]
+  public_deps = [ "//chromecast/public" ]
 
   deps = [
     "//base",
@@ -97,10 +95,6 @@
 }
 
 fuzzer_test("le_scan_result_fuzzer") {
-  sources = [
-    "le_scan_result_fuzzer.cc",
-  ]
-  deps = [
-    ":le",
-  ]
+  sources = [ "le_scan_result_fuzzer.cc" ]
+  deps = [ ":le" ]
 }
diff --git a/chromecast/device/bluetooth/shlib/BUILD.gn b/chromecast/device/bluetooth/shlib/BUILD.gn
index 84ef01f..1c0902b0 100644
--- a/chromecast/device/bluetooth/shlib/BUILD.gn
+++ b/chromecast/device/bluetooth/shlib/BUILD.gn
@@ -10,9 +10,7 @@
     "le_scanner.h",
   ]
 
-  deps = [
-    "//chromecast/public",
-  ]
+  deps = [ "//chromecast/public" ]
 }
 
 cast_source_set("mock_shlib") {
diff --git a/chromecast/graphics/BUILD.gn b/chromecast/graphics/BUILD.gn
index 9df49f6..cad11b4 100644
--- a/chromecast/graphics/BUILD.gn
+++ b/chromecast/graphics/BUILD.gn
@@ -21,9 +21,7 @@
     "//ui/gfx",
   ]
 
-  public_deps = [
-    "//chromecast/ui/mojom",
-  ]
+  public_deps = [ "//chromecast/ui/mojom" ]
 
   if (use_aura) {
     sources += [
@@ -120,9 +118,7 @@
     "osd_plane_default.cc",
   ]
 
-  public_deps = [
-    "//chromecast/public",
-  ]
+  public_deps = [ "//chromecast/public" ]
 
   deps = [
     "//base",
diff --git a/chromecast/net/BUILD.gn b/chromecast/net/BUILD.gn
index f3f21127..ec95aa68 100644
--- a/chromecast/net/BUILD.gn
+++ b/chromecast/net/BUILD.gn
@@ -82,9 +82,7 @@
     "small_message_socket.cc",
     "small_message_socket.h",
   ]
-  public_deps = [
-    "//net",
-  ]
+  public_deps = [ "//net" ]
 
   deps = [
     ":io_buffer_pool",
@@ -102,9 +100,7 @@
     "mock_stream_socket.h",
   ]
 
-  public_deps = [
-    "//testing/gmock",
-  ]
+  public_deps = [ "//testing/gmock" ]
 
   deps = [
     "//base",
diff --git a/chromecast/public/BUILD.gn b/chromecast/public/BUILD.gn
index be4d6204..1d8641fc 100644
--- a/chromecast/public/BUILD.gn
+++ b/chromecast/public/BUILD.gn
@@ -36,21 +36,15 @@
   import("//build/config/android/rules.gni")
 
   java_cpp_enum("java_enums") {
-    sources = [
-      "avsettings.h",
-    ]
+    sources = [ "avsettings.h" ]
   }
 
   java_cpp_enum("java_enums_volume_control") {
-    sources = [
-      "volume_control.h",
-    ]
+    sources = [ "volume_control.h" ]
   }
 
   android_library("volume_control_enums_java") {
     srcjar_deps = [ ":java_enums_volume_control" ]
-    deps = [
-      "//third_party/android_deps:androidx_annotation_annotation_java",
-    ]
+    deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ]
   }
 }
diff --git a/chromecast/public/media/BUILD.gn b/chromecast/public/media/BUILD.gn
index c06f050..76aef7e 100644
--- a/chromecast/public/media/BUILD.gn
+++ b/chromecast/public/media/BUILD.gn
@@ -19,9 +19,7 @@
   ]
 
   public_configs = [ ":public_headers" ]
-  public_deps = [
-    "//chromecast/public",
-  ]
+  public_deps = [ "//chromecast/public" ]
 }
 
 # Any target which includes headers in ":media" should include it in its deps
diff --git a/chromecast/service/BUILD.gn b/chromecast/service/BUILD.gn
index dea872a..e88bc08 100644
--- a/chromecast/service/BUILD.gn
+++ b/chromecast/service/BUILD.gn
@@ -10,7 +10,5 @@
     "cast_service.h",
   ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 }
diff --git a/chromecast/system/reboot/BUILD.gn b/chromecast/system/reboot/BUILD.gn
index 64d6106..4453b88 100644
--- a/chromecast/system/reboot/BUILD.gn
+++ b/chromecast/system/reboot/BUILD.gn
@@ -11,9 +11,7 @@
     "reboot_util_core.cc",
   ]
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 
   if (chromecast_branding == "google") {
     deps += [ "//chromecast/internal/system/reboot:reboot_util" ]
@@ -34,9 +32,7 @@
 }
 
 test("cast_reboot_unittests") {
-  sources = [
-    "reboot_util_test.cc",
-  ]
+  sources = [ "reboot_util_test.cc" ]
 
   deps = [
     ":reboot_util",
@@ -50,23 +46,15 @@
 # Target for OEM partners to override reboot shared library, i.e.
 # libcast_reboot_1.0.so.
 cast_shared_library("libcast_reboot_1.0") {
-  sources = [
-    "reboot_dummy.cc",
-  ]
+  sources = [ "reboot_dummy.cc" ]
 
-  public_deps = [
-    "//chromecast/public",
-  ]
+  public_deps = [ "//chromecast/public" ]
 }
 
 if (is_fuchsia) {
   cast_source_set("fuchsia") {
-    sources = [
-      "reboot_fuchsia.cc",
-    ]
-    public_deps = [
-      "//chromecast/public",
-    ]
+    sources = [ "reboot_fuchsia.cc" ]
+    public_deps = [ "//chromecast/public" ]
     deps = [
       "//base",
       "//third_party/fuchsia-sdk/sdk:fidl",
diff --git a/chromeos/components/multidevice/expiring_remote_device_cache.cc b/chromeos/components/multidevice/expiring_remote_device_cache.cc
index 0c52415..f4d16efd 100644
--- a/chromeos/components/multidevice/expiring_remote_device_cache.cc
+++ b/chromeos/components/multidevice/expiring_remote_device_cache.cc
@@ -21,9 +21,10 @@
     const RemoteDeviceList& remote_devices) {
   remote_device_cache_->SetRemoteDevices(remote_devices);
 
-  device_ids_from_last_set_call_.clear();
+  legacy_device_ids_from_last_set_call_.clear();
+  instance_ids_from_last_set_call_.clear();
   for (const auto& device : remote_devices)
-    device_ids_from_last_set_call_.insert(device.GetDeviceId());
+    RememberIdsFromLastSetCall(device);
 }
 
 RemoteDeviceRefList ExpiringRemoteDeviceCache::GetNonExpiredRemoteDevices()
@@ -31,8 +32,14 @@
   // Only add to the output list if the entry is not stale.
   RemoteDeviceRefList remote_devices;
   for (auto device : remote_device_cache_->GetRemoteDevices()) {
-    if (device_ids_from_last_set_call_.count(device.GetDeviceId()) != 0)
+    if ((!device.instance_id().empty() &&
+         base::Contains(instance_ids_from_last_set_call_,
+                        device.instance_id())) ||
+        (!device.GetDeviceId().empty() &&
+         base::Contains(legacy_device_ids_from_last_set_call_,
+                        device.GetDeviceId()))) {
       remote_devices.push_back(device);
+    }
   }
 
   return remote_devices;
@@ -41,12 +48,22 @@
 void ExpiringRemoteDeviceCache::UpdateRemoteDevice(
     const RemoteDevice& remote_device) {
   remote_device_cache_->SetRemoteDevices({remote_device});
-  device_ids_from_last_set_call_.insert(remote_device.GetDeviceId());
+  RememberIdsFromLastSetCall(remote_device);
 }
 
 base::Optional<RemoteDeviceRef> ExpiringRemoteDeviceCache::GetRemoteDevice(
-    const std::string& device_id) const {
-  return remote_device_cache_->GetRemoteDevice(device_id);
+    const base::Optional<std::string>& instance_id,
+    const base::Optional<std::string>& legacy_device_id) const {
+  return remote_device_cache_->GetRemoteDevice(instance_id, legacy_device_id);
+}
+
+void ExpiringRemoteDeviceCache::RememberIdsFromLastSetCall(
+    const RemoteDevice& device) {
+  if (!device.instance_id.empty())
+    instance_ids_from_last_set_call_.insert(device.instance_id);
+
+  if (!device.GetDeviceId().empty())
+    legacy_device_ids_from_last_set_call_.insert(device.GetDeviceId());
 }
 
 }  // namespace multidevice
diff --git a/chromeos/components/multidevice/expiring_remote_device_cache.h b/chromeos/components/multidevice/expiring_remote_device_cache.h
index b0d49fe8..8ae0706 100644
--- a/chromeos/components/multidevice/expiring_remote_device_cache.h
+++ b/chromeos/components/multidevice/expiring_remote_device_cache.h
@@ -6,7 +6,9 @@
 #define CHROMEOS_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_
 
 #include <memory>
+#include <string>
 
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/optional.h"
 #include "chromeos/components/multidevice/remote_device.h"
@@ -41,12 +43,25 @@
   // as stale.
   void UpdateRemoteDevice(const RemoteDevice& remote_device);
 
+  // Looks up device in cache by Instance ID, |instance_id|, and by the legacy
+  // device ID from RemoteDevice::GetDeviceId(), |legacy_device_id|. Returns the
+  // first device that matches either ID. Returns null if no such device exists.
+  //
+  // For best results, pass in both IDs when available since the device could
+  // have been written to the cache with one of the IDs missing.
   base::Optional<RemoteDeviceRef> GetRemoteDevice(
-      const std::string& device_id) const;
+      const base::Optional<std::string>& instance_id,
+      const base::Optional<std::string>& legacy_device_id) const;
 
  private:
+  void RememberIdsFromLastSetCall(const RemoteDevice& device);
+
   std::unique_ptr<RemoteDeviceCache> remote_device_cache_;
-  std::set<std::string> device_ids_from_last_set_call_;
+
+  // TODO(https://crbug.com/1019206): Only track Instance IDs after v1
+  // DeviceSync is deprecated, when every device is guaranteed an Instance ID.
+  base::flat_set<std::string> legacy_device_ids_from_last_set_call_;
+  base::flat_set<std::string> instance_ids_from_last_set_call_;
 
   DISALLOW_COPY_AND_ASSIGN(ExpiringRemoteDeviceCache);
 };
diff --git a/chromeos/components/multidevice/remote_device_cache.cc b/chromeos/components/multidevice/remote_device_cache.cc
index 51b10efa..96d60ff 100644
--- a/chromeos/components/multidevice/remote_device_cache.cc
+++ b/chromeos/components/multidevice/remote_device_cache.cc
@@ -7,6 +7,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
 #include "base/stl_util.h"
+#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
@@ -41,40 +42,74 @@
 
 void RemoteDeviceCache::SetRemoteDevices(
     const RemoteDeviceList& remote_devices) {
-  for (const auto& remote_device : remote_devices) {
-    if (base::Contains(remote_device_map_, remote_device.GetDeviceId())) {
-      // TODO(khorimoto): Do not overwrite device metadata if the new device
-      // contains a stale timestamp; see https://crbug.com/856746.
+  for (const RemoteDevice& remote_device : remote_devices) {
+    if (remote_device.instance_id.empty() &&
+        remote_device.GetDeviceId().empty()) {
+      PA_LOG(ERROR) << "Cannot add RemoteDevice with missing Instance ID and "
+                    << "legacy device ID to cache.";
+      continue;
+    }
 
-      // Keep the same shared_ptr object, and simply
-      // update the RemoteDevice it references. This transparently updates
-      // the RemoteDeviceRefs used by clients.
-      *remote_device_map_[remote_device.GetDeviceId()] = remote_device;
+    std::shared_ptr<RemoteDevice> cached_device = GetRemoteDeviceFromCache(
+        remote_device.instance_id, remote_device.GetDeviceId());
+    if (cached_device) {
+      // Keep the same shared remote device pointer, and simply update the
+      // RemoteDevice it references. This transparently updates the
+      // RemoteDeviceRefs used by clients.
+      // TODO(https://crbug.com/856746): Do not overwrite device metadata if
+      // the new device contains a stale timestamp.
+      *cached_device = remote_device;
     } else {
-      remote_device_map_[remote_device.GetDeviceId()] =
-          std::make_shared<RemoteDevice>(remote_device);
+      cached_remote_devices_.push_back(
+          std::make_shared<RemoteDevice>(remote_device));
     }
   }
 
   // Intentionally leave behind devices in the map which weren't in
-  // |remote_devices|, to prevent clients from segfaulting by accessing "stale"
-  // devices.
+  // |remote_devices|, to prevent clients from segfaulting by accessing
+  // "stale" devices.
 }
 
 RemoteDeviceRefList RemoteDeviceCache::GetRemoteDevices() const {
   RemoteDeviceRefList remote_devices;
-  for (const auto& it : remote_device_map_)
-    remote_devices.push_back(RemoteDeviceRef(it.second));
+  for (const std::shared_ptr<RemoteDevice> device : cached_remote_devices_) {
+    remote_devices.push_back(RemoteDeviceRef(device));
+  }
 
   return remote_devices;
 }
 
 base::Optional<RemoteDeviceRef> RemoteDeviceCache::GetRemoteDevice(
-    const std::string& device_id) const {
-  if (!base::Contains(remote_device_map_, device_id))
-    return base::nullopt;
+    const base::Optional<std::string>& instance_id,
+    const base::Optional<std::string>& legacy_device_id) const {
+  DCHECK((instance_id && !instance_id->empty()) ||
+         (legacy_device_id && !legacy_device_id->empty()));
 
-  return RemoteDeviceRef(remote_device_map_.at(device_id));
+  std::shared_ptr<RemoteDevice> cached_device =
+      GetRemoteDeviceFromCache(instance_id, legacy_device_id);
+  if (cached_device)
+    return RemoteDeviceRef(cached_device);
+
+  return base::nullopt;
+}
+
+std::shared_ptr<RemoteDevice> RemoteDeviceCache::GetRemoteDeviceFromCache(
+    const base::Optional<std::string>& instance_id,
+    const base::Optional<std::string>& legacy_device_id) const {
+  for (const std::shared_ptr<RemoteDevice> cached_device :
+       cached_remote_devices_) {
+    if (instance_id && !instance_id->empty() &&
+        cached_device->instance_id == *instance_id) {
+      return cached_device;
+    }
+
+    if (legacy_device_id && !legacy_device_id->empty() &&
+        cached_device->GetDeviceId() == *legacy_device_id) {
+      return cached_device;
+    }
+  }
+
+  return nullptr;
 }
 
 }  // namespace multidevice
diff --git a/chromeos/components/multidevice/remote_device_cache.h b/chromeos/components/multidevice/remote_device_cache.h
index 8e0337dc..0ee4f86 100644
--- a/chromeos/components/multidevice/remote_device_cache.h
+++ b/chromeos/components/multidevice/remote_device_cache.h
@@ -6,7 +6,8 @@
 #define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_
 
 #include <memory>
-#include <unordered_map>
+#include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/optional.h"
@@ -21,6 +22,12 @@
 // SetRemoteDevices() are provided different sets of devices, the set of devices
 // returned by GetRemoteDevices() is the union of those different sets (i.e.,
 // devices are not deleted from the cache).
+//
+// All devices in the cache will have a unique Instance ID, if one exists, and a
+// unique legacy device ID, RemoteDevice::GetDeviceId(), if one exists. Every
+// device is guaranteed to have at least one non-trivial ID. If a device is
+// added with either ID matching an existing device, the existing device is
+// overwritten.
 class RemoteDeviceCache {
  public:
   class Factory {
@@ -40,14 +47,24 @@
 
   RemoteDeviceRefList GetRemoteDevices() const;
 
+  // Looks up device in cache by Instance ID, |instance_id|, and by the legacy
+  // device ID from RemoteDevice::GetDeviceId(), |legacy_device_id|. Returns the
+  // first device that matches either ID. Returns null if no such device exists.
+  //
+  // For best results, pass in both IDs when available since the device could
+  // have been written to the cache with one of the IDs missing.
   base::Optional<RemoteDeviceRef> GetRemoteDevice(
-      const std::string& device_id) const;
+      const base::Optional<std::string>& instance_id,
+      const base::Optional<std::string>& legacy_device_id) const;
 
  private:
   RemoteDeviceCache();
 
-  std::unordered_map<std::string, std::shared_ptr<RemoteDevice>>
-      remote_device_map_;
+  std::shared_ptr<RemoteDevice> GetRemoteDeviceFromCache(
+      const base::Optional<std::string>& instance_id,
+      const base::Optional<std::string>& legacy_device_id) const;
+
+  std::vector<std::shared_ptr<RemoteDevice>> cached_remote_devices_;
 
   DISALLOW_COPY_AND_ASSIGN(RemoteDeviceCache);
 };
diff --git a/chromeos/components/multidevice/remote_device_cache_unittest.cc b/chromeos/components/multidevice/remote_device_cache_unittest.cc
index 4a79ec37..f14a9776 100644
--- a/chromeos/components/multidevice/remote_device_cache_unittest.cc
+++ b/chromeos/components/multidevice/remote_device_cache_unittest.cc
@@ -35,7 +35,7 @@
     EXPECT_EQ(expected_remote_device_ref_list, remote_device_ref_list);
   }
 
-  const RemoteDeviceList test_remote_device_list_;
+  RemoteDeviceList test_remote_device_list_;
   const RemoteDeviceRefList test_remote_device_ref_list_;
   std::unique_ptr<RemoteDeviceCache> cache_;
 
@@ -45,6 +45,7 @@
 TEST_F(RemoteDeviceCacheTest, TestNoRemoteDevices) {
   VerifyCacheRemoteDevices(RemoteDeviceRefList());
   EXPECT_EQ(base::nullopt, cache_->GetRemoteDevice(
+                               test_remote_device_ref_list_[0].instance_id(),
                                test_remote_device_ref_list_[0].GetDeviceId()));
 }
 
@@ -54,7 +55,36 @@
   VerifyCacheRemoteDevices(test_remote_device_ref_list_);
   EXPECT_EQ(
       test_remote_device_ref_list_[0],
-      cache_->GetRemoteDevice(test_remote_device_ref_list_[0].GetDeviceId()));
+      cache_->GetRemoteDevice(test_remote_device_ref_list_[0].instance_id(),
+                              test_remote_device_ref_list_[0].GetDeviceId()));
+}
+
+TEST_F(RemoteDeviceCacheTest,
+       TestSetAndGetRemoteDevices_LookupByInstanceIdOrPublicKey) {
+  test_remote_device_list_[0].instance_id.clear();
+  test_remote_device_list_[1].public_key.clear();
+  cache_->SetRemoteDevices(test_remote_device_list_);
+
+  GetMutableRemoteDevice(test_remote_device_ref_list_[0])->instance_id.clear();
+  GetMutableRemoteDevice(test_remote_device_ref_list_[1])->public_key.clear();
+  VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+
+  EXPECT_EQ(
+      test_remote_device_ref_list_[0],
+      cache_->GetRemoteDevice(base::nullopt /* instance_id */,
+                              test_remote_device_ref_list_[0].GetDeviceId()));
+  EXPECT_EQ(
+      test_remote_device_ref_list_[1],
+      cache_->GetRemoteDevice(test_remote_device_ref_list_[1].instance_id(),
+                              base::nullopt /* legacy_device_id */));
+  EXPECT_EQ(
+      test_remote_device_ref_list_[2],
+      cache_->GetRemoteDevice(base::nullopt /* instance_id */,
+                              test_remote_device_ref_list_[2].GetDeviceId()));
+  EXPECT_EQ(
+      test_remote_device_ref_list_[2],
+      cache_->GetRemoteDevice(test_remote_device_ref_list_[2].instance_id(),
+                              base::nullopt /* legacy_device_id */));
 }
 
 TEST_F(RemoteDeviceCacheTest,
@@ -75,8 +105,8 @@
   remote_device.last_update_time_millis = 1000;
   cache_->SetRemoteDevices({remote_device});
 
-  RemoteDeviceRef remote_device_ref =
-      *cache_->GetRemoteDevice(remote_device.GetDeviceId());
+  RemoteDeviceRef remote_device_ref = *cache_->GetRemoteDevice(
+      remote_device.instance_id, remote_device.GetDeviceId());
   EXPECT_EQ(remote_device.name, remote_device_ref.name());
 
   // Update the device's name and update time. Since the incoming remote device
@@ -98,8 +128,8 @@
   remote_device.last_update_time_millis = 1000;
   cache_->SetRemoteDevices({remote_device});
 
-  RemoteDeviceRef remote_device_ref =
-      *cache_->GetRemoteDevice(remote_device.GetDeviceId());
+  RemoteDeviceRef remote_device_ref = *cache_->GetRemoteDevice(
+      remote_device.instance_id, remote_device.GetDeviceId());
   EXPECT_EQ(remote_device.name, remote_device_ref.name());
 
   // Update the device's name and update time, this time reducing the
diff --git a/chromeos/services/device_sync/cryptauth_client_impl.cc b/chromeos/services/device_sync/cryptauth_client_impl.cc
index dac271f..c7cdeb6 100644
--- a/chromeos/services/device_sync/cryptauth_client_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_client_impl.cc
@@ -465,8 +465,6 @@
               error_callback, partial_traffic_annotation);
 }
 
-// TODO(https://crbug.com/953087): Populate the "sender" and "trigger" fields
-// when method is used in codebase.
 void CryptAuthClientImpl::BatchNotifyGroupDevices(
     const cryptauthv2::BatchNotifyGroupDevicesRequest& request,
     const BatchNotifyGroupDevicesCallback& callback,
@@ -477,11 +475,15 @@
           "oauth2_api_call_flow",
           R"(
       semantics {
-        sender: "TBD"
+        sender: "CryptAuth Device Notifier"
         description:
           "The client sends a list of the user's devices that it wants to "
           "tickle via a GCM message."
-        trigger: "TBD"
+        trigger:
+          "The DeviceSync service has a NotifyDevices() method that triggers "
+          "this API call. Currently, that method is only used by the "
+          "multi-device host verifier to alert the current multi-device host "
+          "that it needs to enable its individual multi-device features."
         data:
           "The list of device IDs to notify as well as a specification of the "
           "the CryptAuth service (Enrollment or DeviceSync) and feature "
@@ -542,8 +544,6 @@
       callback, error_callback, partial_traffic_annotation);
 }
 
-// TODO(https://crbug.com/953087): Populate the "sender" and "trigger" fields
-// when method is used in codebase.
 void CryptAuthClientImpl::BatchSetFeatureStatuses(
     const cryptauthv2::BatchSetFeatureStatusesRequest& request,
     const BatchSetFeatureStatusesCallback& callback,
@@ -554,11 +554,15 @@
           "oauth2_api_call_flow",
           R"(
       semantics {
-        sender: "TBD"
+        sender: "CryptAuth Feature Status Setter"
         description:
           "The client requests CryptAuth to set the state of various features "
           "for the user's devices."
-        trigger: "TBD"
+        trigger:
+          "The DeviceSync service has a SetFeatureStatus() method that "
+          "triggers this API call. Currently, that method is only used by the "
+          "multi-device host backend delegate to enable (disable) the "
+          "kBetterTogetherHost bit for the desired (current) multi-device host."
         data: "User device IDs and feature state specifications."
         destination: GOOGLE_OWNED_SERVICE
       }
@@ -578,8 +582,6 @@
               error_callback, partial_traffic_annotation);
 }
 
-// TODO(https://crbug.com/953087): Populate the "sender" and "trigger" fields
-// when method is used in codebase.
 void CryptAuthClientImpl::GetDevicesActivityStatus(
     const cryptauthv2::GetDevicesActivityStatusRequest& request,
     const GetDevicesActivityStatusCallback& callback,
@@ -590,11 +592,15 @@
           "oauth2_api_call_flow",
           R"(
       semantics {
-        sender: "TBD"
+        sender: "CryptAuth Device Activity Getter"
         description:
-          "The client queries CryptAuth for the activity of status of the"
+          "The client queries CryptAuth for the activity of status of the "
           "user's devices."
-        trigger: "TBD"
+        trigger:
+          "The DeviceSync service has a GetDevicesActivityStatus() method that "
+          "triggers this API call. Currently, that method is only used by the "
+          "eligible-host-devices provider in order to better organize the "
+          "drop-down list used for multi-device setup."
         data: "User device ID."
         destination: GOOGLE_OWNED_SERVICE
       }
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
index 084b359a..f2589f8 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
@@ -370,8 +370,10 @@
 void CryptAuthDeviceSyncerImpl::BuildNewDeviceRegistry(
     const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
         id_to_device_software_feature_info_map) {
-  // Add all device information to the new registry except the remote device
+  // Add all device information to the new registry except the new remote device
   // BetterTogether metadata that will be decrypted and added later if possible.
+  // In the interim, use the existing BetterTogether metadata for the device
+  // from the current registry, if available.
   new_device_registry_map_ = CryptAuthDeviceRegistry::InstanceIdToDeviceMap();
   for (const auto& id_device_software_feature_info_pair :
        id_to_device_software_feature_info_map) {
@@ -383,10 +385,16 @@
     DCHECK(packet_it != id_to_device_metadata_packet_map_.end());
     const cryptauthv2::DeviceMetadataPacket& packet = packet_it->second;
 
-    // Add BetterTogetherDeviceMetadata only for the local device.
+    // Add BetterTogetherDeviceMetadata for the local device and all devices
+    // with BetterTogetherDeviceMetadata in the existing device registry.
     base::Optional<cryptauthv2::BetterTogetherDeviceMetadata> beto_metadata;
-    if (id == request_context_.device_id())
+    if (id == request_context_.device_id()) {
       beto_metadata = local_better_together_device_metadata_;
+    } else {
+      const CryptAuthDevice* existing_device = device_registry_->GetDevice(id);
+      if (existing_device)
+        beto_metadata = existing_device->better_together_device_metadata;
+    }
 
     new_device_registry_map_->try_emplace(
         id, id, packet.device_name(), packet.device_public_key(),
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl.h b/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
index 78094930..0d26b20 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
@@ -51,7 +51,9 @@
 // devices that have a valid device ID, device name, device public key, and
 // feature states. If device metadata cannot be decrypted due to an error or
 // because the group private key was not returned by CryptAuth, the device is
-// still added to the registry without the decrypted metadata.
+// still added to the registry without the new decrypted metadata. Any existing
+// decrypted metadata from the device registry will remain there until the new
+// metadata can be decrypted.
 class CryptAuthDeviceSyncerImpl : public CryptAuthDeviceSyncer {
  public:
   class Factory {
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
index 2f7cdc6..ea26fae 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
@@ -178,6 +178,14 @@
             base::Unretained(this)));
   }
 
+  void SetDeviceRegistry(const std::vector<CryptAuthDevice>& devices) {
+    CryptAuthDeviceRegistry::InstanceIdToDeviceMap map;
+    for (const CryptAuthDevice& device : devices)
+      map.insert_or_assign(device.instance_id(), device);
+
+    device_registry_->SetRegistry(map);
+  }
+
   void AddInitialGroupKeyToRegistry(const CryptAuthKey& group_key) {
     key_registry_->AddKey(
         CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey, group_key);
@@ -710,7 +718,8 @@
 
   // Only the local device has its BetterTogetherDeviceMetadata in the device
   // registry since the other metadata cannot be decrypted without the group
-  // private key.
+  // private key, and because the previous device registry did not have any
+  // existing metadata to draw from.
   VerifyDeviceSyncResult(
       CryptAuthDeviceSyncResult(CryptAuthDeviceSyncResult::ResultCode::kSuccess,
                                 true /* device_registry_changed */,
@@ -719,6 +728,44 @@
 }
 
 TEST_F(DeviceSyncCryptAuthDeviceSyncerImplTest,
+       Success_PreserveExistingBetterTogetherMetadata) {
+  // Populate existing registry with all devices and their BetterTogether
+  // metadata.
+  SetDeviceRegistry(GetAllTestDevices());
+
+  AddInitialGroupKeyToRegistry(GetStaleGroupKey());
+
+  CallSync();
+
+  // The initial group key is stale, so CryptAuth provides us with the new
+  // unencrypted group public key but no encrypted group private key. This can
+  // happen if the other devices have not shared their encrypted group private
+  // key with CryptAuth yet.
+  VerifyMetadataSyncerInput(&GetStaleGroupKey());
+  FinishMetadataSyncerAttempt(
+      GetAllTestDeviceMetadataPackets(),
+      GetGroupKeyWithoutPrivateKey() /* new_group_key */,
+      base::nullopt /* encrypted_group_private_key */,
+      cryptauthv2::GetClientDirectiveForTest(),
+      CryptAuthDeviceSyncResult::ResultCode::kSuccess);
+
+  VerifyGroupKeyInRegistry(GetGroupKeyWithoutPrivateKey());
+
+  VerifyFeatureStatusGetterInput(GetAllTestDeviceIds());
+  FinishFeatureStatusGetterAttempt(
+      GetAllTestDeviceIds(), CryptAuthDeviceSyncResult::ResultCode::kSuccess);
+
+  // Even though the new device BetterTogether metadata could not be decrypted,
+  // the new registry should preserve the BetterTogether metadata from the
+  // previous registry.
+  VerifyDeviceSyncResult(
+      CryptAuthDeviceSyncResult(CryptAuthDeviceSyncResult::ResultCode::kSuccess,
+                                false /* device_registry_changed */,
+                                cryptauthv2::GetClientDirectiveForTest()),
+      GetAllTestDevices());
+}
+
+TEST_F(DeviceSyncCryptAuthDeviceSyncerImplTest,
        NonFatalError_FromMetadataSyncer) {
   AddInitialGroupKeyToRegistry(GetGroupKey());
 
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.cc b/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.cc
index 723472f..5a026132 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_device_manager_impl.cc
@@ -224,17 +224,14 @@
   // This is particularly relevant for timeout failures.
   callback_weak_ptr_factory_.InvalidateWeakPtrs();
 
-  // The DeviceSync result might be owned by the device syncer, so we copy the
-  // result here before destroying the device syncer.
-  CryptAuthDeviceSyncResult device_sync_result_copy = device_sync_result;
   device_syncer_.reset();
 
   std::stringstream prefix;
   prefix << "DeviceSync attempt with invocation reason "
          << current_client_metadata_->invocation_reason();
   std::stringstream suffix;
-  suffix << "with result code " << device_sync_result_copy.result_code() << ".";
-  switch (device_sync_result_copy.GetResultType()) {
+  suffix << "with result code " << device_sync_result.result_code() << ".";
+  switch (device_sync_result.GetResultType()) {
     case CryptAuthDeviceSyncResult::ResultType::kSuccess:
       PA_LOG(INFO) << prefix.str() << " succeeded  " << suffix.str();
       break;
@@ -248,7 +245,7 @@
   }
 
   PA_LOG(INFO) << "The device registry "
-               << (device_sync_result_copy.did_device_registry_change()
+               << (device_sync_result.did_device_registry_change()
                        ? "changed."
                        : "did not change.");
 
@@ -257,7 +254,7 @@
   // TODO(nohle): Log DeviceSync result metrics: success, result code, and if
   // devices changed.
 
-  scheduler_->HandleDeviceSyncResult(device_sync_result_copy);
+  scheduler_->HandleDeviceSyncResult(device_sync_result);
 
   base::Optional<base::TimeDelta> time_to_next_attempt = GetTimeToNextAttempt();
   if (time_to_next_attempt) {
@@ -267,14 +264,14 @@
     PA_LOG(INFO) << "No future DeviceSync requests currently scheduled.";
   }
 
-  if (!device_sync_result_copy.IsSuccess()) {
+  if (!device_sync_result.IsSuccess()) {
     PA_LOG(INFO) << "Number of consecutive DeviceSync failures: "
                  << scheduler_->GetNumConsecutiveDeviceSyncFailures() << ".";
   }
 
   SetState(State::kIdle);
 
-  NotifyDeviceSyncFinished(device_sync_result_copy);
+  NotifyDeviceSyncFinished(device_sync_result);
 }
 
 std::ostream& operator<<(std::ostream& stream,
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc b/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
index 791304e..1d76459b9b 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
@@ -20,7 +20,6 @@
 const char kGroupPublicKey[] = "group_key";
 const int64_t kGroupPublicKeyHash = 0xf3666041a2db06e4;
 
-// TODO(nohle): Add last update time when relevant.
 const CryptAuthDevice& GetLocalDeviceForTest() {
   static const base::NoDestructor<CryptAuthDevice> device([] {
     // Note: The local device's (Instance) ID and PII-free device name are not
@@ -62,7 +61,6 @@
   return *device;
 }
 
-// TODO(nohle): Add last update time when relevant.
 const CryptAuthDevice& GetRemoteDeviceNeedsGroupPrivateKeyForTest() {
   static const base::NoDestructor<CryptAuthDevice> device([] {
     const char kRemoteDeviceNeedsGroupPrivateKeyId[] =
@@ -109,7 +107,6 @@
   return *device;
 }
 
-// TODO(nohle): Add last update time when relevant.
 const CryptAuthDevice& GetRemoteDeviceHasGroupPrivateKeyForTest() {
   static const base::NoDestructor<CryptAuthDevice> device([] {
     const char kRemoteDeviceHasGroupPrivateKeyId[] =
diff --git a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
index ba2ddf8..9840171 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_enroller_impl.cc
@@ -447,8 +447,6 @@
   if (!timeout_for_state)
     return;
 
-  // TODO(https://crbug.com/936273): Add metrics to track failure rates due
-  // to async timeouts.
   timer_->Start(FROM_HERE, *timeout_for_state,
                 base::BindOnce(&CryptAuthV2EnrollerImpl::OnTimeout,
                                base::Unretained(this)));
diff --git a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
index eddfd0f..a73cc1d 100644
--- a/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
@@ -475,8 +475,6 @@
   if (state_ != State::kWaitingForClientAppMetadata)
     return;
 
-  // TODO(https://crbug.com/936273): Add metrics to track failure rates due to
-  // async timeouts.
   timer_->Start(FROM_HERE, kWaitingForClientAppMetadataTimeout,
                 base::BindOnce(&CryptAuthV2EnrollmentManagerImpl::OnTimeout,
                                callback_weak_ptr_factory_.GetWeakPtr()));
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
index 46154a06..f1b9a06 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
@@ -128,9 +128,8 @@
 base::Optional<multidevice::RemoteDeviceRef>
 DeviceSyncClientImpl::GetLocalDeviceMetadata() {
   DCHECK(is_ready());
-  return local_device_id_
-             ? expiring_device_cache_->GetRemoteDevice(*local_device_id_)
-             : base::nullopt;
+  return expiring_device_cache_->GetRemoteDevice(local_instance_id_,
+                                                 local_legacy_device_id_);
 }
 
 void DeviceSyncClientImpl::SetSoftwareFeatureState(
@@ -259,7 +258,15 @@
     return;
   }
 
-  local_device_id_ = local_device_metadata->GetDeviceId();
+  local_instance_id_ = local_device_metadata->instance_id.empty()
+                           ? base::nullopt
+                           : base::make_optional<std::string>(
+                                 local_device_metadata->instance_id);
+  local_legacy_device_id_ = local_device_metadata->GetDeviceId().empty()
+                                ? base::nullopt
+                                : base::make_optional<std::string>(
+                                      local_device_metadata->GetDeviceId());
+
   expiring_device_cache_->UpdateRemoteDevice(*local_device_metadata);
 
   waiting_for_local_device_metadata_ = false;
@@ -285,14 +292,16 @@
     std::transform(
         response->eligible_devices.begin(), response->eligible_devices.end(),
         std::back_inserter(eligible_devices), [this](const auto& device) {
-          return *expiring_device_cache_->GetRemoteDevice(device.GetDeviceId());
+          return *expiring_device_cache_->GetRemoteDevice(device.instance_id,
+                                                          device.GetDeviceId());
         });
-    std::transform(
-        response->ineligible_devices.begin(),
-        response->ineligible_devices.end(),
-        std::back_inserter(ineligible_devices), [this](const auto& device) {
-          return *expiring_device_cache_->GetRemoteDevice(device.GetDeviceId());
-        });
+    std::transform(response->ineligible_devices.begin(),
+                   response->ineligible_devices.end(),
+                   std::back_inserter(ineligible_devices),
+                   [this](const auto& device) {
+                     return *expiring_device_cache_->GetRemoteDevice(
+                         device.instance_id, device.GetDeviceId());
+                   });
   }
 
   std::move(callback).Run(result_code, eligible_devices, ineligible_devices);
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
index 474b44ea..1bd85fe 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
@@ -123,7 +123,8 @@
   bool pending_notify_enrollment_finished_ = false;
   bool pending_notify_new_synced_devices_ = false;
 
-  base::Optional<std::string> local_device_id_;
+  base::Optional<std::string> local_instance_id_;
+  base::Optional<std::string> local_legacy_device_id_;
 
   base::WeakPtrFactory<DeviceSyncClientImpl> weak_ptr_factory_{this};
 
diff --git a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
index af554aa..82f39c1 100644
--- a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
+++ b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
@@ -122,8 +122,8 @@
   if (host_device) {
     remote_device_cache_->SetRemoteDevices({*host_device});
     host_status_with_device_ = std::make_pair(
-        host_status,
-        remote_device_cache_->GetRemoteDevice(host_device->GetDeviceId()));
+        host_status, remote_device_cache_->GetRemoteDevice(
+                         host_device->instance_id, host_device->GetDeviceId()));
   } else {
     host_status_with_device_ =
         std::make_pair(host_status, base::nullopt /* host_device */);
@@ -144,12 +144,12 @@
   remote_device_cache_->SetRemoteDevices(eligible_host_devices);
 
   multidevice::RemoteDeviceRefList eligible_host_device_refs;
-  std::transform(
-      eligible_host_devices.begin(), eligible_host_devices.end(),
-      std::back_inserter(eligible_host_device_refs),
-      [this](const auto& device) {
-        return *remote_device_cache_->GetRemoteDevice(device.GetDeviceId());
-      });
+  std::transform(eligible_host_devices.begin(), eligible_host_devices.end(),
+                 std::back_inserter(eligible_host_device_refs),
+                 [this](const auto& device) {
+                   return *remote_device_cache_->GetRemoteDevice(
+                       device.instance_id, device.GetDeviceId());
+                 });
 
   std::move(callback).Run(eligible_host_device_refs);
 }
diff --git a/chromeos/services/secure_channel/ble_service_data_helper_impl.cc b/chromeos/services/secure_channel/ble_service_data_helper_impl.cc
index fb03d25..25ad985 100644
--- a/chromeos/services/secure_channel/ble_service_data_helper_impl.cc
+++ b/chromeos/services/secure_channel/ble_service_data_helper_impl.cc
@@ -74,7 +74,9 @@
 BleServiceDataHelperImpl::GenerateForegroundAdvertisement(
     const DeviceIdPair& device_id_pair) {
   base::Optional<multidevice::RemoteDeviceRef> local_device =
-      remote_device_cache_->GetRemoteDevice(device_id_pair.local_device_id());
+      remote_device_cache_->GetRemoteDevice(
+          base::nullopt /* instance_id */,
+          device_id_pair.local_device_id() /* legacy_device_id */);
   if (!local_device) {
     PA_LOG(ERROR) << "Requested local device does not exist: "
                   << multidevice::RemoteDeviceRef::TruncateDeviceIdForLogs(
@@ -83,7 +85,9 @@
   }
 
   base::Optional<multidevice::RemoteDeviceRef> remote_device =
-      remote_device_cache_->GetRemoteDevice(device_id_pair.remote_device_id());
+      remote_device_cache_->GetRemoteDevice(
+          base::nullopt /* instance_id */,
+          device_id_pair.remote_device_id() /* legacy_device_id */);
   if (!remote_device) {
     PA_LOG(ERROR) << "Requested remote device does not exist: "
                   << multidevice::RemoteDeviceRef::TruncateDeviceIdForLogs(
@@ -103,7 +107,8 @@
       local_device_id_to_remote_device_ids_map;
   for (const auto& device_id_pair : device_id_pair_set) {
     if (!remote_device_cache_->GetRemoteDevice(
-            device_id_pair.local_device_id())) {
+            base::nullopt /* instance_id */,
+            device_id_pair.local_device_id() /* legacy_device_id */)) {
       PA_LOG(ERROR) << "Requested local device does not exist"
                     << multidevice::RemoteDeviceRef::TruncateDeviceIdForLogs(
                            device_id_pair.local_device_id());
@@ -111,7 +116,8 @@
     }
 
     if (!remote_device_cache_->GetRemoteDevice(
-            device_id_pair.remote_device_id())) {
+            base::nullopt /* instance_id */,
+            device_id_pair.remote_device_id() /* legacy_device_id */)) {
       PA_LOG(ERROR) << "Requested remote device does not exist"
                     << multidevice::RemoteDeviceRef::TruncateDeviceIdForLogs(
                            device_id_pair.remote_device_id());
@@ -144,7 +150,9 @@
   if (service_data.size() >= kMinNumBytesInForegroundServiceData) {
     std::vector<cryptauth::BeaconSeed> beacon_seeds =
         multidevice::ToCryptAuthSeedList(
-            remote_device_cache_->GetRemoteDevice(local_device_id)
+            remote_device_cache_
+                ->GetRemoteDevice(base::nullopt /* instance_id */,
+                                  local_device_id /* legacy_device_id */)
                 ->beacon_seeds());
 
     identified_device_id =
@@ -161,7 +169,9 @@
     multidevice::RemoteDeviceRefList remote_devices;
     std::transform(remote_device_ids.begin(), remote_device_ids.end(),
                    std::back_inserter(remote_devices), [this](auto device_id) {
-                     return *remote_device_cache_->GetRemoteDevice(device_id);
+                     return *remote_device_cache_->GetRemoteDevice(
+                         base::nullopt /* instance_id */,
+                         device_id /* legacy_device_id */);
                    });
 
     identified_device_id =
@@ -176,7 +186,9 @@
     return base::nullopt;
 
   return BleServiceDataHelper::DeviceWithBackgroundBool(
-      *remote_device_cache_->GetRemoteDevice(identified_device_id),
+      *remote_device_cache_->GetRemoteDevice(
+          base::nullopt /* instance_id */,
+          identified_device_id /* legacy_device_id */),
       is_background_advertisement);
 }
 
diff --git a/chromeos/services/secure_channel/ble_service_data_helper_impl_unittest.cc b/chromeos/services/secure_channel/ble_service_data_helper_impl_unittest.cc
index 9ecaa8c..c659314 100644
--- a/chromeos/services/secure_channel/ble_service_data_helper_impl_unittest.cc
+++ b/chromeos/services/secure_channel/ble_service_data_helper_impl_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chromeos/components/multidevice/remote_device_cache.h"
 #include "chromeos/components/multidevice/remote_device_test_util.h"
@@ -79,7 +80,8 @@
 
 multidevice::RemoteDeviceRef CreateLocalDevice(int id) {
   return multidevice::RemoteDeviceRefBuilder()
-      .SetPublicKey("local public key " + std::to_string(id))
+      .SetInstanceId("local instance id " + base::NumberToString(id))
+      .SetPublicKey("local public key " + base::NumberToString(id))
       .SetBeaconSeeds(CreateFakeBeaconSeeds(id))
       .Build();
 }
diff --git a/chromeos/services/secure_channel/secure_channel_service_unittest.cc b/chromeos/services/secure_channel/secure_channel_service_unittest.cc
index 82e2935..a8fe5ee 100644
--- a/chromeos/services/secure_channel/secure_channel_service_unittest.cc
+++ b/chromeos/services/secure_channel/secure_channel_service_unittest.cc
@@ -742,13 +742,15 @@
 
     // |device_to_connect| should be in the cache.
     EXPECT_TRUE(multidevice::IsSameDevice(
-        device_to_connect, *remote_device_cache()->GetRemoteDevice(
-                               device_to_connect.GetDeviceId())));
+        device_to_connect,
+        *remote_device_cache()->GetRemoteDevice(
+            device_to_connect.instance_id, device_to_connect.GetDeviceId())));
 
     // |local_device| should also be in the cache.
     EXPECT_TRUE(multidevice::IsSameDevice(
         local_device,
-        *remote_device_cache()->GetRemoteDevice(local_device.GetDeviceId())));
+        *remote_device_cache()->GetRemoteDevice(local_device.instance_id,
+                                                local_device.GetDeviceId())));
 
     return id;
   }
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index 43f29e0a..81479f2 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -7,9 +7,28 @@
 
 android_library("java") {
   sources = [
+    "java/src/org/chromium/components/browser_ui/widget/AlwaysDismissedDialog.java",
     "java/src/org/chromium/components/browser_ui/widget/BoundedLinearLayout.java",
+    "java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java",
+    "java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java",
     "java/src/org/chromium/components/browser_ui/widget/DualControlLayout.java",
     "java/src/org/chromium/components/browser_ui/widget/FadingEdgeScrollView.java",
+    "java/src/org/chromium/components/browser_ui/widget/FadingShadow.java",
+    "java/src/org/chromium/components/browser_ui/widget/FadingShadowView.java",
+    "java/src/org/chromium/components/browser_ui/widget/LoadingView.java",
+    "java/src/org/chromium/components/browser_ui/widget/MaterialProgressBar.java",
+    "java/src/org/chromium/components/browser_ui/widget/MoreProgressButton.java",
+    "java/src/org/chromium/components/browser_ui/widget/PaddedFrameLayout.java",
+    "java/src/org/chromium/components/browser_ui/widget/PromoDialog.java",
+    "java/src/org/chromium/components/browser_ui/widget/PromoDialogLayout.java",
+    "java/src/org/chromium/components/browser_ui/widget/RadioButtonLayout.java",
+    "java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescription.java",
+    "java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayout.java",
+    "java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditText.java",
+    "java/src/org/chromium/components/browser_ui/widget/RoundedCornerImageView.java",
+    "java/src/org/chromium/components/browser_ui/widget/TintedDrawable.java",
+    "java/src/org/chromium/components/browser_ui/widget/ViewResourceFrameLayout.java",
+    "java/src/org/chromium/components/browser_ui/widget/WrappingLayout.java",
     "java/src/org/chromium/components/browser_ui/widget/animation/CancelAwareAnimatorListener.java",
     "java/src/org/chromium/components/browser_ui/widget/animation/FocusAnimator.java",
     "java/src/org/chromium/components/browser_ui/widget/animation/Interpolators.java",
@@ -41,6 +60,7 @@
   custom_package = "org.chromium.components.browser_ui.widget"
   resource_dirs = [ "java/res" ]
   deps = [
+    "//components/browser_ui/strings/android:browser_ui_strings_grd",
     "//components/browser_ui/styles/android:java_resources",
     "//ui/android:ui_java_resources",
   ]
@@ -51,6 +71,13 @@
 
   sources = [
     "java/src/org/chromium/components/browser_ui/widget/DualControlLayoutTest.java",
+    "java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java",
+    "java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java",
+    "java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java",
+    "java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java",
+    "java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayoutTest.java",
+    "java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java",
+    "java/src/org/chromium/components/browser_ui/widget/WrappingLayoutTest.java",
     "java/src/org/chromium/components/browser_ui/widget/text/ChromeTextInputLayoutRenderTest.java",
   ]
   deps = [
diff --git a/chrome/browser/ui/android/widget/java/res/layout/dialog_control_description.xml b/components/browser_ui/widget/android/java/res/layout/dialog_control_description.xml
similarity index 100%
rename from chrome/browser/ui/android/widget/java/res/layout/dialog_control_description.xml
rename to components/browser_ui/widget/android/java/res/layout/dialog_control_description.xml
diff --git a/chrome/browser/ui/android/widget/java/res/layout/indeterminate_progress_view.xml b/components/browser_ui/widget/android/java/res/layout/indeterminate_progress_view.xml
similarity index 100%
rename from chrome/browser/ui/android/widget/java/res/layout/indeterminate_progress_view.xml
rename to components/browser_ui/widget/android/java/res/layout/indeterminate_progress_view.xml
diff --git a/chrome/browser/ui/android/widget/java/res/layout/more_progress_button.xml b/components/browser_ui/widget/android/java/res/layout/more_progress_button.xml
similarity index 88%
rename from chrome/browser/ui/android/widget/java/res/layout/more_progress_button.xml
rename to components/browser_ui/widget/android/java/res/layout/more_progress_button.xml
index d5137d4..39831f5b 100644
--- a/chrome/browser/ui/android/widget/java/res/layout/more_progress_button.xml
+++ b/components/browser_ui/widget/android/java/res/layout/more_progress_button.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file. -->
 
 
-<org.chromium.chrome.browser.ui.widget.MoreProgressButton
+<org.chromium.components.browser_ui.widget.MoreProgressButton
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
@@ -25,4 +25,4 @@
         android:id="@+id/progress_spinner"
         layout="@layout/indeterminate_progress_view" />
 
-</org.chromium.chrome.browser.ui.widget.MoreProgressButton>
\ No newline at end of file
+</org.chromium.components.browser_ui.widget.MoreProgressButton>
\ No newline at end of file
diff --git a/chrome/browser/ui/android/widget/java/res/layout/promo_dialog_layout.xml b/components/browser_ui/widget/android/java/res/layout/promo_dialog_layout.xml
similarity index 96%
rename from chrome/browser/ui/android/widget/java/res/layout/promo_dialog_layout.xml
rename to components/browser_ui/widget/android/java/res/layout/promo_dialog_layout.xml
index 26f90318..35fe349f 100644
--- a/chrome/browser/ui/android/widget/java/res/layout/promo_dialog_layout.xml
+++ b/components/browser_ui/widget/android/java/res/layout/promo_dialog_layout.xml
@@ -14,7 +14,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools">
 
-    <org.chromium.chrome.browser.ui.widget.PromoDialogLayout
+    <org.chromium.components.browser_ui.widget.PromoDialogLayout
             android:id="@+id/promo_dialog_layout"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -95,6 +95,6 @@
                 app:buttonAlignment="end"
                 app:stackedMargin="@dimen/promo_dialog_stacked_margin" />
 
-    </org.chromium.chrome.browser.ui.widget.PromoDialogLayout>
+    </org.chromium.components.browser_ui.widget.PromoDialogLayout>
 
 </merge>
diff --git a/chrome/browser/ui/android/widget/java/res/layout/radio_button_layout_element.xml b/components/browser_ui/widget/android/java/res/layout/radio_button_layout_element.xml
similarity index 100%
rename from chrome/browser/ui/android/widget/java/res/layout/radio_button_layout_element.xml
rename to components/browser_ui/widget/android/java/res/layout/radio_button_layout_element.xml
diff --git a/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_description.xml b/components/browser_ui/widget/android/java/res/layout/radio_button_with_description.xml
similarity index 100%
rename from chrome/browser/ui/android/widget/java/res/layout/radio_button_with_description.xml
rename to components/browser_ui/widget/android/java/res/layout/radio_button_with_description.xml
diff --git a/chrome/browser/ui/android/widget/java/res/layout/radio_button_with_edit_text.xml b/components/browser_ui/widget/android/java/res/layout/radio_button_with_edit_text.xml
similarity index 100%
rename from chrome/browser/ui/android/widget/java/res/layout/radio_button_with_edit_text.xml
rename to components/browser_ui/widget/android/java/res/layout/radio_button_with_edit_text.xml
diff --git a/components/browser_ui/widget/android/java/res/values/attrs.xml b/components/browser_ui/widget/android/java/res/values/attrs.xml
index 2bba9a125..896ffec 100644
--- a/components/browser_ui/widget/android/java/res/values/attrs.xml
+++ b/components/browser_ui/widget/android/java/res/values/attrs.xml
@@ -29,10 +29,44 @@
         <attr name="dualControlLayoutHorizontalPadding" format="dimension"/>
     </declare-styleable>
 
+    <declare-styleable name="MaterialProgressBar">
+        <attr name="colorBackground" format="reference|color" />
+        <attr name="colorProgress" format="reference|color" />
+        <attr name="colorSecondaryProgress" format="reference|color" />
+    </declare-styleable>
+
+    <declare-styleable name="PaddedFrameLayout">
+        <attr name="maxChildWidth" format="dimension" />
+        <attr name="maxChildHeight" format="dimension" />
+    </declare-styleable>
+
+    <declare-styleable name="RadioButtonWithDescription">
+        <attr name="primaryText" format="string" />
+        <attr name="descriptionText" format="string" />
+    </declare-styleable>
+
+    <declare-styleable name="RadioButtonWithEditText">
+        <attr name="android:hint"/>
+        <attr name="android:inputType" />
+    </declare-styleable>
+
+    <declare-styleable name="RoundedCornerImageView">
+        <attr name="cornerRadiusTopStart" format="dimension" />
+        <attr name="cornerRadiusTopEnd" format="dimension" />
+        <attr name="cornerRadiusBottomStart" format="dimension" />
+        <attr name="cornerRadiusBottomEnd" format="dimension" />
+        <attr name="roundedfillColor" format="reference|color" />
+    </declare-styleable>
+
     <declare-styleable name="TextViewWithCompoundDrawables">
         <attr name="drawableWidth" format="dimension"/>
         <attr name="drawableHeight" format="dimension"/>
         <!-- drawableTint wasn't added in Android until API level 23. -->
         <attr name="chromeDrawableTint" format="color"/>
     </declare-styleable>
+
+    <declare-styleable name="WrappingLayout">
+        <attr name="horizontalSpacing" format="dimension" />
+        <attr name="verticalSpacing" format="dimension" />
+    </declare-styleable>
 </resources>
diff --git a/components/browser_ui/widget/android/java/res/values/colors.xml b/components/browser_ui/widget/android/java/res/values/colors.xml
index be291a6..9eee9c1 100644
--- a/components/browser_ui/widget/android/java/res/values/colors.xml
+++ b/components/browser_ui/widget/android/java/res/values/colors.xml
@@ -4,5 +4,11 @@
      found in the LICENSE file. -->
 
 <resources>
+    <color name="modal_dialog_scrim_color">@color/black_alpha_65</color>
     <color name="toolbar_shadow_color">@color/black_alpha_11</color>
+
+    <!-- Progress Bar colors -->
+    <color name="progress_bar_foreground">@color/light_active_color</color>
+    <color name="progress_bar_secondary">@color/modern_grey_600</color>
+    <color name="progress_bar_background">@color/progress_bar_background_deprecated</color>
 </resources>
diff --git a/components/browser_ui/widget/android/java/res/values/dimens.xml b/components/browser_ui/widget/android/java/res/values/dimens.xml
index 1354edb..e1ea6bc5 100644
--- a/components/browser_ui/widget/android/java/res/values/dimens.xml
+++ b/components/browser_ui/widget/android/java/res/values/dimens.xml
@@ -4,9 +4,26 @@
      found in the LICENSE file. -->
 
 <resources>
-    <!-- DualControlLayout dimensions -->
+    <!-- DualControlLayout -->
     <dimen name="dual_control_margin_between_items">8dp</dimen>
 
-    <!-- ChromeTextInputLayout dimensions -->
+    <!-- ChromeTextInputLayout -->
     <dimen name="text_input_layout_padding_start">3dp</dimen>
+
+    <!-- Dialogs -->
+    <dimen name="dialog_max_width">600dp</dimen>
+    <dimen name="dialog_header_margin">14dp</dimen>
+    <dimen name="promo_dialog_illustration_margin">24dp</dimen>
+    <dimen name="promo_dialog_illustration_width">150dp</dimen>
+    <dimen name="promo_dialog_padding">16dp</dimen>
+    <dimen name="promo_dialog_stacked_margin">16dp</dimen>
+    <dimen name="promo_dialog_max_content_width">320dp</dimen>
+    <dimen name="promo_dialog_min_scrollable_height">100dp</dimen>
+
+    <!-- RadioButtonWithDescription -->
+    <dimen name="radio_button_with_description_lateral_padding">16dp</dimen>
+    <dimen name="radio_button_with_description_vertical_padding">10dp</dimen>
+
+    <!-- RadioButton(WithDescription)Layout dimensions -->
+    <dimen name="default_vertical_margin_between_items">8dp</dimen>
 </resources>
diff --git a/components/browser_ui/widget/android/java/res/values/styles.xml b/components/browser_ui/widget/android/java/res/values/styles.xml
new file mode 100644
index 0000000..cbce6d5
--- /dev/null
+++ b/components/browser_ui/widget/android/java/res/values/styles.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<resources>
+    <style name="ListActionButton" parent="@style/TextButton">
+        <item name="android:minHeight">48dp</item>
+    </style>
+
+    <!-- Promo dialogs -->
+    <style name="PromoDialog" >
+        <item name="android:background">@android:color/transparent</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:clipChildren">false</item>
+        <item name="android:clipToPadding">false</item>
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowCloseOnTouchOutside">false</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowFullscreen">false</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+</resources>
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/AlwaysDismissedDialog.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/AlwaysDismissedDialog.java
similarity index 95%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/AlwaysDismissedDialog.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/AlwaysDismissedDialog.java
index 8825bd27..68cbd01 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/AlwaysDismissedDialog.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/AlwaysDismissedDialog.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.app.Activity;
 import android.app.Dialog;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ClipDrawableProgressBar.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ClipDrawableProgressBar.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java
index bf48c49d..51af2ac 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ClipDrawableProgressBar.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.graphics.Color;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ContextMenuDialog.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ContextMenuDialog.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
index 3fdb9243..c30b0a60 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ContextMenuDialog.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.app.Activity;
 import android.graphics.Color;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/FadingShadow.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FadingShadow.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/FadingShadow.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FadingShadow.java
index 57f3f13..05c792e 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/FadingShadow.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FadingShadow.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.graphics.Canvas;
 import android.graphics.Color;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/FadingShadowView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FadingShadowView.java
similarity index 96%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/FadingShadowView.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FadingShadowView.java
index ee80d38..4f39de75 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/FadingShadowView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FadingShadowView.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.graphics.Canvas;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/LoadingView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/LoadingView.java
similarity index 97%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/LoadingView.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/LoadingView.java
index 0f54642..8de2c1c 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/LoadingView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/LoadingView.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MaterialProgressBar.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MaterialProgressBar.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MaterialProgressBar.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MaterialProgressBar.java
index cb15bf9..113d2378 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MaterialProgressBar.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MaterialProgressBar.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MoreProgressButton.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButton.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MoreProgressButton.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButton.java
index 2de5b0b3..76b4561f 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MoreProgressButton.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButton.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MoreProgressButtonTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java
similarity index 97%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MoreProgressButtonTest.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java
index 1448873c..56f79b14 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/MoreProgressButtonTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -21,7 +21,7 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.ui.widget.MoreProgressButton.State;
+import org.chromium.components.browser_ui.widget.MoreProgressButton.State;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
 
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PaddedFrameLayout.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PaddedFrameLayout.java
similarity index 94%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PaddedFrameLayout.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PaddedFrameLayout.java
index 9e679a2..9daf5589c 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PaddedFrameLayout.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PaddedFrameLayout.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -12,11 +12,11 @@
 /**
  * A layout for displaying a View with padding, borders, and a maximum width and/or height. E.g.:
  *
- *   <org.chromium.chrome.browser.ui.widget.PaddedFrameLayout
+ *   <org.chromium.components.browser_ui.widget.PaddedFrameLayout
  *       chrome:maxChildWidth="200dp"
  *       chrome:maxChildHeight="400dp">
  *       ... contents here ...
- *   </org.chromium.chrome.browser.ui.widget.PaddedFrameLayout>
+ *   </org.chromium.components.browser_ui.widget.PaddedFrameLayout>
  */
 public class PaddedFrameLayout extends FrameLayout {
     // Value for mMaxChildWidth or mMaxChildHeight to specify that the width or height should
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialog.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialog.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialog.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialog.java
index f45a28a..c7eb7c97 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialog.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialog.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.app.Activity;
 import android.content.DialogInterface;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialogLayout.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogLayout.java
similarity index 96%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialogLayout.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogLayout.java
index 47fdf5f..496511ed 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialogLayout.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogLayout.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.support.graphics.drawable.VectorDrawableCompat;
@@ -16,9 +16,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import org.chromium.chrome.browser.ui.widget.PromoDialog.DialogParams;
-import org.chromium.components.browser_ui.widget.BoundedLinearLayout;
-import org.chromium.components.browser_ui.widget.DualControlLayout;
+import org.chromium.components.browser_ui.widget.PromoDialog.DialogParams;
 
 /**
  * Lays out a promo dialog that is shown when Clank starts up.
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialogTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialogTest.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java
index 1683200..a7b5834 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/PromoDialogTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.app.Activity;
 import android.content.DialogInterface;
@@ -22,8 +22,8 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.chrome.browser.ui.widget.PromoDialog.DialogParams;
-import org.chromium.chrome.browser.ui.widget.test.R;
+import org.chromium.components.browser_ui.widget.PromoDialog.DialogParams;
+import org.chromium.components.browser_ui.widget.test.R;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
 
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayout.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayout.java
similarity index 97%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayout.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayout.java
index 167824f..4d45cdbc 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayout.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayout.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayoutTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayoutTest.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java
index f4748cb..4c9eab9f 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonLayoutTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
similarity index 97%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonRenderTest.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
index 638d787..b4ec5a03 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonRenderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.app.Activity;
 import android.graphics.Color;
@@ -21,7 +21,7 @@
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.ui.widget.test.R;
+import org.chromium.components.browser_ui.widget.test.R;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
 import org.chromium.ui.test.util.NightModeTestUtils;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescription.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescription.java
index 48caa71..85bc3dc0 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescription.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescription.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -32,7 +32,7 @@
  * The primary of the text and an optional description to be contained in the group may be set in
  * XML. Sample declaration in XML:
  * <pre> {@code
- *   <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+ *   <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
  *      android:id="@+id/system_default"
  *      android:layout_width="match_parent"
  *      android:layout_height="wrap_content"
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayout.java
similarity index 93%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayout.java
index a1d784c..9ed52a3 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayout.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayout.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -36,20 +36,19 @@
  * RadioButtonWithDescription and/or RadioButtonWithEditText children.
  * For example:
  * <pre>{@code
- *  <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout
+ *  <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
  *       android:layout_width="match_parent"
  *       android:layout_height="match_parent" >
- *       <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+ *       <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
  *           ... />
- *       <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+ *       <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
  *           ... />
- *   </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
+ *   </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
  * }</pre>
  * </p>
  */
 public final class RadioButtonWithDescriptionLayout
         extends RadioGroup implements RadioButtonWithDescription.ButtonCheckedStateChangedListener {
-
     private final int mMarginBetweenRows;
     private final List<RadioButtonWithDescription> mRadioButtonsWithDescriptions;
     private OnCheckedChangeListener mOnCheckedChangeListener;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayoutTest.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayoutTest.java
index a9f0b09..40520efe 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithDescriptionLayoutTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayoutTest.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
@@ -23,7 +23,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.chrome.browser.ui.widget.test.R;
+import org.chromium.components.browser_ui.widget.test.R;
 
 import java.util.Arrays;
 import java.util.List;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditText.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditText.java
similarity index 97%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditText.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditText.java
index 3d430f67..750f68e 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditText.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditText.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -42,7 +42,7 @@
  * The input type, text, hint message of EditText box and an optional description to be contained in
  * the group may be set in XML. Sample declaration in XML:
  * <pre>{@code
- *  <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+ *  <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
  *     android:id="@+id/system_default"
  *     android:layout_width="match_parent"
  *     android:layout_height="wrap_content"
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditTextTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditTextTest.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java
index d106127..e69b1654 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RadioButtonWithEditTextTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.app.Activity;
 import android.support.test.filters.SmallTest;
@@ -19,7 +19,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.chrome.browser.ui.widget.test.R;
+import org.chromium.components.browser_ui.widget.test.R;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
 
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedCornerImageView.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedCornerImageView.java
index 7a9a6bf..8347d1d 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/RoundedCornerImageView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedCornerImageView.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -31,7 +31,7 @@
  * A custom {@link ImageView} that is able to render bitmaps and colors with rounded off corners.
  * The corner radii should be set through attributes. E.g.
  *
- *   <org.chromium.chrome.browser.ui.widget.RoundedCornerImageView
+ *   <org.chromium.components.browser_ui.widget.RoundedCornerImageView
  *      app:cornerRadiusTopStart="8dp"
  *      app:cornerRadiusTopEnd="8dp"
  *      app:cornerRadiusBottomStart="8dp"
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/TintedDrawable.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/TintedDrawable.java
index 0f4231e0..63fe9bd 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/TintedDrawable.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/TintedDrawable.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ViewResourceFrameLayout.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ViewResourceFrameLayout.java
similarity index 97%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ViewResourceFrameLayout.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ViewResourceFrameLayout.java
index 5ab8722..8cf79436 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/ViewResourceFrameLayout.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ViewResourceFrameLayout.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/WrappingLayout.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayout.java
similarity index 98%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/WrappingLayout.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayout.java
index c64002d..93c10838 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/WrappingLayout.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayout.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -19,7 +19,7 @@
  * Accounts for padding set on self and margins on children, but uniform spacing between elements
  * can be set through attributes, e.g.:
  *
- *     <org.chromium.chrome.browser.ui.widget.WrappingLayout
+ *     <org.chromium.components.browser_ui.widget.WrappingLayout
  *         android:id="@+id/wrapping_layout"
  *         android:layout_width="match_parent"
  *         android:layout_height="match_parent"
diff --git a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/WrappingLayoutTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayoutTest.java
similarity index 99%
rename from chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/WrappingLayoutTest.java
rename to components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayoutTest.java
index f3c2b5a..2b5a03b 100644
--- a/chrome/browser/ui/android/widget/java/src/org/chromium/chrome/browser/ui/widget/WrappingLayoutTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayoutTest.java
@@ -2,7 +2,7 @@
 // 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.ui.widget;
+package org.chromium.components.browser_ui.widget;
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
diff --git a/chrome/browser/ui/android/widget/test/java/res/drawable-hdpi/promo_dialog_test_drawable.png b/components/browser_ui/widget/android/test/java/res/drawable-hdpi/promo_dialog_test_drawable.png
similarity index 100%
rename from chrome/browser/ui/android/widget/test/java/res/drawable-hdpi/promo_dialog_test_drawable.png
rename to components/browser_ui/widget/android/test/java/res/drawable-hdpi/promo_dialog_test_drawable.png
Binary files differ
diff --git a/chrome/browser/ui/android/widget/test/java/res/drawable-mdpi/promo_dialog_test_drawable.png b/components/browser_ui/widget/android/test/java/res/drawable-mdpi/promo_dialog_test_drawable.png
similarity index 100%
rename from chrome/browser/ui/android/widget/test/java/res/drawable-mdpi/promo_dialog_test_drawable.png
rename to components/browser_ui/widget/android/test/java/res/drawable-mdpi/promo_dialog_test_drawable.png
Binary files differ
diff --git a/chrome/browser/ui/android/widget/test/java/res/drawable-xhdpi/promo_dialog_test_drawable.png b/components/browser_ui/widget/android/test/java/res/drawable-xhdpi/promo_dialog_test_drawable.png
similarity index 100%
rename from chrome/browser/ui/android/widget/test/java/res/drawable-xhdpi/promo_dialog_test_drawable.png
rename to components/browser_ui/widget/android/test/java/res/drawable-xhdpi/promo_dialog_test_drawable.png
Binary files differ
diff --git a/chrome/browser/ui/android/widget/test/java/res/drawable-xxhdpi/promo_dialog_test_drawable.png b/components/browser_ui/widget/android/test/java/res/drawable-xxhdpi/promo_dialog_test_drawable.png
similarity index 100%
rename from chrome/browser/ui/android/widget/test/java/res/drawable-xxhdpi/promo_dialog_test_drawable.png
rename to components/browser_ui/widget/android/test/java/res/drawable-xxhdpi/promo_dialog_test_drawable.png
Binary files differ
diff --git a/chrome/browser/ui/android/widget/test/java/res/drawable-xxxhdpi/promo_dialog_test_drawable.png b/components/browser_ui/widget/android/test/java/res/drawable-xxxhdpi/promo_dialog_test_drawable.png
similarity index 100%
rename from chrome/browser/ui/android/widget/test/java/res/drawable-xxxhdpi/promo_dialog_test_drawable.png
rename to components/browser_ui/widget/android/test/java/res/drawable-xxxhdpi/promo_dialog_test_drawable.png
Binary files differ
diff --git a/chrome/browser/ui/android/widget/test/java/res/drawable/promo_dialog_test_vector.xml b/components/browser_ui/widget/android/test/java/res/drawable/promo_dialog_test_vector.xml
similarity index 87%
rename from chrome/browser/ui/android/widget/test/java/res/drawable/promo_dialog_test_vector.xml
rename to components/browser_ui/widget/android/test/java/res/drawable/promo_dialog_test_vector.xml
index 6d6f897..1f3fa1f6 100644
--- a/chrome/browser/ui/android/widget/test/java/res/drawable/promo_dialog_test_vector.xml
+++ b/components/browser_ui/widget/android/test/java/res/drawable/promo_dialog_test_vector.xml
@@ -13,9 +13,9 @@
     android:height="107dp"
     android:viewportWidth="224"
     android:viewportHeight="107">
-    <path android:fillColor="#F1F3F4"
+    <path android:fillColor="@color/modern_blue_800"
         android:fillType="nonZero"
         android:pathData="M156.91,3.733l18.155,31.59l-90.126,-1.508l12.968,-31.662z"
-        android:strokeColor="#00000000"
+        android:strokeColor="@color/black_alpha_11"
         android:strokeWidth="1"/>
-</vector>
\ No newline at end of file
+</vector>
diff --git a/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_render_test.xml b/components/browser_ui/widget/android/test/java/res/layout/radio_button_render_test.xml
similarity index 85%
rename from chrome/browser/ui/android/widget/test/java/res/layout/radio_button_render_test.xml
rename to components/browser_ui/widget/android/test/java/res/layout/radio_button_render_test.xml
index c26e6b5..78c8cc0 100644
--- a/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_render_test.xml
+++ b/components/browser_ui/widget/android/test/java/res/layout/radio_button_render_test.xml
@@ -8,13 +8,13 @@
     android:layout_width="wrap_content"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout
+    <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
         android:id="@+id/test_radio_button_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
         <!-- RadioButtonWithDescription - With primary, without description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
               android:id="@+id/test_radio_description_1"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
@@ -22,7 +22,7 @@
               app:primaryText="@string/test_string" />
 
         <!-- RadioButtonWithDescription - With primary and description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
             android:id="@+id/test_radio_description_2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -31,7 +31,7 @@
             app:descriptionText="@string/test_string" />
 
         <!-- RadioButtonWithDescription - With primary and description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+        <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
             android:id="@+id/test_radio_edit_text_1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -45,7 +45,7 @@
             app:descriptionText="@string/test_string" />
 
         <!-- RadioButtonWithDescription - With primary, without description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+        <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
             android:id="@+id/test_radio_edit_text_2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -58,7 +58,7 @@
             app:primaryText="@string/test_string" />
 
         <!-- RadioButtonWithDescription - Without primary, with hint, with description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+        <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
             android:id="@+id/test_radio_edit_text_3"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -71,7 +71,7 @@
             app:descriptionText="@string/test_string" />
 
         <!-- RadioButtonWithDescription - Without primary, without description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+        <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
             android:id="@+id/test_radio_edit_text_4"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -81,5 +81,5 @@
             android:inputType="text"
             android:hint="@string/test_uri"
             android:background="?attr/selectableItemBackground" />
-    </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
+    </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
 </FrameLayout>
diff --git a/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_description_layout_test.xml b/components/browser_ui/widget/android/test/java/res/layout/radio_button_with_description_layout_test.xml
similarity index 82%
rename from chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_description_layout_test.xml
rename to components/browser_ui/widget/android/test/java/res/layout/radio_button_with_description_layout_test.xml
index f16e248..0e69fbc 100644
--- a/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_description_layout_test.xml
+++ b/components/browser_ui/widget/android/test/java/res/layout/radio_button_with_description_layout_test.xml
@@ -8,20 +8,20 @@
     android:layout_width="wrap_content"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout
+    <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
         android:id="@+id/test_radio_button_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
         <!-- RadioButtonWithDescription - With primary, without description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
               android:id="@+id/test_radio_description_1"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               app:primaryText="@string/test_primary_1" />
 
         <!-- RadioButtonWithDescription - With primary and description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithDescription
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
             android:id="@+id/test_radio_description_2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -29,7 +29,7 @@
             app:descriptionText="@string/test_desc_2" />
 
         <!-- RadioButtonWithDescription - With primary, without description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+        <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
             android:id="@+id/test_radio_edit_text_1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -41,7 +41,7 @@
             app:primaryText="@string/test_primary_3" />
 
         <!-- RadioButtonWithDescription - With primary and description. -->
-        <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+        <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
             android:id="@+id/test_radio_edit_text_2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -52,5 +52,5 @@
             android:hint="@string/test_uri"
             app:primaryText="@string/test_primary_4"
             app:descriptionText="@string/test_desc_4" />
-    </org.chromium.chrome.browser.ui.widget.RadioButtonWithDescriptionLayout>
+    </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
 </FrameLayout>
diff --git a/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_edit_text_test.xml b/components/browser_ui/widget/android/test/java/res/layout/radio_button_with_edit_text_test.xml
similarity index 92%
rename from chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_edit_text_test.xml
rename to components/browser_ui/widget/android/test/java/res/layout/radio_button_with_edit_text_test.xml
index c791f73..6d3193a3 100644
--- a/chrome/browser/ui/android/widget/test/java/res/layout/radio_button_with_edit_text_test.xml
+++ b/components/browser_ui/widget/android/test/java/res/layout/radio_button_with_edit_text_test.xml
@@ -8,7 +8,7 @@
     android:layout_width="wrap_content"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.ui.widget.RadioButtonWithEditText
+    <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
         android:id="@+id/test_radio_button"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/chrome/browser/ui/android/widget/test/java/res/values/strings.xml b/components/browser_ui/widget/android/test/java/res/values/strings.xml
similarity index 99%
rename from chrome/browser/ui/android/widget/test/java/res/values/strings.xml
rename to components/browser_ui/widget/android/test/java/res/values/strings.xml
index 7f899fc..7e2fc9c2d 100644
--- a/chrome/browser/ui/android/widget/test/java/res/values/strings.xml
+++ b/components/browser_ui/widget/android/test/java/res/values/strings.xml
@@ -20,4 +20,4 @@
 
     <string name="test_desc_2">Test Description 2</string>
     <string name="test_desc_4">Test Description 4</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/components/content_settings/core/browser/cookie_settings.h b/components/content_settings/core/browser/cookie_settings.h
index 35db94bf..a5dde4dd 100644
--- a/components/content_settings/core/browser/cookie_settings.h
+++ b/components/content_settings/core/browser/cookie_settings.h
@@ -28,6 +28,7 @@
 
 // This enum is used in prefs, do not change values.
 // The enum needs to correspond to CookieControlsMode in enums.xml.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.content_settings
 enum class CookieControlsMode {
   kOff = 0,
   kOn = 1,
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index 2281a8c..22e8d2db 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -69,7 +69,6 @@
     "//base",
     "//cc",
     "//components/viz/host",
-    "//components/viz/service",
     "//device/gamepad",
     "//device/gamepad/public/cpp:shared_with_blink",
     "//gpu",
@@ -156,7 +155,7 @@
     ":exo",
     "//base",
     "//base/test:test_support",
-    "//components/viz/service:service",
+    "//components/viz/service",
     "//gpu",
     "//skia",
     "//testing/gtest",
diff --git a/components/exo/DEPS b/components/exo/DEPS
index eae86d93..46520b6 100644
--- a/components/exo/DEPS
+++ b/components/exo/DEPS
@@ -5,7 +5,6 @@
   "+chromeos/constants/chromeos_features.h",
   "+components/viz/common",
   "+components/viz/host",
-  "+components/viz/service",
   "+device/gamepad",
   "+gpu",
   "+mojo/core/embedder/embedder.h",
@@ -23,4 +22,8 @@
   "surface_unittest\.cc": [
     "+components/viz/test",
   ],
+  ".*test.*\.cc": [
+    "+components/viz/service/frame_sinks",
+    "+components/viz/service/surfaces",
+  ],
 }
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index b621d4b7..bb5ae03 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -26,8 +26,6 @@
 #include "components/viz/common/quads/surface_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/single_release_callback.h"
-#include "components/viz/service/surfaces/surface.h"
-#include "components/viz/service/surfaces/surface_manager.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "ui/aura/client/aura_constants.h"
diff --git a/components/exo/wayland/wayland_positioner.cc b/components/exo/wayland/wayland_positioner.cc
index 37777e5..625fad4 100644
--- a/components/exo/wayland/wayland_positioner.cc
+++ b/components/exo/wayland/wayland_positioner.cc
@@ -216,15 +216,18 @@
   }
 
   // Exo overrides the ability to slide in cases when the orthogonal
-  // anchor+gravity would mean the slide can occlude |anchor_rect_|.
+  // anchor+gravity would mean the slide can occlude |anchor_rect_|, unless it
+  // already is occluded.
   //
   // We are doing this in order to stop a common case of clients allowing
   // dropdown menus to occlude the menu header. Whilst this may cause some
   // popups to avoid sliding where they could, for UX reasons we'd rather that
   // than allowing menus to be occluded.
-  if (!(anchor_x == gravity_x && anchor_x != kNeutral))
+  bool x_occluded = !(anchor_x == gravity_x && anchor_x != kNeutral);
+  bool y_occluded = !(anchor_y == gravity_y && anchor_y != kNeutral);
+  if (x_occluded && !y_occluded)
     adjustments_y.slide = false;
-  if (!(anchor_y == gravity_y && anchor_y != kNeutral))
+  if (y_occluded && !x_occluded)
     adjustments_x.slide = false;
 
   std::pair<Range1D, ConstraintAdjustment> x =
diff --git a/components/exo/wayland/wayland_positioner_unittest.cc b/components/exo/wayland/wayland_positioner_unittest.cc
index 1723562..035f3b91 100644
--- a/components/exo/wayland/wayland_positioner_unittest.cc
+++ b/components/exo/wayland/wayland_positioner_unittest.cc
@@ -205,6 +205,18 @@
                 .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE)
                 .SolveToRect(),
             gfx::Rect(2, 3, 3, 2));
+
+  // Here we ensure that the 4x4 popup does slide, which is allowed because
+  // the anchor rect is already occluded.
+  EXPECT_EQ(TestCaseBuilder()
+                .SetSize(4, 4)
+                .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                            ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+                .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_TOP |
+                           ZXDG_POSITIONER_V6_ANCHOR_LEFT)
+                .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE)
+                .SolveToRect(),
+            gfx::Rect(1, 1, 4, 4));
 }
 
 }  // namespace
diff --git a/components/feed/feed_feature_list.cc b/components/feed/feed_feature_list.cc
index 25e14932..0968192 100644
--- a/components/feed/feed_feature_list.cc
+++ b/components/feed/feed_feature_list.cc
@@ -24,4 +24,7 @@
 const base::Feature kInterestFeedNotifications{
     "InterestFeedNotifications", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kInterestFeedFeedback{"InterestFeedFeedback",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace feed
diff --git a/components/feed/feed_feature_list.h b/components/feed/feed_feature_list.h
index 41342be8..aec46e8 100644
--- a/components/feed/feed_feature_list.h
+++ b/components/feed/feed_feature_list.h
@@ -22,6 +22,8 @@
 
 extern const base::Feature kInterestFeedNotifications;
 
+extern const base::Feature kInterestFeedFeedback;
+
 }  // namespace feed
 
 #endif  // COMPONENTS_FEED_FEED_FEATURE_LIST_H_
diff --git a/components/gcm_driver/gcm_account_mapper.cc b/components/gcm_driver/gcm_account_mapper.cc
index 3f70c4ec..55a0840 100644
--- a/components/gcm_driver/gcm_account_mapper.cc
+++ b/components/gcm_driver/gcm_account_mapper.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/guid.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
 #include "components/gcm_driver/gcm_driver_desktop.h"
@@ -48,8 +49,7 @@
       clock_(base::DefaultClock::GetInstance()),
       initialized_(false) {}
 
-GCMAccountMapper::~GCMAccountMapper() {
-}
+GCMAccountMapper::~GCMAccountMapper() = default;
 
 void GCMAccountMapper::Initialize(const AccountMappings& account_mappings,
                                   const DispatchMessageCallback& callback) {
@@ -154,6 +154,8 @@
   DCHECK_EQ(app_id, kGCMAccountMapperAppId);
   // TODO(fgorski): Report Send to Gaia ID failures using UMA.
 
+  base::UmaHistogramBoolean("GCM.AccountMappingMessageReceived", true);
+
   if (dispatch_message_callback_.is_null()) {
     DVLOG(1) << "dispatch_message_callback_ missing in GCMAccountMapper";
     return;
diff --git a/components/invalidation/impl/per_user_topic_subscription_manager.cc b/components/invalidation/impl/per_user_topic_subscription_manager.cc
index 05adbfc..5c3ba07 100644
--- a/components/invalidation/impl/per_user_topic_subscription_manager.cc
+++ b/components/invalidation/impl/per_user_topic_subscription_manager.cc
@@ -54,11 +54,13 @@
 // Note: Taking |topic| and |private_topic_name| by value (rather than const
 // ref) because the caller (in practice, SubscriptionEntry) may be destroyed by
 // the callback.
-using SubscriptionFinishedCallback =
-    base::OnceCallback<void(Topic topic,
-                            Status code,
-                            std::string private_topic_name,
-                            PerUserTopicSubscriptionRequest::RequestType type)>;
+// This is a RepeatingCallback because in case of failure, the request will get
+// retried, so it might actually run multiple times.
+using SubscriptionFinishedCallback = base::RepeatingCallback<void(
+    Topic topic,
+    Status code,
+    std::string private_topic_name,
+    PerUserTopicSubscriptionRequest::RequestType type)>;
 
 static const net::BackoffEntry::Policy kBackoffPolicy = {
     // Number of initial errors (in sequence) to ignore before applying
@@ -201,8 +203,7 @@
 void PerUserTopicSubscriptionManager::SubscriptionEntry::SubscriptionFinished(
     const Status& code,
     const std::string& topic_name) {
-  if (completion_callback)
-    std::move(completion_callback).Run(topic, code, topic_name, type);
+  completion_callback.Run(topic, code, topic_name, type);
 }
 
 void PerUserTopicSubscriptionManager::SubscriptionEntry::Cancel() {
@@ -287,7 +288,7 @@
 
       pending_subscriptions_[topic.first] = std::make_unique<SubscriptionEntry>(
           topic.first,
-          base::BindOnce(
+          base::BindRepeating(
               &PerUserTopicSubscriptionManager::SubscriptionFinishedForTopic,
               base::Unretained(this)),
           PerUserTopicSubscriptionRequest::SUBSCRIBE, topic.second.is_public);
@@ -305,7 +306,7 @@
       // topic, we should probably cancel it first?
       pending_subscriptions_[topic] = std::make_unique<SubscriptionEntry>(
           topic,
-          base::BindOnce(
+          base::BindRepeating(
               &PerUserTopicSubscriptionManager::SubscriptionFinishedForTopic,
               base::Unretained(this)),
           PerUserTopicSubscriptionRequest::UNSUBSCRIBE);
@@ -406,9 +407,6 @@
 
 void PerUserTopicSubscriptionManager::ScheduleRequestForRepetition(
     const Topic& topic) {
-  pending_subscriptions_[topic]->completion_callback = base::BindOnce(
-      &PerUserTopicSubscriptionManager::SubscriptionFinishedForTopic,
-      base::Unretained(this));
   pending_subscriptions_[topic]->request_backoff_.InformOfRequest(false);
   pending_subscriptions_[topic]->request_retry_timer_.Start(
       FROM_HERE,
@@ -473,6 +471,9 @@
   // Only one active request at a time.
   if (access_token_fetcher_ != nullptr)
     return;
+  // TODO(crbug.com/1020117): If the timer is already running, then this method
+  // should probably early-out instead of starting a request immediately. As it
+  // is, this might bypass the exponential backoff.
   request_access_token_retry_timer_.Stop();
   OAuth2AccessTokenManager::ScopeSet oauth2_scopes = {kFCMOAuthScope};
   // Invalidate previous token, otherwise the identity provider will return the
@@ -499,6 +500,8 @@
 void PerUserTopicSubscriptionManager::OnAccessTokenRequestSucceeded(
     const std::string& access_token) {
   // Reset backoff time after successful response.
+  // TODO(crbug.com/1020117): This should probably be .InformOfRequest(true)
+  // rather than .Reset().
   request_access_token_backoff_.Reset();
   access_token_ = access_token;
   // Emit ENABLED when successfully got the token.
diff --git a/components/invalidation/impl/per_user_topic_subscription_manager_unittest.cc b/components/invalidation/impl/per_user_topic_subscription_manager_unittest.cc
index a801481f..977782a 100644
--- a/components/invalidation/impl/per_user_topic_subscription_manager_unittest.cc
+++ b/components/invalidation/impl/per_user_topic_subscription_manager_unittest.cc
@@ -185,6 +185,10 @@
     task_environment_.FastForwardBy(delta);
   }
 
+  signin::IdentityTestEnvironment* identity_test_env() {
+    return &identity_test_env_;
+  }
+
  private:
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -296,6 +300,56 @@
       per_user_topic_subscription_manager->HaveAllRequestsFinishedForTest());
 }
 
+TEST_F(PerUserTopicSubscriptionManagerTest,
+       ShouldInvalidateAccessTokenOnUnauthorized) {
+  // For this test, we need to manually control when access tokens are returned.
+  identity_test_env()->SetAutomaticIssueOfAccessTokens(false);
+
+  auto ids = GetSequenceOfTopics(kInvalidationObjectIdsCount);
+
+  auto per_user_topic_subscription_manager = BuildRegistrationManager();
+  ASSERT_TRUE(per_user_topic_subscription_manager->GetSubscribedTopicsForTest()
+                  .empty());
+
+  // The first subscription attempt will fail with an "unauthorized" error.
+  AddCorrectSubscriptionResponce(
+      /*private_topic=*/std::string(), kFakeInstanceIdToken,
+      net::HTTP_UNAUTHORIZED);
+
+  per_user_topic_subscription_manager->UpdateSubscribedTopics(
+      ids, kFakeInstanceIdToken);
+  // This should have resulted in a request for an access token. Return one
+  // (which is considered invalid, e.g. already expired).
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+      "invalid_access_token", base::Time::Now());
+
+  // Now the subscription requests should be scheduled.
+  ASSERT_FALSE(
+      per_user_topic_subscription_manager->HaveAllRequestsFinishedForTest());
+
+  // Wait for the subscription requests to happen.
+  base::RunLoop().RunUntilIdle();
+
+  // Since the subscriptions failed, the requests should still be pending.
+  ASSERT_FALSE(
+      per_user_topic_subscription_manager->HaveAllRequestsFinishedForTest());
+
+  // A new access token should have been requested. (Note: We'd really want to
+  // check that the previous token got invalidated before a new one was
+  // requested, but the identity test code doesn't expose that.)
+  // Serving a new access token will trigger another subscription attempt; let
+  // this one succeed.
+  AddCorrectSubscriptionResponce();
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+      "valid_access_token", base::Time::Max());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(per_user_topic_subscription_manager->GetSubscribedTopicsForTest()
+                   .empty());
+  EXPECT_TRUE(
+      per_user_topic_subscription_manager->HaveAllRequestsFinishedForTest());
+}
+
 TEST_F(PerUserTopicSubscriptionManagerTest, ShouldRepeatRequestsOnForbidden) {
   auto ids = GetSequenceOfTopics(kInvalidationObjectIdsCount);
 
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index eb8b931..f526df3e 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -145,8 +145,7 @@
   old_matches->BuildProviderToMatchesMove(&old_matches_per_provider);
   for (ProviderToMatches::iterator i = old_matches_per_provider.begin();
        i != old_matches_per_provider.end(); ++i) {
-    MergeMatchesByProvider(input.current_page_classification(), &i->second,
-                           matches_per_provider[i->first]);
+    MergeMatchesByProvider(&i->second, matches_per_provider[i->first]);
   }
 
   SortAndCull(input, template_url_service);
@@ -205,7 +204,7 @@
   MaybeCullTailSuggestions(&matches_, comparing_object);
 #endif
 
-  DeduplicateMatches(input.current_page_classification(), &matches_);
+  DeduplicateMatches(&matches_);
 
   // Sort and trim to the most relevant GetMaxMatches() matches.
   std::sort(matches_.begin(), matches_.end(), comparing_object);
@@ -609,9 +608,7 @@
              : GURL();
 }
 
-void AutocompleteResult::DeduplicateMatches(
-    metrics::OmniboxEventProto::PageClassification page_classification,
-    ACMatches* matches) {
+void AutocompleteResult::DeduplicateMatches(ACMatches* matches) {
   // Group matches by stripped URL and whether it's a calculator suggestion.
   std::unordered_map<std::pair<GURL, bool>, std::vector<ACMatches::iterator>,
                      MatchGURLHash>
@@ -820,10 +817,8 @@
     (*provider_to_matches)[match.provider].push_back(std::move(match));
 }
 
-void AutocompleteResult::MergeMatchesByProvider(
-    metrics::OmniboxEventProto::PageClassification page_classification,
-    ACMatches* old_matches,
-    const ACMatches& new_matches) {
+void AutocompleteResult::MergeMatchesByProvider(ACMatches* old_matches,
+                                                const ACMatches& new_matches) {
   if (new_matches.size() >= old_matches->size())
     return;
 
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index 8f24cdd..c80746a 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -176,9 +176,7 @@
   // Modifies |matches| such that any duplicate matches are coalesced into
   // representative "best" matches. The erased matches are moved into the
   // |duplicate_matches| members of their representative matches.
-  static void DeduplicateMatches(
-      metrics::OmniboxEventProto::PageClassification page_classification,
-      ACMatches* matches);
+  static void DeduplicateMatches(ACMatches* matches);
 
   // Returns true if |matches| contains a match with the same destination as
   // |match|.
@@ -200,10 +198,8 @@
   // Moves matches into this result. |old_matches| gives the matches from the
   // last result, and |new_matches| the results from this result. |old_matches|
   // should not be used afterwards.
-  void MergeMatchesByProvider(
-      metrics::OmniboxEventProto::PageClassification page_classification,
-      ACMatches* old_matches,
-      const ACMatches& new_matches);
+  void MergeMatchesByProvider(ACMatches* old_matches,
+                              const ACMatches& new_matches);
 
   // This pulls the relevant fields out of a match for comparison with other
   // matches for the purpose of deduping. It uses the stripped URL, so that we
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index cf45a40..f3dff82 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -1797,8 +1797,7 @@
 
   FakeAutocompleteProviderClient client;
   result.AppendDedicatedPedalMatches(&client, input);
-  result.DeduplicateMatches(metrics::OmniboxEventProto::OTHER,
-                            &result.matches_);
+  result.DeduplicateMatches(&result.matches_);
 
   // Exactly 2 (not 3) unique Pedals should be added with relevance close to max
   // of the triggering suggestions.
@@ -1813,8 +1812,7 @@
   // no duplicates are added, but the existing Pedal suggestion is updated.
   result.match_at(3)->contents = base::UTF8ToUTF16("open incognito tab");
   result.AppendDedicatedPedalMatches(&client, input);
-  result.DeduplicateMatches(metrics::OmniboxEventProto::OTHER,
-                            &result.matches_);
+  result.DeduplicateMatches(&result.matches_);
   EXPECT_EQ(result.size(), 6u);
   EXPECT_NE(result.match_at(4)->pedal, nullptr);
   EXPECT_NE(result.match_at(5)->pedal, nullptr);
diff --git a/components/omnibox/browser/history_url_provider_unittest.cc b/components/omnibox/browser/history_url_provider_unittest.cc
index fbff269..9bc4cf4 100644
--- a/components/omnibox/browser/history_url_provider_unittest.cc
+++ b/components/omnibox/browser/history_url_provider_unittest.cc
@@ -327,8 +327,7 @@
     for (auto i = matches_.begin(); i != matches_.end(); ++i) {
       i->ComputeStrippedDestinationURL(input, service);
     }
-    AutocompleteResult::DeduplicateMatches(input.current_page_classification(),
-                                           &matches_);
+    AutocompleteResult::DeduplicateMatches(&matches_);
     std::sort(matches_.begin(), matches_.end(),
               &AutocompleteMatch::MoreRelevant);
   }
diff --git a/components/optimization_guide/hint_cache.cc b/components/optimization_guide/hint_cache.cc
index e140b0c..a65da44 100644
--- a/components/optimization_guide/hint_cache.cc
+++ b/components/optimization_guide/hint_cache.cc
@@ -222,7 +222,6 @@
   // If there's no update data, then there's nothing to do.
   if (!update_data)
     return false;
-
   bool processed_hints_to_store = false;
   // Process each hint in the the hint configuration. The hints are mutable
   // because once processing is completed on each individual hint, it is moved
diff --git a/components/optimization_guide/hints_fetcher.cc b/components/optimization_guide/hints_fetcher.cc
index 4942149..c2e52ee 100644
--- a/components/optimization_guide/hints_fetcher.cc
+++ b/components/optimization_guide/hints_fetcher.cc
@@ -56,6 +56,17 @@
   return std::string();
 }
 
+// Returns the subset of URLs from |urls| for which the URL is considered
+// valid and can be included in a hints fetch.
+std::vector<GURL> GetValidURLsForFetching(const std::vector<GURL>& urls) {
+  std::vector<GURL> valid_urls;
+  for (const auto& url : urls) {
+    if (IsValidURLForURLKeyedHint(url))
+      valid_urls.push_back(url);
+  }
+  return valid_urls;
+}
+
 }  // namespace
 
 HintsFetcher::HintsFetcher(
@@ -111,9 +122,13 @@
 
 bool HintsFetcher::FetchOptimizationGuideServiceHints(
     const std::vector<std::string>& hosts,
+    const std::vector<GURL>& urls,
+    const base::flat_set<optimization_guide::proto::OptimizationType>&
+        optimization_types,
     optimization_guide::proto::RequestContext request_context,
     HintsFetchedCallback hints_fetched_callback) {
   SEQUENCE_CHECKER(sequence_checker_);
+  DCHECK_GT(optimization_types.size(), 0u);
 
   if (content::GetNetworkConnectionTracker()->IsOffline()) {
     std::move(hints_fetched_callback)
@@ -131,9 +146,10 @@
 
   std::vector<std::string> filtered_hosts =
       GetSizeLimitedHostsDueForHintsRefresh(hosts);
-  if (filtered_hosts.empty()) {
+  std::vector<GURL> valid_urls = GetValidURLsForFetching(urls);
+  if (filtered_hosts.empty() && valid_urls.empty()) {
     std::move(hints_fetched_callback)
-        .Run(request_context, HintsFetcherRequestStatus::kNoHostsToFetch,
+        .Run(request_context, HintsFetcherRequestStatus::kNoHostsOrURLsToFetch,
              base::nullopt);
     return false;
   }
@@ -141,22 +157,27 @@
   DCHECK_GE(features::MaxHostsForOptimizationGuideServiceHintsFetch(),
             filtered_hosts.size());
 
+  if (optimization_types.empty()) {
+    std::move(hints_fetched_callback)
+        .Run(request_context,
+             HintsFetcherRequestStatus::kNoSupportedOptimizationTypes,
+             base::nullopt);
+    return false;
+  }
+
   hints_fetch_start_time_ = base::TimeTicks::Now();
   request_context_ = request_context;
 
   get_hints_request_ = std::make_unique<proto::GetHintsRequest>();
 
-  // Add all the optimizations supported by the current version of Chrome,
-  // regardless of whether the session is in holdback for any of them.
-  get_hints_request_->add_supported_optimizations(proto::NOSCRIPT);
-  get_hints_request_->add_supported_optimizations(proto::RESOURCE_LOADING);
-  get_hints_request_->add_supported_optimizations(proto::DEFER_ALL_SCRIPT);
-  // TODO(crbug/969558): Figure out a way to either have a registration call
-  // for clients to specify their supported optimization types or have a static
-  // assert on the last OptimizationType.
+  for (const auto& optimization_type : optimization_types)
+    get_hints_request_->add_supported_optimizations(optimization_type);
 
   get_hints_request_->set_context(request_context_);
 
+  for (const auto& url : valid_urls)
+    get_hints_request_->add_urls()->set_url(url.spec());
+
   for (const auto& host : filtered_hosts) {
     proto::HostInfo* host_info = get_hints_request_->add_hosts();
     host_info->set_host(host);
diff --git a/components/optimization_guide/hints_fetcher.h b/components/optimization_guide/hints_fetcher.h
index 282da88..7bb8829 100644
--- a/components/optimization_guide/hints_fetcher.h
+++ b/components/optimization_guide/hints_fetcher.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
@@ -43,11 +44,14 @@
   kNetworkOffline,
   // Fetch request not sent because fetcher was busy with another request.
   kFetcherBusy,
-  // Fetch request not sent because the host list was empty.
-  kNoHostsToFetch,
+  // Fetch request not sent because the host and URL lists were empty.
+  kNoHostsOrURLsToFetch,
+  // Fetch request not sent because no supported optimization types were
+  // provided.
+  kNoSupportedOptimizationTypes,
 
   // Insert new values before this line.
-  kMaxValue = kNoHostsToFetch
+  kMaxValue = kNoSupportedOptimizationTypes
 };
 
 // Callback to inform the caller that the remote hints have been fetched and
@@ -75,12 +79,19 @@
   // Requests hints from the Optimization Guide Service if a request for them is
   // not already in progress. Returns whether a new request was issued.
   // |hints_fetched_callback| is run once when the outcome of this request is
-  // determined (whether a request was actually sent or not).
-  // Virtualized for testing. Hints fetcher may fetch hints for only a subset
-  // of the provided |hosts|. |hosts| should be an ordered list in descending
-  // order of probability that the hints are needed for that host.
+  // determined (whether a request was actually sent or not). Virtualized for
+  // testing. Hints fetcher may fetch hints for only a subset of the provided
+  // |hosts|. |hosts| should be an ordered list in descending order of
+  // probability that the hints are needed for that host. Only supported |urls|
+  // will be included in the fetch. |urls| is an ordered list in descending
+  // order of probability that a hint will be needed for the URL. The supplied
+  // optimization types will be included in the request, if empty no fetch will
+  // be made.
   virtual bool FetchOptimizationGuideServiceHints(
       const std::vector<std::string>& hosts,
+      const std::vector<GURL>& urls,
+      const base::flat_set<optimization_guide::proto::OptimizationType>&
+          optimization_types,
       optimization_guide::proto::RequestContext request_context,
       HintsFetchedCallback hints_fetched_callback);
 
diff --git a/components/optimization_guide/hints_fetcher_unittest.cc b/components/optimization_guide/hints_fetcher_unittest.cc
index 5049a03..e554707 100644
--- a/components/optimization_guide/hints_fetcher_unittest.cc
+++ b/components/optimization_guide/hints_fetcher_unittest.cc
@@ -109,9 +109,11 @@
   }
 
  protected:
-  bool FetchHints(const std::vector<std::string>& hosts) {
+  bool FetchHints(const std::vector<std::string>& hosts,
+                  const std::vector<GURL>& urls) {
     bool status = hints_fetcher_->FetchOptimizationGuideServiceHints(
-        hosts, optimization_guide::proto::CONTEXT_BATCH_UPDATE,
+        hosts, urls, {optimization_guide::proto::NOSCRIPT},
+        optimization_guide::proto::CONTEXT_BATCH_UPDATE,
         base::BindOnce(&HintsFetcherTest::OnHintsFetched,
                        base::Unretained(this)));
     RunUntilIdle();
@@ -167,7 +169,7 @@
   base::HistogramTester histogram_tester;
 
   std::string response_content;
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"foo.com"}));
+  EXPECT_TRUE(FetchHints({"foo.com"}, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_EQ(optimization_guide::HintsFetcherRequestStatus::kSuccess,
@@ -190,14 +192,14 @@
   std::string response_content;
   // Fetch back to back without waiting for Fetch to complete,
   // |fetch_in_progress_| should cause early exit.
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"foo.com"}));
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"bar.com"}));
+  EXPECT_TRUE(FetchHints({"foo.com"}, {} /* urls */));
+  EXPECT_FALSE(FetchHints({"bar.com"}, {} /* urls */));
   EXPECT_EQ(optimization_guide::HintsFetcherRequestStatus::kFetcherBusy,
             fetcher_request_status());
 
   // Once response arrives, check to make sure a new fetch can start.
   SimulateResponse(response_content, net::HTTP_OK);
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"bar.com"}));
+  EXPECT_TRUE(FetchHints({"bar.com"}, {} /* urls */));
   EXPECT_EQ(optimization_guide::HintsFetcherRequestStatus::kSuccess,
             fetcher_request_status());
 }
@@ -211,19 +213,19 @@
   std::string response_content;
   // Fetch back to back without waiting for Fetch to complete,
   // |fetch_in_progress_| should cause early exit.
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"foo.com"}));
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"foo.com"}));
+  EXPECT_TRUE(FetchHints({"foo.com"}, {} /* urls */));
+  EXPECT_FALSE(FetchHints({"foo.com"}, {} /* urls */));
 
   // Once response arrives, check to make sure that the fetch for the same host
   // is not started again.
   SimulateResponse(response_content, net::HTTP_OK);
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"foo.com"}));
+  EXPECT_FALSE(FetchHints({"foo.com"}, {} /* urls */));
   // Ensure a new fetch for a different host can start.
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"bar.com"}));
+  EXPECT_TRUE(FetchHints({"bar.com"}, {} /* urls */));
   SimulateResponse(response_content, net::HTTP_OK);
 
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"foo.com"}));
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"bar.com"}));
+  EXPECT_FALSE(FetchHints({"foo.com"}, {} /* urls */));
+  EXPECT_FALSE(FetchHints({"bar.com"}, {} /* urls */));
 
   std::vector<std::string> hosts{"foo.com", "bar.com"};
   // Advancing the clock so that it's still one hour before the hints need to be
@@ -232,28 +234,28 @@
                      features::GetHintsFetchRefreshDuration() -
                      base::TimeDelta().FromHours(1));
 
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"foo.com"}));
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"bar.com"}));
+  EXPECT_FALSE(FetchHints({"foo.com"}, {} /* urls */));
+  EXPECT_FALSE(FetchHints({"bar.com"}, {} /* urls */));
 
   // Advancing the clock by a little bit more than 1 hour so that the hints are
   // now due for refresh.
   test_clock.Advance(base::TimeDelta::FromMinutes(61));
 
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"foo.com"}));
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"bar.com"}));
+  EXPECT_TRUE(FetchHints({"foo.com"}, {} /* urls */));
+  EXPECT_FALSE(FetchHints({"bar.com"}, {} /* urls */));
   SimulateResponse(response_content, net::HTTP_OK);
 
   // Hints should not be fetched again for foo.com since they were fetched
   // recently. Hints should still be fetched for bar.com.
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"foo.com"}));
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"bar.com"}));
+  EXPECT_FALSE(FetchHints({"foo.com"}, {} /* urls */));
+  EXPECT_TRUE(FetchHints({"bar.com"}, {} /* urls */));
   SimulateResponse(response_content, net::HTTP_OK);
 
   // Hints should not be fetched again for foo.com and bar.com since they were
   // fetched recently. For baz.com, hints should be fetched again.
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"foo.com"}));
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"bar.com"}));
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"baz.com"}));
+  EXPECT_FALSE(FetchHints({"foo.com"}, {} /* urls */));
+  EXPECT_FALSE(FetchHints({"bar.com"}, {} /* urls */));
+  EXPECT_TRUE(FetchHints({"baz.com"}, {} /* urls */));
 }
 
 // Tests 404 response from request.
@@ -262,7 +264,7 @@
 
   std::string response_content;
 
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"foo.com"}));
+  EXPECT_TRUE(FetchHints({"foo.com"}, {} /* urls */));
 
   // Send a 404 to HintsFetcher.
   SimulateResponse(response_content, net::HTTP_NOT_FOUND);
@@ -279,7 +281,7 @@
   base::HistogramTester histogram_tester;
 
   std::string response_content = "not proto";
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"foo.com"}));
+  EXPECT_TRUE(FetchHints({"foo.com"}, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_FALSE(hints_fetched());
@@ -296,7 +298,7 @@
 
   SetConnectionOffline();
   std::string response_content;
-  EXPECT_FALSE(FetchHints(std::vector<std::string>{"foo.com"}));
+  EXPECT_FALSE(FetchHints({"foo.com"}, {} /* urls */));
   EXPECT_FALSE(hints_fetched());
   EXPECT_EQ(optimization_guide::HintsFetcherRequestStatus::kNetworkOffline,
             fetcher_request_status());
@@ -306,7 +308,7 @@
       "OptimizationGuide.HintsFetcher.GetHintsRequest.FetchLatency", 0);
 
   SetConnectionOnline();
-  EXPECT_TRUE(FetchHints(std::vector<std::string>{"foo.com"}));
+  EXPECT_TRUE(FetchHints({"foo.com"}, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_TRUE(hints_fetched());
@@ -322,7 +324,7 @@
   std::vector<std::string> hosts{"host1.com", "host2.com"};
   std::string response_content;
 
-  EXPECT_TRUE(FetchHints(hosts));
+  EXPECT_TRUE(FetchHints(hosts, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_TRUE(hints_fetched());
@@ -347,7 +349,7 @@
   std::vector<std::string> hosts{"host1.com", "host2.com"};
   std::string response_content;
 
-  EXPECT_TRUE(FetchHints(hosts));
+  EXPECT_TRUE(FetchHints(hosts, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_NOT_FOUND));
   EXPECT_FALSE(hints_fetched());
@@ -363,7 +365,7 @@
   std::vector<std::string> hosts{"host1.com", "host2.com"};
   std::string response_content;
 
-  EXPECT_TRUE(FetchHints(hosts));
+  EXPECT_TRUE(FetchHints(hosts, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_TRUE(hints_fetched());
@@ -405,7 +407,7 @@
 
   // Fetch hints for new hosts.
   std::vector<std::string> hosts_valid{"host3.com", "hosts4.com"};
-  EXPECT_TRUE(FetchHints(hosts_valid));
+  EXPECT_TRUE(FetchHints(hosts_valid, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_TRUE(hints_fetched());
@@ -454,7 +456,7 @@
 
   std::vector<std::string> hosts_valid{"host3.com", "host4.com"};
 
-  EXPECT_TRUE(FetchHints(hosts_valid));
+  EXPECT_TRUE(FetchHints(hosts_valid, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_TRUE(hints_fetched());
@@ -488,7 +490,7 @@
 
   std::vector<std::string> extra_hosts{"extra1.com", "extra2.com"};
 
-  EXPECT_TRUE(FetchHints(extra_hosts));
+  EXPECT_TRUE(FetchHints(extra_hosts, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_TRUE(hints_fetched());
@@ -521,7 +523,7 @@
   all_hosts.push_back("extra1.com");
   all_hosts.push_back("extra2.com");
 
-  EXPECT_TRUE(FetchHints(all_hosts));
+  EXPECT_TRUE(FetchHints(all_hosts, {} /* urls */));
   VerifyHasPendingFetchRequests();
   EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
   EXPECT_TRUE(hints_fetched());
@@ -537,4 +539,34 @@
   }
 }
 
+TEST_F(HintsFetcherTest, OnlyURLsToFetch) {
+  base::HistogramTester histogram_tester;
+  std::string response_content;
+
+  EXPECT_TRUE(FetchHints({}, {GURL("https://baz.com/r/werd")}));
+  VerifyHasPendingFetchRequests();
+  EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
+  EXPECT_EQ(optimization_guide::HintsFetcherRequestStatus::kSuccess,
+            fetcher_request_status());
+  EXPECT_TRUE(hints_fetched());
+
+  histogram_tester.ExpectTotalCount(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.FetchLatency", 1);
+  histogram_tester.ExpectTotalCount(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.FetchLatency.BatchUpdate",
+      1);
+}
+
+TEST_F(HintsFetcherTest, NoHostsOrURLsToFetch) {
+  base::HistogramTester histogram_tester;
+  std::string response_content;
+
+  EXPECT_FALSE(FetchHints({} /* hosts */, {} /* urls */
+                          ));
+  EXPECT_FALSE(hints_fetched());
+  EXPECT_EQ(
+      optimization_guide::HintsFetcherRequestStatus::kNoHostsOrURLsToFetch,
+      fetcher_request_status());
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/optimization_guide_enums.h b/components/optimization_guide/optimization_guide_enums.h
index bfe1aa8..1f517777 100644
--- a/components/optimization_guide/optimization_guide_enums.h
+++ b/components/optimization_guide/optimization_guide_enums.h
@@ -93,6 +93,29 @@
   kMaxValue = kFetchNotSuccessful,
 };
 
+// The statuses for racing a hints fetch with the current navigation based
+// on the availability of hints for both the current host and URL.
+//
+// Keep in sync with OptimizationGuideRaceNavigationFetchAttemptStatus in
+// enums.xml.
+enum class RaceNavigationFetchAttemptStatus {
+  kUnknown,
+  // The race was not attempted because hint information for the host and URL
+  // of the current navigation was already available.
+  kRaceNavigationFetchNotAttempted,
+  // The race was attempted for the host of the current navigation but not the
+  // URL.
+  kRaceNavigationFetchHost,
+  // The race was attempted for the URL of the current navigation but not the
+  // host.
+  kRaceNavigationFetchURL,
+  // The race was attempted for the host and URL of the current navigation.
+  kRaceNavigationFetchHostAndURL,
+
+  // Add new values above this line.
+  kMaxValue = kRaceNavigationFetchHostAndURL,
+};
+
 // The statuses for a prediction model in the prediction manager when requested
 // to be evaluated.
 //
diff --git a/components/os_crypt/key_storage_linux.cc b/components/os_crypt/key_storage_linux.cc
index 2f2d818..7232e69f 100644
--- a/components/os_crypt/key_storage_linux.cc
+++ b/components/os_crypt/key_storage_linux.cc
@@ -82,8 +82,6 @@
   return BackendUsage::kDeferFailed;
 }
 
-#if defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
-
 const char* SelectedLinuxBackendToString(
     os_crypt::SelectedLinuxBackend selection) {
   switch (selection) {
@@ -106,9 +104,6 @@
   return nullptr;
 }
 
-#endif  // defined(USE_LIBSECRET) || defined(USE_KEYRING) ||
-        // defined(USE_KWALLET)
-
 }  // namespace
 
 // static
diff --git a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
index 3d46ac06..9a57565 100644
--- a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
@@ -750,7 +750,7 @@
   const page_load_metrics::ContentfulPaintTimingInfo&
       main_frame_largest_image_paint =
           largest_contentful_paint_handler_.MainFrameLargestImagePaint();
-  if (!main_frame_largest_image_paint.IsEmpty() &&
+  if (main_frame_largest_image_paint.ContainsValidTime() &&
       WasStartedInForegroundOptionalEventInForeground(
           main_frame_largest_image_paint.Time(), GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestImagePaint,
@@ -760,7 +760,7 @@
   const page_load_metrics::ContentfulPaintTimingInfo&
       main_frame_largest_text_paint =
           largest_contentful_paint_handler_.MainFrameLargestTextPaint();
-  if (!main_frame_largest_text_paint.IsEmpty() &&
+  if (main_frame_largest_text_paint.ContainsValidTime() &&
       WasStartedInForegroundOptionalEventInForeground(
           main_frame_largest_text_paint.Time(), GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestTextPaint,
@@ -770,7 +770,7 @@
   const page_load_metrics::ContentfulPaintTimingInfo&
       main_frame_largest_contentful_paint =
           largest_contentful_paint_handler_.MainFrameLargestContentfulPaint();
-  if (!main_frame_largest_contentful_paint.IsEmpty() &&
+  if (main_frame_largest_contentful_paint.ContainsValidTime() &&
       WasStartedInForegroundOptionalEventInForeground(
           main_frame_largest_contentful_paint.Time(), GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentfulPaintMainFrame,
@@ -783,7 +783,7 @@
   const page_load_metrics::ContentfulPaintTimingInfo&
       all_frames_largest_contentful_paint =
           largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
-  if (!all_frames_largest_contentful_paint.IsEmpty() &&
+  if (all_frames_largest_contentful_paint.ContainsValidTime() &&
       WasStartedInForegroundOptionalEventInForeground(
           all_frames_largest_contentful_paint.Time(), GetDelegate())) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentfulPaint,
diff --git a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
index 93c1862..acf7d36b 100644
--- a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
@@ -690,6 +690,66 @@
               testing::ElementsAre(base::Bucket(4780, 1)));
 }
 
+TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoading) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  // Largest image is loading so its timestamp is TimeDelta().
+  timing.paint_timing->largest_image_paint = base::TimeDelta();
+  timing.paint_timing->largest_image_paint_size = 100u;
+  // There is a text paint but it's smaller than image. Pick a value that lines
+  // up with a histogram bucket.
+  timing.paint_timing->largest_text_paint =
+      base::TimeDelta::FromMilliseconds(4780);
+  timing.paint_timing->largest_text_paint_size = 70u;
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  tester()->SimulateTimingUpdate(timing);
+  // Navigate again to force histogram recording.
+  NavigateAndCommit(GURL(kDefaultTestUrl2));
+
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestImagePaint, 0);
+  // The image was larger so LCP should NOT be reported.
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestContentfulPaint, 0);
+
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestTextPaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
+}
+
+TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoadingSmallerThanText) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  // Largest image is loading so its timestamp is TimeDelta().
+  timing.paint_timing->largest_image_paint = base::TimeDelta();
+  timing.paint_timing->largest_image_paint_size = 100u;
+  // There is a text paint but it's smaller than image. Pick a value that lines
+  // up with a histogram bucket.
+  timing.paint_timing->largest_text_paint =
+      base::TimeDelta::FromMilliseconds(4780);
+  timing.paint_timing->largest_text_paint_size = 120u;
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  tester()->SimulateTimingUpdate(timing);
+  // Navigate again to force histogram recording.
+  NavigateAndCommit(GURL(kDefaultTestUrl2));
+
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestImagePaint, 0);
+
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestTextPaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestContentfulPaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
+}
+
 TEST_F(CorePageLoadMetricsObserverTest,
        LargestContentfulPaintAllFrames_OnlySubframeProvided) {
   const char kSubframeTestUrl[] = "https://google.com/subframe.html";
@@ -747,6 +807,52 @@
 }
 
 TEST_F(CorePageLoadMetricsObserverTest,
+       LargestContentfulPaintAllFrames_SubframeImageLoading) {
+  const char kSubframeTestUrl[] = "https://google.com/subframe.html";
+
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(100);
+  // Intentionally not set candidates for the main frame.
+  PopulateRequiredTimingFields(&timing);
+
+  // Create a subframe timing with a largest_image_paint.
+  page_load_metrics::mojom::PageLoadTiming subframe_timing;
+  page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
+  subframe_timing.navigation_start = base::Time::FromDoubleT(200);
+  subframe_timing.paint_timing->largest_image_paint = base::TimeDelta();
+  subframe_timing.paint_timing->largest_image_paint_size = 100u;
+  subframe_timing.paint_timing->largest_text_paint =
+      base::TimeDelta::FromMilliseconds(500);
+  subframe_timing.paint_timing->largest_text_paint_size = 80u;
+  PopulateRequiredTimingFields(&subframe_timing);
+
+  // Commit the main frame and a subframe.
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  RenderFrameHost* subframe =
+      NavigationSimulator::NavigateAndCommitFromDocument(
+          GURL(kSubframeTestUrl),
+          RenderFrameHostTester::For(web_contents()->GetMainFrame())
+              ->AppendChild("subframe"));
+
+  // Simulate timing updates in the main frame and the subframe.
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
+
+  // Navigate again to force histogram recording in the main frame.
+  NavigateAndCommit(GURL(kDefaultTestUrl2));
+
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestContentfulPaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestContentfulPaintContentType, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestContentfulPaintMainFrame, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestContentfulPaintMainFrameContentType, 0);
+}
+
+TEST_F(CorePageLoadMetricsObserverTest,
        LargestContentfulPaintAllFrames_OnlyMainFrameProvided) {
   const char kSubframeTestUrl[] = "https://google.com/subframe.html";
 
diff --git a/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
index b8918a9..4807994 100644
--- a/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
+++ b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.cc
@@ -21,12 +21,12 @@
     const ContentfulPaintTimingInfo& timing1,
     const ContentfulPaintTimingInfo& timing2) {
   // When both are empty, just return either.
-  if (timing1.IsEmpty() && timing2.IsEmpty())
+  if (timing1.Empty() && timing2.Empty())
     return timing1;
 
-  if (timing1.IsEmpty() && !timing2.IsEmpty())
+  if (timing1.Empty() && !timing2.Empty())
     return timing2;
-  if (!timing1.IsEmpty() && timing2.IsEmpty())
+  if (!timing1.Empty() && timing2.Empty())
     return timing1;
   if (timing1.Size() > timing2.Size())
     return timing1;
@@ -58,11 +58,16 @@
     const base::Optional<base::TimeDelta>& candidate_new_time,
     const uint64_t& candidate_new_size,
     base::TimeDelta navigation_start_offset) {
-  MergeForSubframesWithAdjustedTime(
-      inout_timing,
-      candidate_new_time ? navigation_start_offset + candidate_new_time.value()
-                         : candidate_new_time,
-      candidate_new_size);
+  base::Optional<base::TimeDelta> new_time = base::nullopt;
+  if (candidate_new_time) {
+    // If |candidate_new_time| is TimeDelta(), this means that the candidate is
+    // an image that has not finished loading. Preserve its meaning by not
+    // adding the |navigation_start_offset|.
+    new_time = *candidate_new_time > base::TimeDelta()
+                   ? navigation_start_offset + candidate_new_time.value()
+                   : base::TimeDelta();
+  }
+  MergeForSubframesWithAdjustedTime(inout_timing, new_time, candidate_new_size);
 }
 
 bool IsSubframe(content::RenderFrameHost* subframe_rfh) {
diff --git a/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
index fbba5dd4..e2912b2 100644
--- a/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
+++ b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
@@ -32,10 +32,19 @@
     return type_;
   }
 
-  bool IsEmpty() const {
+  // Returns true iff this object does not represent any paint.
+  bool Empty() const {
+    // size_ and time_ should both be set or both be unset.
+    DCHECK((size_ != 0u && time_) || (size_ == 0u && !time_));
     return !time_;
   }
 
+  // Returns true iff this object does not represent any paint OR represents an
+  // image that has not finished loading.
+  bool ContainsValidTime() const {
+    return time_ && *time_ != base::TimeDelta();
+  }
+
   std::unique_ptr<base::trace_event::TracedValue> DataAsTraceValue() const;
 
  private:
diff --git a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
index 6c5b9a61..91d8de81 100644
--- a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
+++ b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
@@ -405,24 +405,27 @@
     timing->paint_timing->first_meaningful_paint =
         ClampDelta(perf.FirstMeaningfulPaint(), start);
   }
-  if (perf.LargestImagePaint() > 0.0) {
-    timing->paint_timing->largest_image_paint =
-        ClampDelta(perf.LargestImagePaint(), start);
+  if (perf.LargestImagePaintSize() > 0) {
     timing->paint_timing->largest_image_paint_size =
         perf.LargestImagePaintSize();
-    // LargestImagePaintSize should be available if LargestImagePaint is
-    // available. Note that size can be nonzero while the time is 0 since a time
-    // of 0 is sent when the image is painting. We are intentionally ignoring
-    // these cases, as they should not be reported by the UMA/UKM histograms.
-    DCHECK(perf.LargestImagePaintSize());
+    // Note that size can be nonzero while the time is 0 since a time of 0 is
+    // sent when the image is painting. We assign the time even when it is 0 so
+    // that it's not ignored, but need to be careful when doing operations on
+    // the value.
+    timing->paint_timing->largest_image_paint =
+        perf.LargestImagePaint() == 0.0
+            ? base::TimeDelta()
+            : ClampDelta(perf.LargestImagePaint(), start);
   }
-  if (perf.LargestTextPaint() > 0.0) {
+  if (perf.LargestTextPaintSize() > 0) {
     timing->paint_timing->largest_text_paint =
-        ClampDelta(perf.LargestTextPaint(), start);
+        perf.LargestTextPaint() == 0.0
+            ? base::TimeDelta()
+            : ClampDelta(perf.LargestTextPaint(), start);
     timing->paint_timing->largest_text_paint_size = perf.LargestTextPaintSize();
     // LargestTextPaint and LargestTextPaintSize should be available at the
     // same time. This is a renderer side DCHECK to ensure this.
-    DCHECK(perf.LargestTextPaintSize());
+    DCHECK(perf.LargestTextPaint());
   }
   if (perf.ParseStart() > 0.0)
     timing->parse_timing->parse_start = ClampDelta(perf.ParseStart(), start);
diff --git a/components/password_manager/core/browser/compromised_credentials_observer.cc b/components/password_manager/core/browser/compromised_credentials_observer.cc
index 03328332..13d1c43 100644
--- a/components/password_manager/core/browser/compromised_credentials_observer.cc
+++ b/components/password_manager/core/browser/compromised_credentials_observer.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/metrics/histogram_macros.h"
+#include "components/password_manager/core/browser/compromised_credentials_table.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/safe_browsing/core/features.h"
 
@@ -35,14 +36,24 @@
       !base::FeatureList::IsEnabled(password_manager::features::kLeakHistory))
     return;
 
-  for (const auto& change : changes) {
-    if (change.password_changed()) {
-      store_->RemoveCompromisedCredentials(GURL(change.form().signon_realm),
-                                           change.form().username_value);
-      UMA_HISTOGRAM_ENUMERATION("PasswordManager.RemoveCompromisedCredentials",
-                                change.type());
-    }
+  // If the change is an UPDATE and the password did not change, there is
+  // nothing to remove. If the change is an ADD there is also nothing to remove.
+  if (changes[0].type() == PasswordStoreChange::ADD ||
+      (changes[0].type() == PasswordStoreChange::UPDATE &&
+       !changes[0].password_changed())) {
+    return;
   }
+
+  // An internal update could be a (REMOVE + ADD) or (UPDATE).
+  RemoveCompromisedCredentialsReason reason =
+      changes.size() != 1 || changes[0].type() == PasswordStoreChange::UPDATE
+          ? RemoveCompromisedCredentialsReason::kUpdate
+          : RemoveCompromisedCredentialsReason::kRemove;
+  store_->RemoveCompromisedCredentials(GURL(changes[0].form().signon_realm),
+                                       changes[0].form().username_value,
+                                       reason);
+  UMA_HISTOGRAM_ENUMERATION("PasswordManager.RemoveCompromisedCredentials",
+                            changes[0].type());
 }
 
 }  // namespace password_manager
\ No newline at end of file
diff --git a/components/password_manager/core/browser/compromised_credentials_table.cc b/components/password_manager/core/browser/compromised_credentials_table.cc
index 05902f4..e0432a41 100644
--- a/components/password_manager/core/browser/compromised_credentials_table.cc
+++ b/components/password_manager/core/browser/compromised_credentials_table.cc
@@ -169,8 +169,10 @@
   return s.Run();
 }
 
-bool CompromisedCredentialsTable::RemoveRow(const GURL& url,
-                                            const base::string16& username) {
+bool CompromisedCredentialsTable::RemoveRow(
+    const GURL& url,
+    const base::string16& username,
+    RemoveCompromisedCredentialsReason reason) {
   if (!db_ || !url.is_valid())
     return false;
 
@@ -184,6 +186,8 @@
   for (const auto& compromised_credential : compromised_credentials) {
     UMA_HISTOGRAM_ENUMERATION("PasswordManager.CompromisedCredentials.Remove",
                               compromised_credential.compromise_type);
+    UMA_HISTOGRAM_ENUMERATION(
+        "PasswordManager.RemoveCompromisedCredentials.RemoveReason", reason);
   }
 
   sql::Statement s(db_->GetCachedStatement(
diff --git a/components/password_manager/core/browser/compromised_credentials_table.h b/components/password_manager/core/browser/compromised_credentials_table.h
index 91e2ad0..5b176362 100644
--- a/components/password_manager/core/browser/compromised_credentials_table.h
+++ b/components/password_manager/core/browser/compromised_credentials_table.h
@@ -23,6 +23,14 @@
   kMaxValue = kPhished
 };
 
+enum class RemoveCompromisedCredentialsReason {
+  // If the password was updated in the password store.
+  kUpdate = 0,
+  // If the password is removed from the password store.
+  kRemove = 1,
+  kMaxValue = kRemove
+};
+
 // Represents information about the particular compromised credentials.
 struct CompromisedCredentials {
   CompromisedCredentials(GURL url,
@@ -72,9 +80,12 @@
                  const base::string16& old_username) const;
 
   // Removes information about the credentials compromised for |username| on
-  // |url|. Returns true if the SQL completed successfully.
+  // |url|. |reason| is the reason why the credentials is removed from
+  // the table. Returns true if the SQL completed successfully.
   // Also logs the compromise type in UMA.
-  bool RemoveRow(const GURL& url, const base::string16& username);
+  bool RemoveRow(const GURL& url,
+                 const base::string16& username,
+                 RemoveCompromisedCredentialsReason reason);
 
   // Gets all the rows in the database for the |username| and |url|.
   std::vector<CompromisedCredentials> GetRows(
diff --git a/components/password_manager/core/browser/compromised_credentials_table_unittest.cc b/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
index 2333ff08..9ac49403 100644
--- a/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
+++ b/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
@@ -54,7 +54,8 @@
     EXPECT_THAT(db()->GetAllRows(), ElementsAre(test_data()));
     EXPECT_THAT(db()->GetRows(test_data().url, test_data().username),
                 ElementsAre(test_data()));
-    EXPECT_TRUE(db()->RemoveRow(test_data().url, test_data().username));
+    EXPECT_TRUE(db()->RemoveRow(test_data().url, test_data().username,
+                                RemoveCompromisedCredentialsReason::kRemove));
     EXPECT_THAT(db()->GetAllRows(), IsEmpty());
     EXPECT_THAT(db()->GetRows(test_data().url, test_data().username),
                 IsEmpty());
@@ -105,7 +106,8 @@
   EXPECT_THAT(db()->GetAllRows(), IsEmpty());
   EXPECT_THAT(db()->GetRows(test_data().url, test_data().username),
               ElementsAre());
-  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username));
+  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username,
+                               RemoveCompromisedCredentialsReason::kRemove));
   EXPECT_TRUE(db()->AddRow(test_data()));
 }
 
@@ -286,14 +288,16 @@
   test_data().url = GURL("bad");
   EXPECT_FALSE(db()->AddRow(test_data()));
   EXPECT_THAT(db()->GetAllRows(), IsEmpty());
-  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username));
+  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username,
+                               RemoveCompromisedCredentialsReason::kRemove));
 }
 
 TEST_F(CompromisedCredentialsTableTest, EmptyURL) {
   test_data().url = GURL();
   EXPECT_FALSE(db()->AddRow(test_data()));
   EXPECT_THAT(db()->GetAllRows(), IsEmpty());
-  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username));
+  EXPECT_FALSE(db()->RemoveRow(test_data().url, test_data().username,
+                               RemoveCompromisedCredentialsReason::kRemove));
 }
 
 }  // namespace
diff --git a/components/password_manager/core/browser/leak_detection_delegate.cc b/components/password_manager/core/browser/leak_detection_delegate.cc
index 792b4fb..0e71e4d6 100644
--- a/components/password_manager/core/browser/leak_detection_delegate.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate.cc
@@ -85,10 +85,6 @@
     if (is_leaked) {
       password_store->AddCompromisedCredentials(CompromisedCredentials(
           url, username, base::Time::Now(), CompromiseType::kLeaked));
-    } else {
-      // If the credentials are not saved as leaked in the database, this call
-      // will just get ignored.
-      password_store->RemoveCompromisedCredentials(url, username);
     }
   }
 
diff --git a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
index ae607489..c692812 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
@@ -200,28 +200,6 @@
       "PasswordManager.LeakDetection.NotifyIsLeakedTime", 1);
 }
 
-TEST_F(LeakDetectionDelegateTest, LeakHistoryRemoveCredentials) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kLeakHistory);
-  LeakDetectionDelegateInterface* delegate_interface = &delegate();
-  const autofill::PasswordForm form = CreateTestForm();
-
-  EXPECT_CALL(client(), GetProfilePasswordStore())
-      .WillRepeatedly(testing::Return(store()));
-  EXPECT_CALL(factory(), TryCreateLeakCheck)
-      .WillOnce(Return(ByMove(std::make_unique<MockLeakDetectionCheck>())));
-  delegate().StartLeakCheck(form);
-
-  EXPECT_CALL(client(), NotifyUserCredentialsWereLeaked).Times(0);
-  delegate_interface->OnLeakDetectionDone(
-      /*is_leaked=*/false, form.origin, form.username_value,
-      form.password_value);
-
-  EXPECT_CALL(*store(), RemoveCompromisedCredentialsImpl(form.origin,
-                                                         form.username_value));
-  WaitForPasswordStore();
-}
-
 TEST_F(LeakDetectionDelegateTest, LeakHistoryAddCredentials) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(features::kLeakHistory);
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index aa9bf30..b22ace0 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -75,8 +75,10 @@
   MOCK_METHOD1(RemoveSiteStatsImpl, void(const GURL&));
   MOCK_METHOD1(AddCompromisedCredentialsImpl,
                void(const CompromisedCredentials&));
-  MOCK_METHOD2(RemoveCompromisedCredentialsImpl,
-               void(const GURL&, const base::string16&));
+  MOCK_METHOD3(RemoveCompromisedCredentialsImpl,
+               void(const GURL&,
+                    const base::string16&,
+                    RemoveCompromisedCredentialsReason));
   MOCK_METHOD0(GetAllCompromisedCredentialsImpl,
                std::vector<CompromisedCredentials>());
   MOCK_METHOD3(RemoveCompromisedCredentialsByUrlAndTimeImpl,
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index afc9621..96163927 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -379,10 +379,11 @@
 
 void PasswordStore::RemoveCompromisedCredentials(
     const GURL& url,
-    const base::string16& username) {
+    const base::string16& username,
+    RemoveCompromisedCredentialsReason reason) {
   DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   ScheduleTask(base::BindOnce(&PasswordStore::RemoveCompromisedCredentialsImpl,
-                              this, url, username));
+                              this, url, username, reason));
 }
 
 void PasswordStore::GetAllCompromisedCredentials(
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index eb23b28..1021091 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -19,6 +19,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/keyed_service/core/refcounted_keyed_service.h"
+#include "components/password_manager/core/browser/compromised_credentials_table.h"
 #include "components/password_manager/core/browser/password_store_change.h"
 #include "components/password_manager/core/browser/password_store_sync.h"
 #include "components/sync/model/syncable_service.h"
@@ -252,7 +253,8 @@
 
   // Removes information about credentials compromised on |url| for |username|.
   void RemoveCompromisedCredentials(const GURL& url,
-                                    const base::string16& username);
+                                    const base::string16& username,
+                                    RemoveCompromisedCredentialsReason reason);
 
   // Retrieves all compromised credentials and notifies |consumer| on
   // completion. The request will be cancelled if the consumer is destroyed.
@@ -489,7 +491,8 @@
   // TODO(bdea): Add CompromiseType as a filter.
   virtual void RemoveCompromisedCredentialsImpl(
       const GURL& url,
-      const base::string16& username) = 0;
+      const base::string16& username,
+      RemoveCompromisedCredentialsReason reason) = 0;
   virtual std::vector<CompromisedCredentials>
   GetAllCompromisedCredentialsImpl() = 0;
   virtual void RemoveCompromisedCredentialsByUrlAndTimeImpl(
diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc
index f6093bc..d605be1 100644
--- a/components/password_manager/core/browser/password_store_default.cc
+++ b/components/password_manager/core/browser/password_store_default.cc
@@ -231,10 +231,12 @@
 
 void PasswordStoreDefault::RemoveCompromisedCredentialsImpl(
     const GURL& url,
-    const base::string16& username) {
+    const base::string16& username,
+    RemoveCompromisedCredentialsReason reason) {
   DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
-  if (login_db_)
-    login_db_->compromised_credentials_table().RemoveRow(url, username);
+  if (login_db_) {
+    login_db_->compromised_credentials_table().RemoveRow(url, username, reason);
+  }
 }
 
 std::vector<CompromisedCredentials>
diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h
index 20f3e062..61157dc4 100644
--- a/components/password_manager/core/browser/password_store_default.h
+++ b/components/password_manager/core/browser/password_store_default.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "components/password_manager/core/browser/compromised_credentials_table.h"
 #include "components/password_manager/core/browser/login_database.h"
 #include "components/password_manager/core/browser/password_store.h"
 
@@ -76,7 +77,8 @@
       const CompromisedCredentials& compromised_credentials) override;
   void RemoveCompromisedCredentialsImpl(
       const GURL& url,
-      const base::string16& username) override;
+      const base::string16& username,
+      RemoveCompromisedCredentialsReason reason) override;
   std::vector<CompromisedCredentials> GetAllCompromisedCredentialsImpl()
       override;
   void RemoveCompromisedCredentialsByUrlAndTimeImpl(
diff --git a/components/password_manager/core/browser/password_store_unittest.cc b/components/password_manager/core/browser/password_store_unittest.cc
index 3245e9bc..a2dc541 100644
--- a/components/password_manager/core/browser/password_store_unittest.cc
+++ b/components/password_manager/core/browser/password_store_unittest.cc
@@ -1407,8 +1407,9 @@
   WaitForPasswordStore();
   testing::Mock::VerifyAndClearExpectations(&consumer);
 
-  store->RemoveCompromisedCredentials(compromised_credentials.url,
-                                      compromised_credentials.username);
+  store->RemoveCompromisedCredentials(
+      compromised_credentials.url, compromised_credentials.username,
+      RemoveCompromisedCredentialsReason::kRemove);
   EXPECT_CALL(consumer, OnGetCompromisedCredentials(
                             UnorderedElementsAre(compromised_credentials2)));
   store->GetAllCompromisedCredentials(&consumer);
diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc
index effa4e9..d1e11da2 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -232,7 +232,8 @@
 
 void TestPasswordStore::RemoveCompromisedCredentialsImpl(
     const GURL& url,
-    const base::string16& username) {
+    const base::string16& username,
+    RemoveCompromisedCredentialsReason reason) {
   NOTIMPLEMENTED();
 }
 
diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h
index 8eec4589a..238aef31 100644
--- a/components/password_manager/core/browser/test_password_store.h
+++ b/components/password_manager/core/browser/test_password_store.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequenced_task_runner.h"
+#include "components/password_manager/core/browser/compromised_credentials_table.h"
 #include "components/password_manager/core/browser/password_store.h"
 
 namespace password_manager {
@@ -89,7 +90,8 @@
       const CompromisedCredentials& compromised_credentials) override;
   void RemoveCompromisedCredentialsImpl(
       const GURL& url,
-      const base::string16& username) override;
+      const base::string16& username,
+      RemoveCompromisedCredentialsReason reason) override;
   std::vector<CompromisedCredentials> GetAllCompromisedCredentialsImpl()
       override;
   void RemoveCompromisedCredentialsByUrlAndTimeImpl(
diff --git a/components/sync/driver/resources/traffic_log.html b/components/sync/driver/resources/traffic_log.html
index b97fa8cd..a137329c 100644
--- a/components/sync/driver/resources/traffic_log.html
+++ b/components/sync/driver/resources/traffic_log.html
@@ -1,7 +1,7 @@
 <div id="traffic-event-fullscreen-container">
   <div class="traffic-event-entry-fullscreen"
-        jsselect="events"
-        jseval="chrome.sync.traffic_log_tab.addExpandListener(this)">
+      jsselect="events"
+      jseval="chrome.sync.TrafficLogTag.getInstance().addExpandListener(this)">
     <span class="time" jscontent="(new Date(time)).toLocaleString()"></span>
     <span class="type" jscontent="type"></span>
     <pre class="details" jscontent="details"></pre>
diff --git a/components/sync/driver/resources/traffic_log.js b/components/sync/driver/resources/traffic_log.js
index 6deca5e..4a9818f3 100644
--- a/components/sync/driver/resources/traffic_log.js
+++ b/components/sync/driver/resources/traffic_log.js
@@ -2,99 +2,102 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.exportPath(
-    'chrome.sync.traffic_log_tab', new (class {
-      constructor() {
-        this.protocolEvents = [];
-        this.knownEventTimestamps = new Set();
+cr.define('chrome.sync', function() {
+  class TrafficLogTag {
+    constructor() {
+      this.protocolEvents = [];
+      this.knownEventTimestamps = new Set();
 
-        /** @type {!HTMLElement} */
-        this.container;
+      /** @type {!HTMLElement} */
+      this.container;
+    }
+
+    /**
+     * Helper to determine if the window is scrolled to its bottom limit.
+     * @return {boolean} true if the container is scrolled to the bottom
+     * @private
+     */
+    _isScrolledToBottom() {
+      return (window.innerHeight + window.scrollY) >=
+          document.body.offsetHeight;
+    }
+
+    /**
+     * Helper to scroll the window to its bottom.
+     * @private
+     */
+    _scrollToBottom() {
+      window.scrollTo(0, document.body.scrollHeight);
+    }
+
+    /**
+     * Callback for incoming protocol events.
+     * @param {Event} e The protocol event.
+     * @private
+     */
+    _onReceivedProtocolEvent(e) {
+      const details = e.details;
+
+      if (this.knownEventTimestamps.has(details.time)) {
+        return;
       }
 
-      /**
-       * Helper to determine if the window is scrolled to its bottom limit.
-       * @return {boolean} true if the container is scrolled to the bottom
-       * @private
-       */
-      _isScrolledToBottom() {
-        return (window.innerHeight + window.scrollY) >=
-            document.body.offsetHeight;
+      this.knownEventTimestamps.add(details.time);
+      this.protocolEvents.push(details);
+
+      const shouldScrollDown = this._isScrolledToBottom();
+
+      jstProcess(
+          new JsEvalContext({events: this.protocolEvents}), this.container);
+
+      if (shouldScrollDown) {
+        this._scrollToBottom();
       }
+    }
 
-      /**
-       * Helper to scroll the window to its bottom.
-       * @private
-       */
-      _scrollToBottom() {
-        window.scrollTo(0, document.body.scrollHeight);
+    /**
+     * Toggles the given traffic event entry div's "expanded" state.
+     * @param {!Event} e the click event that triggered the toggle.
+     * @private
+     */
+    _expandListener(e) {
+      if (e.target.classList.contains('proto')) {
+        // We ignore proto clicks to keep it copyable.
+        return;
       }
-
-      /**
-       * Callback for incoming protocol events.
-       * @param {Event} e The protocol event.
-       * @private
-       */
-      _onReceivedProtocolEvent(e) {
-        const details = e.details;
-
-        if (this.knownEventTimestamps.has(details.time)) {
-          return;
-        }
-
-        this.knownEventTimestamps.add(details.time);
-        this.protocolEvents.push(details);
-
-        const shouldScrollDown = this._isScrolledToBottom();
-
-        jstProcess(
-            new JsEvalContext({events: this.protocolEvents}), this.container);
-
-        if (shouldScrollDown) {
-          this._scrollToBottom();
-        }
+      let trafficEventDiv = e.target;
+      // Click might be on div's child.
+      if (trafficEventDiv.nodeName != 'DIV') {
+        trafficEventDiv = trafficEventDiv.parentNode;
       }
+      trafficEventDiv.classList.toggle(
+          'traffic-event-entry-expanded-fullscreen');
+    }
 
-      /**
-       * Toggles the given traffic event entry div's "expanded" state.
-       * @param {!Event} e the click event that triggered the toggle.
-       * @private
-       */
-      _expandListener(e) {
-        if (e.target.classList.contains('proto')) {
-          // We ignore proto clicks to keep it copyable.
-          return;
-        }
-        let trafficEventDiv = e.target;
-        // Click might be on div's child.
-        if (trafficEventDiv.nodeName != 'DIV') {
-          trafficEventDiv = trafficEventDiv.parentNode;
-        }
-        trafficEventDiv.classList.toggle(
-            'traffic-event-entry-expanded-fullscreen');
-      }
+    /**
+     * Attaches a listener to the given traffic event entry div.
+     * @param {HTMLElement} element
+     */
+    addExpandListener(element) {
+      element.addEventListener('click', this._expandListener, false);
+    }
 
-      /**
-       * Attaches a listener to the given traffic event entry div.
-       * @param {HTMLElement} element
-       */
-      addExpandListener(element) {
-        element.addEventListener('click', this._expandListener, false);
-      }
+    onLoad() {
+      this.container = getRequiredElement('traffic-event-fullscreen-container');
 
-      onLoad() {
-        this.container =
-            getRequiredElement('traffic-event-fullscreen-container');
+      chrome.sync.events.addEventListener(
+          'onProtocolEvent', this._onReceivedProtocolEvent.bind(this));
 
-        chrome.sync.events.addEventListener(
-            'onProtocolEvent', this._onReceivedProtocolEvent.bind(this));
+      // Make the prototype jscontent element disappear.
+      jstProcess(new JsEvalContext({}), this.container);
+    }
+  }
 
-        // Make the prototype jscontent element disappear.
-        jstProcess(new JsEvalContext({}), this.container);
-      }
-    }));
+  cr.addSingletonGetter(TrafficLogTag);
 
-document.addEventListener(
-  'DOMContentLoaded',
-  chrome.sync.traffic_log_tab.onLoad.bind(chrome.sync.traffic_log_tab),
-  false);
+  return {TrafficLogTag};
+});
+
+document.addEventListener('DOMContentLoaded', function() {
+  chrome.sync.TrafficLogTag.getInstance().onLoad();
+});
diff --git a/components/vector_icons/README.md b/components/vector_icons/README.md
index bf51d91..88b17ce 100644
--- a/components/vector_icons/README.md
+++ b/components/vector_icons/README.md
@@ -28,6 +28,8 @@
 
 Once you have created an `.icon` file, place it in an appropriate `vector_icon` subdirectory and add the filename to the corresponding `BUILD.gn`. A constant is automatically generated so that the icon can be referenced at runtime. The icon file `foo_bar.icon` is mapped to the constant name of `kFooBarIcon` ('k' + camel-cased filename + 'Icon'), which you can use to reference that icon in code. The icon's name should match its identifier on [the MD icons site](https://material.io/icons/) if that's where it came from. For example, `ic_accessibility` would become `accessibility.icon`.
 
+Make sure not to add [trademarked resources](../../docs/google_chrome_branded_builds.md) such as Google product logos to the Chromium repo.
+
 ### Icons with multiple definitions
 
 To add multiple icon definitions to a single `.icon` file, place the definitions generated by Skiafy in descending order of size. Each definition after the first must start with a `CANVAS_DIMENSIONS` directive.
diff --git a/components/viz/common/frame_sinks/begin_frame_source.cc b/components/viz/common/frame_sinks/begin_frame_source.cc
index 9dace7e2..1b836b28 100644
--- a/components/viz/common/frame_sinks/begin_frame_source.cc
+++ b/components/viz/common/frame_sinks/begin_frame_source.cc
@@ -386,11 +386,11 @@
   DCHECK(obs);
   DCHECK(!base::Contains(observers_, obs));
 
-  bool observers_was_empty = observers_.empty();
+  if (observers_.empty())
+    client_->OnNeedsBeginFrames(true);
+
   observers_.insert(obs);
   obs->OnBeginFrameSourcePausedChanged(paused_);
-  if (observers_was_empty)
-    client_->OnNeedsBeginFrames(true);
 
   // Send a MISSED begin frame if necessary.
   BeginFrameArgs missed_args = GetMissedBeginFrameArgs(obs);
diff --git a/components/viz/common/resources/single_release_callback.cc b/components/viz/common/resources/single_release_callback.cc
index 68296b1..1c69ffb 100644
--- a/components/viz/common/resources/single_release_callback.cc
+++ b/components/viz/common/resources/single_release_callback.cc
@@ -15,9 +15,7 @@
       << "Use a NULL SingleReleaseCallback for an empty callback.";
 }
 
-SingleReleaseCallback::~SingleReleaseCallback() {
-  DCHECK(callback_.is_null()) << "SingleReleaseCallback was never run.";
-}
+SingleReleaseCallback::~SingleReleaseCallback() = default;
 
 void SingleReleaseCallback::Run(const gpu::SyncToken& sync_token,
                                 bool is_lost) {
diff --git a/content/browser/renderer_host/input/synthetic_mouse_driver.cc b/content/browser/renderer_host/input/synthetic_mouse_driver.cc
index 0e3e73c..e0f038e2 100644
--- a/content/browser/renderer_host/input/synthetic_mouse_driver.cc
+++ b/content/browser/renderer_host/input/synthetic_mouse_driver.cc
@@ -78,7 +78,11 @@
       mouse_event_.pointer_type);
   mouse_event_.button =
       SyntheticPointerActionParams::GetWebMouseEventButton(button);
-  mouse_event_.click_count = click_count_;
+
+  // Set click count to 1 to allow pointer release without pointer down. This
+  // prevents MouseEvent::SetClickCount from throwing DCHECK error
+  click_count_ == 0 ? mouse_event_.click_count = 1
+                    : mouse_event_.click_count = click_count_;
   last_modifiers_ =
       last_modifiers_ &
       (~SyntheticPointerActionParams::GetWebMouseEventModifier(button));
@@ -113,7 +117,7 @@
       SyntheticPointerActionParams::PointerActionType::RELEASE) {
     int modifiers =
         SyntheticPointerActionParams::GetWebMouseEventModifier(params.button());
-    if (!(last_modifiers_ & modifiers))
+    if (!modifiers)
       return false;
   }
 
diff --git a/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc b/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
index 4717668..25f423d6 100644
--- a/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
+++ b/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
@@ -929,21 +929,25 @@
       param3, 1, buttons, SyntheticPointerActionParams::Button::LEFT));
 }
 
-TEST_F(SyntheticPointerActionTest, PointerMouseActionTypeInvalid) {
+TEST_F(SyntheticPointerActionTest, PointerMouseRelease) {
   CreateSyntheticPointerActionTarget<MockSyntheticPointerMouseActionTarget>();
 
-  // Cannot send a mouse up without sending a mouse down first.
+  // Verify a mouse up sends without a prior mouse down
   SyntheticPointerActionParams param = SyntheticPointerActionParams(
       SyntheticPointerActionParams::PointerActionType::RELEASE);
   params_.PushPointerActionParams(param);
   pointer_action_.reset(new SyntheticPointerAction(params_));
 
   ForwardSyntheticPointerAction();
-  EXPECT_EQ(0, num_success_);
-  EXPECT_EQ(1, num_failure_);
+  EXPECT_EQ(1, num_success_);
+  EXPECT_EQ(0, num_failure_);
+}
+
+TEST_F(SyntheticPointerActionTest, PointerMouseActionTypeInvalid) {
+  CreateSyntheticPointerActionTarget<MockSyntheticPointerMouseActionTarget>();
 
   // Send a mouse down for one finger.
-  param.set_pointer_action_type(
+  SyntheticPointerActionParams param = SyntheticPointerActionParams(
       SyntheticPointerActionParams::PointerActionType::PRESS);
   param.set_position(gfx::PointF(54, 89));
   params_ = SyntheticPointerActionListParams();
@@ -957,7 +961,7 @@
   MockSyntheticPointerMouseActionTarget* pointer_mouse_target =
       static_cast<MockSyntheticPointerMouseActionTarget*>(target_.get());
   EXPECT_EQ(1, num_success_);
-  EXPECT_EQ(1, num_failure_);
+  EXPECT_EQ(0, num_failure_);
   std::vector<SyntheticPointerActionParams::Button> buttons(
       1, SyntheticPointerActionParams::Button::LEFT);
   EXPECT_TRUE(pointer_mouse_target->SyntheticMouseActionDispatchedCorrectly(
@@ -965,7 +969,7 @@
 
   ForwardSyntheticPointerAction();
   EXPECT_EQ(1, num_success_);
-  EXPECT_EQ(2, num_failure_);
+  EXPECT_EQ(1, num_failure_);
 }
 
 TEST_F(SyntheticPointerActionTest, PointerPenAction) {
diff --git a/docs/origin_trials_integration.md b/docs/origin_trials_integration.md
index 1c4e758..396990e 100644
--- a/docs/origin_trials_integration.md
+++ b/docs/origin_trials_integration.md
@@ -52,6 +52,20 @@
 },
 ```
 
+### CSS Properties
+
+You can also run experiment for new CSS properties with origin trial. After you
+have configured your feature in [runtime\_enabled\_features.json5] as above, head
+to [css\_properties.json5]. As explained in the file, you use `runtime_flag` to associate
+the CSS property with the feature you just defined. This will automatically link the CSS
+property to the origin trial defined in the runtime feature. It will be available
+in both JavaScript (`Element.style`) and CSS (including `@supports`) when the trial
+is enabled.
+
+
+**Example:** [origin-trial-test-property] defines a test css property controlled via
+runtime feature `OriginTrialsSampleAPI` and subsequently an origin trial named `Frobulate`.
+
 ### Gating Access
 
 Once configured, there are two mechanisms to gate access to your feature behind
@@ -74,6 +88,14 @@
 `RuntimeEnabledFeatures::MyFeatureEnabled(ExecutionContext*)` as often as
 necessary to gate access to your feature.
 
+**NOTE:** For CSS properties, you do not need to edit the IDL files, as the exposure
+on the [CSSStyleDeclaration] is handled at runtime.
+
+**ISSUE:** In the rare cases where the origin trial token is added via script after
+the css style declaration, the css property will be enabled and is fully functional,
+however it will not appear on the [CSSStyleDeclaration] interface, i.e. not accessible
+in `Element.style`. This issue is tracked in crbug/1041993.
+
 ### Web Feature Counting
 
 Once the feature is created, in order to run the origin trial you need to track
@@ -200,3 +222,7 @@
 [web\_feature.mojom]: /third_party/blink/public/mojom/web_feature/web_feature.mojom
 [update\_use\_counter\_feature\_enum.py]: /tools/metrics/histograms/update_use_counter_feature_enum.py
 [Measure]: /third_party/blink/renderer/bindings/IDLExtendedAttributes.md#Measure_i_m_a_c
+[css\_properties.json5]: /third_party/blink/renderer/core/css/css_properties.json5
+[origin-trial-test-property]: https://chromium.googlesource.com/chromium/src/+/ff2ab8b89745602c8300322c2a0158e210178c7e/third_party/blink/renderer/core/css/css_properties.json5#2635
+[CSSStyleDeclaration]: /third_party/blink/renderer/core/css/css_style_declaration.idl
+
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 776f6101..8fb06c7d 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -496,6 +496,7 @@
     "//services/device/public/cpp/hid",
     "//services/device/public/cpp/hid:test_support",
     "//services/device/public/mojom",
+    "//services/network:test_support",
     "//services/service_manager/public/cpp",
     "//ui/display:test_support",
   ]
diff --git a/extensions/browser/api/dns/dns_api.cc b/extensions/browser/api/dns/dns_api.cc
index 69905bc6..f3f5a0d 100644
--- a/extensions/browser/api/dns/dns_api.cc
+++ b/extensions/browser/api/dns/dns_api.cc
@@ -14,6 +14,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/network_isolation_key.h"
 #include "net/dns/public/resolve_error_info.h"
+#include "url/origin.h"
 
 using content::BrowserThread;
 using extensions::api::dns::ResolveCallbackResolveInfo;
@@ -36,11 +37,11 @@
   // hostname you'd like to resolve, even though it doesn't use that value in
   // determining its answer.
   net::HostPortPair host_port_pair(params->hostname, 0);
-  // TODO(https://crbug.com/997049): Pass in a non-empty NetworkIsolationKey.
+  url::Origin origin = url::Origin::Create(extension_->url());
   content::BrowserContext::GetDefaultStoragePartition(browser_context())
       ->GetNetworkContext()
-      ->ResolveHost(host_port_pair, net::NetworkIsolationKey::Todo(), nullptr,
-                    receiver_.BindNewPipeAndPassRemote());
+      ->ResolveHost(host_port_pair, net::NetworkIsolationKey(origin, origin),
+                    nullptr, receiver_.BindNewPipeAndPassRemote());
   receiver_.set_disconnect_handler(
       base::BindOnce(&DnsResolveFunction::OnComplete, base::Unretained(this),
                      net::ERR_NAME_NOT_RESOLVED,
diff --git a/extensions/browser/api/dns/dns_apitest.cc b/extensions/browser/api/dns/dns_apitest.cc
index 2dbeea61..4e88dd6 100644
--- a/extensions/browser/api/dns/dns_apitest.cc
+++ b/extensions/browser/api/dns/dns_apitest.cc
@@ -5,8 +5,11 @@
 #include <memory>
 
 #include "base/memory/ref_counted.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/api/dns/dns_api.h"
 #include "extensions/browser/api_test_utils.h"
@@ -14,24 +17,40 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/shell/test/shell_apitest.h"
+#include "net/base/features.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
 #include "net/dns/mock_host_resolver.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/test/test_dns_util.h"
+#include "url/origin.h"
 
 namespace extensions {
 namespace {
 using extensions::api_test_utils::RunFunctionAndReturnSingleResult;
 
-constexpr char kHostname[] = "www.sowbug.com";
+constexpr char kHostname[] = "www.sowbug.test";
 constexpr char kAddress[] = "9.8.7.6";
 }  // namespace
 
 class DnsApiTest : public ShellApiTest {
+ public:
+  DnsApiTest() {
+    // Enable kSplitHostCacheByNetworkIsolationKey so the test can verify that
+    // the correct NetworkIsolationKey was used for the DNS lookup.
+    scoped_feature_list_.InitAndEnableFeature(
+        net::features::kSplitHostCacheByNetworkIsolationKey);
+  }
+
  private:
   void SetUpOnMainThread() override {
     ShellApiTest::SetUpOnMainThread();
     host_resolver()->AddRule(kHostname, kAddress);
-    host_resolver()->AddSimulatedFailure("this.hostname.is.bogus");
+    host_resolver()->AddSimulatedFailure("this.hostname.is.bogus.test");
   }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DnsApiTest, DnsResolveIPLiteral) {
@@ -79,6 +98,37 @@
   std::string address;
   EXPECT_TRUE(dict->GetString("address", &address));
   EXPECT_EQ(kAddress, address);
+
+  // Make sure the extension's NetworkIsolationKey was used. Do a cache only DNS
+  // lookup using the expected NIK, and make sure the IP address is retrieved.
+  network::mojom::NetworkContext* network_context =
+      content::BrowserContext::GetDefaultStoragePartition(browser_context())
+          ->GetNetworkContext();
+  net::HostPortPair host_port_pair(kHostname, 0);
+  network::mojom::ResolveHostParametersPtr params =
+      network::mojom::ResolveHostParameters::New();
+  // Cache only lookup.
+  params->source = net::HostResolverSource::LOCAL_ONLY;
+  url::Origin origin = url::Origin::Create(empty_extension->url());
+  net::NetworkIsolationKey network_isolation_key(origin, origin);
+  network::DnsLookupResult result1 =
+      network::BlockingDnsLookup(network_context, host_port_pair,
+                                 std::move(params), network_isolation_key);
+  EXPECT_EQ(net::OK, result1.error);
+  ASSERT_TRUE(result1.resolved_addresses.has_value());
+  ASSERT_EQ(1u, result1.resolved_addresses->size());
+  EXPECT_EQ(kAddress,
+            result1.resolved_addresses.value()[0].ToStringWithoutPort());
+
+  // Check that the entry isn't present in the cache with the empty
+  // NetworkIsolationKey.
+  params = network::mojom::ResolveHostParameters::New();
+  // Cache only lookup.
+  params->source = net::HostResolverSource::LOCAL_ONLY;
+  network::DnsLookupResult result2 =
+      network::BlockingDnsLookup(network_context, host_port_pair,
+                                 std::move(params), net::NetworkIsolationKey());
+  EXPECT_EQ(net::ERR_DNS_CACHE_MISS, result2.error);
 }
 
 IN_PROC_BROWSER_TEST_F(DnsApiTest, DnsExtension) {
diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc
index de2446f..a0df190 100644
--- a/extensions/browser/api/socket/socket_api.cc
+++ b/extensions/browser/api/socket/socket_api.cc
@@ -200,9 +200,10 @@
   DCHECK(pending_host_resolver_);
   DCHECK(!receiver_.is_bound());
   host_resolver_.Bind(std::move(pending_host_resolver_));
-  // TODO(https://crbug.com/997049): Pass in a non-empty NetworkIsolationKey.
-  host_resolver_->ResolveHost(host_port_pair, net::NetworkIsolationKey::Todo(),
-                              nullptr, receiver_.BindNewPipeAndPassRemote());
+  url::Origin origin = url::Origin::Create(extension_->url());
+  host_resolver_->ResolveHost(host_port_pair,
+                              net::NetworkIsolationKey(origin, origin), nullptr,
+                              receiver_.BindNewPipeAndPassRemote());
   receiver_.set_disconnect_handler(
       base::BindOnce(&SocketExtensionWithDnsLookupFunction::OnComplete,
                      base::Unretained(this), net::ERR_NAME_NOT_RESOLVED,
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
index 0c71e30..2f8fa8f 100644
--- a/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
@@ -7,7 +7,10 @@
 #include "base/command_line.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/network_service_test_helper.h"
@@ -21,16 +24,25 @@
 #include "extensions/test/result_catcher.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
+#include "net/base/features.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/network_isolation_key.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
+#include "services/network/test/test_dns_util.h"
 
 namespace extensions {
 
-const char kHostname[] = "www.foo.com";
+const char kHostname[] = "www.foo.test";
 
 class SocketsTcpApiTest : public ShellApiTest {
  public:
   SocketsTcpApiTest() {
+    // Enable kSplitHostCacheByNetworkIsolationKey so the test can verify that
+    // the correct NetworkIsolationKey was used for the DNS lookup.
+    scoped_feature_list_.InitAndEnableFeature(
+        net::features::kSplitHostCacheByNetworkIsolationKey);
+
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kUseMockCertVerifierForTesting);
   }
@@ -39,6 +51,8 @@
     ShellApiTest::SetUpOnMainThread();
     host_resolver()->AddRule(kHostname, "127.0.0.1");
   }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketsTcpCreateGood) {
@@ -81,12 +95,44 @@
 
   ExtensionTestMessageListener listener("info_please", true);
 
-  ASSERT_TRUE(LoadApp("sockets_tcp/api"));
+  scoped_refptr<const Extension> test_extension = LoadApp("sockets_tcp/api");
+  ASSERT_TRUE(test_extension);
+
   EXPECT_TRUE(listener.WaitUntilSatisfied());
   listener.Reply(
       base::StringPrintf("tcp:%s:%d", host_port_pair.host().c_str(), port));
 
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+
+  // Make sure the extension's NetworkIsolationKey was used. Do a cache only DNS
+  // lookup using the expected NIK, and make sure the IP address is retrieved.
+  network::mojom::NetworkContext* network_context =
+      content::BrowserContext::GetDefaultStoragePartition(browser_context())
+          ->GetNetworkContext();
+  network::mojom::ResolveHostParametersPtr params =
+      network::mojom::ResolveHostParameters::New();
+  // Cache only lookup.
+  params->source = net::HostResolverSource::LOCAL_ONLY;
+  url::Origin origin = url::Origin::Create(test_extension->url());
+  net::NetworkIsolationKey network_isolation_key(origin, origin);
+  network::DnsLookupResult result1 =
+      network::BlockingDnsLookup(network_context, host_port_pair,
+                                 std::move(params), network_isolation_key);
+  EXPECT_EQ(net::OK, result1.error);
+  ASSERT_TRUE(result1.resolved_addresses.has_value());
+  ASSERT_EQ(1u, result1.resolved_addresses->size());
+  EXPECT_EQ("127.0.0.1",
+            result1.resolved_addresses.value()[0].ToStringWithoutPort());
+
+  // Check that the entry isn't present in the cache with the empty
+  // NetworkIsolationKey.
+  params = network::mojom::ResolveHostParameters::New();
+  // Cache only lookup.
+  params->source = net::HostResolverSource::LOCAL_ONLY;
+  network::DnsLookupResult result2 =
+      network::BlockingDnsLookup(network_context, host_port_pair,
+                                 std::move(params), net::NetworkIsolationKey());
+  EXPECT_EQ(net::ERR_DNS_CACHE_MISS, result2.error);
 }
 
 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketTcpExtensionTLS) {
diff --git a/extensions/test/data/api_test/dns/api/background.js b/extensions/test/data/api_test/dns/api/background.js
index 6614579..6ef6bf18 100644
--- a/extensions/test/data/api_test/dns/api/background.js
+++ b/extensions/test/data/api_test/dns/api/background.js
@@ -17,7 +17,7 @@
     chrome.test.assertEq("9.8.7.6", resolveInfo.address);
     chrome.test.succeed("hostname resolved");
   };
-  chrome.dns.resolve("www.sowbug.com", callback);
+  chrome.dns.resolve("www.sowbug.test", callback);
 };
 
 var testNonexistentHostnameResolution = function() {
@@ -26,7 +26,7 @@
     chrome.test.assertEq(-105, resolveInfo.resultCode);
     chrome.test.succeed("hostname correctly failed to resolve");
   };
-  chrome.dns.resolve("this.hostname.is.bogus", callback);
+  chrome.dns.resolve("this.hostname.is.bogus.test", callback);
 };
 
 chrome.test.runTests([testIPLiteralResolution,
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
index 30e3458..a056dcc7 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
@@ -5,6 +5,7 @@
 #include "gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.h"
 
 #include "base/android/android_hardware_buffer_compat.h"
+#include "base/android/scoped_hardware_buffer_fence_sync.h"
 #include "base/bind_helpers.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
@@ -638,7 +639,8 @@
       overlay_representation->BeginScopedReadAccess(true /* needs_gl_image */);
   EXPECT_TRUE(scoped_read_access);
   EXPECT_TRUE(scoped_read_access->gl_image());
-
+  auto buffer = scoped_read_access->gl_image()->GetAHardwareBuffer();
+  DCHECK(buffer);
   scoped_read_access.reset();
   skia_representation.reset();
 }
diff --git a/infra/config/buckets/ci.star b/infra/config/buckets/ci.star
index 68d762934..6283ffe6 100644
--- a/infra/config/buckets/ci.star
+++ b/infra/config/buckets/ci.star
@@ -329,6 +329,10 @@
     name = 'android-pie-arm64-rel',
 )
 
+android_builder(
+    name = 'android-10-arm64-rel',
+)
+
 
 def android_fyi_builder(*, name, **kwargs):
   return builder(
diff --git a/infra/config/buckets/try.star b/infra/config/buckets/try.star
index 2b42eca..0a4d72f 100644
--- a/infra/config/buckets/try.star
+++ b/infra/config/buckets/try.star
@@ -187,6 +187,10 @@
 )
 
 android_builder(
+    name = 'android-10-arm64-rel',
+)
+
+android_builder(
     name = 'android-webview-marshmallow-arm64-dbg',
 )
 
diff --git a/infra/config/consoles/chromium.android.star b/infra/config/consoles/chromium.android.star
index ddf15887..2b78cc6 100644
--- a/infra/config/consoles/chromium.android.star
+++ b/infra/config/consoles/chromium.android.star
@@ -167,6 +167,11 @@
             short_name = 'M proguard',
         ),
         luci.console_view_entry(
+            builder = 'ci/android-10-arm64-rel',
+            category = 'builder_tester',
+            short_name = '10',
+        ),
+        luci.console_view_entry(
             builder = 'ci/android-kitkat-arm-rel',
             category = 'on_cq',
             short_name = 'K',
diff --git a/infra/config/consoles/luci.chromium.try.star b/infra/config/consoles/luci.chromium.try.star
index 246f6d9..e0f29d1b 100644
--- a/infra/config/consoles/luci.chromium.try.star
+++ b/infra/config/consoles/luci.chromium.try.star
@@ -7,6 +7,7 @@
         'try/android-oreo-arm64-cts-networkservice-dbg',
         'try/android-pie-arm64-coverage-rel',
         'try/android-pie-arm64-rel',
+        'try/android-10-arm64-rel',
         'try/android-webview-pie-arm64-fyi-rel',
         'try/android_archive_rel_ng',
         'try/android_arm64_dbg_recipe',
diff --git a/infra/config/consoles/tryserver.chromium.android.star b/infra/config/consoles/tryserver.chromium.android.star
index be76275..b52573d 100644
--- a/infra/config/consoles/tryserver.chromium.android.star
+++ b/infra/config/consoles/tryserver.chromium.android.star
@@ -19,6 +19,7 @@
         'try/android-marshmallow-arm64-rel',
         'try/android-marshmallow-x86-fyi-rel',
         'try/android-pie-arm64-rel',
+        'try/android-10-arm64-rel',
         'try/android-webview-pie-arm64-fyi-rel',
         'try/android_optional_gpu_tests_rel',
         'try/android_unswarmed_pixel_aosp',
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index f71b61f..dbde272 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -45,6 +45,10 @@
         includable_only: true
       >
       builders: <
+        name: "chromium/try/android-10-arm64-rel"
+        includable_only: true
+      >
+      builders: <
         name: "chromium/try/android-asan"
         includable_only: true
       >
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index b741967..d450e79 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -5740,6 +5740,27 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
     >
     builders: <
+      name: "android-10-arm64-rel"
+      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: "ssd:0"
+      recipe: <
+        name: "chromium"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"chromium.android\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
       name: "android-archive-dbg"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -11239,6 +11260,35 @@
   >
   swarming: <
     builders: <
+      name: "android-10-arm64-rel"
+      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: "ssd:0"
+      recipe: <
+        name: "chromium_trybot"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"tryserver.chromium.android\""
+      >
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      caches: <
+        name: "win_toolchain"
+        path: "win_toolchain"
+      >
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage: <
+        value: 5
+      >
+    >
+    builders: <
       name: "android-asan"
       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 5a0b127..08ef07d 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -791,6 +791,11 @@
     short_name: "M proguard"
   >
   builders: <
+    name: "buildbucket/luci.chromium.ci/android-10-arm64-rel"
+    category: "builder_tester"
+    short_name: "10"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/android-kitkat-arm-rel"
     category: "on_cq"
     short_name: "K"
@@ -9816,6 +9821,9 @@
     name: "buildbucket/luci.chromium.try/android-pie-arm64-rel"
   >
   builders: <
+    name: "buildbucket/luci.chromium.try/android-10-arm64-rel"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.try/android-webview-pie-arm64-fyi-rel"
   >
   builders: <
@@ -12227,6 +12235,9 @@
     name: "buildbucket/luci.chromium.try/android-pie-arm64-rel"
   >
   builders: <
+    name: "buildbucket/luci.chromium.try/android-10-arm64-rel"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.try/android-webview-pie-arm64-fyi-rel"
   >
   builders: <
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index c8a7b8e..278686de6 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -322,6 +322,7 @@
   triggers: "android-incremental-dbg"
   triggers: "android-mojo-webview-rel"
   triggers: "android-pie-arm64-rel"
+  triggers: "android-10-arm64-rel"
   triggers: "android-archive-rel"
   triggers: "chromeos-amd64-generic-asan-rel"
   triggers: "chromeos-amd64-generic-cfi-thin-lto-rel"
@@ -1064,6 +1065,16 @@
 }
 
 job {
+  id: "android-10-arm64-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-10-arm64-rel"
+  }
+}
+
+job {
   id: "android-sdk-packager"
   acl_sets: "default"
   # Run weekly, on Sunday morning at midnight.
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index c8a7b8e..278686de6 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -322,6 +322,7 @@
   triggers: "android-incremental-dbg"
   triggers: "android-mojo-webview-rel"
   triggers: "android-pie-arm64-rel"
+  triggers: "android-10-arm64-rel"
   triggers: "android-archive-rel"
   triggers: "chromeos-amd64-generic-asan-rel"
   triggers: "chromeos-amd64-generic-cfi-thin-lto-rel"
@@ -1064,6 +1065,16 @@
 }
 
 job {
+  id: "android-10-arm64-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-10-arm64-rel"
+  }
+}
+
+job {
   id: "android-sdk-packager"
   acl_sets: "default"
   # Run weekly, on Sunday morning at midnight.
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 336bd75..f496829 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1372,6 +1372,9 @@
       <message name="IDS_IOS_RECENT_TABS_RECENTLY_CLOSED" desc="The heading of the recently closed section in the recent tab panel. [Length: 35em] [iOS only]">
         Recently Closed
       </message>
+      <message name="IDS_IOS_RESET_ZOOM" desc="Label for a button to reset the text zoom level to the default state." meaning="Reset text zoom level to the default state">
+        Reset
+      </message>
       <message name="IDS_IOS_SAFE_MODE_AW_SNAP" desc="The title of the safe mode UI after multiple crashes.  This is intended to be a humorous exclamation of dismay. [Length: 30em]">
         Aw, Snap!
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_RESET_ZOOM.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_RESET_ZOOM.png.sha1
new file mode 100644
index 0000000..6afc775
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_RESET_ZOOM.png.sha1
@@ -0,0 +1 @@
+d57acd25ab64a3b34c895f678e56185387f868d8
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/text_zoom/BUILD.gn b/ios/chrome/browser/ui/text_zoom/BUILD.gn
index ac5f02e7..3b9c892 100644
--- a/ios/chrome/browser/ui/text_zoom/BUILD.gn
+++ b/ios/chrome/browser/ui/text_zoom/BUILD.gn
@@ -27,9 +27,22 @@
     "text_zoom_view_controller.mm",
   ]
   deps = [
+    ":constants",
+    "resources:text_zoom_zoom_in",
+    "resources:text_zoom_zoom_out",
     "//components/strings:components_strings",
+    "//ios/chrome/app/strings:ios_strings",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/common/colors",
     "//ui/base",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
+
+source_set("constants") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "text_zoom_constants.h",
+    "text_zoom_constants.mm",
+  ]
+}
diff --git a/ios/chrome/browser/ui/text_zoom/OWNERS b/ios/chrome/browser/ui/text_zoom/OWNERS
new file mode 100644
index 0000000..3fa1ed4
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/OWNERS
@@ -0,0 +1,4 @@
+rkgibson@google.com
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/text_zoom/resources/BUILD.gn b/ios/chrome/browser/ui/text_zoom/resources/BUILD.gn
new file mode 100644
index 0000000..941f33d0
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/resources/BUILD.gn
@@ -0,0 +1,21 @@
+# 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/ios/asset_catalog.gni")
+
+imageset("text_zoom_zoom_in") {
+  sources = [
+    "text_zoom_zoom_in.imageset/Contents.json",
+    "text_zoom_zoom_in.imageset/text_zoom_zoom_in@2x.png",
+    "text_zoom_zoom_in.imageset/text_zoom_zoom_in@3x.png",
+  ]
+}
+
+imageset("text_zoom_zoom_out") {
+  sources = [
+    "text_zoom_zoom_out.imageset/Contents.json",
+    "text_zoom_zoom_out.imageset/text_zoom_zoom_out@2x.png",
+    "text_zoom_zoom_out.imageset/text_zoom_zoom_out@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/Contents.json b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/Contents.json
new file mode 100644
index 0000000..05432cb
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "text_zoom_zoom_in@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "text_zoom_zoom_in@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/text_zoom_zoom_in@2x.png b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/text_zoom_zoom_in@2x.png
new file mode 100644
index 0000000..58e5ac2
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/text_zoom_zoom_in@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/text_zoom_zoom_in@3x.png b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/text_zoom_zoom_in@3x.png
new file mode 100644
index 0000000..af72e61
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_in.imageset/text_zoom_zoom_in@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/Contents.json b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/Contents.json
new file mode 100644
index 0000000..22f49014
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "text_zoom_zoom_out@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "text_zoom_zoom_out@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/text_zoom_zoom_out@2x.png b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/text_zoom_zoom_out@2x.png
new file mode 100644
index 0000000..158178c
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/text_zoom_zoom_out@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/text_zoom_zoom_out@3x.png b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/text_zoom_zoom_out@3x.png
new file mode 100644
index 0000000..8937e92
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/resources/text_zoom_zoom_out.imageset/text_zoom_zoom_out@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/text_zoom/text_zoom_constants.h b/ios/chrome/browser/ui/text_zoom/text_zoom_constants.h
new file mode 100644
index 0000000..7ce177d0
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/text_zoom_constants.h
@@ -0,0 +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.
+
+#ifndef IOS_CHROME_BROWSER_UI_TEXT_ZOOM_TEXT_ZOOM_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_TEXT_ZOOM_TEXT_ZOOM_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+
+// The a11y ID of the "close" button in the text zoom bar.
+extern NSString* const kTextZoomCloseButtonID;
+
+#endif  // IOS_CHROME_BROWSER_UI_TEXT_ZOOM_TEXT_ZOOM_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/text_zoom/text_zoom_constants.mm b/ios/chrome/browser/ui/text_zoom/text_zoom_constants.mm
new file mode 100644
index 0000000..d0040a0
--- /dev/null
+++ b/ios/chrome/browser/ui/text_zoom/text_zoom_constants.mm
@@ -0,0 +1,11 @@
+// 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 "ios/chrome/browser/ui/text_zoom/text_zoom_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NSString* const kTextZoomCloseButtonID = @"kTextZoomCloseButtonID";
diff --git a/ios/chrome/browser/ui/text_zoom/text_zoom_coordinator.mm b/ios/chrome/browser/ui/text_zoom/text_zoom_coordinator.mm
index 24e1006..3f89ecda3 100644
--- a/ios/chrome/browser/ui/text_zoom/text_zoom_coordinator.mm
+++ b/ios/chrome/browser/ui/text_zoom/text_zoom_coordinator.mm
@@ -33,7 +33,10 @@
 #pragma mark - ChromeCoordinator
 
 - (void)start {
-  self.textZoomViewController = [[TextZoomViewController alloc] init];
+  DCHECK(self.browser);
+  DCHECK(self.browserState);
+  self.textZoomViewController = [[TextZoomViewController alloc]
+      initWithDarkAppearance:self.browserState->IsOffTheRecord()];
   self.textZoomViewController.commandHandler =
       HandlerForProtocol(self.browser->GetCommandDispatcher(), BrowserCommands);
 
diff --git a/ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.h b/ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.h
index 191e79c..59b5d19 100644
--- a/ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.h
+++ b/ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.h
@@ -8,9 +8,18 @@
 #import <UIKit/UIKit.h>
 
 @protocol BrowserCommands;
+@class TextZoomViewController;
 
 @interface TextZoomViewController : UIViewController
 
+- (instancetype)initWithDarkAppearance:(BOOL)darkAppearance
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithNibName:(NSString*)nibNameOrNil
+                         bundle:(NSBundle*)nibBundleOrNil NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
+
 @property(nonatomic, weak) id<BrowserCommands> commandHandler;
 
 @end
diff --git a/ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.mm b/ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.mm
index d26ccc5..8c50317 100644
--- a/ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.mm
+++ b/ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.mm
@@ -4,8 +4,13 @@
 
 #import "ios/chrome/browser/ui/text_zoom/text_zoom_view_controller.h"
 
+#include "base/logging.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/browser/ui/text_zoom/text_zoom_constants.h"
+#import "ios/chrome/common/colors/dynamic_color_util.h"
+#import "ios/chrome/common/colors/semantic_color_names.h"
+#include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -16,16 +21,40 @@
 // Horizontal padding between all elements (except the previous/next buttons).
 const CGFloat kPadding = 8;
 const CGFloat kButtonFontSize = 17;
+const CGFloat kButtonSize = 44;
+// Spacing between the increment/decrement buttons and the central divider.
+const CGFloat kButtonDividerSpacing = 19;
+// Width of the divider.
+const CGFloat kDividerWidth = 1;
 }
 
 @interface TextZoomViewController ()
 
+@property(nonatomic, assign) BOOL darkAppearance;
+
 @property(nonatomic, strong) UIButton* closeButton;
+@property(nonatomic, strong) UIButton* resetButton;
+
+// Horizontal stack view to hold all the items that should be centered
+// (increment/decrement, divider).
+@property(nonatomic, strong) UIStackView* centerItemsStackView;
+@property(nonatomic, strong) UIView* divider;
+@property(nonatomic, strong) UIButton* incrementButton;
+@property(nonatomic, strong) UIButton* decrementButton;
+
+@property(nonatomic, assign) int stepperValue;
 
 @end
 
 @implementation TextZoomViewController
 
+- (instancetype)initWithDarkAppearance:(BOOL)darkAppearance {
+  if (self = [super initWithNibName:nil bundle:nil]) {
+    _darkAppearance = darkAppearance;
+  }
+  return self;
+}
+
 #pragma mark - UIViewController
 
 - (void)viewDidLoad {
@@ -33,17 +62,34 @@
 
   self.view.translatesAutoresizingMaskIntoConstraints = NO;
 
+  [self.view addSubview:self.resetButton];
+  [self.view addSubview:self.centerItemsStackView];
   [self.view addSubview:self.closeButton];
 
   [NSLayoutConstraint activateConstraints:@[
+    // Reset button.
+    [self.resetButton.centerYAnchor
+        constraintEqualToAnchor:self.view.centerYAnchor],
+    [self.resetButton.leadingAnchor
+        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor
+                       constant:kPadding],
+    // Use button intrinsic width.
+    [self.resetButton.heightAnchor constraintEqualToConstant:kButtonSize],
+    // Center items stack view.
+    [self.centerItemsStackView.centerYAnchor
+        constraintEqualToAnchor:self.view.centerYAnchor],
+    [self.centerItemsStackView.centerXAnchor
+        constraintEqualToAnchor:self.view.centerXAnchor],
+    [self.centerItemsStackView.heightAnchor
+        constraintEqualToAnchor:self.view.heightAnchor],
     // Close Button.
     [self.closeButton.centerYAnchor
         constraintEqualToAnchor:self.view.centerYAnchor],
-    //  [self.closeButton.heightAnchor constraintEqualToConstant:kButtonLength],
-    // Use button intrinsic width.
     [self.closeButton.trailingAnchor
         constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor
                        constant:-kPadding],
+    // Use button intrinsic width.
+    [self.closeButton.heightAnchor constraintEqualToConstant:kButtonSize],
   ]];
 
   [self.closeButton
@@ -57,17 +103,37 @@
   [self.commandHandler hideTextZoom];
 }
 
+- (void)resetButtonWasTapped:(id)sender {
+  self.stepperValue = 0;
+}
+
+- (void)incrementButtonWasTapped:(id)sender {
+  self.stepperValue += 1;
+}
+
+- (void)decrementButtonWasTapped:(id)sender {
+  self.stepperValue -= 1;
+}
+
+- (void)updateStepperState {
+  self.incrementButton.enabled = self.stepperValue < 5;
+  self.decrementButton.enabled = self.stepperValue > -5;
+}
+
+- (void)setStepperValue:(int)stepperValue {
+  _stepperValue = stepperValue;
+  [self updateStepperState];
+}
+
 #pragma mark - Private property Accessors
 
 // Creates and returns the close button.
 - (UIButton*)closeButton {
   if (!_closeButton) {
-    _closeButton = [UIButton buttonWithType:UIButtonTypeSystem];
+    _closeButton = [self newButtonWithDefaultStyling];
     [_closeButton setTitle:l10n_util::GetNSString(IDS_DONE)
                   forState:UIControlStateNormal];
-    _closeButton.translatesAutoresizingMaskIntoConstraints = NO;
-    //  _closeButton.accessibilityIdentifier = kFindInPageCloseButtonId;
-    _closeButton.titleLabel.font = [UIFont systemFontOfSize:kButtonFontSize];
+    _closeButton.accessibilityIdentifier = kTextZoomCloseButtonID;
     [_closeButton addTarget:self
                      action:@selector(closeButtonWasTapped:)
            forControlEvents:UIControlEventTouchUpInside];
@@ -75,4 +141,91 @@
   return _closeButton;
 }
 
+// Creates and returns the reset button.
+- (UIButton*)resetButton {
+  if (!_resetButton) {
+    _resetButton = [self newButtonWithDefaultStyling];
+    [_resetButton setTitle:l10n_util::GetNSString(IDS_IOS_RESET_ZOOM)
+                  forState:UIControlStateNormal];
+    [_resetButton addTarget:self
+                     action:@selector(resetButtonWasTapped:)
+           forControlEvents:UIControlEventTouchUpInside];
+  }
+  return _resetButton;
+}
+
+// Creates and returns the increment button.
+- (UIButton*)incrementButton {
+  if (!_incrementButton) {
+    _incrementButton = [self newButtonWithDefaultStyling];
+    UIImage* image = [UIImage imageNamed:@"text_zoom_zoom_in"];
+    [_incrementButton setImage:image forState:UIControlStateNormal];
+    [_incrementButton addTarget:self
+                         action:@selector(incrementButtonWasTapped:)
+               forControlEvents:UIControlEventTouchUpInside];
+    [NSLayoutConstraint activateConstraints:@[
+      [_incrementButton.heightAnchor constraintEqualToConstant:kButtonSize],
+      [_incrementButton.widthAnchor
+          constraintEqualToAnchor:_incrementButton.heightAnchor],
+    ]];
+  }
+  return _incrementButton;
+}
+
+// Creates and returns the decrement button.
+- (UIButton*)decrementButton {
+  if (!_decrementButton) {
+    _decrementButton = [self newButtonWithDefaultStyling];
+    UIImage* image = [UIImage imageNamed:@"text_zoom_zoom_out"];
+    [_decrementButton setImage:image forState:UIControlStateNormal];
+    [_decrementButton addTarget:self
+                         action:@selector(decrementButtonWasTapped:)
+               forControlEvents:UIControlEventTouchUpInside];
+    [NSLayoutConstraint activateConstraints:@[
+      [_decrementButton.heightAnchor constraintEqualToConstant:kButtonSize],
+      [_decrementButton.widthAnchor
+          constraintEqualToAnchor:_decrementButton.heightAnchor],
+    ]];
+  }
+  return _decrementButton;
+}
+
+// Creates and returns the divider.
+- (UIView*)divider {
+  if (!_divider) {
+    _divider = [[UIView alloc] init];
+    _divider.translatesAutoresizingMaskIntoConstraints = NO;
+    _divider.backgroundColor = [UIColor colorNamed:kSeparatorColor];
+    [NSLayoutConstraint activateConstraints:@[
+      [_divider.heightAnchor
+          constraintEqualToAnchor:self.centerItemsStackView.heightAnchor
+                         constant:-2 * kPadding],
+      [_divider.widthAnchor constraintEqualToConstant:kDividerWidth],
+    ]];
+  }
+  return _divider;
+}
+
+// Creates and returns the center items stack view.
+- (UIStackView*)centerItemsStackView {
+  if (!_centerItemsStackView) {
+    _centerItemsStackView = [[UIStackView alloc] initWithArrangedSubviews:@[
+      self.decrementButton, self.divider, self.incrementButton
+    ]];
+    _centerItemsStackView.translatesAutoresizingMaskIntoConstraints = NO;
+    _centerItemsStackView.spacing = kButtonDividerSpacing;
+    _centerItemsStackView.alignment = UIStackViewAlignmentCenter;
+  }
+  return _centerItemsStackView;
+}
+
+#pragma mark - Property accessor helpers
+
+- (UIButton*)newButtonWithDefaultStyling {
+  UIButton* button = [UIButton buttonWithType:UIButtonTypeSystem];
+  button.translatesAutoresizingMaskIntoConstraints = NO;
+  button.titleLabel.font = [UIFont systemFontOfSize:kButtonFontSize];
+  return button;
+}
+
 @end
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index b62217c..3e2caf17 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -100,9 +100,7 @@
     public_deps += [ "//third_party/protobuf:protobuf_lite" ]
   }
 
-  deps = [
-    "//base",
-  ]
+  deps = [ "//base" ]
 
   if (enable_ipc_fuzzer) {
     public_configs = [ "//tools/ipc_fuzzer:ipc_fuzzer_config" ]
@@ -170,9 +168,7 @@
   if (is_win || is_mac) {
     # On Windows HandleAttachmentWin needs to generate random IDs.
     # On Mac MachPortAttachmentMac needs to generate random IDs.
-    deps = [
-      "//crypto",
-    ]
+    deps = [ "//crypto" ]
   }
 }
 
@@ -190,9 +186,7 @@
 mojom_component("mojom") {
   output_prefix = "ipc_mojom"
   macro_prefix = "IPC_MOJOM"
-  sources = [
-    "ipc.mojom",
-  ]
+  sources = [ "ipc.mojom" ]
   public_deps = [
     "//mojo/public/interfaces/bindings",
     "//mojo/public/mojom/base",
@@ -204,34 +198,26 @@
 }
 
 mojom("mojom_constants") {
-  sources = [
-    "constants.mojom",
-  ]
+  sources = [ "constants.mojom" ]
 }
 
 mojom("test_interfaces") {
   testonly = true
-  sources = [
-    "ipc_test.mojom",
-  ]
+  sources = [ "ipc_test.mojom" ]
   support_lazy_serialization = true
 }
 
 # This is provided as a separate target so other targets can provide param
 # traits implementations without necessarily linking to all of IPC.
 source_set("param_traits") {
-  public = [
-    "ipc_param_traits.h",
-  ]
+  public = [ "ipc_param_traits.h" ]
 }
 
 if (!is_ios) {
   source_set("run_all_unittests") {
     testonly = true
 
-    sources = [
-      "run_all_unittests.cc",
-    ]
+    sources = [ "run_all_unittests.cc" ]
 
     deps = [
       "//base",
@@ -242,9 +228,7 @@
   }
 
   proto_library("test_proto") {
-    sources = [
-      "test_proto.proto",
-    ]
+    sources = [ "test_proto.proto" ]
   }
 
   test("ipc_tests") {
@@ -338,9 +322,7 @@
       "ipc_test_sink.cc",
       "ipc_test_sink.h",
     ]
-    public_deps = [
-      ":ipc",
-    ]
+    public_deps = [ ":ipc" ]
     deps = [
       "//base",
       "//base/test:test_support",
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 0fbcadf0..637e891 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -88,7 +88,7 @@
 
 // Default enables QUIC ack decimation and adds a connection option to disable
 // it.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_ack_decimation, false)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_ack_decimation, true)
 
 // If true, QUIC offload pacing when using USPS as egress method.
 QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_offload_pacing_to_usps2, false)
diff --git a/net/quic/quic_transport_client.cc b/net/quic/quic_transport_client.cc
index ffa8244..40ba76d 100644
--- a/net/quic/quic_transport_client.cc
+++ b/net/quic/quic_transport_client.cc
@@ -316,11 +316,17 @@
   visitor_->OnIncomingUnidirectionalStreamAvailable();
 }
 
-void QuicTransportClient::OnIncomingDatagramAvailable() {}
+void QuicTransportClient::OnIncomingDatagramAvailable() {
+  visitor_->OnIncomingDatagramAvailable();
+}
 
-void QuicTransportClient::OnCanCreateNewOutgoingBidirectionalStream() {}
+void QuicTransportClient::OnCanCreateNewOutgoingBidirectionalStream() {
+  visitor_->OnCanCreateNewOutgoingBidirectionalStream();
+}
 
-void QuicTransportClient::OnCanCreateNewOutgoingUnidirectionalStream() {}
+void QuicTransportClient::OnCanCreateNewOutgoingUnidirectionalStream() {
+  visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
+}
 
 void QuicTransportClient::OnReadError(int result,
                                       const DatagramClientSocket* socket) {
diff --git a/net/quic/quic_transport_client.h b/net/quic/quic_transport_client.h
index 1bb0fdb..add6abc 100644
--- a/net/quic/quic_transport_client.h
+++ b/net/quic/quic_transport_client.h
@@ -91,6 +91,9 @@
 
     virtual void OnIncomingBidirectionalStreamAvailable() = 0;
     virtual void OnIncomingUnidirectionalStreamAvailable() = 0;
+    virtual void OnIncomingDatagramAvailable() = 0;
+    virtual void OnCanCreateNewOutgoingBidirectionalStream() = 0;
+    virtual void OnCanCreateNewOutgoingUnidirectionalStream() = 0;
   };
 
   // |visitor| and |context| must outlive this object.
diff --git a/net/quic/quic_transport_end_to_end_test.cc b/net/quic/quic_transport_end_to_end_test.cc
index 43be115..637c45c 100644
--- a/net/quic/quic_transport_end_to_end_test.cc
+++ b/net/quic/quic_transport_end_to_end_test.cc
@@ -31,6 +31,9 @@
 
   MOCK_METHOD0(OnIncomingBidirectionalStreamAvailable, void());
   MOCK_METHOD0(OnIncomingUnidirectionalStreamAvailable, void());
+  MOCK_METHOD0(OnIncomingDatagramAvailable, void());
+  MOCK_METHOD0(OnCanCreateNewOutgoingBidirectionalStream, void());
+  MOCK_METHOD0(OnCanCreateNewOutgoingUnidirectionalStream, void());
 };
 
 class QuicTransportEndToEndTest : public TestWithTaskEnvironment {
diff --git a/services/device/serial/serial_io_handler_posix.cc b/services/device/serial/serial_io_handler_posix.cc
index 1a8f215..13ab169 100644
--- a/services/device/serial/serial_io_handler_posix.cc
+++ b/services/device/serial/serial_io_handler_posix.cc
@@ -145,13 +145,13 @@
 
 void SerialIoHandlerPosix::CancelReadImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  file_read_watcher_.reset();
+  StopWatchingFileRead();
   QueueReadCompleted(0, read_cancel_reason());
 }
 
 void SerialIoHandlerPosix::CancelWriteImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  file_write_watcher_.reset();
+  StopWatchingFileWrite();
   QueueWriteCompleted(0, write_cancel_reason());
 }
 
@@ -343,7 +343,7 @@
   } else {
     // Stop watching the fd if we get notifications with no pending
     // reads or writes to avoid starving the message loop.
-    file_read_watcher_.reset();
+    StopWatchingFileRead();
   }
 }
 
@@ -353,7 +353,7 @@
   if (within_read) {
     // Stop watching the fd to avoid more reads until the queued ReadCompleted()
     // completes and releases the pending_read_buffer.
-    file_read_watcher_.reset();
+    StopWatchingFileRead();
 
     QueueReadCompleted(bytes_read, error);
   } else {
@@ -376,7 +376,7 @@
   } else {
     // Stop watching the fd if we get notifications with no pending
     // writes to avoid starving the message loop.
-    file_write_watcher_.reset();
+    StopWatchingFileWrite();
   }
 }
 
@@ -403,6 +403,26 @@
   }
 }
 
+void SerialIoHandlerPosix::StopWatchingFileRead() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (file_read_watcher_) {
+    // Check that file is valid before stopping the watch, to avoid getting a
+    // hard to diagnose crash in MessagePumpLibEvent. https://crbug.com/996777
+    CHECK(file().IsValid());
+    file_read_watcher_.reset();
+  }
+}
+
+void SerialIoHandlerPosix::StopWatchingFileWrite() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (file_write_watcher_) {
+    // Check that file is valid before stopping the watch, to avoid getting a
+    // hard to diagnose crash in MessagePumpLibEvent. https://crbug.com/996777
+    CHECK(file().IsValid());
+    file_write_watcher_.reset();
+  }
+}
+
 bool SerialIoHandlerPosix::Flush() const {
   if (tcflush(file().GetPlatformFile(), TCIOFLUSH) != 0) {
     VPLOG(1) << "Failed to flush port";
diff --git a/services/device/serial/serial_io_handler_posix.h b/services/device/serial/serial_io_handler_posix.h
index 97f64b2..39d187c 100644
--- a/services/device/serial/serial_io_handler_posix.h
+++ b/services/device/serial/serial_io_handler_posix.h
@@ -61,6 +61,9 @@
   void EnsureWatchingReads();
   void EnsureWatchingWrites();
 
+  void StopWatchingFileRead();
+  void StopWatchingFileWrite();
+
   std::unique_ptr<base::FileDescriptorWatcher::Controller> file_read_watcher_;
   std::unique_ptr<base::FileDescriptorWatcher::Controller> file_write_watcher_;
 
diff --git a/services/network/quic_transport.cc b/services/network/quic_transport.cc
index e46480e1..6a3ea9d4 100644
--- a/services/network/quic_transport.cc
+++ b/services/network/quic_transport.cc
@@ -278,9 +278,10 @@
   memcpy(buffer->data(), data.data(), data.size());
   quic::QuicMemSlice slice(
       quic::QuicMemSliceImpl(std::move(buffer), data.size()));
-  const quic::MessageResult result =
-      transport_->session()->SendMessage(quic::QuicMemSliceSpan(&slice));
-  std::move(callback).Run(result.status == quic::MESSAGE_STATUS_SUCCESS);
+  const quic::MessageStatus status =
+      transport_->session()->datagram_queue()->SendOrQueueDatagram(
+          std::move(slice));
+  std::move(callback).Run(status == quic::MESSAGE_STATUS_SUCCESS);
 }
 
 void QuicTransport::CreateStream(
@@ -470,6 +471,18 @@
   }
 }
 
+void QuicTransport::OnIncomingDatagramAvailable() {
+  // TODO(yhirano): Implement this.
+}
+
+void QuicTransport::OnCanCreateNewOutgoingBidirectionalStream() {
+  // TODO(yhirano): Implement this.
+}
+
+void QuicTransport::OnCanCreateNewOutgoingUnidirectionalStream() {
+  // TODO(yhirano): Implement this.
+}
+
 void QuicTransport::TearDown() {
   torn_down_ = true;
   receiver_.reset();
diff --git a/services/network/quic_transport.h b/services/network/quic_transport.h
index 8023bbf..9a289a80a 100644
--- a/services/network/quic_transport.h
+++ b/services/network/quic_transport.h
@@ -69,6 +69,9 @@
   void OnError() override;
   void OnIncomingBidirectionalStreamAvailable() override;
   void OnIncomingUnidirectionalStreamAvailable() override;
+  void OnIncomingDatagramAvailable() override;
+  void OnCanCreateNewOutgoingBidirectionalStream() override;
+  void OnCanCreateNewOutgoingUnidirectionalStream() override;
 
   bool torn_down() const { return torn_down_; }
 
diff --git a/storage/common/blob_storage/OWNERS b/storage/common/blob_storage/OWNERS
deleted file mode 100644
index 4959232..0000000
--- a/storage/common/blob_storage/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-dmurph@chromium.org
-mek@chromium.org
-
-# TEAM: storage-dev@chromium.org
-# COMPONENT: Blink>Storage>FileAPI
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 41ca446..cbd2e9fa 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -318,7 +318,7 @@
             {
               "cipd_package": "chromium/chrome/test/chromedriver/cipd",
               "location": "chrome/test/chromedriver/cipd",
-              "revision": "tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC"
+              "revision": "JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C"
             }
           ],
           "dimension_sets": [
@@ -352,7 +352,7 @@
             {
               "cipd_package": "chromium/chrome/test/chromedriver/cipd",
               "location": "chrome/test/chromedriver/cipd",
-              "revision": "tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC"
+              "revision": "JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C"
             }
           ],
           "dimension_sets": [
@@ -386,7 +386,7 @@
             {
               "cipd_package": "chromium/chrome/test/chromedriver/cipd",
               "location": "chrome/test/chromedriver/cipd",
-              "revision": "tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC"
+              "revision": "JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C"
             }
           ],
           "dimension_sets": [
@@ -472,7 +472,7 @@
             {
               "cipd_package": "chromium/chrome/test/chromedriver/cipd",
               "location": "chrome/test/chromedriver/cipd",
-              "revision": "tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC"
+              "revision": "JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C"
             }
           ],
           "dimension_sets": [
@@ -503,7 +503,7 @@
             {
               "cipd_package": "chromium/chrome/test/chromedriver/cipd",
               "location": "chrome/test/chromedriver/cipd",
-              "revision": "tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC"
+              "revision": "JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C"
             }
           ],
           "dimension_sets": [
@@ -534,7 +534,7 @@
             {
               "cipd_package": "chromium/chrome/test/chromedriver/cipd",
               "location": "chrome/test/chromedriver/cipd",
-              "revision": "tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC"
+              "revision": "JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C"
             }
           ],
           "dimension_sets": [
@@ -1730,7 +1730,8 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android23.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android23.textpb",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.cc_unittests.filter"
         ],
         "merge": {
           "args": [
@@ -6029,7 +6030,8 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android28.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android28.textpb",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.cc_unittests.filter"
         ],
         "merge": {
           "args": [
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index b990534..3f8b971 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -22,6 +22,12 @@
   data = [ "//testing/buildbot/filters/bfcache.android_browsertests.filter" ]
 }
 
+source_set("cc_unittests_filters") {
+  testonly = true
+
+  data = [ "//testing/buildbot/filters/android.emulator.cc_unittests.filter" ]
+}
+
 source_set("blink_web_tests_filter") {
   testonly = true
 
diff --git a/testing/buildbot/filters/android.emulator.cc_unittests.filter b/testing/buildbot/filters/android.emulator.cc_unittests.filter
new file mode 100644
index 0000000..168be93
--- /dev/null
+++ b/testing/buildbot/filters/android.emulator.cc_unittests.filter
@@ -0,0 +1,2 @@
+# https://crbug.com/1039860
+-All/ImageTransferCacheEntryTest.HardwareDecoded*
diff --git a/testing/buildbot/filters/e2e.sync_integration_tests.filter b/testing/buildbot/filters/e2e.sync_integration_tests.filter
index 4dbaf62..6bb03d24 100644
--- a/testing/buildbot/filters/e2e.sync_integration_tests.filter
+++ b/testing/buildbot/filters/e2e.sync_integration_tests.filter
@@ -1,14 +1 @@
-# TODO(crbug.com/1010568): Enable all sync e2e tests.

--TwoClientDictionarySyncTest.RemoveOnAAddOnB_E2ETest

--USS/TwoClientPasswordsSyncTest*

--TwoClientSearchEnginesSyncTest.DeleteSyncedDefault_E2ETest

--TwoClientSearchEnginesSyncTest.Delete_E2ETest

--TwoClientSearchEnginesSyncTest.SyncDefault_E2ETest

--TwoClientSearchEnginesSyncTest.UpdateKeyword_E2ETest

--TwoClientSearchEnginesSyncTest.UpdateName_E2ETest

--TwoClientSearchEnginesSyncTest.UpdateUrl_E2ETest

--TwoClientSessionsSyncTest*

--TwoClientThemesSyncTest*

--TwoClientTypedUrlsSyncTest.AddThenDelete_E2ETest

--TwoClientTypedUrlsSyncTest.DisableEnableSync_E2ETest

--TwoClientAutofillProfileSyncTest.DISABLED_E2ETest_TwoClientsAddAutofillProfiles
\ No newline at end of file
+# This is the filter file for sync e2e tests.

diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index c227616..0640a282 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -236,6 +236,18 @@
           'shards': 3,
         },
       },
+      'android-marshmallow-x86-fyi-rel': {
+        # https://crbug.com/1039860
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.cc_unittests.filter',
+        ],
+      },
+      'android-pie-x86-fyi-rel': {
+        # https://crbug.com/1039860
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.cc_unittests.filter',
+        ],
+      },
     },
   },
   'checkbins': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 7bf81945..56578c2 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -379,7 +379,7 @@
             {
               "cipd_package": 'chromium/chrome/test/chromedriver/cipd',
               'location': 'chrome/test/chromedriver/cipd',
-              'revision': 'tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC',
+              'revision': 'JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C',
             }
           ]
         },
@@ -3205,7 +3205,7 @@
             {
               "cipd_package": 'chromium/chrome/test/chromedriver/cipd',
               'location': 'chrome/test/chromedriver/cipd',
-              'revision': 'tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC',
+              'revision': 'JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C',
             }
           ]
         },
@@ -3262,7 +3262,7 @@
             {
               "cipd_package": 'chromium/chrome/test/chromedriver/cipd',
               'location': 'chrome/test/chromedriver/cipd',
-              'revision': 'tLMjWvOcbQX4nMo5WnMsKWn0aaVludzuSX9yiVFUCoUC',
+              'revision': 'JvTGvPUc-qPtmBqyJhvc-1i28yILiGeTjqGi-BvjiN4C',
             }
           ]
         },
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 5e30d5d..58abbb7 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -101,7 +101,7 @@
   'load_library_perf_tests',
   'media_perftests',
   'net_perftests',
-  'performance_browser_tests',
+  'browser_tests',
   'services_perftests',
   'tracing_perftests',
   'views_perftests',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a761129c..8c51f58 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1584,6 +1584,21 @@
             ]
         }
     ],
+    "ChromeOSAssistantDspHotword": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "EnableDspHotword"
+                    ]
+                }
+            ]
+        }
+    ],
     "ChromeOSHeuristicPalm": [
         {
             "platforms": [
@@ -3382,6 +3397,21 @@
             ]
         }
     ],
+    "InterestFeedFeedback": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "FeedbackOnInterestFeed",
+                    "enable_features": [
+                        "InterestFeedFeedback"
+                    ]
+                }
+            ]
+        }
+    ],
     "InvalidationsGCMUpstream": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/use_counter/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
index 94bea29..363827f 100644
--- a/third_party/blink/public/mojom/use_counter/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
@@ -726,6 +726,7 @@
     kIntrinsicSize = 651,
     kIntrinsicWidth = 652,
     kRenderSubtree = 653,
+    kOriginTrialTestProperty = 654,
     // 1. Add new features above this line (don't change the assigned numbers of
     // the existing items).
     // 2. Run the update_use_counter_css.py script in
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index f4e2cfdb2..d0c89c8ec 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -2632,6 +2632,17 @@
       typedom_types: ["Number"]
     },
     {
+      // This property is used for testing with origin trial intergration only.
+      // It should never be web-exposed.
+      name: "origin-trial-test-property",
+      property_methods: ["CSSValueFromComputedStyleInternal"],
+      field_template: "keyword",
+      default_value: "normal",
+      keywords: ["normal", "none"],
+      typedom_types: ["Keyword"],
+      runtime_flag: "OriginTrialsSampleAPI"
+    },
+    {
       name: "orphans",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
       interpolable: true,
diff --git a/third_party/blink/renderer/core/css/css_properties_ranking.json5 b/third_party/blink/renderer/core/css/css_properties_ranking.json5
index 0c41d95..04e08d9 100644
--- a/third_party/blink/renderer/core/css/css_properties_ranking.json5
+++ b/third_party/blink/renderer/core/css/css_properties_ranking.json5
@@ -694,6 +694,7 @@
         "inset-block-start",
         "syntax",
         "render-subtree",
+        "origin-trial-test-property",
     ],
     "properties": {}
 }
diff --git a/third_party/blink/renderer/core/css/css_selector.cc b/third_party/blink/renderer/core/css/css_selector.cc
index 6b66a73..dfd9de7 100644
--- a/third_party/blink/renderer/core/css/css_selector.cc
+++ b/third_party/blink/renderer/core/css/css_selector.cc
@@ -787,7 +787,7 @@
         case kPseudoLang:
         case kPseudoState:
           builder.Append('(');
-          builder.Append(simple_selector->Argument());
+          SerializeIdentifier(simple_selector->Argument(), builder);
           builder.Append(')');
           break;
         case kPseudoNot:
@@ -804,7 +804,7 @@
       }
     } else if (simple_selector->match_ == kPseudoElement) {
       builder.Append("::");
-      builder.Append(simple_selector->SerializingValue());
+      SerializeIdentifier(simple_selector->SerializingValue(), builder);
       switch (simple_selector->GetPseudoType()) {
         case kPseudoPart: {
           char separator = '(';
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
index c49899b..44459c1 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
@@ -963,6 +963,8 @@
     case CSSPropertyID::kOverscrollBehaviorY:
       return value_id == CSSValueID::kAuto ||
              value_id == CSSValueID::kContain || value_id == CSSValueID::kNone;
+    case CSSPropertyID::kOriginTrialTestProperty:
+      return value_id == CSSValueID::kNormal || value_id == CSSValueID::kNone;
     default:
       NOTREACHED();
       return false;
@@ -1077,6 +1079,7 @@
     case CSSPropertyID::kWordBreak:
     case CSSPropertyID::kWritingMode:
     case CSSPropertyID::kScrollSnapStop:
+    case CSSPropertyID::kOriginTrialTestProperty:
       return true;
     default:
       return false;
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 882b5adf..8f43ee8 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
@@ -4404,6 +4404,15 @@
                                         CSSPrimitiveValue::UnitType::kNumber);
 }
 
+const CSSValue* OriginTrialTestProperty::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const SVGComputedStyle&,
+    const LayoutObject*,
+    bool allow_visited_style) const {
+  return CSSIdentifierValue::Create(style.OriginTrialTestProperty());
+  ;
+}
+
 const CSSValue* Orphans::ParseSingleValue(CSSParserTokenRange& range,
                                           const CSSParserContext& context,
                                           const CSSParserLocalContext&) const {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 01a058bf..c08a62dd 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -736,7 +736,7 @@
   WebFeature feature = WebFeature::kOffscreenCanvasTransferToImageBitmapWebGL;
   UseCounter::Count(ExecutionContext::From(script_state), feature);
   return MakeGarbageCollected<ImageBitmap>(
-      GetDrawingBuffer()->TransferToStaticBitmapImage(nullptr));
+      GetDrawingBuffer()->TransferToStaticBitmapImage());
 }
 
 void WebGLRenderingContextBase::commit() {
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index 6b562aab..4f3fbe3 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -500,8 +500,6 @@
 
   frame_transport_->FramePreImage(webgl_context->ContextGL());
 
-  std::unique_ptr<viz::SingleReleaseCallback> image_release_callback;
-
   if (frame_transport_->DrawingIntoSharedBuffer()) {
     // Image is written to shared buffer already. Just submit with a
     // placeholder.
@@ -509,13 +507,12 @@
     DVLOG(3) << __FUNCTION__ << ": FrameSubmit for SharedBuffer mode";
     frame_transport_->FrameSubmit(immersive_presentation_provider_.get(),
                                   webgl_context->ContextGL(), webgl_context,
-                                  std::move(image_ref),
-                                  std::move(image_release_callback), frame_id_);
+                                  std::move(image_ref), frame_id_);
     return;
   }
 
   scoped_refptr<StaticBitmapImage> image_ref =
-      layer->TransferToStaticBitmapImage(&image_release_callback);
+      layer->TransferToStaticBitmapImage();
 
   if (!image_ref)
     return;
@@ -529,8 +526,7 @@
 
   frame_transport_->FrameSubmit(immersive_presentation_provider_.get(),
                                 webgl_context->ContextGL(), webgl_context,
-                                std::move(image_ref),
-                                std::move(image_release_callback), frame_id_);
+                                std::move(image_ref), frame_id_);
 
   // Reset our frame id, since anything we'd want to do (resizing/etc) can
   // no-longer happen to this frame.
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
index 064dc8f..3f32909e 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -296,10 +296,9 @@
   viewports_dirty_ = true;
 }
 
-scoped_refptr<StaticBitmapImage> XRWebGLLayer::TransferToStaticBitmapImage(
-    std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+scoped_refptr<StaticBitmapImage> XRWebGLLayer::TransferToStaticBitmapImage() {
   if (drawing_buffer_) {
-    return drawing_buffer_->TransferToStaticBitmapImage(out_release_callback);
+    return drawing_buffer_->TransferToStaticBitmapImage();
   }
   return nullptr;
 }
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
index 24765b0..95d27ea 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
@@ -74,8 +74,7 @@
   // mailbox holder and its size respectively.
   void HandleBackgroundImage(const gpu::MailboxHolder&, const IntSize&) {}
 
-  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(
-      std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage();
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index a3e56a35..ffb4a4b 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1121,6 +1121,7 @@
     "graphics/subtree_paint_property_update_reason.h",
     "graphics/surface_layer_bridge.cc",
     "graphics/surface_layer_bridge.h",
+    "graphics/texture_holder.cc",
     "graphics/texture_holder.h",
     "graphics/touch_action.h",
     "graphics/unaccelerated_static_bitmap_image.cc",
diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.cc b/third_party/blink/renderer/platform/bindings/dom_data_store.cc
index 5e10e70..e0b9911 100644
--- a/third_party/blink/renderer/platform/bindings/dom_data_store.cc
+++ b/third_party/blink/renderer/platform/bindings/dom_data_store.cc
@@ -10,20 +10,16 @@
     : is_main_world_(is_main_world) {}
 
 void DOMDataStore::Dispose() {
-  for (const auto& it : wrapper_map_) {
+  for (auto& it : wrapper_map_) {
     // Explicitly reset references so that a following V8 GC will not find them
     // and treat them as roots. There's optimizations (see
     // EmbedderHeapTracer::IsRootForNonTracingGC) that would not treat them as
     // roots and then Blink would not be able to find and remove them from a DOM
     // world. Explicitly resetting on disposal avoids that problem
-    it.value->ref.Clear();
+    it.value.Clear();
   }
 }
 
-void DOMDataStore::WrappedReference::Trace(Visitor* visitor) {
-  visitor->Trace(ref);
-}
-
 void DOMDataStore::Trace(Visitor* visitor) {
   visitor->Trace(wrapper_map_);
 }
diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.h b/third_party/blink/renderer/platform/bindings/dom_data_store.h
index 975c6b56..0d8cfbc5 100644
--- a/third_party/blink/renderer/platform/bindings/dom_data_store.h
+++ b/third_party/blink/renderer/platform/bindings/dom_data_store.h
@@ -35,6 +35,7 @@
 #include "base/optional.h"
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
 #include "third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -118,7 +119,7 @@
       return object->MainWorldWrapper(isolate);
     auto it = wrapper_map_.find(object);
     if (it != wrapper_map_.end())
-      return it->value->ref.NewLocal(isolate);
+      return it->value.NewLocal(isolate);
     return v8::Local<v8::Object>();
   }
 
@@ -132,13 +133,12 @@
       return object->SetWrapper(isolate, wrapper_type_info, wrapper);
 
     auto result = wrapper_map_.insert(
-        object, MakeGarbageCollected<WrappedReference>(isolate, wrapper));
+        object, TraceWrapperV8Reference<v8::Object>(isolate, wrapper));
     if (LIKELY(result.is_new_entry)) {
-      wrapper_type_info->ConfigureWrapper(
-          &result.stored_value->value->ref.Get());
+      wrapper_type_info->ConfigureWrapper(&result.stored_value->value.Get());
     } else {
-      DCHECK(!result.stored_value->value->ref.IsEmpty());
-      wrapper = result.stored_value->value->ref.NewLocal(isolate);
+      DCHECK(!result.stored_value->value.IsEmpty());
+      wrapper = result.stored_value->value.NewLocal(isolate);
     }
     return result.is_new_entry;
   }
@@ -149,8 +149,8 @@
     DCHECK(!is_main_world_);
     const auto& it = wrapper_map_.find(object);
     if (it != wrapper_map_.end()) {
-      if (it->value->ref.Get() == handle) {
-        it->value->ref.Clear();
+      if (it->value.Get() == handle) {
+        it->value.Clear();
         wrapper_map_.erase(it);
         return true;
       }
@@ -164,7 +164,7 @@
       return object->SetReturnValue(return_value);
     auto it = wrapper_map_.find(object);
     if (it != wrapper_map_.end()) {
-      return_value.Set(it->value->ref.Get());
+      return_value.Set(it->value.Get());
       return true;
     }
     return false;
@@ -198,24 +198,10 @@
     return wrappable->IsEqualTo(holder);
   }
 
-  // Wrapper around TraceWrapperV8Reference to allow use in ephemeron hash map
-  // below.
-  class PLATFORM_EXPORT WrappedReference final
-      : public GarbageCollected<WrappedReference> {
-   public:
-    WrappedReference() = default;
-    WrappedReference(v8::Isolate* isolate, v8::Local<v8::Object> handle)
-        : ref(isolate, handle) {}
-
-    virtual void Trace(Visitor*);
-
-    TraceWrapperV8Reference<v8::Object> ref;
-  };
-
   bool is_main_world_;
-  // Ephemeron map: WrappedReference will be kept alive as long as
-  // ScriptWrappable is alive.
-  HeapHashMap<WeakMember<const ScriptWrappable>, Member<WrappedReference>>
+  // Ephemeron map: V8 wrapper will be kept alive as long as ScriptWrappable is.
+  HeapHashMap<WeakMember<const ScriptWrappable>,
+              TraceWrapperV8Reference<v8::Object>>
       wrapper_map_;
 
   DISALLOW_COPY_AND_ASSIGN(DOMDataStore);
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
index 9e2843f..d8b8868 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
@@ -27,48 +27,22 @@
 namespace blink {
 
 scoped_refptr<AcceleratedStaticBitmapImage>
-AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
-    const gpu::Mailbox& mailbox,
-    const gpu::SyncToken& sync_token,
-    unsigned texture_id,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
-        context_provider_wrapper,
-    IntSize mailbox_size,
-    bool is_origin_top_left) {
-  return base::AdoptRef(new AcceleratedStaticBitmapImage(
-      mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
-      mailbox_size, is_origin_top_left));
-}
-
-scoped_refptr<AcceleratedStaticBitmapImage>
 AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
     const gpu::Mailbox& mailbox,
     const gpu::SyncToken& sync_token,
     GLuint shared_image_texture_id,
     const SkImageInfo& sk_image_info,
     GLenum texture_target,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
-    PlatformThreadId context_thread_id,
     bool is_origin_top_left,
+    base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
+    base::PlatformThreadRef context_thread_ref,
+    scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
     std::unique_ptr<viz::SingleReleaseCallback> release_callback) {
   return base::AdoptRef(new AcceleratedStaticBitmapImage(
       mailbox, sync_token, shared_image_texture_id, sk_image_info,
-      texture_target, std::move(context_provider_wrapper), context_thread_id,
-      is_origin_top_left, std::move(release_callback)));
-}
-
-AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
-    const gpu::Mailbox& mailbox,
-    const gpu::SyncToken& sync_token,
-    unsigned texture_id,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
-        context_provider_wrapper,
-    IntSize mailbox_size,
-    bool is_origin_top_left)
-    : paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
-  mailbox_texture_holder_ = std::make_unique<MailboxTextureHolder>(
-      mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
-      mailbox_size, is_origin_top_left);
+      texture_target, is_origin_top_left, std::move(context_provider_wrapper),
+      context_thread_ref, std::move(context_task_runner),
+      std::move(release_callback)));
 }
 
 AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
@@ -77,17 +51,20 @@
     GLuint shared_image_texture_id,
     const SkImageInfo& sk_image_info,
     GLenum texture_target,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
-        context_provider_wrapper,
-    PlatformThreadId context_thread_id,
     bool is_origin_top_left,
+    base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
+    base::PlatformThreadRef context_thread_ref,
+    scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
     std::unique_ptr<viz::SingleReleaseCallback> release_callback)
     : mailbox_ref_(base::MakeRefCounted<TextureHolder::MailboxRef>(
+          sync_token,
+          context_thread_ref,
+          std::move(context_task_runner),
           std::move(release_callback))),
       paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
   mailbox_texture_holder_ = std::make_unique<MailboxTextureHolder>(
-      mailbox, sync_token, std::move(context_provider_wrapper), mailbox_ref_,
-      context_thread_id, sk_image_info, texture_target, is_origin_top_left);
+      mailbox, context_provider_wrapper, mailbox_ref_, sk_image_info,
+      texture_target, is_origin_top_left);
   if (shared_image_texture_id) {
     skia_texture_holder_ = std::make_unique<SkiaTextureHolder>(
         mailbox_texture_holder_.get(), shared_image_texture_id);
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
index 3909e75c..5c8958a 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
@@ -30,23 +30,10 @@
  public:
   ~AcceleratedStaticBitmapImage() override;
 
-  // Can specify the GrContext that created the texture backing. Ideally all
-  // callers would use this option.
-  // The |mailbox| is a name for the texture backing, allowing other contexts to
-  // use the same backing.
-  static scoped_refptr<AcceleratedStaticBitmapImage>
-  CreateFromWebGLContextImage(
-      const gpu::Mailbox&,
-      const gpu::SyncToken&,
-      unsigned texture_id,
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
-      IntSize mailbox_size,
-      bool is_origin_top_left);
-
-  // Creates an image wrapping a shared image mailbox. |release_callback| is a
-  // callback to be invoked when this mailbox/texture can be safely destroyed.
-  // It can be invoked on any thread. Note that it is assumed that the mailbox
-  // can only be used for read operations, no writes are allowed.
+  // Creates an image wrapping a shared image mailbox.
+  //
+  // |sync_token| is the token that must be waited on before reading the
+  // contents of this mailbox.
   //
   // |shared_image_texture_id| is an optional texture bound to the shared image
   // mailbox imported into the provided context. If provided the caller must
@@ -54,18 +41,35 @@
   // and has a read lock on the shared image until the |release_callback| is
   // invoked.
   //
-  // If the image is created on a different thread than |context_thread_id| then
-  // the provided sync_token must be verified and no |shared_image_texture_id|
-  // should be provided.
+  // |sk_image_info| provides the metadata associated with the backing.
+  //
+  // |texture_target| is the target that the texture should be bound to if the
+  // backing is used with GL.
+  //
+  // |is_origin_top_left| indicates whether the origin in texture space
+  // corresponds to the top-left content pixel.
+  //
+  // |context_provider| is the context that the mailbox was created with.
+  // |context_thread_ref| and |context_task_runner| refer to the thread the
+  // context is bound to. If the image is created on a different thread than
+  // |context_thread_ref| then the provided sync_token must be verified and no
+  // |shared_image_texture_id| should be provided.
+  //
+  // |release_callback| is a callback to be invoked when this mailbox can be
+  // safely destroyed. It is guaranteed to be invoked on the context thread.
+  //
+  // Note that it is assumed that the mailbox can only be used for read
+  // operations, no writes are allowed.
   static scoped_refptr<AcceleratedStaticBitmapImage> CreateFromCanvasMailbox(
       const gpu::Mailbox&,
       const gpu::SyncToken&,
       GLuint shared_image_texture_id,
       const SkImageInfo& sk_image_info,
       GLenum texture_target,
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
-      PlatformThreadId context_thread_id,
       bool is_origin_top_left,
+      base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
+      base::PlatformThreadRef context_thread_ref,
+      scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
       std::unique_ptr<viz::SingleReleaseCallback> release_callback);
 
   bool CurrentFrameKnownToBeOpaque() override;
@@ -98,20 +102,27 @@
                      const IntRect& source_sub_rectangle) override;
 
   bool HasMailbox() const final { return !!mailbox_texture_holder_; }
-  // To be called on sender thread before performing a transfer
+
+  // To be called on sender thread before performing a transfer to a different
+  // thread.
   void Transfer() final;
 
   void EnsureMailbox(MailboxSyncMode, GLenum filter) final;
 
+  // Provides the mailbox backing for this image. The caller must wait on the
+  // sync token from GetSyncToken before accessing this mailbox.
   const gpu::Mailbox& GetMailbox() const final {
-    static const gpu::Mailbox mailbox;
-    return mailbox_texture_holder_ ? mailbox_texture_holder_->GetMailbox()
-                                   : mailbox;
+    return mailbox_texture_holder_->GetMailbox();
   }
   const gpu::SyncToken& GetSyncToken() const final {
-    static const gpu::SyncToken sync_token;
-    return mailbox_texture_holder_ ? mailbox_texture_holder_->GetSyncToken()
-                                   : sync_token;
+    return mailbox_texture_holder_->GetSyncToken();
+  }
+
+  // Updates the sync token that must be waited on before recycling or deleting
+  // the mailbox for this image. This must be set by callers using the mailbox
+  // externally to this class.
+  void UpdateSyncToken(const gpu::SyncToken& sync_token) final {
+    mailbox_texture_holder_->UpdateSyncToken(sync_token);
   }
   bool IsOriginTopLeft() const final {
     return texture_holder()->IsOriginTopLeft();
@@ -123,19 +134,13 @@
   AcceleratedStaticBitmapImage(
       const gpu::Mailbox&,
       const gpu::SyncToken&,
-      unsigned texture_id,
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
-      IntSize mailbox_size,
-      bool is_origin_top_left);
-  AcceleratedStaticBitmapImage(
-      const gpu::Mailbox&,
-      const gpu::SyncToken&,
       GLuint shared_image_texture_id,
       const SkImageInfo& sk_image_info,
       GLenum texture_target,
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
-      PlatformThreadId context_thread_id,
       bool is_origin_top_left,
+      base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
+      base::PlatformThreadRef context_thread_ref,
+      scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
       std::unique_ptr<viz::SingleReleaseCallback> release_callback);
 
   void CreateImageFromMailboxIfNeeded();
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc
index cdc6fa8..30b1312f 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 
+#include "base/test/null_task_runner.h"
 #include "base/test/task_environment.h"
 #include "components/viz/test/test_gles2_interface.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -47,9 +48,14 @@
 
 scoped_refptr<StaticBitmapImage> CreateBitmap() {
   auto mailbox = gpu::Mailbox::GenerateForSharedImage();
-  return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
-      mailbox, GenTestSyncToken(100), 0,
-      SharedGpuContext::ContextProviderWrapper(), IntSize(100, 100), true);
+  auto release_callback = viz::SingleReleaseCallback::Create(
+      base::BindOnce([](const gpu::SyncToken&, bool) {}));
+  return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
+      mailbox, GenTestSyncToken(100), 0, SkImageInfo::MakeN32Premul(100, 100),
+      GL_TEXTURE_2D, true, SharedGpuContext::ContextProviderWrapper(),
+      base::PlatformThread::CurrentRef(),
+      base::MakeRefCounted<base::NullTaskRunner>(),
+      std::move(release_callback));
 }
 
 class AcceleratedStaticBitmapImageTest : public Test {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index 5c73a169e..e978da4 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -48,7 +48,8 @@
 CanvasResource::CanvasResource(base::WeakPtr<CanvasResourceProvider> provider,
                                SkFilterQuality filter_quality,
                                const CanvasColorParams& color_params)
-    : owning_thread_id_(base::PlatformThread::CurrentId()),
+    : owning_thread_ref_(base::PlatformThread::CurrentRef()),
+      owning_thread_task_runner_(Thread::Current()->GetTaskRunner()),
       provider_(std::move(provider)),
       filter_quality_(filter_quality),
       color_params_(color_params) {}
@@ -60,7 +61,7 @@
 }
 
 void CanvasResource::OnDestroy() {
-  if (owning_thread_id_ != base::PlatformThread::CurrentId()) {
+  if (is_cross_thread()) {
     // Destroyed on wrong thread. This can happen when the thread of origin was
     // torn down, in which case the GPU context owning any underlying resources
     // no longer exists.
@@ -332,8 +333,7 @@
                     BufferFormat(ColorParams().TransferableResourceFormat()),
                     context_provider_wrapper_->ContextProvider()
                         ->GetCapabilities())
-              : GL_TEXTURE_2D),
-      owning_thread_task_runner_(Thread::Current()->GetTaskRunner()) {
+              : GL_TEXTURE_2D) {
   if (!context_provider_wrapper_)
     return;
 
@@ -480,15 +480,7 @@
     bool has_read_ref_on_texture,
     const gpu::SyncToken& sync_token,
     bool is_lost) {
-  if (resource->is_cross_thread()) {
-    auto& task_runner = *resource->owning_thread_task_runner_;
-    PostCrossThreadTask(
-        task_runner, FROM_HERE,
-        CrossThreadBindOnce(&CanvasResourceSharedImage::OnBitmapImageDestroyed,
-                            std::move(resource), has_read_ref_on_texture,
-                            sync_token, is_lost));
-    return;
-  }
+  DCHECK(!resource->is_cross_thread());
 
   if (has_read_ref_on_texture) {
     DCHECK_GT(resource->owning_thread_data().bitmap_image_read_refs, 0u);
@@ -570,8 +562,8 @@
   gpu::SyncToken token = is_cross_thread() ? sync_token() : gpu::SyncToken();
   image = AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
       mailbox(), token, texture_id_for_image, image_info, texture_target_,
-      context_provider_wrapper_, owning_thread_id_, is_origin_top_left_,
-      std::move(release_callback));
+      is_origin_top_left_, context_provider_wrapper_, owning_thread_ref_,
+      owning_thread_task_runner_, std::move(release_callback));
 
   DCHECK(image);
   return image;
@@ -704,8 +696,9 @@
 
   return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
       mailbox_, GetSyncToken(), /*shared_image_texture_id=*/0u,
-      CreateSkImageInfo(), texture_target_, context_provider_wrapper_,
-      owning_thread_id_, is_origin_top_left_, std::move(release_callback));
+      CreateSkImageInfo(), texture_target_, is_origin_top_left_,
+      context_provider_wrapper_, owning_thread_ref_, owning_thread_task_runner_,
+      std::move(release_callback));
 }
 
 void ExternalCanvasResource::TearDown() {
@@ -789,7 +782,7 @@
   // It's safe to share the front buffer texture id if we're on the same thread
   // since the |release_callback| ensures this resource will be alive.
   GLuint shared_texture_id = 0u;
-  if (base::PlatformThread::CurrentId() == owning_thread_id_)
+  if (!is_cross_thread())
     shared_texture_id = front_buffer_texture_id_;
 
   // The |release_callback| keeps a ref on this resource to ensure the backing
@@ -802,8 +795,10 @@
 
   return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
       front_buffer_mailbox_, sync_token_, shared_texture_id, image_info,
-      GL_TEXTURE_2D, context_provider_wrapper_, owning_thread_id_,
-      /*is_origin_top_left=*/true, std::move(release_callback));
+      GL_TEXTURE_2D,
+      /*is_origin_top_left=*/true, context_provider_wrapper_,
+      owning_thread_ref_, owning_thread_task_runner_,
+      std::move(release_callback));
 }
 
 void CanvasResourceSwapChain::Abandon() {
@@ -851,7 +846,7 @@
 }
 
 void CanvasResourceSwapChain::PresentSwapChain() {
-  DCHECK_EQ(base::PlatformThread::CurrentId(), owning_thread_id_);
+  DCHECK(!is_cross_thread());
   DCHECK(context_provider_wrapper_);
   TRACE_EVENT0("blink", "CanvasResourceSwapChain::PresentSwapChain");
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index 6e7df2e9..9a4d77a 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -153,6 +153,10 @@
 
   SkImageInfo CreateSkImageInfo() const;
 
+  bool is_cross_thread() const {
+    return base::PlatformThread::CurrentRef() != owning_thread_ref_;
+  }
+
  protected:
   CanvasResource(base::WeakPtr<CanvasResourceProvider>,
                  SkFilterQuality,
@@ -199,7 +203,8 @@
   CanvasResourceProvider* Provider() { return provider_.get(); }
   base::WeakPtr<CanvasResourceProvider> WeakProvider() { return provider_; }
 
-  const base::PlatformThreadId owning_thread_id_;
+  const base::PlatformThreadRef owning_thread_ref_;
+  const scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_;
 
  protected:
   // Returns the texture target for the resource.
@@ -298,9 +303,6 @@
   GLenum TextureTarget() const override { return texture_target_; }
 
   void WillDraw();
-  bool is_cross_thread() const {
-    return base::PlatformThread::CurrentId() != owning_thread_id_;
-  }
   bool has_read_access() const {
     return owning_thread_data().bitmap_image_read_refs > 0u;
   }
@@ -354,11 +356,11 @@
                             uint32_t shared_image_usage_flags);
 
   OwningThreadData& owning_thread_data() {
-    DCHECK_EQ(base::PlatformThread::CurrentId(), owning_thread_id_);
+    DCHECK(!is_cross_thread());
     return owning_thread_data_;
   }
   const OwningThreadData& owning_thread_data() const {
-    DCHECK_EQ(base::PlatformThread::CurrentId(), owning_thread_id_);
+    DCHECK(!is_cross_thread());
     return owning_thread_data_;
   }
 
@@ -391,7 +393,6 @@
   const bool is_accelerated_;
   const bool is_overlay_candidate_;
   const GLenum texture_target_;
-  const scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_;
 
   OwningThreadData owning_thread_data_;
 };
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index b8f760e..7a52c31 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -52,6 +52,7 @@
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
@@ -190,7 +191,8 @@
       opengl_flip_y_extension_(
           ContextProvider()->GetCapabilities().mesa_framebuffer_flip_y),
       initial_gpu_(gpu_preference),
-      current_active_gpu_(gpu_preference) {
+      current_active_gpu_(gpu_preference),
+      weak_factory_(this) {
   // Used by browser tests to detect the use of a DrawingBuffer.
   TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation",
                        TRACE_EVENT_SCOPE_GLOBAL);
@@ -390,9 +392,9 @@
   // This holds a ref on the DrawingBuffer that will keep it alive until the
   // mailbox is released (and while the release callback is running). It also
   // owns the SharedBitmap.
-  auto func = WTF::Bind(&DrawingBuffer::MailboxReleasedSoftware,
-                        scoped_refptr<DrawingBuffer>(this),
-                        WTF::Passed(std::move(registered)));
+  auto func = base::BindOnce(&DrawingBuffer::MailboxReleasedSoftware,
+                             weak_factory_.GetWeakPtr(),
+                             WTF::Passed(std::move(registered)));
   *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
 
   ResetBuffersToAutoClear();
@@ -501,9 +503,8 @@
 
     // This holds a ref on the DrawingBuffer that will keep it alive until the
     // mailbox is released (and while the release callback is running).
-    auto func =
-        WTF::Bind(&DrawingBuffer::MailboxReleasedGpu,
-                  scoped_refptr<DrawingBuffer>(this), color_buffer_for_mailbox);
+    auto func = base::BindOnce(&DrawingBuffer::NotifyMailboxReleasedGpu,
+                               color_buffer_for_mailbox);
     *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
   }
 
@@ -515,18 +516,29 @@
   return true;
 }
 
+// static
+void DrawingBuffer::NotifyMailboxReleasedGpu(
+    scoped_refptr<ColorBuffer> color_buffer,
+    const gpu::SyncToken& sync_token,
+    bool lost_resource) {
+  DCHECK(color_buffer->owning_thread_ref == base::PlatformThread::CurrentRef());
+
+  // Update the SyncToken to ensure that we will wait for it even if we
+  // immediately destroy this buffer.
+  color_buffer->receive_sync_token = sync_token;
+  if (color_buffer->drawing_buffer) {
+    color_buffer->drawing_buffer->MailboxReleasedGpu(color_buffer,
+                                                     lost_resource);
+  }
+}
+
 void DrawingBuffer::MailboxReleasedGpu(scoped_refptr<ColorBuffer> color_buffer,
-                                       const gpu::SyncToken& sync_token,
                                        bool lost_resource) {
   // If the mailbox has been returned by the compositor then it is no
   // longer being presented, and so is no longer the front buffer.
   if (color_buffer == front_color_buffer_)
     front_color_buffer_ = nullptr;
 
-  // Update the SyncToken to ensure that we will wait for it even if we
-  // immediately destroy this buffer.
-  color_buffer->receive_sync_token = sync_token;
-
   if (destruction_in_progress_ || color_buffer->size != size_ ||
       gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lost_resource ||
       is_hidden_) {
@@ -558,8 +570,7 @@
   recycled_bitmaps_.push_back(std::move(registered));
 }
 
-scoped_refptr<StaticBitmapImage> DrawingBuffer::TransferToStaticBitmapImage(
-    std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+scoped_refptr<StaticBitmapImage> DrawingBuffer::TransferToStaticBitmapImage() {
   ScopedStateRestorer scoped_state_restorer(this);
 
   viz::TransferableResource transferable_resource;
@@ -580,35 +591,10 @@
         SkImage::MakeFromBitmap(black_bitmap));
   }
 
+  DCHECK(release_callback);
   DCHECK_EQ(size_.Width(), transferable_resource.size.width());
   DCHECK_EQ(size_.Height(), transferable_resource.size.height());
 
-  // Make our own textureId that is a reference on the same texture backing
-  // being used as the front buffer (which was returned from
-  // PrepareTransferableResourceInternal()). We do not need to wait on the sync
-  // token in |transferable_resource| since the mailbox was produced on the same
-  // |m_gl| context that we are using here. Similarly, the |release_callback|
-  // will run on the same context so we don't need to send a sync token for this
-  // consume action back to it.
-  // TODO(danakj): Instead of using PrepareTransferableResourceInternal(), we
-  // could just use the actual texture id and avoid needing to produce/consume a
-  // mailbox.
-  GLuint texture_id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(
-      transferable_resource.mailbox_holder.mailbox.name);
-
-  if (out_release_callback) {
-    // Allow the consumer to release the resource when done using it, so it can
-    // be recycled.
-    *out_release_callback = std::move(release_callback);
-  } else {
-    // Return the mailbox but report that the resource is lost to prevent trying
-    // to use the backing for future frames. We keep it alive with our own
-    // reference to the backing via our |textureId|.
-    gpu::SyncToken sync_token;
-    gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
-    release_callback->Run(sync_token, true /* lost_resource */);
-  }
-
   // We reuse the same mailbox name from above since our texture id was consumed
   // from it.
   const auto& sk_image_mailbox = transferable_resource.mailbox_holder.mailbox;
@@ -620,12 +606,18 @@
   const auto& sk_image_sync_token =
       transferable_resource.mailbox_holder.sync_token;
 
+  const SkImageInfo sk_image_info =
+      SkImageInfo::MakeN32Premul(size_.Width(), size_.Height());
+
   // TODO(xidachen): Create a small pool of recycled textures from
   // ImageBitmapRenderingContext's transferFromImageBitmap, and try to use them
   // in DrawingBuffer.
-  return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
-      sk_image_mailbox, sk_image_sync_token, texture_id,
-      context_provider_->GetWeakPtr(), size_, opengl_flip_y_extension_);
+  return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
+      sk_image_mailbox, sk_image_sync_token, /* shared_image_texture_id = */ 0,
+      sk_image_info, transferable_resource.mailbox_holder.texture_target,
+      /* is_origin_top_left = */ opengl_flip_y_extension_,
+      context_provider_->GetWeakPtr(), base::PlatformThread::CurrentRef(),
+      Thread::Current()->GetTaskRunner(), std::move(release_callback));
 }
 
 scoped_refptr<DrawingBuffer::ColorBuffer>
@@ -675,18 +667,33 @@
 }
 
 DrawingBuffer::ColorBuffer::ColorBuffer(
-    DrawingBuffer* drawing_buffer,
+    base::WeakPtr<DrawingBuffer> drawing_buffer,
     const IntSize& size,
     GLuint texture_id,
     std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
     gpu::Mailbox mailbox)
-    : drawing_buffer(drawing_buffer),
+    : owning_thread_ref(base::PlatformThread::CurrentRef()),
+      drawing_buffer(std::move(drawing_buffer)),
       size(size),
       texture_id(texture_id),
       gpu_memory_buffer(std::move(gpu_memory_buffer)),
       mailbox(mailbox) {}
 
 DrawingBuffer::ColorBuffer::~ColorBuffer() {
+  if (base::PlatformThread::CurrentRef() != owning_thread_ref ||
+      !drawing_buffer) {
+    // If the owning thread or the drawing buffer has been torn down, then the
+    // GL context and its associated resources will be destroyed with this
+    // context. The only resource we need to explicitly clean up is the shared
+    // image mailbox.
+    if (auto shared_context = SharedGpuContext::ContextProviderWrapper()) {
+      shared_context->ContextProvider()
+          ->SharedImageInterface()
+          ->DestroySharedImage(receive_sync_token, mailbox);
+    }
+    return;
+  }
+
   gpu::gles2::GLES2Interface* gl = drawing_buffer->gl_;
   gpu::SharedImageInterface* sii =
       drawing_buffer->ContextProvider()->SharedImageInterface();
@@ -1625,8 +1632,9 @@
     // Import frontbuffer of swap chain into GL.
     texture_id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(
         front_buffer_mailbox.name);
-    front_color_buffer_ = base::AdoptRef(
-        new ColorBuffer(this, size, texture_id, nullptr, front_buffer_mailbox));
+    front_color_buffer_ = base::MakeRefCounted<ColorBuffer>(
+        weak_factory_.GetWeakPtr(), size, texture_id, nullptr,
+        front_buffer_mailbox);
   }
   // Import the backbuffer of swap chain or allocated SharedImage into GL.
   texture_id =
@@ -1653,9 +1661,9 @@
     gl_->DeleteFramebuffers(1, &fbo);
   }
 
-  return base::AdoptRef(new ColorBuffer(this, size, texture_id,
-                                        std::move(gpu_memory_buffer),
-                                        back_buffer_mailbox));
+  return base::MakeRefCounted<ColorBuffer>(
+      weak_factory_.GetWeakPtr(), size, texture_id,
+      std::move(gpu_memory_buffer), back_buffer_mailbox);
 }
 
 void DrawingBuffer::AttachColorBufferToReadFramebuffer() {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index c9ab3f2..227be51e 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -228,10 +228,7 @@
   // contents of the front buffer. This is done without any pixel copies. The
   // texture in the ImageBitmap is from the active ContextProvider on the
   // DrawingBuffer.
-  // If out_release_callback is null, the image is discarded.  If it is non-null
-  // the image must be recycled or discarded by calling *out_release_callback.
-  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(
-      std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage();
 
   bool CopyToPlatformTexture(gpu::gles2::GLES2Interface*,
                              GLenum dst_target,
@@ -356,18 +353,22 @@
     bool pixel_pack_buffer_binding_dirty_ = false;
   };
 
-  struct ColorBuffer : public RefCounted<ColorBuffer> {
-    ColorBuffer(DrawingBuffer*,
+  struct ColorBuffer : public base::RefCountedThreadSafe<ColorBuffer> {
+    ColorBuffer(base::WeakPtr<DrawingBuffer> drawing_buffer,
                 const IntSize&,
                 GLuint texture_id,
                 std::unique_ptr<gfx::GpuMemoryBuffer>,
                 gpu::Mailbox mailbox);
     ~ColorBuffer();
 
+    // The thread on which the ColorBuffer is created and the DrawingBuffer is
+    // bound to.
+    const base::PlatformThreadRef owning_thread_ref;
+
     // The owning DrawingBuffer. Note that DrawingBuffer is explicitly destroyed
     // by the beginDestruction method, which will eventually drain all of its
     // ColorBuffers.
-    scoped_refptr<DrawingBuffer> drawing_buffer;
+    base::WeakPtr<DrawingBuffer> drawing_buffer;
     const IntSize size;
     const GLuint texture_id = 0;
     std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
@@ -430,8 +431,10 @@
 
   // Callbacks for mailboxes given to the compositor from
   // FinishPrepareTransferableResource{Gpu,Software}.
+  static void NotifyMailboxReleasedGpu(scoped_refptr<ColorBuffer>,
+                                       const gpu::SyncToken&,
+                                       bool lost_resource);
   void MailboxReleasedGpu(scoped_refptr<ColorBuffer>,
-                          const gpu::SyncToken&,
                           bool lost_resource);
   void MailboxReleasedSoftware(RegisteredBitmap,
                                const gpu::SyncToken&,
@@ -630,6 +633,8 @@
   const gl::GpuPreference initial_gpu_;
   gl::GpuPreference current_active_gpu_;
 
+  base::WeakPtrFactory<DrawingBuffer> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(DrawingBuffer);
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
index 19d15ce..537c1af 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
@@ -203,9 +203,8 @@
   drawing_buffer_->BeginDestruction();
 }
 
-TEST_F(DrawingBufferTest, VerifyDestructionCompleteAfterAllResourceReleased) {
-  bool live = true;
-  drawing_buffer_->live_ = &live;
+TEST_F(DrawingBufferTest, VerifySharedImagesReleasedAfterReleaseCallback) {
+  auto* sii = drawing_buffer_->SharedImageInterfaceForTests();
 
   viz::TransferableResource resource1;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
@@ -231,67 +230,20 @@
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
                                                            &release_callback3));
 
-  EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
-  release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
-
-  drawing_buffer_->BeginDestruction();
-  ASSERT_EQ(live, true);
-
-  DrawingBufferForTests* raw_pointer = drawing_buffer_.get();
-  drawing_buffer_ = nullptr;
-  ASSERT_EQ(live, true);
-
-  EXPECT_FALSE(raw_pointer->MarkContentsChanged());
-  release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
-  ASSERT_EQ(live, true);
-
-  EXPECT_FALSE(raw_pointer->MarkContentsChanged());
-  release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
-  ASSERT_EQ(live, false);
-}
-
-TEST_F(DrawingBufferTest, verifyDrawingBufferStaysAliveIfResourcesAreLost) {
-  bool live = true;
-  drawing_buffer_->live_ = &live;
-
-  viz::TransferableResource resource1;
-  std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
-  viz::TransferableResource resource2;
-  std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
-  viz::TransferableResource resource3;
-  std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
-
-  EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
-  EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
-                                                           &release_callback1));
-  VerifyStateWasRestored();
-  EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
-  EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
-                                                           &release_callback2));
-  VerifyStateWasRestored();
-  EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
-  EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
-                                                           &release_callback3));
-  VerifyStateWasRestored();
+  EXPECT_EQ(sii->shared_image_count(), 4u);
 
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   release_callback1->Run(gpu::SyncToken(), true /* lostResource */);
-  EXPECT_EQ(live, true);
+  EXPECT_EQ(sii->shared_image_count(), 3u);
+
+  release_callback2->Run(gpu::SyncToken(), true /* lostResource */);
+  EXPECT_EQ(sii->shared_image_count(), 2u);
+
+  // The resource is not marked lost so it's recycled after the callback.
+  release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+  EXPECT_EQ(sii->shared_image_count(), 2u);
 
   drawing_buffer_->BeginDestruction();
-  EXPECT_EQ(live, true);
-
-  EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
-  release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
-  EXPECT_EQ(live, true);
-
-  DrawingBufferForTests* raw_ptr = drawing_buffer_.get();
-  drawing_buffer_ = nullptr;
-  EXPECT_EQ(live, true);
-
-  EXPECT_FALSE(raw_ptr->MarkContentsChanged());
-  release_callback3->Run(gpu::SyncToken(), true /* lostResource */);
-  EXPECT_EQ(live, false);
 }
 
 TEST_F(DrawingBufferTest, VerifyOnlyOneRecycledResourceMustBeKept) {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
index 63b92488..8e66f9b 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
@@ -18,9 +18,7 @@
 
 XRFrameTransport::XRFrameTransport() : submit_frame_client_receiver_(this) {}
 
-XRFrameTransport::~XRFrameTransport() {
-  CallPreviousFrameCallback();
-}
+XRFrameTransport::~XRFrameTransport() = default;
 
 void XRFrameTransport::PresentChange() {
   frame_copier_ = nullptr;
@@ -79,13 +77,6 @@
   }
 }
 
-void XRFrameTransport::CallPreviousFrameCallback() {
-  if (previous_image_release_callback_) {
-    previous_image_release_callback_->Run(gpu::SyncToken(), false);
-    previous_image_release_callback_ = nullptr;
-  }
-}
-
 void XRFrameTransport::FrameSubmitMissing(
     device::mojom::blink::XRPresentationProvider* vr_presentation_provider,
     gpu::gles2::GLES2Interface* gl,
@@ -101,7 +92,6 @@
     gpu::gles2::GLES2Interface* gl,
     DrawingBuffer::Client* drawing_buffer_client,
     scoped_refptr<Image> image_ref,
-    std::unique_ptr<viz::SingleReleaseCallback> release_callback,
     int16_t vr_frame_id) {
   DCHECK(transport_options_);
 
@@ -114,8 +104,6 @@
     // without waiting.
     if (transport_options_->wait_for_transfer_notification)
       WaitForPreviousTransfer();
-    CallPreviousFrameCallback();
-    previous_image_release_callback_ = std::move(release_callback);
     if (!frame_copier_ || !last_transfer_succeeded_) {
       frame_copier_ = std::make_unique<GpuMemoryBufferImageCopy>(gl);
     }
@@ -173,8 +161,6 @@
     if (transport_options_->wait_for_transfer_notification)
       WaitForPreviousTransfer();
     previous_image_ = std::move(image_ref);
-    CallPreviousFrameCallback();
-    previous_image_release_callback_ = std::move(release_callback);
 
     // Create mailbox and sync token for transfer.
     TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::GetMailbox");
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
index f0fa052..4a094aa 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
@@ -52,7 +52,6 @@
                    gpu::gles2::GLES2Interface*,
                    DrawingBuffer::Client*,
                    scoped_refptr<Image> image_ref,
-                   std::unique_ptr<viz::SingleReleaseCallback>,
                    int16_t vr_frame_id);
 
   void FrameSubmitMissing(device::mojom::blink::XRPresentationProvider*,
@@ -65,7 +64,6 @@
   void WaitForPreviousTransfer();
   base::TimeDelta WaitForPreviousRenderToFinish();
   base::TimeDelta WaitForGpuFenceReceived();
-  void CallPreviousFrameCallback();
 
   // XRPresentationClient
   void OnSubmitFrameTransferred(bool success) override;
@@ -78,7 +76,6 @@
   // Used to keep the image alive until the next frame if using
   // waitForPreviousTransferToFinish.
   scoped_refptr<Image> previous_image_;
-  std::unique_ptr<viz::SingleReleaseCallback> previous_image_release_callback_;
 
   bool waiting_for_previous_frame_transfer_ = false;
   bool last_transfer_succeeded_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
index f466089..b3c914c 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -25,16 +26,31 @@
 // some of the common bits into a base class?
 
 XRWebGLDrawingBuffer::ColorBuffer::ColorBuffer(
-    XRWebGLDrawingBuffer* drawing_buffer,
+    base::WeakPtr<XRWebGLDrawingBuffer> drawing_buffer,
     const IntSize& size,
     const gpu::Mailbox& mailbox,
     GLuint texture_id)
-    : drawing_buffer(drawing_buffer),
+    : owning_thread_ref(base::PlatformThread::CurrentRef()),
+      drawing_buffer(std::move(drawing_buffer)),
       size(size),
       texture_id(texture_id),
       mailbox(mailbox) {}
 
 XRWebGLDrawingBuffer::ColorBuffer::~ColorBuffer() {
+  if (base::PlatformThread::CurrentRef() != owning_thread_ref ||
+      !drawing_buffer) {
+    // If the owning thread or the drawing buffer has been torn down, then the
+    // GL context and its associated resources will be destroyed with this
+    // context. The only resource we need to explicitly clean up is the shared
+    // image mailbox.
+    if (auto shared_context = SharedGpuContext::ContextProviderWrapper()) {
+      shared_context->ContextProvider()
+          ->SharedImageInterface()
+          ->DestroySharedImage(receive_sync_token, mailbox);
+    }
+    return;
+  }
+
   gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL();
   if (receive_sync_token.HasData())
     gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData());
@@ -105,68 +121,6 @@
   return xr_drawing_buffer;
 }
 
-void XRWebGLDrawingBuffer::MirrorClient::OnMirrorImageAvailable(
-    scoped_refptr<StaticBitmapImage> image,
-    std::unique_ptr<viz::SingleReleaseCallback> callback) {
-  // Replace the next image if we have one already.
-  if (next_image_ && next_release_callback_) {
-    next_release_callback_->Run(gpu::SyncToken(), false);
-  }
-
-  // Set our new image.
-  next_image_ = image;
-  next_release_callback_ = std::move(callback);
-}
-
-void XRWebGLDrawingBuffer::MirrorClient::BeginDestruction() {
-  // Call all callbacks we have to clean up associated resources.  For
-  // next_release_callback_, we report the previous image as "not lost", meaning
-  // we can reuse the texture/image.  For previous_release_callback_
-  // and current_release_callback_, we report the image as lost, because we
-  // don't know if the consumer is still using them, so they should not be
-  // reused.
-  if (previous_release_callback_) {
-    previous_release_callback_->Run(gpu::SyncToken(), true);
-    previous_release_callback_ = nullptr;
-  }
-
-  if (current_release_callback_) {
-    current_release_callback_->Run(gpu::SyncToken(), true);
-    current_release_callback_ = nullptr;
-  }
-
-  if (next_release_callback_) {
-    next_release_callback_->Run(gpu::SyncToken(), false);
-    next_release_callback_ = nullptr;
-  }
-
-  next_image_ = nullptr;
-}
-
-scoped_refptr<StaticBitmapImage>
-XRWebGLDrawingBuffer::MirrorClient::GetLastImage() {
-  if (!next_image_)
-    return nullptr;
-
-  scoped_refptr<StaticBitmapImage> ret = next_image_;
-  next_image_ = nullptr;
-  DCHECK(!previous_release_callback_);
-  previous_release_callback_ = std::move(current_release_callback_);
-  DCHECK(!current_release_callback_);
-  current_release_callback_ = std::move(next_release_callback_);
-  return ret;
-}
-
-void XRWebGLDrawingBuffer::MirrorClient::CallLastReleaseCallback() {
-  if (previous_release_callback_)
-    previous_release_callback_->Run(gpu::SyncToken(), false);
-  previous_release_callback_ = nullptr;
-}
-
-XRWebGLDrawingBuffer::MirrorClient::~MirrorClient() {
-  BeginDestruction();
-}
-
 XRWebGLDrawingBuffer::XRWebGLDrawingBuffer(DrawingBuffer* drawing_buffer,
                                            GLuint framebuffer,
                                            bool discard_framebuffer_supported,
@@ -178,11 +132,10 @@
       discard_framebuffer_supported_(discard_framebuffer_supported),
       depth_(want_depth_buffer),
       stencil_(want_stencil_buffer),
-      alpha_(want_alpha_channel) {}
+      alpha_(want_alpha_channel),
+      weak_factory_(this) {}
 
 void XRWebGLDrawingBuffer::BeginDestruction() {
-  mirror_client_ = nullptr;
-
   if (back_color_buffer_) {
     gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
     gl->EndSharedImageAccessDirectCHROMIUM(back_color_buffer_->texture_id);
@@ -242,18 +195,6 @@
   return drawing_buffer_->ContextGL();
 }
 
-void XRWebGLDrawingBuffer::SetMirrorClient(scoped_refptr<MirrorClient> client) {
-  mirror_client_ = client;
-  if (mirror_client_) {
-    // Immediately send a black 1x1 image to the mirror client to ensure that
-    // it has content to show.
-    sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(1, 1);
-    mirror_client_->OnMirrorImageAvailable(
-        UnacceleratedStaticBitmapImage::Create(surface->makeImageSnapshot()),
-        nullptr);
-  }
-}
-
 bool XRWebGLDrawingBuffer::ContextLost() {
   return drawing_buffer_->destroyed();
 }
@@ -523,7 +464,8 @@
   DrawingBuffer::Client* client = drawing_buffer_->client();
   client->DrawingBufferClientRestoreTexture2DBinding();
 
-  return base::AdoptRef(new ColorBuffer(this, size_, mailbox, texture_id));
+  return base::MakeRefCounted<ColorBuffer>(weak_factory_.GetWeakPtr(), size_,
+                                           mailbox, texture_id);
 }
 
 scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer>
@@ -626,8 +568,7 @@
 }
 
 scoped_refptr<StaticBitmapImage>
-XRWebGLDrawingBuffer::TransferToStaticBitmapImage(
-    std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+XRWebGLDrawingBuffer::TransferToStaticBitmapImage() {
   gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
   scoped_refptr<ColorBuffer> buffer;
   bool success = false;
@@ -661,47 +602,45 @@
   // This holds a ref on the XRWebGLDrawingBuffer that will keep it alive
   // until the mailbox is released (and while the callback is running).
   auto func =
-      WTF::Bind(mirror_client_ ? &XRWebGLDrawingBuffer::MailboxReleasedToMirror
-                               : &XRWebGLDrawingBuffer::MailboxReleased,
-                scoped_refptr<XRWebGLDrawingBuffer>(this), buffer);
+      base::BindOnce(&XRWebGLDrawingBuffer::NotifyMailboxReleased, buffer);
 
   std::unique_ptr<viz::SingleReleaseCallback> release_callback =
       viz::SingleReleaseCallback::Create(std::move(func));
+  const SkImageInfo sk_image_info =
+      SkImageInfo::MakeN32Premul(size_.Width(), size_.Height());
 
-  // Make our own textureId that is a reference on the same shared image being
-  // used as the front buffer.  We do not need a
-  // Begin/EndSharedImageAccessDirectCHROMIUM as the texture id is just for
-  // lifetime, not actual access.  We do not need to wait on the sync token
-  // since the GL context has already waited on it.  Similarly, the release
-  // callback will run on the same context so we don't need to send a sync token
-  // for this consume action back to it.
-  GLuint texture_id =
-      gl->CreateAndTexStorage2DSharedImageCHROMIUM(buffer->mailbox.name);
+  return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
+      buffer->mailbox, buffer->produce_sync_token,
+      /* shared_image_texture_id = */ 0, sk_image_info, GL_TEXTURE_2D,
+      /* is_origin_top_left = */ false,
+      drawing_buffer_->ContextProviderWeakPtr(),
+      base::PlatformThread::CurrentRef(), Thread::Current()->GetTaskRunner(),
+      std::move(release_callback));
+}
 
-  if (out_release_callback) {
-    *out_release_callback = std::move(release_callback);
-  } else {
-    release_callback->Run(gpu::SyncToken(), true /* lost_resource */);
+// static
+void XRWebGLDrawingBuffer::NotifyMailboxReleased(
+    scoped_refptr<ColorBuffer> color_buffer,
+    const gpu::SyncToken& sync_token,
+    bool lost_resource) {
+  DCHECK(color_buffer->owning_thread_ref == base::PlatformThread::CurrentRef());
+
+  // Update the SyncToken to ensure that we will wait for it even if we
+  // immediately destroy this buffer.
+  color_buffer->receive_sync_token = sync_token;
+  if (color_buffer->drawing_buffer) {
+    color_buffer->drawing_buffer->MailboxReleased(color_buffer, lost_resource);
   }
-
-  return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
-      buffer->mailbox, buffer->produce_sync_token, texture_id,
-      drawing_buffer_->ContextProviderWeakPtr(), size_, false);
 }
 
 void XRWebGLDrawingBuffer::MailboxReleased(
     scoped_refptr<ColorBuffer> color_buffer,
-    const gpu::SyncToken& sync_token,
     bool lost_resource) {
   // If the mailbox has been returned by the compositor then it is no
   // longer being presented, and so is no longer the front buffer.
   if (color_buffer == front_color_buffer_)
     front_color_buffer_ = nullptr;
 
-  // Update the SyncToken to ensure that we will wait for it even if we
-  // immediately destroy this buffer.
-  color_buffer->receive_sync_token = sync_token;
-
   if (drawing_buffer_->destroyed() || color_buffer->size != size_ ||
       lost_resource) {
     return;
@@ -714,35 +653,4 @@
   recycled_color_buffer_queue_.push_front(color_buffer);
 }
 
-void XRWebGLDrawingBuffer::MailboxReleasedToMirror(
-    scoped_refptr<ColorBuffer> color_buffer,
-    const gpu::SyncToken& sync_token,
-    bool lost_resource) {
-  if (!mirror_client_ || lost_resource) {
-    MailboxReleased(std::move(color_buffer), sync_token, lost_resource);
-    return;
-  }
-
-  gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
-  color_buffer->receive_sync_token = sync_token;
-
-  auto func =
-      WTF::Bind(&XRWebGLDrawingBuffer::MailboxReleased,
-                scoped_refptr<XRWebGLDrawingBuffer>(this), color_buffer);
-
-  std::unique_ptr<viz::SingleReleaseCallback> release_callback =
-      viz::SingleReleaseCallback::Create(std::move(func));
-
-  GLuint texture_id =
-      gl->CreateAndTexStorage2DSharedImageCHROMIUM(color_buffer->mailbox.name);
-
-  scoped_refptr<StaticBitmapImage> image =
-      AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
-          color_buffer->mailbox, color_buffer->produce_sync_token, texture_id,
-          drawing_buffer_->ContextProviderWeakPtr(), color_buffer->size, false);
-
-  mirror_client_->OnMirrorImageAvailable(std::move(image),
-                                         std::move(release_callback));
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
index 44649d8..26f28d0c 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
@@ -43,28 +43,7 @@
 
   void Resize(const IntSize&);
 
-  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(
-      std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
-
-  class PLATFORM_EXPORT MirrorClient : public RefCounted<MirrorClient> {
-   public:
-    void OnMirrorImageAvailable(scoped_refptr<StaticBitmapImage>,
-                                std::unique_ptr<viz::SingleReleaseCallback>);
-
-    void BeginDestruction();
-    scoped_refptr<StaticBitmapImage> GetLastImage();
-    void CallLastReleaseCallback();
-
-    ~MirrorClient();
-
-   private:
-    scoped_refptr<StaticBitmapImage> next_image_;
-    std::unique_ptr<viz::SingleReleaseCallback> next_release_callback_;
-    std::unique_ptr<viz::SingleReleaseCallback> current_release_callback_;
-    std::unique_ptr<viz::SingleReleaseCallback> previous_release_callback_;
-  };
-
-  void SetMirrorClient(scoped_refptr<MirrorClient> mirror_client);
+  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage();
 
   void UseSharedBuffer(const gpu::MailboxHolder&);
   void DoneWithSharedBuffer();
@@ -75,17 +54,22 @@
   void BeginDestruction();
 
  private:
-  struct PLATFORM_EXPORT ColorBuffer : public RefCounted<ColorBuffer> {
-    ColorBuffer(XRWebGLDrawingBuffer*,
+  struct PLATFORM_EXPORT ColorBuffer
+      : public base::RefCountedThreadSafe<ColorBuffer> {
+    ColorBuffer(base::WeakPtr<XRWebGLDrawingBuffer>,
                 const IntSize&,
                 const gpu::Mailbox& mailbox,
                 GLuint texture_id);
     ~ColorBuffer();
 
+    // The thread on which the ColorBuffer is created and the DrawingBuffer is
+    // bound to.
+    const base::PlatformThreadRef owning_thread_ref;
+
     // The owning XRWebGLDrawingBuffer. Note that DrawingBuffer is explicitly
     // destroyed by the BeginDestruction method, which will eventually drain all
     // of its ColorBuffers.
-    scoped_refptr<XRWebGLDrawingBuffer> drawing_buffer;
+    base::WeakPtr<XRWebGLDrawingBuffer> drawing_buffer;
     const IntSize size;
 
     // The id of the texture that imports the shared image into the
@@ -126,12 +110,10 @@
 
   void ClearBoundFramebuffer();
 
-  void MailboxReleased(scoped_refptr<ColorBuffer>,
-                       const gpu::SyncToken&,
-                       bool lost_resource);
-  void MailboxReleasedToMirror(scoped_refptr<ColorBuffer>,
-                               const gpu::SyncToken&,
-                               bool lost_resource);
+  static void NotifyMailboxReleased(scoped_refptr<ColorBuffer>,
+                                    const gpu::SyncToken&,
+                                    bool lost_resource);
+  void MailboxReleased(scoped_refptr<ColorBuffer>, bool lost_resource);
 
   // Reference to the DrawingBuffer that owns the GL context for this object.
   scoped_refptr<DrawingBuffer> drawing_buffer_;
@@ -174,7 +156,7 @@
   int max_texture_size_ = 0;
   int sample_count_ = 0;
 
-  scoped_refptr<MirrorClient> mirror_client_;
+  base::WeakPtrFactory<XRWebGLDrawingBuffer> weak_factory_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc b/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc
index 6e0c7398..a263542 100644
--- a/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc
@@ -101,6 +101,10 @@
     gl_->EndSharedImageAccessDirectCHROMIUM(source_texture_id);
   gl_->DeleteTextures(1, &source_texture_id);
 
+  gpu::SyncToken copy_done_sync_token;
+  gl_->GenSyncTokenCHROMIUM(copy_done_sync_token.GetData());
+  static_image->UpdateSyncToken(copy_done_sync_token);
+
   // Cleanup the draw framebuffer, associated image and texture.
   gl_->BindTexture(target, dest_texture_id);
   gl_->ReleaseTexImage2DCHROMIUM(target, image_id);
diff --git a/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc b/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc
index 17aa70c1..a9a52b3 100644
--- a/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc
+++ b/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc
@@ -14,55 +14,10 @@
 
 namespace blink {
 
-namespace {
-
-void ReleaseTexture(
-    bool is_converted_from_skia_texture,
-    unsigned texture_id,
-    std::unique_ptr<gpu::Mailbox> mailbox,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider,
-    std::unique_ptr<gpu::SyncToken> sync_token) {
-  if (!is_converted_from_skia_texture && texture_id && context_provider) {
-    context_provider->ContextProvider()->ContextGL()->WaitSyncTokenCHROMIUM(
-        sync_token->GetData());
-    context_provider->ContextProvider()->ContextGL()->DeleteTextures(
-        1, &texture_id);
-  }
-}
-
-}  // namespace
-
 MailboxTextureHolder::MailboxTextureHolder(
     const gpu::Mailbox& mailbox,
-    const gpu::SyncToken& sync_token,
-    unsigned texture_id_to_delete_after_mailbox_consumed,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
-        context_provider_wrapper,
-    IntSize mailbox_size,
-    bool is_origin_top_left)
-    : TextureHolder(std::move(context_provider_wrapper),
-                    base::MakeRefCounted<MailboxRef>(nullptr),
-                    is_origin_top_left),
-      mailbox_(mailbox),
-      texture_id_(texture_id_to_delete_after_mailbox_consumed),
-      is_converted_from_skia_texture_(false),
-      thread_id_(0),
-      sk_image_info_(SkImageInfo::MakeN32Premul(mailbox_size.Width(),
-                                                mailbox_size.Height())),
-      texture_target_(GL_TEXTURE_2D) {
-  DCHECK(mailbox_.IsSharedImage());
-
-  mailbox_ref()->set_sync_token(sync_token);
-  InitCommon();
-}
-
-MailboxTextureHolder::MailboxTextureHolder(
-    const gpu::Mailbox& mailbox,
-    const gpu::SyncToken& sync_token,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
-        context_provider_wrapper,
+    base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
     scoped_refptr<MailboxRef> mailbox_ref,
-    PlatformThreadId context_thread_id,
     const SkImageInfo& sk_image_info,
     GLenum texture_target,
     bool is_origin_top_left)
@@ -70,16 +25,9 @@
                     std::move(mailbox_ref),
                     is_origin_top_left),
       mailbox_(mailbox),
-      texture_id_(0),
-      is_converted_from_skia_texture_(false),
-      thread_id_(context_thread_id),
       sk_image_info_(sk_image_info),
       texture_target_(texture_target) {
-  DCHECK(thread_id_);
-  DCHECK(!IsCrossThread() || sync_token.verified_flush());
   DCHECK(mailbox_.IsSharedImage());
-
-  this->mailbox_ref()->set_sync_token(sync_token);
 }
 
 void MailboxTextureHolder::Sync(MailboxSyncMode mode) {
@@ -133,12 +81,6 @@
   }
 }
 
-void MailboxTextureHolder::InitCommon() {
-  DCHECK(!thread_id_);
-  thread_id_ = base::PlatformThread::CurrentId();
-  texture_thread_task_runner_ = Thread::Current()->GetTaskRunner();
-}
-
 bool MailboxTextureHolder::IsValid() const {
   if (IsCrossThread()) {
     // If context is is from another thread, validity cannot be verified.
@@ -149,29 +91,9 @@
 }
 
 bool MailboxTextureHolder::IsCrossThread() const {
-  return thread_id_ != base::PlatformThread::CurrentId();
+  return mailbox_ref()->is_cross_thread();
 }
 
-MailboxTextureHolder::~MailboxTextureHolder() {
-  std::unique_ptr<gpu::SyncToken> passed_sync_token(
-      new gpu::SyncToken(mailbox_ref()->sync_token()));
-  std::unique_ptr<gpu::Mailbox> passed_mailbox(new gpu::Mailbox(mailbox_));
-
-  if (texture_thread_task_runner_ && IsCrossThread()) {
-    PostCrossThreadTask(
-        *texture_thread_task_runner_, FROM_HERE,
-        CrossThreadBindOnce(&ReleaseTexture, is_converted_from_skia_texture_,
-                            texture_id_, WTF::Passed(std::move(passed_mailbox)),
-                            WTF::Passed(ContextProviderWrapper()),
-                            WTF::Passed(std::move(passed_sync_token))));
-  } else {
-    ReleaseTexture(is_converted_from_skia_texture_, texture_id_,
-                   std::move(passed_mailbox), ContextProviderWrapper(),
-                   std::move(passed_sync_token));
-  }
-
-  texture_id_ = 0u;  // invalidate the texture.
-  texture_thread_task_runner_ = nullptr;
-}
+MailboxTextureHolder::~MailboxTextureHolder() = default;
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h b/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h
index 45e1902..452965d9 100644
--- a/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h
+++ b/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h
@@ -40,35 +40,18 @@
   GLenum texture_target() const { return texture_target_; }
 
   void Sync(MailboxSyncMode);
-  // In WebGL's commit or transferToImageBitmap calls, it will call the
-  // DrawingBuffer::transferToStaticBitmapImage function, which produces the
-  // input parameters for this method.
-  MailboxTextureHolder(const gpu::Mailbox&,
-                       const gpu::SyncToken&,
-                       unsigned texture_id_to_delete_after_mailbox_consumed,
-                       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
-                       IntSize mailbox_size,
-                       bool is_origin_top_left);
   // This function may be used when the MailboxTextureHolder is created on a
   // different thread. The caller must provide a verified sync token if it is
   // created cross-thread.
   MailboxTextureHolder(const gpu::Mailbox&,
-                       const gpu::SyncToken&,
-                       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
+                       base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
                        scoped_refptr<MailboxRef> mailbox_ref,
-                       PlatformThreadId context_thread_id,
                        const SkImageInfo& sk_image_info,
                        GLenum texture_target,
                        bool is_origin_top_left);
 
  private:
-  void InitCommon();
-
   gpu::Mailbox mailbox_;
-  unsigned texture_id_;
-  bool is_converted_from_skia_texture_;
-  scoped_refptr<base::SingleThreadTaskRunner> texture_thread_task_runner_;
-  base::PlatformThreadId thread_id_;
   bool did_issue_ordering_barrier_ = false;
   SkImageInfo sk_image_info_;
   GLenum texture_target_;
diff --git a/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc b/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc
index 8b7181e..531f2b37 100644
--- a/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc
+++ b/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc
@@ -17,11 +17,6 @@
 namespace blink {
 
 namespace {
-bool IsSkImageOriginTopLeft(sk_sp<SkImage> image) {
-  GrSurfaceOrigin origin;
-  image->getBackendTexture(false, &origin);
-  return origin == kTopLeft_GrSurfaceOrigin;
-}
 
 struct ReleaseContext {
   scoped_refptr<TextureHolder::MailboxRef> mailbox_ref;
@@ -46,15 +41,6 @@
 }  // namespace
 
 SkiaTextureHolder::SkiaTextureHolder(
-    sk_sp<SkImage> image,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
-        context_provider_wrapper)
-    : TextureHolder(std::move(context_provider_wrapper),
-                    base::MakeRefCounted<MailboxRef>(nullptr),
-                    IsSkImageOriginTopLeft(image)),
-      image_(std::move(image)) {}
-
-SkiaTextureHolder::SkiaTextureHolder(
     const MailboxTextureHolder* mailbox_texture_holder,
     GLuint shared_image_texture_id)
     : TextureHolder(SharedGpuContext::ContextProviderWrapper(),
diff --git a/third_party/blink/renderer/platform/graphics/skia_texture_holder.h b/third_party/blink/renderer/platform/graphics/skia_texture_holder.h
index 4aa25299..75de7d9 100644
--- a/third_party/blink/renderer/platform/graphics/skia_texture_holder.h
+++ b/third_party/blink/renderer/platform/graphics/skia_texture_holder.h
@@ -32,11 +32,6 @@
 
   const sk_sp<SkImage>& GetSkImage() const { return image_; }
 
-  // When creating a AcceleratedStaticBitmap from a texture-backed SkImage, this
-  // function will be called to create a TextureHolder object.
-  SkiaTextureHolder(sk_sp<SkImage>,
-                    base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&);
-
   // This function consumes the mailbox in the input parameter and turn it into
   // a texture-backed SkImage.
   // |shared_image_texture_id| is an optional texture bound to the
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
index 7f55e36..cd29634 100644
--- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
@@ -89,6 +89,7 @@
     return mailbox;
   }
   virtual const gpu::SyncToken& GetSyncToken() const;
+  virtual void UpdateSyncToken(const gpu::SyncToken&) {}
   virtual bool IsPremultiplied() const { return true; }
 
   // Methods have exactly the same implementation for all sub-classes
diff --git a/third_party/blink/renderer/platform/graphics/texture_holder.cc b/third_party/blink/renderer/platform/graphics/texture_holder.cc
new file mode 100644
index 0000000..48ca1ca
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/texture_holder.cc
@@ -0,0 +1,39 @@
+// 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 "third_party/blink/renderer/platform/graphics/texture_holder.h"
+
+namespace blink {
+
+namespace {
+void ReleaseCallbackOnContextThread(
+    std::unique_ptr<viz::SingleReleaseCallback> callback,
+    const gpu::SyncToken sync_token) {
+  callback->Run(sync_token, /* is_lost = */ false);
+}
+}  // namespace
+
+TextureHolder::MailboxRef::MailboxRef(
+    const gpu::SyncToken& sync_token,
+    base::PlatformThreadRef context_thread_ref,
+    scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
+    std::unique_ptr<viz::SingleReleaseCallback> release_callback)
+    : sync_token_(sync_token),
+      context_thread_ref_(context_thread_ref),
+      context_task_runner_(std::move(context_task_runner)),
+      release_callback_(std::move(release_callback)) {
+  DCHECK(!is_cross_thread() || sync_token_.verified_flush());
+}
+
+TextureHolder::MailboxRef::~MailboxRef() {
+  if (context_thread_ref_ == base::PlatformThread::CurrentRef()) {
+    ReleaseCallbackOnContextThread(std::move(release_callback_), sync_token_);
+  } else {
+    context_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&ReleaseCallbackOnContextThread,
+                                  std::move(release_callback_), sync_token_));
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/texture_holder.h b/third_party/blink/renderer/platform/graphics/texture_holder.h
index a1ba07e2..5a79d5b4 100644
--- a/third_party/blink/renderer/platform/graphics/texture_holder.h
+++ b/third_party/blink/renderer/platform/graphics/texture_holder.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEXTURE_HOLDER_H_
 
 #include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
 #include "components/viz/common/resources/single_release_callback.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/sync_token.h"
@@ -24,19 +25,22 @@
  public:
   class MailboxRef : public ThreadSafeRefCounted<MailboxRef> {
    public:
-    explicit MailboxRef(
-        std::unique_ptr<viz::SingleReleaseCallback> release_callback)
-        : release_callback_(std::move(release_callback)) {}
-    ~MailboxRef() {
-      if (release_callback_)
-        release_callback_->Run(sync_token_, false /* resource_lost */);
-    }
+    MailboxRef(const gpu::SyncToken& sync_token,
+               base::PlatformThreadRef context_thread_ref,
+               scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
+               std::unique_ptr<viz::SingleReleaseCallback> release_callback);
+    ~MailboxRef();
 
+    bool is_cross_thread() const {
+      return base::PlatformThread::CurrentRef() != context_thread_ref_;
+    }
     void set_sync_token(gpu::SyncToken token) { sync_token_ = token; }
     const gpu::SyncToken& sync_token() const { return sync_token_; }
 
    private:
     gpu::SyncToken sync_token_;
+    const base::PlatformThreadRef context_thread_ref_;
+    const scoped_refptr<base::SingleThreadTaskRunner> context_task_runner_;
     std::unique_ptr<viz::SingleReleaseCallback> release_callback_;
   };
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e01f580..3a90019 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1762,34 +1762,8 @@
 # ======
 
 # Group 1 Leftovers:
-crbug.com/1035582 fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/calendar-picker-datetimelocal.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/calendar-picker-with-step.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/month-picker-key-operations.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/month-picker-mouse-operations.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/month-picker-touch-operations.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/calendar-picker/week-picker-key-operations.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/date-multiple-fields/date-multiple-fields-mouse-events.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events.html [ Failure Pass ]
-crbug.com/1035582 fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html [ Failure Pass ]
 
 # Group 2 Leftovers:
-crbug.com/1035582 fast/forms/date-multiple-fields/* [ Skip ]
-crbug.com/1035582 fast/forms/datetimelocal-multiple-fields/* [ Skip ]
-crbug.com/1035582 fast/forms/month-multiple-fields/* [ Skip ]
-crbug.com/1035582 fast/forms/week-multiple-fields/* [ Skip ]
-crbug.com/1035582 fast/forms/time-multiple-fields/* [ Skip ]
-crbug.com/1035582 fast/forms/suggestion-picker/* [ Skip ]
-crbug.com/1035582 fast/forms/select/listbox-tap.html [ Failure Pass ]
 # Uncomment the one on 5715:
 crbug.com/1035582 fast/forms/suggested-value.html [ Failure Pass ]
 crbug.com/1035582 virtual/form-controls-refresh-disabled/fast/forms/select/menulist-update-text-popup.html [ Failure Pass ]
@@ -1847,6 +1821,11 @@
 crbug.com/1035582 [ Win10 ] fast/forms/color/color-suggestion-picker-appearance-zoom125.html [ Skip ]
 crbug.com/1035582 [ Win10 ] fast/multicol/multicol-with-child-renderLayer-for-input.html [ Skip ]
 
+# This seems broken:
+crbug.com/1035582 fast/forms/select/listbox-tap.html [ Skip ]
+crbug.com/1035582 [ Win7 ] fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar.html [ Skip ]
+
+
 # ======
 # ====== End of rebaselines for crbug.com/1035582 ======
 # ======
@@ -6424,3 +6403,7 @@
 
 # Disable for landing devtools changes
 crbug.com/1006759 http/tests/devtools/console/argument-hints.js [ Pass Failure ]
+
+# Failing origin trial for css proeprties test
+crbug.com/1041993 http/tests/origin_trials/sample-api-script-added-after-css-declaration.html [ Failure ]
+virtual/omt-worker-fetch/http/tests/origin_trials/sample-api-script-added-after-css-declaration.html [ Failure ]
diff --git a/third_party/blink/web_tests/custom-elements/state/state-pseudo-class.html b/third_party/blink/web_tests/custom-elements/state/state-pseudo-class.html
index d14a5e6e..e551d42 100644
--- a/third_party/blink/web_tests/custom-elements/state/state-pseudo-class.html
+++ b/third_party/blink/web_tests/custom-elements/state/state-pseudo-class.html
@@ -11,6 +11,7 @@
 #state-and-part:state(outerFoo)::part(inner) {
   opacity: 0.25;
 }
+:state( \(escaped\ state  ) {}
 </style>
 <body>
 <script>
@@ -65,6 +66,8 @@
 test(() => {
   assert_equals(document.styleSheets[0].cssRules[1].cssText,
       '#state-and-part::part(inner):state(innerFoo) { opacity: 0.5; }');
+  assert_equals(document.styleSheets[0].cssRules[3].selectorText,
+      ':state(\\(escaped\\ state)');
 }, ':state() serialization');
 
 test(() => {
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom/selectorSerialize.html b/third_party/blink/web_tests/external/wpt/css/cssom/selectorSerialize.html
index e95f453b..f3a402f 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom/selectorSerialize.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom/selectorSerialize.html
@@ -70,6 +70,7 @@
             test(function(){
                 assert_selector_serializes_to(':lang(ja)', ':lang(ja)');
                 assert_selector_serializes_to(':lang( ja )', ':lang(ja)');
+                assert_selector_serializes_to(':lang( j\\ a )', ':lang(j\\ a)');
             }, 'single pseudo (simple) selector "lang" which accepts arguments in the sequence of simple selectors that is not a universal selector')
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/webkit-pseudo-element.html b/third_party/blink/web_tests/external/wpt/css/selectors/webkit-pseudo-element.html
index 8b4addd..5ba98eb 100644
--- a/third_party/blink/web_tests/external/wpt/css/selectors/webkit-pseudo-element.html
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/webkit-pseudo-element.html
@@ -10,7 +10,7 @@
 #test {
   color: rgb(255, 0, 0);
 }
-span::-webkit-something-invalid, #test, ::-WeBkIt-sOmEtHiNg-NoNeXiSt123 {
+span::-webkit-something-invalid, #test, ::-WeBkIt-sOmEtHiNg-NoNeXiSt123, ::-webkit-\ escaped {
   color: rgb(0, 255, 0);
 }
 ::-webkitfoo, #test {
@@ -29,7 +29,8 @@
     let sheet = document.getElementById("style").sheet;
     assert_equals(sheet.cssRules[1].selectorText,
                   "span::-webkit-something-invalid, " +
-                  "#test, ::-webkit-something-nonexist123");
+                  "#test, ::-webkit-something-nonexist123, " +
+                  "::-webkit-\\ escaped");
   }, "webkit-prefixed pseudo-element selectors should be accessible from CSSOM");
 
   test(() => {
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.css b/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.css
new file mode 100644
index 0000000..bba5d0e
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.css
@@ -0,0 +1,5 @@
+@supports(origin-trial-test-property: initial) {
+    html {
+        origin-trial-test-property: none;
+    }
+}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.js b/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.js
index 032587d..0811e2ea 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.js
+++ b/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.js
@@ -52,6 +52,20 @@
     'Static member should return boolean value');
 }
 
+// Verify that the given member exists in element styles.
+expect_style_member = (member_name) => {
+  var testObject = document.createElement('div');
+  var testInterface = testObject.style;
+  assert_own_property(testInterface, member_name);
+}
+
+// Verify that the CSS supports return true for given member and value, and
+// style declarations in @supports are applied.
+expect_css_supports = (member_name, member_css_name, member_value, element, computed_style) => {
+  assert_true(CSS.supports(member_css_name, member_value));
+  assert_equals(getComputedStyle(element)[member_name], computed_style);
+}
+
 // Verify that the given constant exists, and returns the expected value, and
 // is not modifiable.
 expect_constant = (constant_name, constant_value, get_value_func) => {
@@ -112,6 +126,22 @@
   assert_equals(testInterface[member_name], undefined);
 }
 
+// Verify that the given member does not exist in element style, and does not
+// provide a value (i.e. is undefined).
+expect_style_member_fails = (member_name) => {
+  var testObject = document.createElement('div');
+  var testInterface = testObject.style;
+  assert_false(member_name in testInterface);
+  assert_equals(testInterface[member_name], undefined);
+}
+
+// Verify that the CSS supports return false for given member and value, and
+// style declarations in @supports are ignored
+expect_css_supports_fails = (member_name, member_css_name, member_value, element) => {
+  assert_false(CSS.supports(member_css_name, member_value));
+  assert_equals(getComputedStyle(element)[member_name], undefined);
+}
+
 // These tests verify that any gated parts of the API are not available.
 expect_failure = (skip_worker) => {
 
@@ -135,6 +165,19 @@
   }
 };
 
+// These tests verify that gated css properties are not available.
+expect_failure_css = (element) => {
+
+  test(() => {
+    expect_style_member_fails('originTrialTestProperty');
+  }, 'CSS property should not exist in style, with trial disabled');
+
+  test(() => {
+      expect_css_supports_fails('originTrialTestProperty',
+        'origin-trial-test-property', 'initial', element);
+    }, 'CSS @supports should fail for property, with trial disabled');
+};
+
 // These tests verify that any gated parts of the API are not available for a
 // deprecation trial.
 expect_failure_deprecation = (skip_worker) => {
@@ -198,6 +241,20 @@
   fetch_tests_from_worker(new Worker('resources/enabled-worker.js'));
 };
 
+
+// These tests verify that css properties functions correctly with an enabled trial.
+expect_success_css = (element, computed_style) => {
+
+  test(() => {
+    expect_style_member('originTrialTestProperty');
+  }, 'CSS property should exist in style');
+
+  test(() => {
+      expect_css_supports('originTrialTestProperty',
+        'origin-trial-test-property', 'initial', element, computed_style);
+    }, 'CSS @supports should pass for property and rules are correctly applied');
+};
+
 // These tests verify that the API functions correctly with a deprecation trial
 // that is enabled.
 expect_success_deprecation = (opt_description_suffix, skip_worker) => {
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/sample-api-disabled.html b/third_party/blink/web_tests/http/tests/origin_trials/sample-api-disabled.html
index adb9bf3..a6157409 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/sample-api-disabled.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/sample-api-disabled.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Test Sample API when trial is disabled (no token)</title>
+<link rel="stylesheet" href="resources/origintrials.css">
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
 <script src="resources/origintrials.js"></script>
@@ -10,5 +11,6 @@
 expect_failure();
 expect_failure_bindings();
 expect_failure_implied();
+expect_failure_css(document.documentElement);
 
 </script>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/sample-api-enabled-header.php b/third_party/blink/web_tests/http/tests/origin_trials/sample-api-enabled-header.php
index 8df2c94..0e6f2ea 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/sample-api-enabled-header.php
+++ b/third_party/blink/web_tests/http/tests/origin_trials/sample-api-enabled-header.php
@@ -9,6 +9,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Test Sample API when trial is enabled</title>
+<link rel="stylesheet" href="resources/origintrials.css">
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
 <script src="resources/origintrials.js"></script>
@@ -17,5 +18,7 @@
 // The trial is enabled by the token above in header.
 expect_success();
 expect_success_bindings();
+expect_success_css(document.documentElement, 'none');
+
 
 </script>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/sample-api-enabled.html b/third_party/blink/web_tests/http/tests/origin_trials/sample-api-enabled.html
index 33da808..bc9a59a 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/sample-api-enabled.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/sample-api-enabled.html
@@ -7,6 +7,7 @@
 generate_token.py http://127.0.0.1:8000 Frobulate --expire-timestamp=2000000000
 -->
 <meta http-equiv="origin-trial" content="AlCoOPbezqtrGMzSzbLQC4c+oPqO6yuioemcBPjgcXajF8jtmZr4B8tJRPAARPbsX6hDeVyXCKHzEJfpBXvZgQEAAABReyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiRnJvYnVsYXRlIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9" />
+<link rel="stylesheet" href="resources/origintrials.css">
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
 <script src="resources/origintrials.js"></script>
@@ -16,5 +17,6 @@
 expect_success();
 expect_success_bindings();
 expect_success_implied();
+expect_success_css(document.documentElement, 'none');
 
 </script>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/sample-api-script-added-after-css-declaration.html b/third_party/blink/web_tests/http/tests/origin_trials/sample-api-script-added-after-css-declaration.html
new file mode 100644
index 0000000..1341905
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/origin_trials/sample-api-script-added-after-css-declaration.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test Sample API enabled CSS property when trial is enabled by script-added meta tag</title>
+<link rel="stylesheet" href="resources/origintrials.css">
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/origin-trials-helper.js"></script>
+<script src="resources/origintrials.js"></script>
+<script>
+
+// TODO(iclelland): Generate this sample token during the build. The token
+//   below will expire in 2033, but it would be better to always have a token which
+//   is guaranteed to be valid when the tests are run. -->
+// Generate these tokens with the given commands:
+// generate_token.py http://127.0.0.1:8000 Frobulate --expire-timestamp=2000000000
+var token = "AlCoOPbezqtrGMzSzbLQC4c+oPqO6yuioemcBPjgcXajF8jtmZr4B8tJRPAARPbsX6hDeVyXCKHzEJfpBXvZgQEAAABReyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiRnJvYnVsYXRlIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9";
+
+// The trial is not enabled, as no token is provided.
+expect_failure_css(document.documentElement);
+
+// Add the token to enable the trial.
+OriginTrialsHelper.add_token(token);
+
+// The trial is now enabled, by the token added via script.
+expect_success();
+expect_success_css(document.documentElement, 'none');
+
+</script>
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-expected.png
index 4ad4c35..4c8bee9a 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
index a2136c7e..e5da2753 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
index 693fb4a2..9f502b1 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-expected.png
index fe8c896..86df6df 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew-expected.png
index 905959f..d7e38c35 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl-expected.png
index a8d72ac0..c7e1651 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
index aa5b4185..6c3b603 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
index d0940d2..9307241 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
index 24a260e..cd4ac1e 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png
index 1d8cda9..1c646a0 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl-expected.png
index 5b3a042..2889cf50 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar-expected.png
index b94a2ff3..96dcbea 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-expected.png
index 04035069..322d3f7 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
index f2f522fe..04a482e 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
index 89c489f..f5312a6 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-expected.png
index fa76163..3fca2870 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
index 10676f5..f90f1ec 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
index d03fbe6..3978dbc 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-expected.png
index 150fe31b..ac3d5cf 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew-expected.png
index c424a33a..9fed00d 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl-expected.png
index b88a432..7f4e468 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
index 66a4e4f0..095b5d2 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
index 85995c3..c6c31d8 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
index e963830..3e4d334 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png
index 16ab55ba..1ca65b2 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl-expected.png
index 9239fe7..0e7f943 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar-expected.png
index 7c5758d..c12e74e 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-expected.png
index a135fb6..ee353428 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
index 68cdca4..e56db62 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
index ea6efdc..81f400f 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
index ec52ecb..f1e09ac 100644
--- a/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png b/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
index a9defb6..48fece0 100644
--- a/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
index 6e62d5e5..414817c 100644
--- a/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/resources/gesture-util.js b/third_party/blink/web_tests/resources/gesture-util.js
index 3dbed91..9b9b23c 100644
--- a/third_party/blink/web_tests/resources/gesture-util.js
+++ b/third_party/blink/web_tests/resources/gesture-util.js
@@ -232,7 +232,8 @@
       chrome.gpuBenchmarking.pointerActionSequence([
         {source: 'mouse',
          actions: [
-            { name: 'pointerUp', x: xPosition, y: yPosition },
+            { name: 'pointerMove', x: xPosition, y: yPosition },
+            { name: 'pointerUp' },
       ]}], resolve);
     } else {
       reject('This test requires chrome.gpuBenchmarking');
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-datetimelocal-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-datetimelocal-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-datetimelocal-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-datetimelocal-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-datetimelocal-with-step.html
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-datetimelocal.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-datetimelocal.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-datetimelocal.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-datetimelocal.html
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-should-not-change-datetimelocal-time.html
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-with-step-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-with-step-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-with-step-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-with-step-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-with-step.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-with-step.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-with-step.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-with-step.html
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value.html
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value.html
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-key-operations-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-key-operations-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-key-operations-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-key-operations-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-key-operations.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-key-operations.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-key-operations.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-key-operations.html
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-mouse-operations-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-mouse-operations-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-mouse-operations.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-mouse-operations.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-mouse-operations.html
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-touch-operations-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-touch-operations-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-touch-operations-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-touch-operations-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-touch-operations.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-touch-operations.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/month-picker-touch-operations.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-touch-operations.html
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/resources/calendar-picker-common.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/resources/calendar-picker-common.js
new file mode 100644
index 0000000..0332f7b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/resources/calendar-picker-common.js
@@ -0,0 +1,211 @@
+function currentMonth() {
+    return popupWindow.global.picker.currentMonth().toString();
+}
+
+function availableDayCells() {
+    skipAnimation();
+    return Array.prototype.map.call(popupWindow.document.querySelectorAll(".day-cell:not(.disabled):not(.hidden)"), function(element) {
+        return element.$view.day.toString();
+    }).sort().join();
+}
+
+function highlightedDayCells() {
+    skipAnimation();
+    return Array.prototype.map.call(popupWindow.document.querySelectorAll(".day-cell.highlighted:not(.hidden)"), function(element) {
+        return element.$view.day.toString();
+    }).sort().join();
+}
+
+function selectedDayCells() {
+    skipAnimation();
+    return Array.prototype.map.call(popupWindow.document.querySelectorAll(".day-cell.selected:not(.hidden)"), function(element) {
+        return element.$view.day.toString();
+    }).sort().join();
+}
+
+function availableWeekNumberCells() {
+    skipAnimation();
+    return Array.prototype.map.call(popupWindow.document.querySelectorAll(".week-number-cell.highlighted:not(.hidden)"), function(element) {
+        return element.$view.day.toString();
+    }).sort().join();
+}
+
+function highlightedWeekNumberCells() {
+    skipAnimation();
+    return Array.prototype.map.call(popupWindow.document.querySelectorAll(".week-number-cell.highlighted:not(.hidden)"), function(element) {
+        return element.$view.day.toString();
+    }).sort().join();
+}
+
+function selectedWeekNumberCells() {
+    skipAnimation();
+    return Array.prototype.map.call(popupWindow.document.querySelectorAll(".week-number-cell.selected:not(.hidden)"), function(element) {
+        return element.$view.day.toString();
+    }).sort().join();
+}
+
+function highlightedValue() {
+    var highlight = popupWindow.global.picker.highlight();
+    if (highlight)
+        return highlight.toString();
+    return null;
+}
+
+function selectedValue() {
+    var selection = popupWindow.global.picker.selection();
+    if (selection)
+        return selection.toString();
+    return null;
+}
+
+function skipAnimation() {
+    popupWindow.AnimationManager.shared._animationFrameCallback(Infinity);
+}
+
+function hoverOverDayCellAt(column, row) {
+  skipAnimation();
+  const calendarTableView = popupWindow.global.picker.datePicker ?
+      popupWindow.global.picker.datePicker.calendarTableView :
+      popupWindow.global.picker.calendarTableView;
+  var offset = cumulativeOffset(calendarTableView.element);
+  var x = offset[0];
+  var y = offset[1];
+  if (calendarTableView.hasWeekNumberColumn)
+    x += popupWindow.WeekNumberCell.Width;
+  x += (column + 0.5) * popupWindow.DayCell.GetWidth();
+  y += (row + 0.5) * popupWindow.DayCell.GetHeight() +
+      popupWindow.CalendarTableHeaderView.GetHeight();
+  eventSender.mouseMoveTo(x, y);
+};
+
+function clickDayCellAt(column, row) {
+  hoverOverDayCellAt(column, row);
+  eventSender.mouseDown();
+  eventSender.mouseUp();
+}
+
+function hoverOverTimeCellAt(column, row) {
+  const timeCellWidth = 56;
+  const timeCellHeight = 36;
+
+  skipAnimation();
+  const timeColumns = popupWindow.global.picker.timePicker ?
+      popupWindow.global.picker.timePicker.timeColumns :
+      popupWindow.global.picker.timeColumns;
+  var offset = cumulativeOffset(timeColumns);
+  var x = offset[0];
+  var y = offset[1];
+  x += (column + 0.5) * timeCellWidth;
+  y += (row + 0.5) * timeCellHeight;
+  eventSender.mouseMoveTo(x, y);
+}
+
+function clickTimeCellAt(column, row) {
+  hoverOverTimeCellAt(column, row);
+  eventSender.mouseDown();
+  eventSender.mouseUp();
+}
+
+function highlightedMonthButton() {
+    skipAnimation();
+    var year = popupWindow.global.picker.monthPopupView.yearListView.selectedRow + 1;
+    return Array.prototype.map.call(popupWindow.document.querySelectorAll(".month-button.highlighted"), function(element) {
+        return new popupWindow.Month(year, Number(element.dataset.month)).toString();
+    }).sort().join();
+}
+
+function skipAnimationAndGetPositionOfMonthPopupButton() {
+    skipAnimation();
+    var buttonElement = popupWindow.global.picker.calendarHeaderView.monthPopupButton.element;
+    var offset = cumulativeOffset(buttonElement);
+    return {x: offset[0] + buttonElement.offsetWidth / 2, y: offset[1] + buttonElement.offsetHeight / 2};
+}
+
+function hoverOverMonthPopupButton() {
+    var position = skipAnimationAndGetPositionOfMonthPopupButton();
+    eventSender.mouseMoveTo(position.x, position.y);
+}
+
+function clickMonthPopupButton() {
+    hoverOverMonthPopupButton();
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+}
+
+function clickYearListCell(year) {
+    skipAnimation();
+    var row = year - 1;
+    var rowCell = popupWindow.global.picker.monthPopupView.yearListView.cellAtRow(row);
+
+    var rowScrollOffset = popupWindow.global.picker.monthPopupView.yearListView.scrollOffsetForRow(row);
+    var scrollOffset = popupWindow.global.picker.monthPopupView.yearListView.scrollView.contentOffset();
+    var rowOffsetFromViewportTop = rowScrollOffset - scrollOffset;
+
+    var scrollViewOffset = cumulativeOffset(popupWindow.global.picker.monthPopupView.yearListView.scrollView.element);
+    var rowCellCenterX = scrollViewOffset[0] + rowCell.element.offsetWidth / 2;
+    var rowCellCenterY = scrollViewOffset[1] + rowOffsetFromViewportTop + rowCell.element.offsetHeight / 2;
+    eventSender.mouseMoveTo(rowCellCenterX, rowCellCenterY);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+}
+
+function skipAnimationAndGetPositionOfMonthButton(year, month, inMonthPicker) {
+    let yearListView = inMonthPicker ? popupWindow.global.picker.yearListView_
+                                     : popupWindow.global.picker.monthPopupView.yearListView;
+    skipAnimation();
+    var row = year - 1;
+    var rowCell = yearListView.cellAtRow(row);
+    var rowScrollOffset = yearListView.scrollOffsetForRow(row);
+    var scrollOffset = yearListView.scrollView.contentOffset();
+    var rowOffsetFromViewportTop = rowScrollOffset - scrollOffset;
+
+    var button = yearListView.buttonForMonth(new popupWindow.Month(year, month));
+    var buttonOffset = cumulativeOffset(button);
+    var rowCellOffset = cumulativeOffset(rowCell.element);
+    var buttonOffsetRelativeToRowCell = [buttonOffset[0] - rowCellOffset[0], buttonOffset[1] - rowCellOffset[1]];
+
+    var scrollViewOffset = cumulativeOffset(yearListView.scrollView.element);
+    var buttonCenterX = scrollViewOffset[0] + buttonOffsetRelativeToRowCell[0] + button.offsetWidth / 2;
+    var buttonCenterY = scrollViewOffset[1] + buttonOffsetRelativeToRowCell[1] + rowOffsetFromViewportTop + button.offsetHeight / 2;
+    return {x: buttonCenterX, y: buttonCenterY};
+}
+
+function hoverOverMonthButton(year, month) {
+    var position = skipAnimationAndGetPositionOfMonthButton(year, month, false);
+    eventSender.mouseMoveTo(position.x, position.y);
+}
+
+function clickMonthButton(year, month) {
+    hoverOverMonthButton(year, month);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+}
+
+function hoverOverMonthButtonForMonthPicker(year, month) {
+    var position = skipAnimationAndGetPositionOfMonthButton(year, month, true);
+    eventSender.mouseMoveTo(position.x, position.y);
+}
+
+var lastYearListViewScrollOffset = NaN;
+function checkYearListViewScrollOffset() {
+    skipAnimation();
+    var scrollOffset = popupWindow.global.picker.monthPopupView.yearListView.scrollView.contentOffset();
+    var change = lastYearListViewScrollOffset - scrollOffset;
+    lastYearListViewScrollOffset = scrollOffset;
+    return change;
+}
+
+function isCalendarTableScrollingWithAnimation() {
+    var animator = popupWindow.global.picker.calendarTableView.scrollView.scrollAnimator();
+    if (!animator)
+        return false;
+    return animator.isRunning();
+}
+
+function removeCommitDelay() {
+    popupWindow.CalendarPicker.commitDelayMs = 0;
+}
+
+function setNoCloseOnCommit() {
+    popupWindow.CalendarPicker.commitDelayMs = -1;
+}
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-key-operations-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/week-picker-key-operations-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-key-operations-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/week-picker-key-operations-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-key-operations.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/week-picker-key-operations.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/calendar-picker/week-picker-key-operations.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/week-picker-key-operations.html
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-clearbutton-preventdefault-mousecapture-status.html
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-mouse-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-mouse-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-mouse-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-mouse-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-mouse-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-mouse-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-mouse-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-mouse-events.html
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-spinbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-mouse-events.html
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-spinbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-mouse-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-mouse-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-mouse-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-mouse-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-mouse-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-mouse-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-mouse-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-mouse-events.html
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-spinbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-spinbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-spinbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-spinbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-spinbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-spinbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-spinbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-spinbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event.html
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-clearbutton-change-and-input-events.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-clearbutton-change-and-input-events.js
new file mode 100644
index 0000000..c3793e27
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-clearbutton-change-and-input-events.js
@@ -0,0 +1,43 @@
+var inputEventCounter = 0;
+var changeEventCounter = 0;
+var testInput;
+
+function testClearButtonChangeAndInputEvents(inputType, initialValue)
+{
+    description('Test for event dispatching by spin buttons in a type=' + inputType + ' input.');
+    if (!window.eventSender) {
+        debug('No eventSender');
+        return;
+    }
+
+    testInput = document.createElement('input');
+    testInput.type = inputType;
+    testInput.value = initialValue;
+    testInput.onchange = function() { changeEventCounter++; };
+    testInput.oninput = function() { inputEventCounter++; };
+    document.body.appendChild(testInput);
+    var anotherInput = document.createElement('input');
+    document.body.appendChild(anotherInput);
+
+    debug('Initial state');
+    eventSender.mouseMoveTo(0, 0);
+    shouldEvaluateTo('changeEventCounter', 0);
+    shouldEvaluateTo('inputEventCounter', 0);
+
+    debug('Click the clear button');
+    // Move the cursor on to the clear button.
+    var clearButton = getElementByPseudoId(internals.shadowRoot(testInput), "-webkit-clear-button");
+    eventSender.mouseMoveTo(clearButton.offsetLeft + clearButton.offsetWidth / 2, clearButton.offsetTop + clearButton.offsetHeight / 2);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+    shouldBeEqualToString('testInput.value', '');
+    shouldEvaluateTo('changeEventCounter', 1);
+    shouldEvaluateTo('inputEventCounter', 1);
+
+    debug('Focus on another field');
+    anotherInput.focus();
+    shouldEvaluateTo('changeEventCounter', 1);
+    shouldEvaluateTo('inputEventCounter', 1);
+
+    parent.innerHTML = '';
+}
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-spinbutton-change-and-input-events.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-spinbutton-change-and-input-events.js
new file mode 100644
index 0000000..116d140
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-spinbutton-change-and-input-events.js
@@ -0,0 +1,63 @@
+var inputEventCounter = 0;
+var changeEventCounter = 0;
+var testInput;
+
+function testSpinButtonChangeAndInputEvents(inputType, initialValue, expectedValue, maximumValue)
+{
+    description('Test for event dispatching by spin buttons in a type=' + inputType + ' input.');
+    if (!window.eventSender) {
+        debug('No eventSender');
+        return;
+    }
+
+    var parent = document.createElement('div');
+    document.body.appendChild(parent);
+    parent.innerHTML = '<input id=test><input id=another>';
+    testInput = document.getElementById('test');
+    var anotherInput = document.getElementById('another');
+
+    testInput.type = inputType;
+    if (maximumValue != undefined)
+        testInput.setAttribute("max", maximumValue);
+    testInput.setAttribute("value", initialValue);
+    testInput.onchange = function() { changeEventCounter++; };
+    testInput.oninput = function() { inputEventCounter++; };
+
+    debug('Initial state');
+    eventSender.mouseMoveTo(0, 0);
+    shouldEvaluateTo('changeEventCounter', 0);
+    shouldEvaluateTo('inputEventCounter', 0);
+    testInput.focus();
+
+    debug('Click the upper button');
+    // Move the cursor on the upper button.
+    var spinButton = getElementByPseudoId(internals.shadowRoot(testInput), "-webkit-inner-spin-button");
+    var rect = spinButton.getBoundingClientRect();
+    eventSender.mouseMoveTo(rect.left, rect.top + rect.height / 4);
+    eventSender.mouseDown();
+    debug('Triggers only input event on mouseDown');
+    shouldBeEqualToString('testInput.value', expectedValue);
+    shouldEvaluateTo('changeEventCounter', 0);
+    shouldEvaluateTo('inputEventCounter', 1);
+    debug('Triggers only change event on mouseUp');
+    eventSender.mouseUp();
+    shouldBeEqualToString('testInput.value', expectedValue);
+    shouldEvaluateTo('changeEventCounter', 1);
+    shouldEvaluateTo('inputEventCounter', 1);
+
+    if (testInput.hasAttribute("max")) {
+        debug('Click again, but the value is not changed.');
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        shouldBeEqualToString('testInput.value', expectedValue);
+        shouldEvaluateTo('changeEventCounter', 1);
+        shouldEvaluateTo('inputEventCounter', 1);
+    }
+
+    debug('Focus on another field');
+    anotherInput.focus();
+    shouldEvaluateTo('changeEventCounter', 1);
+    shouldEvaluateTo('inputEventCounter', 1);
+
+    parent.innerHTML = '';
+}
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-spinbutton-click-in-iframe.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-spinbutton-click-in-iframe.js
new file mode 100644
index 0000000..bb0b5778
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-spinbutton-click-in-iframe.js
@@ -0,0 +1,55 @@
+window.jsTestIsAsync = true;
+
+var iframe;
+var testInput;
+
+function getSpinButton(input)
+{
+    if (!window.internals)
+        return null;
+    return getElementByPseudoId(internals.shadowRoot(input), "-webkit-inner-spin-button");
+}
+
+function mouseClick()
+{
+    if (!window.eventSender)
+        return;
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+}
+
+function mouseMoveTo(x, y)
+{
+    if (!window.eventSender)
+        return;
+    eventSender.mouseMoveTo(x, y);
+}
+
+function runIFrameLoaded(config)
+{
+    testInput = iframe.contentDocument.getElementById('test');
+    testInput.focus();
+    var spinButton = getSpinButton(testInput);
+    if (spinButton) {
+        var rect = spinButton.getBoundingClientRect();
+        mouseMoveTo(
+            iframe.offsetLeft + rect.left + rect.width / 2,
+            iframe.offsetTop + rect.top + rect.height / 4);
+    }
+    mouseClick();
+    shouldBeEqualToString('testInput.value', config['expectedValue']);
+    iframe.parentNode.removeChild(iframe);
+    finishJSTest();
+}
+
+function testClickSpinButtonInIFrame(config)
+{
+    description('Checks mouse click on spin button in iframe.');
+    if (!window.eventSender)
+        debug('Please run in DumpRenderTree');
+
+    iframe = document.createElement('iframe');
+    iframe.addEventListener('load', function () { runIFrameLoaded(config) });
+    iframe.srcdoc = '<input id=test type=' + config['inputType'] + ' value="' + config['initialValue'] + '">';
+    document.body.appendChild(iframe);
+}
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-wheel-event.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-wheel-event.js
new file mode 100644
index 0000000..8e952e6
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-wheel-event.js
@@ -0,0 +1,57 @@
+function dispatchWheelEvent(element, deltaX, deltaY)
+{
+    var rect = element.getClientRects()[0]
+    eventSender.mouseMoveTo(rect.left, rect.top);
+    eventSender.mouseScrollBy(deltaX, deltaY);
+}
+
+var input;
+function testWheelEvent(parameters)
+{
+    var inputType = parameters['inputType'];
+    var initialValue = parameters['initialValue'];
+    var stepUpValue1 = parameters['stepUpValue1'];
+    var stepUpValue2 = parameters['stepUpValue2'];
+    description('Test for wheel operations for &lt;input type=' + inputType + '>');
+    var parent = document.createElement('div');
+    document.body.appendChild(parent);
+    parent.innerHTML = '<input type=' + inputType + ' id=test value="' + initialValue + '"> <input id=another>';
+    input = document.getElementById('test');
+    input.focus();
+
+    debug('Initial value is ' + initialValue + '. We\'ll wheel up by 1:');
+    dispatchWheelEvent(input, 0, 1);
+    shouldBeEqualToString('input.value', stepUpValue1);
+
+    debug('Wheel up by 100:');
+    dispatchWheelEvent(input, 0, 100);
+    shouldBeEqualToString('input.value', stepUpValue2);
+
+    debug('Wheel down by 1:');
+    dispatchWheelEvent(input, 0, -1);
+    shouldBeEqualToString('input.value', stepUpValue1);
+
+    debug('Wheel down by 256:');
+    dispatchWheelEvent(input, 0, -256);
+    shouldBeEqualToString('input.value', initialValue);
+
+    debug('Disabled input element:');
+    input.disabled = true;
+    dispatchWheelEvent(input, 0, 1);
+    shouldBeEqualToString('input.value', initialValue);
+    input.removeAttribute('disabled');
+
+
+    debug('Read-only input element:');
+    input.readOnly = true;
+    dispatchWheelEvent(input, 0, 1);
+    shouldBeEqualToString('input.value', initialValue);
+    input.readOnly = false;
+
+    debug('No focus:');
+    document.getElementById('another').focus();
+    dispatchWheelEvent(input, 0, 1);
+    shouldBeEqualToString('input.value', initialValue);
+
+    parent.parentNode.removeChild(parent);
+}
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common.js
new file mode 100644
index 0000000..041d375
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common.js
@@ -0,0 +1,244 @@
+function $(id) {
+    return document.getElementById(id);
+}
+
+function createFormControlDataSet() {
+    // A list of labelable elements resides in http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#category-label
+    var formControlClassNames = [
+        'HTMLButtonElement',
+        'HTMLDataListElement',
+        'HTMLFieldSetElement',
+        'HTMLInputElement',
+        'HTMLLabelElement',
+        'HTMLLegendElement',
+        'HTMLMeterElement',
+        'HTMLObjectElement',
+        'HTMLOptGroupElement',
+        'HTMLOptionElement',
+        'HTMLOutputElement',
+        'HTMLProgressElement',
+        'HTMLSelectElement',
+        'HTMLTextAreaElement'
+    ];
+    var formControlDataSet = {};
+    for (var i = 0; i < formControlClassNames.length; i++) {
+        var className = formControlClassNames[i];
+        var tagName = className.toLowerCase().substring(4, className.length - 7);
+        var element = document.createElement(tagName);
+        formControlDataSet[tagName] = {
+            inputType: null,
+            isLabelable: true,
+            isSupported: element.toString() == '[object ' + className + ']',
+            name: tagName,
+            tagName: tagName,
+        };
+    }
+    formControlDataSet.datalist.isLabelable = false;
+    formControlDataSet.fieldset.isLabelable = false;
+    formControlDataSet.label.isLabelable = false;
+    formControlDataSet.legend.isLabelable = false;
+    formControlDataSet.object.isLabelable = false;
+    formControlDataSet.optgroup.isLabelable = false;
+    formControlDataSet.option.isLabelable = false;
+
+    // Input type names reside in http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
+    var inputTypeNames = [
+        'button',
+        'checkbox',
+        'color',
+        'date',
+        'datetime',
+        'datetime-local',
+        'email',
+        'file',
+        'hidden',
+        'image',
+        'month',
+        'number',
+        'password',
+        'radio',
+        'range',
+        'reset',
+        'search',
+        'submit',
+        'tel',
+        'text',
+        'time',
+        'url',
+        'week',
+    ];
+    for (var i = 0; i < inputTypeNames.length; i++) {
+        var typeName = inputTypeNames[i];
+        var name = typeName + 'Type';
+        var element = document.createElement('input');
+        element.type = typeName;
+        formControlDataSet[name] = {
+            inputType: typeName,
+            isLabelable: true,
+            isSupported: element.type == typeName,
+            name: name,
+            tagName: 'input',
+      };
+    }
+    formControlDataSet.hiddenType.isLabelable = false;
+
+    return formControlDataSet;
+}
+
+function getAbsoluteRect(element) {
+    var rect = element.getBoundingClientRect();
+    rect.top += document.scrollingElement.scrollTop;
+    rect.bottom += document.scrollingElement.scrollTop;
+    rect.left += document.scrollingElement.scrollLeft;
+    rect.right += document.scrollingElement.scrollLeft;
+    return rect;
+}
+
+function searchCancelButtonPosition(element) {
+    var offset = cumulativeOffset(element);
+    var pos = {};
+    pos.x = offset[0] + element.offsetWidth - 9;
+    pos.y = offset[1] + element.offsetHeight / 2;
+    return pos;
+}
+
+function rtlSearchCancelButtonPosition(element) {
+    var offset = cumulativeOffset(element);
+    var pos = {};
+    pos.x = offset[0] + 9;
+    pos.y = offset[1] + element.offsetHeight / 2;
+    return pos;
+}
+
+function mouseMoveToIndexInListbox(index, listboxId) {
+    var listbox = document.getElementById(listboxId);
+    var itemHeight = Math.floor(listbox.offsetHeight / listbox.size);
+    var border = 1;
+    var y = border + index * itemHeight;
+    if (window.eventSender)
+        eventSender.mouseMoveTo(listbox.offsetLeft + border, listbox.offsetTop + y - window.pageYOffset);
+}
+
+function getUserAgentShadowTextContent(element) {
+    return internals.shadowRoot(element).textContent;
+};
+
+function cumulativeOffset(element) {
+    var x = 0;
+    var y = 0;
+    var parentFrame = element.ownerDocument.defaultView.frameElement;
+    if (parentFrame) {
+        var parentFrameOffset = cumulativeOffset(parentFrame);
+        x = parentFrameOffset[0];
+        y = parentFrameOffset[1];
+    }
+    if (element.parentNode) {
+        do {
+            x += element.offsetLeft || 0;
+            y += element.offsetTop  || 0;
+            element = element.offsetParent;
+        } while (element);
+    }
+    return [x, y];
+}
+
+function elementCenterPosition(element) {
+    var offset = cumulativeOffset(element);
+    var centerX = offset[0] + element.offsetWidth / 2;
+    var centerY = offset[1] + element.offsetHeight / 2;
+    return [centerX, centerY];
+}
+
+function hoverOverElement(element) {
+    var center = elementCenterPosition(element);
+    eventSender.mouseMoveTo(center[0], center[1]);
+}
+
+function clickElement(element) {
+    hoverOverElement(element);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+}
+
+function pressElement(element) {
+    hoverOverElement(element);
+    eventSender.mouseDown();
+}
+
+function traverseNextNode(node, stayWithin) {
+    var nextNode = node.firstChild;
+    if (nextNode)
+        return nextNode;
+
+    if (stayWithin && node === stayWithin)
+        return null;
+
+    nextNode = node.nextSibling;
+    if (nextNode)
+        return nextNode;
+
+    nextNode = node;
+    while (nextNode && !nextNode.nextSibling && (!stayWithin || !nextNode.parentNode || nextNode.parentNode !== stayWithin))
+        nextNode = nextNode.parentNode;
+    if (!nextNode)
+        return null;
+
+    return nextNode.nextSibling;
+}
+
+function getElementByPseudoId(root, pseudoId) {
+    if (!window.internals)
+        return null;
+    var node = root;
+    while (node) {
+        if (node.nodeType === Node.ELEMENT_NODE && internals.shadowPseudoId(node) === pseudoId)
+            return node;
+        node = traverseNextNode(node, root);
+    }
+    return null;
+}
+
+function doneLater() {
+    setTimeout(function() {
+        testRunner.notifyDone();
+    }, 0);
+}
+
+function waitUntilLoadedAndAutofocused(callback) {
+    var loaded = false;
+    var autofocused = false;
+    // Use doneLater() because some rendering tests need repaint after focus.
+    callback  = callback || doneLater;
+    // Does both of waitUntilDone and jsTestIsAsync because we want to support
+    // tests with/without js-test.js.
+    testRunner.waitUntilDone();
+    window.jsTestIsAsync = true;
+    window.addEventListener('load', function() {
+        loaded = true;
+        if (autofocused)
+            callback();
+    }, false);
+    document.addEventListener('focusin', function() {
+        if (internals.hasAutofocusRequest(document))
+            return;
+        if (autofocused)
+            return;
+        autofocused = true;
+        if (loaded)
+            callback();
+    }, false);
+}
+
+function sendString(str) {
+    if (!window.eventSender) {
+        console.log('Require eventSender.');
+        return;
+    }
+    for (var i = 0; i < str.length; ++i) {
+        var key = str.charAt(i);
+        if (key == '\n')
+            key = 'Enter';
+        eventSender.keyDown(key);
+    }
+}
+
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/picker-common.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/picker-common.js
new file mode 100644
index 0000000..016a3ce
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/picker-common.js
@@ -0,0 +1,99 @@
+window.jsTestIsAsync = true;
+
+var popupWindow = null;
+
+var popupOpenCallback = null;
+
+function popupOpenCallbackWrapper() {
+    popupWindow.removeEventListener("didOpenPicker", popupOpenCallbackWrapper);
+    // We need some delay.  Without it, testRunner.notifyDone() freezes.
+    // See crbug.com/562311.
+    setTimeout(popupOpenCallback, 20);
+}
+
+function waitUntilClosing(callback) {
+    setTimeout(callback, 1);
+}
+
+function rootWindow() {
+    var currentWindow = window;
+    while (currentWindow !== currentWindow.parent) {
+        currentWindow = currentWindow.parent;
+    }
+    return currentWindow;
+}
+
+// openPicker opens a picker UI for the following types:
+// - menulist SELECT
+// - INPUT color
+// - INPUT date/datetime-local/month/week
+//
+// |callback| is called if we successfully open the picker UI. However it is
+// called only for the following types:
+// - menulist SELECT on Windows, Linux, and CrOS
+// - INPUT color with DATALIST
+// - INPUT date/datetime-local/month/week
+function openPicker(element, callback, errorCallback) {
+    popupWindow = openPickerHelper(element);
+    if (typeof callback === "function" && popupWindow)
+        setPopupOpenCallback(callback);
+    else if (typeof errorCallback === "function" && !popupWindow)
+        errorCallback();
+}
+
+// openPickerWithPromise opens a picker UI for the following types:
+// - menulist SELECT
+// - INPUT color
+// - INPUT date/datetime-local/month/week
+//
+// Returns a Promise that resolves when the popup has been opened.
+function openPickerWithPromise(element) {
+    return new Promise(function(resolve, reject) {
+        popupWindow = openPickerHelper(element);
+        if (popupWindow) {
+            popupOpenCallback = resolve;
+            popupWindow.addEventListener("didOpenPicker", popupOpenCallbackWrapper, false);
+        } else {
+            reject();
+        }
+    });
+}
+
+// Helper function for openPicker and openPickerWithPromise.
+// Performs the keystrokes that will cause the picker to open,
+// and returns the popup window, or null.
+function openPickerHelper(element) {
+    element.offsetTop; // Force to lay out
+    element.focus();
+    if (element.tagName === "SELECT") {
+        eventSender.keyDown("ArrowDown", ["altKey"]);
+    } else if (element.tagName === "INPUT") {
+        if (element.type === "color") {
+            eventSender.keyDown(" ");
+        } else {
+            eventSender.keyDown("ArrowDown", ["altKey"]);
+        }
+    }
+    return internals.pagePopupWindow;
+}
+
+function clickToOpenPicker(x, y, callback, errorCallback) {
+    eventSender.mouseMoveTo(x, y);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+    popupWindow = internals.pagePopupWindow;
+    if (typeof callback === "function" && popupWindow)
+        setPopupOpenCallback(callback);
+    else if (typeof errorCallback === "function" && !popupWindow)
+        errorCallback();
+}
+
+function setPopupOpenCallback(callback) {
+    console.assert(popupWindow);
+    popupOpenCallback = callback;
+    try {
+        popupWindow.addEventListener("didOpenPicker", popupOpenCallbackWrapper, false);
+    } catch(e) {
+        debug(e.name);
+    }
+}
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-key-operations-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-key-operations-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-key-operations-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-key-operations-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-key-operations.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-key-operations.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-key-operations.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-key-operations.html
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/month-suggestion-picker-key-operations-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/month-suggestion-picker-key-operations-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/month-suggestion-picker-key-operations-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/month-suggestion-picker-key-operations-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/month-suggestion-picker-key-operations.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/month-suggestion-picker-key-operations.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/month-suggestion-picker-key-operations.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/month-suggestion-picker-key-operations.html
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/resources/suggestion-picker-common.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/resources/suggestion-picker-common.js
new file mode 100644
index 0000000..2d95358
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/resources/suggestion-picker-common.js
@@ -0,0 +1,23 @@
+function valueForEntry(element) {
+    if (!element)
+        return null;
+    var value = element.dataset.value;
+    if (typeof value === "string")
+        return value;
+    var action = element.dataset.action;
+    if (typeof action === "string")
+        return "@" + action;
+    return null;
+}
+
+function highlightedEntry() {
+    return valueForEntry(popupWindow.document.activeElement);
+}
+
+function entryValues() {
+    var elements = popupWindow.document.getElementsByClassName("suggestion-list-entry");
+    var values = [];
+    for (var i = 0; i < elements.length; ++i)
+        values.push(valueForEntry(elements[i]));
+    return values;
+}
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-key-operations-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-key-operations-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-key-operations-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-key-operations-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-key-operations.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-key-operations.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-key-operations.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-key-operations.html
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-min-max-attribute-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-min-max-attribute-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-min-max-attribute-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-min-max-attribute-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-min-max-attribute.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-min-max-attribute.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-min-max-attribute.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-min-max-attribute.html
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-step-attribute-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-step-attribute-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-step-attribute-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-step-attribute-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-step-attribute.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-step-attribute.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-step-attribute.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/suggestion-picker/time-suggestion-picker-step-attribute.html
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-mouse-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-mouse-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-mouse-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-mouse-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-mouse-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-mouse-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-mouse-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-mouse-events.html
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-click-in-iframe-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-click-in-iframe-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-click-in-iframe-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-click-in-iframe-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-click-in-iframe.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-click-in-iframe.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-click-in-iframe.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-spinbutton-click-in-iframe.html
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event.html
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-mouse-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-mouse-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-mouse-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-mouse-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-mouse-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-mouse-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-mouse-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-mouse-events.html
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-spinbutton-change-and-input-events-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-spinbutton-change-and-input-events-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-spinbutton-change-and-input-events-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-spinbutton-change-and-input-events-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-spinbutton-change-and-input-events.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-spinbutton-change-and-input-events.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-spinbutton-change-and-input-events.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-spinbutton-change-and-input-events.html
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event-expected.txt b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event-expected.txt
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event-expected.txt
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event.html
similarity index 100%
rename from third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event.html
rename to third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event.html
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/resources/js-test.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/resources/js-test.js
new file mode 100644
index 0000000..7778d18
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/resources/js-test.js
@@ -0,0 +1,885 @@
+// js-test now supports lazily printing test results which dumps all test
+// results once at the end of the test instead of building them up. To enable
+// this option, call setPrintTestResultsLazily() before running any tests.
+var _lazyTestResults; // Set by setPrintTestResultsLazily().
+var _lazyDescription; // Set by description() after setPrintTestResultsLazily().
+
+// If pixelTestingOnly is true, we will dump pixel results only.
+// If enablePixelTesting=true, we will dump text + pixel results.
+// Otherwise we will dump text results only.
+if (self.testRunner && !self.pixelTestingOnly) {
+    if (self.enablePixelTesting)
+        testRunner.dumpAsTextWithPixelResults();
+    else
+        testRunner.dumpAsText();
+}
+
+var isJsTest = true;
+
+var description, debug, successfullyParsed, getOrCreateTestElement;
+
+var expectingError; // set by shouldHaveError()
+var expectedErrorMessage; // set by onerror when expectingError is true
+var unexpectedErrorMessage; // set by onerror when expectingError is not true
+
+(function() {
+
+    getOrCreateTestElement = function(id, tagName)
+    {
+        var element = document.getElementById(id);
+        if (element)
+            return element;
+
+        element = document.createElement(tagName);
+        element.id = id;
+        var refNode;
+        var parent = document.body || document.documentElement;
+        if (id == "description")
+            refNode = getOrCreateTestElement("console", "div");
+        else
+            refNode = parent.firstChild;
+
+        parent.insertBefore(element, refNode);
+        return element;
+    }
+
+    description = function description(msg, quiet)
+    {
+        // For MSIE 6 compatibility
+        var span = document.createElement("span");
+        if (quiet)
+            span.innerHTML = '<p>' + msg + '</p><p>On success, you will see no "<span class="fail">FAIL</span>" messages, followed by "<span class="pass">TEST COMPLETE</span>".</p>';
+        else
+            span.innerHTML = '<p>' + msg + '</p><p>On success, you will see a series of "<span class="pass">PASS</span>" messages, followed by "<span class="pass">TEST COMPLETE</span>".</p>';
+
+        if (_lazyTestResults) {
+          _lazyDescription = span;
+          return;
+        }
+
+        var description = getOrCreateTestElement("description", "p");
+        if (description.firstChild)
+            description.replaceChild(span, description.firstChild);
+        else
+            description.appendChild(span);
+    };
+
+    debug = function debug(msg)
+    {
+        if (self._lazyTestResults) {
+            self._lazyTestResults.push(msg);
+        } else {
+            var span = document.createElement("div");
+            // insert it first so XHTML knows the namespace;
+            getOrCreateTestElement("console", "div").appendChild(span);
+            // Some tests use debug('') to insert an empty line.
+            span.innerHTML = msg !== '' ? msg : '<br />';
+        }
+    };
+
+    var css =
+        ".pass {" +
+            "font-weight: bold;" +
+            "color: green;" +
+        "}" +
+        ".fail {" +
+            "font-weight: bold;" +
+            "color: red;" +
+        "}" +
+        "#console {" +
+            "white-space: pre-wrap;" +
+            "font-family: monospace;" +
+        "}";
+
+    function insertStyleSheet()
+    {
+        var styleElement = document.createElement("style");
+        styleElement.textContent = css;
+        (document.head || document.documentElement).appendChild(styleElement);
+    }
+
+    function handleTestFinished()
+    {
+        // FIXME: Get rid of this boolean.
+        wasPostTestScriptParsed = true;
+        if (window.jsTestIsAsync) {
+            if (window.testRunner)
+                testRunner.waitUntilDone();
+            if (window.wasFinishJSTestCalled)
+                finishJSTest();
+        } else
+            finishJSTest();
+    }
+
+    if (!isWorker()) {
+        window.addEventListener('DOMContentLoaded', handleTestFinished, false);
+        insertStyleSheet();
+    }
+
+    if (!self.isOnErrorTest) {
+        self.onerror = function(message)
+        {
+            if (self.expectingError) {
+                self.expectedErrorMessage = message;
+                self.expectingError = false;
+                return;
+            }
+            self.unexpectedErrorMessage = message;
+            if (self.jsTestIsAsync) {
+                self.testFailed("Unexpected error: " + message);
+                finishJSTest();
+            }
+        };
+    }
+})();
+
+function isWorker()
+{
+    // It's conceivable that someone would stub out 'document' in a worker so
+    // also check for childNodes, an arbitrary DOM-related object that is
+    // meaningless in a WorkerContext.
+    return (typeof document === 'undefined' || typeof document.childNodes === 'undefined') && !!self.importScripts;
+}
+
+function descriptionQuiet(msg) { description(msg, true); }
+
+function escapeHTML(text)
+{
+    return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/\0/g, "\\0");
+}
+
+function testPassed(msg)
+{
+    debug('<span><span class="pass">PASS</span> ' + escapeHTML(msg) + '</span>');
+}
+
+function testFailed(msg)
+{
+    debug('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>');
+}
+
+function areArraysEqual(a, b)
+{
+    try {
+        if (a.length !== b.length)
+            return false;
+        for (var i = 0; i < a.length; i++)
+            if (a[i] !== b[i])
+                return false;
+    } catch (ex) {
+        return false;
+    }
+    return true;
+}
+
+function isMinusZero(n)
+{
+    // the only way to tell 0 from -0 in JS is the fact that 1/-0 is
+    // -Infinity instead of Infinity
+    return n === 0 && 1/n < 0;
+}
+
+function isNewSVGTearOffType(v)
+{
+    return ['[object SVGLength]', '[object SVGLengthList]', '[object SVGPoint]', '[object SVGPointList]', '[object SVGNumber]', '[object SVGTransform]', '[object SVGTransformList]'].indexOf(""+v) != -1;
+}
+
+function isResultCorrect(actual, expected)
+{
+    if (expected === 0)
+        return actual === expected && (1/actual) === (1/expected);
+    if (actual === expected)
+        return true;
+    // http://crbug.com/308818 : The new implementation of SVGListProperties do not necessary return the same wrapper object, so === operator would not work. We compare for their string representation instead.
+    if (isNewSVGTearOffType(expected) && typeof(expected) == typeof(actual) && actual.valueAsString == expected.valueAsString)
+        return true;
+    if (typeof(expected) == "number" && isNaN(expected))
+        return typeof(actual) == "number" && isNaN(actual);
+    if (expected && (Object.prototype.toString.call(expected) == Object.prototype.toString.call([])))
+        return areArraysEqual(actual, expected);
+    return false;
+}
+
+// Returns a sorted array of property names of object.  This function returns
+// not only own properties but also properties on prototype chains.
+function getAllPropertyNames(object) {
+    var properties = [];
+    for (var property in object) {
+        properties.push(property);
+    }
+    return properties.sort();
+}
+
+function stringify(v)
+{
+    if (isNewSVGTearOffType(v))
+        return v.valueAsString;
+    if (v === 0 && 1/v < 0)
+        return "-0";
+    else return "" + v;
+}
+
+function evalAndLog(_a, _quiet)
+{
+  if (typeof _a != "string")
+    debug("WARN: tryAndLog() expects a string argument");
+
+  // Log first in case things go horribly wrong or this causes a sync event.
+  if (!_quiet)
+    debug(_a);
+
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+    testFailed(_a + " threw exception " + e);
+  }
+  return _av;
+}
+
+function shouldBe(_a, _b, quiet, opt_tolerance)
+{
+  if (typeof _a != "string" || typeof _b != "string")
+    debug("WARN: shouldBe() expects string arguments");
+  var _exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+     _exception = e;
+  }
+  var _bv = eval(_b);
+
+  if (_exception)
+    testFailed(_a + " should be " + _bv + ". Threw exception " + _exception);
+  else if (isResultCorrect(_av, _bv) || (typeof opt_tolerance == 'number' && typeof _av == 'number' && Math.abs(_av - _bv) <= opt_tolerance)) {
+    if (!quiet) {
+        testPassed(_a + " is " + _b);
+    }
+  } else if (typeof(_av) == typeof(_bv))
+    testFailed(_a + " should be " + _bv + ". Was " + stringify(_av) + ".");
+  else
+    testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ").");
+}
+
+// Execute condition every animation frame until it succeeds or failureTime is reached.
+// completionHandler is executed on success, failureHandler is executed on timeout.
+function _waitForCondition(condition, failureTime, completionHandler, failureHandler)
+{
+  if (condition()) {
+    completionHandler();
+  } else if (Date.now() > failureTime) {
+    failureHandler();
+  } else {
+    requestAnimationFrame(function() {
+      _waitForCondition(condition, failureTime, completionHandler, failureHandler);
+    });
+  }
+}
+
+function shouldBecomeEqual(_a, _b, _completionHandler, _timeout)
+{
+  if (typeof _a != "string" || typeof _b != "string")
+    debug("WARN: shouldBecomeEqual() expects string arguments");
+
+  if (_timeout === undefined)
+    _timeout = 500;
+
+  var _bv;
+  var _condition = function() {
+    var _exception;
+    var _av;
+    try {
+      _av = eval(_a);
+    } catch (e) {
+        _exception = e;
+    }
+    _bv = eval(_b);
+    if (_exception)
+      testFailed(_a + " should become " + _bv + ". Threw exception " + _exception);
+    if (isResultCorrect(_av, _bv)) {
+      testPassed(_a + " became " + _b);
+      return true;
+    }
+    return false;
+  };
+  var _failureTime = Date.now() + _timeout;
+  var _failureHandler = function () {
+    testFailed(_a + " failed to change to " + _bv + " in " + (_timeout / 1000) + " seconds.");
+    _completionHandler();
+  };
+  _waitForCondition(_condition, _failureTime, _completionHandler, _failureHandler);
+}
+
+function shouldBecomeEqualToString(value, reference, completionHandler, timeout)
+{
+  if (typeof value !== "string" || typeof reference !== "string")
+    debug("WARN: shouldBecomeEqualToString() expects string arguments");
+  var unevaledString = JSON.stringify(reference);
+  shouldBecomeEqual(value, unevaledString, completionHandler, timeout);
+}
+
+function shouldBeType(_a, _type) {
+  var _exception;
+  var _av;
+  try {
+    _av = eval(_a);
+  } catch (e) {
+    _exception = e;
+  }
+
+  var _typev = eval(_type);
+  if (_av instanceof _typev) {
+    testPassed(_a + " is an instance of " + _type);
+  } else {
+    testFailed(_a + " is not an instance of " + _type);
+  }
+}
+
+// Variant of shouldBe()--confirms that result of eval(_a_raw) is within
+// numeric _tolerance of _b_raw.
+function shouldBeCloseTo(_a_raw, _b_raw, _tolerance, _quiet)
+{
+  if (typeof _a_raw != "string") {
+    testFailed("shouldBeCloseTo() requires string argument _a_raw. was type " + typeof _a_raw);
+    return;
+  }
+  if (typeof _b_raw != "number" && typeof _b_raw != "string") {
+    testFailed("shouldBeCloseTo() requires numeric or string argument _b_raw. was type " + typeof _b_raw);
+    return;
+  }
+  if (typeof _tolerance != "number") {
+    testFailed("shouldBeCloseTo() requires numeric argument _tolerance. was type " + typeof _tolerance);
+    return;
+  }
+
+  var _a_evaled;
+  try {
+     _a_evaled = eval(_a_raw);
+  } catch (e) {
+    testFailed(_a_raw + " should be within " + _tolerance + " of "
+               + _b_raw + ". Threw exception " + e);
+    return;
+  }
+
+  var _b_evaled;
+  if (typeof _b_raw == "number") {
+    _b_evaled = _b_raw;
+  } else {
+    try {
+      _b_evaled = eval(_b_raw);
+    } catch (e) {
+      testFailed(_a_raw + " should be within " + _tolerance + " of "
+          + _b_raw + ". Threw exception " + e);
+      return;
+    }
+  }
+
+  if (typeof(_a_evaled) != typeof(_b_evaled)) {
+    testFailed(_a_raw + " should be of type " + typeof _b_evaled
+               + " but was of type " + typeof _a_evaled);
+  } else if (Math.abs(_a_evaled - _b_evaled) <= _tolerance) {
+    if (!_quiet) {
+        testPassed(_a_raw + " is within " + _tolerance + " of " + _b_raw);
+    }
+  } else {
+    testFailed(_a_raw + " should be within " + _tolerance + " of " + _b_raw
+               + ". Was " + _a_evaled + ".");
+  }
+}
+
+function shouldNotBe(_a, _b, _quiet)
+{
+  if (typeof _a != "string" || typeof _b != "string")
+    debug("WARN: shouldNotBe() expects string arguments");
+  var _exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+     _exception = e;
+  }
+  var _bv = eval(_b);
+
+  if (_exception)
+    testFailed(_a + " should not be " + _bv + ". Threw exception " + _exception);
+  else if (!isResultCorrect(_av, _bv)) {
+    if (!_quiet) {
+        testPassed(_a + " is not " + _b);
+    }
+  } else
+    testFailed(_a + " should not be " + _bv + ".");
+}
+
+function shouldBecomeDifferent(_a, _b, _completionHandler, _timeout)
+{
+  if (typeof _a != "string" || typeof _b != "string")
+    debug("WARN: shouldBecomeDifferent() expects string arguments");
+  if (_timeout === undefined)
+    _timeout = 500;
+
+  var _bv;
+  var _condition = function() {
+    var _exception;
+    var _av;
+    try {
+      _av = eval(_a);
+    } catch (e) {
+      _exception = e;
+    }
+    _bv = eval(_b);
+    if (_exception)
+      testFailed(_a + " should became not equal to " + _bv + ". Threw exception " + _exception);
+    if (!isResultCorrect(_av, _bv)) {
+      testPassed(_a + " became different from " + _b);
+      return true;
+    }
+    return false;
+  };
+  var _failureTime = Date.now() + _timeout;
+  var _failureHandler = function () {
+    testFailed(_a + " did not become different from " + _bv + " in " + (_timeout / 1000) + " seconds.");
+    _completionHandler();
+  };
+  _waitForCondition(_condition, _failureTime, _completionHandler, _failureHandler);
+}
+
+function shouldBeTrue(a, quiet) { shouldBe(a, "true", quiet); }
+function shouldBeTrueQuiet(a) { shouldBe(a, "true", true); }
+function shouldBeFalse(a, quiet) { shouldBe(a, "false", quiet); }
+function shouldBeNaN(a, quiet) { shouldBe(a, "NaN", quiet); }
+function shouldBeNull(a, quiet) { shouldBe(a, "null", quiet); }
+function shouldBeZero(a, quiet) { shouldBe(a, "0", quiet); }
+
+function shouldBeEqualToString(a, b)
+{
+  if (typeof a !== "string" || typeof b !== "string")
+    debug("WARN: shouldBeEqualToString() expects string arguments");
+  var unevaledString = JSON.stringify(b);
+  shouldBe(a, unevaledString);
+}
+
+function shouldBeEqualToNumber(a, b)
+{
+  if (typeof a !== "string" || typeof b !== "number")
+    debug("WARN: shouldBeEqualToNumber() expects a string and a number arguments");
+  var unevaledString = JSON.stringify(b);
+  shouldBe(a, unevaledString);
+}
+
+function shouldBeEmptyString(a) { shouldBeEqualToString(a, ""); }
+
+function shouldEvaluateTo(actual, expected, opt_tolerance) {
+  // A general-purpose comparator.  'actual' should be a string to be
+  // evaluated, as for shouldBe(). 'expected' may be any type and will be
+  // used without being eval'ed.
+  if (expected == null) {
+    // Do this before the object test, since null is of type 'object'.
+    shouldBeNull(actual);
+  } else if (typeof expected == "undefined") {
+    shouldBeUndefined(actual);
+  } else if (typeof expected == "function") {
+    // All this fuss is to avoid the string-arg warning from shouldBe().
+    try {
+      var actualValue = eval(actual);
+    } catch (e) {
+      testFailed("Evaluating " + actual + ": Threw exception " + e);
+      return;
+    }
+    shouldBe("'" + actualValue.toString().replace(/\n/g, "") + "'",
+             "'" + expected.toString().replace(/\n/g, "") + "'");
+  } else if (typeof expected == "object") {
+    shouldBeTrue(actual + " == '" + expected + "'");
+  } else if (typeof expected == "string") {
+    shouldBe(actual, expected, undefined, opt_tolerance);
+  } else if (typeof expected == "boolean") {
+    shouldBe("typeof " + actual, "'boolean'");
+    if (expected)
+      shouldBeTrue(actual);
+    else
+      shouldBeFalse(actual);
+  } else if (typeof expected == "number") {
+    if (opt_tolerance)
+        shouldBeCloseTo(actual, expected, opt_tolerance);
+    else
+        shouldBe(actual, stringify(expected));
+  } else {
+    debug(expected + " is unknown type " + typeof expected);
+    shouldBeTrue(actual, "'"  +expected.toString() + "'");
+  }
+}
+
+function shouldEvaluateToSameObject(actual, expected, quiet) {
+  if (typeof actual != "string")
+    debug("WARN: shouldEvaluateToSameObject() expects the first argument (actual) to be a string.");
+  try {
+    actualEvaled = eval(actual);
+  } catch (e) {
+    testFailed("Evaluating " + actual + ": Threw exception " + e);
+    return;
+  }
+  if (isResultCorrect(actualEvaled, expected)) {
+    if (!quiet)
+      testPassed(actual + " is " + stringify(expected));
+  } else {
+    testFailed(actual + " should be " + stringify(expected) + ". Was " + stringify(actualEvaled));
+  }
+}
+
+function shouldBeNonZero(_a)
+{
+  var _exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+     _exception = e;
+  }
+
+  if (_exception)
+    testFailed(_a + " should be non-zero. Threw exception " + _exception);
+  else if (_av != 0)
+    testPassed(_a + " is non-zero.");
+  else
+    testFailed(_a + " should be non-zero. Was " + _av);
+}
+
+function shouldBeNonNull(_a)
+{
+  var _exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+     _exception = e;
+  }
+
+  if (_exception)
+    testFailed(_a + " should be non-null. Threw exception " + _exception);
+  else if (_av != null)
+    testPassed(_a + " is non-null.");
+  else
+    testFailed(_a + " should be non-null. Was " + _av);
+}
+
+function shouldBeUndefined(_a)
+{
+  var _exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+      _exception = e;
+  }
+
+  if (_exception)
+    testFailed(_a + " should be undefined. Threw exception " + _exception);
+  else if (typeof _av == "undefined")
+    testPassed(_a + " is undefined.");
+  else
+    testFailed(_a + " should be undefined. Was " + _av);
+}
+
+function shouldBeDefined(_a)
+{
+  var _exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+     _exception = e;
+  }
+
+  if (_exception)
+    testFailed(_a + " should be defined. Threw exception " + _exception);
+  else if (_av !== undefined)
+    testPassed(_a + " is defined.");
+  else
+    testFailed(_a + " should be defined. Was " + _av);
+}
+
+function shouldBeGreaterThan(_a, _b) {
+    if (typeof _a != "string" || typeof _b != "string")
+        debug("WARN: shouldBeGreaterThan expects string arguments");
+
+    var _exception;
+    var _av;
+    try {
+        _av = eval(_a);
+    } catch (e) {
+        _exception = e;
+    }
+    var _bv = eval(_b);
+
+    if (_exception)
+        testFailed(_a + " should be > " + _b + ". Threw exception " + _exception);
+    else if (typeof _av == "undefined" || _av <= _bv)
+        testFailed(_a + " should be > " + _b + ". Was " + _av + " (of type " + typeof _av + ").");
+    else
+        testPassed(_a + " is > " + _b);
+}
+
+function shouldBeGreaterThanOrEqual(_a, _b) {
+    if (typeof _a != "string" || typeof _b != "string")
+        debug("WARN: shouldBeGreaterThanOrEqual expects string arguments");
+
+    var _exception;
+    var _av;
+    try {
+        _av = eval(_a);
+    } catch (e) {
+        _exception = e;
+    }
+    var _bv = eval(_b);
+
+    if (_exception)
+        testFailed(_a + " should be >= " + _b + ". Threw exception " + _exception);
+    else if (typeof _av == "undefined" || _av < _bv)
+        testFailed(_a + " should be >= " + _b + ". Was " + _av + " (of type " + typeof _av + ").");
+    else
+        testPassed(_a + " is >= " + _b);
+}
+
+function shouldNotThrow(_a) {
+    try {
+        eval(_a);
+        testPassed(_a + " did not throw exception.");
+    } catch (e) {
+        testFailed(_a + " should not throw exception. Threw exception " + e + ".");
+    }
+}
+
+function shouldThrow(_a, _e)
+{
+  var _exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+     _exception = e;
+  }
+
+  var _ev;
+  if (_e)
+      _ev = eval(_e);
+
+  if (_exception) {
+    if (typeof _e == "undefined" || _exception == _ev)
+      testPassed(_a + " threw exception " + _exception + ".");
+    else
+      testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + _exception + ".");
+  } else if (typeof _av == "undefined")
+    testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined.");
+  else
+    testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + ".");
+}
+
+function shouldBeNow(a, delta)
+{
+    // Right now, V8 and Chromium / Blink use two different clock
+    // implementations. On Windows, the implementations are non-trivial and can
+    // be slightly out of sync. The delta is intended to compensate for that.
+    //
+    // FIXME: reconsider this when the V8 and Blink clocks get unified, see http://crbug.com/324110
+    if (delta === undefined)
+        delta = 1000;
+
+    for (var i = 0; i < 1000; ++i) {
+        var startDate = Date.now();
+        var av = eval(a);
+        var date = av.valueOf();
+        var endDate = Date.now();
+
+        // On some occasions such as NTP updates, the current time can go
+        // backwards. This should only happen rarely, so we can get away with
+        // retrying the test a few times if we detect the time going backwards.
+        if (startDate > endDate)
+            continue;
+
+        if (typeof date !== "number") {
+            testFailed(a + " is not a number or a Date. Got " + av);
+            return;
+        }
+        if (date < startDate - delta) {
+            testFailed(a + " is not the curent time. Got " + av + " which is " + (startDate - date) / 1000 + " seconds in the past.");
+            return;
+        }
+        if (date > endDate + delta) {
+            testFailed(a + " is not the current time. Got " + av + " which is " + (date - endDate) / 1000 + " seconds in the future.");
+            return;
+        }
+
+        testPassed(a + " is equivalent to Date.now().");
+        return;
+    }
+    testFailed(a + " cannot be tested against the current time. The clock is going backwards too often.");
+}
+
+function expectError()
+{
+    if (expectingError) {
+        testFailed("shouldHaveError() called twice before an error occurred!");
+    }
+    expectingError = true;
+}
+
+function shouldHaveHadError(message)
+{
+    if (expectingError) {
+        testFailed("No error thrown between expectError() and shouldHaveHadError()");
+        return;
+    }
+
+    if (expectedErrorMessage) {
+        if (!message)
+            testPassed("Got expected error");
+        else if (expectedErrorMessage.indexOf(message) !== -1)
+            testPassed("Got expected error: '" + message + "'");
+        else
+            testFailed("Unexpected error '" + message + "'");
+        expectedErrorMessage = undefined;
+        return;
+    }
+
+    testFailed("expectError() not called before shouldHaveHadError()");
+}
+
+function gc() {
+    if (typeof GCController !== "undefined")
+        GCController.collectAll();
+    else {
+        var gcRec = function (n) {
+            if (n < 1)
+                return {};
+            var temp = {i: "ab" + i + (i / 100000)};
+            temp += "foo";
+            gcRec(n-1);
+        };
+        for (var i = 0; i < 1000; i++)
+            gcRec(10);
+    }
+}
+
+function setPrintTestResultsLazily() {
+    self._lazyTestResults = self._lazyTestResults || [];
+}
+
+function isSuccessfullyParsed()
+{
+    // FIXME: Remove this and only report unexpected syntax errors.
+    successfullyParsed = !unexpectedErrorMessage;
+    shouldBeTrue("successfullyParsed");
+    debug('<br /><span class="pass">TEST COMPLETE<br /></span>');
+}
+
+var wasPostTestScriptParsed, wasFinishJSTestCalled, jsTestIsAsync;
+
+// It's possible for an async test to call finishJSTest() before js-test-post.js
+// has been parsed.
+function finishJSTest()
+{
+    wasFinishJSTestCalled = true;
+    if (!self.wasPostTestScriptParsed)
+        return;
+    isSuccessfullyParsed();
+
+    if (self._lazyDescription)
+      getOrCreateTestElement("description", "p").appendChild(self._lazyDescription);
+
+    if (self._lazyTestResults && self._lazyTestResults.length > 0) {
+        var consoleElement = getOrCreateTestElement("console", "div");
+        self._lazyTestResults.forEach(function(msg) {
+            var span = document.createElement("span");
+            consoleElement.appendChild(span);
+            span.innerHTML = msg + '<br />';
+        });
+    }
+
+    if (self.jsTestIsAsync && self.testRunner)
+        testRunner.notifyDone();
+}
+
+function startWorker(testScriptURL, workerType)
+{
+    self.jsTestIsAsync = true;
+    debug('Starting worker: ' + testScriptURL);
+    var worker;
+    if (workerType == 'shared')
+        worker = new SharedWorker(testScriptURL, "Shared Worker");
+    else
+        worker = new Worker(testScriptURL);
+    worker.onmessage = function(event)
+    {
+        var workerPrefix = "[Worker] ";
+        if (event.data.length < 5 || event.data.charAt(4) != ':') {
+          debug(workerPrefix + event.data);
+          return;
+        }
+        var code = event.data.substring(0, 4);
+        var payload = workerPrefix + event.data.substring(5);
+        if (code == "PASS")
+            testPassed(payload);
+        else if (code == "FAIL")
+            testFailed(payload);
+        else if (code == "DESC")
+            description(payload);
+        else if (code == "DONE")
+            finishJSTest();
+        else
+            debug(workerPrefix + event.data);
+    };
+
+    worker.onerror = function(event)
+    {
+        debug('Got error from worker: ' + event.message);
+        finishJSTest();
+    };
+
+    if (workerType == 'shared') {
+        worker.port.onmessage = function(event) { worker.onmessage(event); };
+        worker.port.start();
+    }
+    return worker;
+}
+
+if (isWorker()) {
+    var workerPort = self;
+    if (self.name == "Shared Worker") {
+        self.onconnect = function(e) {
+            workerPort = e.ports[0];
+            workerPort.onmessage = function(event)
+            {
+                var colon = event.data.indexOf(":");
+                if (colon == -1) {
+                    testFailed("Unrecognized message to shared worker: " + event.data);
+                    return;
+                }
+                var code = event.data.substring(0, colon);
+                var payload = event.data.substring(colon + 1);
+                try {
+                    if (code == "IMPORT")
+                        importScripts(payload);
+                    else
+                        testFailed("Unrecognized message to shared worker: " + event.data);
+                } catch (ex) {
+                    testFailed("Caught exception in shared worker onmessage: " + ex);
+                }
+            };
+        };
+    }
+    description = function(msg, quiet) {
+        workerPort.postMessage('DESC:' + msg);
+    };
+    testFailed = function(msg) {
+        workerPort.postMessage('FAIL:' + msg);
+    };
+    testPassed = function(msg) {
+        workerPort.postMessage('PASS:' + msg);
+    };
+    finishJSTest = function() {
+        workerPort.postMessage('DONE:');
+    };
+    debug = function(msg) {
+        workerPort.postMessage(msg);
+    };
+}
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 00bfa2c..691d556d 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -72,6 +72,7 @@
       'android-marshmallow-arm64-rel': 'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild',
 
       'android-pie-arm64-rel': 'android_release_bot_minimal_symbols_arm64_webview_google',
+      'android-10-arm64-rel': 'android_release_bot_minimal_symbols_arm64_fastbuild_webview_google',
     },
 
     'chromium.android.fyi': {
@@ -686,6 +687,7 @@
       'android-pie-arm64-rel': 'android_release_trybot_arm64_webview_google',
       'android-webview-pie-arm64-fyi-rel': 'android_release_trybot_arm64_webview_google',
       'android-pie-x86-fyi-rel': 'android_release_trybot_x86_fastbuild_webview_google',
+      'android-10-arm64-rel': 'android_release_trybot_arm64_fastbuild_webview_google',
       'android-webview-marshmallow-arm64-dbg': 'android_release_trybot_arm64_webview_google',
       'android-webview-nougat-arm64-dbg': 'android_release_trybot_arm64_webview_google',
       'android-webview-oreo-arm64-dbg': 'android_release_trybot_arm64_webview_google',
@@ -1167,6 +1169,11 @@
       'strip_debug_info',
     ],
 
+    'android_release_bot_minimal_symbols_arm64_fastbuild_webview_google': [
+      'android', 'release_bot', 'minimal_symbols', 'arm64',
+      'strip_debug_info', 'android_fastbuild', 'webview_google',
+    ],
+
     'android_release_bot_minimal_symbols_arm64_webview_google': [
       'android', 'release_bot', 'minimal_symbols', 'arm64',
       'strip_debug_info', 'webview_google',
@@ -1195,6 +1202,11 @@
       'android', 'release_trybot', 'arm64', 'strip_debug_info',
     ],
 
+    'android_release_trybot_arm64_fastbuild_webview_google': [
+      'android', 'release_trybot', 'arm64', 'strip_debug_info',
+      'android_fastbuild', 'webview_google',
+    ],
+
     'android_release_trybot_arm64_webview_google': [
       'android', 'release_trybot', 'arm64', 'strip_debug_info',
       'webview_google',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bd1ab99..a58e46c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -5027,6 +5027,8 @@
   <int value="14" label="Swiped back on bottom snapped window and minimized"/>
   <int value="15" label="Swiped back in overview and aborted"/>
   <int value="16" label="Swiped back in overview and went back"/>
+  <int value="17"
+      label="Swiped back to exit the underneath window's fullscreen mode"/>
 </enum>
 
 <enum name="BackGestureStartScenarioType">
@@ -37803,6 +37805,8 @@
   <int value="-208587977" label="OmniboxMaterialDesignWeatherIcons:enabled"/>
   <int value="-208435024" label="EnableUsernameCorrection:enabled"/>
   <int value="-206393363" label="enable-scroll-prediction"/>
+  <int value="-205068159"
+      label="enable-experimental-accessibility-chromevox-search-menus"/>
   <int value="-204355195" label="secondary-ui-md"/>
   <int value="-203968600" label="SyncSupportSecondaryAccount:disabled"/>
   <int value="-202007318" label="AndroidAIAFetching:enabled"/>
@@ -38755,6 +38759,7 @@
   <int value="1012942422" label="HorizontalTabSwitcherAndroid:disabled"/>
   <int value="1015895665" label="drop-sync-credential:enabled"/>
   <int value="1017364362" label="VrIconInDaydreamHome:enabled"/>
+  <int value="1018165268" label="InterestFeedFeedback:enabled"/>
   <int value="1018797564" label="EnableAppListSearchAutocomplete:disabled"/>
   <int value="1018998019" label="memlog"/>
   <int value="1019623058" label="ash-enable-shelf-model-synchronization"/>
@@ -39308,6 +39313,7 @@
   <int value="1688075820" label="OmniboxExperimentalKeywordMode:disabled"/>
   <int value="1689123607" label="enable-app-link"/>
   <int value="1689183477" label="enable-merge-key-char-events"/>
+  <int value="1689275095" label="InterestFeedFeedback:disabled"/>
   <int value="1690837904" label="save-previous-document-resources"/>
   <int value="1691568199" label="AndroidSpellCheckerNonLowEnd:disabled"/>
   <int value="1693094211" label="FilesNG:disabled"/>
@@ -41067,6 +41073,7 @@
   <int value="651" label="intrinsic-size"/>
   <int value="652" label="intrinsic-width"/>
   <int value="653" label="render-subtree"/>
+  <int value="654" label="origin-trial-test-property"/>
 </enum>
 
 <enum name="MappedEditingCommands">
@@ -47427,8 +47434,11 @@
   <int value="4" label="Fetcher Busy">
     Request not sent because fetcher busy with another request.
   </int>
-  <int value="5" label="No Hosts to Fetch">
-    Request not sent because no hosts left after filtering.
+  <int value="5" label="No Hosts or URLs to Fetch">
+    Request not sent because no hosts or URLs left after filtering.
+  </int>
+  <int value="6" label="No Supported Optimization Types to Fetch">
+    Request not sent because there were no supported optimizations types.
   </int>
 </enum>
 
@@ -47594,6 +47604,23 @@
   </int>
 </enum>
 
+<enum name="OptimizationGuideRaceNavigationFetchAttemptStatus">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Fetch race not needed">
+    Hints for the current navigation were already available so the fetch race
+    was not needed.
+  </int>
+  <int value="2" label="Fetch race for navigation host">
+    The fetch race was attempted for only the host of the current navigation.
+  </int>
+  <int value="3" label="Fetch race for navigation URL">
+    The fetch race was attempted for only the URL of the current navigation.
+  </int>
+  <int value="4" label="Fetch race for navigation host and URL">
+    The fetch race was attempted for the host and URL of the current navigation.
+  </int>
+</enum>
+
 <enum name="OptOutBlacklistReason">
   <int value="0" label="Not loaded">
     The Blacklist was not loaded from disk when the page load committed and
@@ -54542,6 +54569,11 @@
   <int value="3" label="Audio and video"/>
 </enum>
 
+<enum name="RemoveCompromisedCredentialsReason">
+  <int value="0" label="Update - If the credentials was updated."/>
+  <int value="1" label="Remove - If the credentials was removed."/>
+</enum>
+
 <enum name="RendererSchedulerFrameOriginType">
   <int value="0" label="MainFrame"/>
   <int value="1" label="SameOriginFrame"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 5385f7b..5ba359973 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -21553,7 +21553,7 @@
 </histogram>
 
 <histogram name="ChildAccountReconcilor.ForcedUserExitOnReconcileError"
-    enum="BooleanHit" expires_after="2020-02-23">
+    enum="BooleanHit" expires_after="2021-02-23">
   <owner>sinhak@chromium.org</owner>
   <owner>escordeiro@chromium.org</owner>
   <summary>
@@ -54085,6 +54085,16 @@
   </summary>
 </histogram>
 
+<histogram name="GCM.AccountMappingMessageReceived" enum="BooleanReceived"
+    expires_after="2020-06-01">
+  <owner>peter@chromium.org</owner>
+  <owner>knollr@chromium.org</owner>
+  <summary>
+    Records that a message has been received from a delivery channel powered by
+    the GCM account mappings.
+  </summary>
+</histogram>
+
 <histogram name="GCM.AndroidGcmReceiverError" enum="GcmReceiverStatus"
     expires_after="2016-01-25">
   <obsolete>
@@ -75616,7 +75626,7 @@
   </summary>
 </histogram>
 
-<histogram name="Mobile.Legacy.Translate.Toggle.Delay" units="secs"
+<histogram name="Mobile.Legacy.Translate.Toggle.Delay" units="ms"
     expires_after="2020-04-01">
   <owner>sczs@chromium.org</owner>
   <owner>thegreenfrog@chromium.org</owner>
@@ -75626,7 +75636,7 @@
   </summary>
 </histogram>
 
-<histogram name="Mobile.Legacy.Translate.Unused.Duration" units="secs"
+<histogram name="Mobile.Legacy.Translate.Unused.Duration" units="ms"
     expires_after="2020-04-01">
   <owner>sczs@chromium.org</owner>
   <owner>thegreenfrog@chromium.org</owner>
@@ -81694,7 +81704,7 @@
 </histogram>
 
 <histogram name="Net.Cors.PreflightCacheKeySize" units="bytes"
-    expires_after="M79">
+    expires_after="M82">
   <owner>toyoshim@chromium.org</owner>
   <owner>yhirano@chromium.org</owner>
   <summary>
@@ -81704,7 +81714,7 @@
 </histogram>
 
 <histogram name="Net.Cors.PreflightCacheResult" enum="CorsPreflightCacheResult"
-    expires_after="M79">
+    expires_after="M82">
   <owner>toyoshim@chromium.org</owner>
   <owner>yhirano@chromium.org</owner>
   <summary>
@@ -102845,6 +102855,19 @@
   </summary>
 </histogram>
 
+<histogram
+    name="OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus"
+    enum="OptimizationGuideRaceNavigationFetchAttemptStatus"
+    expires_after="M85">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>sophiechang@chromium.org</owner>
+  <summary>
+    Status of a fetch attempt being raced against the current navigation start
+    and whether it includes hosts and urls. Recorded on navigation start
+    (including client redirects) by the hints manager.
+  </summary>
+</histogram>
+
 <histogram name="OptimizationGuide.IsPredictionModelValid" units="BooleanValid"
     expires_after="M85">
   <owner>mcrouse@chromium.org</owner>
@@ -110277,6 +110300,16 @@
   <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
+    Collects the cause of the change in the Password Store login which causes a
+    call of a remove SQL statement on the compromised credentials table.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.RemoveCompromisedCredentials.RemoveReason"
+    enum="RemoveCompromisedCredentialsReason" expires_after="2020-11-26">
+  <owner>bdea@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
     Collects the cause of why remove SQL statement is called on the compromised
     credentials table.
   </summary>
@@ -151661,7 +151694,7 @@
 </histogram>
 
 <histogram name="Sync.BookmarksModelMetadataCorruptionReason"
-    enum="SyncBookmarkModelMetadataCorruptionReason" expires_after="M80">
+    enum="SyncBookmarkModelMetadataCorruptionReason" expires_after="M85">
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -152379,7 +152412,7 @@
 </histogram>
 
 <histogram name="Sync.Entities.PositioningScheme" enum="SyncPositioningScheme"
-    expires_after="M80">
+    expires_after="M85">
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -176552,7 +176585,6 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="CompromisedCredentialsAction" separator=".">
-  <suffix name="Add" label="Saved password is added tp the database"/>
   <suffix name="Remove" label="Saved password is removed from the database"/>
   <suffix name="Update" label="Saved password is updated in the database"/>
   <affected-histogram name="PasswordManager.CompromisedCredentials"/>
diff --git a/ui/accessibility/accessibility_switches.cc b/ui/accessibility/accessibility_switches.cc
index 7573534..3d2e14a 100644
--- a/ui/accessibility/accessibility_switches.cc
+++ b/ui/accessibility/accessibility_switches.cc
@@ -45,6 +45,10 @@
 const char kEnableExperimentalAccessibilityChromeVoxLanguageSwitching[] =
     "enable-experimental-accessibility-chromevox-language-switching";
 
+// Enables search bar to search the ChromeVox menus.
+const char kEnableExperimentalAccessibilityChromeVoxSearchMenus[] =
+    "enable-experimental-accessibility-chromevox-search-menus";
+
 // Enables ChromeVox language switching at the inner node level. This feature
 // hasn't launched yet.
 const char kEnableExperimentalAccessibilityChromeVoxSubNodeLanguageSwitching[] =
diff --git a/ui/accessibility/accessibility_switches.h b/ui/accessibility/accessibility_switches.h
index 682dae9..ffb6ac9 100644
--- a/ui/accessibility/accessibility_switches.h
+++ b/ui/accessibility/accessibility_switches.h
@@ -22,6 +22,8 @@
 AX_EXPORT extern const char
     kEnableExperimentalAccessibilityChromeVoxLanguageSwitching[];
 AX_EXPORT extern const char
+    kEnableExperimentalAccessibilityChromeVoxSearchMenus[];
+AX_EXPORT extern const char
     kEnableExperimentalAccessibilityChromeVoxSubNodeLanguageSwitching[];
 
 // Returns true if experimental accessibility language detection is enabled.
diff --git a/ui/android/java/res/values/color_palette.xml b/ui/android/java/res/values/color_palette.xml
index 7eac39c3..16fbd4c 100644
--- a/ui/android/java/res/values/color_palette.xml
+++ b/ui/android/java/res/values/color_palette.xml
@@ -36,6 +36,7 @@
 
     <color name="black_alpha_11" tools:ignore="UnusedResources">#1D000000</color>
     <color name="black_alpha_38" tools:ignore="UnusedResources">#61000000</color>
+    <color name="black_alpha_65" tools:ignore="UnusedResources">#A6000000</color>
 
     <color name="white_alpha_8" tools:ignore="UnusedResources">#14FFFFFF</color>
     <color name="white_alpha_10" tools:ignore="UnusedResources">#1AFFFFFF</color>
@@ -69,4 +70,7 @@
     <color name="black_alpha_30" tools:ignore="UnusedResources">#4D000000</color>
 
     <color name="white_alpha_65" tools:ignore="UnusedResources">#A6FFFFFF</color>
+
+    <color name="progress_bar_background_deprecated" tools:ignore="UnusedResources">#3D4386F7</color>
+
 </resources>
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm
index 10c9480..3af1d8d 100644
--- a/ui/base/clipboard/clipboard_mac.mm
+++ b/ui/base/clipboard/clipboard_mac.mm
@@ -167,7 +167,7 @@
   }
 
   *fragment_start = 0;
-  DCHECK(markup->length() <= std::numeric_limits<uint32_t>::max());
+  DCHECK_LE(markup->length(), std::numeric_limits<uint32_t>::max());
   *fragment_end = static_cast<uint32_t>(markup->length());
 }
 
diff --git a/ui/base/clipboard/clipboard_ozone.cc b/ui/base/clipboard/clipboard_ozone.cc
index c7b5ed2e..62cb1bd 100644
--- a/ui/base/clipboard/clipboard_ozone.cc
+++ b/ui/base/clipboard/clipboard_ozone.cc
@@ -401,7 +401,7 @@
       async_clipboard_ozone_->ReadClipboardDataAndWait(buffer, kMimeTypeHTML);
   *markup = base::UTF8ToUTF16(base::StringPiece(
       reinterpret_cast<char*>(clipboard_data.data()), clipboard_data.size()));
-  DCHECK(markup->length() <= std::numeric_limits<uint32_t>::max());
+  DCHECK_LE(markup->length(), std::numeric_limits<uint32_t>::max());
   *fragment_end = static_cast<uint32_t>(markup->length());
 }
 
diff --git a/ui/base/clipboard/clipboard_win.cc b/ui/base/clipboard/clipboard_win.cc
index 669f086..56a673e 100644
--- a/ui/base/clipboard/clipboard_win.cc
+++ b/ui/base/clipboard/clipboard_win.cc
@@ -629,7 +629,7 @@
 }
 
 void ClipboardWin::WriteWebSmartPaste() {
-  DCHECK(clipboard_owner_->hwnd() != nullptr);
+  DCHECK_NE(clipboard_owner_->hwnd(), nullptr);
   ::SetClipboardData(
       ClipboardFormatType::GetWebKitSmartPasteType().ToFormatEtc().cfFormat,
       nullptr);
@@ -743,9 +743,10 @@
 }
 
 void ClipboardWin::WriteToClipboard(unsigned int format, HANDLE handle) {
-  DCHECK(clipboard_owner_->hwnd() != nullptr);
+  DCHECK_NE(clipboard_owner_->hwnd(), nullptr);
   if (handle && !::SetClipboardData(format, handle)) {
-    DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
+    DCHECK_NE(GetLastError(),
+              static_cast<unsigned long>(ERROR_CLIPBOARD_NOT_OPEN));
     FreeData(format, handle);
   }
 }
diff --git a/ui/base/clipboard/clipboard_x11.cc b/ui/base/clipboard/clipboard_x11.cc
index 7a1155d..c38c41ab 100644
--- a/ui/base/clipboard/clipboard_x11.cc
+++ b/ui/base/clipboard/clipboard_x11.cc
@@ -621,7 +621,7 @@
     *markup = data.GetHtml();
 
     *fragment_start = 0;
-    DCHECK(markup->length() <= std::numeric_limits<uint32_t>::max());
+    DCHECK_LE(markup->length(), std::numeric_limits<uint32_t>::max());
     *fragment_end = static_cast<uint32_t>(markup->length());
   }
 }
diff --git a/ui/compositor/BUILD.gn b/ui/compositor/BUILD.gn
index ca07e673..e905a1b 100644
--- a/ui/compositor/BUILD.gn
+++ b/ui/compositor/BUILD.gn
@@ -88,9 +88,7 @@
 
   defines = [ "COMPOSITOR_IMPLEMENTATION" ]
 
-  public_deps = [
-    "//cc",
-  ]
+  public_deps = [ "//cc" ]
   deps = [
     "//base",
     "//base/third_party/dynamic_annotations",
@@ -167,9 +165,7 @@
     sources += [ "test/test_compositor_host_win.cc" ]
   }
 
-  public_deps = [
-    ":compositor",
-  ]
+  public_deps = [ ":compositor" ]
   deps = [
     "//base/test:test_support",
     "//cc",
@@ -233,9 +229,7 @@
     "transform_animation_curve_adapter_unittest.cc",
   ]
 
-  data = [
-    "//ui/gfx/test/data/compositor/",
-  ]
+  data = [ "//ui/gfx/test/data/compositor/" ]
 
   deps = [
     ":compositor",
diff --git a/ui/compositor/host/BUILD.gn b/ui/compositor/host/BUILD.gn
index 21b1752..d3881a7 100644
--- a/ui/compositor/host/BUILD.gn
+++ b/ui/compositor/host/BUILD.gn
@@ -6,12 +6,8 @@
 
 source_set("host") {
   if (use_aura || is_mac) {
-    public = [
-      "host_context_factory_private.h",
-    ]
-    sources = [
-      "host_context_factory_private.cc",
-    ]
+    public = [ "host_context_factory_private.h" ]
+    sources = [ "host_context_factory_private.cc" ]
 
     deps = [
       "//cc/mojo_embedder",
@@ -22,8 +18,6 @@
       "//ui/gfx",
     ]
 
-    public_deps = [
-      "//services/viz/public/mojom",
-    ]
+    public_deps = [ "//services/viz/public/mojom" ]
   }
 }
diff --git a/ui/gfx/font_fallback_mac.mm b/ui/gfx/font_fallback_mac.mm
index 6547cb1..debe3f19 100644
--- a/ui/gfx/font_fallback_mac.mm
+++ b/ui/gfx/font_fallback_mac.mm
@@ -16,6 +16,8 @@
 namespace gfx {
 namespace {
 
+constexpr char kLastResortFontName[] = ".LastResort";
+
 // CTFontCreateForString() sometimes re-wraps its result in a new CTFontRef with
 // identical attributes. This wastes time shaping the text run and confounds
 // Skia's internal typeface cache.
@@ -85,7 +87,7 @@
     return false;
 
   *result = Font(base::mac::CFToNSCast(ct_result.get()));
-  return true;
+  return result->GetFontName() != kLastResortFontName;
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 9207970f..7054817 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -108,6 +108,24 @@
          U_BPT_NONE;
 }
 
+// Remove ignorables codepoint from text.
+base::string16 RemoveIgnorableCodepoints(const base::StringPiece16 text) {
+  base::string16 result;
+  result.resize(text.size());
+  size_t offset = 0;
+  bool is_error = false;
+  for (base::i18n::UTF16CharIterator iter(text.data(), text.length());
+       !iter.end(); iter.Advance()) {
+    const UChar32 codepoint = iter.get();
+    if (!u_hasBinaryProperty(codepoint, UCHAR_DEFAULT_IGNORABLE_CODE_POINT)) {
+      U16_APPEND(&result[0], offset, result.length(), codepoint, is_error);
+    }
+  }
+  DCHECK(!is_error);
+  result.resize(offset);
+  return result;
+}
+
 // Writes the script and the script extensions of the Unicode |codepoint|.
 // Returns the number of written scripts.
 size_t GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) {
@@ -2002,6 +2020,16 @@
                                          current_run->range.length());
       fallback_found =
           GetFallbackFont(primary_font, locale_, run_text, &fallback_font);
+
+      if (!fallback_found) {
+        // Ignorable codepoints are handled by Harfbuzz and can be safely
+        // ignored. The default behavior of harfbuzz is to hide these glyphs by
+        // using the space glyph and zeroing the advance width.
+        const base::string16 fallback_run_text =
+            RemoveIgnorableCodepoints(run_text);
+        fallback_found = GetFallbackFont(primary_font, locale_,
+                                         fallback_run_text, &fallback_font);
+      }
     }
 
     if (fallback_found) {
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 76864fa..7fb0803 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -4584,6 +4584,24 @@
   }
 }
 
+TEST_F(RenderTextTest, SameFontAccrossIgnorableCodepoints) {
+  RenderText* render_text = GetRenderText();
+
+  render_text->SetText(WideToUTF16(L"\u060F"));
+  const std::vector<FontSpan> spans1 = GetFontSpans();
+  ASSERT_EQ(1u, spans1.size());
+  Font font1 = spans1[0].first;
+
+  render_text->SetText(WideToUTF16(L"\u060F\u200C\u060F"));
+  const std::vector<FontSpan> spans2 = GetFontSpans();
+  ASSERT_EQ(1u, spans2.size());
+  Font font2 = spans2[0].first;
+
+  // Ensures the same font is used with or without the joiners
+  // (see http://cdbug.com/1036652).
+  EXPECT_EQ(font1.GetFontName(), font2.GetFontName());
+}
+
 // Make sure the caret width is always >=1 so that the correct
 // caret is drawn at high DPI. crbug.com/164100.
 TEST_F(RenderTextTest, CaretWidth) {
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index 0d311ec..b2aa71f 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -348,11 +348,12 @@
 }
 
 void MessageView::OnSlideOut() {
-  MessageCenter::Get()->RemoveNotification(notification_id_,
-                                           true /* by_user */);
-
   for (auto& observer : slide_observers_)
     observer.OnSlideOut(notification_id_);
+
+  // RemoveNotification() may delete |this|.
+  MessageCenter::Get()->RemoveNotification(notification_id_,
+                                           true /* by_user */);
 }
 
 void MessageView::OnWillChangeFocus(views::View* before, views::View* now) {}
diff --git a/ui/ozone/platform/drm/host/drm_cursor.cc b/ui/ozone/platform/drm/host/drm_cursor.cc
index 8e420a59..afed5c2 100644
--- a/ui/ozone/platform/drm/host/drm_cursor.cc
+++ b/ui/ozone/platform/drm/host/drm_cursor.cc
@@ -220,6 +220,7 @@
 
 void DrmCursor::InitializeOnEvdev() {
   DCHECK(evdev_thread_checker_.CalledOnValidThread());
+  base::AutoLock lock(lock_);
   proxy_->InitializeOnEvdevIfNecessary();
 }
 
diff --git a/ui/ozone/platform/drm/host/host_cursor_proxy.cc b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
index e354721..1486038b 100644
--- a/ui/ozone/platform/drm/host/host_cursor_proxy.cc
+++ b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
@@ -6,11 +6,21 @@
 
 #include <utility>
 
+#include "base/threading/thread_task_runner_handle.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/ozone/public/gpu_platform_support_host.h"
 
 namespace ui {
 
+namespace {
+
+void DestroyRemoteOnEvdevThread(
+    mojo::AssociatedRemote<ui::ozone::mojom::DeviceCursor> remote) {
+  // Don't do anything. |remote| will automatically get destroyed.
+}
+
+}  // namespace
+
 // We assume that this is invoked only on the Mus/UI thread.
 HostCursorProxy::HostCursorProxy(
     mojo::PendingAssociatedRemote<ui::ozone::mojom::DeviceCursor> main_cursor,
@@ -19,7 +29,13 @@
       evdev_cursor_pending_remote_(std::move(evdev_cursor)),
       ui_thread_ref_(base::PlatformThread::CurrentRef()) {}
 
-HostCursorProxy::~HostCursorProxy() {}
+HostCursorProxy::~HostCursorProxy() {
+  if (evdev_task_runner_) {
+    evdev_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(DestroyRemoteOnEvdevThread, std::move(evdev_cursor_)));
+  }
+}
 
 void HostCursorProxy::CursorSet(gfx::AcceleratedWidget widget,
                                 const std::vector<SkBitmap>& bitmaps,
@@ -47,6 +63,7 @@
   if (evdev_cursor_.is_bound())
     return;
   evdev_cursor_.Bind(std::move(evdev_cursor_pending_remote_));
+  evdev_task_runner_ = base::ThreadTaskRunnerHandle::Get();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/host/host_cursor_proxy.h b/ui/ozone/platform/drm/host/host_cursor_proxy.h
index 2879df4..05e6ffa 100644
--- a/ui/ozone/platform/drm/host/host_cursor_proxy.h
+++ b/ui/ozone/platform/drm/host/host_cursor_proxy.h
@@ -5,6 +5,7 @@
 #ifndef UI_OZONE_PLATFORM_DRM_HOST_HOST_CURSOR_PROXY_H_
 #define UI_OZONE_PLATFORM_DRM_HOST_HOST_CURSOR_PROXY_H_
 
+#include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "ui/gfx/native_widget_types.h"
@@ -44,6 +45,7 @@
       evdev_cursor_pending_remote_;
 
   base::PlatformThreadRef ui_thread_ref_;
+  scoped_refptr<base::SingleThreadTaskRunner> evdev_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(HostCursorProxy);
 };
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index 316e64a..de7320f 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -121,6 +121,44 @@
 
 }  // namespace
 
+class BubbleDialogDelegateView::AnchorViewObserver : public ViewObserver {
+ public:
+  AnchorViewObserver(BubbleDialogDelegateView* parent, View* anchor_view)
+      : parent_(parent), anchor_view_(anchor_view) {
+    anchor_view_->AddObserver(this);
+  }
+
+  AnchorViewObserver(const AnchorViewObserver&) = delete;
+  AnchorViewObserver& operator=(const AnchorViewObserver&) = delete;
+
+  ~AnchorViewObserver() override { anchor_view_->RemoveObserver(this); }
+
+  View* anchor_view() const { return anchor_view_; }
+
+  // ViewObserver:
+  void OnViewIsDeleting(View* observed_view) override {
+    // The anchor is being deleted, make sure the parent bubble no longer
+    // observes it.
+    DCHECK_EQ(anchor_view_, observed_view);
+    parent_->SetAnchorView(nullptr);
+  }
+
+  void OnViewBoundsChanged(View* observed_view) override {
+    // This code really wants to know the anchor bounds in screen coordinates
+    // have changed. There isn't a good way to detect this outside of the view.
+    // Observing View bounds changing catches some cases but not all of them.
+    DCHECK_EQ(anchor_view_, observed_view);
+    parent_->OnAnchorBoundsChanged();
+  }
+
+  // TODO(pbos): Consider observing View visibility changes and only updating
+  // view bounds when the anchor is visible.
+
+ private:
+  BubbleDialogDelegateView* const parent_;
+  View* const anchor_view_;
+};
+
 BubbleDialogDelegateView::~BubbleDialogDelegateView() {
   if (GetWidget())
     GetWidget()->RemoveObserver(this);
@@ -252,7 +290,9 @@
 }
 
 View* BubbleDialogDelegateView::GetAnchorView() const {
-  return anchor_view_tracker_->view();
+  if (!anchor_view_observer_)
+    return nullptr;
+  return anchor_view_observer_->anchor_view();
 }
 
 void BubbleDialogDelegateView::SetHighlightedButton(
@@ -311,6 +351,10 @@
 }
 
 void BubbleDialogDelegateView::OnAnchorBoundsChanged() {
+  if (!GetWidget())
+    return;
+  // TODO(pbos): Reconsider whether to update the anchor when the view isn't
+  // drawn.
   SizeToContents();
 }
 
@@ -321,7 +365,6 @@
                                                    BubbleBorder::Arrow arrow,
                                                    BubbleBorder::Shadow shadow)
     : close_on_deactivate_(true),
-      anchor_view_tracker_(std::make_unique<ViewTracker>()),
       anchor_widget_(nullptr),
       shadow_(shadow),
       color_explicitly_set_(false),
@@ -398,8 +441,10 @@
 void BubbleDialogDelegateView::Init() {}
 
 void BubbleDialogDelegateView::SetAnchorView(View* anchor_view) {
-  if (GetAnchorView())
+  if (GetAnchorView()) {
     GetAnchorView()->ClearProperty(kAnchoredDialogKey);
+    anchor_view_observer_.reset();
+  }
 
   // When the anchor view gets set the associated anchor widget might
   // change as well.
@@ -428,9 +473,9 @@
     }
   }
 
-  anchor_view_tracker_->SetView(anchor_view);
-
-  if (anchor_view && GetWidget()) {
+  if (anchor_view) {
+    anchor_view_observer_ =
+        std::make_unique<AnchorViewObserver>(this, anchor_view);
     // Do not update anchoring for NULL views; this could indicate
     // that our NativeWindow is being destroyed, so it would be
     // dangerous for us to update our anchor bounds at that
@@ -511,7 +556,7 @@
 
 void BubbleDialogDelegateView::UpdateHighlightedButton(bool highlighted) {
   Button* button = Button::AsButton(highlighted_button_tracker_.view());
-  button = button ? button : Button::AsButton(anchor_view_tracker_->view());
+  button = button ? button : Button::AsButton(GetAnchorView());
   if (button && highlight_button_when_shown_)
     button->SetHighlighted(highlighted);
 }
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index 34a1ab7..ea8d72f 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -197,6 +197,8 @@
   friend class BubbleWindowTargeter;
   friend class ui_devtools::PageAgentViews;
 
+  class AnchorViewObserver;
+
   FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CreateDelegate);
   FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, NonClientHitTest);
 
@@ -221,10 +223,9 @@
   // A flag controlling bubble closure on deactivation.
   bool close_on_deactivate_;
 
-  // The view and widget to which this bubble is anchored. Since an anchor view
-  // can be deleted without notice, we store it in a ViewTracker and retrieve
-  // it from there. It will make sure that the view is still valid.
-  std::unique_ptr<ViewTracker> anchor_view_tracker_;
+  // The view and widget to which this bubble is anchored. AnchorViewObserver
+  // is used to observe bounds changes and view deletion.
+  std::unique_ptr<AnchorViewObserver> anchor_view_observer_;
   Widget* anchor_widget_;
   std::unique_ptr<Widget::PaintAsActiveLock> paint_as_active_lock_;
 
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index 3066064..98b1f69 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -432,6 +432,10 @@
 void NativeWidgetAura::InitModalType(ui::ModalType modal_type) {
   if (modal_type != ui::MODAL_TYPE_NONE)
     window_->SetProperty(aura::client::kModalKey, modal_type);
+  if (modal_type == ui::MODAL_TYPE_WINDOW) {
+    wm::TransientWindowManager::GetOrCreate(window_)
+        ->set_parent_controls_visibility(true);
+  }
 }
 
 gfx::Rect NativeWidgetAura::GetWindowBoundsInScreen() const {
diff --git a/ui/views/widget/native_widget_aura_unittest.cc b/ui/views/widget/native_widget_aura_unittest.cc
index b043944..5280b0b 100644
--- a/ui/views/widget/native_widget_aura_unittest.cc
+++ b/ui/views/widget/native_widget_aura_unittest.cc
@@ -681,5 +681,64 @@
   EXPECT_TRUE(child.IsVisible());
 }
 
+class ModalWidgetDelegate : public WidgetDelegate {
+ public:
+  explicit ModalWidgetDelegate(Widget* widget) : widget_(widget) {}
+  ~ModalWidgetDelegate() override = default;
+
+  // WidgetDelegate:
+  void DeleteDelegate() override { delete this; }
+  const Widget* GetWidgetImpl() const override { return widget_; }
+  ui::ModalType GetModalType() const override {
+    return ui::ModalType::MODAL_TYPE_WINDOW;
+  }
+
+ private:
+  Widget* widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModalWidgetDelegate);
+};
+
+// Tests that for a child transient window, if its modal type is
+// ui::MODAL_TYPE_WINDOW, then its visibility is controlled by its transient
+// parent's visibility.
+TEST_F(NativeWidgetAuraTest, TransientChildModalWindowVisibility) {
+  // Create a parent window.
+  Widget parent;
+  Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW);
+  parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  parent_params.context = root_window();
+  parent.Init(std::move(parent_params));
+  parent.SetBounds(gfx::Rect(0, 0, 400, 400));
+  parent.Show();
+  EXPECT_TRUE(parent.IsVisible());
+
+  // Create a ui::MODAL_TYPE_WINDOW modal type transient child window.
+  Widget child;
+  Widget::InitParams child_params(Widget::InitParams::TYPE_WINDOW);
+  child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  child_params.parent = parent.GetNativeWindow();
+  child_params.delegate = new ModalWidgetDelegate(&child);
+  child.Init(std::move(child_params));
+  child.SetBounds(gfx::Rect(0, 0, 200, 200));
+  child.Show();
+  EXPECT_TRUE(parent.IsVisible());
+  EXPECT_TRUE(child.IsVisible());
+
+  // Hide the parent window should also hide the child window.
+  parent.Hide();
+  EXPECT_FALSE(parent.IsVisible());
+  EXPECT_FALSE(child.IsVisible());
+
+  // The child window can't be shown if the parent window is hidden.
+  child.Show();
+  EXPECT_FALSE(parent.IsVisible());
+  EXPECT_FALSE(child.IsVisible());
+
+  parent.Show();
+  EXPECT_TRUE(parent.IsVisible());
+  EXPECT_TRUE(child.IsVisible());
+}
+
 }  // namespace
 }  // namespace views
diff --git a/ui/webui/.eslintrc.js b/ui/webui/.eslintrc.js
new file mode 100644
index 0000000..5b1b4b9
--- /dev/null
+++ b/ui/webui/.eslintrc.js
@@ -0,0 +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.
+
+module.exports = {
+  'env': {
+    'browser': true,
+    'es6': true,
+  },
+  'rules': {
+    'eqeqeq': ['error', 'always', {'null': 'ignore'}],
+  },
+};