diff --git a/DEPS b/DEPS
index 7742ae635..4f34ce9 100644
--- a/DEPS
+++ b/DEPS
@@ -195,11 +195,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': '652124c372e528937645533147c7da8ec8ac4e08',
+  'skia_revision': 'df9ed89229db82e606c67fbf0f5c24b4c383c1e1',
   # 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': '9e3426ebd5780d3e984c142cc521f2c33b6e32f2',
+  'v8_revision': '2bc574faad33dcdbded0b78e08f1961df1a0299a',
   # 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.
@@ -207,11 +207,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': 'cc958e0e87034f591ea2a76d2809b13dad186c93',
+  'angle_revision': '07ae53f59079cde6bd97542852bac8dffbd2231d',
   # 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': 'cda86eff64617e9b48167f1aa6a31bcdb823c10b',
+  'swiftshader_revision': 'abe07b9438553f741e776dedb59a2d4b502a9983',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -266,7 +266,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': '86c9a7134948a3e2f0c82edbf1cab340ed6e2c20',
+  'devtools_frontend_revision': 'f20a184a017ffbe5a2123596aab54ff6af719232',
   # 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.
@@ -302,7 +302,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '13a65b1aee425a97b9ca73d81212d99fd2bf9227',
+  'spv_tools_revision': 'df859f77dab392dbc78c213d36ba19216b3d068e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -318,7 +318,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': '4a486be696b44151bbda5e837a784560b2d9154f',
+  'dawn_revision': '2b1c0b0e92e745b96ed282f9c4f58e815fb8679d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -545,7 +545,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'c48cd65ade15bc29ab2329ed9e8032e1f5f01a2c',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '225cd3c0cba6ac194c77a418188d1c4421227f2e',
       'condition': 'checkout_ios',
   },
 
@@ -875,7 +875,7 @@
 
   # Build tools for Chrome OS.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'bca9dcdf05aaccf4e0c2dea6bd4cddced50f0c88',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '35492ebf6ba2deca7f54bfde94013d773a777a93',
       'condition': 'checkout_chromeos',
   },
 
@@ -1248,7 +1248,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'dbee93ac203d9269fe93c1ff1e48eba6148e542c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '78d7b4b8250e8fb9509115c2e0b98dd32bb5b9f4',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1550,7 +1550,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'yzqZBKPmH-7JzPqoGRyX5xzi_i8ljeOuLly8tsBta8YC',
+        'version': 'fX1CZHk_Z55AAvLsAiH1mQ6Dr6f33Mf7P1iCopozfbEC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 6f1eeba..a5b9b75 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -5099,6 +5099,11 @@
         new_id_to_msg_map = grd_helper.GetGrdMessages(
           StringIO(unicode('\n'.join(f.NewContents()))), file_dir)
 
+    grd_name, ext = input_api.os_path.splitext(
+        input_api.os_path.basename(file_path))
+    screenshots_dir = input_api.os_path.join(
+        input_api.os_path.dirname(file_path), grd_name + ext.replace('.', '_'))
+
     # Compute added, removed and modified message IDs.
     old_ids = set(old_id_to_msg_map)
     new_ids = set(new_id_to_msg_map)
@@ -5108,12 +5113,16 @@
     for key in old_ids.intersection(new_ids):
       if (old_id_to_msg_map[key].FormatXml()
           != new_id_to_msg_map[key].FormatXml()):
-        modified_ids.add(key)
-
-    grd_name, ext = input_api.os_path.splitext(
-        input_api.os_path.basename(file_path))
-    screenshots_dir = input_api.os_path.join(
-        input_api.os_path.dirname(file_path), grd_name + ext.replace('.', '_'))
+        sha1_path = input_api.os_path.join(
+          screenshots_dir, key + '.png.sha1')
+        if sha1_path not in new_or_added_paths and \
+           not input_api.os_path.exists(sha1_path):
+          # This message does not yet have a screenshot. Require one.
+          modified_ids.add(key)
+        elif (old_id_to_msg_map[key].ContentsAsXml('', True)
+          != new_id_to_msg_map[key].ContentsAsXml('', True)):
+          # The message content itself changed. Require an updated screenshot.
+          modified_ids.add(key)
 
     if run_screenshot_check:
       # Check the screenshot directory for .png files. Warn if there is any.
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index cb9461d..c6f01c89f 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -2872,6 +2872,22 @@
       '</message>',
     '</grit-part>')
 
+  NEW_GRDP_CONTENTS3 = (
+    '<?xml version="1.0" encoding="utf-8"?>',
+      '<grit-part>',
+        '<message name="IDS_PART_TEST1" desc="Description with typo.">',
+          'Part string 1',
+        '</message>',
+    '</grit-part>')
+
+  NEW_GRDP_CONTENTS4 = (
+    '<?xml version="1.0" encoding="utf-8"?>',
+      '<grit-part>',
+        '<message name="IDS_PART_TEST1" desc="Description with typo fixed.">',
+          'Part string 1',
+        '</message>',
+    '</grit-part>')
+
   # A grdp file with one ICU syntax message without syntax errors.
   NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1 = (
     '<?xml version="1.0" encoding="utf-8"?>',
@@ -2975,6 +2991,25 @@
         os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
     ], warnings[0].items)
 
+  def testModifiedMessageDescription(self):
+    # CL modified a message description for a message that does not yet have a
+    # screenshot. Should warn.
+    input_api = self.makeInputApi([
+      MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
+                       self.NEW_GRDP_CONTENTS4, action='M')])
+    warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
+    self.assertEqual(1, len(warnings))
+
+    # CL modified a message description for a message that already has a
+    # screenshot. Should not warn.
+    input_api = self.makeInputApi([
+      MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
+                       self.NEW_GRDP_CONTENTS4, action='M'),
+      MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
+               'binary', action='A')])
+    warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
+    self.assertEqual(0, len(warnings))
+
   def testPngAddedSha1NotAdded(self):
     # CL added one new message in a grd file and added the png file associated
     # with it, but did not add the corresponding sha1 file. This should warn
diff --git a/WATCHLISTS b/WATCHLISTS
index 02bfdad..92a95be 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1541,6 +1541,10 @@
                   '|chrome/browser/content_settings/permission*'\
                   '|permission_context',
     },
+    'phonehub': {
+      'filepath': 'chrome/browser/chromeos/phonehub/|'\
+                  'chromeos/components/phonehub/',
+    },
     'picture_in_picture': {
       'filepath': 'third_party/blink/renderer/modules/picture_in_picture/'
     },
@@ -2670,6 +2674,9 @@
                     'hanxi+watch@chromium.org',
                     'mlamouri+watch-permissions@chromium.org',
                     'permissions-reviews@chromium.org'],
+    'phonehub': ['hsuregan+watch-phonehub@chromium.org',
+                 'khorimoto+watch-phonehub@chromium.org',
+                 'themaxli+watch-phonehub@chromium.org'],
     'picture_in_picture': ['beaufort.francois+pip@gmail.com'],
     'polymer': ['michaelpg+watch-polymer@chromium.org'],
     'popup_blocker': ['csharrison+watch-popups@chromium.org'],
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 996e4d4..ea618988 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -469,12 +469,16 @@
     "hud_display/hud_constants.h",
     "hud_display/hud_display.cc",
     "hud_display/hud_display.h",
+    "hud_display/hud_header_view.cc",
+    "hud_display/hud_header_view.h",
     "hud_display/hud_properties.cc",
     "hud_display/hud_properties.h",
     "hud_display/hud_settings_view.cc",
     "hud_display/hud_settings_view.h",
     "hud_display/memory_status.cc",
     "hud_display/memory_status.h",
+    "hud_display/tab_strip.cc",
+    "hud_display/tab_strip.h",
     "ime/ime_controller_impl.cc",
     "ime/ime_controller_impl.h",
     "ime/ime_mode_indicator_view.cc",
diff --git a/ash/hud_display/hud_constants.h b/ash/hud_display/hud_constants.h
index db884dd..993d5b0 100644
--- a/ash/hud_display/hud_constants.h
+++ b/ash/hud_display/hud_constants.h
@@ -12,6 +12,33 @@
 
 constexpr SkAlpha kHUDAlpha = 204;  // = 0.8 * 255
 
+// Use light orange color.
+constexpr SkColor kHUDDefaultColor =
+    SkColorSetARGB(kHUDAlpha, 0xFF, 0xB2, 0x66);
+
+constexpr SkColor kHUDBackground = SkColorSetARGB(kHUDAlpha, 17, 17, 17);
+
+// Radius of rounded corners for tabs.
+// Must be be divisible by 3 to make kTabOverlayWidth integer.
+constexpr int kTabOverlayCornerRadius = 9;
+
+// Border around settings icon in the settings button.
+constexpr int kSettingsIconBorder = 5;
+
+// Settings button icon size.
+constexpr int kHUDSettingsIconSize = 18;
+
+// Visible border inside the HUDDisplayView rectangle around contents.
+// HUDDisplayView does not use insets itself. Children substitute this inset
+// where needed.
+constexpr int kHUDInset = 5;
+
+// HUD display modes.
+enum class DisplayModes {
+  MEMORY_DISPLAY,
+  DEFAULT = MEMORY_DISPLAY
+};
+
 }  // namespace hud_display
 }  // namespace ash
 
diff --git a/ash/hud_display/hud_display.cc b/ash/hud_display/hud_display.cc
index 11a834d..a738cdd 100644
--- a/ash/hud_display/hud_display.cc
+++ b/ash/hud_display/hud_display.cc
@@ -8,21 +8,20 @@
 #include "ash/fast_ink/view_tree_host_widget.h"
 #include "ash/hud_display/graphs_container_view.h"
 #include "ash/hud_display/hud_constants.h"
+#include "ash/hud_display/hud_header_view.h"
 #include "ash/hud_display/hud_properties.h"
 #include "ash/hud_display/hud_settings_view.h"
+#include "ash/hud_display/tab_strip.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
+#include "base/strings/utf_string_conversions.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/aura/window.h"
 #include "ui/base/hit_test.h"
 #include "ui/events/base_event_utils.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/skia_util.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/widget/widget.h"
@@ -31,101 +30,46 @@
 namespace hud_display {
 namespace {
 
-constexpr int kVectorIconSize = 18;
+constexpr size_t kHUDGraphsInset = 5;
+
+// Default HUDDisplayView height.
+static constexpr size_t kDefaultHUDGraphHeight = 300;
+
+// Top border + Header height + margin + graph height + bottom border..
+constexpr int kHUDViewDefaultHeight =
+    kHUDInset + (kHUDSettingsIconSize + 2 * kSettingsIconBorder) +
+    kHUDGraphsInset + kDefaultHUDGraphHeight + kHUDInset;
 
 std::unique_ptr<views::Widget> g_hud_widget;
 
-constexpr SkColor kBackground = SkColorSetARGB(kHUDAlpha, 17, 17, 17);
-
-// Basically views::SolidBackground with SkBlendMode::kSrc paint mode.
-class SolidSourceBackground : public views::Background {
+// ClientView that return HTNOWHERE by default. A child view can receive event
+// by setting kHitTestComponentKey property to HTCLIENT.
+class HTClientView : public views::ClientView {
  public:
-  explicit SolidSourceBackground(SkColor color) {
-    SetNativeControlColor(color);
-  }
+  METADATA_HEADER(HTClientView);
 
-  SolidSourceBackground(const SolidSourceBackground&) = delete;
-  SolidSourceBackground& operator=(const SolidSourceBackground&) = delete;
+  HTClientView(HUDDisplayView* hud_display,
+               views::Widget* widget,
+               views::View* contents_view)
+      : views::ClientView(widget, contents_view), hud_display_(hud_display) {}
+  HTClientView(const HTClientView&) = delete;
+  HTClientView& operator=(const HTClientView&) = delete;
 
-  void Paint(gfx::Canvas* canvas, views::View* /*view*/) const override {
-    // Fill the background. Note that we don't constrain to the bounds as
-    // canvas is already clipped for us.
-    canvas->DrawColor(get_color(), SkBlendMode::kSrc);
-  }
-};
+  ~HTClientView() override = default;
 
-std::unique_ptr<views::View> CreateButtonsContainer() {
-  auto container = std::make_unique<views::View>();
-  auto layout_manager = std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kHorizontal);
-  layout_manager->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kStart);
-  container->SetLayoutManager(std::move(layout_manager));
-  return container;
-}
-
-std::unique_ptr<views::ImageButton> CreateSettingsButton(HUDDisplayView* hud) {
-  auto button = std::make_unique<views::ImageButton>(hud);
-  button->SetVisible(false);
-  button->SetImage(
-      views::Button::ButtonState::STATE_NORMAL,
-      gfx::CreateVectorIcon(vector_icons::kSettingsIcon, kVectorIconSize,
-                            HUDSettingsView::kDefaultColor));
-  button->SetBorder(views::CreateEmptyBorder(gfx::Insets(5)));
-  button->SetProperty(kHUDClickHandler, HTCLIENT);
-  button->SetBackground(std::make_unique<SolidSourceBackground>(kBackground));
-  return button;
-}
-
-class HUDOverlayContainerView : public views::View {
- public:
-  METADATA_HEADER(HUDOverlayContainerView);
-
-  explicit HUDOverlayContainerView(HUDDisplayView* hud);
-  ~HUDOverlayContainerView() override = default;
-
-  HUDOverlayContainerView(const HUDOverlayContainerView&) = delete;
-  HUDOverlayContainerView& operator=(const HUDOverlayContainerView&) = delete;
-
-  HUDSettingsView* settings_view() const { return settings_view_; }
-  views::ImageButton* settings_trigger_button() const {
-    return settings_trigger_button_;
+  // views::ClientView
+  int NonClientHitTest(const gfx::Point& point) override {
+    return hud_display_->NonClientHitTest(point);
   }
 
  private:
-  HUDSettingsView* settings_view_ = nullptr;
-  views::ImageButton* settings_trigger_button_ = nullptr;
+  HUDDisplayView* hud_display_;
 };
 
-BEGIN_METADATA(HUDOverlayContainerView)
-METADATA_PARENT_CLASS(View)
+BEGIN_METADATA(HTClientView)
+METADATA_PARENT_CLASS(ClientView)
 END_METADATA()
 
-HUDOverlayContainerView::HUDOverlayContainerView(HUDDisplayView* hud) {
-  // Overlay container has two child views stacked vertically and stretched
-  // horizontally.
-  // The top is a container for "Settings" button.
-  // The bottom is Settings UI view.
-  views::BoxLayout* layout_manager =
-      SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kVertical));
-  layout_manager->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kStretch);
-  {
-    // Buttons container arranges buttons horizontally and does not alter
-    // button sizes.
-    views::View* buttons_container = AddChildView(CreateButtonsContainer());
-    settings_trigger_button_ =
-        buttons_container->AddChildView(CreateSettingsButton(hud));
-  }
-  // HUDSettingsView starts invisible.
-  settings_view_ = AddChildView(std::make_unique<HUDSettingsView>());
-
-  // Make settings view occupy all the remaining space.
-  layout_manager->SetFlexForView(settings_view_, 1,
-                                 /*use_min_size=*/false);
-}
-
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -152,7 +96,7 @@
                                       kShellWindowId_OverlayContainer);
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.bounds =
-      gfx::Rect(Graph::kDefaultWidth + 2 * kHUDInset, kDefaultHUDHeight);
+      gfx::Rect(Graph::kDefaultWidth + 2 * kHUDInset, kHUDViewDefaultHeight);
   auto* widget = CreateViewTreeHostWidget(std::move(params));
   widget->GetLayer()->SetName("HUDDisplayView");
   widget->Show();
@@ -163,43 +107,53 @@
 HUDDisplayView::HUDDisplayView() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
 
-  SetBackground(views::CreateSolidBackground(kBackground));
-  SetBorder(views::CreateEmptyBorder(gfx::Insets(5)));
+  // Layout:
+  // ----------------------
+  // |      Header        | // Buttons, tabs, controls
+  // ----------------------
+  // |                    | // Data views full-size, z-stacked.
+  // |      Data          |
+  // |                    |
+  // ----------------------
 
-  SetLayoutManager(std::make_unique<views::FillLayout>());
+  // Create two child views for header and data. Vertically stacked.
+  views::BoxLayout* layout_manager =
+      SetLayoutManager(std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kVertical));
+  layout_manager->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kStretch);
+
+  header_view_ = AddChildView(std::make_unique<HUDHeaderView>(this));
+  views::View* data = AddChildView(std::make_unique<views::View>());
+
+  // Data view takes the rest of the host view.
+  layout_manager->SetFlexForView(data, 1, /*use_min_size=*/false);
+
+  // Setup header.
+
+  // TODO: Add tab buttons via:
+  // header_view_->tab_strip()->AddTabButton(this, DisplayModes::MEMORY_DISPLAY,
+  //                                        base::ASCIIToUTF16("RAM"));
+
+  // Setup data.
+  data->SetBackground(views::CreateSolidBackground(kHUDBackground));
+  data->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(kHUDGraphsInset, kHUDInset, kHUDInset, kHUDInset)));
 
   // We have two child views z-stacked.
   // The bottom one is GraphsContainerView with all the graph lines.
-  // The top one lays out buttons and settings UI overlays.
-  graphs_container_ = AddChildView(std::make_unique<GraphsContainerView>());
-
-  HUDOverlayContainerView* overlay_container =
-      AddChildView(std::make_unique<HUDOverlayContainerView>(this));
-  settings_view_ = overlay_container->settings_view();
-  settings_trigger_button_ = overlay_container->settings_trigger_button();
-
-  // Receive OnMouseEnter/OnMouseLEave when hovering over the child views too.
-  set_notify_enter_exit_on_child(true);
+  // The top one is settings UI overlay.
+  data->SetLayoutManager(std::make_unique<views::FillLayout>());
+  graphs_container_ =
+      data->AddChildView(std::make_unique<GraphsContainerView>());
+  settings_view_ = data->AddChildView(std::make_unique<HUDSettingsView>());
+  settings_view_->SetVisible(false);
 }
 
 HUDDisplayView::~HUDDisplayView() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-void HUDDisplayView::OnMouseEntered(const ui::MouseEvent& /*event*/) {
-  settings_trigger_button_->SetVisible(true);
-}
-
-void HUDDisplayView::OnMouseExited(const ui::MouseEvent& /*event*/) {
-  // Button is always visible if Settings UI is visible.
-  if (settings_view_->GetVisible())
-    return;
-
-  settings_trigger_button_->SetVisible(false);
-}
-
 int HUDDisplayView::NonClientHitTest(const gfx::Point& point) {
   const View* view = GetEventHandlerForPoint(point);
   if (!view)
@@ -208,24 +162,9 @@
   return view->GetProperty(kHUDClickHandler);
 }
 
-// ClientView that return HTNOWHERE by default. A child view can receive event
-// by setting kHitTestComponentKey property to HTCLIENT.
-class HTClientView : public views::ClientView {
- public:
-  HTClientView(HUDDisplayView* hud_display,
-               views::Widget* widget,
-               views::View* contents_view)
-      : views::ClientView(widget, contents_view), hud_display_(hud_display) {}
-  ~HTClientView() override = default;
-
-  int NonClientHitTest(const gfx::Point& point) override;
-
- private:
-  HUDDisplayView* hud_display_ = nullptr;
-};
-
-int HTClientView::NonClientHitTest(const gfx::Point& point) {
-  return hud_display_->NonClientHitTest(point);
+void HUDDisplayView::TabButtonPressed(const HUDTabButton* tab_button) {
+  header_view_->tab_strip()->ActivateTab(tab_button);
+  // TODO: switch on tab_button->display_mode().
 }
 
 views::ClientView* HUDDisplayView::CreateClientView(views::Widget* widget) {
diff --git a/ash/hud_display/hud_display.h b/ash/hud_display/hud_display.h
index 3237bae2..99d9cfe 100644
--- a/ash/hud_display/hud_display.h
+++ b/ash/hud_display/hud_display.h
@@ -5,19 +5,18 @@
 #ifndef ASH_HUD_DISPLAY_HUD_DISPLAY_H_
 #define ASH_HUD_DISPLAY_HUD_DISPLAY_H_
 
+#include "ash/hud_display/hud_constants.h"
 #include "base/sequence_checker.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/widget/widget_delegate.h"
 
-namespace views {
-class ImageButton;
-}
-
 namespace ash {
 namespace hud_display {
 
 class GraphsContainerView;
+class HUDHeaderView;
 class HUDSettingsView;
+class HUDTabButton;
 
 // HUDDisplayView class can be used to display a system monitoring overview.
 class HUDDisplayView : public views::WidgetDelegateView,
@@ -25,21 +24,11 @@
  public:
   METADATA_HEADER(HUDDisplayView);
 
-  // Default HUDDisplayView height.
-  static constexpr size_t kDefaultHUDHeight = 300;
-
-  // Border width inside the HUDDisplayView rectangle around contents.
-  static constexpr size_t kHUDInset = 5;
-
   HUDDisplayView();
-  ~HUDDisplayView() override;
-
   HUDDisplayView(const HUDDisplayView&) = delete;
   HUDDisplayView& operator=(const HUDDisplayView&) = delete;
 
-  // view::
-  void OnMouseEntered(const ui::MouseEvent& event) override;
-  void OnMouseExited(const ui::MouseEvent& event) override;
+  ~HUDDisplayView() override;
 
   // WidgetDelegate:
   views::ClientView* CreateClientView(views::Widget* widget) override;
@@ -58,10 +47,13 @@
   // of the children.
   int NonClientHitTest(const gfx::Point& point);
 
+  // Changes UI display mode.
+  void TabButtonPressed(const HUDTabButton* tab_button);
+
  private:
-  GraphsContainerView* graphs_container_ = nullptr;        // not owned
-  HUDSettingsView* settings_view_ = nullptr;               // not owned
-  views::ImageButton* settings_trigger_button_ = nullptr;  // not owned
+  HUDHeaderView* header_view_ = nullptr;             // not owned
+  GraphsContainerView* graphs_container_ = nullptr;  // not owned
+  HUDSettingsView* settings_view_ = nullptr;         // not owned
 
   SEQUENCE_CHECKER(ui_sequence_checker_);
 };
diff --git a/ash/hud_display/hud_header_view.cc b/ash/hud_display/hud_header_view.cc
new file mode 100644
index 0000000..fc49c698
--- /dev/null
+++ b/ash/hud_display/hud_header_view.cc
@@ -0,0 +1,247 @@
+// 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 "ash/hud_display/hud_header_view.h"
+
+#include "ash/hud_display/hud_constants.h"
+#include "ash/hud_display/hud_display.h"
+#include "ash/hud_display/hud_properties.h"
+#include "ash/hud_display/tab_strip.h"
+#include "components/vector_icons/vector_icons.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/layout_manager.h"
+
+namespace ash {
+namespace hud_display {
+namespace {
+
+// Basically views::SolidBackground with SkBlendMode::kSrc paint mode.
+class SolidSourceBackground : public views::Background {
+ public:
+  // Background will have top rounded corners with |top_rounding_radius|.
+  SolidSourceBackground(SkColor color, SkScalar top_rounding_radius)
+      : top_rounding_radius_(top_rounding_radius) {
+    SetNativeControlColor(color);
+  }
+
+  SolidSourceBackground(const SolidSourceBackground&) = delete;
+  SolidSourceBackground& operator=(const SolidSourceBackground&) = delete;
+
+  ~SolidSourceBackground() override = default;
+
+  // views::Background
+  void Paint(gfx::Canvas* canvas, views::View* view) const override {
+    if (top_rounding_radius_ == 0) {
+      // Fill the background. Note that we don't constrain to the bounds as
+      // canvas is already clipped for us.
+      canvas->DrawColor(get_color(), SkBlendMode::kSrc);
+    } else {
+      const SkScalar circle_size = top_rounding_radius_ * 2;
+      const int right_edge = view->width();
+      const int bottom_edge = view->height();
+
+      SkPath path;
+      path.moveTo(0, bottom_edge);
+      /* |false| will draw straight line to the start of the arc */
+      path.arcTo({0, 0, circle_size, circle_size}, -180, 90, false);
+      path.arcTo({right_edge - circle_size, 0, right_edge, circle_size}, -90,
+                 90, false);
+      path.lineTo(right_edge, bottom_edge);
+
+      cc::PaintFlags flags;
+      flags.setAntiAlias(true);
+      flags.setBlendMode(SkBlendMode::kSrc);
+      flags.setStyle(cc::PaintFlags::kFill_Style);
+      flags.setColor(get_color());
+      canvas->DrawPath(path, flags);
+    }
+  }
+
+ private:
+  SkScalar top_rounding_radius_;
+};
+
+// Draws bottom left rounded background triangle.
+class BottomLeftOuterBackground : public views::Background {
+ public:
+  // Background will have left bottom rounded corner with |top_rounding_radius|.
+  BottomLeftOuterBackground(SkColor color, SkScalar top_rounding_radius)
+      : inner_radius_(top_rounding_radius) {
+    SetNativeControlColor(color);
+  }
+
+  BottomLeftOuterBackground(const BottomLeftOuterBackground&) = delete;
+  BottomLeftOuterBackground& operator=(const BottomLeftOuterBackground&) =
+      delete;
+
+  ~BottomLeftOuterBackground() override = default;
+
+  // views::Background
+  void Paint(gfx::Canvas* canvas, views::View* view) const override {
+    const SkScalar circle_size = inner_radius_ * 2;
+    const int bottom_edge = view->height();
+
+    SkPath path;
+    path.moveTo(0, bottom_edge);
+    /* |false| will draw straight line to the start of the arc */
+    path.arcTo({0, bottom_edge - circle_size, circle_size, bottom_edge}, 90, 90,
+               false);
+    path.lineTo(0, bottom_edge);
+
+    cc::PaintFlags flags;
+    flags.setAntiAlias(true);
+    flags.setBlendMode(SkBlendMode::kSrc);
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    flags.setColor(get_color());
+    canvas->DrawPath(path, flags);
+  }
+
+ private:
+  SkScalar inner_radius_;
+};
+
+// ImageButton with underline
+class SettingsButton : public views::ImageButton {
+ public:
+  METADATA_HEADER(SettingsButton);
+
+  explicit SettingsButton(views::ButtonListener* listener)
+      : views::ImageButton(listener) {
+    SetImage(views::Button::ButtonState::STATE_NORMAL,
+             gfx::CreateVectorIcon(vector_icons::kSettingsIcon,
+                                   kHUDSettingsIconSize, kHUDDefaultColor));
+    SetBorder(views::CreateEmptyBorder(gfx::Insets(kSettingsIconBorder)));
+    SetProperty(kHUDClickHandler, HTCLIENT);
+  }
+
+  SettingsButton(const SettingsButton&) = delete;
+  SettingsButton& operator=(const SettingsButton&) = delete;
+
+  ~SettingsButton() override = default;
+
+ protected:
+  // ImageButton
+  void PaintButtonContents(gfx::Canvas* canvas) override {
+    views::ImageButton::PaintButtonContents(canvas);
+
+    SkPath path;
+    path.moveTo(0, height());
+    path.lineTo(height(), width());
+
+    cc::PaintFlags flags;
+    flags.setAntiAlias(true);
+    flags.setBlendMode(SkBlendMode::kSrc);
+    flags.setStyle(cc::PaintFlags::kStroke_Style);
+    flags.setStrokeWidth(1);
+    flags.setColor(kHUDDefaultColor);
+    canvas->DrawPath(path, flags);
+  }
+};
+
+BEGIN_METADATA(SettingsButton)
+METADATA_PARENT_CLASS(ImageButton)
+END_METADATA()
+
+// Basically FillLayout that matches host size to the given data view.
+// Padding will take the rest of the host view to the right.
+class HUDHeaderLayout : public views::LayoutManager {
+ public:
+  HUDHeaderLayout(const views::View* data_view, views::View* padding)
+      : data_view_(data_view), padding_(padding) {}
+
+  HUDHeaderLayout(const HUDHeaderLayout&) = delete;
+  HUDHeaderLayout& operator=(const HUDHeaderLayout&) = delete;
+
+  ~HUDHeaderLayout() override = default;
+
+  // views::LayoutManager:
+  void Layout(views::View* host) override;
+  gfx::Size GetPreferredSize(const views::View* host) const override;
+
+ private:
+  const views::View* data_view_;
+  views::View* padding_;
+};
+
+gfx::Size HUDHeaderLayout::GetPreferredSize(const views::View* host) const {
+  return data_view_->GetPreferredSize() + padding_->GetPreferredSize();
+}
+
+void HUDHeaderLayout::Layout(views::View* host) {
+  // Assume there are only 3 child views (data, background and padding).
+  DCHECK_EQ(host->children().size(), 3U);
+
+  const gfx::Size preferred_size = data_view_->GetPreferredSize();
+
+  for (auto* child : host->children()) {
+    if (child != padding_) {
+      child->SetPosition({0, 0});
+      child->SetSize(preferred_size);
+    }
+  }
+  // Layout padding
+  padding_->SetPosition({preferred_size.width(), 0});
+  padding_->SetSize({host->width() - preferred_size.width(), host->height()});
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// HUDHeaderView
+
+BEGIN_METADATA(HUDHeaderView)
+METADATA_PARENT_CLASS(View)
+END_METADATA()
+
+HUDHeaderView::HUDHeaderView(HUDDisplayView* hud) {
+  // Header is rendered as horizontal container with three children:
+  // background, buttons container and padding.
+
+  // Header should have background under the buttons area only.
+  // To achieve this we have buttons container view to calculate total width,
+  // and special layout manager that matches size of the background view to the
+  // size of the buttons.
+  //
+  // There is additional "padding" view an the end to draw bottom inner
+  // rounded piece of background.
+
+  views::View* header_background =
+      AddChildView(std::make_unique<views::View>());
+  header_background->SetBackground(std::make_unique<SolidSourceBackground>(
+      kHUDBackground, kTabOverlayCornerRadius));
+
+  views::View* header_buttons = AddChildView(std::make_unique<views::View>());
+  header_buttons
+      ->SetLayoutManager(std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kHorizontal))
+      ->set_cross_axis_alignment(
+          views::BoxLayout::CrossAxisAlignment::kStretch);
+  // Header does not have margin between header and data.
+  // Data has its top margin (kHUDGraphsInset).
+  header_buttons->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(kHUDInset, kHUDInset, 0, kHUDInset)));
+
+  // Add buttons and tab strip.
+  header_buttons->AddChildView(std::make_unique<SettingsButton>(hud));
+  tab_strip_ = header_buttons->AddChildView(std::make_unique<HUDTabStrip>(hud));
+
+  // Padding will take the rest of the header and draw bottom inner left
+  // background padding.
+  views::View* padding = AddChildView(std::make_unique<views::View>());
+  padding->SetBackground(std::make_unique<BottomLeftOuterBackground>(
+      kHUDBackground, kTabOverlayCornerRadius));
+
+  SetLayoutManager(std::make_unique<HUDHeaderLayout>(header_buttons, padding));
+}
+
+HUDHeaderView::~HUDHeaderView() = default;
+
+}  // namespace hud_display
+}  // namespace ash
diff --git a/ash/hud_display/hud_header_view.h b/ash/hud_display/hud_header_view.h
new file mode 100644
index 0000000..743b1400
--- /dev/null
+++ b/ash/hud_display/hud_header_view.h
@@ -0,0 +1,37 @@
+// 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 ASH_HUD_DISPLAY_HUD_HEADER_VIEW_H_
+#define ASH_HUD_DISPLAY_HUD_HEADER_VIEW_H_
+
+#include "ui/views/view.h"
+
+namespace ash {
+namespace hud_display {
+
+class HUDDisplayView;
+class HUDTabStrip;
+
+// HUDHeaderView renders header (with buttons and tabs) of the HUD.
+class HUDHeaderView : public views::View {
+ public:
+  METADATA_HEADER(HUDHeaderView);
+
+  explicit HUDHeaderView(HUDDisplayView* hud);
+
+  HUDHeaderView(const HUDHeaderView&) = delete;
+  HUDHeaderView& operator=(const HUDHeaderView&) = delete;
+
+  ~HUDHeaderView() override;
+
+  HUDTabStrip* tab_strip() { return tab_strip_; }
+
+ private:
+  HUDTabStrip* tab_strip_ = nullptr;  // not owned
+};
+
+}  // namespace hud_display
+}  // namespace ash
+
+#endif  // ASH_HUD_DISPLAY_HUD_HEADER_VIEW_H_
diff --git a/ash/hud_display/hud_settings_view.cc b/ash/hud_display/hud_settings_view.cc
index 0853e29..b9bb882 100644
--- a/ash/hud_display/hud_settings_view.cc
+++ b/ash/hud_display/hud_settings_view.cc
@@ -79,26 +79,23 @@
 METADATA_PARENT_CLASS(View)
 END_METADATA()
 
-constexpr SkColor HUDSettingsView::kDefaultColor;
-
 HUDSettingsView::HUDSettingsView() {
   SetVisible(false);
 
-  auto layout_manager = std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical);
+  views::BoxLayout* layout_manager =
+      SetLayoutManager(std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kVertical));
   layout_manager->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kStart);
-  SetLayoutManager(std::move(layout_manager));
-  SetBorder(views::CreateSolidBorder(1, kDefaultColor));
+  SetBorder(views::CreateSolidBorder(1, kHUDDefaultColor));
 
   auto add_checkbox = [](HUDSettingsView* self,
                          const base::string16& text) -> views::Checkbox* {
-    auto checkbox = std::make_unique<views::Checkbox>(text, self);
-    views::Checkbox* result = checkbox.get();
-    checkbox->SetEnabledTextColors(kDefaultColor);
+    views::Checkbox* checkbox =
+        self->AddChildView(std::make_unique<views::Checkbox>(text, self));
+    checkbox->SetEnabledTextColors(kHUDDefaultColor);
     checkbox->SetProperty(kHUDClickHandler, HTCLIENT);
-    self->AddChildView(std::move(checkbox));
-    return result;
+    return checkbox;
   };
 
   checkbox_handlers_.push_back(std::make_unique<HUDCheckboxHandler>(
diff --git a/ash/hud_display/hud_settings_view.h b/ash/hud_display/hud_settings_view.h
index 5b742f10..60a5a0c3 100644
--- a/ash/hud_display/hud_settings_view.h
+++ b/ash/hud_display/hud_settings_view.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "ash/hud_display/hud_constants.h"
-#include "third_party/skia/include/core/SkColor.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
@@ -22,10 +21,6 @@
  public:
   METADATA_HEADER(HUDSettingsView);
 
-  // Use light orange color.
-  static constexpr SkColor kDefaultColor =
-      SkColorSetARGB(kHUDAlpha, 0xFF, 0xB2, 0x66);
-
   HUDSettingsView();
   ~HUDSettingsView() override;
 
diff --git a/ash/hud_display/tab_strip.cc b/ash/hud_display/tab_strip.cc
new file mode 100644
index 0000000..8c2365d
--- /dev/null
+++ b/ash/hud_display/tab_strip.cc
@@ -0,0 +1,215 @@
+// 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 "ash/hud_display/tab_strip.h"
+
+#include <cmath>
+
+#include "ash/hud_display/hud_display.h"
+#include "ash/hud_display/hud_properties.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/text_constants.h"
+#include "ui/views/layout/layout_manager.h"
+
+namespace ash {
+namespace hud_display {
+namespace {
+
+// The width in pixels of overlaying adjacent tabs. Must be even number.
+constexpr int kTabOverlayWidth = 2 * kTabOverlayCornerRadius / 3;
+
+// Border around tab text (tab overlay width will be added to this).
+constexpr int kTabTitleBorder = 3;
+
+class HUDTabStripLayout : public views::LayoutManager {
+ public:
+  HUDTabStripLayout() = default;
+
+  HUDTabStripLayout(const HUDTabStripLayout&) = delete;
+  HUDTabStripLayout& operator=(const HUDTabStripLayout&) = delete;
+
+  ~HUDTabStripLayout() override = default;
+
+  // views::LayoutManager:
+  void Layout(views::View* host) override;
+  gfx::Size GetPreferredSize(const views::View* host) const override;
+};
+
+gfx::Size HUDTabStripLayout::GetPreferredSize(const views::View* host) const {
+  gfx::Size result;
+  for (const auto* child : host->children()) {
+    const gfx::Size child_preferred = child->GetPreferredSize();
+    // Tab strip is always horizontal.
+    result.set_width(result.width() + child_preferred.width() -
+                     kTabOverlayWidth);
+    result.set_height(std::max(result.height(), child_preferred.height()));
+  }
+  // Assume all children have equal left and right border, which is used to
+  // overlay the tabs. Add one overlay width to compensate one edge.
+  if (host->children().size())
+    result.set_width(result.width() + kTabOverlayWidth);
+
+  // Add right padding equal to the padding of the settings icon.
+  result.set_width(result.width() + kSettingsIconBorder);
+  return result;
+}
+
+void HUDTabStripLayout::Layout(views::View* host) {
+  // Assume all children have equal left and right border, which is used to
+  // overlay the tabs.
+  int left_offset = 0;
+  for (auto* child : host->children()) {
+    const gfx::Size preferred = child->GetPreferredSize();
+    const gfx::Size child_size({preferred.width(), host->height()});
+    child->SetSize(child_size);
+    child->SetPosition({left_offset, 0});
+    left_offset += child_size.width() - kTabOverlayWidth;
+  }
+}
+
+}  // namespace
+
+BEGIN_METADATA(HUDTabButton)
+METADATA_PARENT_CLASS(LabelButton)
+END_METADATA()
+
+HUDTabButton::HUDTabButton(Style style,
+                           HUDTabStrip* tab_strip,
+                           const DisplayModes display_mode,
+                           const base::string16& text)
+    : views::LabelButton(tab_strip, text),
+      style_(style),
+      display_mode_(display_mode) {
+  SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  SetEnabledTextColors(kHUDDefaultColor);
+  SetProperty(kHUDClickHandler, HTCLIENT);
+  SetBorder(views::CreateEmptyBorder(
+      kSettingsIconBorder, kTabOverlayWidth + kTabTitleBorder,
+      kSettingsIconBorder, kTabOverlayWidth + kTabTitleBorder));
+}
+
+void HUDTabButton::SetStyle(Style style) {
+  if (style_ == style)
+    return;
+
+  style_ = style;
+  SchedulePaint();
+}
+
+void HUDTabButton::PaintButtonContents(gfx::Canvas* canvas) {
+  // Horizontal offset from tab {0,0} where two tab arcs cross.
+  constexpr int kTabArcCrossedX = kTabOverlayWidth / 2;
+
+  // Reduce kTabArcCrossedX by one pixel when calculating partial arc so that
+  // the pixels along kTabArcCrossedX vertical line are drawn by full arc only.
+  static const float kTabPartialArcAngle =
+      90 - 180 *
+               asinf((kTabOverlayCornerRadius - kTabArcCrossedX + 1) /
+                     (float)kTabOverlayCornerRadius) /
+               M_PI;
+
+  const int kCircleSize = kTabOverlayCornerRadius * 2;
+  const int right_edge = width();
+  const int bottom_edge = height();
+
+  SkPath path;
+
+  // Draw left vertical line and arc
+  if (style_ == Style::RIGHT) {
+    /* |true| - move to the start of the arc */
+    path.arcTo({0, 0, kCircleSize, kCircleSize}, -90 - kTabPartialArcAngle,
+               kTabPartialArcAngle, true);
+  } else {
+    if (style_ == Style::LEFT) {
+      // Draw bottom line from the right edge. Adjust for 2 pixels crossing the
+      // right vertical line.
+      path.moveTo(right_edge - kTabOverlayWidth / 2 - 2, bottom_edge);
+      path.lineTo(0, bottom_edge);
+    } else {
+      // No bottom line. Just move to the start of the vertical line.
+      path.moveTo(0, bottom_edge);
+    }
+    /* |false| will draw straight line to the start of the arc */
+    path.arcTo({0, 0, kCircleSize - 1, kCircleSize}, -180, 90, false);
+  }
+  // Draw top line, right arc and right vertical line
+  if (style_ == Style::LEFT) {
+    /* |false| will draw straight line to the start of the arc */
+    path.arcTo({right_edge - kCircleSize, 0, right_edge, kCircleSize}, -90,
+               kTabPartialArcAngle, false);
+  } else {
+    /* |false| will draw straight line to the start of the arc */
+    path.arcTo({right_edge - kCircleSize, 0, right_edge, kCircleSize}, -90, 90,
+               false);
+    path.lineTo(right_edge, bottom_edge);
+    if (style_ == Style::RIGHT) {
+      // Draw bottom line to the left edge. Adjust for 2 pixels crossing the
+      // left vertical line.
+      path.lineTo(kTabOverlayWidth / 2 + 2, bottom_edge);
+    }
+  }
+
+  cc::PaintFlags flags;
+  flags.setAntiAlias(true);
+  flags.setBlendMode(SkBlendMode::kSrc);
+  flags.setStyle(cc::PaintFlags::kStroke_Style);
+  flags.setStrokeWidth(1);
+  flags.setColor(kHUDDefaultColor);
+  canvas->DrawPath(path, flags);
+}
+
+BEGIN_METADATA(HUDTabStrip)
+METADATA_PARENT_CLASS(View)
+END_METADATA()
+
+HUDTabStrip::HUDTabStrip(HUDDisplayView* hud) : hud_(hud) {
+  SetLayoutManager(std::make_unique<HUDTabStripLayout>());
+}
+
+HUDTabStrip::~HUDTabStrip() = default;
+
+HUDTabButton* HUDTabStrip::AddTabButton(HUDDisplayView* hud,
+                                        const DisplayModes display_mode,
+                                        const base::string16& label) {
+  // Make first tab active by default.
+  HUDTabButton* tab_button = AddChildView(std::make_unique<HUDTabButton>(
+      tabs_.size() ? HUDTabButton::Style::RIGHT : HUDTabButton::Style::ACTIVE,
+      this, display_mode, label));
+  tabs_.push_back(tab_button);
+  return tab_button;
+}
+
+void HUDTabStrip::ButtonPressed(views::Button* sender,
+                                const ui::Event& /*event*/) {
+  for (const auto* tab : tabs_) {
+    if (tab == sender) {
+      hud_->TabButtonPressed(tab);
+      return;
+    }
+  }
+  NOTREACHED();
+}
+
+void HUDTabStrip::ActivateTab(const views::View* active_tab_button) {
+  // True if we find given active tab.
+  bool found = false;
+
+  for (HUDTabButton* tab : tabs_) {
+    if (found) {
+      tab->SetStyle(HUDTabButton::Style::RIGHT);
+      continue;
+    }
+    if (tab == active_tab_button) {
+      found = true;
+      tab->SetStyle(HUDTabButton::Style::ACTIVE);
+      continue;
+    }
+    tab->SetStyle(HUDTabButton::Style::LEFT);
+  }
+  DCHECK(found);
+}
+
+}  // namespace hud_display
+}  // namespace ash
diff --git a/ash/hud_display/tab_strip.h b/ash/hud_display/tab_strip.h
new file mode 100644
index 0000000..b3ac3b0
--- /dev/null
+++ b/ash/hud_display/tab_strip.h
@@ -0,0 +1,90 @@
+// 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 ASH_HUD_DISPLAY_TAB_STRIP_H_
+#define ASH_HUD_DISPLAY_TAB_STRIP_H_
+
+#include "ash/hud_display/hud_constants.h"
+#include "base/strings/string16.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/metadata/metadata_header_macros.h"
+
+namespace gfx {
+class Canvas;
+}
+
+namespace views {
+class View;
+}
+
+namespace ash {
+namespace hud_display {
+
+class HUDDisplayView;
+class HUDTabStrip;
+
+class HUDTabButton : public views::LabelButton {
+ public:
+  // Defines tab paint style.
+  enum class Style {
+    LEFT,    // Tab to the left of the active tab.
+    ACTIVE,  // Active tab.
+    RIGHT    // Tab to the right of the active tab.
+  };
+
+  METADATA_HEADER(HUDTabButton);
+
+  HUDTabButton(Style style,
+               HUDTabStrip* tab_strip,
+               const DisplayModes display_mode,
+               const base::string16& text);
+  HUDTabButton(const HUDTabButton&) = delete;
+  HUDTabButton& operator=(const HUDTabButton&) = delete;
+
+  ~HUDTabButton() override = default;
+
+  void SetStyle(Style style);
+
+  DisplayModes display_mode() const { return display_mode_; }
+
+ protected:
+  // views::LabelButton:
+  void PaintButtonContents(gfx::Canvas* canvas) override;
+
+ private:
+  Style style_ = Style::LEFT;
+
+  // Tab activation sends this display mode to the HUD.
+  DisplayModes display_mode_ = DisplayModes::DEFAULT;
+};
+
+class HUDTabStrip : public views::View, public views::ButtonListener {
+ public:
+  METADATA_HEADER(HUDTabStrip);
+
+  explicit HUDTabStrip(HUDDisplayView* hud);
+
+  HUDTabStrip(const HUDTabStrip&) = delete;
+  HUDTabStrip& operator=(const HUDTabStrip&) = delete;
+
+  ~HUDTabStrip() override;
+
+  HUDTabButton* AddTabButton(HUDDisplayView* hud,
+                             const DisplayModes display_mode,
+                             const base::string16& label);
+
+  // views::ButtonListener
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  // Mark tabs around the active one need repaint to modify borders.
+  void ActivateTab(const views::View* active_tab_button);
+
+ private:
+  HUDDisplayView* hud_;
+  std::vector<HUDTabButton*> tabs_;  // Ordered list of child tabs.
+};
+
+}  // namespace hud_display
+}  // namespace ash
+#endif  // ASH_HUD_DISPLAY_TAB_STRIP_H_
diff --git a/ash/in_session_auth/auth_dialog_debug_view.cc b/ash/in_session_auth/auth_dialog_debug_view.cc
index 18fb49f..28d444a9 100644
--- a/ash/in_session_auth/auth_dialog_debug_view.cc
+++ b/ash/in_session_auth/auth_dialog_debug_view.cc
@@ -12,10 +12,12 @@
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/in_session_auth_dialog_controller.h"
+#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/bind_helpers.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/controls/label.h"
@@ -31,7 +33,7 @@
 };
 
 const char kTitle[] = "Verify it's you";
-const char kFingerprintPrompt[] = "Touch the fingerprint sensor";
+const char kFingerprintPrompt[] = "Authenticate with fingerprint";
 // If fingerprint option is available, password input field will be hidden
 // until the user taps the MoreOptions button.
 const char kMoreOptionsButtonText[] = "More options";
@@ -47,9 +49,60 @@
 const int kTitleFontSize = 14;
 const int kPromptFontSize = 12;
 
+constexpr int kFingerprintIconSizeDp = 28;
+constexpr int kFingerprintIconTopSpacingDp = 20;
+constexpr int kSpacingBetweenFingerprintIconAndLabelDp = 15;
+constexpr int kFingerprintViewWidthDp = 204;
+
 }  // namespace
 
-AuthDialogDebugView::AuthDialogDebugView() {
+// Consists of fingerprint icon view and a label.
+class AuthDialogDebugView::FingerprintView : public views::View {
+ public:
+  FingerprintView() {
+    SetBorder(views::CreateEmptyBorder(kFingerprintIconTopSpacingDp, 0, 0, 0));
+
+    auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+        views::BoxLayout::Orientation::kVertical, gfx::Insets(),
+        kSpacingBetweenFingerprintIconAndLabelDp));
+    layout->set_main_axis_alignment(
+        views::BoxLayout::MainAxisAlignment::kCenter);
+
+    icon_ = AddChildView(std::make_unique<AnimatedRoundedImageView>(
+        gfx::Size(kFingerprintIconSizeDp, kFingerprintIconSizeDp),
+        0 /*corner_radius*/));
+    icon_->SetImage(gfx::CreateVectorIcon(
+        kLockScreenFingerprintIcon, kFingerprintIconSizeDp, SK_ColorDKGRAY));
+
+    label_ = AddChildView(std::make_unique<views::Label>());
+    label_->SetSubpixelRenderingEnabled(false);
+    label_->SetAutoColorReadabilityEnabled(false);
+    label_->SetEnabledColor(SK_ColorDKGRAY);
+    label_->SetMultiLine(true);
+    label_->SetText(base::UTF8ToUTF16(kFingerprintPrompt));
+
+    SetVisible(true);
+  }
+  FingerprintView(const FingerprintView&) = delete;
+  FingerprintView& operator=(const FingerprintView&) = delete;
+  ~FingerprintView() override = default;
+
+  // views::View:
+  gfx::Size CalculatePreferredSize() const override {
+    gfx::Size size = views::View::CalculatePreferredSize();
+    size.set_width(kFingerprintViewWidthDp);
+    return size;
+  }
+
+ private:
+  views::Label* label_ = nullptr;
+  AnimatedRoundedImageView* icon_ = nullptr;
+};
+
+AuthDialogDebugView::AuthDialogDebugView(uint32_t auth_methods)
+    : auth_methods_(auth_methods) {
+  DCHECK(auth_methods_ & kAuthPassword);
+
   SetLayoutManager(std::make_unique<views::FillLayout>());
   container_ = AddChildView(std::make_unique<NonAccessibleView>());
   container_->SetBackground(views::CreateSolidBackground(SK_ColorWHITE));
@@ -60,17 +113,21 @@
   main_layout_->set_main_axis_alignment(
       views::BoxLayout::MainAxisAlignment::kStart);
   main_layout_->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kStart);
+      views::BoxLayout::CrossAxisAlignment::kCenter);
 
   AddVerticalSpacing(kTopVerticalSpacing);
   AddTitleView();
   AddVerticalSpacing(kVerticalSpacingBetweenTitleAndPrompt);
-  AddPromptView();
   // TODO(b/156258540): Add proper spacing once all elements are determined.
   AddPasswordView();
   AddPinView();
   AddVerticalSpacing(kVerticalSpacingBetweenPasswordAndPINKeyboard);
-  // TODO(b/156258540): Add fingerprint icon view and proper spacing.
+
+  if (auth_methods_ & kAuthFingerprint) {
+    fingerprint_view_ =
+        container_->AddChildView(std::make_unique<FingerprintView>());
+  }
+
   AddActionButtonsView();
   AddVerticalSpacing(kBottomVerticalSpacing);
 
@@ -125,9 +182,11 @@
   password_view_->SetFocusEnabledForChildViews(true);
   password_view_->SetVisible(true);
 
-  // TODO(b/156258540): Set this text according to "has PIN or not".
   password_view_->SetPlaceholderText(
-      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_POD_PASSWORD_PIN_PLACEHOLDER));
+      (auth_methods_ & kAuthPin)
+          ? l10n_util::GetStringUTF16(
+                IDS_ASH_LOGIN_POD_PASSWORD_PIN_PLACEHOLDER)
+          : l10n_util::GetStringUTF16(IDS_ASH_LOGIN_POD_PASSWORD_PLACEHOLDER));
 }
 
 void AuthDialogDebugView::AddPinView() {
@@ -139,7 +198,7 @@
                           base::Unretained(password_view_)),
       base::BindRepeating(&LoginPasswordView::SubmitPassword,
                           base::Unretained(password_view_))));
-  pin_view_->SetVisible(true);
+  pin_view_->SetVisible(auth_methods_ & kAuthPin);
 }
 
 void AuthDialogDebugView::InitPasswordView() {
diff --git a/ash/in_session_auth/auth_dialog_debug_view.h b/ash/in_session_auth/auth_dialog_debug_view.h
index a759f8e..79de4bad 100644
--- a/ash/in_session_auth/auth_dialog_debug_view.h
+++ b/ash/in_session_auth/auth_dialog_debug_view.h
@@ -25,7 +25,15 @@
 // AuthDialogController.
 class AuthDialogDebugView : public views::View, public views::ButtonListener {
  public:
-  AuthDialogDebugView();
+  // Flags which describe the set of currently visible auth methods.
+  enum AuthMethods {
+    kAuthNone = 0,              // No auth methods.
+    kAuthPassword = 1 << 0,     // Display password.
+    kAuthPin = 1 << 1,          // Display PIN keyboard.
+    kAuthFingerprint = 1 << 2,  // Use fingerprint to unlock.
+  };
+
+  explicit AuthDialogDebugView(uint32_t auth_methods);
   AuthDialogDebugView(const AuthDialogDebugView&) = delete;
   AuthDialogDebugView& operator=(const AuthDialogDebugView&) = delete;
   ~AuthDialogDebugView() override;
@@ -34,6 +42,8 @@
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
  private:
+  class FingerprintView;
+
   // Add a view for dialog title.
   void AddTitleView();
 
@@ -84,6 +94,11 @@
   // PIN pad view.
   LoginPinView* pin_view_ = nullptr;
 
+  FingerprintView* fingerprint_view_ = nullptr;
+
+  // Flags of auth methods that should be visible.
+  uint32_t auth_methods_ = 0u;
+
   // Show other authentication mechanisms if more than one.
   views::LabelButton* more_options_button_ = nullptr;
 
diff --git a/ash/in_session_auth/in_session_auth_dialog.cc b/ash/in_session_auth/in_session_auth_dialog.cc
index a566d41..5a3174e 100644
--- a/ash/in_session_auth/in_session_auth_dialog.cc
+++ b/ash/in_session_auth/in_session_auth_dialog.cc
@@ -15,7 +15,9 @@
 namespace ash {
 namespace {
 
-constexpr gfx::Size kDefaultSize(340, 224);
+// The initial height does nothing except determining the vertical position of
+// the dialog, since the dialog is centered with the initial height.
+constexpr gfx::Size kDefaultSize(340, 490);
 
 class AuthDialogWidgetDelegate : public views::WidgetDelegate {
  public:
@@ -54,11 +56,16 @@
 
 }  // namespace
 
-InSessionAuthDialog::InSessionAuthDialog() {
+InSessionAuthDialog::InSessionAuthDialog(uint32_t auth_methods) {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kShowAuthDialogDevOverlay)) {
     widget_ = CreateAuthDialogWidget(nullptr);
-    widget_->SetContentsView(std::make_unique<AuthDialogDebugView>());
+    auto* contents_view = widget_->SetContentsView(
+        std::make_unique<AuthDialogDebugView>(auth_methods));
+    gfx::Rect bound = widget_->GetWindowBoundsInScreen();
+    // Calculate initial height based on which child views are shown.
+    bound.set_height(contents_view->GetPreferredSize().height());
+    widget_->SetBounds(bound);
 
     widget_->Show();
   }
diff --git a/ash/in_session_auth/in_session_auth_dialog.h b/ash/in_session_auth/in_session_auth_dialog.h
index a9d72026..41969b6 100644
--- a/ash/in_session_auth/in_session_auth_dialog.h
+++ b/ash/in_session_auth/in_session_auth_dialog.h
@@ -20,7 +20,7 @@
 // completed.
 class InSessionAuthDialog {
  public:
-  InSessionAuthDialog();
+  explicit InSessionAuthDialog(uint32_t auth_methods);
   InSessionAuthDialog(const InSessionAuthDialog&) = delete;
   InSessionAuthDialog& operator=(const InSessionAuthDialog&) = delete;
   ~InSessionAuthDialog();
diff --git a/ash/in_session_auth/in_session_auth_dialog_controller_impl.cc b/ash/in_session_auth/in_session_auth_dialog_controller_impl.cc
index 9337b75..219d355d 100644
--- a/ash/in_session_auth/in_session_auth_dialog_controller_impl.cc
+++ b/ash/in_session_auth/in_session_auth_dialog_controller_impl.cc
@@ -4,7 +4,10 @@
 
 #include "ash/in_session_auth/in_session_auth_dialog_controller_impl.h"
 
+#include "ash/in_session_auth/auth_dialog_debug_view.h"
 #include "ash/public/cpp/in_session_auth_dialog_client.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/strings/string_util.h"
@@ -23,7 +26,29 @@
 }
 
 void InSessionAuthDialogControllerImpl::ShowAuthenticationDialog() {
-  dialog_ = std::make_unique<InSessionAuthDialog>();
+  DCHECK(client_);
+
+  AccountId account_id =
+      Shell::Get()->session_controller()->GetActiveAccountId();
+  // Password should always be available.
+  uint32_t auth_methods = AuthDialogDebugView::kAuthPassword;
+
+  if (client_->IsFingerprintAuthAvailable(account_id))
+    auth_methods |= AuthDialogDebugView::kAuthFingerprint;
+
+  client_->CheckPinAuthAvailability(
+      account_id,
+      base::BindOnce(&InSessionAuthDialogControllerImpl::OnPinCanAuthenticate,
+                     weak_factory_.GetWeakPtr(), auth_methods));
+}
+
+void InSessionAuthDialogControllerImpl::OnPinCanAuthenticate(
+    uint32_t auth_methods,
+    bool pin_auth_available) {
+  if (pin_auth_available)
+    auth_methods |= AuthDialogDebugView::kAuthPin;
+
+  dialog_ = std::make_unique<InSessionAuthDialog>(auth_methods);
 }
 
 void InSessionAuthDialogControllerImpl::DestroyAuthenticationDialog() {
diff --git a/ash/in_session_auth/in_session_auth_dialog_controller_impl.h b/ash/in_session_auth/in_session_auth_dialog_controller_impl.h
index 2359fe52..ab73ff3 100644
--- a/ash/in_session_auth/in_session_auth_dialog_controller_impl.h
+++ b/ash/in_session_auth/in_session_auth_dialog_controller_impl.h
@@ -13,6 +13,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 
+class AccountId;
+
 namespace ash {
 
 class InSessionAuthDialogClient;
@@ -42,6 +44,9 @@
       OnAuthenticateCallback callback) override;
 
  private:
+  bool IsFingerprintAvailable(const AccountId& account_id);
+  void OnPinCanAuthenticate(uint32_t auth_methods, bool pin_auth_available);
+
   // Callback to execute when auth on ChromeOS side completes.
   void OnAuthenticateComplete(OnAuthenticateCallback callback, bool success);
 
diff --git a/ash/in_session_auth/mock_in_session_auth_dialog_client.h b/ash/in_session_auth/mock_in_session_auth_dialog_client.h
index 81ad3f5e..e8990811 100644
--- a/ash/in_session_auth/mock_in_session_auth_dialog_client.h
+++ b/ash/in_session_auth/mock_in_session_auth_dialog_client.h
@@ -26,6 +26,17 @@
                bool authenticated_by_pin,
                base::OnceCallback<void(bool)> callback),
               (override));
+
+  MOCK_METHOD(bool,
+              IsFingerprintAuthAvailable,
+              (const AccountId& account_id),
+              (override));
+
+  MOCK_METHOD(void,
+              CheckPinAuthAvailability,
+              (const AccountId& account_id,
+               base::OnceCallback<void(bool)> callback),
+              (override));
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 8794eaeb..430b1d2d 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -37,18 +37,9 @@
 const base::Feature kDragToSnapInClamshellMode{
     "DragToSnapInClamshellMode", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kMovablePartialScreenshot{
-    "MovablePartialScreenshot", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kEnableOverviewRoundedCorners{
-    "EnableOverviewRoundedCorners", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kLimitAltTabToActiveDesk{"LimitAltTabToActiveDesk",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kPerDeskShelf{"PerDeskShelf",
-                                  base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kLockScreenNotifications{"LockScreenNotifications",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -71,6 +62,9 @@
 const base::Feature kMediaSessionNotification{"MediaSessionNotification",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kMovablePartialScreenshot{
+    "MovablePartialScreenshot", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kMultiDisplayOverviewAndSplitView{
     "MultiDisplayOverviewAndSplitView", base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -85,6 +79,9 @@
 const base::Feature kNotificationScrollBar{"NotificationScrollBar",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kPerDeskShelf{"PerDeskShelf",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kPipRoundedCorners{"PipRoundedCorners",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 2d2a561..aa94190 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -43,22 +43,10 @@
 // TODO(crbug.com/890029): Remove this when the feature is fully launched.
 ASH_PUBLIC_EXPORT extern const base::Feature kDragToSnapInClamshellMode;
 
-// Enables resizing/moving the selection region for partial screenshot.
-ASH_PUBLIC_EXPORT extern const base::Feature kMovablePartialScreenshot;
-
-// Enables rounded corners in overview mode for testing.
-// TODO(crbug.com/903486): Remove this when new rounded corners implementation
-// has landed.
-ASH_PUBLIC_EXPORT extern const base::Feature kEnableOverviewRoundedCorners;
-
 // Limits the windows listed in Alt-Tab to the ones in the currently active
 // desk.
 ASH_PUBLIC_EXPORT extern const base::Feature kLimitAltTabToActiveDesk;
 
-// Limits the items on the shelf to the ones associated with windows the
-// currently active desk.
-ASH_PUBLIC_EXPORT extern const base::Feature kPerDeskShelf;
-
 // Enables notifications on the lock screen.
 ASH_PUBLIC_EXPORT extern const base::Feature kLockScreenNotifications;
 
@@ -89,6 +77,9 @@
 // TODO(beccahughes): Remove after launch. (https://crbug.com/897836)
 ASH_PUBLIC_EXPORT extern const base::Feature kMediaSessionNotification;
 
+// Enables resizing/moving the selection region for partial screenshot.
+ASH_PUBLIC_EXPORT extern const base::Feature kMovablePartialScreenshot;
+
 // Enables multi-display support for overview and split view.
 // TODO(crbug.com/952461): Remove this when the feature is fully launched.
 ASH_PUBLIC_EXPORT extern const base::Feature kMultiDisplayOverviewAndSplitView;
@@ -106,6 +97,10 @@
 // Enables notification scroll bar in UnifiedSystemTray.
 ASH_PUBLIC_EXPORT extern const base::Feature kNotificationScrollBar;
 
+// Limits the items on the shelf to the ones associated with windows the
+// currently active desk.
+ASH_PUBLIC_EXPORT extern const base::Feature kPerDeskShelf;
+
 // Enables rounded corners for the Picture-in-picture window.
 ASH_PUBLIC_EXPORT extern const base::Feature kPipRoundedCorners;
 
diff --git a/ash/public/cpp/in_session_auth_dialog_client.h b/ash/public/cpp/in_session_auth_dialog_client.h
index e6312d8..f5582198 100644
--- a/ash/public/cpp/in_session_auth_dialog_client.h
+++ b/ash/public/cpp/in_session_auth_dialog_client.h
@@ -9,6 +9,7 @@
 
 #include "ash/public/cpp/ash_public_export.h"
 #include "base/callback_forward.h"
+#include "components/account_id/account_id.h"
 
 namespace ash {
 
@@ -25,6 +26,14 @@
       bool authenticated_by_pin,
       base::OnceCallback<void(bool)> callback) = 0;
 
+  // Check whether fingerprint auth is available for |account_id|.
+  virtual bool IsFingerprintAuthAvailable(const AccountId& account_id) = 0;
+
+  // Check whether PIN auth is available for |account_id|.
+  virtual void CheckPinAuthAvailability(
+      const AccountId& account_id,
+      base::OnceCallback<void(bool)> callback) = 0;
+
  protected:
   virtual ~InSessionAuthDialogClient() = default;
 };
diff --git a/base/trace_event/traced_value.cc b/base/trace_event/traced_value.cc
index 68e7dc8..f8ee48a 100644
--- a/base/trace_event/traced_value.cc
+++ b/base/trace_event/traced_value.cc
@@ -597,6 +597,11 @@
   writer_->AppendString(value);
 }
 
+void TracedValue::AppendPointer(void* value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  writer_->AppendString(PointerToString(value));
+}
+
 void TracedValue::BeginArray() {
   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
   DEBUG_PUSH_CONTAINER(kStackTypeArray);
@@ -642,71 +647,202 @@
   writer_->EstimateTraceMemoryOverhead(overhead);
 }
 
-TracedValue::DictionaryItem::DictionaryItem(const char* name, int value) {
-  name_ = name;
+TracedValue::Array::Array(const std::initializer_list<ArrayItem> items) {
+  items_ = std::move(items);
+}
+
+TracedValue::Array::Array(TracedValue::Array&& other) {
+  items_ = std::move(other.items_);
+}
+
+void TracedValue::Array::WriteToValue(TracedValue* value) const {
+  for (const auto& item : items_) {
+    item.WriteToValue(value);
+  }
+}
+
+TracedValue::Dictionary::Dictionary(
+    const std::initializer_list<DictionaryItem> items) {
+  items_ = items;
+}
+
+TracedValue::Dictionary::Dictionary(TracedValue::Dictionary&& other) {
+  items_ = std::move(other.items_);
+}
+
+void TracedValue::Dictionary::WriteToValue(TracedValue* value) const {
+  for (const auto& item : items_) {
+    item.WriteToValue(value);
+  }
+}
+
+TracedValue::ValueHolder::ValueHolder(int value) {
   kept_value_.int_value = value;
   kept_value_type_ = KeptValueType::kIntType;
 }
 
-TracedValue::DictionaryItem::DictionaryItem(const char* name, double value) {
-  name_ = name;
+TracedValue::ValueHolder::ValueHolder(double value) {
   kept_value_.double_value = value;
   kept_value_type_ = KeptValueType::kDoubleType;
 }
 
-TracedValue::DictionaryItem::DictionaryItem(const char* name, bool value) {
-  name_ = name;
+TracedValue::ValueHolder::ValueHolder(bool value) {
   kept_value_.bool_value = value;
   kept_value_type_ = KeptValueType::kBoolType;
 }
 
-TracedValue::DictionaryItem::DictionaryItem(const char* name,
-                                            base::StringPiece value) {
-  name_ = name;
+TracedValue::ValueHolder::ValueHolder(base::StringPiece value) {
   kept_value_.string_piece_value = value;
   kept_value_type_ = KeptValueType::kStringPieceType;
 }
 
-TracedValue::DictionaryItem::DictionaryItem(const char* name, void* value) {
-  name_ = name;
+TracedValue::ValueHolder::ValueHolder(const std::string& value) {
+  kept_value_.string_piece_value = value;
+  kept_value_type_ = KeptValueType::kStringPieceType;
+}
+
+TracedValue::ValueHolder::ValueHolder(void* value) {
   kept_value_.void_ptr_value = value;
   kept_value_type_ = KeptValueType::kVoidPtrType;
 }
 
-TracedValue::DictionaryItem::DictionaryItem(const char* name,
-                                            const char* value) {
-  name_ = name;
+TracedValue::ValueHolder::ValueHolder(const char* value) {
   kept_value_.string_piece_value = value;
   kept_value_type_ = KeptValueType::kStringPieceType;
 }
 
-void TracedValue::DictionaryItem::WriteToValue(TracedValue* value) const {
-  switch (kept_value_type_) {
+TracedValue::ValueHolder::ValueHolder(TracedValue::Dictionary& value) {
+  new (&kept_value_.dictionary_value) TracedValue::Dictionary(std::move(value));
+  kept_value_type_ = KeptValueType::kDictionaryType;
+}
+
+TracedValue::ValueHolder::ValueHolder(TracedValue::Array& value) {
+  new (&kept_value_.array_value) TracedValue::Array(std::move(value));
+  kept_value_type_ = KeptValueType::kArrayType;
+}
+
+TracedValue::ValueHolder::ValueHolder(TracedValue::ValueHolder&& other) {
+  // Remember to call a destructor if ever necessary.
+  switch (other.kept_value_type_) {
     case KeptValueType::kIntType: {
-      value->SetInteger(name_, kept_value_.int_value);
+      kept_value_.int_value = other.kept_value_.int_value;
       break;
     }
     case KeptValueType::kDoubleType: {
-      value->SetDouble(name_, kept_value_.double_value);
+      kept_value_.double_value = other.kept_value_.double_value;
       break;
     }
     case KeptValueType::kBoolType: {
-      value->SetBoolean(name_, kept_value_.bool_value);
+      kept_value_.bool_value = other.kept_value_.bool_value;
       break;
     }
     case KeptValueType::kStringPieceType: {
-      value->SetString(name_, kept_value_.string_piece_value);
+      kept_value_.string_piece_value = other.kept_value_.string_piece_value;
       break;
     }
     case KeptValueType::kVoidPtrType: {
-      value->SetPointer(name_, kept_value_.void_ptr_value);
+      kept_value_.void_ptr_value = other.kept_value_.void_ptr_value;
+      break;
+    }
+    case KeptValueType::kArrayType: {
+      new (&kept_value_.array_value)
+          TracedValue::Array(std::move(other.kept_value_.array_value));
+      break;
+    }
+    case KeptValueType::kDictionaryType: {
+      new (&kept_value_.dictionary_value) TracedValue::Dictionary(
+          std::move(other.kept_value_.dictionary_value));
+      break;
+    }
+  }
+  kept_value_type_ = other.kept_value_type_;
+}
+
+void TracedValue::ValueHolder::WriteToValue(TracedValue* value) const {
+  switch (kept_value_type_) {
+    case KeptValueType::kIntType: {
+      value->AppendInteger(kept_value_.int_value);
+      break;
+    }
+    case KeptValueType::kDoubleType: {
+      value->AppendDouble(kept_value_.double_value);
+      break;
+    }
+    case KeptValueType::kBoolType: {
+      value->AppendBoolean(kept_value_.bool_value);
+      break;
+    }
+    case KeptValueType::kStringPieceType: {
+      value->AppendString(kept_value_.string_piece_value);
+      break;
+    }
+    case KeptValueType::kVoidPtrType: {
+      value->AppendPointer(kept_value_.void_ptr_value);
+      break;
+    }
+    case KeptValueType::kArrayType: {
+      value->BeginArray();
+      kept_value_.array_value.WriteToValue(value);
+      value->EndArray();
+      break;
+    }
+    case KeptValueType::kDictionaryType: {
+      value->BeginDictionary();
+      kept_value_.dictionary_value.WriteToValue(value);
+      value->EndDictionary();
       break;
     }
   }
 }
 
+void TracedValue::ValueHolder::WriteToValue(const char* name,
+                                            TracedValue* value) const {
+  switch (kept_value_type_) {
+    case KeptValueType::kIntType: {
+      value->SetInteger(name, kept_value_.int_value);
+      break;
+    }
+    case KeptValueType::kDoubleType: {
+      value->SetDouble(name, kept_value_.double_value);
+      break;
+    }
+    case KeptValueType::kBoolType: {
+      value->SetBoolean(name, kept_value_.bool_value);
+      break;
+    }
+    case KeptValueType::kStringPieceType: {
+      value->SetString(name, kept_value_.string_piece_value);
+      break;
+    }
+    case KeptValueType::kVoidPtrType: {
+      value->SetPointer(name, kept_value_.void_ptr_value);
+      break;
+    }
+    case KeptValueType::kArrayType: {
+      value->BeginArray(name);
+      kept_value_.array_value.WriteToValue(value);
+      value->EndArray();
+      break;
+    }
+    case KeptValueType::kDictionaryType: {
+      value->BeginDictionary(name);
+      kept_value_.dictionary_value.WriteToValue(value);
+      value->EndDictionary();
+      break;
+    }
+  }
+}
+
+void TracedValue::ArrayItem::WriteToValue(TracedValue* value) const {
+  ValueHolder::WriteToValue(value);
+}
+
+void TracedValue::DictionaryItem::WriteToValue(TracedValue* value) const {
+  ValueHolder::WriteToValue(name_, value);
+}
+
 std::unique_ptr<TracedValue> TracedValue::Build(
-    std::initializer_list<DictionaryItem> items) {
+    const std::initializer_list<DictionaryItem> items) {
   std::unique_ptr<TracedValue> value(new TracedValue());
   for (const auto& item : items) {
     item.WriteToValue(value.get());
diff --git a/base/trace_event/traced_value.h b/base/trace_event/traced_value.h
index bdb6784..5c8d04f 100644
--- a/base/trace_event/traced_value.h
+++ b/base/trace_event/traced_value.h
@@ -57,6 +57,7 @@
   void AppendDouble(double);
   void AppendBoolean(bool);
   void AppendString(base::StringPiece);
+  void AppendPointer(void*);
   void BeginArray();
   void BeginDictionary();
 
@@ -66,57 +67,116 @@
 
   void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override;
 
-  // TracedValue::Build is a friend of TracedValue::DictionaryItem.
+  class BASE_EXPORT Array;
+  class BASE_EXPORT Dictionary;
+  class BASE_EXPORT ValueHolder;
+  class BASE_EXPORT ArrayItem;
   class BASE_EXPORT DictionaryItem;
 
-  // Helper to enable easier initialization of TracedValue. This is intended for
-  // quick local debugging as there is overhead of creating
-  // std::initializer_list of name-value objects. This method does not support
-  // creation of dictionaries or arrays.
+  // Helper to enable easier initialization of |TracedValue|. This is intended
+  // for quick local debugging as there is overhead of creating
+  // |std::initializer_list| of name-value objects (in the case of containers
+  // the value is also a |std::initializer_list|). Generally the helper types
+  // |TracedValue::Dictionary|, |TracedValue::Array|,
+  // |TracedValue::DictionaryItem|, |TracedValue::ArrayItem| must be valid as
+  // well as their internals (e.g., |base::StringPiece| data should be valid
+  // when |TracedValue::Build| is called; |TracedValue::Array| or
+  // |TracedValue::Dictionary| holds a |std::initializer_list| whose underlying
+  // array needs to be valid when calling |TracedValue::Build|).
   //
   // Example:
   //    auto value = TracedValue::Build({
   //      {"int_var_name", 42},
   //      {"double_var_name", 3.14},
-  //      {"string_var_name", "hello world"}
+  //      {"string_var_name", "hello world"},
+  //      {"empty_array", TracedValue::Array({})},
+  //      {"dictionary", TracedValue::Dictionary({
+  //        {"my_ptr", static_cast<void*>(my_ptr)},
+  //        {"nested_array", TracedValue::Array({1, false, 0.5})},
+  //      })},
   //    });
-  //
-  // |name| is assumed to be a long lived "quoted" string.
   static std::unique_ptr<TracedValue> Build(
-      std::initializer_list<DictionaryItem> items);
+      const std::initializer_list<DictionaryItem> items);
 
-  // DictionaryItem instance represents a single name-value pair.
-  class DictionaryItem {
+  // An |Array| instance represents an array of |ArrayItem| objects. This is a
+  // helper to allow initializer list like construction of arrays using
+  // |TracedValue::Build|.
+  //
+  // An instance holds an |std::initializer_list<TracedValue::ArrayItem>| and is
+  // cheap to copy (copying the initializer_list does not copy the underlying
+  // objects). The underlying array must exist at the time when
+  // |TracedValue::Build| is called.
+  class Array {
    public:
-    // These constructors assume that |name| is a long lived "quoted" string.
-    DictionaryItem(const char* name, int value);
-    DictionaryItem(const char* name, double value);
-    DictionaryItem(const char* name, bool value);
-    DictionaryItem(const char* name, void* value);
+    // This constructor expects that the initializer_list is valid when
+    // |TracedValue::Build| is called.
+    Array(const std::initializer_list<ArrayItem> items);
+    Array(Array&&);
+    void WriteToValue(TracedValue* value) const;
+
+   private:
+    std::initializer_list<ArrayItem> items_;
+  };
+
+  // A helper to hold a dictionary. Similar to |TracedValue::Array|.
+  class Dictionary {
+   public:
+    // This constructor expects that the initializer_list is valid when
+    // |TracedValue::Build| is called.
+    Dictionary(const std::initializer_list<DictionaryItem> items);
+    Dictionary(Dictionary&&);
+    void WriteToValue(TracedValue* value) const;
+
+   private:
+    std::initializer_list<DictionaryItem> items_;
+  };
+
+  // A |ValueHolder| holds a single value or a container (int, double... or an
+  // |Array| / |Dictionary|). Not to be used outside of the context of
+  // |TracedValue::Build| (has one parameter implicit constructors).
+  //
+  // Base class for |TracedValue::ArrayItem| and |TracedValue::DictionaryItem|.
+  class ValueHolder {
+   public:
+    // Implicit constructors allow constructing |DictionaryItem| without having
+    // to write |{"name", TracedValue::ValueHolder(1)}|.
+    ValueHolder(int value);     // NOLINT(google-explicit-constructor)
+    ValueHolder(double value);  // NOLINT(google-explicit-constructor)
+    ValueHolder(bool value);    // NOLINT(google-explicit-constructor)
+    ValueHolder(void* value);   // NOLINT(google-explicit-constructor)
     // StringPiece's backing storage / const char* pointer needs to remain valid
     // until TracedValue::Build is called.
-    DictionaryItem(const char* name, base::StringPiece value);
+    // NOLINTNEXTLINE(google-explicit-constructor)
+    ValueHolder(base::StringPiece value);
+    // NOLINTNEXTLINE(google-explicit-constructor)
+    ValueHolder(const std::string& value);
     // Define an explicit overload for const char* to resolve the ambiguity
     // between the base::StringPiece, void*, and bool constructors for string
     // literals.
-    DictionaryItem(const char* name, const char* value);
+    ValueHolder(const char* value);  // NOLINT(google-explicit-constructor)
+    ValueHolder(Array& value);       // NOLINT(google-explicit-constructor)
+    ValueHolder(Dictionary& value);  // NOLINT(google-explicit-constructor)
+    ValueHolder(ValueHolder&&);
+
+   protected:
+    void WriteToValue(TracedValue* value) const;
+    void WriteToValue(const char* name, TracedValue* value) const;
 
    private:
-    friend std::unique_ptr<TracedValue> TracedValue::Build(
-        std::initializer_list<DictionaryItem> items);
-
-    void WriteToValue(TracedValue* value) const;
-
     union KeptValue {
+      // Copy is handled by the holder (based on
+      // |TracedValue::ValueHolder::kept_value_type_|).
       int int_value;
       double double_value;
       bool bool_value;
       base::StringPiece string_piece_value;
       void* void_ptr_value;
+      Array array_value;
+      Dictionary dictionary_value;
 
       // Default constructor is implicitly deleted because union field has a
       // non-trivial default constructor.
-      KeptValue() {}
+      KeptValue() {}  // NOLINT(modernize-use-equals-default)
     };
 
     // Reimplementing a subset of C++17 std::variant.
@@ -126,13 +186,43 @@
       kBoolType,
       kStringPieceType,
       kVoidPtrType,
+      kArrayType,
+      kDictionaryType,
     };
 
     KeptValue kept_value_;
-    const char* name_;
     KeptValueType kept_value_type_;
   };
 
+  // |ArrayItem| is a |ValueHolder| which can be used to construct an |Array|.
+  class ArrayItem : public ValueHolder {
+   public:
+    // Implicit constructors allow calling |TracedValue::Array({1, true, 3.14})|
+    // instead of |TracedValue::Array({TracedValue::ArrayItem(1),
+    // TracedValue::ArrayItem(true), TracedValue::ArrayItem(3.14)})|.
+    template <typename T>
+    // NOLINTNEXTLINE(google-explicit-constructor)
+    ArrayItem(T value) : ValueHolder(value) {}
+
+    void WriteToValue(TracedValue* value) const;
+  };
+
+  // |DictionaryItem| instance represents a single name-value pair.
+  //
+  // |name| is assumed to be a long lived "quoted" string.
+  class DictionaryItem : public ValueHolder {
+   public:
+    // These constructors assume that |name| is a long lived "quoted" string.
+    template <typename T>
+    DictionaryItem(const char* name, T value)
+        : ValueHolder(value), name_(name) {}
+
+    void WriteToValue(TracedValue* value) const;
+
+   private:
+    const char* name_;
+  };
+
   // A custom serialization class can be supplied by implementing the
   // Writer interface and supplying a factory class to SetWriterFactoryCallback.
   // Primarily used by Perfetto to write TracedValues directly into its proto
diff --git a/base/trace_event/traced_value_unittest.cc b/base/trace_event/traced_value_unittest.cc
index 86e6ec4..24238a78 100644
--- a/base/trace_event/traced_value_unittest.cc
+++ b/base/trace_event/traced_value_unittest.cc
@@ -15,6 +15,31 @@
 namespace base {
 namespace trace_event {
 
+TEST(TraceEventArgumentTest, InitializerListCreatedContainers) {
+  std::string json;
+  TracedValue::Build(
+      {
+          {"empty_array", TracedValue::Array({})},
+          {"empty_dictionary", TracedValue::Dictionary({})},
+          {"nested_array", TracedValue::Array({
+                               TracedValue::Array({}),
+                               TracedValue::Dictionary({}),
+                               true,
+                           })},
+          {"nested_dictionary", TracedValue::Dictionary({
+                                    {"d", TracedValue::Dictionary({})},
+                                    {"a", TracedValue::Array({})},
+                                    {"b", true},
+                                })},
+      })
+      ->AppendAsTraceFormat(&json);
+  EXPECT_EQ(
+      "{\"empty_array\":[],\"empty_dictionary\":{},"
+      "\"nested_array\":[[],{},true],"
+      "\"nested_dictionary\":{\"d\":{},\"a\":[],\"b\":true}}",
+      json);
+}
+
 TEST(TraceEventArgumentTest, InitializerListCreatedFlatDictionary) {
   std::string json;
   TracedValue::Build({{"bool_var", true},
@@ -28,23 +53,34 @@
       json);
 }
 
+std::string SayHello() {
+  // Create a string by concatenating two strings, so that there is no literal
+  // corresponding to the result.
+  return std::string("hello ") + std::string("world");
+}
+
 TEST(TraceEventArgumentTest, StringAndPointerConstructors) {
   std::string json;
   const char* const_char_ptr_var = "const char* value";
-  TracedValue::Build({
-                         {"literal_var", "literal"},
-                         {"std_string_var", std::string("std::string value")},
-                         {"base_string_piece_var",
-                          base::StringPiece("base::StringPiece value")},
-                         {"const_char_ptr_var", const_char_ptr_var},
-                         {"void_nullptr", static_cast<void*>(nullptr)},
-                         {"int_nullptr", static_cast<int*>(nullptr)},
-                         {"void_1234ptr", reinterpret_cast<void*>(0x1234)},
-                     })
+  TracedValue::Build(
+      {
+          {"literal_var", "literal"},
+          {"std_string_var", std::string("std::string value")},
+          {"string_from_function", SayHello()},
+          {"string_from_lambda", []() { return std::string("hello"); }()},
+          {"base_string_piece_var",
+           base::StringPiece("base::StringPiece value")},
+          {"const_char_ptr_var", const_char_ptr_var},
+          {"void_nullptr", static_cast<void*>(nullptr)},
+          {"int_nullptr", static_cast<int*>(nullptr)},
+          {"void_1234ptr", reinterpret_cast<void*>(0x1234)},
+      })
       ->AppendAsTraceFormat(&json);
   EXPECT_EQ(
       "{\"literal_var\":\"literal\","
       "\"std_string_var\":\"std::string value\","
+      "\"string_from_function\":\"hello world\","
+      "\"string_from_lambda\":\"hello\","
       "\"base_string_piece_var\":\"base::StringPiece value\","
       "\"const_char_ptr_var\":\"const char* value\","
       "\"void_nullptr\":\"0x0\","
diff --git a/build/android/gyp/util/diff_utils.py b/build/android/gyp/util/diff_utils.py
index 674de31b..10814e58 100755
--- a/build/android/gyp/util/diff_utils.py
+++ b/build/android/gyp/util/diff_utils.py
@@ -88,9 +88,9 @@
   diff_text = _DiffFileContents(options.expected_file, actual_data)
 
   if not diff_text:
-    return
-
-  fail_msg = """
+    fail_msg = ''
+  else:
+    fail_msg = """
 Expectations need updating:
 https://chromium.googlesource.com/chromium/src/+/HEAD/chrome/android/expectations/README.md
 
@@ -105,10 +105,10 @@
 ############ END ############
 """.format(diff_text)
 
-  sys.stderr.write(fail_msg)
+    sys.stderr.write(fail_msg)
+
   if options.failure_file:
-    build_utils.MakeDirectory(os.path.dirname(options.failure_file))
     with open(options.failure_file, 'w') as f:
       f.write(fail_msg)
-  if options.fail_on_expectations:
+  if fail_msg and options.fail_on_expectations:
     sys.exit(1)
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index bb99e54..e3530c4 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1266,7 +1266,10 @@
         _failure_file =
             "$_expectations_failure_dir/" +
             string_replace(invoker.expected_proguard_config, "/", "_")
-        outputs = [ _actual_file ]
+        outputs = [
+          _actual_file,
+          _failure_file,
+        ]
         args = _args + [
                  "--depfile",
                  rebase_path(depfile, root_build_dir),
@@ -2649,7 +2652,10 @@
           invoker.build_config,
           invoker.expected_android_manifest,
         ]
-        outputs = [ _actual_file ]
+        outputs = [
+          _actual_file,
+          _failure_file,
+        ]
         deps = [
           invoker.android_manifest_dep,
           invoker.build_config_dep,
@@ -2928,7 +2934,10 @@
           invoker.expected_libs_and_assets,
         ]
         deps = [ invoker.build_config_dep ]
-        outputs = [ _actual_file ]
+        outputs = [
+          _actual_file,
+          _failure_file,
+        ]
         script = _script
         args = _args + [
                  "--expected-file",
@@ -4156,7 +4165,10 @@
         inputs += [ invoker.secondary_abi_native_libraries_config ]
         deps += [ invoker.secondary_abi_native_libraries_config_target ]
       }
-      outputs = [ _actual_file ]
+      outputs = [
+        _actual_file,
+        _failure_file,
+      ]
       script = _script
       args = _args + [
                "--expected-file",
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index b0fc14ff..4903621 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200812.4.1
+0.20200813.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index b0fc14ff..6d458d1 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200812.4.1
+0.20200813.0.1
diff --git a/chrome/android/DEPS b/chrome/android/DEPS
index 7a62ccb..dbafd44b 100644
--- a/chrome/android/DEPS
+++ b/chrome/android/DEPS
@@ -14,7 +14,6 @@
   "+chrome/browser/ui/messages/android",
   "+chrome/browser/util/android/java",
   "+chrome/browser/webauthn/android",
-  "+chrome/browser/xsurface/android",
   "+components/browser_ui/android/bottomsheet",
   "+components/browser_ui/banners/android",
   "+components/browser_ui/display_cutout/android",
@@ -56,3 +55,9 @@
   "+services/device/public",
   "+services/media_session/public",
 ]
+
+specific_include_rules = {
+  "chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java": [
+    "+chrome/browser/xsurface/android",
+  ],
+}
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBackButtonIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBackButtonIntegrationTest.java
index 44580c9..2c0363c 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBackButtonIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBackButtonIntegrationTest.java
@@ -167,18 +167,14 @@
         Espresso.pressBack();
         waitUntilViewMatchesCondition(withText(R.string.undo), isCompletelyDisplayed());
         onView(withId(R.id.autofill_assistant)).check(doesNotExist());
-
-        assertThat(
-                ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab()).getSpec(),
+        assertThat(mTestRule.getActivity().getActivityTab().getUrl().getSpec(),
                 is(getURL(TEST_PAGE_B)));
 
         // Third press on back button navigates back.
         Espresso.pressBack();
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_A)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_A)));
     }
 
     @Test
@@ -246,8 +242,7 @@
         Espresso.pressBack();
         waitUntilViewMatchesCondition(withText("Back button pressed"), isCompletelyDisplayed());
         waitUntilViewMatchesCondition(withText("Undo"), isDisplayed());
-        assertThat(
-                ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab()).getSpec(),
+        assertThat(mTestRule.getActivity().getActivityTab().getUrl().getSpec(),
                 is(getURL(TEST_PAGE_B)));
 
         // Undo should get back to the prompt state.
@@ -258,18 +253,15 @@
         Espresso.pressBack();
         waitUntilViewMatchesCondition(withText("Back button pressed"), isCompletelyDisplayed());
         waitUntilViewMatchesCondition(withText("Undo"), isDisplayed());
-        assertThat(
-                ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab()).getSpec(),
+        assertThat(mTestRule.getActivity().getActivityTab().getUrl().getSpec(),
                 is(getURL(TEST_PAGE_B)));
 
         // Third press on back button destroys Autofill UI and navigates back.
         Espresso.pressBack();
         waitUntilViewAssertionTrue(withId(R.id.autofill_assistant), doesNotExist(), 3000L);
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_A)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_A)));
     }
 
     @Test
@@ -303,11 +295,9 @@
                 withId(R.id.autofill_assistant), doesNotExist(), DEFAULT_MAX_TIME_TO_POLL);
         onView(withText("Shutdown")).check(doesNotExist());
         onView(withText(R.string.undo)).check(doesNotExist());
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_A)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_A)));
     }
 
     @Test
@@ -365,8 +355,7 @@
         Espresso.pressBack();
         waitUntilViewMatchesCondition(withText("Back button pressed"), isCompletelyDisplayed());
         waitUntilViewMatchesCondition(withText("Undo"), isDisplayed());
-        assertThat(
-                ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab()).getSpec(),
+        assertThat(mTestRule.getActivity().getActivityTab().getUrl().getSpec(),
                 is(getURL(TEST_PAGE_B)));
 
         // Navigation destroys the Autofill Assistant UI.
@@ -413,11 +402,9 @@
 
         // Second press on back button navigates back, without removing the Autofill Assistannt UI.
         Espresso.pressBack();
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_A)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_A)));
         onView(withId(R.id.autofill_assistant)).check(matches(isDisplayed()));
         onView(withId(R.id.status_message)).check(matches(withText("Prompt")));
     }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java
index f8c6689..c58469e 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java
@@ -123,11 +123,9 @@
         onView(withId(org.chromium.chrome.R.id.url_bar))
                 .perform(click(), typeText(getURL(TEST_PAGE_B)), pressImeActionButton());
         waitUntilViewMatchesCondition(withText(containsString("Sorry")), isCompletelyDisplayed());
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_B)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_B)));
     }
 
     @Test
@@ -159,11 +157,9 @@
         startAutofillAssistantOnTab(TEST_PAGE_A);
 
         waitUntilViewMatchesCondition(withText("Prompt"), isCompletelyDisplayed());
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_B)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_B)));
     }
 
     @Test
@@ -196,11 +192,9 @@
         startAutofillAssistantOnTab(TEST_PAGE_A);
 
         waitUntilViewMatchesCondition(withText("Prompt"), isCompletelyDisplayed());
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_B)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_B)));
     }
 
     @Test
@@ -263,28 +257,21 @@
         onView(withText("Navigate")).perform(click());
 
         waitUntilViewMatchesCondition(withText("Page A"), isCompletelyDisplayed());
-
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_A)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_A)));
         onView(withText("Go back")).perform(click());
 
         waitUntilViewMatchesCondition(withText("Page B"), isCompletelyDisplayed());
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_B)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_B)));
         onView(withText("Go forward")).perform(click());
 
         waitUntilViewMatchesCondition(withText("Page A"), isCompletelyDisplayed());
-        waitUntil(
-                ()
-                        -> ChromeTabUtils.getUrlOnUiThread(mTestRule.getActivity().getActivityTab())
-                                   .getSpec()
-                                   .equals(getURL(TEST_PAGE_A)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_A)));
     }
 
     @Test
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingUiCaptureTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingUiCaptureTest.java
index 73f9fb99..5c0f6d2 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingUiCaptureTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingUiCaptureTest.java
@@ -66,9 +66,9 @@
     @MediumTest
     @DisableFeatures(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)
     @Feature({"KeyboardAccessory", "LTR", "UiCatalogue"})
-    @DisableIf.
-    Build(sdk_is_greater_than = VERSION_CODES.LOLLIPOP_MR1, sdk_is_less_than = VERSION_CODES.N,
-            message = "Flaky on Marshmallow https://crbug.com/1102302")
+    @DisableIf.Build(sdk_is_greater_than = VERSION_CODES.KITKAT, sdk_is_less_than = VERSION_CODES.N,
+            message =
+                    "Flaky on Marshmallow https://crbug.com/1102302, Failing on Lollipop https://crbug.com/1095672")
     public void
     testCaptureKeyboardAccessoryWithPasswords() throws InterruptedException, TimeoutException {
         mHelper.loadTestPage(false);
@@ -96,9 +96,9 @@
     @MediumTest
     @DisableFeatures(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)
     @Feature({"KeyboardAccessory", "RTL", "UiCatalogue"})
-    @DisableIf.
-    Build(sdk_is_greater_than = VERSION_CODES.LOLLIPOP_MR1, sdk_is_less_than = VERSION_CODES.N,
-            message = "Flaky on Marshmallow https://crbug.com/1102302")
+    @DisableIf.Build(sdk_is_greater_than = VERSION_CODES.KITKAT, sdk_is_less_than = VERSION_CODES.N,
+            message =
+                    "Flaky on Marshmallow https://crbug.com/1102302, Failing on Lollipop https://crbug.com/1095672")
     public void
     testCaptureKeyboardAccessoryWithPasswordsRTL() throws InterruptedException, TimeoutException {
         mHelper.loadTestPage(true);
@@ -125,9 +125,9 @@
     @MediumTest
     @EnableFeatures(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)
     @Feature({"KeyboardAccessoryModern", "LTR", "UiCatalogue"})
-    @DisableIf.
-    Build(sdk_is_greater_than = VERSION_CODES.LOLLIPOP_MR1, sdk_is_less_than = VERSION_CODES.N,
-            message = "Flaky on Marshmallow https://crbug.com/1102302")
+    @DisableIf.Build(sdk_is_greater_than = VERSION_CODES.KITKAT, sdk_is_less_than = VERSION_CODES.N,
+            message =
+                    "Flaky on Marshmallow https://crbug.com/1102302, Failing on Lollipop https://crbug.com/1095672")
     public void
     testCaptureKeyboardAccessoryV2WithPasswords() throws InterruptedException, TimeoutException {
         mHelper.loadTestPage(false);
@@ -158,9 +158,9 @@
     @MediumTest
     @EnableFeatures(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)
     @Feature({"KeyboardAccessoryModern", "RTL", "UiCatalogue"})
-    @DisableIf.
-    Build(sdk_is_greater_than = VERSION_CODES.LOLLIPOP_MR1, sdk_is_less_than = VERSION_CODES.N,
-            message = "Flaky on Marshmallow https://crbug.com/1102302")
+    @DisableIf.Build(sdk_is_greater_than = VERSION_CODES.KITKAT, sdk_is_less_than = VERSION_CODES.N,
+            message =
+                    "Flaky on Marshmallow https://crbug.com/1102302, Failing on Lollipop https://crbug.com/1095672")
     public void
     testCaptureKeyboardAccessoryV2WithPasswordsRTL() throws InterruptedException, TimeoutException {
         mHelper.loadTestPage(true);
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
index 278391e..65cb0ee 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -1572,7 +1572,7 @@
                 .check(waitForView(allOf(withText(expectedTerm), isDisplayed())));
 
         // Click the chip and check the tab navigates back to the search result page.
-        assertEquals(mUrl, ChromeTabUtils.getUrlStringOnUiThread(currentTab));
+        assertEquals(mUrl, currentTab.getUrlString());
         OverviewModeBehaviorWatcher hideWatcher = TabUiTestHelper.createOverviewHideWatcher(cta);
         onView(withId(R.id.search_button))
                 .check(waitForView(allOf(withText(expectedTerm), isDisplayed())));
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
index f08fa39b..8fb296c9 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
@@ -460,7 +460,7 @@
 
     private static void verifyAllTabsHaveUrl(TabModel tabModel, String url) {
         for (int i = 0; i < tabModel.getCount(); i++) {
-            assertEquals(url, ChromeTabUtils.getUrlStringOnUiThread(tabModel.getTabAt(i)));
+            assertEquals(url, tabModel.getTabAt(i).getUrlString());
         }
     }
 
diff --git a/chrome/android/feed/DEPS b/chrome/android/feed/DEPS
index 18ced55..c29df56 100644
--- a/chrome/android/feed/DEPS
+++ b/chrome/android/feed/DEPS
@@ -3,8 +3,11 @@
   "+chrome/browser/profiles/android/java",
   "+chrome/browser/tab/java",
   "+chrome/browser/ui/messages/android/java",
+  "+chrome/browser/xsurface/android",
   "+components/background_task_scheduler",
   "+components/feature_engagement",
   "+components/feed",
+
+  "-content",
   "+content/public/android/java/src/org/chromium/content_public",
 ]
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
index 79722b6..0dce68d5 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
@@ -291,7 +291,7 @@
 
         mIsCachePopulatedInAccountManagerFacade = true;
         TestThreadUtils.runOnUiThreadBlocking(mTab::reload);
-        ChromeTabUtils.waitForTabPageLoaded(mTab, ChromeTabUtils.getUrlStringOnUiThread(mTab));
+        ChromeTabUtils.waitForTabPageLoaded(mTab, mTab.getUrlString());
 
         // Check that the sign-in promo is displayed this time.
         onView(instanceOf(RecyclerView.class))
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
index 32fa31f..32c2f883 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java
@@ -5,12 +5,16 @@
 package org.chromium.chrome.browser.infobar;
 
 import android.graphics.Bitmap;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
 
 import androidx.annotation.ColorRes;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.components.infobars.ConfirmInfoBar;
 import org.chromium.components.infobars.InfoBarLayout;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
 
 /**
  * An infobar to present a Safety Tip. This is a thin vineer over standard ConfirmInfoBar to provide
@@ -19,6 +23,7 @@
 public class SafetyTipInfoBar extends ConfirmInfoBar {
     private static final String TAG = "SafetyTipInfoBar";
     private String mDescription;
+    private String mLearnMoreLinkText;
 
     /**
      * Creates and begins the process for showing a SafetyTipInfoBar.  This constructor is similar
@@ -43,14 +48,24 @@
     private SafetyTipInfoBar(int iconDrawableId, @ColorRes int iconTintId, Bitmap iconBitmap,
             String message, String linkText, String primaryButtonText, String secondaryButtonText,
             String description) {
-        super(iconDrawableId, iconTintId, iconBitmap, message, linkText, primaryButtonText,
+        super(iconDrawableId, iconTintId, iconBitmap, message, null, primaryButtonText,
                 secondaryButtonText);
         mDescription = description;
+        mLearnMoreLinkText = linkText;
     }
 
     @Override
     public void createContent(InfoBarLayout layout) {
         super.createContent(layout);
-        layout.getMessageLayout().addDescription(mDescription);
+
+        SpannableStringBuilder descriptionMessage = new SpannableStringBuilder(mDescription);
+        if (mLearnMoreLinkText != null && !mLearnMoreLinkText.isEmpty()) {
+            SpannableString link = new SpannableString(mLearnMoreLinkText);
+            link.setSpan(
+                    new NoUnderlineClickableSpan(layout.getResources(), view -> onLinkClicked()), 0,
+                    link.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            descriptionMessage.append(" ").append(link);
+        }
+        layout.getMessageLayout().addDescription(descriptionMessage);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index 8d113af..8c3f2ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -137,6 +137,11 @@
     private LoadUrlParams mPendingLoadParams;
 
     /**
+     * URL of the page currently loading. Used as a fall-back in case tab restore fails.
+     */
+    private GURL mUrl;
+
+    /**
      * True while a page load is in progress.
      */
     private boolean mIsLoading;
@@ -327,7 +332,6 @@
         return mId;
     }
 
-    // TODO(crbug.com/1113249) move getUrl() and getUrlString() to CriticalPersistedTabData
     @Override
     public String getUrlString() {
         return getUrl().getSpec();
@@ -341,12 +345,10 @@
         // If we have a ContentView, or a NativePage, or the url is not empty, we have a WebContents
         // so cache the WebContent's url. If not use the cached version.
         if (getWebContents() != null || isNativePage() || !url.getSpec().isEmpty()) {
-            CriticalPersistedTabData.from(this).setUrl(url);
+            mUrl = url;
         }
 
-        return CriticalPersistedTabData.from(this).getUrl() != null
-                ? CriticalPersistedTabData.from(this).getUrl()
-                : GURL.emptyGURL();
+        return mUrl != null ? mUrl : GURL.emptyGURL();
     }
 
     @Override
@@ -758,9 +760,7 @@
             CriticalPersistedTabData.from(this).setLaunchTypeAtCreation(mLaunchType);
             mCreationState = creationState;
             mPendingLoadParams = loadUrlParams;
-            if (loadUrlParams != null) {
-                CriticalPersistedTabData.from(this).setUrl(new GURL(loadUrlParams.getUrl()));
-            }
+            if (loadUrlParams != null) mUrl = new GURL(loadUrlParams.getUrl());
 
             TabHelpers.initTabHelpers(this, parent);
 
@@ -822,8 +822,7 @@
         assert state != null;
         CriticalPersistedTabData.from(this).setWebContentsState(state.contentsState);
         CriticalPersistedTabData.from(this).setTimestampMillis(state.timestampMillis);
-        CriticalPersistedTabData.from(this).setUrl(
-                new GURL(state.contentsState.getVirtualUrlFromState()));
+        mUrl = new GURL(state.contentsState.getVirtualUrlFromState());
         CriticalPersistedTabData.from(this).setTitle(
                 state.contentsState.getDisplayTitleFromState());
         CriticalPersistedTabData.from(this).setLaunchTypeAtCreation(state.tabLaunchTypeAtCreation);
@@ -1394,9 +1393,7 @@
             initWebContents(webContents);
 
             if (!restored) {
-                String url = CriticalPersistedTabData.from(this).getUrl().getSpec().isEmpty()
-                        ? UrlConstants.NTP_URL
-                        : CriticalPersistedTabData.from(this).getUrl().getSpec();
+                String url = mUrl.getSpec().isEmpty() ? UrlConstants.NTP_URL : mUrl.getSpec();
                 loadUrl(new LoadUrlParams(url, PageTransition.GENERATED));
             }
         } finally {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
index c3c59f5..8ba6590 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -73,7 +73,6 @@
     private final ObserverList<Callback<WebContents>> mInitObservers = new ObserverList<>();
     private final Handler mHandler = new Handler();
     private WebContentsObserver mObserver;
-    private String mLastUrl;
 
     public static TabWebContentsObserver from(Tab tab) {
         TabWebContentsObserver observer = get(tab);
@@ -295,7 +294,6 @@
                 recordErrorInPolicyAuditor(
                         navigation.getUrl(), navigation.errorDescription(), navigation.errorCode());
             }
-            mLastUrl = navigation.getUrl();
 
             if (!navigation.hasCommitted()) return;
 
@@ -355,7 +353,7 @@
         @Override
         public void destroy() {
             MediaCaptureNotificationService.updateMediaNotificationForTab(
-                    ContextUtils.getApplicationContext(), mTab.getId(), null, mLastUrl);
+                    ContextUtils.getApplicationContext(), mTab.getId(), null, mTab.getUrlString());
             super.destroy();
         }
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java
index 35d47bf..a64c72d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java
@@ -63,8 +63,7 @@
     public void testLaunchActivity() {
         // Launch chrome
         mActivityTestRule.startMainActivityFromLauncher();
-        String currentUrl = ChromeTabUtils.getUrlStringOnUiThread(
-                mActivityTestRule.getActivity().getActivityTab());
+        String currentUrl = mActivityTestRule.getActivity().getActivityTab().getUrlString();
         Assert.assertNotNull(currentUrl);
         Assert.assertEquals(false, currentUrl.isEmpty());
     }
@@ -79,17 +78,14 @@
     public void testNewTabPageLaunch() {
         // Launch chrome with NTP.
         mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
-        String currentUrl = ChromeTabUtils.getUrlStringOnUiThread(
-                mActivityTestRule.getActivity().getActivityTab());
+        String currentUrl = mActivityTestRule.getActivity().getActivityTab().getUrlString();
         Assert.assertNotNull(currentUrl);
         Assert.assertEquals(false, currentUrl.isEmpty());
 
         // Open NTP.
         ChromeTabUtils.newTabFromMenu(
                 InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-
-        currentUrl = ChromeTabUtils.getUrlStringOnUiThread(
-                mActivityTestRule.getActivity().getActivityTab());
+        currentUrl = mActivityTestRule.getActivity().getActivityTab().getUrlString();
         Assert.assertNotNull(currentUrl);
         Assert.assertEquals(false, currentUrl.isEmpty());
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/NavigateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/NavigateTest.java
index ce7886447..343de79 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/NavigateTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/NavigateTest.java
@@ -99,10 +99,8 @@
             Criteria.checkThat("urlBar is null", urlBar, Matchers.notNullValue());
             Criteria.checkThat("UrlBar text wrong", urlBar.getText().toString(),
                     Matchers.is(expectedLocation(endUrl)));
-
             Criteria.checkThat("Tab url wrong",
-                    ChromeTabUtils.getUrlStringOnUiThread(
-                            mActivityTestRule.getActivity().getActivityTab()),
+                    mActivityTestRule.getActivity().getActivityTab().getUrlString(),
                     Matchers.is(endUrl));
         });
     }
@@ -236,8 +234,7 @@
         DOMUtils.clickNode(tab.getWebContents(), "aboutLink");
         ChromeTabUtils.waitForTabPageLoaded(tab, url2);
         Assert.assertEquals("Desired Link not open", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 
     /**
@@ -323,7 +320,7 @@
             @Override
             public void onPageLoadStarted(Tab tab, String newUrl) {
                 tab.removeObserver(this);
-                Assert.assertEquals(url1, ChromeTabUtils.getUrlStringOnUiThread(tab));
+                Assert.assertEquals(url1, tab.getUrlString());
                 Assert.assertEquals(url2, newUrl);
             }
         };
@@ -332,8 +329,7 @@
         DOMUtils.clickNode(tab.getWebContents(), "aboutLink");
         ChromeTabUtils.waitForTabPageLoaded(tab, url2);
         Assert.assertEquals("Desired Link not open", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 
     /**
@@ -351,8 +347,7 @@
         typeInOmniboxAndNavigate(initialUrl, null);
 
         CriteriaHelper.pollInstrumentationThread(() -> {
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(
-                                       mActivityTestRule.getActivity().getActivityTab()),
+            Criteria.checkThat(mActivityTestRule.getActivity().getActivityTab().getUrlString(),
                     Matchers.is(redirectedUrl));
         });
     }
@@ -366,9 +361,8 @@
     @Feature({"Navigation"})
     public void testIntentFallbackRedirection() throws Exception {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        Assert.assertEquals(NEW_TAB_PAGE,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+        Assert.assertEquals(
+                NEW_TAB_PAGE, mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         final String fallbackUrl =
                 mTestServer.getURL("/chrome/test/data/android/redirect/about.html");
@@ -388,8 +382,7 @@
 
         // Now intent fallback should be triggered assuming 'non_existent' scheme cannot be handled.
         CriteriaHelper.pollInstrumentationThread(() -> {
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(
-                                       mActivityTestRule.getActivity().getActivityTab()),
+            Criteria.checkThat(mActivityTestRule.getActivity().getActivityTab().getUrlString(),
                     Matchers.is(targetUrl));
         });
 
@@ -441,9 +434,7 @@
                             "URL mismatch after pressing back button for the 1st time in repetition"
                                     + "%d.",
                             i),
-                    urls[1],
-                    ChromeTabUtils.getUrlStringOnUiThread(
-                            mActivityTestRule.getActivity().getActivityTab()));
+                    urls[1], mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
             TouchCommon.singleClickView(
                     mActivityTestRule.getActivity().findViewById(R.id.back_button));
@@ -453,9 +444,7 @@
                             "URL mismatch after pressing back button for the 2nd time in repetition"
                                     + "%d.",
                             i),
-                    urls[0],
-                    ChromeTabUtils.getUrlStringOnUiThread(
-                            mActivityTestRule.getActivity().getActivityTab()));
+                    urls[0], mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
             TouchCommon.singleClickView(
                     mActivityTestRule.getActivity().findViewById(R.id.forward_button));
@@ -465,9 +454,7 @@
                             "URL mismatch after pressing fwd button for the 1st time in repetition"
                                     + "%d.",
                             i),
-                    urls[1],
-                    ChromeTabUtils.getUrlStringOnUiThread(
-                            mActivityTestRule.getActivity().getActivityTab()));
+                    urls[1], mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
             TouchCommon.singleClickView(
                     mActivityTestRule.getActivity().findViewById(R.id.forward_button));
@@ -477,9 +464,7 @@
                             "URL mismatch after pressing fwd button for the 2nd time in repetition"
                                     + "%d.",
                             i),
-                    urls[2],
-                    ChromeTabUtils.getUrlStringOnUiThread(
-                            mActivityTestRule.getActivity().getActivityTab()));
+                    urls[2], mActivityTestRule.getActivity().getActivityTab().getUrlString());
         }
     }
 
@@ -618,8 +603,7 @@
 
     private String getTabUrlOnUIThread(final Tab tab) {
         try {
-            return TestThreadUtils.runOnUiThreadBlocking(
-                    () -> ChromeTabUtils.getUrlStringOnUiThread(tab));
+            return TestThreadUtils.runOnUiThreadBlocking(() -> tab.getUrlString());
         } catch (ExecutionException ex) {
             assert false : "Unexpected ExecutionException";
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
index f6b0eff1..82701ea 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
@@ -402,8 +402,7 @@
         int newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url1,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // Launch a new URL from the same app, it should open in the same tab.
         originalTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
@@ -411,8 +410,7 @@
         newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // And pressing back should close Clank.
         Assert.assertTrue("Window does not have focus before pressing back.",
@@ -440,8 +438,7 @@
         launchUrlFromExternalApp(url1, EXTERNAL_APP_1_ID, false);
 
         Assert.assertEquals("Selected tab is not on the right URL.", url1,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // Launch the same URL without app ID. It should open a new tab.
         int originalTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
@@ -449,8 +446,7 @@
         int newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url1,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // Launch another URL without app ID. It should open a new tab.
         originalTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
@@ -458,8 +454,7 @@
         newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // And pressing back should close Clank.
         Assert.assertTrue("Window does not have focus before pressing back.",
@@ -490,8 +485,7 @@
         int newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url1,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // Launch a new URL from the same app with the right extra to open in a new tab.
         originalTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
@@ -499,8 +493,7 @@
         newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // And pressing back should close Clank.
         Assert.assertTrue("Window does not have focus before pressing back.",
@@ -524,8 +517,7 @@
         // Launch Clank from the external app.
         mActivityTestRule.startMainActivityFromExternalApp(url1, EXTERNAL_APP_1_ID);
         Assert.assertEquals("Selected tab is not on the right URL.", url1,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // Launch a new URL from the same app, it should open in the same tab.
         int originalTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
@@ -533,8 +525,7 @@
         int newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // And pressing back should close Clank.
         Assert.assertTrue("Window does not have focus before pressing back.",
@@ -569,8 +560,7 @@
         int newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         // Also try with no app id, it should also open in a new tab.
         originalTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
@@ -578,8 +568,7 @@
         newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url3,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 
     /**
@@ -608,8 +597,7 @@
         int newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 
     /**
@@ -657,8 +645,7 @@
         int newTabCount = ChromeTabUtils.getNumOpenTabs(mActivityTestRule.getActivity());
         Assert.assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount);
         Assert.assertEquals("Selected tab is not on the right URL.", url2,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 
     private static class TestTabObserver extends EmptyTabObserver {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
index 92818af0..9af5fab 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
@@ -46,7 +46,6 @@
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.contextmenu.ContextMenuUtils;
@@ -399,9 +398,7 @@
         // Only check for the URL matching as the tab will not be fully created in svelte mode.
         final String expectedUrl = mTestServer.getURL(expectedPath);
         CriteriaHelper.pollUiThread(
-                ()
-                        -> Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(newTab.get()),
-                                Matchers.is(expectedUrl)));
+                () -> Criteria.checkThat(newTab.get().getUrlString(), Matchers.is(expectedUrl)));
     }
 
     @Test
@@ -537,21 +534,17 @@
                 "Number of open tabs does not match", numOpenedTabs, tabModel.getCount());
 
         // Verify the Url is still the same of Parent page.
-        Assert.assertEquals(mTestUrl,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mDownloadTestRule.getActivity().getActivityTab()));
+        Assert.assertEquals(
+                mTestUrl, mDownloadTestRule.getActivity().getActivityTab().getUrlString());
 
         // Verify that the background tabs were opened in the expected order.
         String newTabUrl = mTestServer.getURL(
                 "/chrome/test/data/android/contextmenu/test_link.html");
-
-        Assert.assertEquals(newTabUrl,
-                ChromeTabUtils.getUrlStringOnUiThread(tabModel.getTabAt(indexOfLinkPage)));
+        Assert.assertEquals(newTabUrl, tabModel.getTabAt(indexOfLinkPage).getUrlString());
 
         String imageUrl = mTestServer.getURL(
                 "/chrome/test/data/android/contextmenu/test_link2.html");
-        Assert.assertEquals(imageUrl,
-                ChromeTabUtils.getUrlStringOnUiThread(tabModel.getTabAt(indexOfLinkPage2)));
+        Assert.assertEquals(imageUrl, tabModel.getTabAt(indexOfLinkPage2).getUrlString());
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java
index 5bf0a93..325812d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java
@@ -36,7 +36,6 @@
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.contextmenu.RevampedContextMenuUtils;
 import org.chromium.content_public.browser.test.util.Criteria;
@@ -152,9 +151,7 @@
         final String expectedUrl =
                 mTestServer.getURL("/chrome/test/data/android/contextmenu/test_image.png");
         CriteriaHelper.pollUiThread(
-                ()
-                        -> Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(newTab.get()),
-                                Matchers.is(expectedUrl)));
+                () -> Criteria.checkThat(newTab.get().getUrlString(), Matchers.is(expectedUrl)));
     }
 
     @Test
@@ -282,20 +279,17 @@
                 "Number of open tabs does not match", numOpenedTabs, tabModel.getCount());
 
         // Verify the Url is still the same of Parent page.
-        Assert.assertEquals(mTestUrl,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mDownloadTestRule.getActivity().getActivityTab()));
+        Assert.assertEquals(
+                mTestUrl, mDownloadTestRule.getActivity().getActivityTab().getUrlString());
 
         // Verify that the background tabs were opened in the expected order.
         String newTabUrl =
                 mTestServer.getURL("/chrome/test/data/android/contextmenu/test_link.html");
-        Assert.assertEquals(newTabUrl,
-                ChromeTabUtils.getUrlStringOnUiThread(tabModel.getTabAt(indexOfLinkPage)));
+        Assert.assertEquals(newTabUrl, tabModel.getTabAt(indexOfLinkPage).getUrlString());
 
         String imageUrl =
                 mTestServer.getURL("/chrome/test/data/android/contextmenu/test_link2.html");
-        Assert.assertEquals(imageUrl,
-                ChromeTabUtils.getUrlStringOnUiThread(tabModel.getTabAt(indexOfLinkPage2)));
+        Assert.assertEquals(imageUrl, tabModel.getTabAt(indexOfLinkPage2).getUrlString());
     }
 
     private void saveMediaFromContextMenu(String mediaDOMElement, int saveMenuID,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 653fdc5..477e31d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -1098,9 +1098,7 @@
                             CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage2));
                 }));
         CriteriaHelper.pollInstrumentationThread(() -> {
-            Criteria.checkThat(
-                    ChromeTabUtils.getUrlStringOnUiThread(getActivity().getActivityTab()),
-                    is(mTestPage));
+            Criteria.checkThat(getActivity().getActivityTab().getUrlString(), is(mTestPage));
         });
         Assert.assertTrue("CustomTabContentHandler can't handle intent with same session",
                 TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
@@ -1117,9 +1115,7 @@
         });
         pageLoadFinishedHelper.waitForCallback(0);
         CriteriaHelper.pollInstrumentationThread(() -> {
-            Criteria.checkThat(
-                    ChromeTabUtils.getUrlStringOnUiThread(getActivity().getActivityTab()),
-                    is(mTestPage2));
+            Criteria.checkThat(getActivity().getActivityTab().getUrlString(), is(mTestPage2));
         });
     }
 
@@ -1367,7 +1363,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
         CriteriaHelper.pollUiThread(() -> {
             final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
+            Criteria.checkThat(currentTab.getUrlString(), is(url));
         });
         CriteriaHelper.pollUiThread(() -> {
             CustomTabToolbar toolbar =
@@ -1396,7 +1392,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
         CriteriaHelper.pollInstrumentationThread(() -> {
             final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
+            Criteria.checkThat(currentTab.getUrlString(), is(mTestPage));
         });
         Assert.assertTrue(
                 connection.postMessage(token, "Message", null) == CustomTabsService.RESULT_SUCCESS);
@@ -1428,7 +1424,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
         CriteriaHelper.pollInstrumentationThread(() -> {
             final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
+            Criteria.checkThat(currentTab.getUrlString(), is(mTestPage));
         });
         Assert.assertTrue(
                 connection.postMessage(token, "Message", null) == CustomTabsService.RESULT_SUCCESS);
@@ -1466,7 +1462,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
         CriteriaHelper.pollInstrumentationThread(() -> {
             final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
+            Criteria.checkThat(currentTab.getUrlString(), is(mTestPage));
         });
         Assert.assertTrue(connection.postMessage(token, "Message", null)
                 == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
@@ -1490,7 +1486,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
         CriteriaHelper.pollInstrumentationThread(() -> {
             final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
+            Criteria.checkThat(currentTab.getUrlString(), is(url));
         });
         Assert.assertTrue(connection.postMessage(token, "New title", null)
                 == CustomTabsService.RESULT_SUCCESS);
@@ -1568,7 +1564,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
         CriteriaHelper.pollInstrumentationThread(() -> {
             final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
+            Criteria.checkThat(currentTab.getUrlString(), is(url));
         });
 
         session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
@@ -1669,7 +1665,7 @@
 
         CriteriaHelper.pollInstrumentationThread(() -> {
             final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
+            Criteria.checkThat(currentTab.getUrlString(), is(url));
         });
 
         if (requestTime == AFTER_INTENT) {
@@ -1717,7 +1713,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
         CriteriaHelper.pollInstrumentationThread(() -> {
             final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
+            Criteria.checkThat(currentTab.getUrlString(), is(mTestPage));
         });
 
         Assert.assertFalse(mCustomTabActivityTestRule.getActivity().getActivityTab().canGoBack());
@@ -2176,8 +2172,8 @@
             Tab tab = tabbedActivity.get().getActivityTab();
             Criteria.checkThat("Tab is null", tab, Matchers.notNullValue());
             Criteria.checkThat("Incognito tab not selected", tab.isIncognito(), is(true));
-            Criteria.checkThat("Wrong URL loaded in incognito tab",
-                    ChromeTabUtils.getUrlStringOnUiThread(tab), is("about:blank"));
+            Criteria.checkThat(
+                    "Wrong URL loaded in incognito tab", tab.getUrlString(), is("about:blank"));
         });
 
         ApplicationStatus.unregisterActivityStateListener(listener);
@@ -2389,7 +2385,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(
                 CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage));
         Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-        assertEquals(mTestPage, ChromeTabUtils.getUrlStringOnUiThread(tab));
+        assertEquals(mTestPage, tab.getUrlString());
     }
 
     private ChromeActivity reparentAndVerifyTab() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java
index a186597..adb6e38 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java
@@ -34,7 +34,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 
 import java.util.Arrays;
 import java.util.List;
@@ -97,8 +96,7 @@
         ((CustomTabActivityTestRule) mActivityTestRule).startCustomTabActivityWithIntent(intent);
 
         CustomTabActivityTabProvider tabProvider = getActivityTabProvider();
-        assertEquals(
-                mediaViewerUrl, ChromeTabUtils.getUrlOnUiThread(tabProvider.getTab()).getSpec());
+        assertEquals(mediaViewerUrl, tabProvider.getTab().getUrl().getSpec());
         assertNotEquals(TabCreationMode.FROM_STARTUP_TAB_PRELOADER,
                 tabProvider.getInitialTabCreationMode());
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTest.java
index eef8cc8..bd37b28 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTest.java
@@ -209,7 +209,7 @@
         // Load the page that has an offline copy. The offline page should be shown.
         Tab tab = mDownloadTestRule.getActivity().getActivityTab();
         Assert.assertFalse(isOfflinePage(tab));
-        mDownloadTestRule.loadUrl(ChromeTabUtils.getUrlOnUiThread(tab).getSpec());
+        mDownloadTestRule.loadUrl(tab.getUrl().getSpec());
         Assert.assertTrue(isOfflinePage(tab));
     }
 
@@ -430,9 +430,7 @@
     private void waitForDistillation(@SuppressWarnings("SameParameterValue") String expectedTitle,
             Tab tab) throws TimeoutException {
         CriteriaHelper.pollUiThread(
-                ()
-                        -> Criteria.checkThat(ChromeTabUtils.getUrlOnUiThread(tab).getScheme(),
-                                is("chrome-distiller")));
+                () -> Criteria.checkThat(tab.getUrl().getScheme(), is("chrome-distiller")));
         ChromeTabUtils.waitForTabPageLoaded(tab, null);
         // Distiller Viewer load the content dynamically, so waitForTabPageLoaded() is not enough.
         CriteriaHelper.pollUiThread(() -> Criteria.checkThat(tab.getTitle(), is(expectedTitle)));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
index 19a56e1..fa26608e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
@@ -97,11 +97,8 @@
     private void assertNavigateOnSwipeFrom(boolean edge, String toUrl) {
         ChromeTabUtils.waitForTabPageLoaded(currentTab(), toUrl, () -> swipeFromEdge(edge), 10);
         CriteriaHelper.pollUiThread(
-                ()
-                        -> Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab()),
-                                Matchers.is(toUrl)));
-        Assert.assertEquals(
-                "Didn't navigate back", toUrl, ChromeTabUtils.getUrlStringOnUiThread(currentTab()));
+                () -> Criteria.checkThat(currentTab().getUrlString(), Matchers.is(toUrl)));
+        Assert.assertEquals("Didn't navigate back", toUrl, currentTab().getUrlString());
     }
 
     private void swipeFromEdge(boolean leftEdge) {
@@ -148,7 +145,7 @@
         CriteriaHelper.pollUiThread(mNavigationLayout::isLayoutDetached,
                 "Navigation Layout should be detached after use");
         Assert.assertEquals("Current page should not change", UrlConstants.NTP_URL,
-                ChromeTabUtils.getUrlStringOnUiThread(currentTab()));
+                currentTab().getUrlString());
     }
 
     @Test
@@ -227,7 +224,7 @@
                 mActivityTestRule.getActivity().isInOverviewMode());
         setTabSwitcherModeAndWait(false);
         Assert.assertEquals("Current page should not change", UrlConstants.RECENT_TABS_URL,
-                ChromeTabUtils.getUrlStringOnUiThread(currentTab()));
+                currentTab().getUrlString());
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
index fab4a56..6494f8e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
@@ -140,8 +140,7 @@
         destroyAndRestartActivity();
 
         Assert.assertEquals("Start up homepage should be the same as the policy setting", TEST_URL,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 
     @Test
@@ -153,8 +152,7 @@
                 .fullyLoadUrl(anotherUrl);
 
         Assert.assertNotEquals("Did not switch to a different URL", TEST_URL,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         ChromeTabUtils.waitForTabPageLoaded(
                 mActivityTestRule.getActivity().getActivityTab(), TEST_URL, () -> {
@@ -175,8 +173,7 @@
                 });
 
         Assert.assertEquals("After clicking HomeButton, URL should be back to Homepage", TEST_URL,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 
     @Test
@@ -229,7 +226,6 @@
         // Start a new ChromeActivity.
         mActivityTestRule.startActivityCompletely(intent);
         Assert.assertEquals("Start up page is not homepage", HomepageManager.getHomepageUri(),
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+                mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
index 23fc7ae..35e33ab 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
@@ -22,7 +22,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.net.test.EmbeddedTestServer;
 
@@ -57,7 +56,7 @@
     public void testNTPIsDefault() {
         Tab tab = mActivityTestRule.getActivity().getActivityTab();
         Assert.assertNotNull(tab);
-        String url = ChromeTabUtils.getUrlStringOnUiThread(tab);
+        String url = tab.getUrlString();
         Assert.assertTrue("Unexpected url: " + url,
                 url.startsWith("chrome-native://newtab/")
                         || url.startsWith("chrome-native://bookmarks/")
@@ -73,9 +72,7 @@
     public void testNavigatingFromNTP() {
         String url = mTestServer.getURL("/chrome/test/data/android/google.html");
         mActivityTestRule.loadUrl(url);
-        Assert.assertEquals(url,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+        Assert.assertEquals(url, mActivityTestRule.getActivity().getActivityTab().getUrlString());
     }
 
     /**
@@ -87,14 +84,12 @@
     public void testNavigateBackToNTPViaUrl() {
         String url = mTestServer.getURL("/chrome/test/data/android/google.html");
         mActivityTestRule.loadUrl(url);
-        Assert.assertEquals(url,
-                ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab()));
+        Assert.assertEquals(url, mActivityTestRule.getActivity().getActivityTab().getUrlString());
 
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
         Tab tab = mActivityTestRule.getActivity().getActivityTab();
         Assert.assertNotNull(tab);
-        url = ChromeTabUtils.getUrlStringOnUiThread(tab);
+        url = tab.getUrlString();
         Assert.assertEquals(UrlConstants.NTP_URL, url);
 
         // Check that the NTP is actually displayed.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index 22a4028c..951a51554 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -258,8 +258,7 @@
                 TouchCommon.singleClickView(mostVisitedItem);
             }
         });
-        Assert.assertEquals(
-                mSiteSuggestions.get(0).url, ChromeTabUtils.getUrlStringOnUiThread(mTab));
+        Assert.assertEquals(mSiteSuggestions.get(0).url, mTab.getUrlString());
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/promo/HomepagePromoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/promo/HomepagePromoTest.java
index 1e8f217..bc7da2e2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/promo/HomepagePromoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/promo/HomepagePromoTest.java
@@ -44,6 +44,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -288,6 +289,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "https://crbug.com/1115870")
     public void testToggleFeed_WithHomepage() {
         // Test to toggle NTP when feed is hidden.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java
index 321e02a8..c43885a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java
@@ -351,7 +351,7 @@
         Tab tab = mActivityTestRule.getActivity().getActivityTab();
 
         mActivityTestRule.loadUrl(pageUrl);
-        Assert.assertEquals(pageUrl, ChromeTabUtils.getUrlStringOnUiThread(tab));
+        Assert.assertEquals(pageUrl, tab.getUrlString());
         if (mIsConnected) {
             Assert.assertFalse(isErrorPage(tab));
             Assert.assertFalse(isOfflinePage(tab));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
index 2bb2bd2..5c6eda6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
@@ -76,8 +76,7 @@
     @Feature({"Homepage"})
     public void testHomepageInitialLoading() {
         Assert.assertEquals(Uri.parse(TestPartnerBrowserCustomizationsProvider.HOMEPAGE_URI),
-                Uri.parse(ChromeTabUtils.getUrlStringOnUiThread(
-                        mActivityTestRule.getActivity().getActivityTab())));
+                Uri.parse(mActivityTestRule.getActivity().getActivityTab().getUrlString()));
     }
 
     /**
@@ -94,8 +93,7 @@
             mActivityTestRule.loadUrl(testServer.getURL(TEST_PAGE));
             UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
             Assert.assertNotSame(Uri.parse(TestPartnerBrowserCustomizationsProvider.HOMEPAGE_URI),
-                    Uri.parse(ChromeTabUtils.getUrlStringOnUiThread(
-                            mActivityTestRule.getActivity().getActivityTab())));
+                    Uri.parse(mActivityTestRule.getActivity().getActivityTab().getUrlString()));
 
             // Click homepage button.
             ChromeTabUtils.waitForTabPageLoaded(mActivityTestRule.getActivity().getActivityTab(),
@@ -110,8 +108,7 @@
                         }
                     });
             Assert.assertEquals(Uri.parse(TestPartnerBrowserCustomizationsProvider.HOMEPAGE_URI),
-                    Uri.parse(ChromeTabUtils.getUrlStringOnUiThread(
-                            mActivityTestRule.getActivity().getActivityTab())));
+                    Uri.parse(mActivityTestRule.getActivity().getActivityTab().getUrlString()));
         } finally {
             testServer.stopAndDestroyServer();
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java
index 99efc03e..dd5a5d18a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java
@@ -34,6 +34,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
@@ -548,6 +549,7 @@
     @LargeTest
     @Feature({"Portals"})
     @MinAndroidSdkLevel(Build.VERSION_CODES.M)
+    @FlakyTest(message = "https://crbug.com/1115888")
     public void testMediaCaptureNotificationVisibleAfterAdoption() throws Exception {
         String mainUrl = mTestServer.getURL("/chrome/test/data/android/portals/media-capture.html");
         mActivityTestRule.startMainActivityWithURL(mainUrl);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/query_tiles/OmniboxQueryTileSuggestionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/query_tiles/OmniboxQueryTileSuggestionTest.java
index c8ff83e..c72422f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/query_tiles/OmniboxQueryTileSuggestionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/query_tiles/OmniboxQueryTileSuggestionTest.java
@@ -45,7 +45,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.NewTabPageTestUtils;
 import org.chromium.chrome.test.util.OmniboxTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
@@ -236,8 +235,7 @@
     }
 
     private String getTabUrl() {
-        return ChromeTabUtils.getUrlOnUiThread(mActivityTestRule.getActivity().getActivityTab())
-                .getValidSpecOrEmpty();
+        return mActivityTestRule.getActivity().getActivityTab().getUrl().getValidSpecOrEmpty();
     }
 
     private void waitForOmniboxQueryTileSuggestion(boolean visible) {
@@ -255,8 +253,7 @@
 
     private void waitForSearchResultsPage() {
         CriteriaHelper.pollUiThread(() -> {
-            Criteria.checkThat("The SRP was never loaded.",
-                    ChromeTabUtils.getUrlOnUiThread(mTab).getValidSpecOrEmpty(),
+            Criteria.checkThat("The SRP was never loaded.", mTab.getUrl().getValidSpecOrEmpty(),
                     Matchers.containsString(SEARCH_URL_PATTERN));
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/query_tiles/QueryTileSectionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/query_tiles/QueryTileSectionTest.java
index 4bb2037..7fb46ecb0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/query_tiles/QueryTileSectionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/query_tiles/QueryTileSectionTest.java
@@ -40,7 +40,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.NewTabPageTestUtils;
 import org.chromium.chrome.test.util.OmniboxTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
@@ -210,14 +209,12 @@
     }
 
     private String getTabUrl() {
-        return ChromeTabUtils.getUrlOnUiThread(mActivityTestRule.getActivity().getActivityTab())
-                .getValidSpecOrEmpty();
+        return mActivityTestRule.getActivity().getActivityTab().getUrl().getValidSpecOrEmpty();
     }
 
     private void waitForSearchResultsPage() {
         CriteriaHelper.pollUiThread(() -> {
-            Criteria.checkThat("The SRP was never loaded.",
-                    ChromeTabUtils.getUrlOnUiThread(mTab).getValidSpecOrEmpty(),
+            Criteria.checkThat("The SRP was never loaded.", mTab.getUrl().getValidSpecOrEmpty(),
                     Matchers.containsString(SEARCH_URL_PATTERN));
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java
index 1b7b9fce..2b0cd07 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java
@@ -54,7 +54,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
@@ -295,7 +294,7 @@
         tabAddedCallback.waitForCallback(0);
         Tab tab = TestThreadUtils.runOnUiThreadBlocking(
                 () -> mTabbedActivityTestRule.getActivity().getActivityTab());
-        Assert.assertTrue(NewTabPage.isNTPUrl(ChromeTabUtils.getUrlOnUiThread(tab)));
+        Assert.assertTrue(NewTabPage.isNTPUrl(tab.getUrl()));
         Assert.assertFalse(tab.isIncognito());
         Assert.assertEquals(initialTabCount + 1,
                 mTabbedActivityTestRule.getActivity().getTabModelSelector().getTotalTabCount());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
index c5e501e..ef743182 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
@@ -1462,7 +1462,7 @@
         tab0 = model.getTabAt(0);
         Tab tab1 = model.getTabAt(1);
         tabs = new Tab[]{tab0, tab1};
-        Assert.assertEquals(TEST_URL_0, ChromeTabUtils.getUrlStringOnUiThread(tab1));
+        Assert.assertEquals(TEST_URL_0, tab1.getUrlString());
         checkState(model, tabs, tab0, EMPTY, tabs, tab0);
     }
 
@@ -1537,8 +1537,8 @@
                 firstModelTab);
         checkState(secondModel, secondWindowTabs, secondModelTab, EMPTY, secondWindowTabs,
                 secondModelTab);
-        Assert.assertEquals(TEST_URL_0, ChromeTabUtils.getUrlStringOnUiThread(firstWindowTabs[1]));
-        Assert.assertEquals(TEST_URL_1, ChromeTabUtils.getUrlStringOnUiThread(secondWindowTabs[1]));
+        Assert.assertEquals(TEST_URL_0, firstWindowTabs[1].getUrlString());
+        Assert.assertEquals(TEST_URL_1, secondWindowTabs[1].getUrlString());
 
         secondActivity.finishAndRemoveTask();
     }
@@ -1602,6 +1602,6 @@
         Tab tab1 = firstModel.getTabAt(1);
         Tab[] firstWindowTabs = new Tab[]{tab0, tab1};
         checkState(firstModel, firstWindowTabs, tab0, EMPTY, firstWindowTabs, tab0);
-        Assert.assertEquals(TEST_URL_1, ChromeTabUtils.getUrlStringOnUiThread(tab1));
+        Assert.assertEquals(TEST_URL_1, tab1.getUrlString());
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java
index 884ab05..be3f220c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java
@@ -32,7 +32,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.MenuUtils;
 import org.chromium.chrome.test.util.OmniboxTestUtils;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
@@ -127,7 +126,7 @@
 
         // Load new tab page.
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
-        Assert.assertEquals(UrlConstants.NTP_URL, ChromeTabUtils.getUrlStringOnUiThread(tab));
+        Assert.assertEquals(UrlConstants.NTP_URL, tab.getUrlString());
         assertFalse(isErrorPage(tab));
 
         // Stop the server and also disconnect the network.
@@ -136,7 +135,7 @@
                 () -> NetworkChangeNotifier.forceConnectivityState(false));
 
         mActivityTestRule.loadUrl(testUrl);
-        Assert.assertEquals(testUrl, ChromeTabUtils.getUrlStringOnUiThread(tab));
+        Assert.assertEquals(testUrl, tab.getUrlString());
         assertTrue(isErrorPage(tab));
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
index f2587a5..015f40d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
@@ -356,7 +356,7 @@
         String otherInScopeUrl =
                 WebappTestPage.getNonServiceWorkerUrl(mActivityTestRule.getTestServer());
         mActivityTestRule.loadUrlInTab(otherInScopeUrl, PageTransition.LINK, tab);
-        assertEquals(otherInScopeUrl, ChromeTabUtils.getUrlStringOnUiThread(tab));
+        assertEquals(otherInScopeUrl, tab.getUrlString());
 
         mActivityTestRule.loadUrlInTab(
                 offOriginUrl(), PageTransition.LINK, tab, 10 /* secondsToWait */);
diff --git a/chrome/android/junit/DEPS b/chrome/android/junit/DEPS
index c399fc8..3dc7290 100644
--- a/chrome/android/junit/DEPS
+++ b/chrome/android/junit/DEPS
@@ -10,6 +10,7 @@
   "+chrome/browser/tabmodel",
   "+chrome/browser/thumbnail/generator/android/java",
   "+chrome/browser/ui/messages/android/java",
+  "+chrome/browser/xsurface/android",
   "+components/autofill/android/java/src/org/chromium/components/autofill",
   "+components/background_task_scheduler/android",
   "+components/bookmarks/common/android",
diff --git a/chrome/android/monochrome/scripts/monochrome_apk_checker.py b/chrome/android/monochrome/scripts/monochrome_apk_checker.py
index 1bccea34..4bf3857 100755
--- a/chrome/android/monochrome/scripts/monochrome_apk_checker.py
+++ b/chrome/android/monochrome/scripts/monochrome_apk_checker.py
@@ -141,7 +141,7 @@
                 match.group('cmpr') == 0))
   return apk_entries
 
-def VerifySameFile(monochrome_dict, apk, changes):
+def VerifySameFile(monochrome_dict, apk, changes, apk_name):
   """Verify apk file content matches same files in monochrome.
 
   Verify files from apk are same as those in monochrome except files
@@ -154,11 +154,16 @@
     if m and m.CRC != a.CRC and not changes.match(m.filename):
       diff.append(a.filename)
   if len(diff):
-    raise Exception("The following files are not same as Monochrome:\n %s" %
-                    '\n'.join(diff))
+    msg = """\
+Unless specifcially excepted, all files in {0} should be exactly the same as
+the similarly named file in Monochrome. However these files were present in
+both monochrome and {0}, but had different contents:
+{1}
+""".format(apk_name, '\n'.join(diff))
+    raise Exception(msg)
 
 
-def VerifyUncompressed(monochrome, apk):
+def VerifyUncompressed(monochrome, apk, apk_name):
   """Verify uncompressed files in apk are a subset of those in monochrome.
 
   Verify files not being compressed in apk are also uncompressed in
@@ -168,10 +173,15 @@
   monochrome_uncompressed = [i.filename for i in monochrome if i.uncompressed]
   compressed = [u for u in uncompressed if u not in monochrome_uncompressed]
   if len(compressed):
-    raise Exception("The following files are compressed in Monochrome:\n %s" %
-                    '\n'.join(compressed))
+    msg = """\
+Uncompressed files in {0} should also be uncompressed in Monochrome.
+However these files were uncompressed in {0} but compressed in Monochrome:
+{1}
+""".format(apk_name, '\n'.join(compressed))
+    raise Exception(msg)
 
-def SuperSetOf(monochrome, apk):
+
+def SuperSetOf(monochrome, apk, apk_name):
   """Verify Monochrome is super set of apk."""
 
   def exists_in_some_form(f):
@@ -189,8 +199,12 @@
 
   missing_files = [f for f in apk if not exists_in_some_form(f)]
   if len(missing_files):
-    raise Exception('The following files are missing in Monochrome:\n %s' %
-                    '\n'.join(missing_files))
+    msg = """\
+Monochrome is expected to have a superset of the files in {0}.
+However these files were present in {0} but not in Monochrome:
+{1}
+""".format(apk_name, '\n'.join(missing_files))
+    raise Exception(msg)
 
 
 def RemoveSpecific(apk_entries, specific):
@@ -228,20 +242,29 @@
   Returns:
     An Namespace from argparse.parse_args()
   """
-  parser = argparse.ArgumentParser(prog='monochrome_apk_checker')
+  parser = argparse.ArgumentParser(
+      prog='monochrome_apk_checker',
+      description='This script enforces expectations about similarities '
+      'between Chrome, Monochrome and Webview APKs.',
+      epilog='If the release APK is obfuscated, you will find its pathmap next '
+      'to the apk in your output directory, ending with ".pathmap".')
 
-  parser.add_argument(
-      '--monochrome-apk', required=True, help='The monochrome APK path.')
+  required_args = parser.add_argument_group('required arguments')
+
+  required_args.add_argument(
+      '--monochrome-apk', required=True, help='The path to the monochrome APK.')
   parser.add_argument(
       '--monochrome-pathmap', help='The monochrome APK resources pathmap path.')
-  parser.add_argument('--chrome-apk',
-                      required=True,
-                      help='The chrome APK path.')
+  required_args.add_argument(
+      '--chrome-apk',
+      required=True,
+      help='The path to the chrome APK.')
   parser.add_argument(
       '--chrome-pathmap', help='The chrome APK resources pathmap path.')
-  parser.add_argument('--system-webview-apk',
-                      required=True,
-                      help='The system webview APK path.')
+  required_args.add_argument(
+      '--system-webview-apk',
+      required=True,
+      help='The path to the system webview APK.')
   parser.add_argument(
       '--system-webview-pathmap',
       help='The system webview APK resources pathmap path.')
@@ -265,25 +288,31 @@
   chrome = RemoveSpecific(DumpAPK(options.chrome_apk),
                           CHROME_SPECIFIC)
   if len(chrome) == 0:
-    raise Exception('Chrome should have common files with Monochrome')
+    raise Exception(
+        'Chrome should have common files with Monochrome. However the passed '
+        'in APKs do not have any files in common. Are you sure you are passing '
+        'in the right arguments?')
 
   webview = RemoveSpecific(DumpAPK(options.system_webview_apk),
                            WEBVIEW_SPECIFIC)
   if len(webview) == 0:
-    raise Exception('WebView should have common files with Monochrome')
+    raise Exception(
+        'Webview should have common files with Monochrome. However the passed '
+        'in APKs do not have any files in common. Are you sure you are passing '
+        'in the right arguments?')
 
-  def check_apk(apk, pathmap):
+  def check_apk(apk, pathmap, apk_name):
     apk_files = [DeobfuscateFilename(f.filename, pathmap) for f in apk]
-    SuperSetOf(monochrome_files, apk_files)
-    VerifyUncompressed(monochrome, apk)
-    VerifySameFile(monochrome_dict, chrome, CHROME_CHANGES)
-    VerifySameFile(monochrome_dict, webview, WEBVIEW_CHANGES)
+    SuperSetOf(monochrome_files, apk_files, apk_name)
+    VerifyUncompressed(monochrome, apk, apk_name)
 
   chrome_pathmap = LoadPathmap(options.chrome_pathmap)
-  check_apk(chrome, chrome_pathmap)
+  check_apk(chrome, chrome_pathmap, 'Chrome')
+  VerifySameFile(monochrome_dict, chrome, CHROME_CHANGES, 'Chrome')
 
   webview_pathmap = LoadPathmap(options.system_webview_pathmap)
-  check_apk(webview, webview_pathmap)
+  check_apk(webview, webview_pathmap, 'Webview')
+  VerifySameFile(monochrome_dict, webview, WEBVIEW_CHANGES, 'Webview')
 
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index b336af2e..ed689bf 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-86.0.4229.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-86.0.4231.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 4f491cf..3373ba25 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -365,6 +365,9 @@
   <message name="IDS_UPDATE_AVAILABLE" desc="Notification for available update">
     System update available. Preparing to download&#x2026;
   </message>
+  <message name="IDS_UPDATE_COMPLETED_REBOOTING" desc="Notification for update completed and reboot is in progress">
+    Restarting to apply updates
+  </message>
   <message name="IDS_UPDATE_COMPLETED" desc="Notification for update completed">
     System update complete. Please restart the system.
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_UPDATE_COMPLETED_REBOOTING.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_UPDATE_COMPLETED_REBOOTING.png.sha1
new file mode 100644
index 0000000..b65b854
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_UPDATE_COMPLETED_REBOOTING.png.sha1
@@ -0,0 +1 @@
+5b1d9d9d154116b2fbcd85c2fad7dba0f521f1fa
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3058db5..cd8e168 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10521,6 +10521,9 @@
         other {# open windows}
       }
     </message>
+    <message name="IDS_INCOGNITO_PROFILE_MENU_CREATE_SHORTCUT_BUTTON" desc="Label of the button to create a desktop shortcut for incognito mode.">
+      Create shortcut
+    </message>
     <message name="IDS_INCOGNITO_PROFILE_MENU_CLOSE_BUTTON" desc="The text of the button offering to close all incognito windows.">
       Exit Incognito
     </message>
diff --git a/chrome/app/generated_resources_grd/IDS_INCOGNITO_PROFILE_MENU_CREATE_SHORTCUT_BUTTON.png.sha1 b/chrome/app/generated_resources_grd/IDS_INCOGNITO_PROFILE_MENU_CREATE_SHORTCUT_BUTTON.png.sha1
new file mode 100644
index 0000000..90c6af5
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_INCOGNITO_PROFILE_MENU_CREATE_SHORTCUT_BUTTON.png.sha1
@@ -0,0 +1 @@
+94bf3c59a0ed0ba39d2a2c8629c81d2cc1b77504
\ No newline at end of file
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp
index cb9d39e..7e18e04 100644
--- a/chrome/app/profiles_strings.grdp
+++ b/chrome/app/profiles_strings.grdp
@@ -682,6 +682,9 @@
     <message name="IDS_PROFILE_PICKER_PROFILE_MENU_REMOVE_TEXT" desc="Text of the remove button in profile card menu and on the title of the remove warning.">
       Remove
     </message>
+    <message name="IDS_PROFILE_PICKER_PROFILE_MENU_CUSTOMIZE_TEXT" desc="Text of the customize button in profile card menu">
+      Customize
+    </message>
     <message name="IDS_PROFILE_PICKER_REMOVE_WARNING_LOCAL_PROFILE" desc="Main text shown as a warning when attempting to remove an user.">
       This will permanently delete your browsing data from this device.
     </message>
diff --git a/chrome/app/profiles_strings_grdp/IDS_PROFILE_PICKER_PROFILE_MENU_CUSTOMIZE_TEXT.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_PROFILE_PICKER_PROFILE_MENU_CUSTOMIZE_TEXT.png.sha1
new file mode 100644
index 0000000..84bc98d2
--- /dev/null
+++ b/chrome/app/profiles_strings_grdp/IDS_PROFILE_PICKER_PROFILE_MENU_CUSTOMIZE_TEXT.png.sha1
@@ -0,0 +1 @@
+0eb8b376ac429f0fc430c520e66c21845aeb307c
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index b86fb5f..a86406b 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1223,6 +1223,9 @@
   <message name="IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD" desc="This text points out that Safe Browsing is enabled as standard protection.">
     Standard Protection is on
   </message>
+  <message name="IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD_AVAILABLE_ENHANCED" desc="This text points out that Safe Browsing is enabled as standard protection, but the user can enable enhanced protection.">
+    Standard protection is on. For even more security, use enhanced protection.
+  </message>
   <message name="IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_ENHANCED" desc="This text points out that Safe Browsing is enabled as enhanced protection.">
     Enhanced Protection is on
   </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD_AVAILABLE_ENHANCED.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD_AVAILABLE_ENHANCED.png.sha1
new file mode 100644
index 0000000..d93b432
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD_AVAILABLE_ENHANCED.png.sha1
@@ -0,0 +1 @@
+86ab9204e762414a4b73373a9ae2183fd571b1df
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d95c972c..ac06474 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -5544,11 +5544,6 @@
      FEATURE_VALUE_TYPE(ash::features::kContextualNudges)},
 #endif  // defined(OS_CHROMEOS)
 
-    {"policy-atomic-group-enabled",
-     flag_descriptions::kPolicyAtomicGroupsEnabledName,
-     flag_descriptions::kPolicyAtomicGroupsEnabledDescription, kOsAll,
-     FEATURE_VALUE_TYPE(policy::features::kPolicyAtomicGroup)},
-
     {"decode-jpeg-images-to-yuv",
      flag_descriptions::kDecodeJpeg420ImagesToYUVName,
      flag_descriptions::kDecodeJpeg420ImagesToYUVDescription, kOsAll,
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index c21bf72..6b819552 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -69,6 +69,7 @@
 using SizeToImageSkiaRep = std::map<int, gfx::ImageSkiaRep>;
 using ScaleToImageSkiaReps = std::map<float, SizeToImageSkiaRep>;
 using MaskImageSkiaReps = std::pair<SkBitmap, ScaleToImageSkiaReps>;
+using ScaleToSize = std::map<float, int>;
 
 MaskImageSkiaReps& GetMaskResourceIconCache() {
   static base::NoDestructor<MaskImageSkiaReps> mask_cache;
@@ -111,20 +112,23 @@
   return image_rep;
 }
 
-gfx::ImageSkia LoadMaskImage(const gfx::ImageSkia& image) {
-  std::map<float, gfx::Size> scale_to_size;
-  if (image.image_reps().empty()) {
-    scale_to_size[1.0f] = image.size();
+ScaleToSize GetScaleToSize(const gfx::ImageSkia& image_skia) {
+  ScaleToSize scale_to_size;
+  if (image_skia.image_reps().empty()) {
+    scale_to_size[1.0f] = image_skia.size().width();
   } else {
-    for (const auto& rep : image.image_reps()) {
-      scale_to_size[rep.scale()] = rep.pixel_size();
+    for (const auto& rep : image_skia.image_reps()) {
+      scale_to_size[rep.scale()] = rep.pixel_width();
     }
   }
+  return scale_to_size;
+}
 
+gfx::ImageSkia LoadMaskImage(const ScaleToSize& scale_to_size) {
   gfx::ImageSkia mask_image;
   for (const auto& it : scale_to_size) {
     float scale = it.first;
-    int size_hint_in_dip = it.second.width();
+    int size_hint_in_dip = it.second;
     mask_image.AddRepresentation(
         GetMaskAsImageSkiaRep(scale, size_hint_in_dip));
   }
@@ -711,8 +715,7 @@
 void IconLoadingPipeline::ApplyBackgroundAndMask(const gfx::ImageSkia& image) {
   std::move(image_skia_callback_)
       .Run(gfx::ImageSkiaOperations::CreateResizedImage(
-          gfx::ImageSkiaOperations::CreateButtonBackground(
-              SK_ColorWHITE, image, LoadMaskImage(image)),
+          apps::ApplyBackgroundAndMask(image),
           skia::ImageOperations::RESIZE_LANCZOS3,
           gfx::Size(size_hint_in_dip_, size_hint_in_dip_)));
 }
@@ -740,10 +743,8 @@
 
   std::move(image_skia_callback_)
       .Run(gfx::ImageSkiaOperations::CreateResizedImage(
-          gfx::ImageSkiaOperations::CreateMaskedImage(
-              gfx::ImageSkiaOperations::CreateSuperimposedImage(
-                  background_image_, foreground_image_),
-              LoadMaskImage(image)),
+          apps::CompositeImagesAndApplyMask(foreground_image_,
+                                            background_image_),
           skia::ImageOperations::RESIZE_BEST,
           gfx::Size(size_hint_in_dip_, size_hint_in_dip_)));
 }
@@ -939,6 +940,21 @@
 }
 
 #if defined(OS_CHROMEOS)
+
+gfx::ImageSkia ApplyBackgroundAndMask(const gfx::ImageSkia& image) {
+  return gfx::ImageSkiaOperations::CreateButtonBackground(
+      SK_ColorWHITE, image, LoadMaskImage(GetScaleToSize(image)));
+}
+
+gfx::ImageSkia CompositeImagesAndApplyMask(
+    const gfx::ImageSkia& foreground_image,
+    const gfx::ImageSkia& background_image) {
+  return gfx::ImageSkiaOperations::CreateMaskedImage(
+      gfx::ImageSkiaOperations::CreateSuperimposedImage(background_image,
+                                                        foreground_image),
+      LoadMaskImage(GetScaleToSize(foreground_image)));
+}
+
 void ArcRawIconPngDataToImageSkia(
     arc::mojom::RawIconPngDataPtr icon,
     int size_hint_in_dip,
@@ -1015,7 +1031,7 @@
   }
 
   if (icon_effects & IconEffects::kCrOsStandardMask) {
-    auto mask_image = LoadMaskImage(*image_skia);
+    auto mask_image = LoadMaskImage(GetScaleToSize(*image_skia));
     *image_skia =
         gfx::ImageSkiaOperations::CreateMaskedImage(*image_skia, mask_image);
   }
diff --git a/chrome/browser/apps/app_service/app_icon_factory.h b/chrome/browser/apps/app_service/app_icon_factory.h
index 475d38a..e466c9e 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.h
+++ b/chrome/browser/apps/app_service/app_icon_factory.h
@@ -68,6 +68,12 @@
                                            float rep_icon_scale);
 
 #if defined(OS_CHROMEOS)
+gfx::ImageSkia ApplyBackgroundAndMask(const gfx::ImageSkia& image);
+
+gfx::ImageSkia CompositeImagesAndApplyMask(
+    const gfx::ImageSkia& foreground_image,
+    const gfx::ImageSkia& background_image);
+
 void ArcRawIconPngDataToImageSkia(
     arc::mojom::RawIconPngDataPtr icon,
     int size_hint_in_dip,
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index c4607a1de..7bb928f 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -107,10 +107,10 @@
       if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) {
         iv->uncompressed =
             icon->is_adaptive_icon()
-                ? gfx::ImageSkiaOperations::CreateSuperimposedImage(
-                      icon->background_image_skia(),
-                      icon->foreground_image_skia())
-                : icon->foreground_image_skia();
+                ? apps::CompositeImagesAndApplyMask(
+                      icon->foreground_image_skia(),
+                      icon->background_image_skia())
+                : apps::ApplyBackgroundAndMask(icon->foreground_image_skia());
       } else {
         iv->uncompressed = icon->image_skia();
       }
@@ -1364,10 +1364,6 @@
 IconEffects ArcApps::GetIconEffects(const std::string& app_id,
                                     const ArcAppListPrefs::AppInfo& app_info) {
   IconEffects icon_effects = IconEffects::kNone;
-  if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) {
-    icon_effects =
-        static_cast<IconEffects>(icon_effects | IconEffects::kCrOsStandardMask);
-  }
   if (app_info.suspended) {
     icon_effects =
         static_cast<IconEffects>(icon_effects | IconEffects::kBlocked);
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index f20e00d..3f9e4e71 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1902,6 +1902,8 @@
     "platform_keys/key_permissions/key_permissions.h",
     "platform_keys/key_permissions/key_permissions_policy_handler.cc",
     "platform_keys/key_permissions/key_permissions_policy_handler.h",
+    "platform_keys/platform_keys.cc",
+    "platform_keys/platform_keys.h",
     "platform_keys/platform_keys_service.cc",
     "platform_keys/platform_keys_service.h",
     "platform_keys/platform_keys_service_factory.cc",
diff --git a/chrome/browser/chromeos/arc/enterprise/cert_store/arc_cert_store_bridge_browsertest.cc b/chrome/browser/chromeos/arc/enterprise/cert_store/arc_cert_store_bridge_browsertest.cc
index 2f7742c7..be1e008 100644
--- a/chrome/browser/chromeos/arc/enterprise/cert_store/arc_cert_store_bridge_browsertest.cc
+++ b/chrome/browser/chromeos/arc/enterprise/cert_store/arc_cert_store_bridge_browsertest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/chromeos/arc/session/arc_service_launcher.h"
 #include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.h"
-#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/net/nss_context.h"
@@ -269,8 +269,7 @@
         client_cert1_->derPublicKey.data,
         client_cert1_->derPublicKey.data + client_cert1_->derPublicKey.len);
     permissions_for_ext->RegisterKeyForCorporateUsage(
-        client_cert1_spki,
-        {chromeos::platform_keys::KeyPermissions::KeyLocation::kUserSlot});
+        client_cert1_spki, {chromeos::platform_keys::TokenId::kUser});
     done_callback.Run();
   }
 
diff --git a/chrome/browser/chromeos/arc/enterprise/cert_store/arc_smart_card_manager_bridge.cc b/chrome/browser/chromeos/arc/enterprise/cert_store/arc_smart_card_manager_bridge.cc
index 6a65553..e1a6bfd 100644
--- a/chrome/browser/chromeos/arc/enterprise/cert_store/arc_smart_card_manager_bridge.cc
+++ b/chrome/browser/chromeos/arc/enterprise/cert_store/arc_smart_card_manager_bridge.cc
@@ -15,7 +15,7 @@
 #include "chrome/browser/chromeos/arc/policy/arc_policy_bridge.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
-#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/common/net/x509_certificate_model_nss.h"
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "components/policy/core/common/policy_map.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.cc
index 9a21d841..46b8242f 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.cc
@@ -8,6 +8,7 @@
 #include "base/notreached.h"
 #include "base/optional.h"
 #include "base/time/time.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h
index b497dc1..4ce13af 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h
@@ -12,6 +12,7 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chromeos/dbus/constants/attestation_constants.h"
 #include "components/policy/proto/device_management_backend.pb.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers.cc
index c4e80bd..4d49c0a8 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers.cc
@@ -12,6 +12,7 @@
 #include "base/optional.h"
 #include "base/stl_util.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 
 namespace chromeos {
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers_unittest.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers_unittest.cc
index bb0752b3..e66d270 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers_unittest.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h"
 #include "chrome/browser/chromeos/platform_keys/mock_platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
index 3b3ab8f..1a4b08b 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
index 5d0cd77..cf0d4af0 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_invalidator.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_platform_keys_helpers.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chromeos/network/network_state_handler_observer.h"
 #include "components/prefs/pref_change_registrar.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_unittest.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_unittest.cc
index ee82f0d..2e0150d 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_unittest.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h"
 #include "chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h"
 #include "chrome/browser/chromeos/platform_keys/mock_platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/network/network_state_test_helper.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h
index 5050fb181..52e63c64 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h
@@ -9,7 +9,7 @@
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/platform_keys/mock_platform_keys_service.h"
-#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/dbus/cryptohome/fake_cryptohome_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
index ccd7a2a..aa7d1a6 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_invalidator.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_serializer.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
index 103b30a..556a7bb 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "net/base/backoff_entry.h"
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc
index c468917..4a52666 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h"
 #include "chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_invalidator.h"
 #include "chrome/browser/chromeos/platform_keys/mock_platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
diff --git a/chrome/browser/chromeos/login/error_screen_browsertest.cc b/chrome/browser/chromeos/login/error_screen_browsertest.cc
index d75e945..5bd96de 100644
--- a/chrome/browser/chromeos/login/error_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/error_screen_browsertest.cc
@@ -333,7 +333,8 @@
 };
 
 // Verify that certificate manager dialog opens.
-IN_PROC_BROWSER_TEST_F(KioskErrorScreenTest, OpenCertificateConfig) {
+// Disabled for being flaky. See crbug.com/1116058.
+IN_PROC_BROWSER_TEST_F(KioskErrorScreenTest, DISABLED_OpenCertificateConfig) {
   apps_loaded_waiter()->Wait();
   EXPECT_TRUE(ash::LoginScreenTestApi::LaunchApp(kTestKioskAppId));
 
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index 2240b88..8ee63d42 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -1035,13 +1035,12 @@
   WaitForAppLaunchSuccess();
 }
 
-// TODO(https://crbug.com/964333): Flakily seg faults.
-IN_PROC_BROWSER_TEST_F(KioskTest, DISABLED_LaunchAppUserCancel) {
-  // Make fake_cws_ return empty update response.
-  set_test_app_version("");
-  OobeScreenWaiter splash_waiter(AppLaunchSplashScreenView::kScreenId);
+IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppUserCancel) {
   StartAppLaunchFromLoginScreen(
-      NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE);
+      NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE);
+  OobeScreenWaiter splash_waiter(AppLaunchSplashScreenView::kScreenId);
+  // Do not let the app be run to avoid race condition.
+  BlockAppLaunch(true);
   splash_waiter.Wait();
 
   settings_helper_.SetBoolean(
diff --git a/chrome/browser/chromeos/login/screens/mock_update_screen.h b/chrome/browser/chromeos/login/screens/mock_update_screen.h
index 2f9aa13f..d0bc772 100644
--- a/chrome/browser/chromeos/login/screens/mock_update_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_update_screen.h
@@ -41,6 +41,7 @@
   MOCK_METHOD(void, SetShowEstimatedTimeLeft, (bool value));
   MOCK_METHOD(void, SetUpdateCompleted, (bool value));
   MOCK_METHOD(void, SetShowCurtain, (bool value));
+  MOCK_METHOD(void, SetManualRebootNeeded, (bool value));
   MOCK_METHOD(void, SetProgressMessage, (const base::string16& value));
   MOCK_METHOD(void, SetProgress, (int value));
   MOCK_METHOD(void, SetRequiresPermissionForCellular, (bool value));
diff --git a/chrome/browser/chromeos/login/screens/update_screen.cc b/chrome/browser/chromeos/login/screens/update_screen.cc
index b27e1d5..0e7ecba 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.cc
+++ b/chrome/browser/chromeos/login/screens/update_screen.cc
@@ -36,6 +36,10 @@
 
 const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline";
 
+// Time in seconds after which we initiate reboot.
+constexpr const base::TimeDelta kWaitBeforeRebootTime =
+    base::TimeDelta::FromSeconds(2);
+
 // Delay before showing error message if captive portal is detected.
 // We wait for this delay to let captive portal to perform redirect and show
 // its login page before error message appears.
@@ -108,6 +112,7 @@
       histogram_helper_(
           std::make_unique<ErrorScreensHistogramHelper>("Update")),
       version_updater_(std::make_unique<VersionUpdater>(this)),
+      wait_before_reboot_time_(kWaitBeforeRebootTime),
       tick_clock_(base::DefaultTickClock::GetInstance()) {
   if (chromeos::features::IsBetterUpdateEnabled())
     PowerManagerClient::Get()->AddObserver(this);
@@ -216,8 +221,13 @@
 void UpdateScreen::OnWaitForRebootTimeElapsed() {
   LOG(ERROR) << "Unable to reboot - asking user for a manual reboot.";
   MakeSureScreenIsShown();
-  if (view_)
+  if (!view_)
+    return;
+  if (chromeos::features::IsBetterUpdateEnabled()) {
+    view_->SetManualRebootNeeded(true);
+  } else {
     view_->SetUpdateCompleted(true);
+  }
 }
 
 void UpdateScreen::PrepareForUpdateCheck() {
@@ -344,7 +354,14 @@
                            finalize_time_);
         RecordDownloadingTime(tick_clock_->NowTicks() -
                               start_update_downloading_);
-        version_updater_->RebootAfterUpdate();
+        if (chromeos::features::IsBetterUpdateEnabled()) {
+          ShowRebootInProgress();
+          wait_reboot_timer_.Start(FROM_HERE, wait_before_reboot_time_,
+                                   version_updater_.get(),
+                                   &VersionUpdater::RebootAfterUpdate);
+        } else {
+          version_updater_->RebootAfterUpdate();
+        }
       } else {
         hide_progress_on_exit_ = true;
         ExitUpdate(Result::UPDATE_NOT_REQUIRED);
@@ -387,6 +404,12 @@
   UpdateBatteryWarningVisibility();
 }
 
+void UpdateScreen::ShowRebootInProgress() {
+  MakeSureScreenIsShown();
+  if (view_)
+    view_->SetUpdateCompleted(true);
+}
+
 void UpdateScreen::UpdateBatteryWarningVisibility() {
   if (!view_)
     return;
diff --git a/chrome/browser/chromeos/login/screens/update_screen.h b/chrome/browser/chromeos/login/screens/update_screen.h
index eb9973d..b7d5d2f 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.h
+++ b/chrome/browser/chromeos/login/screens/update_screen.h
@@ -107,6 +107,15 @@
     tick_clock_ = tick_clock;
   }
 
+  void set_wait_before_reboot_time_for_testing(
+      base::TimeDelta wait_before_reboot_time) {
+    wait_before_reboot_time_ = wait_before_reboot_time;
+  }
+
+  base::OneShotTimer* GetWaitRebootTimerForTesting() {
+    return &wait_reboot_timer_;
+  }
+
  protected:
   // BaseScreen:
   bool MaybeSkip(WizardContext* context) override;
@@ -144,6 +153,9 @@
   // stages. Called when power or update status changes.
   void UpdateBatteryWarningVisibility();
 
+  // Show reboot waiting screen.
+  void ShowRebootInProgress();
+
   UpdateView* view_;
   ErrorScreen* error_screen_;
   ScreenExitCallback exit_callback_;
@@ -187,6 +199,13 @@
   // instead.
   base::OneShotTimer error_message_timer_;
 
+  // Timer for the interval to wait for the reboot progress screen to be shown
+  // for at least wait_before_reboot_time_ before reboot call.
+  base::OneShotTimer wait_reboot_timer_;
+
+  // Time in seconds after which we initiate reboot.
+  base::TimeDelta wait_before_reboot_time_;
+
   const base::TickClock* tick_clock_;
 
   base::TimeTicks start_update_downloading_;
diff --git a/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc
index 1a3704b..b61da657 100644
--- a/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_mock_time_message_loop_task_runner.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/login/login_wizard.h"
@@ -66,6 +67,11 @@
                                                 "battery-warning"};
 const test::UIPath kErrorMessage = {"error-message"};
 
+// Paths for better update screen https://crbug.com/1101317
+const test::UIPath kRestartingDialog = {"oobe-update", "restarting-dialog"};
+const test::UIPath kBetterUpdateCompletedDialog = {
+    "oobe-update", "better-update-complete-dialog"};
+
 // UMA names for better test reading.
 const char kTimeCheck[] = "OOBE.UpdateScreen.StageTime.Check";
 const char kTimeDownload[] = "OOBE.UpdateScreen.StageTime.Download";
@@ -86,6 +92,8 @@
     base::TimeDelta::FromSeconds(10);
 constexpr base::TimeDelta kTimeAdvanceSeconds60 =
     base::TimeDelta::FromSeconds(60);
+constexpr base::TimeDelta kTimeDefaultWaiting =
+    base::TimeDelta::FromSeconds(10);
 
 std::string GetDownloadingString(int status_resource_id) {
   return l10n_util::GetStringFUTF8(
@@ -181,6 +189,17 @@
   }
   ~BetterUpdateScreenTest() override = default;
 
+  void SetTickClockAndDefaultDelaysForTesting(
+      const base::TickClock* tick_clock) {
+    version_updater_->set_tick_clock_for_testing(tick_clock);
+    update_screen_->set_tick_clock_for_testing(tick_clock);
+    // Set time for waiting in the test to not update constants manually, if
+    // they change.
+    version_updater_->set_wait_for_reboot_time_for_testing(kTimeDefaultWaiting);
+    update_screen_->set_wait_before_reboot_time_for_testing(
+        kTimeDefaultWaiting);
+  }
+
  protected:
   chromeos::FakePowerManagerClient* power_manager_client() {
     return chromeos::FakePowerManagerClient::Get();
@@ -778,6 +797,9 @@
 
 IN_PROC_BROWSER_TEST_F(BetterUpdateScreenTest,
                        TestBatteryWarningDuringUpdateStages) {
+  base::ScopedMockTimeMessageLoopTaskRunner mocked_task_runner;
+  SetTickClockAndDefaultDelaysForTesting(
+      mocked_task_runner->GetMockTickClock());
   update_screen_->set_ignore_update_deadlines_for_testing(true);
   ShowUpdateScreen();
   EXPECT_TRUE(power_manager_client()->HasObserver(update_screen_));
@@ -832,6 +854,10 @@
   update_engine_client()->set_default_status(status);
   update_engine_client()->NotifyObserversThatStatusChanged(status);
 
+  // Show waiting for reboot screen for several seconds.
+  ASSERT_TRUE(update_screen_->GetWaitRebootTimerForTesting()->IsRunning());
+  mocked_task_runner->FastForwardBy(kTimeDefaultWaiting);
+
   // UpdateStatusChanged(status) calls RebootAfterUpdate().
   EXPECT_EQ(update_engine_client()->reboot_after_update_call_count(), 1);
   test::OobeJS().ExpectVisiblePath(kLowBatteryWarningMessage);
@@ -884,4 +910,42 @@
   test::OobeJS().ExpectHiddenPath(kLowBatteryWarningMessage);
 }
 
+IN_PROC_BROWSER_TEST_F(BetterUpdateScreenTest,
+                       TestUpdateCompletedRebootNeeded) {
+  base::ScopedMockTimeMessageLoopTaskRunner mocked_task_runner;
+  SetTickClockAndDefaultDelaysForTesting(
+      mocked_task_runner->GetMockTickClock());
+  update_screen_->set_ignore_update_deadlines_for_testing(true);
+  ShowUpdateScreen();
+
+  update_engine::StatusResult status;
+  status.set_current_operation(update_engine::Operation::UPDATED_NEED_REBOOT);
+  status.set_new_version("latest and greatest");
+  status.set_new_size(1'000'000'000);
+  update_engine_client()->set_default_status(status);
+  update_engine_client()->NotifyObserversThatStatusChanged(status);
+
+  test::OobeJS().CreateVisibilityWaiter(true, kRestartingDialog)->Wait();
+  test::OobeJS().ExpectHiddenPath(kCheckingForUpdatesDialog);
+  test::OobeJS().ExpectHiddenPath(kCellularPermissionDialog);
+  test::OobeJS().ExpectHiddenPath(kUpdatingDialog);
+  test::OobeJS().ExpectHiddenPath(kBetterUpdateCompletedDialog);
+
+  // Make sure that after the screen is shown waiting timer starts.
+  mocked_task_runner->RunUntilIdle();
+  // Show waiting for reboot screen for several seconds.
+  ASSERT_TRUE(update_screen_->GetWaitRebootTimerForTesting()->IsRunning());
+  mocked_task_runner->FastForwardBy(kTimeDefaultWaiting);
+
+  // UpdateStatusChanged(status) calls RebootAfterUpdate().
+  ASSERT_EQ(update_engine_client()->reboot_after_update_call_count(), 1);
+
+  // Simulate the situation where reboot does not happen in time.
+  ASSERT_TRUE(version_updater_->GetRebootTimerForTesting()->IsRunning());
+  mocked_task_runner->FastForwardBy(kTimeDefaultWaiting);
+
+  test::OobeJS().ExpectHiddenPath(kRestartingDialog);
+  test::OobeJS().ExpectVisiblePath(kBetterUpdateCompletedDialog);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/phonehub/OWNERS b/chrome/browser/chromeos/phonehub/OWNERS
new file mode 100644
index 0000000..f603cffa
--- /dev/null
+++ b/chrome/browser/chromeos/phonehub/OWNERS
@@ -0,0 +1,3 @@
+file://chromeos/components/phonehub/OWNERS
+
+# COMPONENT: OS>Systems>Multidevice>PhoneHub
diff --git a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.cc b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.cc
index ee9037d..c578eef 100644
--- a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.cc
+++ b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.cc
@@ -16,6 +16,7 @@
 #include "base/optional.h"
 #include "base/stl_util.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -53,26 +54,6 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
-// Converts |token_ids| (string-based token identifiers used in the
-// platformKeys API) to a vector of KeyPermissions::KeyLocation.
-std::vector<platform_keys::KeyPermissions::KeyLocation> TokenIdsToKeyLocations(
-    const std::vector<platform_keys::TokenId>& token_ids) {
-  std::vector<platform_keys::KeyPermissions::KeyLocation> key_locations;
-  for (const auto& token_id : token_ids) {
-    switch (token_id) {
-      case platform_keys::TokenId::kUser:
-        key_locations.push_back(
-            platform_keys::KeyPermissions::KeyLocation::kUserSlot);
-        break;
-      case platform_keys::TokenId::kSystem:
-        key_locations.push_back(
-            platform_keys::KeyPermissions::KeyLocation::kSystemSlot);
-        break;
-    }
-  }
-  return key_locations;
-}
-
 }  // namespace
 
 class ExtensionPlatformKeysService::Task {
@@ -172,8 +153,7 @@
   }
 
   void UpdatePermissionsAndCallBack() {
-    std::vector<platform_keys::KeyPermissions::KeyLocation> key_locations =
-        TokenIdsToKeyLocations({token_id_});
+    std::vector<platform_keys::TokenId> key_locations = {token_id_};
     extension_permissions_->RegisterKeyForCorporateUsage(public_key_spki_der_,
                                                          key_locations);
     callback_.Run(public_key_spki_der_, platform_keys::Status::kSuccess);
@@ -368,7 +348,7 @@
       return;
     }
 
-    key_locations_ = TokenIdsToKeyLocations(token_ids);
+    key_locations_ = token_ids;
     DoStep();
   }
 
@@ -424,7 +404,7 @@
   std::unique_ptr<platform_keys::KeyPermissions::PermissionsForExtension>
       extension_permissions_;
   platform_keys::KeyPermissions* const key_permissions_;
-  std::vector<platform_keys::KeyPermissions::KeyLocation> key_locations_;
+  std::vector<platform_keys::TokenId> key_locations_;
   ExtensionPlatformKeysService* const service_;
   base::WeakPtrFactory<SignTask> weak_factory_{this};
 
@@ -615,17 +595,14 @@
     const std::string public_key_spki_der(
         platform_keys::GetSubjectPublicKeyInfo(certificate));
 
-    std::vector<platform_keys::KeyPermissions::KeyLocation> key_locations =
-        TokenIdsToKeyLocations(token_ids);
-
     // Use this key if the user can use it for signing or can grant permission
     // for it.
     if (key_permissions_->CanUserGrantPermissionFor(public_key_spki_der,
-                                                    key_locations) ||
+                                                    token_ids) ||
         extension_permissions_->CanUseKeyForSigning(public_key_spki_der,
-                                                    key_locations)) {
+                                                    token_ids)) {
       matches_.push_back(certificate);
-      key_locations_for_matches_[public_key_spki_der] = key_locations;
+      key_locations_for_matches_[public_key_spki_der] = token_ids;
     }
     DoStep();
   }
@@ -729,10 +706,9 @@
   std::deque<scoped_refptr<net::X509Certificate>>
       matches_pending_key_locations_;
   net::CertificateList matches_;
-  // Mapping of DER-encoded Subject Public Key Info to the KeyLocations
-  // determined for the corresponding private key.
-  base::flat_map<std::string,
-                 std::vector<platform_keys::KeyPermissions::KeyLocation>>
+  // Mapping of DER-encoded Subject Public Key Info to the TokenIds determined
+  // for the corresponding private key.
+  base::flat_map<std::string, std::vector<platform_keys::TokenId>>
       key_locations_for_matches_;
   scoped_refptr<net::X509Certificate> selected_cert_;
   platform_keys::ClientCertificateRequest request_;
diff --git a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h
index 47f7be1..d4fd2a29 100644
--- a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h
+++ b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "components/keyed_service/core/keyed_service.h"
 
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.cc b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.cc
index 705b6a4..523727f 100644
--- a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.cc
+++ b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.cc
@@ -12,6 +12,7 @@
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_namespace.h"
@@ -146,9 +147,8 @@
   return allow_corporate_key_usage;
 }
 
-bool IsKeyOnUserSlot(
-    const std::vector<KeyPermissions::KeyLocation>& key_locations) {
-  return base::Contains(key_locations, KeyPermissions::KeyLocation::kUserSlot);
+bool IsKeyOnUserSlot(const std::vector<TokenId>& key_locations) {
+  return base::Contains(key_locations, TokenId::kUser);
 }
 
 }  // namespace
@@ -194,7 +194,7 @@
 
 bool KeyPermissions::PermissionsForExtension::CanUseKeyForSigning(
     const std::string& public_key_spki_der,
-    const std::vector<KeyLocation>& key_locations) {
+    const std::vector<TokenId>& key_locations) {
   if (key_locations.empty())
     return false;
 
@@ -227,7 +227,7 @@
 
 void KeyPermissions::PermissionsForExtension::SetKeyUsedForSigning(
     const std::string& public_key_spki_der,
-    const std::vector<KeyLocation>& key_locations) {
+    const std::vector<TokenId>& key_locations) {
   if (key_locations.empty())
     return;
 
@@ -248,7 +248,7 @@
 
 void KeyPermissions::PermissionsForExtension::RegisterKeyForCorporateUsage(
     const std::string& public_key_spki_der,
-    const std::vector<KeyLocation>& key_locations) {
+    const std::vector<TokenId>& key_locations) {
   if (key_locations.empty()) {
     NOTREACHED();
     return;
@@ -287,7 +287,7 @@
 
 void KeyPermissions::PermissionsForExtension::SetUserGrantedPermission(
     const std::string& public_key_spki_der,
-    const std::vector<KeyLocation>& key_locations) {
+    const std::vector<TokenId>& key_locations) {
   if (!key_permissions_->CanUserGrantPermissionFor(public_key_spki_der,
                                                    key_locations)) {
     LOG(WARNING) << "Tried to grant permission for a key although prohibited "
@@ -422,7 +422,7 @@
 
 bool KeyPermissions::CanUserGrantPermissionFor(
     const std::string& public_key_spki_der,
-    const std::vector<KeyLocation>& key_locations) const {
+    const std::vector<TokenId>& key_locations) const {
   if (key_locations.empty())
     return false;
 
@@ -480,14 +480,14 @@
 
 bool KeyPermissions::IsCorporateKey(
     const std::string& public_key_spki_der_b64,
-    const std::vector<KeyPermissions::KeyLocation>& key_locations) const {
-  for (const KeyLocation key_location : key_locations) {
+    const std::vector<TokenId>& key_locations) const {
+  for (const auto key_location : key_locations) {
     switch (key_location) {
-      case KeyLocation::kUserSlot:
+      case TokenId::kUser:
         if (IsCorporateKeyForProfile(public_key_spki_der_b64, profile_prefs_))
           return true;
         break;
-      case KeyLocation::kSystemSlot:
+      case TokenId::kSystem:
         return true;
       default:
         NOTREACHED();
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.h b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.h
index e2c3874f..148a8601 100644
--- a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.h
+++ b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions.h
@@ -12,6 +12,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 
 class PrefService;
 
@@ -73,21 +74,6 @@
 // permitted by the administrator.
 class KeyPermissions {
  public:
-  // Specifies where a private key is stored.
-  enum class KeyLocation {
-    // The key resides on the user's private slot. This is user-specific
-    // hardware-backed storage. Therefore the key is only available while the
-    // corresponding user is signed in.
-    kUserSlot,
-
-    // The key resides on the system slot. This is system-wide hardware-backed
-    // storage. The key is potentially accessible throughout all types of
-    // sessions, however, additional constraints may be imposed by the API logic
-    // - for instance, depending on the session type (e.g. affiliated user
-    // session or unaffiliated user session).
-    kSystemSlot
-  };
-
   // Allows querying and modifying permissions and registering keys for a
   // specific extension.
   class PermissionsForExtension {
@@ -108,8 +94,9 @@
     // used for signing by the extension with id |extension_id_|.
     // |key_locations| must describe locations available to the user the private
     // key is stored on.
-    bool CanUseKeyForSigning(const std::string& public_key_spki_der,
-                             const std::vector<KeyLocation>& key_locations);
+    bool CanUseKeyForSigning(
+        const std::string& public_key_spki_der,
+        const std::vector<platform_keys::TokenId>& key_locations);
 
     // Registers the private key matching |public_key_spki_der| as being
     // generated by the extension with id |extension_id| and marks it for
@@ -117,7 +104,7 @@
     // user the private key is stored on.
     void RegisterKeyForCorporateUsage(
         const std::string& public_key_spki_der,
-        const std::vector<KeyLocation>& key_locations);
+        const std::vector<platform_keys::TokenId>& key_locations);
 
     // Sets the user granted permission that the extension with id
     // |extension_id| can use the private key matching |public_key_spki_der| for
@@ -125,7 +112,7 @@
     // the private key is stored on.
     void SetUserGrantedPermission(
         const std::string& public_key_spki_der,
-        const std::vector<KeyLocation>& key_locations);
+        const std::vector<platform_keys::TokenId>& key_locations);
 
     // Must be called when the extension with id |extension_id| used the private
     // key matching |public_key_spki_der| for signing. |key_locations| must
@@ -133,8 +120,9 @@
     // Updates the permissions accordingly.  E.g. if this extension generated
     // the key and no other permission was granted then the permission to sign
     // with this key is removed.
-    void SetKeyUsedForSigning(const std::string& public_key_spki_der,
-                              const std::vector<KeyLocation>& key_locations);
+    void SetKeyUsedForSigning(
+        const std::string& public_key_spki_der,
+        const std::vector<platform_keys::TokenId>& key_locations);
 
    private:
     struct KeyEntry;
@@ -197,7 +185,7 @@
   // locations available to the user the private key is stored on.
   bool CanUserGrantPermissionFor(
       const std::string& public_key_spki_der,
-      const std::vector<KeyLocation>& key_locations) const;
+      const std::vector<platform_keys::TokenId>& key_locations) const;
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
@@ -212,8 +200,9 @@
       policy::PolicyService* const profile_policies);
 
  private:
-  bool IsCorporateKey(const std::string& public_key_spki_der_b64,
-                      const std::vector<KeyLocation>& key_locations) const;
+  bool IsCorporateKey(
+      const std::string& public_key_spki_der_b64,
+      const std::vector<platform_keys::TokenId>& key_locations) const;
 
   // Creates a PermissionsForExtension object from |extension_id| and |value|
   // and passes the object to |callback|.
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys.cc b/chrome/browser/chromeos/platform_keys/platform_keys.cc
new file mode 100644
index 0000000..7abfcac
--- /dev/null
+++ b/chrome/browser/chromeos/platform_keys/platform_keys.cc
@@ -0,0 +1,108 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "net/base/hash_value.h"
+#include "net/base/net_errors.h"
+#include "net/cert/x509_certificate.h"
+
+namespace {
+
+void IntersectOnWorkerThread(const net::CertificateList& certs1,
+                             const net::CertificateList& certs2,
+                             net::CertificateList* intersection) {
+  std::map<net::SHA256HashValue, scoped_refptr<net::X509Certificate>>
+      fingerprints2;
+
+  // Fill the map with fingerprints of certs from |certs2|.
+  for (const auto& cert2 : certs2) {
+    fingerprints2[net::X509Certificate::CalculateFingerprint256(
+        cert2->cert_buffer())] = cert2;
+  }
+
+  // Compare each cert from |certs1| with the entries of the map.
+  for (const auto& cert1 : certs1) {
+    const net::SHA256HashValue fingerprint1 =
+        net::X509Certificate::CalculateFingerprint256(cert1->cert_buffer());
+    const auto it = fingerprints2.find(fingerprint1);
+    if (it == fingerprints2.end())
+      continue;
+    const auto& cert2 = it->second;
+    DCHECK(cert1->EqualsExcludingChain(cert2.get()));
+    intersection->push_back(cert1);
+  }
+}
+
+}  // namespace
+
+namespace chromeos {
+namespace platform_keys {
+
+std::string StatusToString(Status status) {
+  switch (status) {
+    case Status::kSuccess:
+      return "The operation was successfully executed.";
+    case Status::kErrorAlgorithmNotSupported:
+      return "Algorithm not supported.";
+    case Status::kErrorCertificateNotFound:
+      return "Certificate could not be found.";
+    case Status::kErrorInternal:
+      return "Internal Error.";
+    case Status::kErrorKeyAttributeRetrievalFailed:
+      return "Key attribute value retrieval failed.";
+    case Status::kErrorKeyAttributeSettingFailed:
+      return "Setting key attribute value failed.";
+    case Status::kErrorKeyNotAllowedForSigning:
+      return "This key is not allowed for signing. Either it was used for "
+             "signing before or it was not correctly generated.";
+    case Status::kErrorKeyNotFound:
+      return "Key not found.";
+    case Status::kErrorShutDown:
+      return "Delegate shut down.";
+    case Status::kNetErrorAddUserCertFailed:
+      return net::ErrorToString(net::ERR_ADD_USER_CERT_FAILED);
+    case Status::kNetErrorCertificateDateInvalid:
+      return net::ErrorToString(net::ERR_CERT_DATE_INVALID);
+    case Status::kNetErrorCertificateInvalid:
+      return net::ErrorToString(net::ERR_CERT_INVALID);
+  }
+}
+
+void IntersectCertificates(
+    const net::CertificateList& certs1,
+    const net::CertificateList& certs2,
+    const base::Callback<void(std::unique_ptr<net::CertificateList>)>&
+        callback) {
+  std::unique_ptr<net::CertificateList> intersection(new net::CertificateList);
+  net::CertificateList* const intersection_ptr = intersection.get();
+
+  // This is triggered by a call to the
+  // chrome.platformKeys.selectClientCertificates extensions API. Completion
+  // does not affect browser responsiveness, hence the BEST_EFFORT priority.
+  base::ThreadPool::PostTaskAndReply(
+      FROM_HERE,
+      {base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(&IntersectOnWorkerThread, certs1, certs2,
+                     intersection_ptr),
+      base::BindOnce(callback, base::Passed(&intersection)));
+}
+
+ClientCertificateRequest::ClientCertificateRequest() = default;
+
+ClientCertificateRequest::ClientCertificateRequest(
+    const ClientCertificateRequest& other) = default;
+
+ClientCertificateRequest::~ClientCertificateRequest() = default;
+
+}  // namespace platform_keys
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys.h b/chrome/browser/chromeos/platform_keys/platform_keys.h
new file mode 100644
index 0000000..2409d4e8
--- /dev/null
+++ b/chrome/browser/chromeos/platform_keys/platform_keys.h
@@ -0,0 +1,115 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_PLATFORM_KEYS_H_
+#define CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_PLATFORM_KEYS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "net/cert/x509_certificate.h"
+
+namespace chromeos {
+namespace platform_keys {
+
+// Supported key types.
+enum class KeyType { kRsassaPkcs1V15, kEcdsa };
+
+// Supported key attribute types.
+enum class KeyAttributeType { kCertificateProvisioningId };
+
+// Supported hash algorithms.
+enum HashAlgorithm {
+  HASH_ALGORITHM_NONE,  // The value if no hash function is selected.
+  HASH_ALGORITHM_SHA1,
+  HASH_ALGORITHM_SHA256,
+  HASH_ALGORITHM_SHA384,
+  HASH_ALGORITHM_SHA512
+};
+
+// Supported token IDs.
+// A token is a store for keys or certs and can provide cryptographic
+// operations.
+// ChromeOS provides itself a user token and conditionally a system wide token.
+enum class TokenId { kUser, kSystem };
+
+// The service possible statuses.
+// For every platform keys service operation callback, a status is passed
+// signaling the success or failure of the operation.
+enum class Status {
+  kSuccess,
+  kErrorAlgorithmNotSupported,
+  kErrorCertificateNotFound,
+  kErrorInternal,
+  kErrorKeyAttributeRetrievalFailed,
+  kErrorKeyAttributeSettingFailed,
+  kErrorKeyNotAllowedForSigning,
+  kErrorKeyNotFound,
+  kErrorShutDown,
+  // kNetError* are for errors occurred during net::* operations.
+  kNetErrorAddUserCertFailed,
+  kNetErrorCertificateDateInvalid,
+  kNetErrorCertificateInvalid,
+};
+
+// These strings can be used to be passed to extensions as well as for logging
+// purposes.
+// Note: Do not change already existing status-to-string translations, since
+// extensions may hardcode specific messages.
+std::string StatusToString(Status status);
+
+// Returns the DER encoding of the X.509 Subject Public Key Info of the public
+// key in |certificate|.
+std::string GetSubjectPublicKeyInfo(
+    const scoped_refptr<net::X509Certificate>& certificate);
+
+// Intersects the two certificate lists |certs1| and |certs2| and passes the
+// intersection to |callback|. The intersction preserves the order of |certs1|.
+void IntersectCertificates(
+    const net::CertificateList& certs1,
+    const net::CertificateList& certs2,
+    const base::Callback<void(std::unique_ptr<net::CertificateList>)>&
+        callback);
+
+// Obtains information about the public key in |certificate|.
+// If |certificate| contains an RSA key, sets |key_size_bits| to the modulus
+// length, and |key_type| to type RSA and returns true.
+// If |certificate| contains any other key type, or if the public exponent of
+// the RSA key in |certificate| is not F4, returns false and does not update any
+// of the output parameters.
+// All pointer arguments must not be null.
+bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
+                  net::X509Certificate::PublicKeyType* key_type,
+                  size_t* key_size_bits);
+
+// Obtains information about the public key in |spki|.
+// If |spki| is an RSA key, sets |key_size_bits| to the modulus
+// length, and |key_type| to type RSA and returns true.
+// If |spki| is any other key type, returns false and does not update any
+// of the output parameters.
+// All pointer arguments must not be null.
+bool GetPublicKeyBySpki(const std::string& spki,
+                        net::X509Certificate::PublicKeyType* key_type,
+                        size_t* key_size_bits);
+
+struct ClientCertificateRequest {
+  ClientCertificateRequest();
+  ClientCertificateRequest(const ClientCertificateRequest& other);
+  ~ClientCertificateRequest();
+
+  // The list of the types of certificates requested, sorted in order of the
+  // server's preference.
+  std::vector<net::X509Certificate::PublicKeyType> certificate_key_types;
+
+  // List of distinguished names of certificate authorities allowed by the
+  // server. Each entry must be a DER-encoded X.509 DistinguishedName.
+  std::vector<std::string> certificate_authorities;
+};
+
+}  // namespace platform_keys
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_PLATFORM_KEYS_H_
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
index 6f56822..6d8d650 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
@@ -13,101 +13,12 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "content/public/browser/browser_thread.h"
-#include "net/base/hash_value.h"
 #include "net/base/net_errors.h"
 #include "net/cert/x509_certificate.h"
 
 namespace chromeos {
 namespace platform_keys {
 
-namespace {
-
-void IntersectOnWorkerThread(const net::CertificateList& certs1,
-                             const net::CertificateList& certs2,
-                             net::CertificateList* intersection) {
-  std::map<net::SHA256HashValue, scoped_refptr<net::X509Certificate>>
-      fingerprints2;
-
-  // Fill the map with fingerprints of certs from |certs2|.
-  for (const auto& cert2 : certs2) {
-    fingerprints2[net::X509Certificate::CalculateFingerprint256(
-        cert2->cert_buffer())] = cert2;
-  }
-
-  // Compare each cert from |certs1| with the entries of the map.
-  for (const auto& cert1 : certs1) {
-    const net::SHA256HashValue fingerprint1 =
-        net::X509Certificate::CalculateFingerprint256(cert1->cert_buffer());
-    const auto it = fingerprints2.find(fingerprint1);
-    if (it == fingerprints2.end())
-      continue;
-    const auto& cert2 = it->second;
-    DCHECK(cert1->EqualsExcludingChain(cert2.get()));
-    intersection->push_back(cert1);
-  }
-}
-
-}  // namespace
-
-std::string StatusToString(Status status) {
-  switch (status) {
-    case Status::kSuccess:
-      return "The operation was successfully executed.";
-    case Status::kErrorAlgorithmNotSupported:
-      return "Algorithm not supported.";
-    case Status::kErrorCertificateNotFound:
-      return "Certificate could not be found.";
-    case Status::kErrorInternal:
-      return "Internal Error.";
-    case Status::kErrorKeyAttributeRetrievalFailed:
-      return "Key attribute value retrieval failed.";
-    case Status::kErrorKeyAttributeSettingFailed:
-      return "Setting key attribute value failed.";
-    case Status::kErrorKeyNotAllowedForSigning:
-      return "This key is not allowed for signing. Either it was used for "
-             "signing before or it was not correctly generated.";
-    case Status::kErrorKeyNotFound:
-      return "Key not found.";
-    case Status::kErrorShutDown:
-      return "Delegate shut down.";
-    case Status::kNetErrorAddUserCertFailed:
-      return net::ErrorToString(net::ERR_ADD_USER_CERT_FAILED);
-    case Status::kNetErrorCertificateDateInvalid:
-      return net::ErrorToString(net::ERR_CERT_DATE_INVALID);
-    case Status::kNetErrorCertificateInvalid:
-      return net::ErrorToString(net::ERR_CERT_INVALID);
-  }
-}
-
-void IntersectCertificates(
-    const net::CertificateList& certs1,
-    const net::CertificateList& certs2,
-    const base::Callback<void(std::unique_ptr<net::CertificateList>)>&
-        callback) {
-  std::unique_ptr<net::CertificateList> intersection(new net::CertificateList);
-  net::CertificateList* const intersection_ptr = intersection.get();
-
-  // This is triggered by a call to the
-  // chrome.platformKeys.selectClientCertificates extensions API. Completion
-  // does not affect browser responsiveness, hence the BEST_EFFORT priority.
-  base::ThreadPool::PostTaskAndReply(
-      FROM_HERE,
-      {base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::BindOnce(&IntersectOnWorkerThread, certs1, certs2,
-                     intersection_ptr),
-      base::BindOnce(callback, base::Passed(&intersection)));
-}
-
-// =================== ClientCertificateRequest ================================
-
-ClientCertificateRequest::ClientCertificateRequest() = default;
-
-ClientCertificateRequest::ClientCertificateRequest(
-    const ClientCertificateRequest& other) = default;
-
-ClientCertificateRequest::~ClientCertificateRequest() = default;
-
 // =============== PlatformKeysServiceImplDelegate =============================
 
 PlatformKeysServiceImplDelegate::PlatformKeysServiceImplDelegate() = default;
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.h b/chrome/browser/chromeos/platform_keys/platform_keys_service.h
index e6237dc0..29b572f 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service.h
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.h
@@ -17,6 +17,7 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/optional.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "net/cert/x509_certificate.h"
 
@@ -28,100 +29,6 @@
 namespace chromeos {
 namespace platform_keys {
 
-// Supported key types.
-enum class KeyType { kRsassaPkcs1V15, kEcdsa };
-
-// Supported key attribute types.
-enum class KeyAttributeType { kCertificateProvisioningId };
-
-// Supported hash algorithms.
-enum HashAlgorithm {
-  HASH_ALGORITHM_NONE,  // The value if no hash function is selected.
-  HASH_ALGORITHM_SHA1,
-  HASH_ALGORITHM_SHA256,
-  HASH_ALGORITHM_SHA384,
-  HASH_ALGORITHM_SHA512
-};
-
-// Supported token IDs.
-// A token is a store for keys or certs and can provide cryptographic
-// operations.
-// ChromeOS provides itself a user token and conditionally a system wide token.
-enum class TokenId { kUser, kSystem };
-
-// The service possible statuses.
-// For every platform keys service operation callback, a status is passed
-// signaling the success or failure of the operation.
-enum class Status {
-  kSuccess,
-  kErrorAlgorithmNotSupported,
-  kErrorCertificateNotFound,
-  kErrorInternal,
-  kErrorKeyAttributeRetrievalFailed,
-  kErrorKeyAttributeSettingFailed,
-  kErrorKeyNotAllowedForSigning,
-  kErrorKeyNotFound,
-  kErrorShutDown,
-  // kNetError* are for errors occurred during net::* operations.
-  kNetErrorAddUserCertFailed,
-  kNetErrorCertificateDateInvalid,
-  kNetErrorCertificateInvalid,
-};
-
-// These strings can be used to be passed to extensions as well as for logging
-// purposes.
-// Note: Do not change already existing status-to-string translations, since
-// extensions may hardcode specific messages.
-std::string StatusToString(Status status);
-
-// Returns the DER encoding of the X.509 Subject Public Key Info of the public
-// key in |certificate|.
-std::string GetSubjectPublicKeyInfo(
-    const scoped_refptr<net::X509Certificate>& certificate);
-
-// Intersects the two certificate lists |certs1| and |certs2| and passes the
-// intersection to |callback|. The intersction preserves the order of |certs1|.
-void IntersectCertificates(
-    const net::CertificateList& certs1,
-    const net::CertificateList& certs2,
-    const base::Callback<void(std::unique_ptr<net::CertificateList>)>&
-        callback);
-
-// Obtains information about the public key in |certificate|.
-// If |certificate| contains an RSA key, sets |key_size_bits| to the modulus
-// length, and |key_type| to type RSA and returns true.
-// If |certificate| contains any other key type, or if the public exponent of
-// the RSA key in |certificate| is not F4, returns false and does not update any
-// of the output parameters.
-// All pointer arguments must not be null.
-bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
-                  net::X509Certificate::PublicKeyType* key_type,
-                  size_t* key_size_bits);
-
-// Obtains information about the public key in |spki|.
-// If |spki| is an RSA key, sets |key_size_bits| to the modulus
-// length, and |key_type| to type RSA and returns true.
-// If |spki| is any other key type, returns false and does not update any
-// of the output parameters.
-// All pointer arguments must not be null.
-bool GetPublicKeyBySpki(const std::string& spki,
-                        net::X509Certificate::PublicKeyType* key_type,
-                        size_t* key_size_bits);
-
-struct ClientCertificateRequest {
-  ClientCertificateRequest();
-  ClientCertificateRequest(const ClientCertificateRequest& other);
-  ~ClientCertificateRequest();
-
-  // The list of the types of certificates requested, sorted in order of the
-  // server's preference.
-  std::vector<net::X509Certificate::PublicKeyType> certificate_key_types;
-
-  // List of distinguished names of certificate authorities allowed by the
-  // server. Each entry must be a DER-encoded X.509 DistinguishedName.
-  std::vector<std::string> certificate_authorities;
-};
-
 using GenerateKeyCallback =
     base::Callback<void(const std::string& public_key_spki_der, Status status)>;
 
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service_browsertest.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service_browsertest.cc
index f5f0c77..fb7b28d2 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service_browsertest.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service_browsertest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
 #include "chrome/browser/chromeos/login/test/scoped_policy_update.h"
 #include "chrome/browser/chromeos/login/test/user_policy_mixin.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service_nss.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service_nss.cc
index 1aa472e..69efabd4 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service_nss.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service_nss.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chromeos/net/client_cert_store_chromeos.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "content/public/browser/browser_context.h"
diff --git a/chrome/browser/component_updater/zxcvbn_data_component_installer.cc b/chrome/browser/component_updater/zxcvbn_data_component_installer.cc
index b1ac18c..d757a50 100644
--- a/chrome/browser/component_updater/zxcvbn_data_component_installer.cc
+++ b/chrome/browser/component_updater/zxcvbn_data_component_installer.cc
@@ -19,6 +19,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/strings/string_split.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/util/ranges/algorithm.h"
@@ -30,6 +31,7 @@
 #include "components/update_client/update_client_errors.h"
 #include "components/update_client/utils.h"
 #include "third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp"
+#include "third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists_common.hpp"
 
 namespace component_updater {
 
@@ -69,33 +71,27 @@
      ZxcvbnDataComponentInstallerPolicy::kUsTvAndFilmTxtFileName},
 }};
 
-using DictionaryMap = base::flat_map<zxcvbn::DictionaryTag, std::string>;
-DictionaryMap ReadDictionaries(const base::FilePath& install_dir) {
-  DictionaryMap result;
-  result.reserve(kTagAndFileNamePairs.size());
+using RankedDictionaries =
+    std::unordered_map<zxcvbn::DictionaryTag, zxcvbn::RankedDict>;
+RankedDictionaries ParseRankedDictionaries(const base::FilePath& install_dir) {
+  RankedDictionaries result;
   for (const auto& pair : kTagAndFileNamePairs) {
     base::FilePath dictionary_path = install_dir.Append(pair.file_name);
     DVLOG(1) << "Reading Dictionary from file: " << dictionary_path;
 
     std::string dictionary;
-    if (base::ReadFileToString(dictionary_path, &dictionary))
-      result.try_emplace(result.end(), pair.tag, std::move(dictionary));
-    else
+    if (base::ReadFileToString(dictionary_path, &dictionary)) {
+      result.emplace(pair.tag, zxcvbn::build_ranked_dict(base::SplitStringPiece(
+                                   dictionary, "\r\n", base::TRIM_WHITESPACE,
+                                   base::SPLIT_WANT_NONEMPTY)));
+    } else {
       VLOG(1) << "Failed reading from " << dictionary_path;
+    }
   }
 
   return result;
 }
 
-void ParseDictionaries(const DictionaryMap& dictionaries) {
-  for (const auto& pair : dictionaries) {
-    if (!zxcvbn::ParseRankedDictionary(pair.first, pair.second)) {
-      VLOG(1) << "Failed to parse dictionary for tag "
-              << static_cast<int>(pair.first) << ". Contents: " << pair.second;
-    }
-  }
-}
-
 // The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
 // The extension id is: ojhpjlocmbogdgmfpkhlaaeamibhnphh
 constexpr std::array<uint8_t, 32> kZxcvbnDataPublicKeySha256 = {
@@ -141,8 +137,8 @@
 
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-      base::BindOnce(&ReadDictionaries, install_dir),
-      base::BindOnce(&ParseDictionaries));
+      base::BindOnce(&ParseRankedDictionaries, install_dir),
+      base::BindOnce(&zxcvbn::SetRankedDicts));
 }
 
 base::FilePath ZxcvbnDataComponentInstallerPolicy::GetRelativeInstallDir()
diff --git a/chrome/browser/component_updater/zxcvbn_data_component_installer_unittest.cc b/chrome/browser/component_updater/zxcvbn_data_component_installer_unittest.cc
index 781b1a7..2ac9781 100644
--- a/chrome/browser/component_updater/zxcvbn_data_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/zxcvbn_data_component_installer_unittest.cc
@@ -17,8 +17,8 @@
 
 namespace {
 
-using ::testing::ElementsAre;
 using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
 
 }  // namespace
 
@@ -103,15 +103,15 @@
   base::WriteFile(
       GetPath().Append(
           ZxcvbnDataComponentInstallerPolicy::kEnglishWikipediaTxtFileName),
-      "english_wikipedia");
+      "english\nwikipedia");
   base::WriteFile(
       GetPath().Append(
           ZxcvbnDataComponentInstallerPolicy::kFemaleNamesTxtFileName),
-      "female_names");
+      "female\nnames");
   base::WriteFile(
       GetPath().Append(
           ZxcvbnDataComponentInstallerPolicy::kMaleNamesTxtFileName),
-      "male_names");
+      "male\nnames");
   base::WriteFile(
       GetPath().Append(
           ZxcvbnDataComponentInstallerPolicy::kPasswordsTxtFileName),
@@ -122,26 +122,28 @@
   base::WriteFile(
       GetPath().Append(
           ZxcvbnDataComponentInstallerPolicy::kUsTvAndFilmTxtFileName),
-      "us_tv_and_film");
+      "us\ntv\nand\nfilm");
 
   policy().ComponentReady(version(), GetPath(), nullptr);
   task_env().RunUntilIdle();
 
   zxcvbn::RankedDicts ranked_dicts = zxcvbn::default_ranked_dicts();
-  EXPECT_THAT(zxcvbn::default_ranked_dicts(),
-              ::testing::UnorderedElementsAre(
-                  Pair(zxcvbn::DictionaryTag::ENGLISH_WIKIPEDIA,
-                       ElementsAre(Pair("english_wikipedia", 1))),
-                  Pair(zxcvbn::DictionaryTag::FEMALE_NAMES,
-                       ElementsAre(Pair("female_names", 1))),
-                  Pair(zxcvbn::DictionaryTag::MALE_NAMES,
-                       ElementsAre(Pair("male_names", 1))),
-                  Pair(zxcvbn::DictionaryTag::PASSWORDS,
-                       ElementsAre(Pair("passwords", 1))),
-                  Pair(zxcvbn::DictionaryTag::SURNAMES,
-                       ElementsAre(Pair("surnames", 1))),
-                  Pair(zxcvbn::DictionaryTag::US_TV_AND_FILM,
-                       ElementsAre(Pair("us_tv_and_film", 1)))));
+  EXPECT_THAT(
+      zxcvbn::default_ranked_dicts(),
+      UnorderedElementsAre(
+          Pair(zxcvbn::DictionaryTag::ENGLISH_WIKIPEDIA,
+               UnorderedElementsAre(Pair("english", 1), Pair("wikipedia", 2))),
+          Pair(zxcvbn::DictionaryTag::FEMALE_NAMES,
+               UnorderedElementsAre(Pair("female", 1), Pair("names", 2))),
+          Pair(zxcvbn::DictionaryTag::MALE_NAMES,
+               UnorderedElementsAre(Pair("male", 1), Pair("names", 2))),
+          Pair(zxcvbn::DictionaryTag::PASSWORDS,
+               UnorderedElementsAre(Pair("passwords", 1))),
+          Pair(zxcvbn::DictionaryTag::SURNAMES,
+               UnorderedElementsAre(Pair("surnames", 1))),
+          Pair(zxcvbn::DictionaryTag::US_TV_AND_FILM,
+               UnorderedElementsAre(Pair("us", 1), Pair("tv", 2),
+                                    Pair("and", 3), Pair("film", 4)))));
 }
 
 }  // namespace component_updater
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index ddd8f65..20f850f2 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -1523,11 +1523,13 @@
   return agent_host_.get() == agent_host;
 }
 
-void DevToolsUIBindings::CallClientMethod(const std::string& object_name,
-                                          const std::string& method_name,
-                                          const base::Value& arg1,
-                                          const base::Value& arg2,
-                                          const base::Value& arg3) {
+void DevToolsUIBindings::CallClientMethod(
+    const std::string& object_name,
+    const std::string& method_name,
+    const base::Value& arg1,
+    const base::Value& arg2,
+    const base::Value& arg3,
+    base::OnceCallback<void(base::Value)> completion_callback) {
   // If we're not exposing bindings, we shouldn't call functions either.
   if (!frontend_host_)
     return;
@@ -1547,7 +1549,7 @@
   }
   javascript.append(");");
   web_contents_->GetMainFrame()->ExecuteJavaScript(
-      base::UTF8ToUTF16(javascript), base::NullCallback());
+      base::UTF8ToUTF16(javascript), std::move(completion_callback));
 }
 
 void DevToolsUIBindings::ReadyToCommitNavigation(
diff --git a/chrome/browser/devtools/devtools_ui_bindings.h b/chrome/browser/devtools/devtools_ui_bindings.h
index b3e3ad1d..d7d4361 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.h
+++ b/chrome/browser/devtools/devtools_ui_bindings.h
@@ -80,11 +80,14 @@
 
   // Takes ownership over the |delegate|.
   void SetDelegate(Delegate* delegate);
-  void CallClientMethod(const std::string& object_name,
-                        const std::string& method_name,
-                        const base::Value& arg1 = {},
-                        const base::Value& arg2 = {},
-                        const base::Value& arg3 = {});
+  void CallClientMethod(
+      const std::string& object_name,
+      const std::string& method_name,
+      const base::Value& arg1 = {},
+      const base::Value& arg2 = {},
+      const base::Value& arg3 = {},
+      base::OnceCallback<void(base::Value)> completion_callback =
+          base::OnceCallback<void(base::Value)>());
   void AttachTo(const scoped_refptr<content::DevToolsAgentHost>& agent_host);
   void Detach();
   bool IsAttachedTo(content::DevToolsAgentHost* agent_host);
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 3c29640d..7ebe9562 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -406,6 +406,10 @@
   if (throttle_)
     throttle_->ResumeThrottle();
 
+  if (reattach_complete_callback_) {
+    std::move(reattach_complete_callback_).Run();
+  }
+
   life_stage_ = kClosing;
 
   UpdateBrowserWindow();
@@ -767,12 +771,19 @@
 }
 
 void DevToolsWindow::UpdateInspectedWebContents(
-    content::WebContents* new_web_contents) {
+    content::WebContents* new_web_contents,
+    base::OnceCallback<void()> callback) {
+  DCHECK(!reattach_complete_callback_);
+  reattach_complete_callback_ = std::move(callback);
+
   inspected_contents_observer_ =
       std::make_unique<ObserverWithAccessor>(new_web_contents);
   bindings_->AttachTo(
       content::DevToolsAgentHost::GetOrCreateFor(new_web_contents));
-  bindings_->CallClientMethod("DevToolsAPI", "reattachMainTarget");
+  bindings_->CallClientMethod(
+      "DevToolsAPI", "reattachMainTarget", {}, {}, {},
+      base::BindOnce(&DevToolsWindow::OnReattachMainTargetComplete,
+                     base::Unretained(this)));
 }
 
 void DevToolsWindow::ScheduleShow(const DevToolsToggleAction& action) {
@@ -1640,3 +1651,7 @@
   web_modal::WebContentsModalDialogManager::FromWebContents(main_web_contents_)
       ->SetDelegate(browser);
 }
+
+void DevToolsWindow::OnReattachMainTargetComplete(base::Value) {
+  std::move(reattach_complete_callback_).Run();
+}
diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h
index e2c979c..18fa5e9 100644
--- a/chrome/browser/devtools/devtools_window.h
+++ b/chrome/browser/devtools/devtools_window.h
@@ -128,7 +128,8 @@
   // Updates the WebContents inspected by the DevToolsWindow by reattaching
   // the binding to |new_web_contents|. Called when swapping an outer
   // WebContents with its inner WebContents.
-  void UpdateInspectedWebContents(content::WebContents* new_web_contents);
+  void UpdateInspectedWebContents(content::WebContents* new_web_contents,
+                                  base::OnceCallback<void()> callback);
 
   // Sets closure to be called after load is done. If already loaded, calls
   // closure immediately.
@@ -381,6 +382,8 @@
   // display web modal dialogs triggered by it.
   void RegisterModalDialogManager(Browser* browser);
 
+  void OnReattachMainTargetComplete(base::Value);
+
   std::unique_ptr<ObserverWithAccessor> inspected_contents_observer_;
 
   FrontendType frontend_type_;
@@ -426,6 +429,8 @@
   Throttle* throttle_ = nullptr;
   bool open_new_window_for_popups_ = false;
 
+  base::OnceCallback<void()> reattach_complete_callback_;
+
   friend class DevToolsEventForwarder;
   DISALLOW_COPY_AND_ASSIGN(DevToolsWindow);
 };
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.cc
index df8bfc03..42e7751 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.cc
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.cc
@@ -11,6 +11,7 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/extensions/api/platform_keys/platform_keys_api.h"
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.h b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.h
index 782e09ea..c66964f2 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.h
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_ash.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
-#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h"
 #include "extensions/browser/extension_function.h"
 
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
index 4c2c914..473e04a 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
@@ -16,7 +16,7 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h"
-#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/extensions/api/platform_keys/verify_trust_api.h"
 #include "chrome/common/extensions/api/platform_keys_internal.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_api.h b/chrome/browser/extensions/api/platform_keys/platform_keys_api.h
index b51daf6..f629cae7 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_api.h
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_api.h
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "base/optional.h"
-#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "extensions/browser/extension_function.h"
 
 namespace net {
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
index 3c46570..384c4ad 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
@@ -14,6 +14,7 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h"
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h"
 #include "chrome/browser/net/nss_context.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
@@ -174,8 +175,7 @@
     std::string client_cert1_spki =
         chromeos::platform_keys::GetSubjectPublicKeyInfo(client_cert1_);
     permissions_for_ext->RegisterKeyForCorporateUsage(
-        client_cert1_spki,
-        {chromeos::platform_keys::KeyPermissions::KeyLocation::kUserSlot});
+        client_cert1_spki, {chromeos::platform_keys::TokenId::kUser});
     done_callback.Run();
   }
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d3eac89e..3494c6884 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1875,12 +1875,6 @@
     "to true, pointer movements wil not be affected by the underlying platform "
     "modications such as mouse accelaration.";
 
-const char kPolicyAtomicGroupsEnabledName[] = "Policy Atomic Groups Enabled";
-const char kPolicyAtomicGroupsEnabledDescription[] =
-    "Enables the concept of policy atomic groups that makes policies of an "
-    "atomic group that do not share the highest priority source from that group"
-    "ignored.";
-
 const char kPreviewsAllowedName[] = "Previews Allowed";
 const char kPreviewsAllowedDescription[] =
     "Allows previews to be shown subject to specific preview types being "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index dafd34d..465f449 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1083,9 +1083,6 @@
 extern const char kPointerLockOptionsName[];
 extern const char kPointerLockOptionsDescription[];
 
-extern const char kPolicyAtomicGroupsEnabledName[];
-extern const char kPolicyAtomicGroupsEnabledDescription[];
-
 extern const char kPreviewsAllowedName[];
 extern const char kPreviewsAllowedDescription[];
 
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc
index 8a229986..4f0fc2d 100644
--- a/chrome/browser/metrics/ukm_browsertest.cc
+++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -616,20 +616,12 @@
 }
 #endif  // !defined(OS_ANDROID)
 
-// https://crbug.com/1102747
-#if defined(OS_ANDROID)
-#define MAYBE_AddSyncedUserBirthYearAndGenderToProtoData \
-  DISABLED_AddSyncedUserBirthYearAndGenderToProtoData
-#else
-#define MAYBE_AddSyncedUserBirthYearAndGenderToProtoData \
-  AddSyncedUserBirthYearAndGenderToProtoData
-#endif
 // TODO(crbug/1016118): Add the remaining test cases.
 // Keep this test in sync with testUKMDemographicsReportingWithFeatureEnabled
 // and testUKMDemographicsReportingWithFeatureDisabled in
 // ios/chrome/browser/metrics/demographics_egtest.mm.
 IN_PROC_BROWSER_TEST_P(UkmBrowserTestWithDemographics,
-                       MAYBE_AddSyncedUserBirthYearAndGenderToProtoData) {
+                       AddSyncedUserBirthYearAndGenderToProtoData) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   test::DemographicsTestParams param = GetParam();
   MetricsConsentOverride metrics_consent(true);
@@ -679,7 +671,12 @@
     histogram.ExpectTotalCount("UKM.UserDemographics.Status", /*count=*/0);
   }
 
-  harness->service()->GetUserSettings()->SetSyncRequested(false);
+#if !defined(OS_CHROMEOS)
+  // Sign out the user to revoke all refresh tokens. This prevents any posted
+  // tasks from successfully fetching an access token during the tear-down
+  // phase and crashing on a DCHECK. See crbug/1102746 for more details.
+  harness->SignOutPrimaryAccount();
+#endif  // !defined(OS_CHROMEOS)
   ClosePlatformBrowser(browser);
 }
 
@@ -975,11 +972,13 @@
 
 // ChromeOS doesn't have the concept of sign-out so this test doesn't make sense
 // there.
+//
 // Flaky on Android: https://crbug.com/1096047.
-#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+//
 // Make sure that UKM is disabled when the profile signs out of Sync.
 // Keep in sync with testSingleSyncSignout in ios/chrome/browser/metrics/
 // ukm_egtest.mm.
+#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, SingleSyncSignoutCheck) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
diff --git a/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc b/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
index ae34a44..14fafd1c 100644
--- a/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
+++ b/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
@@ -336,6 +336,12 @@
       /*provider_id=*/std::string());
 }
 
+bool ChromeNativeFileSystemPermissionContext::CanObtainReadPermission(
+    const url::Origin& origin) {
+  return GetReadGuardContentSetting(origin) == CONTENT_SETTING_ASK ||
+         GetReadGuardContentSetting(origin) == CONTENT_SETTING_ALLOW;
+}
+
 bool ChromeNativeFileSystemPermissionContext::CanObtainWritePermission(
     const url::Origin& origin) {
   return GetWriteGuardContentSetting(origin) == CONTENT_SETTING_ASK ||
diff --git a/chrome/browser/native_file_system/chrome_native_file_system_permission_context.h b/chrome/browser/native_file_system/chrome_native_file_system_permission_context.h
index ac9eb52..49d1fa6 100644
--- a/chrome/browser/native_file_system/chrome_native_file_system_permission_context.h
+++ b/chrome/browser/native_file_system/chrome_native_file_system_permission_context.h
@@ -56,6 +56,7 @@
       std::unique_ptr<content::NativeFileSystemWriteItem> item,
       content::GlobalFrameRoutingId frame_id,
       base::OnceCallback<void(AfterWriteCheckResult)> callback) override;
+  bool CanObtainReadPermission(const url::Origin& origin) override;
   bool CanObtainWritePermission(const url::Origin& origin) override;
 
   ContentSetting GetReadGuardContentSetting(const url::Origin& origin);
diff --git a/chrome/browser/native_file_system/origin_scoped_native_file_system_permission_context.cc b/chrome/browser/native_file_system/origin_scoped_native_file_system_permission_context.cc
index 3bc2546e..7f5f68f 100644
--- a/chrome/browser/native_file_system/origin_scoped_native_file_system_permission_context.cc
+++ b/chrome/browser/native_file_system/origin_scoped_native_file_system_permission_context.cc
@@ -331,12 +331,20 @@
       existing_grant->SetStatus(PermissionStatus::GRANTED);
       break;
     case CONTENT_SETTING_ASK:
-      // Files automatically get read access when picked by the user,
-      // directories need to first be confirmed.
-      if (user_action != UserAction::kLoadFromStorage &&
-          handle_type == HandleType::kFile) {
-        existing_grant->SetStatus(PermissionStatus::GRANTED);
-        ScheduleUsageIconUpdate();
+      switch (user_action) {
+        case UserAction::kOpen:
+        case UserAction::kSave:
+          // Open and Save dialog only grant read access for individual files.
+          if (handle_type == HandleType::kDirectory)
+            break;
+          FALLTHROUGH;
+        case UserAction::kDragAndDrop:
+          // Drag&drop grants read access for all handles.
+          existing_grant->SetStatus(PermissionStatus::GRANTED);
+          ScheduleUsageIconUpdate();
+          break;
+        case UserAction::kLoadFromStorage:
+          break;
       }
       break;
     case CONTENT_SETTING_BLOCK:
@@ -391,9 +399,16 @@
       existing_grant->SetStatus(PermissionStatus::GRANTED);
       break;
     case CONTENT_SETTING_ASK:
-      if (user_action == UserAction::kSave) {
-        existing_grant->SetStatus(PermissionStatus::GRANTED);
-        ScheduleUsageIconUpdate();
+      switch (user_action) {
+        case UserAction::kSave:
+          // Only automatically grant write access for save dialogs.
+          existing_grant->SetStatus(PermissionStatus::GRANTED);
+          ScheduleUsageIconUpdate();
+          break;
+        case UserAction::kOpen:
+        case UserAction::kDragAndDrop:
+        case UserAction::kLoadFromStorage:
+          break;
       }
       break;
     case CONTENT_SETTING_BLOCK:
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
index 309a958..e8f9a923 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -567,11 +567,12 @@
 
   // Once get the advertisement, the first thing to do is decrypt the
   // certificate.
-  std::vector<uint8_t> encrypted_metadata = advertisement->encrypted_metadata;
+  std::vector<uint8_t> encrypted_metadata_key =
+      advertisement->encrypted_metadata_key;
   std::vector<uint8_t> salt = advertisement->salt;
 
   GetCertificateManager()->GetDecryptedPublicCertificate(
-      std::move(encrypted_metadata), std::move(salt),
+      std::move(encrypted_metadata_key), std::move(salt),
       base::BindOnce(&NearbySharingServiceImpl::OnOutgoingDecryptedCertificate,
                      weak_ptr_factory_.GetWeakPtr(), endpoint_id,
                      std::move(advertisement)));
@@ -1334,11 +1335,12 @@
     return;
   }
 
-  std::vector<uint8_t> encrypted_metadata = advertisement->encrypted_metadata;
+  std::vector<uint8_t> encrypted_metadata_key =
+      advertisement->encrypted_metadata_key;
   std::vector<uint8_t> salt = advertisement->salt;
 
   GetCertificateManager()->GetDecryptedPublicCertificate(
-      std::move(encrypted_metadata), std::move(salt),
+      std::move(encrypted_metadata_key), std::move(salt),
       base::BindOnce(&NearbySharingServiceImpl::OnIncomingDecryptedCertificate,
                      weak_ptr_factory_.GetWeakPtr(), endpoint_id, connection,
                      std::move(advertisement)));
diff --git a/chrome/browser/password_check/android/internal/BUILD.gn b/chrome/browser/password_check/android/internal/BUILD.gn
index 82b53e7..f1e93d7 100644
--- a/chrome/browser/password_check/android/internal/BUILD.gn
+++ b/chrome/browser/password_check/android/internal/BUILD.gn
@@ -67,6 +67,8 @@
     "//components/embedder_support/android:util_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
+    "//third_party/android_deps:androidx_core_core_java",
     "//third_party/android_deps:androidx_fragment_fragment_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
     "//third_party/android_deps:androidx_preference_preference_java",
diff --git a/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_item.xml b/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_item.xml
index f141ef69..29b06579 100644
--- a/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_item.xml
+++ b/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_item.xml
@@ -43,9 +43,13 @@
 
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/credential_change_button"
+            android:drawablePadding="@dimen/compromised_credential_row_button_icon_padding"
+            android:drawableStart="@drawable/permission_popups"
             android:layout_marginTop="@dimen/compromised_credential_row_button_margin_top"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
+            android:paddingStart="@dimen/compromised_credential_row_button_icon_start_padding"
+            android:paddingEnd="@dimen/compromised_credential_row_button_icon_end_padding"
             android:text="@string/password_check_credential_row_change_button_caption"
             style="@style/FilledButton.Flat" />
 
diff --git a/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_with_script_item.xml b/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_with_script_item.xml
index 96cd250..6c633f2c7 100644
--- a/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_with_script_item.xml
+++ b/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_with_script_item.xml
@@ -46,8 +46,10 @@
             android:layout_marginTop="@dimen/compromised_credential_row_button_margin_top"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
+            android:paddingStart="@dimen/compromised_credential_row_button_icon_start_padding"
+            android:paddingEnd="@dimen/compromised_credential_row_button_icon_end_padding"
             android:text="@string/password_check_credential_row_change_button_caption"
-            android:drawablePadding="3dp"
+            android:drawablePadding="@dimen/compromised_credential_row_button_icon_padding"
             android:drawableStart="@drawable/ic_autofill_assistant_white_24dp"
             style="@style/FilledButton.Flat" />
 
@@ -64,6 +66,8 @@
             android:layout_marginTop="@dimen/compromised_credential_row_button_margin_top"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
+            android:paddingStart="0dp"
+            android:paddingEnd="0dp"
             android:text="@string/password_check_credential_row_change_manually_button_caption"
             style="@style/TextButton"/>
 
diff --git a/chrome/browser/password_check/android/internal/java/res/values/dimens.xml b/chrome/browser/password_check/android/internal/java/res/values/dimens.xml
index f254933d..da937db 100644
--- a/chrome/browser/password_check/android/internal/java/res/values/dimens.xml
+++ b/chrome/browser/password_check/android/internal/java/res/values/dimens.xml
@@ -18,6 +18,9 @@
   <dimen name="check_status_message_idle_margin_vertical">27dp</dimen>
   <dimen name="check_status_message_running_margin_vertical">36dp</dimen>
 
+  <dimen name="compromised_credential_row_button_icon_padding">8dp</dimen>
+  <dimen name="compromised_credential_row_button_icon_start_padding">12dp</dimen>
+  <dimen name="compromised_credential_row_button_icon_end_padding">16dp</dimen>
   <dimen name="compromised_credential_row_button_margin_top">16dp</dimen>
   <dimen name="compromised_credential_row_more_padding_end">8dp</dimen>
   <dimen name="compromised_credential_row_more_padding_start">16dp</dimen>
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckViewBinder.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckViewBinder.java
index facc2be..e868167 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckViewBinder.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckViewBinder.java
@@ -19,6 +19,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
@@ -27,7 +28,10 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.ColorRes;
 import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.content.res.AppCompatResources;
+import androidx.core.graphics.drawable.DrawableCompat;
 
 import org.chromium.chrome.browser.password_check.PasswordCheckProperties.ItemType;
 import org.chromium.chrome.browser.password_check.internal.R;
@@ -144,6 +148,8 @@
             button.setOnClickListener(unusedView -> {
                 model.get(CREDENTIAL_HANDLER).onChangePasswordButtonClick(credential);
             });
+            setTintListForCompoundDrawables(button.getCompoundDrawablesRelative(),
+                    view.getContext(), org.chromium.ui.R.color.default_text_color_inverse);
             if (credential.hasScript()) {
                 ButtonCompat button_with_script =
                         view.findViewById(R.id.credential_change_button_with_script);
@@ -468,4 +474,13 @@
     private static int getDimensionPixelOffset(View view, int resourceId) {
         return view.getContext().getResources().getDimensionPixelOffset(resourceId);
     }
+
+    private static void setTintListForCompoundDrawables(
+            Drawable[] compoundDrawables, Context context, @ColorRes int tintColorList) {
+        for (Drawable drawable : compoundDrawables) {
+            if (drawable == null) continue;
+            DrawableCompat.setTintList(
+                    drawable, AppCompatResources.getColorStateList(context, tintColorList));
+        }
+    }
 }
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 827f118..b275fd38 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -36,13 +36,18 @@
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/autofill/content/browser/content_autofill_driver.h"
+#include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_switches.h"
+#include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/password_form.h"
+#include "components/autofill/core/common/renderer_id.h"
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
 #include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
+#include "components/password_manager/core/browser/form_parsing/password_field_prediction.h"
 #include "components/password_manager/core/browser/http_auth_manager.h"
 #include "components/password_manager/core/browser/http_auth_observer.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
@@ -77,6 +82,8 @@
 using base::Feature;
 using testing::_;
 using testing::ElementsAre;
+using FieldPrediction =
+    autofill::AutofillQueryResponseContents::Field::FieldPrediction;
 
 namespace password_manager {
 namespace {
@@ -1168,6 +1175,169 @@
   // The only thing we check here is that there is no use-after-free reported.
 }
 
+// Get form data for /password/nonplaceholder_username.html
+autofill::FormData GetPlaceholderUsernameFormData(
+    autofill::PasswordForm signin_form) {
+  // Build server predictions
+  autofill::FormData form_data;
+  constexpr autofill::FormRendererId form_id(0);
+  form_data.unique_renderer_id = form_id;
+  form_data.name_attribute = ASCIIToUTF16("testform");
+  form_data.name = form_data.name_attribute;
+  form_data.action = GURL(signin_form.action.spec() + "password/done.html");
+  form_data.url = signin_form.url;
+  // Username
+  autofill::FormFieldData username_field;
+  username_field.form_control_type = "text";
+  username_field.id_attribute = ASCIIToUTF16("username_field");
+  username_field.name = username_field.id_attribute;
+  username_field.value = ASCIIToUTF16("example@example.com");
+  username_field.label = username_field.value;
+  username_field.unique_renderer_id = autofill::FieldRendererId(0);
+  form_data.fields.push_back(username_field);
+  // Password
+  autofill::FormFieldData password_field;
+  password_field.form_control_type = "password";
+  password_field.id_attribute = ASCIIToUTF16("password_field");
+  password_field.name = password_field.id_attribute;
+  password_field.value = ASCIIToUTF16("htmlPass");
+  password_field.label = password_field.value;
+  password_field.unique_renderer_id = autofill::FieldRendererId(1);
+  form_data.fields.push_back(password_field);
+
+  return form_data;
+}
+
+// If there is a username and password with prefilled values, do not overwrite
+// the password if the username does not look like a placeholder value
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
+                       NonPlaceholderPasswordNotOverwritten) {
+  // Save a credential to the password store.
+  scoped_refptr<password_manager::TestPasswordStore> password_store =
+      static_cast<password_manager::TestPasswordStore*>(
+          PasswordStoreFactory::GetForProfile(
+              browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
+              .get());
+  autofill::PasswordForm signin_form;
+  signin_form.signon_realm = embedded_test_server()->base_url().spec();
+  signin_form.url = embedded_test_server()->base_url();
+  signin_form.action = embedded_test_server()->base_url();
+  signin_form.username_value = base::ASCIIToUTF16("example@example.com");
+  signin_form.password_value = base::ASCIIToUTF16("savedPass");
+  password_store->AddLogin(signin_form);
+
+  password_manager::PasswordFormManager::
+      set_wait_for_server_predictions_for_filling(true);
+
+  // Get form data
+  autofill::FormData form_data = GetPlaceholderUsernameFormData(signin_form);
+
+  // Username
+  bool is_placeholder = false;
+  autofill::FormStructure form_structure(form_data);
+  form_structure.field(0)->set_server_type(autofill::USERNAME);
+  std::vector<FieldPrediction> username_predictions;
+  FieldPrediction username_prediction;
+  username_prediction.set_type(autofill::USERNAME);
+  username_prediction.set_may_use_prefilled_placeholder(is_placeholder);
+  username_predictions.push_back(username_prediction);
+  form_structure.field(0)->set_server_predictions(username_predictions);
+
+  // Password
+  form_structure.field(1)->set_server_type(autofill::PASSWORD);
+  std::vector<FieldPrediction> password_predictions;
+  FieldPrediction password_prediction;
+  password_prediction.set_type(autofill::PASSWORD);
+  password_predictions.push_back(password_prediction);
+  form_structure.field(1)->set_server_predictions(password_predictions);
+
+  // Navigate to the page
+  NavigateToFile("/password/nonplaceholder_username.html");
+
+  // Use autofill predictions
+  autofill::ChromeAutofillClient* autofill_client =
+      autofill::ChromeAutofillClient::FromWebContents(WebContents());
+  autofill_client->PropagateAutofillPredictions(WebContents()->GetMainFrame(),
+                                                {&form_structure});
+
+  // Check original values before interaction
+  CheckElementValue("username_field", "example@example.com");
+  CheckElementValue("password_field", "htmlPass");
+
+  // Have user interact with the page
+  content::SimulateMouseClickAt(
+      WebContents(), 0, blink::WebMouseEvent::Button::kLeft, gfx::Point(1, 1));
+
+  // Now make sure the fields aren't overwritten
+  CheckElementValue("username_field", "example@example.com");
+  CheckElementValue("password_field", "htmlPass");
+}
+
+// If there is a username and password with prefilled values, overwrite the
+// password if the username looks like a placeholder value
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
+                       PlaceholderPasswordOverwritten) {
+  // Save a credential to the password store.
+  scoped_refptr<password_manager::TestPasswordStore> password_store =
+      static_cast<password_manager::TestPasswordStore*>(
+          PasswordStoreFactory::GetForProfile(
+              browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
+              .get());
+  autofill::PasswordForm signin_form;
+  signin_form.signon_realm = embedded_test_server()->base_url().spec();
+  signin_form.url = embedded_test_server()->base_url();
+  signin_form.action = embedded_test_server()->base_url();
+  signin_form.username_value = base::ASCIIToUTF16("example@example.com");
+  signin_form.password_value = base::ASCIIToUTF16("savedPass");
+  password_store->AddLogin(signin_form);
+
+  password_manager::PasswordFormManager::
+      set_wait_for_server_predictions_for_filling(true);
+
+  // Get form data
+  autofill::FormData form_data = GetPlaceholderUsernameFormData(signin_form);
+
+  // Username
+  bool is_placeholder = true;
+  autofill::FormStructure form_structure(form_data);
+  form_structure.field(0)->set_server_type(autofill::USERNAME);
+  std::vector<FieldPrediction> username_predictions;
+  FieldPrediction username_prediction;
+  username_prediction.set_type(autofill::USERNAME);
+  username_prediction.set_may_use_prefilled_placeholder(is_placeholder);
+  username_predictions.push_back(username_prediction);
+  form_structure.field(0)->set_server_predictions(username_predictions);
+
+  // Password
+  form_structure.field(1)->set_server_type(autofill::PASSWORD);
+  std::vector<FieldPrediction> password_predictions;
+  FieldPrediction password_prediction;
+  password_prediction.set_type(autofill::PASSWORD);
+  password_predictions.push_back(password_prediction);
+  form_structure.field(1)->set_server_predictions(password_predictions);
+
+  // Navigate to the page
+  NavigateToFile("/password/nonplaceholder_username.html");
+
+  // Use autofill predictions
+  autofill::ChromeAutofillClient* autofill_client =
+      autofill::ChromeAutofillClient::FromWebContents(WebContents());
+  autofill_client->PropagateAutofillPredictions(WebContents()->GetMainFrame(),
+                                                {&form_structure});
+
+  // Check original values before interaction
+  CheckElementValue("username_field", "example@example.com");
+  CheckElementValue("password_field", "htmlPass");
+
+  // Have user interact with the page
+  content::SimulateMouseClickAt(
+      WebContents(), 0, blink::WebMouseEvent::Button::kLeft, gfx::Point(1, 1));
+
+  // Now make sure the fields are overwritten
+  CheckElementValue("username_field", "example@example.com");
+  CheckElementValue("password_field", "savedPass");
+}
+
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
                        UsernameAndPasswordValueAccessible) {
   // At first let us save a credential to the password store.
diff --git a/chrome/browser/payments/ssl_validity_checker.cc b/chrome/browser/payments/ssl_validity_checker.cc
index fd4e82e..a8e21d7 100644
--- a/chrome/browser/payments/ssl_validity_checker.cc
+++ b/chrome/browser/payments/ssl_validity_checker.cc
@@ -15,6 +15,21 @@
 #include "url/gurl.h"
 
 namespace payments {
+namespace {
+
+// Returns the security level of |web_contents|. The |web_contents| parameter
+// should not be null.
+security_state::SecurityLevel GetSecurityLevel(
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+  SecurityStateTabHelper::CreateForWebContents(web_contents);
+  SecurityStateTabHelper* helper =
+      SecurityStateTabHelper::FromWebContents(web_contents);
+  DCHECK(helper);
+  return helper->GetSecurityLevel();
+}
+
+}  // namespace
 
 // static std::string
 std::string SslValidityChecker::GetInvalidSslCertificateErrorMessage(
@@ -82,17 +97,4 @@
   return true;
 }
 
-// static
-// Returns the security level of |web_contents|. The |web_contents|
-// parameter should not be null.
-security_state::SecurityLevel SslValidityChecker::GetSecurityLevel(
-    content::WebContents* web_contents) {
-  DCHECK(web_contents);
-  SecurityStateTabHelper::CreateForWebContents(web_contents);
-  SecurityStateTabHelper* helper =
-      SecurityStateTabHelper::FromWebContents(web_contents);
-  DCHECK(helper);
-  return helper->GetSecurityLevel();
-}
-
 }  // namespace payments
diff --git a/chrome/browser/payments/ssl_validity_checker.h b/chrome/browser/payments/ssl_validity_checker.h
index 9e38d1f..7cd70d5a 100644
--- a/chrome/browser/payments/ssl_validity_checker.h
+++ b/chrome/browser/payments/ssl_validity_checker.h
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "base/macros.h"
-#include "components/security_state/core/security_state.h"
 
 namespace content {
 class WebContents;
@@ -34,9 +33,6 @@
   static bool IsValidPageInPaymentHandlerWindow(
       content::WebContents* web_contents);
 
-  static security_state::SecurityLevel GetSecurityLevel(
-      content::WebContents* web_contents);
-
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(SslValidityChecker);
 };
diff --git a/chrome/browser/portal/portal_browsertest.cc b/chrome/browser/portal/portal_browsertest.cc
index 25cba5f..038d741 100644
--- a/chrome/browser/portal/portal_browsertest.cc
+++ b/chrome/browser/portal/portal_browsertest.cc
@@ -110,6 +110,26 @@
                 browser()->tab_strip_model()->GetActiveWebContents(), nullptr));
 }
 
+IN_PROC_BROWSER_TEST_F(
+    PortalBrowserTest,
+    DevToolsWindowIsAttachedToOriginalWebContentsWhenActivationFails) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url(embedded_test_server()->GetURL("/portal/portal-no-src.html"));
+  ui_test_utils::NavigateToURL(browser(), url);
+  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
+  DevToolsWindow* dev_tools_window =
+      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true);
+  WebContents* main_web_contents =
+      DevToolsWindowTesting::Get(dev_tools_window)->main_web_contents();
+  EXPECT_EQ(main_web_contents,
+            DevToolsWindow::GetInTabWebContents(contents, nullptr));
+
+  EXPECT_EQ(true, content::EvalJs(contents, "activate()"));
+  EXPECT_EQ(main_web_contents,
+            DevToolsWindow::GetInTabWebContents(
+                browser()->tab_strip_model()->GetActiveWebContents(), nullptr));
+}
+
 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, HttpBasicAuthenticationInPortal) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/title1.html"));
diff --git a/chrome/browser/profiles/profile_shortcut_manager.h b/chrome/browser/profiles/profile_shortcut_manager.h
index 38e5550..43304ac6 100644
--- a/chrome/browser/profiles/profile_shortcut_manager.h
+++ b/chrome/browser/profiles/profile_shortcut_manager.h
@@ -31,6 +31,11 @@
   // profile created.
   virtual void CreateProfileShortcut(const base::FilePath& profile_path) = 0;
 
+  // Create an incognito desktop shortcut for the profile with path
+  // |profile_path|
+  virtual void CreateIncognitoProfileShortcut(
+      const base::FilePath& profile_path) = 0;
+
   // Removes any desktop profile shortcuts for the profile corresponding to
   // |profile_path|.
   virtual void RemoveProfileShortcuts(const base::FilePath& profile_path) = 0;
diff --git a/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc b/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
index 199e1ca9..d23d212a 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
+++ b/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
@@ -397,6 +397,21 @@
             profiles::internal::CreateProfileShortcutFlags(profile_path));
 }
 
+// Test ensures that the incognito switch and parent profile are added when
+// creating profile shortcut flags for incognito mode.
+TEST_F(ProfileShortcutManagerTest, IncognitoShortcutFlags) {
+  const base::string16 kProfileName = L"MyProfileX";
+  const base::FilePath profile_path =
+      profile_manager_->profiles_dir().Append(kProfileName);
+  const base::string16 shortcut_flags =
+      profiles::internal::CreateProfileShortcutFlags(profile_path,
+                                                     /*incognito=*/true);
+  EXPECT_NE(
+      shortcut_flags.find(L"--profile-directory=\"" + kProfileName + L"\""),
+      shortcut_flags.size());
+  EXPECT_NE(shortcut_flags.find(L"--incognito"), shortcut_flags.size());
+}
+
 TEST_F(ProfileShortcutManagerTest, DesktopShortcutsCreate) {
   SetupDefaultProfileShortcut(FROM_HERE);
   // Validation is done by |ValidateProfileShortcutAtPath()| which is called
diff --git a/chrome/browser/profiles/profile_shortcut_manager_win.cc b/chrome/browser/profiles/profile_shortcut_manager_win.cc
index 54da82ca..1263c0e 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_win.cc
+++ b/chrome/browser/profiles/profile_shortcut_manager_win.cc
@@ -385,11 +385,13 @@
       base::FilePath profile_path,
       ProfileShortcutManagerWin::CreateOrUpdateMode create_mode,
       ProfileShortcutManagerWin::NonProfileShortcutAction action,
-      bool single_profile)
+      bool single_profile,
+      bool incognito = false)
       : create_mode(create_mode),
         action(action),
         profile_path(profile_path),
-        single_profile(single_profile) {}
+        single_profile(single_profile),
+        incognito(incognito) {}
   ~CreateOrUpdateShortcutsParams() {}
 
   ProfileShortcutManagerWin::CreateOrUpdateMode create_mode;
@@ -406,6 +408,10 @@
   // badged icon or the name of profile in the shortcut name.
   bool single_profile;
 
+  // If true, this adds an incognito switch that allows the user to open an
+  // incognito window directly from the desktop shortcut.
+  bool incognito;
+
   // Avatar images for this profile.
   SkBitmap avatar_image_1x;
   SkBitmap avatar_image_2x;
@@ -438,7 +444,8 @@
   std::set<base::FilePath> desktop_contents = ListUserDesktopContents(nullptr);
 
   const base::string16 command_line =
-      profiles::internal::CreateProfileShortcutFlags(params.profile_path);
+      profiles::internal::CreateProfileShortcutFlags(params.profile_path,
+                                                     params.incognito);
   ChromeCommandLineFilter filter(
       chrome_exe, command_line,
       params.action == ProfileShortcutManagerWin::UPDATE_NON_PROFILE_SHORTCUTS);
@@ -689,10 +696,18 @@
                      iswdigit);
 }
 
-base::string16 CreateProfileShortcutFlags(const base::FilePath& profile_path) {
-  return base::StringPrintf(
+base::string16 CreateProfileShortcutFlags(const base::FilePath& profile_path,
+                                          const bool incognito) {
+  base::string16 flags = base::StringPrintf(
       L"--%ls=\"%ls\"", base::ASCIIToUTF16(switches::kProfileDirectory).c_str(),
       profile_path.BaseName().value().c_str());
+
+  if (incognito) {
+    flags.append(base::StringPrintf(
+        L" --%ls", base::ASCIIToUTF16(switches::kIncognito).c_str()));
+  }
+
+  return flags;
 }
 
 }  // namespace internal
@@ -748,13 +763,25 @@
 void ProfileShortcutManagerWin::CreateOrUpdateProfileIcon(
     const base::FilePath& profile_path) {
   CreateOrUpdateShortcutsForProfileAtPath(
-      profile_path, CREATE_OR_UPDATE_ICON_ONLY, IGNORE_NON_PROFILE_SHORTCUTS);
+      profile_path, CREATE_OR_UPDATE_ICON_ONLY, IGNORE_NON_PROFILE_SHORTCUTS,
+      /*incognito=*/false);
+}
+
+// Creates an incognito desktop shortcut for the current profile.
+// TODO(crbug.com/1113162): Update the shortcut label and icon to chrome +
+// incognito.
+void ProfileShortcutManagerWin::CreateIncognitoProfileShortcut(
+    const base::FilePath& profile_path) {
+  CreateOrUpdateShortcutsForProfileAtPath(profile_path, CREATE_WHEN_NONE_FOUND,
+                                          IGNORE_NON_PROFILE_SHORTCUTS,
+                                          /*incognito=*/true);
 }
 
 void ProfileShortcutManagerWin::CreateProfileShortcut(
     const base::FilePath& profile_path) {
   CreateOrUpdateShortcutsForProfileAtPath(profile_path, CREATE_WHEN_NONE_FOUND,
-                                          IGNORE_NON_PROFILE_SHORTCUTS);
+                                          IGNORE_NON_PROFILE_SHORTCUTS,
+                                          /*incognito=*/false);
 }
 
 void ProfileShortcutManagerWin::RemoveProfileShortcuts(
@@ -815,9 +842,9 @@
     // When the second profile is added, make existing non-profile and
     // non-badged shortcuts point to the first profile and be badged/named
     // appropriately.
-    CreateOrUpdateShortcutsForProfileAtPath(GetOtherProfilePath(profile_path),
-                                            UPDATE_EXISTING_ONLY,
-                                            UPDATE_NON_PROFILE_SHORTCUTS);
+    CreateOrUpdateShortcutsForProfileAtPath(
+        GetOtherProfilePath(profile_path), UPDATE_EXISTING_ONLY,
+        UPDATE_NON_PROFILE_SHORTCUTS, /*incognito=*/false);
   }
 }
 
@@ -834,7 +861,8 @@
     // This is needed to unbadge the icon.
     CreateOrUpdateShortcutsForProfileAtPath(
         storage.GetAllProfilesAttributes().front()->GetPath(),
-        UPDATE_EXISTING_ONLY, IGNORE_NON_PROFILE_SHORTCUTS);
+        UPDATE_EXISTING_ONLY, IGNORE_NON_PROFILE_SHORTCUTS,
+        /*incognito=*/false);
   }
 
   base::FilePath first_profile_path;
@@ -853,7 +881,8 @@
     const base::FilePath& profile_path,
     const base::string16& old_profile_name) {
   CreateOrUpdateShortcutsForProfileAtPath(profile_path, UPDATE_EXISTING_ONLY,
-                                          IGNORE_NON_PROFILE_SHORTCUTS);
+                                          IGNORE_NON_PROFILE_SHORTCUTS,
+                                          /*incognito=*/false);
 }
 
 void ProfileShortcutManagerWin::OnProfileAvatarChanged(
@@ -892,7 +921,8 @@
 void ProfileShortcutManagerWin::CreateOrUpdateShortcutsForProfileAtPath(
     const base::FilePath& profile_path,
     CreateOrUpdateMode create_mode,
-    NonProfileShortcutAction action) {
+    NonProfileShortcutAction action,
+    bool incognito) {
   DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
          BrowserThread::CurrentlyOn(BrowserThread::UI));
   ProfileAttributesStorage& storage =
@@ -905,7 +935,8 @@
   bool remove_badging = storage.GetNumberOfProfiles() == 1u;
 
   CreateOrUpdateShortcutsParams params(profile_path, create_mode, action,
-                                       /*single_profile=*/remove_badging);
+                                       /*single_profile=*/remove_badging,
+                                       incognito);
 
   params.old_profile_name = entry->GetShortcutName();
 
diff --git a/chrome/browser/profiles/profile_shortcut_manager_win.h b/chrome/browser/profiles/profile_shortcut_manager_win.h
index 6d5ad2e6..298bbe5 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_win.h
+++ b/chrome/browser/profiles/profile_shortcut_manager_win.h
@@ -55,7 +55,8 @@
 };
 
 // Returns the command-line flags to launch Chrome with the given profile.
-base::string16 CreateProfileShortcutFlags(const base::FilePath& profile_path);
+base::string16 CreateProfileShortcutFlags(const base::FilePath& profile_path,
+                                          const bool incognito = false);
 
 }  // namespace internal
 }  // namespace profiles
@@ -84,6 +85,8 @@
   ~ProfileShortcutManagerWin() override;
 
   // ProfileShortcutManager implementation:
+  void CreateIncognitoProfileShortcut(
+      const base::FilePath& profile_path) override;
   void CreateOrUpdateProfileIcon(const base::FilePath& profile_path) override;
   void CreateProfileShortcut(const base::FilePath& profile_path) override;
   void RemoveProfileShortcuts(const base::FilePath& profile_path) override;
@@ -111,12 +114,13 @@
   base::FilePath GetOtherProfilePath(const base::FilePath& profile_path);
 
   // Creates or updates shortcuts for the profile at |profile_path| according
-  // to the specified |create_mode| and |action|. This will always involve
-  // creating or updating the icon file for this profile.
+  // to the specified |create_mode|, |action|, and |incognito|. This will always
+  // involve creating or updating the icon file for this profile.
   void CreateOrUpdateShortcutsForProfileAtPath(
       const base::FilePath& profile_path,
       CreateOrUpdateMode create_mode,
-      NonProfileShortcutAction action);
+      NonProfileShortcutAction action,
+      bool incognito);
 
   ProfileManager* profile_manager_;
 
diff --git a/chrome/browser/reputation/safety_tip_infobar_delegate.cc b/chrome/browser/reputation/safety_tip_infobar_delegate.cc
index f8d1481..3354d34 100644
--- a/chrome/browser/reputation/safety_tip_infobar_delegate.cc
+++ b/chrome/browser/reputation/safety_tip_infobar_delegate.cc
@@ -100,6 +100,15 @@
       ->SetUserIgnore(web_contents_, url_, action_taken_);
 }
 
+base::string16 SafetyTipInfoBarDelegate::GetLinkText() const {
+  return l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_MORE_INFO_LINK);
+}
+
+bool SafetyTipInfoBarDelegate::LinkClicked(WindowOpenDisposition disposition) {
+  OpenHelpCenterFromSafetyTip(web_contents_);
+  return false;
+}
+
 base::string16 SafetyTipInfoBarDelegate::GetDescriptionText() const {
   return GetSafetyTipDescription(safety_tip_status_, suggested_url_);
 }
diff --git a/chrome/browser/reputation/safety_tip_infobar_delegate.h b/chrome/browser/reputation/safety_tip_infobar_delegate.h
index 3329110..a970025 100644
--- a/chrome/browser/reputation/safety_tip_infobar_delegate.h
+++ b/chrome/browser/reputation/safety_tip_infobar_delegate.h
@@ -31,6 +31,8 @@
   infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
   int GetIconId() const override;
   void InfoBarDismissed() override;
+  base::string16 GetLinkText() const override;
+  bool LinkClicked(WindowOpenDisposition disposition) override;
 
   // This function is the equivalent of GetMessageText(), but for the portion of
   // the infobar below the 'message' title.
diff --git a/chrome/browser/resources/chromeos/login/oobe_update.html b/chrome/browser/resources/chromeos/login/oobe_update.html
index 9e030266..8c059e9 100644
--- a/chrome/browser/resources/chromeos/login/oobe_update.html
+++ b/chrome/browser/resources/chromeos/login/oobe_update.html
@@ -50,7 +50,26 @@
       </oobe-dialog>
     </div>
     <div hidden="[[!betterUpdateScreenFeatureEnabled_]]">
-      <oobe-dialog>
+      <oobe-dialog footer-shrinkable id="restarting-dialog"
+          hidden="[[!isRebootProgressBarShown_(manualRebootNeeded,
+                                               updateCompleted)]]"
+          title-key="updateCompeletedRebootingMsg"
+          aria-live="polite">
+        <hd-iron-icon slot="oobe-icon"
+            icon1x="oobe-32:googleg" icon2x="oobe-64:googleg">
+        </hd-iron-icon>
+        <paper-progress slot="progress" id="restarting-progress" indeterminate>
+        </paper-progress>
+      </oobe-dialog>
+      <oobe-dialog footer-shrinkable id="better-update-complete-dialog"
+          hidden="[[!manualRebootNeeded]]"
+          title-key="updateCompeletedMsg"
+          aria-live="polite">
+        <hd-iron-icon slot="oobe-icon"
+            icon1x="oobe-32:googleg" icon2x="oobe-64:googleg">
+        </hd-iron-icon>
+      </oobe-dialog>
+      <oobe-dialog hidden="[[!showLowBatteryWarning]]">
         <div hidden="[[!showLowBatteryWarning]]" id="battery-warning"
             slot="footer">
         </div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_update.js b/chrome/browser/resources/chromeos/login/oobe_update.js
index eb877e6..c7da373 100644
--- a/chrome/browser/resources/chromeos/login/oobe_update.js
+++ b/chrome/browser/resources/chromeos/login/oobe_update.js
@@ -23,6 +23,7 @@
     'setEstimatedTimeLeft',
     'showEstimatedTimeLeft',
     'setUpdateCompleted',
+    'setManualRebootNeeded',
     'showUpdateCurtain',
     'setProgressMessage',
     'setUpdateProgress',
@@ -101,6 +102,14 @@
     },
 
     /**
+     * True if update is fully completed and manual action is required.
+     */
+    manualRebootNeeded: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
      * If update cancellation is allowed.
      */
     cancelAllowed: {
@@ -140,8 +149,10 @@
   },
 
   onBeforeShow() {
-    cr.ui.login.invokePolymerMethod(
-        this.$['checking-downloading-update'], 'onBeforeShow');
+    if (!this.betterUpdateScreenFeatureEnabled_) {
+      cr.ui.login.invokePolymerMethod(
+          this.$['checking-downloading-update'], 'onBeforeShow');
+    }
   },
 
   onBackClicked_() {
@@ -210,6 +221,13 @@
   },
 
   /**
+   * @param {boolean} is_needed True if manual reboot after update is needed.
+   */
+  setManualRebootNeeded(is_needed) {
+    this.manualRebootNeeded = is_needed;
+  },
+
+  /**
    * Shows or hides update curtain.
    * @param {boolean} visible Are curtains visible?
    */
@@ -225,5 +243,15 @@
     this.showLowBatteryWarning = visible;
   },
 
+  /**
+   * Calculates visibility of the reboot progress dialog.
+   * @param {Boolean} manualRebootNeeded If the automatic reboot timer has
+   * elapsed and manual reboot is needed.
+   * @param {Boolean} updateCompleted If update is completed and all
+   * intermediate status elements are hidden.
+   */
+  isRebootProgressBarShown_(manualRebootNeeded, updateCompleted) {
+    return updateCompleted && !manualRebootNeeded;
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/autofill_page/merge_exceptions_store_copies_behavior.js b/chrome/browser/resources/settings/autofill_page/merge_exceptions_store_copies_behavior.js
index 0a204f2..456143d 100644
--- a/chrome/browser/resources/settings/autofill_page/merge_exceptions_store_copies_behavior.js
+++ b/chrome/browser/resources/settings/autofill_page/merge_exceptions_store_copies_behavior.js
@@ -67,7 +67,16 @@
     const frontendIdToMergedEntry = new Map();
     for (const entry of exceptionList) {
       if (frontendIdToMergedEntry.has(entry.frontendId)) {
-        frontendIdToMergedEntry.get(entry.frontendId).merge(entry);
+        const mergeSucceded =
+            frontendIdToMergedEntry.get(entry.frontendId).mergeInPlace(entry);
+        if (mergeSucceded) {
+          // The merge is in-place, so nothing to be done.
+        } else {
+          // The merge can fail in weird cases despite |frontendId| matching.
+          // If so, just create another entry in the UI for |entry|. See also
+          // crbug.com/1114697.
+          multiStoreEntries.push(new MultiStoreExceptionEntry(entry));
+        }
       } else {
         const multiStoreEntry = new MultiStoreExceptionEntry(entry);
         frontendIdToMergedEntry.set(entry.frontendId, multiStoreEntry);
diff --git a/chrome/browser/resources/settings/autofill_page/merge_passwords_store_copies_behavior.js b/chrome/browser/resources/settings/autofill_page/merge_passwords_store_copies_behavior.js
index 87ded984..8d7db83 100644
--- a/chrome/browser/resources/settings/autofill_page/merge_passwords_store_copies_behavior.js
+++ b/chrome/browser/resources/settings/autofill_page/merge_passwords_store_copies_behavior.js
@@ -77,7 +77,16 @@
     const frontendIdToMergedEntry = new Map();
     for (const entry of passwordList) {
       if (frontendIdToMergedEntry.has(entry.frontendId)) {
-        frontendIdToMergedEntry.get(entry.frontendId).merge(entry);
+        const mergeSucceded =
+            frontendIdToMergedEntry.get(entry.frontendId).mergeInPlace(entry);
+        if (mergeSucceded) {
+          // The merge is in-place, so nothing to be done.
+        } else {
+          // The merge can fail in weird cases despite |frontendId| matching.
+          // If so, just create another entry in the UI for |entry|. See also
+          // crbug.com/1114697.
+          multiStoreEntries.push(new MultiStorePasswordUiEntry(entry));
+        }
       } else {
         const multiStoreEntry = new MultiStorePasswordUiEntry(entry);
         frontendIdToMergedEntry.set(entry.frontendId, multiStoreEntry);
diff --git a/chrome/browser/resources/settings/autofill_page/multi_store_exception_entry.js b/chrome/browser/resources/settings/autofill_page/multi_store_exception_entry.js
index 67c6f6a..7c552e34 100644
--- a/chrome/browser/resources/settings/autofill_page/multi_store_exception_entry.js
+++ b/chrome/browser/resources/settings/autofill_page/multi_store_exception_entry.js
@@ -18,37 +18,37 @@
  */
 export class MultiStoreExceptionEntry extends MultiStoreIdHandler {
   /**
-   * Creates a multi-store entry from duplicates |entry1| and (optional)
-   * |entry2|. If both arguments are passed, they should have the same contents
-   * but should be from different stores.
-   * @param {!PasswordManagerProxy.ExceptionEntry} entry1
-   * @param {PasswordManagerProxy.ExceptionEntry=} entry2
+   * @param {!PasswordManagerProxy.ExceptionEntry} entry
    */
-  constructor(entry1, entry2) {
+  constructor(entry) {
     super();
 
     /** @type {!PasswordManagerProxy.UrlCollection} */
-    this.urls_ = entry1.urls;
+    this.urls_ = entry.urls;
 
-    this.setId(entry1.id, entry1.fromAccountStore);
-
-    if (entry2) {
-      this.merge(entry2);
-    }
+    this.setId(entry.id, entry.fromAccountStore);
   }
 
   /**
    * Incorporates the id of |otherEntry|, as long as |otherEntry| matches
-   * |contents_| and the id corresponding to its store is not set.
+   * |contents_| and the id corresponding to its store is not set. If these
+   * preconditions are not satisfied, results in a no-op.
    * @param {!PasswordManagerProxy.ExceptionEntry} otherEntry
+   * @return {boolean} Returns whether the merge succeeded.
    */
   // TODO(crbug.com/1102294) Consider asserting frontendId as well.
-  merge(otherEntry) {
-    assert(
-        (this.isPresentInAccount() && !otherEntry.fromAccountStore) ||
-        (this.isPresentOnDevice() && otherEntry.fromAccountStore));
-    assert(JSON.stringify(this.urls_) === JSON.stringify(otherEntry.urls));
+  mergeInPlace(otherEntry) {
+    const alreadyHasCopyFromStore =
+        (this.isPresentInAccount() && otherEntry.fromAccountStore) ||
+        (this.isPresentOnDevice() && !otherEntry.fromAccountStore);
+    if (alreadyHasCopyFromStore) {
+      return false;
+    }
+    if (JSON.stringify(this.urls_) !== JSON.stringify(otherEntry.urls)) {
+      return false;
+    }
     this.setId(otherEntry.id, otherEntry.fromAccountStore);
+    return true;
   }
 
   get urls() {
diff --git a/chrome/browser/resources/settings/autofill_page/multi_store_password_ui_entry.js b/chrome/browser/resources/settings/autofill_page/multi_store_password_ui_entry.js
index d5a4ea40..fa164968 100644
--- a/chrome/browser/resources/settings/autofill_page/multi_store_password_ui_entry.js
+++ b/chrome/browser/resources/settings/autofill_page/multi_store_password_ui_entry.js
@@ -18,42 +18,41 @@
  */
 export class MultiStorePasswordUiEntry extends MultiStoreIdHandler {
   /**
-   * Creates a multi-store item from duplicates |entry1| and (optional)
-   * |entry2|. If both arguments are passed, they should have the same contents
-   * but should be from different stores.
-   * @param {!PasswordManagerProxy.PasswordUiEntry} entry1
-   * @param {PasswordManagerProxy.PasswordUiEntry=} entry2
+   * @param {!PasswordManagerProxy.PasswordUiEntry} entry
    */
-  constructor(entry1, entry2) {
+  constructor(entry) {
     super();
 
     /** @type {!MultiStorePasswordUiEntry.Contents} */
-    this.contents_ = MultiStorePasswordUiEntry.getContents_(entry1);
+    this.contents_ = MultiStorePasswordUiEntry.getContents_(entry);
 
     /** @type {string} */
     this.password_ = '';
 
-    this.setId(entry1.id, entry1.fromAccountStore);
-
-    if (entry2) {
-      this.merge(entry2);
-    }
+    this.setId(entry.id, entry.fromAccountStore);
   }
 
   /**
    * Incorporates the id of |otherEntry|, as long as |otherEntry| matches
-   * |contents_| and the id corresponding to its store is not set.
+   * |contents_| and the id corresponding to its store is not set. If these
+   * preconditions are not satisfied, results in a no-op.
    * @param {!PasswordManagerProxy.PasswordUiEntry} otherEntry
+   * @return {boolean} Returns whether the merge succeeded.
    */
   // TODO(crbug.com/1102294) Consider asserting frontendId as well.
-  merge(otherEntry) {
-    assert(
-        (this.isPresentInAccount() && !otherEntry.fromAccountStore) ||
-        (this.isPresentOnDevice() && otherEntry.fromAccountStore));
-    assert(
-        JSON.stringify(this.contents_) ===
-        JSON.stringify(MultiStorePasswordUiEntry.getContents_(otherEntry)));
+  mergeInPlace(otherEntry) {
+    const alreadyHasCopyFromStore =
+        (this.isPresentInAccount() && otherEntry.fromAccountStore) ||
+        (this.isPresentOnDevice() && !otherEntry.fromAccountStore);
+    if (alreadyHasCopyFromStore) {
+      return false;
+    }
+    if (JSON.stringify(this.contents_) !==
+        JSON.stringify(MultiStorePasswordUiEntry.getContents_(otherEntry))) {
+      return false;
+    }
     this.setId(otherEntry.id, otherEntry.fromAccountStore);
+    return true;
   }
 
   /** @return {!PasswordManagerProxy.UrlCollection} */
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_browser_proxy.js b/chrome/browser/resources/settings/safety_check_page/safety_check_browser_proxy.js
index 227552c..ce842a2 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_browser_proxy.js
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_browser_proxy.js
@@ -88,6 +88,7 @@
   DISABLED_BY_EXTENSION: 4,
   ENABLED_STANDARD: 5,
   ENABLED_ENHANCED: 6,
+  ENABLED_STANDARD_AVAILABLE_ENHANCED: 7,
 };
 
 /**
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_child.js b/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_child.js
index 17b0fd4c..072c938 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_child.js
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_child.js
@@ -86,6 +86,7 @@
         return SafetyCheckIconStatus.RUNNING;
       case SafetyCheckSafeBrowsingStatus.ENABLED_STANDARD:
       case SafetyCheckSafeBrowsingStatus.ENABLED_ENHANCED:
+      case SafetyCheckSafeBrowsingStatus.ENABLED_STANDARD_AVAILABLE_ENHANCED:
         return SafetyCheckIconStatus.SAFE;
       case SafetyCheckSafeBrowsingStatus.ENABLED:
         // ENABLED is deprecated.
diff --git a/chrome/browser/resources/signin/profile_picker/BUILD.gn b/chrome/browser/resources/signin/profile_picker/BUILD.gn
index be443e29..25f9d0b 100644
--- a/chrome/browser/resources/signin/profile_picker/BUILD.gn
+++ b/chrome/browser/resources/signin/profile_picker/BUILD.gn
@@ -43,7 +43,6 @@
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:load_time_data.m",
   ]
 }
 
diff --git a/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js b/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
index c59bfa2..189c052 100644
--- a/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
+++ b/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.js
@@ -39,6 +39,13 @@
    */
   launchSelectedProfile(profilePath) {}
 
+  /**
+   * Opens profile on manage profile settings sub page and closes the
+   * profile picker.
+   * @param {string} profilePath
+   */
+  openManageProfileSettingsSubPage(profilePath) {}
+
   /** Launches Guest profile. */
   launchGuestProfile() {}
 
@@ -85,6 +92,11 @@
   }
 
   /** @override */
+  openManageProfileSettingsSubPage(profilePath) {
+    chrome.send('openManageProfileSettingsSubPage', [profilePath]);
+  }
+
+  /** @override */
   launchGuestProfile() {
     chrome.send('launchGuestProfile');
   }
diff --git a/chrome/browser/resources/signin/profile_picker/navigation_behavior.js b/chrome/browser/resources/signin/profile_picker/navigation_behavior.js
index 754a7e7..6f09aa4 100644
--- a/chrome/browser/resources/signin/profile_picker/navigation_behavior.js
+++ b/chrome/browser/resources/signin/profile_picker/navigation_behavior.js
@@ -3,9 +3,8 @@
 // found in the LICENSE file.
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
-import './strings.js';
+import {isForceSigninEnabled, isSignInProfileCreationSupported} from './policy_helper.js';
 
 /**
  * Valid route pathnames.
@@ -36,14 +35,16 @@
     case Routes.MAIN:
       return 'mainView';
     case Routes.NEW_PROFILE:
-      // TODO(msalama): Add support in profile creation mode for policies like:
-      // - ForceSignIn --> load signin page directly.
-      // - DisallowSignIn --> open local profile customization.
-      if (loadTimeData.getBoolean('signInProfileCreationFlow')) {
-        return ProfileCreationSteps.PROFILE_TYPE_CHOICE;
-      } else {
+      // TODO(msalama): Adjust once sign in profile creation is supported.
+      // Check DisallowSignIn policy.
+      if (!isSignInProfileCreationSupported()) {
+        assert(!isForceSigninEnabled());
         return ProfileCreationSteps.LOCAL_PROFILE_CUSTOMIZATION;
       }
+      if (isForceSigninEnabled()) {
+        return ProfileCreationSteps.LOAD_SIGNIN;
+      }
+      return ProfileCreationSteps.PROFILE_TYPE_CHOICE;
     default:
       assertNotReached();
   }
@@ -146,4 +147,4 @@
    * @param {string} step
    */
   onRouteChange: function(route, step) {},
-};
\ No newline at end of file
+};
diff --git a/chrome/browser/resources/signin/profile_picker/policy_helper.js b/chrome/browser/resources/signin/profile_picker/policy_helper.js
new file mode 100644
index 0000000..17cc602
--- /dev/null
+++ b/chrome/browser/resources/signin/profile_picker/policy_helper.js
@@ -0,0 +1,31 @@
+// 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 {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import './strings.js';
+
+/** @return {boolean} */
+export function isGuestModeEnabled() {
+  return loadTimeData.getBoolean('isGuestModeEnabled');
+}
+
+/** @return {boolean} */
+export function isProfileCreationAllowed() {
+  return loadTimeData.getBoolean('isProfileCreationAllowed');
+}
+
+/** @return {boolean} */
+export function isForceSigninEnabled() {
+  const enabled = loadTimeData.getBoolean('isForceSigninEnabled');
+  // Force sign in policy is not supported yet. The picker should not be shown
+  // in case this policy exists.
+  assert(!enabled);
+  return enabled;
+}
+
+/** @return {boolean} */
+export function isSignInProfileCreationSupported() {
+  return loadTimeData.getBoolean('signInProfileCreationFlowSupported');
+}
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card_menu.html b/chrome/browser/resources/signin/profile_picker/profile_card_menu.html
index b193eeb..21bf57b1 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card_menu.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_card_menu.html
@@ -86,6 +86,9 @@
   <button class="dropdown-item" on-click="onRemoveButtonClicked_">
     $i18n{profileMenuRemoveText}
   </button>
+  <button class="dropdown-item" on-click="onCustomizeButtonClicked_">
+    $i18n{profileMenuCustomizeText}
+  </button>
 </cr-action-menu>
 
 <cr-action-menu id="removeActionMenu" role-description="$i18n{menu}">
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card_menu.js b/chrome/browser/resources/signin/profile_picker/profile_card_menu.js
index a3f0f55..1e0d1dc 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card_menu.js
+++ b/chrome/browser/resources/signin/profile_picker/profile_card_menu.js
@@ -222,4 +222,11 @@
     this.$.actionMenu.close();
     this.$.removeActionMenu.close();
   },
+
+  /** @private */
+  onCustomizeButtonClicked_() {
+    this.manageProfilesBrowserProxy_.openManageProfileSettingsSubPage(
+        this.profileState.profilePath);
+    this.$.actionMenu.close();
+  },
 });
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_app.js b/chrome/browser/resources/signin/profile_picker/profile_picker_app.js
index 2f41a02..f34e21f3 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_app.js
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_app.js
@@ -11,7 +11,8 @@
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxy, ManageProfilesBrowserProxyImpl} from './manage_profiles_browser_proxy.js';
-import {NavigationBehavior, ProfileCreationSteps, Routes} from './navigation_behavior.js';
+import {navigateTo, NavigationBehavior, ProfileCreationSteps, Routes} from './navigation_behavior.js';
+import {isProfileCreationAllowed} from './policy_helper.js';
 import {ensureLazyLoaded} from './profile_creation_flow/ensure_lazy_loaded.js';
 
 Polymer({
@@ -50,6 +51,11 @@
    * @private
    */
   onRouteChange(route, step) {
+    if (!isProfileCreationAllowed() && route === Routes.NEW_PROFILE) {
+      navigateTo(Routes.MAIN);
+      return;
+    }
+
     if (step == ProfileCreationSteps.LOAD_SIGNIN) {
       this.manageProfilesBrowserProxy_.loadSignInProfileCreationFlow();
       return;
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.js b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.js
index d161b9b3..11dfd30 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.js
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.js
@@ -16,7 +16,7 @@
 
 import {ManageProfilesBrowserProxy, ManageProfilesBrowserProxyImpl, ProfileState} from './manage_profiles_browser_proxy.js';
 import {navigateTo, NavigationBehavior, Routes} from './navigation_behavior.js';
-
+import {isGuestModeEnabled, isProfileCreationAllowed} from './policy_helper.js';
 
 Polymer({
   is: 'profile-picker-main-view',
@@ -48,6 +48,14 @@
 
   /** @override */
   ready() {
+    if (!isGuestModeEnabled()) {
+      this.$.browseAsGuestButton.style.display = 'none';
+    }
+
+    if (!isProfileCreationAllowed()) {
+      this.$.addProfile.style.display = 'none';
+    }
+
     this.manageProfilesBrowserProxy_ =
         ManageProfilesBrowserProxyImpl.getInstance();
   },
@@ -92,11 +100,17 @@
 
   /** @private */
   onAddProfileClick_() {
+    if (!isProfileCreationAllowed()) {
+      return;
+    }
     navigateTo(Routes.NEW_PROFILE);
   },
 
   /** @private */
   onLaunchGuestProfileClick_() {
+    if (!isGuestModeEnabled()) {
+      return;
+    }
     this.manageProfilesBrowserProxy_.launchGuestProfile();
   },
 
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_resources.grd b/chrome/browser/resources/signin/profile_picker/profile_picker_resources.grd
index 465789f..604da43 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_resources.grd
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_resources.grd
@@ -65,6 +65,11 @@
           type="chrome_html"
           compress="false"/>
       <structure
+          name="IDR_PROFILE_PICKER_POLICY_HELPER_JS"
+          file="policy_helper.js"
+          type="chrome_html"
+          compress="false"/>
+      <structure
           name="IDR_PROFILE_PICKER_ICONS_JS"
           file="icons.js"
           type="chrome_html"
diff --git a/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.html b/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.html
index 76816769..7782702 100644
--- a/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.html
+++ b/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.html
@@ -61,23 +61,31 @@
     padding-inline-start: 16px;
   }
 </style>
+
+<!--
+  Use the 'consent-description' attribute to annotate all the UI elements
+  that are part of the text the user reads before consenting to use passwords
+  from their account. Similarly, use 'consent-confirmation' on the UI element on
+  which user clicks to indicate consent.
+-->
+
 <div id="illustrationContainer">
   <div id="illustration"></div>
   <img src="[[accountImageSrc_]]">
 </div>
 <div id="contentContainer">
-  <h1 id="signinReauthTitle">
+  <h1 id="signinReauthTitle" consent-description>
     $i18n{signinReauthTitle}
   </h1>
   <div class="message-container">
-    <div>$i18n{signinReauthDesc}</div>
+    <div consent-description>$i18n{signinReauthDesc}</div>
   </div>
 </div>
 <div class="action-container">
   <paper-spinner-lite active hidden$="[[!confirmButtonHidden_]]">
   </paper-spinner-lite>
   <cr-button id="confirmButton" class="action-button" on-click="onConfirm_"
-      hidden="[[confirmButtonHidden_]]">
+      hidden="[[confirmButtonHidden_]]" consent-confirmation>
     [[confirmButtonLabel_]]
   </cr-button>
   <cr-button id="cancelButton" on-click="onCancel_"
diff --git a/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.js b/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.js
index e2c6b4e..6ce8e1f 100644
--- a/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.js
+++ b/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.js
@@ -8,6 +8,7 @@
 import './strings.m.js';
 import './signin_shared_css.js';
 
+import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
@@ -52,9 +53,14 @@
     this.signinReauthBrowserProxy_.initialize();
   },
 
-  /** @private */
-  onConfirm_() {
-    this.signinReauthBrowserProxy_.confirm();
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onConfirm_(e) {
+    this.signinReauthBrowserProxy_.confirm(
+        this.getConsentDescription_(),
+        this.getConsentConfirmation_(e.composedPath()));
   },
 
   /** @private */
@@ -75,4 +81,31 @@
         this.i18n('signinReauthNextLabel') :
         this.i18n('signinReauthConfirmLabel');
   },
+
+  /** @return {!Array<string>} Text of the consent description elements. */
+  getConsentDescription_() {
+    const consentDescription =
+        Array.from(this.shadowRoot.querySelectorAll('[consent-description]'))
+            .filter(element => element.clientWidth * element.clientHeight > 0)
+            .map(element => element.innerHTML.trim());
+    assert(consentDescription);
+    return consentDescription;
+  },
+
+  /**
+   * @param {!Array<!HTMLElement>} path Path of the click event. Must contain
+   *     a consent confirmation element.
+   * @return {string} The text of the consent confirmation element.
+   * @private
+   */
+  getConsentConfirmation_(path) {
+    for (const element of path) {
+      if (element.nodeType !== Node.DOCUMENT_FRAGMENT_NODE &&
+          element.hasAttribute('consent-confirmation')) {
+        return element.innerHTML.trim();
+      }
+    }
+    assertNotReached('No consent confirmation element found.');
+    return '';
+  },
 });
diff --git a/chrome/browser/resources/signin/signin_reauth/signin_reauth_browser_proxy.js b/chrome/browser/resources/signin/signin_reauth/signin_reauth_browser_proxy.js
index d4a0404..4dfbe1a 100644
--- a/chrome/browser/resources/signin/signin_reauth/signin_reauth_browser_proxy.js
+++ b/chrome/browser/resources/signin/signin_reauth/signin_reauth_browser_proxy.js
@@ -17,8 +17,12 @@
 
   /**
    * Called when the user confirms the signin reauth dialog.
+   * @param {!Array<string>} description Strings that the user was presented
+   *     with in the UI.
+   * @param {string} confirmation Text of the element that the user
+   *     clicked on.
    */
-  confirm() {}
+  confirm(description, confirmation) {}
 
   /**
    * Called when the user cancels the signin reauth.
@@ -34,8 +38,8 @@
   }
 
   /** @override */
-  confirm() {
-    chrome.send('confirm');
+  confirm(description, confirmation) {
+    chrome.send('confirm', [description, confirmation]);
   }
 
   /** @override */
diff --git a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckProperties.java b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckProperties.java
index b1eca24f..6cfe5fc 100644
--- a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckProperties.java
+++ b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckProperties.java
@@ -95,6 +95,7 @@
                 return SafeBrowsingState.CHECKING;
             case SafeBrowsingStatus.ENABLED:
             case SafeBrowsingStatus.ENABLED_STANDARD:
+            case SafeBrowsingStatus.ENABLED_STANDARD_AVAILABLE_ENHANCED:
                 return SafeBrowsingState.ENABLED_STANDARD;
             case SafeBrowsingStatus.ENABLED_ENHANCED:
                 return SafeBrowsingState.ENABLED_ENHANCED;
diff --git a/chrome/browser/signin/dice_signed_in_profile_creator.cc b/chrome/browser/signin/dice_signed_in_profile_creator.cc
index 8836c954..490ad96 100644
--- a/chrome/browser/signin/dice_signed_in_profile_creator.cc
+++ b/chrome/browser/signin/dice_signed_in_profile_creator.cc
@@ -7,6 +7,8 @@
 #include "base/check.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
@@ -129,6 +131,7 @@
 DiceSignedInProfileCreator::DiceSignedInProfileCreator(
     Profile* source_profile,
     CoreAccountId account_id,
+    const std::string& local_profile_name,
     base::OnceCallback<void(Profile*)> callback)
     : source_profile_(source_profile),
       account_id_(account_id),
@@ -137,9 +140,12 @@
       g_browser_process->profile_manager()->GetProfileAttributesStorage();
   size_t icon_index = storage.ChooseAvatarIconIndexForNewProfile();
 
+  base::string16 name = local_profile_name.empty()
+                            ? storage.ChooseNameForNewProfile(icon_index)
+                            : base::UTF8ToUTF16(local_profile_name);
+
   ProfileManager::CreateMultiProfileAsync(
-      storage.ChooseNameForNewProfile(icon_index),
-      profiles::GetDefaultAvatarIconUrl(icon_index),
+      name, profiles::GetDefaultAvatarIconUrl(icon_index),
       base::BindRepeating(&DiceSignedInProfileCreator::OnNewProfileCreated,
                           weak_pointer_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/signin/dice_signed_in_profile_creator.h b/chrome/browser/signin/dice_signed_in_profile_creator.h
index 65ad8fc..d144576 100644
--- a/chrome/browser/signin/dice_signed_in_profile_creator.h
+++ b/chrome/browser/signin/dice_signed_in_profile_creator.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_SIGNIN_DICE_SIGNED_IN_PROFILE_CREATOR_H_
 #define CHROME_BROWSER_SIGNIN_DICE_SIGNED_IN_PROFILE_CREATOR_H_
 
+#include <string>
+
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/profiles/profile.h"
@@ -18,8 +20,11 @@
   // Creates a new profile and moves the account from source_profile to the new
   // profile. The callback is called with the new profile or nullptr in case of
   // failure. The callback is never called synchronously.
+  // If |local_profile_name| is not empty, it will be set as local name for the
+  // new profile.
   DiceSignedInProfileCreator(Profile* source_profile,
                              CoreAccountId account_id,
+                             const std::string& local_profile_name,
                              base::OnceCallback<void(Profile*)> callback);
 
   ~DiceSignedInProfileCreator();
@@ -36,8 +41,9 @@
   // Callback invoked once the token service is ready for the new profile.
   void OnNewProfileTokensLoaded(Profile* new_profile);
 
-  Profile* source_profile_;
-  CoreAccountId account_id_;
+  Profile* const source_profile_;
+  const CoreAccountId account_id_;
+
   base::OnceCallback<void(Profile*)> callback_;
   std::unique_ptr<TokensLoadedCallbackRunner> tokens_loaded_callback_runner_;
 
diff --git a/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc b/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
index c0c6f0e..e357bfb 100644
--- a/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
+++ b/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
@@ -10,7 +10,10 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -24,6 +27,8 @@
 
 namespace {
 
+const char kProfileTestName[] = "profile_test_name";
+
 std::unique_ptr<TestingProfile> BuildTestingProfile(const base::FilePath& path,
                                                     Profile::Delegate* delegate,
                                                     bool tokens_loaded) {
@@ -158,7 +163,7 @@
   base::RunLoop loop;
   std::unique_ptr<DiceSignedInProfileCreator> creator =
       std::make_unique<DiceSignedInProfileCreator>(
-          profile(), account_info.account_id,
+          profile(), account_info.account_id, kProfileTestName,
           base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
                          base::Unretained(this), loop.QuitClosure()));
   loop.Run();
@@ -175,6 +180,15 @@
                     .size());
   EXPECT_TRUE(IdentityManagerFactory::GetForProfile(signed_in_profile())
                   ->HasAccountWithRefreshToken(account_info.account_id));
+
+  // Check the profile name.
+  ProfileAttributesEntry* entry = nullptr;
+  ProfileAttributesStorage& storage =
+      profile_manager()->GetProfileAttributesStorage();
+  ASSERT_TRUE(storage.GetProfileAttributesWithPath(
+      signed_in_profile()->GetPath(), &entry));
+  ASSERT_TRUE(entry);
+  EXPECT_EQ(base::UTF8ToUTF16(kProfileTestName), entry->GetLocalProfileName());
 }
 
 TEST_F(DiceSignedInProfileCreatorTest, CreateWithTokensNotLoaded) {
@@ -187,7 +201,7 @@
   set_profile_added_closure(profile_added_loop.QuitClosure());
   std::unique_ptr<DiceSignedInProfileCreator> creator =
       std::make_unique<DiceSignedInProfileCreator>(
-          profile(), account_info.account_id,
+          profile(), account_info.account_id, kProfileTestName,
           base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
                          base::Unretained(this), creator_loop.QuitClosure()));
   profile_added_loop.Run();
@@ -222,7 +236,7 @@
       identity_test_env()->MakeAccountAvailable("bob@example.com");
   std::unique_ptr<DiceSignedInProfileCreator> creator =
       std::make_unique<DiceSignedInProfileCreator>(
-          profile(), account_info.account_id,
+          profile(), account_info.account_id, kProfileTestName,
           base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
                          base::Unretained(this), base::OnceClosure()));
   EXPECT_FALSE(creator_callback_called());
@@ -241,7 +255,7 @@
   set_profile_added_closure(profile_added_loop.QuitClosure());
   std::unique_ptr<DiceSignedInProfileCreator> creator =
       std::make_unique<DiceSignedInProfileCreator>(
-          profile(), account_info.account_id,
+          profile(), account_info.account_id, kProfileTestName,
           base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
                          base::Unretained(this), creator_loop.QuitClosure()));
   profile_added_loop.Run();
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index 6272dacc..01684db 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -265,11 +265,23 @@
     return;
   }
 
+  std::string profile_name;
+  base::Optional<AccountInfo> account_info =
+      identity_manager_
+          ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId(
+              account_id_);
+  if (account_info) {
+    bool is_managed = !account_info->hosted_domain.empty() &&
+                      account_info->hosted_domain != kNoHostedDomainFound;
+    profile_name =
+        is_managed ? account_info->hosted_domain : account_info->given_name;
+  }
+
   DCHECK(!dice_signed_in_profile_creator_);
   // Unretained is fine because the profile creator is owned by this.
   dice_signed_in_profile_creator_ =
       std::make_unique<DiceSignedInProfileCreator>(
-          profile_, account_id_,
+          profile_, account_id_, profile_name,
           base::BindOnce(&DiceWebSigninInterceptor::OnNewSignedInProfileCreated,
                          base::Unretained(this)));
 }
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
index aeca348..68a2d6d 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
@@ -5,7 +5,10 @@
 #include "chrome/browser/signin/dice_web_signin_interceptor.h"
 
 #include "base/scoped_observer.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
@@ -181,6 +184,15 @@
   EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
       account_info.account_id));
 
+  // Check the profile name.
+  ProfileAttributesEntry* entry = nullptr;
+  ProfileAttributesStorage& storage =
+      g_browser_process->profile_manager()->GetProfileAttributesStorage();
+  ASSERT_TRUE(
+      storage.GetProfileAttributesWithPath(new_profile->GetPath(), &entry));
+  ASSERT_TRUE(entry);
+  EXPECT_EQ("givenname", base::UTF16ToUTF8(entry->GetLocalProfileName()));
+
   // Add the account to the cookies (simulates the account reconcilor).
   EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
   signin::SetCookieAccounts(new_identity_manager, test_url_loader_factory(),
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
index 1d107bc6..788bae1 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.WebContentsState;
 import org.chromium.chrome.browser.tab.proto.CriticalPersistedTabData.CriticalPersistedTabDataProto;
-import org.chromium.url.GURL;
 
 import java.nio.ByteBuffer;
 import java.util.Locale;
@@ -39,11 +38,6 @@
      * Title of the ContentViews webpage.
      */
     private String mTitle;
-
-    /**
-     * URL of the page currently loading. Used as a fall-back in case tab restore fails.
-     */
-    private GURL mUrl;
     private int mParentId;
     private int mRootId;
     private long mTimestampMillis;
@@ -324,21 +318,6 @@
     }
 
     /**
-     * @return {@link GURL} for the {@link Tab}
-     */
-    public GURL getUrl() {
-        return mUrl;
-    }
-
-    /**
-     * Set {@link GURL} for the {@link Tab}
-     * @param url to set
-     */
-    public void setUrl(GURL url) {
-        mUrl = url;
-    }
-
-    /**
      * @return identifier for the {@link Tab}
      */
     public int getTabId() {
diff --git a/chrome/browser/ui/ash/in_session_auth_dialog_client.cc b/chrome/browser/ui/ash/in_session_auth_dialog_client.cc
index ef99b31..a078d4f8 100644
--- a/chrome/browser/ui/ash/in_session_auth_dialog_client.cc
+++ b/chrome/browser/ui/ash/in_session_auth_dialog_client.cc
@@ -10,8 +10,11 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/login/quick_unlock/fingerprint_storage.h"
+#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
+#include "components/account_id/account_id.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -49,6 +52,22 @@
   return g_auth_dialog_client_instance;
 }
 
+bool InSessionAuthDialogClient::IsFingerprintAuthAvailable(
+    const AccountId& account_id) {
+  chromeos::quick_unlock::QuickUnlockStorage* quick_unlock_storage =
+      chromeos::quick_unlock::QuickUnlockFactory::GetForAccountId(account_id);
+  return quick_unlock_storage &&
+         quick_unlock_storage->fingerprint_storage()->IsFingerprintAvailable();
+}
+
+void InSessionAuthDialogClient::CheckPinAuthAvailability(
+    const AccountId& account_id,
+    base::OnceCallback<void(bool)> callback) {
+  // PinBackend may be using cryptohome backend or prefs backend.
+  chromeos::quick_unlock::PinBackend::GetInstance()->CanAuthenticate(
+      account_id, std::move(callback));
+}
+
 void InSessionAuthDialogClient::AuthenticateUserWithPasswordOrPin(
     const std::string& password,
     bool authenticated_by_pin,
diff --git a/chrome/browser/ui/ash/in_session_auth_dialog_client.h b/chrome/browser/ui/ash/in_session_auth_dialog_client.h
index f1c84e5..6d92d8d 100644
--- a/chrome/browser/ui/ash/in_session_auth_dialog_client.h
+++ b/chrome/browser/ui/ash/in_session_auth_dialog_client.h
@@ -14,6 +14,8 @@
 #include "chromeos/login/auth/extended_authenticator.h"
 #include "chromeos/login/auth/user_context.h"
 
+class AccountId;
+
 // Handles method calls sent from Ash to ChromeOS.
 class InSessionAuthDialogClient : public ash::InSessionAuthDialogClient,
                                   public chromeos::AuthStatusConsumer {
@@ -34,6 +36,10 @@
       const std::string& password,
       bool authenticated_by_pin,
       AuthenticateCallback callback) override;
+  bool IsFingerprintAuthAvailable(const AccountId& account_id) override;
+  void CheckPinAuthAvailability(
+      const AccountId& account_id,
+      base::OnceCallback<void(bool)> callback) override;
 
   // AuthStatusConsumer:
   void OnAuthFailure(const chromeos::AuthFailure& error) override;
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index 37a3ec2..6dc0cf6 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -269,8 +269,8 @@
                kDontCheckTitle);
 }
 
-#if defined(OS_MAC) && defined(ADDRESS_SANITIZER)
-// Flaky on ASAN on Mac. See https://crbug.com/674497.
+// TODO(crbug.com/1115886): Flaky on Mac ASAN and Chrome OS.
+#if (defined(OS_MAC) && defined(ADDRESS_SANITIZER)) || defined(OS_CHROMEOS)
 #define MAYBE_BlockWebContentsCreationIncognito \
   DISABLED_BlockWebContentsCreationIncognito
 #else
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 6689b358..af67ed8 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1491,6 +1491,20 @@
   return SwapWebContents(predecessor_contents, std::move(portal_contents));
 }
 
+void Browser::UpdateInspectedWebContentsIfNecessary(
+    content::WebContents* old_contents,
+    content::WebContents* new_contents,
+    base::OnceCallback<void()> callback) {
+  DevToolsWindow* dev_tools_window =
+      DevToolsWindow::GetInstanceForInspectedWebContents(old_contents);
+  if (dev_tools_window) {
+    dev_tools_window->UpdateInspectedWebContents(new_contents,
+                                                 std::move(callback));
+  } else {
+    std::move(callback).Run();
+  }
+}
+
 std::unique_ptr<content::WebContents> Browser::SwapWebContents(
     content::WebContents* old_contents,
     std::unique_ptr<content::WebContents> new_contents) {
@@ -1506,11 +1520,6 @@
       new_view->TakeFallbackContentFrom(old_view);
   }
 
-  DevToolsWindow* dev_tools_window =
-      DevToolsWindow::GetInstanceForInspectedWebContents(old_contents);
-  if (dev_tools_window)
-    dev_tools_window->UpdateInspectedWebContents(new_contents.get());
-
   // TODO(crbug.com/836409): TabLoadTracker should not rely on being notified
   // directly about tab contents swaps.
   resource_coordinator::TabLoadTracker::Get()->SwapTabContents(
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 44b517f0..443a039 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -610,6 +610,10 @@
   std::unique_ptr<content::WebContents> ActivatePortalWebContents(
       content::WebContents* predecessor_contents,
       std::unique_ptr<content::WebContents> portal_contents) override;
+  void UpdateInspectedWebContentsIfNecessary(
+      content::WebContents* old_contents,
+      content::WebContents* new_contents,
+      base::OnceCallback<void()> callback) override;
   bool ShouldShowStaleContentOnEviction(content::WebContents* source) override;
   bool IsFrameLowPriority(
       const content::WebContents* web_contents,
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index e804f54..a3692e25 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -13,6 +13,7 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/password_manager/account_password_store_factory.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
@@ -36,6 +37,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/browsing_data/content/browsing_data_helper.h"
+#include "components/feature_engagement/public/tracker.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/form_saver_impl.h"
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
@@ -503,6 +505,19 @@
   }
   save_fallback_timer_.Stop();
   passwords_data_.form_manager()->Save();
+
+  if (base::FeatureList::IsEnabled(
+          password_manager::features::kEnablePasswordsAccountStorage)) {
+    // If we just saved a password to the account store, notify the IPH tracker
+    // about it (so it can decide not to show the IPH again).
+    if (GetPasswordFeatureManager()->GetDefaultPasswordStore() ==
+        autofill::PasswordForm::Store::kAccountStore) {
+      feature_engagement::TrackerFactory::GetForBrowserContext(
+          Profile::FromBrowserContext(web_contents()->GetBrowserContext()))
+          ->NotifyEvent("passwords_account_storage_used");
+    }
+  }
+
   if (base::FeatureList::IsEnabled(
           password_manager::features::kCompromisedPasswordsReengagement)) {
     post_save_compromised_helper_ =
diff --git a/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc b/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc
index f120248..9910433c 100644
--- a/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc
+++ b/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc
@@ -17,9 +17,9 @@
     content::WebContents* web_contents,
     const BubbleParameters& bubble_parameters,
     base::OnceCallback<void(bool)> callback) {
-  if (bubble_parameters.interception_type !=
-      DiceWebSigninInterceptor::SigninInterceptionType::kEnterprise) {
-    // Only the enterprise interception is currently implemented.
+  if (bubble_parameters.interception_type ==
+      DiceWebSigninInterceptor::SigninInterceptionType::kProfileSwitch) {
+    // The bubble for profile switch is not implemented.
     std::move(callback).Run(false);
     return;
   }
diff --git a/chrome/browser/ui/signin_reauth_view_controller.cc b/chrome/browser/ui/signin_reauth_view_controller.cc
index b5d08bc..fc0cf00 100644
--- a/chrome/browser/ui/signin_reauth_view_controller.cc
+++ b/chrome/browser/ui/signin_reauth_view_controller.cc
@@ -13,6 +13,7 @@
 #include "base/optional.h"
 #include "base/task_runner.h"
 #include "base/time/time.h"
+#include "chrome/browser/consent_auditor/consent_auditor_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/reauth_result.h"
 #include "chrome/browser/signin/reauth_tab_helper.h"
@@ -21,6 +22,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/webui/signin/signin_reauth_ui.h"
+#include "components/consent_auditor/consent_auditor.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/site_instance.h"
@@ -125,10 +127,15 @@
   CompleteReauth(signin::ReauthResult::kDismissedByUser);
 }
 
-void SigninReauthViewController::OnReauthConfirmed() {
+void SigninReauthViewController::OnReauthConfirmed(
+    sync_pb::UserConsentTypes::AccountPasswordsConsent consent) {
   if (user_confirmed_reauth_)
     return;
 
+  // Cache the consent. It will be actually recorded later, in CompleteReauth(),
+  // if the user successfully completed the reauth.
+  consent_ = consent;
+
   user_confirmed_reauth_ = true;
   user_confirmed_reauth_time_ = base::TimeTicks::Now();
   OnStateChanged();
@@ -219,6 +226,12 @@
     raw_reauth_web_contents_ = nullptr;
   }
 
+  if (result == signin::ReauthResult::kSuccess) {
+    CHECK(consent_.has_value());
+    ConsentAuditorFactory::GetForProfile(browser_->profile())
+        ->RecordAccountPasswordsConsent(account_id_, *consent_);
+  }
+
   signin_ui_util::RecordTransactionalReauthResult(access_point_, result);
   if (reauth_callback_)
     std::move(reauth_callback_).Run(result);
diff --git a/chrome/browser/ui/signin_reauth_view_controller.h b/chrome/browser/ui/signin_reauth_view_controller.h
index 3a52cc5..b256ea7 100644
--- a/chrome/browser/ui/signin_reauth_view_controller.h
+++ b/chrome/browser/ui/signin_reauth_view_controller.h
@@ -13,6 +13,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/ui/signin_view_controller_delegate.h"
 #include "components/signin/public/base/signin_metrics.h"
+#include "components/sync/protocol/user_consent_types.pb.h"
 #include "google_apis/gaia/core_account_id.h"
 
 class Browser;
@@ -134,7 +135,8 @@
   // Called when the user clicks the confirm button in the reauth confirmation
   // dialog.
   // This happens before the Gaia reauth page is shown.
-  void OnReauthConfirmed();
+  void OnReauthConfirmed(
+      sync_pb::UserConsentTypes::AccountPasswordsConsent consent);
   // Called when the user clicks the cancel button in the reauth confirmation
   // dialog.
   // This happens before the Gaia reauth page is shown.
@@ -199,6 +201,7 @@
 
   // The state of the reauth flow.
   bool user_confirmed_reauth_ = false;
+  base::Optional<sync_pb::UserConsentTypes::AccountPasswordsConsent> consent_;
   GaiaReauthPageState gaia_reauth_page_state_ = GaiaReauthPageState::kStarted;
   base::Optional<signin::ReauthResult> gaia_reauth_page_result_;
 
diff --git a/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc b/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
index 4d84afc..b0f0e5e6 100644
--- a/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
+++ b/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
@@ -300,6 +300,7 @@
 IN_PROC_BROWSER_TEST_F(SigninReauthViewControllerBrowserTest,
                        CancelReauthDialog) {
   ShowReauthPrompt();
+  RedirectGaiaChallengeTo(https_server()->GetURL(kReauthDonePath));
   ASSERT_TRUE(login_ui_test_utils::CancelReauthConfirmationDialog(
       browser(), kReauthDialogTimeout));
   EXPECT_EQ(WaitForReauthResult(), signin::ReauthResult::kDismissedByUser);
@@ -312,17 +313,26 @@
 IN_PROC_BROWSER_TEST_F(SigninReauthViewControllerBrowserTest,
                        GaiaChallengeLoadFailed) {
   ShowReauthPrompt();
-  ASSERT_TRUE(login_ui_test_utils::ConfirmReauthConfirmationDialog(
-      browser(), kReauthDialogTimeout));
+
+  // Make the Gaia page fail to load.
   const GURL target_url = https_server()->GetURL("/close-socket");
   content::TestNavigationObserver target_content_observer(target_url);
   target_content_observer.WatchExistingWebContents();
   RedirectGaiaChallengeTo(target_url);
   target_content_observer.Wait();
+
   EXPECT_TRUE(browser()->signin_view_controller()->ShowsModalDialog());
   EXPECT_FALSE(target_content_observer.last_navigation_succeeded());
 
-  // Check that |kLoadFailed| is returned as the result.
+  // Now confirm the pre-reauth confirmation dialog, and wait for the Gaia page
+  // (an error page in this case) to show up.
+  ReauthTestObserver reauth_observer(signin_reauth_view_controller());
+  ASSERT_TRUE(login_ui_test_utils::ConfirmReauthConfirmationDialog(
+      browser(), kReauthDialogTimeout));
+  reauth_observer.WaitUntilGaiaReauthPageIsShown();
+
+  // Close the modal dialog and check that |kLoadFailed| is returned as the
+  // result.
   SimulateCloseButtonClick();
   EXPECT_EQ(WaitForReauthResult(), signin::ReauthResult::kLoadFailed);
   EXPECT_THAT(
@@ -337,7 +347,7 @@
 // Tests clicking on the confirm button in the reauth dialog. Reauth completes
 // before the confirmation.
 IN_PROC_BROWSER_TEST_F(SigninReauthViewControllerBrowserTest,
-                       ConfirmReauthDialog_AfterReauthSuccess) {
+                       ConfirmReauthDialog) {
   ShowReauthPrompt();
   RedirectGaiaChallengeTo(https_server()->GetURL(kReauthDonePath));
   ASSERT_TRUE(login_ui_test_utils::ConfirmReauthConfirmationDialog(
@@ -355,27 +365,6 @@
       kReauthGaiaNavigationDurationFromConfirmClickHistogramName, 1);
 }
 
-// Tests clicking on the confirm button in the reauth dialog. Reauth completes
-// after the confirmation.
-IN_PROC_BROWSER_TEST_F(SigninReauthViewControllerBrowserTest,
-                       ConfirmReauthDialog_BeforeReauthSuccess) {
-  ShowReauthPrompt();
-  ASSERT_TRUE(login_ui_test_utils::ConfirmReauthConfirmationDialog(
-      browser(), kReauthDialogTimeout));
-  RedirectGaiaChallengeTo(https_server()->GetURL(kReauthDonePath));
-  EXPECT_EQ(WaitForReauthResult(), signin::ReauthResult::kSuccess);
-  histogram_tester()->ExpectUniqueSample(
-      kReauthUserActionHistogramName,
-      SigninReauthViewController::UserAction::kClickConfirmButton, 1);
-  histogram_tester()->ExpectUniqueSample(
-      kReauthUserActionToFillPasswordHistogramName,
-      SigninReauthViewController::UserAction::kClickConfirmButton, 1);
-  histogram_tester()->ExpectTotalCount(
-      kReauthGaiaNavigationDurationFromReauthStartHistogramName, 1);
-  histogram_tester()->ExpectTotalCount(
-      kReauthGaiaNavigationDurationFromConfirmClickHistogramName, 1);
-}
-
 // Tests completing the Gaia reauth challenge in a dialog.
 IN_PROC_BROWSER_TEST_F(SigninReauthViewControllerBrowserTest,
                        CompleteReauthInDialog) {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
index 8b490f8..59d5eeb 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -3264,7 +3264,8 @@
 // Tests that in the reveal-on-hover field trial variation (without
 // hide-on-interaction), the path is faded back in after focus, then blur, then
 // hover.
-TEST_P(OmniboxViewViewsRevealOnHoverTest, AfterBlur) {
+// TODO(crbug.com/1115551): Test is flaky.
+TEST_P(OmniboxViewViewsRevealOnHoverTest, DISABLED_AfterBlur) {
   SetUpSimplifiedDomainTest();
 
   // Focus and blur the omnibox, then hover over it. The URL should unelide.
diff --git a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
index 588de90..e548ee4 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
@@ -505,12 +505,21 @@
 
 void PasswordSaveUpdateWithAccountStoreView::OnPerformAction(
     views::Combobox* combobox) {
-  controller_.OnToggleAccountStore(
-      /*is_account_store_selected=*/combobox->GetSelectedIndex() == 0);
+  bool is_account_store_selected = combobox->GetSelectedIndex() == 0;
+  controller_.OnToggleAccountStore(is_account_store_selected);
+  // If the user explicitly switched to "save on this device only", record this
+  // with the IPH tracker (so it can decide not to show the IPH again).
+  if (!is_account_store_selected) {
+    if (!iph_tracker_) {
+      iph_tracker_ = feature_engagement::TrackerFactory::GetForBrowserContext(
+          controller_.GetProfile());
+    }
+    iph_tracker_->NotifyEvent("passwords_account_storage_unselected");
+  }
   // The IPH shown upon failure in reauth is used to informs the user that the
   // password will be stored on device. This is why it's important to close it
   // if the user changes the destination to account.
-  if (currenly_shown_iph_type == IPHType::kFailedReauth)
+  if (currenly_shown_iph_type_ == IPHType::kFailedReauth)
     CloseIPHBubbleIfOpen();
 }
 
@@ -521,12 +530,12 @@
     observed_account_storage_promo_.Remove(widget);
     // If the reauth failed, we have shown the IPH unconditionally. No need to
     // inform the tracker. Only regular IPH's are tracked
-    if (currenly_shown_iph_type == IPHType::kRegular) {
+    if (currenly_shown_iph_type_ == IPHType::kRegular) {
       DCHECK(iph_tracker_);
       iph_tracker_->Dismissed(
           feature_engagement::kIPHPasswordsAccountStorageFeature);
     }
-    currenly_shown_iph_type = IPHType::kNone;
+    currenly_shown_iph_type_ = IPHType::kNone;
     account_storage_promo_ = nullptr;
   }
 }
@@ -730,7 +739,7 @@
   set_close_on_deactivate(close_save_bubble_on_deactivate_original_value);
   observed_account_storage_promo_.Add(account_storage_promo_->GetWidget());
 
-  currenly_shown_iph_type = type;
+  currenly_shown_iph_type_ = type;
 }
 
 void PasswordSaveUpdateWithAccountStoreView::CloseIPHBubbleIfOpen() {
diff --git a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.h b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.h
index 4ed037c..a2154ac 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.h
+++ b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.h
@@ -132,7 +132,7 @@
   // its NativeWidget.
   FeaturePromoBubbleView* account_storage_promo_ = nullptr;
 
-  IPHType currenly_shown_iph_type = IPHType::kNone;
+  IPHType currenly_shown_iph_type_ = IPHType::kNone;
 
   // Observes the |account_storage_promo_|'s Widget.  Used to tell whether the
   // promo is open and get called back when it closes.
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
index 9ad892d6..7cd2eb1f 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -10,21 +10,14 @@
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/payments/ssl_validity_checker.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/omnibox/omnibox_theme.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/omnibox/browser/location_bar_model_util.h"
 #include "components/payments/content/icon/icon_size.h"
-#include "components/payments/core/features.h"
 #include "components/payments/core/native_error_strings.h"
-#include "components/payments/core/payments_experimental_features.h"
 #include "components/payments/core/url_util.h"
-#include "components/security_state/core/security_state.h"
-#include "components/vector_icons/vector_icons.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
 #include "content/public/browser/navigation_handle.h"
@@ -36,7 +29,6 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/image/image_skia.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"
@@ -71,8 +63,6 @@
   ReadOnlyOriginView(const base::string16& page_title,
                      const GURL& origin,
                      const SkBitmap* icon_bitmap,
-                     Profile* profile,
-                     security_state::SecurityLevel security_level,
                      SkColor background_color,
                      views::ButtonListener* site_settings_listener) {
     auto title_origin_container = std::make_unique<views::View>();
@@ -100,30 +90,8 @@
       title_label->SetEnabledColor(foreground);
     }
 
-    auto origin_container = std::make_unique<views::View>();
-    views::GridLayout* origin_layout = origin_container->SetLayoutManager(
-        std::make_unique<views::GridLayout>());
-
-    columns = origin_layout->AddColumnSet(0);
-    columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
-                       1.0, views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
-    columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
-                       1.0, views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
-    origin_layout->StartRow(views::GridLayout::kFixedSize, 0);
-    if (PaymentsExperimentalFeatures::IsEnabled(
-            features::kPaymentHandlerSecurityIcon)) {
-      // TODO(https://crbug.com/1052493):
-      // Selecting the correct icon based on the SSL certificate state
-      // and adding test coverage for this code path.
-      auto security_icon = std::make_unique<views::ImageView>();
-      const ui::ThemeProvider& theme_provider =
-          ThemeService::GetThemeProviderForProfile(profile);
-      security_icon->SetImage(gfx::CreateVectorIcon(
-          location_bar_model::GetSecurityVectorIcon(security_level), 16,
-          GetOmniboxSecurityChipColor(&theme_provider, security_level)));
-      origin_layout->AddView(std::move(security_icon));
-    }
-    auto* origin_label = origin_layout->AddView(
+    title_origin_layout->StartRow(views::GridLayout::kFixedSize, 0);
+    auto* origin_label = title_origin_layout->AddView(
         std::make_unique<views::Label>(base::UTF8ToUTF16(origin.host())));
     origin_label->SetElideBehavior(gfx::ELIDE_HEAD);
     if (!title_is_valid) {
@@ -140,8 +108,6 @@
     origin_label->SetAutoColorReadabilityEnabled(false);
     origin_label->SetEnabledColor(foreground);
     origin_label->SetBackgroundColor(background_color);
-    title_origin_layout->StartRow(views::GridLayout::kFixedSize, 0);
-    title_origin_layout->AddView(std::move(origin_container));
 
     views::GridLayout* top_level_layout =
         SetLayoutManager(std::make_unique<views::GridLayout>());
@@ -272,10 +238,7 @@
       GetHeaderBackground(header_view);
   return std::make_unique<ReadOnlyOriginView>(
       GetPaymentHandlerDialogTitle(web_contents()), origin,
-      state()->selected_app()->icon_bitmap(), profile_,
-      web_contents() ? SslValidityChecker::GetSecurityLevel(web_contents())
-                     : security_state::NONE,
-      background->get_color(), this);
+      state()->selected_app()->icon_bitmap(), background->get_color(), this);
 }
 
 std::unique_ptr<views::Background>
@@ -304,11 +267,8 @@
 void PaymentHandlerWebFlowViewController::VisibleSecurityStateChanged(
     content::WebContents* source) {
   DCHECK_EQ(source, web_contents());
-  if (!SslValidityChecker::IsValidPageInPaymentHandlerWindow(source)) {
+  if (!SslValidityChecker::IsValidPageInPaymentHandlerWindow(source))
     AbortPayment();
-  } else {
-    UpdateHeaderView();
-  }
 }
 
 void PaymentHandlerWebFlowViewController::DidStartNavigation(
diff --git a/chrome/browser/ui/views/profiles/incognito_menu_view.cc b/chrome/browser/ui/views/profiles/incognito_menu_view.cc
index f4eac332..67d7d51f 100644
--- a/chrome/browser/ui/views/profiles/incognito_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/incognito_menu_view.cc
@@ -26,6 +26,12 @@
 #include "ui/views/controls/image_view.h"
 #include "ui/views/style/typography.h"
 
+#if defined(OS_WIN)
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/chrome_features.h"
+#endif
+
 IncognitoMenuView::IncognitoMenuView(views::Button* anchor_button,
                                      Browser* browser)
     : ProfileMenuViewBase(anchor_button, browser) {
@@ -57,6 +63,19 @@
                                              incognito_window_count)
           : base::string16());
 
+#if defined(OS_WIN)
+  if (ProfileShortcutManager::IsFeatureEnabled() &&
+      base::FeatureList::IsEnabled(
+          features::kEnableIncognitoShortcutOnDesktop)) {
+    // TODO(crbug.com/1113162): Add desktop shortcut icon to the menu entry.
+    AddFeatureButton(
+        l10n_util::GetStringUTF16(
+            IDS_INCOGNITO_PROFILE_MENU_CREATE_SHORTCUT_BUTTON),
+        base::BindRepeating(&IncognitoMenuView::OnCreateShortcutButtonClicked,
+                            base::Unretained(this)));
+  }
+#endif
+
   AddFeatureButton(
       l10n_util::GetStringUTF16(IDS_INCOGNITO_PROFILE_MENU_CLOSE_BUTTON),
       base::BindRepeating(&IncognitoMenuView::OnExitButtonClicked,
@@ -71,6 +90,19 @@
           browser()->profile()));
 }
 
+#if defined(OS_WIN)
+void IncognitoMenuView::OnCreateShortcutButtonClicked() {
+  RecordClick(ActionableItem::kCreateIncognitoShortcutButton);
+  ProfileShortcutManager* shortcut_manager =
+      g_browser_process->profile_manager()->profile_shortcut_manager();
+
+  DCHECK(shortcut_manager);
+  if (shortcut_manager)
+    shortcut_manager->CreateIncognitoProfileShortcut(
+        browser()->profile()->GetPath());
+}
+#endif
+
 void IncognitoMenuView::OnExitButtonClicked() {
   RecordClick(ActionableItem::kExitProfileButton);
   base::RecordAction(base::UserMetricsAction("IncognitoMenu_ExitClicked"));
diff --git a/chrome/browser/ui/views/profiles/incognito_menu_view.h b/chrome/browser/ui/views/profiles/incognito_menu_view.h
index 92dc032..60c25ba 100644
--- a/chrome/browser/ui/views/profiles/incognito_menu_view.h
+++ b/chrome/browser/ui/views/profiles/incognito_menu_view.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/views/profiles/profile_menu_view_base.h"
 
 namespace views {
@@ -34,6 +35,9 @@
   base::string16 GetAccessibleWindowTitle() const override;
 
   // Button actions.
+#if defined(OS_WIN)
+  void OnCreateShortcutButtonClicked();
+#endif
   void OnExitButtonClicked();
 
   DISALLOW_COPY_AND_ASSIGN(IncognitoMenuView);
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.h b/chrome/browser/ui/views/profiles/profile_menu_view_base.h
index c2641d7..045c45d8 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.h
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.h
@@ -63,7 +63,8 @@
     kAddNewProfileButton = 15,
     kSyncSettingsButton = 16,
     kEditProfileButton = 17,
-    kMaxValue = kEditProfileButton,
+    kCreateIncognitoShortcutButton = 18,
+    kMaxValue = kCreateIncognitoShortcutButton,
   };
 
   enum class SyncInfoContainerBackgroundState {
diff --git a/chrome/browser/ui/views/read_later/read_later_bubble_view_browsertest.cc b/chrome/browser/ui/views/read_later/read_later_bubble_view_browsertest.cc
index 872b2cf4..4de2c36 100644
--- a/chrome/browser/ui/views/read_later/read_later_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/read_later/read_later_bubble_view_browsertest.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -54,6 +55,12 @@
   DISALLOW_COPY_AND_ASSIGN(ReadLaterBubbleViewBrowserTest);
 };
 
-IN_PROC_BROWSER_TEST_F(ReadLaterBubbleViewBrowserTest, InvokeUi_default) {
+// TODO(1115950): Flaky on Windows.
+#if defined(OS_WIN)
+#define MAYBE_InvokeUi_default DISABLED_InvokeUi_default
+#else
+#define MAYBE_InvokeUi_default InvokeUi_default
+#endif
+IN_PROC_BROWSER_TEST_F(ReadLaterBubbleViewBrowserTest, MAYBE_InvokeUi_default) {
   ShowAndVerifyUi();
 }
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index dd9f6d9..4926ee5 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -711,6 +711,9 @@
 }
 
 void TabHoverCardBubbleView::FadeInToShow() {
+  // Make sure the hover card isn't accidentally shown if the anchor is gone.
+  if (!GetAnchorView())
+    return;
   fade_animation_delegate_->FadeIn();
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.cc b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
index f70e6c8..435db0d7 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
@@ -48,7 +48,8 @@
 bool WebAppBrowserController::HasMinimalUiButtons() const {
   if (has_tab_strip())
     return false;
-  DisplayMode app_display_mode = registrar().GetAppDisplayMode(GetAppId());
+  DisplayMode app_display_mode =
+      registrar().GetEffectiveDisplayModeFromManifest(GetAppId());
   return app_display_mode == DisplayMode::kBrowser ||
          app_display_mode == DisplayMode::kMinimalUi;
 }
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 1a7c7d27..0df6439 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -295,6 +295,8 @@
                           /*open_as_window=*/true));
   EXPECT_FALSE(has_buttons(DisplayMode::kStandalone,
                            /*open_as_window=*/true));
+  EXPECT_FALSE(has_buttons(DisplayMode::kFullscreen,
+                           /*open_as_window=*/true));
 
   EXPECT_TRUE(has_buttons(DisplayMode::kBrowser,
                           /*open_as_window=*/false));
@@ -302,6 +304,8 @@
                           /*open_as_window=*/false));
   EXPECT_FALSE(has_buttons(DisplayMode::kStandalone,
                            /*open_as_window=*/false));
+  EXPECT_FALSE(has_buttons(DisplayMode::kFullscreen,
+                           /*open_as_window=*/false));
 }
 
 IN_PROC_BROWSER_TEST_P(WebAppBrowserTest_DisplayOverride, DisplayOverride) {
@@ -321,6 +325,63 @@
   EXPECT_EQ(DisplayMode::kStandalone, app_display_mode_override[1]);
 }
 
+IN_PROC_BROWSER_TEST_P(WebAppBrowserTest_DisplayOverride, HasMinimalUiButtons) {
+  int index = 0;
+  auto has_buttons = [this, &index](DisplayMode display_override_mode,
+                                    bool open_as_window) -> bool {
+    base::HistogramTester tester;
+    const std::string base_url = "https://example.com/path";
+    auto web_app_info = std::make_unique<WebApplicationInfo>();
+    web_app_info->app_url = GURL(base_url + base::NumberToString(index++));
+    web_app_info->scope = web_app_info->app_url;
+
+    DisplayMode display_mode = DisplayMode::kStandalone;
+
+    if (display_override_mode == DisplayMode::kFullscreen ||
+        display_override_mode == DisplayMode::kStandalone) {
+      display_mode = DisplayMode::kMinimalUi;
+    }
+
+    web_app_info->display_mode = display_mode;
+    web_app_info->open_as_window = open_as_window;
+    web_app_info->display_override.push_back(display_override_mode);
+
+    AppId app_id = InstallWebApp(std::move(web_app_info));
+    Browser* app_browser = LaunchWebAppBrowser(app_id);
+    DCHECK(app_browser->app_controller());
+    tester.ExpectUniqueSample(kLaunchWebAppDisplayModeHistogram, display_mode,
+                              1);
+
+    bool matches;
+    EXPECT_TRUE(ExecuteScriptAndExtractBool(
+        app_browser->tab_strip_model()->GetActiveWebContents(),
+        "window.domAutomationController.send(window.matchMedia('(display-mode: "
+        "minimal-ui)').matches)",
+        &matches));
+    EXPECT_EQ(app_browser->app_controller()->HasMinimalUiButtons(), matches);
+
+    return matches;
+  };
+
+  EXPECT_TRUE(has_buttons(DisplayMode::kBrowser,
+                          /*open_as_window=*/true));
+  EXPECT_TRUE(has_buttons(DisplayMode::kMinimalUi,
+                          /*open_as_window=*/true));
+  EXPECT_FALSE(has_buttons(DisplayMode::kStandalone,
+                           /*open_as_window=*/true));
+  EXPECT_FALSE(has_buttons(DisplayMode::kFullscreen,
+                           /*open_as_window=*/true));
+
+  EXPECT_TRUE(has_buttons(DisplayMode::kBrowser,
+                          /*open_as_window=*/false));
+  EXPECT_TRUE(has_buttons(DisplayMode::kMinimalUi,
+                          /*open_as_window=*/false));
+  EXPECT_FALSE(has_buttons(DisplayMode::kStandalone,
+                           /*open_as_window=*/false));
+  EXPECT_FALSE(has_buttons(DisplayMode::kFullscreen,
+                           /*open_as_window=*/false));
+}
+
 // Tests that desktop PWAs open links in the browser.
 IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, DesktopPWAsOpenLinksInApp) {
   const GURL app_url = GetSecureAppURL();
diff --git a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
index ead15df7..ea640407 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
@@ -62,6 +62,10 @@
   CallJS("login.UpdateScreen.setUpdateCompleted", value);
 }
 
+void UpdateScreenHandler::SetManualRebootNeeded(bool value) {
+  CallJS("login.UpdateScreen.setManualRebootNeeded", value);
+}
+
 void UpdateScreenHandler::SetShowCurtain(bool value) {
   CallJS("login.UpdateScreen.showUpdateCurtain", value);
 }
@@ -92,6 +96,7 @@
   builder->AddF("installingUpdateDesc", IDS_UPDATE_MSG,
                 ui::GetChromeOSDeviceName());
   builder->Add("updateCompeletedMsg", IDS_UPDATE_COMPLETED);
+  builder->Add("updateCompeletedRebootingMsg", IDS_UPDATE_COMPLETED_REBOOTING);
   builder->Add("updateScreenAccessibleTitle",
                IDS_UPDATE_SCREEN_ACCESSIBLE_TITLE);
   builder->Add("checkingForUpdates", IDS_CHECKING_FOR_UPDATES);
diff --git a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
index 9c1bc3c..ed5c44c 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
@@ -39,6 +39,7 @@
   virtual void SetEstimatedTimeLeft(int value) = 0;
   virtual void SetShowEstimatedTimeLeft(bool value) = 0;
   virtual void SetUpdateCompleted(bool value) = 0;
+  virtual void SetManualRebootNeeded(bool value) = 0;
   virtual void SetShowCurtain(bool value) = 0;
   virtual void SetProgressMessage(const base::string16& value) = 0;
   virtual void SetProgress(int value) = 0;
@@ -63,6 +64,7 @@
   void SetEstimatedTimeLeft(int value) override;
   void SetShowEstimatedTimeLeft(bool value) override;
   void SetUpdateCompleted(bool value) override;
+  void SetManualRebootNeeded(bool value) override;
   void SetShowCurtain(bool value) override;
   void SetProgressMessage(const base::string16& value) override;
   void SetProgress(int value) override;
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler.cc b/chrome/browser/ui/webui/settings/safety_check_handler.cc
index 9a5cb44..0ed79e3 100644
--- a/chrome/browser/ui/webui/settings/safety_check_handler.cc
+++ b/chrome/browser/ui/webui/settings/safety_check_handler.cc
@@ -515,6 +515,9 @@
     case SafeBrowsingStatus::kDisabledByExtension:
       return l10n_util::GetStringUTF16(
           IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_DISABLED_BY_EXTENSION);
+    case SafeBrowsingStatus::kEnabledStandardAvailableEnhanced:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD_AVAILABLE_ENHANCED);
   }
 }
 
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
index 3d9194c..aba8ab6 100644
--- a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
@@ -518,12 +518,11 @@
 }
 
 TEST_F(SafetyCheckHandlerTest, CheckSafeBrowsing_EnabledStandard) {
-  Profile::FromWebUI(&test_web_ui_)
-      ->GetPrefs()
-      ->SetBoolean(prefs::kSafeBrowsingEnabled, true);
-  Profile::FromWebUI(&test_web_ui_)
-      ->GetPrefs()
-      ->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
+  TestingProfile::FromWebUI(&test_web_ui_)
+      ->AsTestingProfile()
+      ->GetTestingPrefService()
+      ->SetManagedPref(prefs::kSafeBrowsingEnabled,
+                       std::make_unique<base::Value>(true));
   safety_check_->PerformSafetyCheck();
   const base::DictionaryValue* event =
       GetSafetyCheckStatusChangedWithDataIfExists(
@@ -537,6 +536,30 @@
       SafetyCheckHandler::SafeBrowsingStatus::kEnabledStandard, 1);
 }
 
+TEST_F(SafetyCheckHandlerTest,
+       CheckSafeBrowsing_EnabledStandardAvailableEnhanced) {
+  Profile::FromWebUI(&test_web_ui_)
+      ->GetPrefs()
+      ->SetBoolean(prefs::kSafeBrowsingEnabled, true);
+  Profile::FromWebUI(&test_web_ui_)
+      ->GetPrefs()
+      ->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
+  safety_check_->PerformSafetyCheck();
+  const base::DictionaryValue* event =
+      GetSafetyCheckStatusChangedWithDataIfExists(
+          kSafeBrowsing,
+          static_cast<int>(SafetyCheckHandler::SafeBrowsingStatus::
+                               kEnabledStandardAvailableEnhanced));
+  ASSERT_TRUE(event);
+  VerifyDisplayString(event,
+                      "Standard protection is on. For even more security, use "
+                      "enhanced protection.");
+  histogram_tester_.ExpectBucketCount(
+      "Settings.SafetyCheck.SafeBrowsingResult",
+      SafetyCheckHandler::SafeBrowsingStatus::kEnabledStandardAvailableEnhanced,
+      1);
+}
+
 TEST_F(SafetyCheckHandlerTest, CheckSafeBrowsing_EnabledEnhanced) {
   Profile::FromWebUI(&test_web_ui_)
       ->GetPrefs()
diff --git a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc
index c418b7e..86f687d 100644
--- a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc
+++ b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc
@@ -371,6 +371,7 @@
   dice_signed_in_profile_creator_ =
       std::make_unique<DiceSignedInProfileCreator>(
           profile_, account_info_.account_id,
+          /*local_profile_name=*/std::string(),
           base::BindOnce(&DiceTurnSyncOnHelper::OnNewSignedInProfileCreated,
                          base::Unretained(this)));
 }
diff --git a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
index 8d8acc2..66beae0 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -254,10 +254,12 @@
       "  window.domAutomationController.send('DocumentNotReady');"
       "} else if (%s == null) {"
       "  window.domAutomationController.send('NotFound');"
+      "} else if (%s.hidden) {"
+      "  window.domAutomationController.send('Hidden');"
       "} else {"
       "  window.domAutomationController.send('Ok');"
       "}",
-      element_selector.c_str());
+      element_selector.c_str(), element_selector.c_str());
   EXPECT_TRUE(content::ExecuteScriptAndExtractString(
       web_contents, find_element_js, &message));
   return message == "Ok";
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
index 71750b6..76d802e 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -18,10 +18,12 @@
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/profile_picker.h"
 #include "chrome/browser/ui/webui/profile_helper.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/search/generated_colors_info.h"
+#include "chrome/common/webui_url_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/gfx/color_utils.h"
@@ -45,7 +47,11 @@
   web_ui()->RegisterMessageCallback(
       "launchSelectedProfile",
       base::BindRepeating(&ProfilePickerHandler::HandleLaunchSelectedProfile,
-                          base::Unretained(this)));
+                          base::Unretained(this), /*open_settings=*/false));
+  web_ui()->RegisterMessageCallback(
+      "openManageProfileSettingsSubPage",
+      base::BindRepeating(&ProfilePickerHandler::HandleLaunchSelectedProfile,
+                          base::Unretained(this), /*open_settings=*/true));
   web_ui()->RegisterMessageCallback(
       "launchGuestProfile",
       base::BindRepeating(&ProfilePickerHandler::HandleLaunchGuestProfile,
@@ -92,6 +98,7 @@
 }
 
 void ProfilePickerHandler::HandleLaunchSelectedProfile(
+    bool open_settings,
     const base::ListValue* args) {
   const base::Value* profile_path_value = nullptr;
   if (!args->Get(0, &profile_path_value))
@@ -119,8 +126,8 @@
 
   profiles::SwitchToProfile(
       *profile_path, /*always_create=*/false,
-      base::Bind(&ProfilePickerHandler::OnSwitchToProfileComplete,
-                 weak_factory_.GetWeakPtr()));
+      base::BindRepeating(&ProfilePickerHandler::OnSwitchToProfileComplete,
+                          weak_factory_.GetWeakPtr(), open_settings));
 }
 
 void ProfilePickerHandler::HandleLaunchGuestProfile(
@@ -129,7 +136,7 @@
   // checking has been added to the UI.
   profiles::SwitchToGuestProfile(
       base::Bind(&ProfilePickerHandler::OnSwitchToProfileComplete,
-                 weak_factory_.GetWeakPtr()));
+                 weak_factory_.GetWeakPtr(), false));
 }
 
 void ProfilePickerHandler::HandleAskOnStartupChanged(
@@ -232,11 +239,15 @@
 }
 
 void ProfilePickerHandler::OnSwitchToProfileComplete(
+    bool open_settings,
     Profile* profile,
     Profile::CreateStatus profile_create_status) {
   Browser* browser = chrome::FindAnyBrowser(profile, false);
   DCHECK(browser);
   DCHECK(browser->window());
+  if (open_settings) {
+    chrome::ShowSettingsSubPage(browser, chrome::kManageProfileSubPage);
+  }
   ProfilePicker::Hide();
 }
 
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.h b/chrome/browser/ui/webui/signin/profile_picker_handler.h
index d85d7f5..68c66fd 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.h
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.h
@@ -26,7 +26,8 @@
 
  private:
   void HandleMainViewInitialize(const base::ListValue* args);
-  void HandleLaunchSelectedProfile(const base::ListValue* args);
+  void HandleLaunchSelectedProfile(bool open_settings,
+                                   const base::ListValue* args);
   void HandleLaunchGuestProfile(const base::ListValue* args);
   void HandleAskOnStartupChanged(const base::ListValue* args);
   void HandleGetNewProfileSuggestedThemeInfo(const base::ListValue* args);
@@ -37,7 +38,8 @@
   void GatherProfileStatistics(Profile* profile);
   void OnProfileStatisticsReceived(base::FilePath profile_path,
                                    profiles::ProfileCategoryStats result);
-  void OnSwitchToProfileComplete(Profile* profile,
+  void OnSwitchToProfileComplete(bool open_settings,
+                                 Profile* profile,
                                  Profile::CreateStatus profile_create_status);
   void PushProfilesList();
   base::Value GetProfilesList();
diff --git a/chrome/browser/ui/webui/signin/profile_picker_ui.cc b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
index 936ea7e..18b074e 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_ui.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
@@ -7,6 +7,7 @@
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_util.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/signin/profile_picker_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
@@ -22,6 +23,17 @@
 #include "ui/base/webui/web_ui_util.h"
 
 namespace {
+bool IsProfileCreationAllowed() {
+  PrefService* service = g_browser_process->local_state();
+  DCHECK(service);
+  return service->GetBoolean(prefs::kBrowserAddPersonEnabled);
+}
+
+bool IsGuestModeEnabled() {
+  PrefService* service = g_browser_process->local_state();
+  DCHECK(service);
+  return service->GetBoolean(prefs::kBrowserGuestModeEnabled);
+}
 
 void AddStrings(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
@@ -33,6 +45,8 @@
       {"menu", IDS_MENU},
       {"profileMenuName", IDS_PROFILE_PICKER_PROFILE_MENU_BUTTON_NAME},
       {"profileMenuRemoveText", IDS_PROFILE_PICKER_PROFILE_MENU_REMOVE_TEXT},
+      {"profileMenuCustomizeText",
+       IDS_PROFILE_PICKER_PROFILE_MENU_CUSTOMIZE_TEXT},
       {"removeWarningLocalProfile",
        IDS_PROFILE_PICKER_REMOVE_WARNING_LOCAL_PROFILE},
       {"removeWarningSignedInProfile",
@@ -58,8 +72,16 @@
                           g_browser_process->local_state()->GetBoolean(
                               prefs::kBrowserShowProfilePickerOnStartup));
   html_source->AddBoolean(
-      "signInProfileCreationFlow",
+      "signInProfileCreationFlowSupported",
       base::FeatureList::IsEnabled(features::kSignInProfileCreationFlow));
+
+  // Add policies.
+  html_source->AddBoolean("isForceSigninEnabled",
+                          signin_util::IsForceSigninEnabled());
+  html_source->AddBoolean("isGuestModeEnabled", IsGuestModeEnabled());
+  html_source->AddBoolean("isProfileCreationAllowed",
+                          IsProfileCreationAllowed());
+  // TODO(crbug.com/1063856): Check if |BrowserSignin| device policy exists.
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/signin/signin_reauth_handler.cc b/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
index b56edbb..3b127dc 100644
--- a/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
+++ b/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
@@ -4,13 +4,20 @@
 
 #include "chrome/browser/ui/webui/signin/signin_reauth_handler.h"
 
+#include <vector>
+
 #include "base/bind.h"
+#include "base/logging.h"
 #include "chrome/browser/ui/signin_reauth_view_controller.h"
+#include "components/sync/protocol/user_consent_types.pb.h"
 #include "content/public/browser/web_ui.h"
 #include "ui/base/webui/web_ui_util.h"
 
-SigninReauthHandler::SigninReauthHandler(SigninReauthViewController* controller)
-    : controller_(controller) {
+SigninReauthHandler::SigninReauthHandler(
+    SigninReauthViewController* controller,
+    base::flat_map<std::string, int> string_to_grd_id_map)
+    : controller_(controller),
+      string_to_grd_id_map_(std::move(string_to_grd_id_map)) {
   DCHECK(controller_);
   controller_observer_.Add(controller_);
 }
@@ -63,10 +70,44 @@
 
 void SigninReauthHandler::HandleConfirm(const base::ListValue* args) {
   if (controller_)
-    controller_->OnReauthConfirmed();
+    controller_->OnReauthConfirmed(BuildConsent(args));
 }
 
 void SigninReauthHandler::HandleCancel(const base::ListValue* args) {
   if (controller_)
     controller_->OnReauthDismissed();
 }
+
+sync_pb::UserConsentTypes::AccountPasswordsConsent
+SigninReauthHandler::BuildConsent(const base::ListValue* args) const {
+  CHECK_EQ(2U, args->GetSize());
+  base::Value::ConstListView consent_description = args->GetList()[0].GetList();
+  const std::string& consent_confirmation = args->GetList()[1].GetString();
+
+  // The strings returned by the WebUI are not free-form, they must belong into
+  // a pre-determined set of strings (stored in |string_to_grd_id_map_|). As
+  // this has privacy and legal implications, CHECK the integrity of the strings
+  // received from the renderer process before recording the consent.
+  std::vector<int> consent_description_ids;
+  for (const base::Value& description : consent_description) {
+    auto iter = string_to_grd_id_map_.find(description.GetString());
+    CHECK(iter != string_to_grd_id_map_.end()) << "Unexpected string:\n"
+                                               << description.GetString();
+    consent_description_ids.push_back(iter->second);
+  }
+
+  auto iter = string_to_grd_id_map_.find(consent_confirmation);
+  CHECK(iter != string_to_grd_id_map_.end()) << "Unexpected string:\n"
+                                             << consent_confirmation;
+  int consent_confirmation_id = iter->second;
+
+  sync_pb::UserConsentTypes::AccountPasswordsConsent consent;
+  consent.set_confirmation_grd_id(consent_confirmation_id);
+  for (int id : consent_description_ids) {
+    consent.add_description_grd_ids(id);
+  }
+  consent.set_status(sync_pb::UserConsentTypes::ConsentStatus::
+                         UserConsentTypes_ConsentStatus_GIVEN);
+
+  return consent;
+}
diff --git a/chrome/browser/ui/webui/signin/signin_reauth_handler.h b/chrome/browser/ui/webui/signin/signin_reauth_handler.h
index 2ab5bd48..0e4912de 100644
--- a/chrome/browser/ui/webui/signin/signin_reauth_handler.h
+++ b/chrome/browser/ui/webui/signin/signin_reauth_handler.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_REAUTH_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_REAUTH_HANDLER_H_
 
+#include "base/containers/flat_map.h"
 #include "chrome/browser/ui/signin_reauth_view_controller.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
@@ -17,7 +18,8 @@
                             public SigninReauthViewController::Observer {
  public:
   // Creates a SigninReauthHandler for the |controller|.
-  explicit SigninReauthHandler(SigninReauthViewController* controller);
+  SigninReauthHandler(SigninReauthViewController* controller,
+                      base::flat_map<std::string, int> string_to_grd_id_map);
   ~SigninReauthHandler() override;
 
   SigninReauthHandler(const SigninReauthHandler&) = delete;
@@ -46,12 +48,19 @@
   virtual void HandleCancel(const base::ListValue* args);
 
  private:
+  sync_pb::UserConsentTypes::AccountPasswordsConsent BuildConsent(
+      const base::ListValue* args) const;
+
   // May be null if |controller_| gets destroyed earlier than |this|.
   SigninReauthViewController* controller_;
 
   ScopedObserver<SigninReauthViewController,
                  SigninReauthViewController::Observer>
       controller_observer_{this};
+
+  // Mapping between strings displayed in the UI corresponding to this handler
+  // and their respective GRD IDs.
+  base::flat_map<std::string, int> string_to_grd_id_map_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_REAUTH_HANDLER_H_
diff --git a/chrome/browser/ui/webui/signin/signin_reauth_ui.cc b/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
index b455aac6..a102575 100644
--- a/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
+++ b/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/check.h"
+#include "base/containers/flat_map.h"
 #include "base/optional.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
@@ -24,6 +25,7 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "google_apis/gaia/core_account_id.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/gfx/image/image.h"
 #include "ui/resources/grit/webui_resources.h"
@@ -114,16 +116,16 @@
   source->AddResourcePath(
       "images/signin_reauth_illustration_dark.svg",
       IDR_SIGNIN_REAUTH_IMAGES_ACCOUNT_PASSWORDS_REAUTH_ILLUSTRATION_DARK_SVG);
-  source->AddLocalizedString("signinReauthTitle",
-                             GetReauthTitleStringId(access_point));
-  source->AddLocalizedString("signinReauthDesc",
-                             IDS_ACCOUNT_PASSWORDS_REAUTH_DESC);
-  source->AddLocalizedString("signinReauthConfirmLabel",
-                             GetReauthConfirmButtonLabelStringId(access_point));
-  source->AddLocalizedString("signinReauthNextLabel",
-                             IDS_ACCOUNT_PASSWORDS_REAUTH_NEXT_BUTTON_LABEL);
-  source->AddLocalizedString("signinReauthCloseLabel",
-                             IDS_ACCOUNT_PASSWORDS_REAUTH_CLOSE_BUTTON_LABEL);
+  AddStringResource(source, "signinReauthTitle",
+                    GetReauthTitleStringId(access_point));
+  AddStringResource(source, "signinReauthDesc",
+                    IDS_ACCOUNT_PASSWORDS_REAUTH_DESC);
+  AddStringResource(source, "signinReauthConfirmLabel",
+                    GetReauthConfirmButtonLabelStringId(access_point));
+  AddStringResource(source, "signinReauthNextLabel",
+                    IDS_ACCOUNT_PASSWORDS_REAUTH_NEXT_BUTTON_LABEL);
+  AddStringResource(source, "signinReauthCloseLabel",
+                    IDS_ACCOUNT_PASSWORDS_REAUTH_CLOSE_BUTTON_LABEL);
 
   content::WebUIDataSource::Add(profile, source);
 }
@@ -132,8 +134,28 @@
 
 void SigninReauthUI::InitializeMessageHandlerWithReauthController(
     SigninReauthViewController* controller) {
-  web_ui()->AddMessageHandler(
-      std::make_unique<SigninReauthHandler>(controller));
+  web_ui()->AddMessageHandler(std::make_unique<SigninReauthHandler>(
+      controller,
+      base::flat_map<std::string, int>(js_localized_string_to_ids_)));
 }
 
 void SigninReauthUI::InitializeMessageHandlerWithBrowser(Browser* browser) {}
+
+void SigninReauthUI::AddStringResource(content::WebUIDataSource* source,
+                                       base::StringPiece name,
+                                       int ids) {
+  source->AddLocalizedString(name, ids);
+
+  // When the strings are passed to the HTML, the Unicode NBSP symbol (\u00A0)
+  // will be automatically replaced with "&nbsp;". This change must be mirrored
+  // in the string-to-ids map. Note that "\u00A0" is actually two characters,
+  // so we must use base::ReplaceSubstrings* rather than base::ReplaceChars.
+  // TODO(treib): De-dupe this with the similar code in SyncConfirmationUI,
+  // SyncConsentScreenHandler, and possibly other places.
+  std::string sanitized_string =
+      base::UTF16ToUTF8(l10n_util::GetStringUTF16(ids));
+  base::ReplaceSubstringsAfterOffset(&sanitized_string, 0, "\u00A0" /* NBSP */,
+                                     "&nbsp;");
+
+  js_localized_string_to_ids_.emplace_back(sanitized_string, ids);
+}
diff --git a/chrome/browser/ui/webui/signin/signin_reauth_ui.h b/chrome/browser/ui/webui/signin/signin_reauth_ui.h
index e32947e..bdebd68 100644
--- a/chrome/browser/ui/webui/signin/signin_reauth_ui.h
+++ b/chrome/browser/ui/webui/signin/signin_reauth_ui.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_REAUTH_UI_H_
 #define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_REAUTH_UI_H_
 
+#include <string>
+#include <vector>
+
 #include "chrome/browser/ui/webui/signin/signin_web_dialog_ui.h"
 
 class Browser;
@@ -12,6 +15,7 @@
 
 namespace content {
 class WebUI;
+class WebUIDataSource;
 }
 
 // WebUI controller for the signin reauth dialog.
@@ -44,6 +48,18 @@
   // This class relies on InitializeMessageHandlerWithReauthController() so this
   // method does nothing.
   void InitializeMessageHandlerWithBrowser(Browser* browser) override;
+
+ private:
+  // Adds a string resource with the given GRD |ids| to the WebUI data |source|
+  // named as |name|. Also stores a reverse mapping from the localized version
+  // of the string to the |ids| in order to later pass it to
+  // SigninReauthHandler.
+  void AddStringResource(content::WebUIDataSource* source,
+                         base::StringPiece name,
+                         int ids);
+
+  // For consent auditing.
+  std::vector<std::pair<std::string, int>> js_localized_string_to_ids_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_REAUTH_UI_H_
diff --git a/chrome/browser/web_applications/components/app_registrar.cc b/chrome/browser/web_applications/components/app_registrar.cc
index 398019f..3d1ab56c 100644
--- a/chrome/browser/web_applications/components/app_registrar.cc
+++ b/chrome/browser/web_applications/components/app_registrar.cc
@@ -275,6 +275,19 @@
                                      user_display_mode);
 }
 
+DisplayMode AppRegistrar::GetEffectiveDisplayModeFromManifest(
+    const AppId& app_id) const {
+  if (base::FeatureList::IsEnabled(features::kWebAppManifestDisplayOverride)) {
+    std::vector<DisplayMode> display_mode_overrides =
+        GetAppDisplayModeOverride(app_id);
+
+    if (!display_mode_overrides.empty())
+      return display_mode_overrides[0];
+  }
+
+  return GetAppDisplayMode(app_id);
+}
+
 bool AppRegistrar::IsInExperimentalTabbedWindowMode(const AppId& app_id) const {
   return base::FeatureList::IsEnabled(features::kDesktopPWAsTabStrip) &&
          GetBoolWebAppPref(profile()->GetPrefs(), app_id,
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h
index d82c433..439954f 100644
--- a/chrome/browser/web_applications/components/app_registrar.h
+++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -163,8 +163,14 @@
   // complete installation via the PendingAppManager.
   bool IsPlaceholderApp(const AppId& app_id) const;
 
+  // Computes and returns the DisplayMode, accounting for user preference
+  // to launch in a browser window and entries in the web app manifest.
   DisplayMode GetAppEffectiveDisplayMode(const AppId& app_id) const;
 
+  // Computes and returns the DisplayMode only accounting for
+  // entries in the web app manifest.
+  DisplayMode GetEffectiveDisplayModeFromManifest(const AppId& app_id) const;
+
   // TODO(crbug.com/897314): Finish experiment by legitimising it as a
   // DisplayMode or removing entirely.
   bool IsInExperimentalTabbedWindowMode(const AppId& app_id) const;
diff --git a/chrome/browser/web_applications/web_app_registrar_unittest.cc b/chrome/browser/web_applications/web_app_registrar_unittest.cc
index 8d4e528c4..0d1c6499 100644
--- a/chrome/browser/web_applications/web_app_registrar_unittest.cc
+++ b/chrome/browser/web_applications/web_app_registrar_unittest.cc
@@ -813,4 +813,28 @@
             registrar().GetAppEffectiveDisplayMode(app_id));
 }
 
+TEST_F(WebAppRegistrarTest_DisplayOverride,
+       CheckDisplayOverrideFromGetEffectiveDisplayModeFromManifest) {
+  controller().Init();
+
+  auto web_app = CreateWebApp("https://example.com/path");
+  const AppId app_id = web_app->app_id();
+  std::vector<DisplayMode> display_mode_overrides;
+  display_mode_overrides.push_back(DisplayMode::kFullscreen);
+  display_mode_overrides.push_back(DisplayMode::kMinimalUi);
+
+  web_app->SetDisplayMode(DisplayMode::kStandalone);
+  web_app->SetUserDisplayMode(DisplayMode::kStandalone);
+  web_app->SetDisplayModeOverride(display_mode_overrides);
+  web_app->SetIsLocallyInstalled(false);
+  RegisterApp(std::move(web_app));
+
+  EXPECT_EQ(DisplayMode::kFullscreen,
+            registrar().GetEffectiveDisplayModeFromManifest(app_id));
+
+  sync_bridge().SetAppIsLocallyInstalled(app_id, true);
+  EXPECT_EQ(DisplayMode::kFullscreen,
+            registrar().GetEffectiveDisplayModeFromManifest(app_id));
+}
+
 }  // namespace web_app
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 8c90f1ae..6b88243 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1597275686-7da657e756f53f89a89030a34195741313ee681f.profdata
+chrome-mac-master-1597319582-be9645d468b25404b91084b442e1381733e63a51.profdata
diff --git a/chrome/services/sharing/nearby/decoder/nearby_decoder_unittest.cc b/chrome/services/sharing/nearby/decoder/nearby_decoder_unittest.cc
index 643455c..692448ac 100644
--- a/chrome/services/sharing/nearby/decoder/nearby_decoder_unittest.cc
+++ b/chrome/services/sharing/nearby/decoder/nearby_decoder_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/task_environment.h"
 #include "chrome/services/sharing/public/cpp/advertisement.h"
+#include "chrome/services/sharing/public/cpp/conversions.h"
 #include "chrome/services/sharing/public/mojom/nearby_decoder_types.mojom.h"
 #include "chrome/services/sharing/public/proto/wire_format.pb.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -35,55 +36,7 @@
                   const mojom::AdvertisementPtr& other) {
   EXPECT_EQ(self.device_name(), other->device_name);
   EXPECT_EQ(self.salt(), other->salt);
-  EXPECT_EQ(self.encrypted_metadata_key(), other->encrypted_metadata);
-}
-
-sharing::nearby::FileMetadata_Type ConvertFileMetadataType(
-    mojom::FileMetadata::Type type) {
-  switch (type) {
-    case mojom::FileMetadata::Type::kImage:
-      return sharing::nearby::FileMetadata_Type_IMAGE;
-    case mojom::FileMetadata::Type::kVideo:
-      return sharing::nearby::FileMetadata_Type_VIDEO;
-    case mojom::FileMetadata::Type::kApp:
-      return sharing::nearby::FileMetadata_Type_APP;
-    case mojom::FileMetadata::Type::kAudio:
-      return sharing::nearby::FileMetadata_Type_AUDIO;
-    case mojom::FileMetadata::Type::kUnknown:
-      return sharing::nearby::FileMetadata_Type_UNKNOWN;
-  }
-}
-
-sharing::nearby::TextMetadata_Type ConvertTextMetadataType(
-    mojom::TextMetadata::Type type) {
-  switch (type) {
-    case mojom::TextMetadata::Type::kText:
-      return sharing::nearby::TextMetadata_Type_TEXT;
-    case mojom::TextMetadata::Type::kUrl:
-      return sharing::nearby::TextMetadata_Type_URL;
-    case mojom::TextMetadata::Type::kAddress:
-      return sharing::nearby::TextMetadata_Type_ADDRESS;
-    case mojom::TextMetadata::Type::kPhoneNumber:
-      return sharing::nearby::TextMetadata_Type_PHONE_NUMBER;
-    case mojom::TextMetadata::Type::kUnknown:
-      return sharing::nearby::TextMetadata_Type_UNKNOWN;
-  }
-}
-
-sharing::nearby::WifiCredentialsMetadata_SecurityType
-ConvertWifiCredentialsMetadataType(
-    mojom::WifiCredentialsMetadata::SecurityType type) {
-  switch (type) {
-    case mojom::WifiCredentialsMetadata::SecurityType::kOpen:
-      return sharing::nearby::WifiCredentialsMetadata_SecurityType_OPEN;
-    case mojom::WifiCredentialsMetadata::SecurityType::kWpaPsk:
-      return sharing::nearby::WifiCredentialsMetadata_SecurityType_WPA_PSK;
-    case mojom::WifiCredentialsMetadata::SecurityType::kWep:
-      return sharing::nearby::WifiCredentialsMetadata_SecurityType_WEP;
-    case mojom::WifiCredentialsMetadata::SecurityType::kUnknownSecurityType:
-      return sharing::nearby::
-          WifiCredentialsMetadata_SecurityType_UNKNOWN_SECURITY_TYPE;
-  }
+  EXPECT_EQ(self.encrypted_metadata_key(), other->encrypted_metadata_key);
 }
 
 void ExpectFrameContainsIntroduction(
diff --git a/chrome/services/sharing/public/cpp/BUILD.gn b/chrome/services/sharing/public/cpp/BUILD.gn
index 93b26231..8c23b9d 100644
--- a/chrome/services/sharing/public/cpp/BUILD.gn
+++ b/chrome/services/sharing/public/cpp/BUILD.gn
@@ -6,12 +6,16 @@
   sources = [
     "advertisement.cc",
     "advertisement.h",
+    "conversions.cc",
+    "conversions.h",
     "sharing_webrtc_metrics.cc",
     "sharing_webrtc_metrics.h",
   ]
 
   public_deps = [
     "//base",
+    "//chrome/services/sharing/public/mojom",
+    "//chrome/services/sharing/public/proto",
     "//mojo/public/cpp/bindings",
   ]
 }
diff --git a/chrome/services/sharing/public/cpp/advertisement.h b/chrome/services/sharing/public/cpp/advertisement.h
index 661d0a0..7169970 100644
--- a/chrome/services/sharing/public/cpp/advertisement.h
+++ b/chrome/services/sharing/public/cpp/advertisement.h
@@ -56,11 +56,14 @@
   // ways of parsing the endpoint id.
   int version_;
 
-  // A randomized salt used in the hash of the account identifier.
+  // Random bytes that were used as salt during encryption of public certificate
+  // metadata.
   std::vector<uint8_t> salt_;
 
-  // A salted hash of an account identifier that signifies who the remote device
-  // is.
+  // An encrypted symmetric key that was used to encrypt public certificate
+  // metadata, including an account identifier signifying the remote device.
+  // The key can be decrypted using |salt| and the corresponding public
+  // certificate's secret/authenticity key.
   std::vector<uint8_t> encrypted_metadata_key_;
 
   // The human readable name of the remote device.
diff --git a/chrome/services/sharing/public/cpp/conversions.cc b/chrome/services/sharing/public/cpp/conversions.cc
new file mode 100644
index 0000000..ec7055a7
--- /dev/null
+++ b/chrome/services/sharing/public/cpp/conversions.cc
@@ -0,0 +1,55 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/sharing/public/cpp/conversions.h"
+
+namespace sharing {
+
+nearby::FileMetadata_Type ConvertFileMetadataType(
+    mojom::FileMetadata::Type type) {
+  switch (type) {
+    case mojom::FileMetadata::Type::kImage:
+      return nearby::FileMetadata_Type_IMAGE;
+    case mojom::FileMetadata::Type::kVideo:
+      return nearby::FileMetadata_Type_VIDEO;
+    case mojom::FileMetadata::Type::kApp:
+      return nearby::FileMetadata_Type_APP;
+    case mojom::FileMetadata::Type::kAudio:
+      return nearby::FileMetadata_Type_AUDIO;
+    case mojom::FileMetadata::Type::kUnknown:
+      return nearby::FileMetadata_Type_UNKNOWN;
+  }
+}
+
+nearby::TextMetadata_Type ConvertTextMetadataType(
+    mojom::TextMetadata::Type type) {
+  switch (type) {
+    case mojom::TextMetadata::Type::kText:
+      return nearby::TextMetadata_Type_TEXT;
+    case mojom::TextMetadata::Type::kUrl:
+      return nearby::TextMetadata_Type_URL;
+    case mojom::TextMetadata::Type::kAddress:
+      return nearby::TextMetadata_Type_ADDRESS;
+    case mojom::TextMetadata::Type::kPhoneNumber:
+      return nearby::TextMetadata_Type_PHONE_NUMBER;
+    case mojom::TextMetadata::Type::kUnknown:
+      return nearby::TextMetadata_Type_UNKNOWN;
+  }
+}
+
+nearby::WifiCredentialsMetadata_SecurityType ConvertWifiCredentialsMetadataType(
+    mojom::WifiCredentialsMetadata::SecurityType type) {
+  switch (type) {
+    case mojom::WifiCredentialsMetadata::SecurityType::kOpen:
+      return nearby::WifiCredentialsMetadata_SecurityType_OPEN;
+    case mojom::WifiCredentialsMetadata::SecurityType::kWpaPsk:
+      return nearby::WifiCredentialsMetadata_SecurityType_WPA_PSK;
+    case mojom::WifiCredentialsMetadata::SecurityType::kWep:
+      return nearby::WifiCredentialsMetadata_SecurityType_WEP;
+    case mojom::WifiCredentialsMetadata::SecurityType::kUnknownSecurityType:
+      return nearby::WifiCredentialsMetadata_SecurityType_UNKNOWN_SECURITY_TYPE;
+  }
+}
+
+}  // namespace sharing
diff --git a/chrome/services/sharing/public/cpp/conversions.h b/chrome/services/sharing/public/cpp/conversions.h
new file mode 100644
index 0000000..e08491a
--- /dev/null
+++ b/chrome/services/sharing/public/cpp/conversions.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_SERVICES_SHARING_PUBLIC_CPP_CONVERSIONS_H_
+#define CHROME_SERVICES_SHARING_PUBLIC_CPP_CONVERSIONS_H_
+
+#include "chrome/services/sharing/public/mojom/nearby_decoder_types.mojom.h"
+#include "chrome/services/sharing/public/proto/wire_format.pb.h"
+
+namespace sharing {
+
+nearby::FileMetadata_Type ConvertFileMetadataType(
+    mojom::FileMetadata::Type type);
+
+nearby::TextMetadata_Type ConvertTextMetadataType(
+    mojom::TextMetadata::Type type);
+
+nearby::WifiCredentialsMetadata_SecurityType ConvertWifiCredentialsMetadataType(
+    mojom::WifiCredentialsMetadata::SecurityType type);
+
+}  // namespace sharing
+
+#endif  // CHROME_SERVICES_SHARING_PUBLIC_CPP_CONVERSIONS_H_
diff --git a/chrome/services/sharing/public/mojom/nearby_decoder_types.mojom b/chrome/services/sharing/public/mojom/nearby_decoder_types.mojom
index 12bd12b..4d4694d6 100644
--- a/chrome/services/sharing/public/mojom/nearby_decoder_types.mojom
+++ b/chrome/services/sharing/public/mojom/nearby_decoder_types.mojom
@@ -8,11 +8,14 @@
 
 // An advertisement send by a device in receiving mode.
 struct Advertisement {
-  // Random data erived from a device's private key.
+  // Random bytes that were used as salt during encryption of public
+  // certificate metadata.
   array<uint8, 2> salt;
 
-  // Derived from a device's private key. Identifying data for the device.
-  array<uint8, 14> encrypted_metadata;
+  // An encrypted symmetric key that was used to encrypt public certificate
+  // metadata. The key can be decrypted using |salt| and the corresponding
+  // public certificate's secret/authenticity key.
+  array<uint8, 14> encrypted_metadata_key;
 
   // If present, the device is visible to everyone rather than contacts only.
   string? device_name;
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
index 073546f..5756bb0 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
@@ -421,7 +421,7 @@
 
         ChromeTabUtils.waitForTabPageLoaded(tab, (String) null);
 
-        if (tab != null && NewTabPage.isNTPUrl(ChromeTabUtils.getUrlStringOnUiThread(tab))
+        if (tab != null && NewTabPage.isNTPUrl(tab.getUrlString())
                 && !getActivity().isInOverviewMode()) {
             NewTabPageTestUtils.waitForNtpLoaded(tab);
         }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
index f418e10..437c158 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
@@ -40,7 +40,6 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
-import org.chromium.url.GURL;
 
 import java.util.List;
 import java.util.Locale;
@@ -120,8 +119,7 @@
     }
 
     private static boolean loadComplete(Tab tab, String url) {
-        return !tab.isLoading()
-                && (url == null || TextUtils.equals(getUrlStringOnUiThread(tab), url))
+        return !tab.isLoading() && (url == null || TextUtils.equals(tab.getUrlString(), url))
                 && !tab.getWebContents().isLoadingToDifferentDocument();
     }
 
@@ -131,18 +129,6 @@
         return res.get();
     }
 
-    public static String getUrlStringOnUiThread(Tab tab) {
-        AtomicReference<String> res = new AtomicReference<>();
-        TestThreadUtils.runOnUiThreadBlocking(() -> { res.set(tab.getUrlString()); });
-        return res.get();
-    }
-
-    public static GURL getUrlOnUiThread(Tab tab) {
-        AtomicReference<GURL> res = new AtomicReference<>();
-        TestThreadUtils.runOnUiThreadBlocking(() -> { res.set(tab.getUrl()); });
-        return res.get();
-    }
-
     /**
      * Waits for the given tab to finish loading the given URL, or, if the given URL is
      * null, waits for the current page to load.
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
index 10e9e9c..4bd1108 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
@@ -67,7 +67,12 @@
      * Tears down the AccountManagerFacade mock and signs out if user is signed in.
      */
     public void tearDownRule() {
-        if (mIsSignedIn) {
+        if (mIsSignedIn && getCurrentSignedInAccount() != null) {
+            // For android_browsertests that sign out during the test body, like
+            // UkmBrowserTest.SingleSyncSignoutCheck, we should sign out during tear-down test stage
+            // only if an account is signed in. Otherwise, tearDownRule() ultimately results a crash
+            // in SignoutManager::signOut(). This is because sign out is attempted when a sign-out
+            // operation is already in progress. See crbug/1102746 for more details.
             signOut();
         }
         AccountManagerFacadeProvider.resetInstanceForTests();
diff --git a/chrome/test/data/password/nonplaceholder_username.html b/chrome/test/data/password/nonplaceholder_username.html
new file mode 100644
index 0000000..f7dd46d
--- /dev/null
+++ b/chrome/test/data/password/nonplaceholder_username.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+<form method="POST" action="done.html" onsubmit="return true;" id="testform">
+  <input type="text" id="username_field" name="username_field" value="example@example.com">
+  <input type="password" id="password_field" name="password_field" value="htmlPass">
+  <input type="submit" id="input_submit_button" name="input_submit_button">
+</form>
+</body>
+</html>
diff --git a/chrome/test/data/portal/portal-no-src.html b/chrome/test/data/portal/portal-no-src.html
new file mode 100644
index 0000000..6172ffa
--- /dev/null
+++ b/chrome/test/data/portal/portal-no-src.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<body>
+  <portal></portal>
+  <script>
+    function activate() {
+      return document.querySelector("portal").activate().catch(err => true);
+    }
+  </script>
+</body>
diff --git a/chrome/test/data/webui/settings/multi_store_exception_entry_test.js b/chrome/test/data/webui/settings/multi_store_exception_entry_test.js
index 0623259..5785550 100644
--- a/chrome/test/data/webui/settings/multi_store_exception_entry_test.js
+++ b/chrome/test/data/webui/settings/multi_store_exception_entry_test.js
@@ -26,12 +26,30 @@
     expectTrue(multiStoreAccountEntry.isPresentInAccount());
     expectEquals(multiStoreAccountEntry.getAnyId(), 1);
 
-    const multiStoreEntryFromBoth =
-        new MultiStoreExceptionEntry(deviceEntry, accountEntry);
+    const multiStoreEntryFromBoth = new MultiStoreExceptionEntry(deviceEntry);
+    expectTrue(multiStoreEntryFromBoth.mergeInPlace(accountEntry));
     expectTrue(multiStoreEntryFromBoth.isPresentOnDevice());
     expectTrue(multiStoreEntryFromBoth.isPresentInAccount());
     expectTrue(
         multiStoreEntryFromBoth.getAnyId() === 0 ||
         multiStoreEntryFromBoth.getAnyId() === 1);
   });
+
+  test('mergeFailsForRepeatedStore', function() {
+    const deviceEntry1 =
+        createExceptionEntry({url: 'g.com', id: 0, fromAccountStore: false});
+    const deviceEntry2 =
+        createExceptionEntry({url: 'g.com', id: 1, fromAccountStore: false});
+    expectFalse(
+        new MultiStoreExceptionEntry(deviceEntry1).mergeInPlace(deviceEntry2));
+  });
+
+  test('mergeFailsForDifferentContents', function() {
+    const deviceEntry =
+        createExceptionEntry({url: 'a.com', id: 0, fromAccountStore: false});
+    const accountEntry =
+        createExceptionEntry({url: 'b.com', id: 1, fromAccountStore: true});
+    expectFalse(
+        new MultiStoreExceptionEntry(deviceEntry).mergeInPlace(accountEntry));
+  });
 });
diff --git a/chrome/test/data/webui/settings/multi_store_password_ui_entry_test.js b/chrome/test/data/webui/settings/multi_store_password_ui_entry_test.js
index b5c35c7..d3f7984 100644
--- a/chrome/test/data/webui/settings/multi_store_password_ui_entry_test.js
+++ b/chrome/test/data/webui/settings/multi_store_password_ui_entry_test.js
@@ -26,12 +26,30 @@
     expectTrue(multiStoreAccountEntry.isPresentInAccount());
     expectEquals(multiStoreAccountEntry.getAnyId(), 1);
 
-    const multiStoreEntryFromBoth =
-        new MultiStorePasswordUiEntry(deviceEntry, accountEntry);
+    const multiStoreEntryFromBoth = new MultiStorePasswordUiEntry(deviceEntry);
+    expectTrue(multiStoreEntryFromBoth.mergeInPlace(accountEntry));
     expectTrue(multiStoreEntryFromBoth.isPresentOnDevice());
     expectTrue(multiStoreEntryFromBoth.isPresentInAccount());
     expectTrue(
         multiStoreEntryFromBoth.getAnyId() === 0 ||
         multiStoreEntryFromBoth.getAnyId() === 1);
   });
+
+  test('mergeFailsForRepeatedStore', function() {
+    const deviceEntry1 = createPasswordEntry(
+        {url: 'g.com', username: 'user', id: 0, fromAccountStore: false});
+    const deviceEntry2 = createPasswordEntry(
+        {url: 'g.com', username: 'user', id: 1, fromAccountStore: false});
+    expectFalse(
+        new MultiStorePasswordUiEntry(deviceEntry1).mergeInPlace(deviceEntry2));
+  });
+
+  test('mergeFailsForDifferentContents', function() {
+    const deviceEntry = createPasswordEntry(
+        {url: 'g.com', username: 'user', id: 0, fromAccountStore: false});
+    const accountEntry = createPasswordEntry(
+        {url: 'g.com', username: 'user2', id: 1, fromAccountStore: true});
+    expectFalse(
+        new MultiStorePasswordUiEntry(deviceEntry).mergeInPlace(accountEntry));
+  });
 });
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
index 9dacdea..48b8dd73 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
@@ -87,8 +87,13 @@
     });
   }
 
+  if (deviceEntry && accountEntry) {
+    const mergedEntry = new MultiStorePasswordUiEntry(deviceEntry);
+    mergedEntry.mergeInPlace(accountEntry);
+    return mergedEntry;
+  }
   if (deviceEntry) {
-    return new MultiStorePasswordUiEntry(deviceEntry, accountEntry);
+    return new MultiStorePasswordUiEntry(deviceEntry);
   }
   if (accountEntry) {
     return new MultiStorePasswordUiEntry(accountEntry);
@@ -158,8 +163,13 @@
     });
   }
 
+  if (deviceEntry && accountEntry) {
+    const mergedEntry = new MultiStoreExceptionEntry(deviceEntry);
+    mergedEntry.mergeInPlace(accountEntry);
+    return mergedEntry;
+  }
   if (deviceEntry) {
-    return new MultiStoreExceptionEntry(deviceEntry, accountEntry);
+    return new MultiStoreExceptionEntry(deviceEntry);
   }
   if (accountEntry) {
     return new MultiStoreExceptionEntry(accountEntry);
diff --git a/chrome/test/data/webui/settings/safety_check_page_test.js b/chrome/test/data/webui/settings/safety_check_page_test.js
index 618f473a..9942d54 100644
--- a/chrome/test/data/webui/settings/safety_check_page_test.js
+++ b/chrome/test/data/webui/settings/safety_check_page_test.js
@@ -647,6 +647,17 @@
     });
   });
 
+  test('safeBrowsingEnabledStandardAvailableEnhancedUiTest', function() {
+    fireSafetyCheckSafeBrowsingEvent(
+        SafetyCheckSafeBrowsingStatus.ENABLED_STANDARD_AVAILABLE_ENHANCED);
+    flush();
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.SAFE,
+      label: 'Safe Browsing',
+    });
+  });
+
   test('safeBrowsingEnabledEnhancedUiTest', function() {
     fireSafetyCheckSafeBrowsingEvent(
         SafetyCheckSafeBrowsingStatus.ENABLED_ENHANCED);
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index a82be5d7..39a593f 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13402.0.0
\ No newline at end of file
+13405.0.0
\ No newline at end of file
diff --git a/chromeos/components/phonehub/OWNERS b/chromeos/components/phonehub/OWNERS
new file mode 100644
index 0000000..0269806
--- /dev/null
+++ b/chromeos/components/phonehub/OWNERS
@@ -0,0 +1,3 @@
+khorimoto@chromium.org
+
+# COMPONENT: OS>Systems>Multidevice>PhoneHub
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 955a732..f209925 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -309,6 +309,8 @@
 static_library("arc_test_support") {
   testonly = true
   sources = [
+    "test/arc_payment_app_bridge_test_support.cc",
+    "test/arc_payment_app_bridge_test_support.h",
     "test/fake_accessibility_helper_instance.cc",
     "test/fake_accessibility_helper_instance.h",
     "test/fake_app_instance.cc",
diff --git a/components/arc/mojom/payment_app.mojom b/components/arc/mojom/payment_app.mojom
index b46a1b5..a52ba82 100644
--- a/components/arc/mojom/payment_app.mojom
+++ b/components/arc/mojom/payment_app.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 2
+// Next MinVersion: 3
 
 module arc.mojom;
 
@@ -77,14 +77,44 @@
   // schemelessIframeOrigin" parameter to createIsReadyToPayIntent() and
   // createPayIntent() methods.
   string payment_request_origin;
+
+  // The free-form identifier for this pending transaction as set either by the
+  // merchant website in PaymentRequest() constructor in JavaScript, or (more
+  // commonly) a browser-generated GUID string. This is the "String id"
+  // parameter to createPayIntent() method. The createIsReadyToPayIntent() does
+  // not currently need this parameter.
+  [MinVersion=2]
+  string? payment_request_id;
 };
 
-// After the browser calls IsReadyToPay(), ARC sends this back in response.
+// After the browser calls IsReadyToPay(), ARC sends back this result.
 union IsReadyToPayResult {
   bool response;
   string error;
 };
 
+// After the browser calls InvokePaymentApp(), ARC sends back this result, if
+// there are no errors.
+struct InvokePaymentAppValidResult {
+  // Whether the intent return status is Activity.RESULT_OK.
+  bool is_activity_result_ok;
+
+  // The JSON serialization of a JavaScript object that's the response to
+  // PaymentRequest API. For example:
+  //
+  //  {"receiptIdentifier": "test_receipt_identifier"}
+  //
+  // This string is serialized in TWA and is parsed in Blink. The browser does
+  // not parse this string.
+  string stringified_details;
+};
+
+// After the browser calls InvokePaymentApp(), ARC sends back this result.
+union InvokePaymentAppResult {
+  InvokePaymentAppValidResult valid;
+  string error;
+};
+
 // The service that runs in ARC and allows the browser to invoke the TWA payment
 // app that is installed in ARC, if it implements payment intents as described
 // in https://web.dev/android-payment-apps-overview/. At first, only
@@ -99,7 +129,7 @@
 // |                  |      |                                                |
 // --------------------      --------------------------------------------------
 //
-// Next method ID: 2
+// Next method ID: 3
 interface PaymentAppInstance {
   // Called by the browser to check whether the TWA with |package_name| (e.g.,
   // "com.example.app") in ARC has intent filters for
@@ -122,5 +152,17 @@
   // or guest mode.
   [MinVersion=1]
   IsReadyToPay@1(PaymentParameters parameters)
-      => (IsReadyToPayResult result);
+      => (IsReadyToPayResult response);
+
+  // Forwards the request to WebPaymentIntentHelper.createPayIntent() and fires
+  // the resulting intent. This invokes the payment flow.
+  //
+  // The "String merchantName" parameter to createPayIntent() should be empty
+  // (but not null).
+  //
+  // The total amount for createPayIntent() should always be "0" with currency
+  // "ZZZ".
+  [MinVersion=2]
+  InvokePaymentApp@2(PaymentParameters parameters)
+      => (InvokePaymentAppResult response);
 };
diff --git a/components/arc/pay/arc_payment_app_bridge.cc b/components/arc/pay/arc_payment_app_bridge.cc
index 535fbcb..33c435d0 100644
--- a/components/arc/pay/arc_payment_app_bridge.cc
+++ b/components/arc/pay/arc_payment_app_bridge.cc
@@ -86,4 +86,18 @@
   payment_app->IsReadyToPay(std::move(parameters), std::move(callback));
 }
 
+void ArcPaymentAppBridge::InvokePaymentApp(
+    mojom::PaymentParametersPtr parameters,
+    InvokePaymentAppCallback callback) {
+  mojom::PaymentAppInstance* payment_app = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_bridge_service_->payment_app(), InvokePaymentApp);
+  if (!payment_app) {
+    std::move(callback).Run(
+        mojom::InvokePaymentAppResult::NewError(kUnableToConnectErrorMessage));
+    return;
+  }
+
+  payment_app->InvokePaymentApp(std::move(parameters), std::move(callback));
+}
+
 }  // namespace arc
diff --git a/components/arc/pay/arc_payment_app_bridge.h b/components/arc/pay/arc_payment_app_bridge.h
index 710135d..2d8583bd 100644
--- a/components/arc/pay/arc_payment_app_bridge.h
+++ b/components/arc/pay/arc_payment_app_bridge.h
@@ -26,6 +26,8 @@
       base::OnceCallback<void(mojom::IsPaymentImplementedResultPtr)>;
   using IsReadyToPayCallback =
       base::OnceCallback<void(mojom::IsReadyToPayResultPtr)>;
+  using InvokePaymentAppCallback =
+      base::OnceCallback<void(mojom::InvokePaymentAppResultPtr)>;
 
   // Returns the instance owned by the given BrowserContext, or nullptr if the
   // browser |context| is not allowed to use ARC.
@@ -54,6 +56,10 @@
   void IsReadyToPay(mojom::PaymentParametersPtr parameters,
                     IsReadyToPayCallback callback);
 
+  // Invokes the TWA payment app flow.
+  void InvokePaymentApp(mojom::PaymentParametersPtr parameters,
+                        InvokePaymentAppCallback callback);
+
  private:
   ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
 };
diff --git a/components/arc/pay/arc_payment_app_bridge_unittest.cc b/components/arc/pay/arc_payment_app_bridge_unittest.cc
index 2a44b65..57876c4e 100644
--- a/components/arc/pay/arc_payment_app_bridge_unittest.cc
+++ b/components/arc/pay/arc_payment_app_bridge_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/session/arc_bridge_service.h"
+#include "components/arc/test/arc_payment_app_bridge_test_support.h"
 #include "components/arc/test/test_browser_context.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -16,17 +17,6 @@
 namespace arc {
 namespace {
 
-class MockPaymentAppInstance : public mojom::PaymentAppInstance {
- public:
-  MOCK_METHOD2(
-      IsPaymentImplemented,
-      void(const std::string& package_name,
-           ArcPaymentAppBridge::IsPaymentImplementedCallback callback));
-  MOCK_METHOD2(IsReadyToPay,
-               void(mojom::PaymentParametersPtr,
-                    ArcPaymentAppBridge::IsReadyToPayCallback));
-};
-
 class ArcPaymentAppBridgeTest : public testing::Test {
  public:
   ArcPaymentAppBridgeTest() = default;
@@ -45,45 +35,24 @@
     is_ready_to_pay_ = std::move(response);
   }
 
-  // The |manager_| must be used on the same thread as where it was created, so
-  // create it in the test instead of using ArcServiceManager::Get().
-  ArcServiceManager manager_;
-  MockPaymentAppInstance instance_;
+  void OnInvokePaymentAppResponse(mojom::InvokePaymentAppResultPtr response) {
+    invoke_app_ = std::move(response);
+  }
 
-  // Required for the TestBrowserContext.
-  content::BrowserTaskEnvironment task_environment_;
-
-  // Used for retrieving an instance of ArcPaymentAppBridge owned by a
-  // BrowserContext.
-  TestBrowserContext context_;
-
+  ArcPaymentAppBridgeTestSupport support_;
   mojom::IsPaymentImplementedResultPtr is_implemented_;
   mojom::IsReadyToPayResultPtr is_ready_to_pay_;
-};
-
-class ScopedSetInstance {
- public:
-  ScopedSetInstance(ArcServiceManager* manager,
-                    MockPaymentAppInstance* instance)
-      : manager_(manager), instance_(instance) {
-    manager_->arc_bridge_service()->payment_app()->SetInstance(instance_);
-  }
-
-  ~ScopedSetInstance() {
-    manager_->arc_bridge_service()->payment_app()->CloseInstance(instance_);
-  }
-
- private:
-  ArcServiceManager* manager_;
-  MockPaymentAppInstance* instance_;
+  mojom::InvokePaymentAppResultPtr invoke_app_;
 };
 
 TEST_F(ArcPaymentAppBridgeTest, UnableToConnectInIsImplemented) {
   // Intentionally do not set an instance.
 
-  EXPECT_CALL(instance_, IsPaymentImplemented(testing::_, testing::_)).Times(0);
+  EXPECT_CALL(*support_.instance(),
+              IsPaymentImplemented(testing::_, testing::_))
+      .Times(0);
 
-  ArcPaymentAppBridge::GetForBrowserContextForTesting(&context_)
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
       ->IsPaymentImplemented(
           "com.example.app",
           base::BindOnce(&ArcPaymentAppBridgeTest::OnPaymentImplementedResponse,
@@ -96,9 +65,10 @@
 }
 
 TEST_F(ArcPaymentAppBridgeTest, IsImplemented) {
-  ScopedSetInstance scoped_set_instance(&manager_, &instance_);
+  auto scoped_set_instance = support_.CreateScopedSetInstance();
 
-  EXPECT_CALL(instance_, IsPaymentImplemented(testing::_, testing::_))
+  EXPECT_CALL(*support_.instance(),
+              IsPaymentImplemented(testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const std::string& package_name,
              ArcPaymentAppBridge::IsPaymentImplementedCallback callback) {
@@ -109,7 +79,7 @@
                 mojom::IsPaymentImplementedResult::NewValid(std::move(valid)));
           }));
 
-  ArcPaymentAppBridge::GetForBrowserContextForTesting(&context_)
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
       ->IsPaymentImplemented(
           "com.example.app",
           base::BindOnce(&ArcPaymentAppBridgeTest::OnPaymentImplementedResponse,
@@ -126,9 +96,10 @@
 }
 
 TEST_F(ArcPaymentAppBridgeTest, IsNotImplemented) {
-  ScopedSetInstance scoped_set_instance(&manager_, &instance_);
+  auto scoped_set_instance = support_.CreateScopedSetInstance();
 
-  EXPECT_CALL(instance_, IsPaymentImplemented(testing::_, testing::_))
+  EXPECT_CALL(*support_.instance(),
+              IsPaymentImplemented(testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const std::string& package_name,
              ArcPaymentAppBridge::IsPaymentImplementedCallback callback) {
@@ -136,7 +107,7 @@
                 mojom::IsPaymentImplementedValidResult::New()));
           }));
 
-  ArcPaymentAppBridge::GetForBrowserContextForTesting(&context_)
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
       ->IsPaymentImplemented(
           "com.example.app",
           base::BindOnce(&ArcPaymentAppBridgeTest::OnPaymentImplementedResponse,
@@ -151,9 +122,10 @@
 }
 
 TEST_F(ArcPaymentAppBridgeTest, ImplementationCheckError) {
-  ScopedSetInstance scoped_set_instance(&manager_, &instance_);
+  auto scoped_set_instance = support_.CreateScopedSetInstance();
 
-  EXPECT_CALL(instance_, IsPaymentImplemented(testing::_, testing::_))
+  EXPECT_CALL(*support_.instance(),
+              IsPaymentImplemented(testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const std::string& package_name,
              ArcPaymentAppBridge::IsPaymentImplementedCallback callback) {
@@ -161,7 +133,7 @@
                 mojom::IsPaymentImplementedResult::NewError("Error message."));
           }));
 
-  ArcPaymentAppBridge::GetForBrowserContextForTesting(&context_)
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
       ->IsPaymentImplemented(
           "com.example.app",
           base::BindOnce(&ArcPaymentAppBridgeTest::OnPaymentImplementedResponse,
@@ -176,12 +148,14 @@
 TEST_F(ArcPaymentAppBridgeTest, UnableToConnectInIsReadyToPay) {
   // Intentionally do not set an instance.
 
-  EXPECT_CALL(instance_, IsReadyToPay(testing::_, testing::_)).Times(0);
+  EXPECT_CALL(*support_.instance(), IsReadyToPay(testing::_, testing::_))
+      .Times(0);
 
-  ArcPaymentAppBridge::GetForBrowserContextForTesting(&context_)->IsReadyToPay(
-      mojom::PaymentParameters::New(),
-      base::BindOnce(&ArcPaymentAppBridgeTest::OnIsReadyToPayResponse,
-                     base::Unretained(this)));
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
+      ->IsReadyToPay(
+          mojom::PaymentParameters::New(),
+          base::BindOnce(&ArcPaymentAppBridgeTest::OnIsReadyToPayResponse,
+                         base::Unretained(this)));
 
   ASSERT_FALSE(is_ready_to_pay_.is_null());
   EXPECT_FALSE(is_ready_to_pay_->is_response());
@@ -190,9 +164,9 @@
 }
 
 TEST_F(ArcPaymentAppBridgeTest, IsReadyToPay) {
-  ScopedSetInstance scoped_set_instance(&manager_, &instance_);
+  auto scoped_set_instance = support_.CreateScopedSetInstance();
 
-  EXPECT_CALL(instance_, IsReadyToPay(testing::_, testing::_))
+  EXPECT_CALL(*support_.instance(), IsReadyToPay(testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](mojom::PaymentParametersPtr parameters,
              ArcPaymentAppBridge::IsReadyToPayCallback callback) {
@@ -200,10 +174,11 @@
                 mojom::IsReadyToPayResult::NewResponse(true));
           }));
 
-  ArcPaymentAppBridge::GetForBrowserContextForTesting(&context_)->IsReadyToPay(
-      mojom::PaymentParameters::New(),
-      base::BindOnce(&ArcPaymentAppBridgeTest::OnIsReadyToPayResponse,
-                     base::Unretained(this)));
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
+      ->IsReadyToPay(
+          mojom::PaymentParameters::New(),
+          base::BindOnce(&ArcPaymentAppBridgeTest::OnIsReadyToPayResponse,
+                         base::Unretained(this)));
 
   ASSERT_FALSE(is_ready_to_pay_.is_null());
   EXPECT_FALSE(is_ready_to_pay_->is_error());
@@ -212,9 +187,9 @@
 }
 
 TEST_F(ArcPaymentAppBridgeTest, IsNotReadyToPay) {
-  ScopedSetInstance scoped_set_instance(&manager_, &instance_);
+  auto scoped_set_instance = support_.CreateScopedSetInstance();
 
-  EXPECT_CALL(instance_, IsReadyToPay(testing::_, testing::_))
+  EXPECT_CALL(*support_.instance(), IsReadyToPay(testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](mojom::PaymentParametersPtr parameters,
              ArcPaymentAppBridge::IsReadyToPayCallback callback) {
@@ -222,10 +197,11 @@
                 mojom::IsReadyToPayResult::NewResponse(false));
           }));
 
-  ArcPaymentAppBridge::GetForBrowserContextForTesting(&context_)->IsReadyToPay(
-      mojom::PaymentParameters::New(),
-      base::BindOnce(&ArcPaymentAppBridgeTest::OnIsReadyToPayResponse,
-                     base::Unretained(this)));
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
+      ->IsReadyToPay(
+          mojom::PaymentParameters::New(),
+          base::BindOnce(&ArcPaymentAppBridgeTest::OnIsReadyToPayResponse,
+                         base::Unretained(this)));
 
   ASSERT_FALSE(is_ready_to_pay_.is_null());
   EXPECT_FALSE(is_ready_to_pay_->is_error());
@@ -233,5 +209,101 @@
   EXPECT_FALSE(is_ready_to_pay_->get_response());
 }
 
+TEST_F(ArcPaymentAppBridgeTest, UnableToConnectInInvokePaymentApp) {
+  // Intentionally do not set an instance.
+
+  EXPECT_CALL(*support_.instance(), InvokePaymentApp(testing::_, testing::_))
+      .Times(0);
+
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
+      ->InvokePaymentApp(
+          mojom::PaymentParameters::New(),
+          base::BindOnce(&ArcPaymentAppBridgeTest::OnInvokePaymentAppResponse,
+                         base::Unretained(this)));
+
+  ASSERT_FALSE(invoke_app_.is_null());
+  EXPECT_FALSE(invoke_app_->is_valid());
+  ASSERT_TRUE(invoke_app_->is_error());
+  EXPECT_EQ("Unable to invoke Android apps.", invoke_app_->get_error());
+}
+
+TEST_F(ArcPaymentAppBridgeTest, InvokePaymentAppResultOK) {
+  auto scoped_set_instance = support_.CreateScopedSetInstance();
+
+  EXPECT_CALL(*support_.instance(), InvokePaymentApp(testing::_, testing::_))
+      .WillOnce(testing::Invoke(
+          [](mojom::PaymentParametersPtr parameters,
+             ArcPaymentAppBridge::InvokePaymentAppCallback callback) {
+            auto valid = mojom::InvokePaymentAppValidResult::New();
+            valid->is_activity_result_ok = true;
+            valid->stringified_details = "{}";
+            std::move(callback).Run(
+                mojom::InvokePaymentAppResult::NewValid(std::move(valid)));
+          }));
+
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
+      ->InvokePaymentApp(
+          mojom::PaymentParameters::New(),
+          base::BindOnce(&ArcPaymentAppBridgeTest::OnInvokePaymentAppResponse,
+                         base::Unretained(this)));
+
+  ASSERT_FALSE(invoke_app_.is_null());
+  EXPECT_FALSE(invoke_app_->is_error());
+  ASSERT_TRUE(invoke_app_->is_valid());
+  ASSERT_FALSE(invoke_app_->get_valid().is_null());
+  EXPECT_TRUE(invoke_app_->get_valid()->is_activity_result_ok);
+  EXPECT_EQ("{}", invoke_app_->get_valid()->stringified_details);
+}
+
+TEST_F(ArcPaymentAppBridgeTest, InvokePaymentAppResultCancelled) {
+  auto scoped_set_instance = support_.CreateScopedSetInstance();
+
+  EXPECT_CALL(*support_.instance(), InvokePaymentApp(testing::_, testing::_))
+      .WillOnce(testing::Invoke(
+          [](mojom::PaymentParametersPtr parameters,
+             ArcPaymentAppBridge::InvokePaymentAppCallback callback) {
+            auto valid = mojom::InvokePaymentAppValidResult::New();
+            // User cancelled payment.
+            valid->is_activity_result_ok = false;
+            std::move(callback).Run(
+                mojom::InvokePaymentAppResult::NewValid(std::move(valid)));
+          }));
+
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
+      ->InvokePaymentApp(
+          mojom::PaymentParameters::New(),
+          base::BindOnce(&ArcPaymentAppBridgeTest::OnInvokePaymentAppResponse,
+                         base::Unretained(this)));
+
+  ASSERT_FALSE(invoke_app_.is_null());
+  EXPECT_FALSE(invoke_app_->is_error());
+  ASSERT_TRUE(invoke_app_->is_valid());
+  ASSERT_FALSE(invoke_app_->get_valid().is_null());
+  EXPECT_FALSE(invoke_app_->get_valid()->is_activity_result_ok);
+}
+
+TEST_F(ArcPaymentAppBridgeTest, InvokePaymentAppError) {
+  auto scoped_set_instance = support_.CreateScopedSetInstance();
+
+  EXPECT_CALL(*support_.instance(), InvokePaymentApp(testing::_, testing::_))
+      .WillOnce(testing::Invoke(
+          [](mojom::PaymentParametersPtr parameters,
+             ArcPaymentAppBridge::InvokePaymentAppCallback callback) {
+            std::move(callback).Run(
+                mojom::InvokePaymentAppResult::NewError("Error message."));
+          }));
+
+  ArcPaymentAppBridge::GetForBrowserContextForTesting(support_.context())
+      ->InvokePaymentApp(
+          mojom::PaymentParameters::New(),
+          base::BindOnce(&ArcPaymentAppBridgeTest::OnInvokePaymentAppResponse,
+                         base::Unretained(this)));
+
+  ASSERT_FALSE(invoke_app_.is_null());
+  EXPECT_FALSE(invoke_app_->is_valid());
+  ASSERT_TRUE(invoke_app_->is_error());
+  EXPECT_EQ("Error message.", invoke_app_->get_error());
+}
+
 }  // namespace
 }  // namespace arc
diff --git a/components/arc/test/DEPS b/components/arc/test/DEPS
index cc564af..03ebaac 100644
--- a/components/arc/test/DEPS
+++ b/components/arc/test/DEPS
@@ -1,6 +1,3 @@
-specific_include_rules = {
-  "test_browser_context.h": [
-    "+content/public/test/test_browser_context.h",
-  ]
+include_rules = {
+  "+content/public/test",
 }
-
diff --git a/components/arc/test/OWNERS b/components/arc/test/OWNERS
index 4a73d756..96e956b 100644
--- a/components/arc/test/OWNERS
+++ b/components/arc/test/OWNERS
@@ -1 +1,2 @@
 per-file fake_*_instance.*=*
+per-file payment_app_service_test_support.cc=file://components/arc/pay/OWNERS
diff --git a/components/arc/test/arc_payment_app_bridge_test_support.cc b/components/arc/test/arc_payment_app_bridge_test_support.cc
new file mode 100644
index 0000000..eb8c6ee
--- /dev/null
+++ b/components/arc/test/arc_payment_app_bridge_test_support.cc
@@ -0,0 +1,37 @@
+// 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 "components/arc/test/arc_payment_app_bridge_test_support.h"
+
+#include "components/arc/session/arc_bridge_service.h"
+
+namespace arc {
+
+ArcPaymentAppBridgeTestSupport::MockPaymentAppInstance::
+    MockPaymentAppInstance() = default;
+
+ArcPaymentAppBridgeTestSupport::MockPaymentAppInstance::
+    ~MockPaymentAppInstance() = default;
+
+ArcPaymentAppBridgeTestSupport::ScopedSetInstance::ScopedSetInstance(
+    ArcServiceManager* manager,
+    mojom::PaymentAppInstance* instance)
+    : manager_(manager), instance_(instance) {
+  manager_->arc_bridge_service()->payment_app()->SetInstance(instance_);
+}
+
+ArcPaymentAppBridgeTestSupport::ScopedSetInstance::~ScopedSetInstance() {
+  manager_->arc_bridge_service()->payment_app()->CloseInstance(instance_);
+}
+
+ArcPaymentAppBridgeTestSupport::ArcPaymentAppBridgeTestSupport() = default;
+
+ArcPaymentAppBridgeTestSupport::~ArcPaymentAppBridgeTestSupport() = default;
+
+std::unique_ptr<ArcPaymentAppBridgeTestSupport::ScopedSetInstance>
+ArcPaymentAppBridgeTestSupport::CreateScopedSetInstance() {
+  return std::make_unique<ScopedSetInstance>(manager(), instance());
+}
+
+}  // namespace arc
diff --git a/components/arc/test/arc_payment_app_bridge_test_support.h b/components/arc/test/arc_payment_app_bridge_test_support.h
new file mode 100644
index 0000000..7c14fa2
--- /dev/null
+++ b/components/arc/test/arc_payment_app_bridge_test_support.h
@@ -0,0 +1,104 @@
+// 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 COMPONENTS_ARC_TEST_ARC_PAYMENT_APP_BRIDGE_TEST_SUPPORT_H_
+#define COMPONENTS_ARC_TEST_ARC_PAYMENT_APP_BRIDGE_TEST_SUPPORT_H_
+
+#include <memory>
+#include <string>
+
+#include "components/arc/arc_service_manager.h"
+#include "components/arc/mojom/payment_app.mojom.h"
+#include "components/arc/pay/arc_payment_app_bridge.h"
+#include "components/arc/test/test_browser_context.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace arc {
+
+// Common support utility for tests of payment_app.mojom interface.
+class ArcPaymentAppBridgeTestSupport {
+ public:
+  // The mock payment_app.mojom interface.
+  class MockPaymentAppInstance : public mojom::PaymentAppInstance {
+   public:
+    MockPaymentAppInstance();
+    ~MockPaymentAppInstance() override;
+
+    MockPaymentAppInstance(const MockPaymentAppInstance& other) = delete;
+    MockPaymentAppInstance& operator=(const MockPaymentAppInstance& other) =
+        delete;
+
+    MOCK_METHOD2(
+        IsPaymentImplemented,
+        void(const std::string& package_name,
+             ArcPaymentAppBridge::IsPaymentImplementedCallback callback));
+    MOCK_METHOD2(IsReadyToPay,
+                 void(mojom::PaymentParametersPtr,
+                      ArcPaymentAppBridge::IsReadyToPayCallback));
+    MOCK_METHOD2(InvokePaymentApp,
+                 void(mojom::PaymentParametersPtr,
+                      ArcPaymentAppBridge::InvokePaymentAppCallback));
+  };
+
+  // Sets up the payment_app.mojom connection in the constructor and disconnects
+  // in the destructor.
+  class ScopedSetInstance {
+   public:
+    ScopedSetInstance(ArcServiceManager* manager,
+                      mojom::PaymentAppInstance* instance);
+    ~ScopedSetInstance();
+
+    ScopedSetInstance(const ScopedSetInstance& other) = delete;
+    ScopedSetInstance& operator=(const ScopedSetInstance& other) = delete;
+
+   private:
+    ArcServiceManager* manager_;
+    mojom::PaymentAppInstance* instance_;
+  };
+
+  ArcPaymentAppBridgeTestSupport();
+  ~ArcPaymentAppBridgeTestSupport();
+
+  ArcPaymentAppBridgeTestSupport(const ArcPaymentAppBridgeTestSupport& other) =
+      delete;
+  ArcPaymentAppBridgeTestSupport& operator=(
+      const ArcPaymentAppBridgeTestSupport& other) = delete;
+
+  // Creates a ScopedSetInstance to be placed on the stack, so that
+  // payment_app.mojom connection is available in the test and is cleaned up
+  // when the returned value goes out of scope.
+  std::unique_ptr<ScopedSetInstance> CreateScopedSetInstance();
+
+  // The ARC service manager.
+  ArcServiceManager* manager() { return ArcServiceManager::Get(); }
+
+  // The mock payment_app.mojom connection.
+  MockPaymentAppInstance* instance() { return &instance_; }
+
+  // The browser context that should be used in the test.
+  content::BrowserContext* context() { return &context_; }
+
+ private:
+  // Required for the TestBrowserContext and ArcServiceManager.
+  content::BrowserTaskEnvironment task_environment_;
+
+  // Used for retrieving an instance of ArcPaymentAppBridge owned by a
+  // BrowserContext.
+  TestBrowserContext context_;
+
+  // The unit test must create an instance of ArcServiceManager for
+  // ArcServiceManager::Get() to work correctly.
+  ArcServiceManager manager_;
+
+  MockPaymentAppInstance instance_;
+};
+
+}  // namespace arc
+
+#endif  // COMPONENTS_ARC_TEST_ARC_PAYMENT_APP_BRIDGE_TEST_SUPPORT_H_
diff --git a/components/autofill/core/common/autofill_regex_constants.cc b/components/autofill/core/common/autofill_regex_constants.cc
index fef8cd6..da36233 100644
--- a/components/autofill/core/common/autofill_regex_constants.cc
+++ b/components/autofill/core/common/autofill_regex_constants.cc
@@ -317,9 +317,7 @@
     "|(\\b|_|\\*)(isim|ad|ad(i|ı|iniz|ınız)?)(\\b|_|\\*)"  // tr
     "|नाम";                                                // hi
 const char kMiddleInitialRe[] = "middle.*initial|m\\.i\\.|mi$|\\bmi\\b";
-const char kMiddleNameRe[] =
-    "middle.*name|mname|middle$"
-    "|apellido.?materno|lastlastname";  // es
+const char kMiddleNameRe[] = "middle.*name|mname|middle$";
 const char kLastNameRe[] =
     "last.*name|lname|surname(?!\\d)|last$|secondname|family.*name"
     "|nachname"                                               // de-DE
diff --git a/components/autofill/core/common/password_form.h b/components/autofill/core/common/password_form.h
index 40b29f2..ef87deb 100644
--- a/components/autofill/core/common/password_form.h
+++ b/components/autofill/core/common/password_form.h
@@ -118,6 +118,9 @@
   // form parsing and not persisted.
   FieldRendererId username_element_renderer_id;
 
+  // True if the server-side classification was successful.
+  bool server_side_classification_successful = false;
+
   // True if the server-side classification believes that the field may be
   // pre-filled with a placeholder in the value attribute. It is set during
   // form parsing and not persisted.
diff --git a/components/browser_ui/strings/android/site_settings.grdp b/components/browser_ui/strings/android/site_settings.grdp
index af65c55..767e05e 100644
--- a/components/browser_ui/strings/android/site_settings.grdp
+++ b/components/browser_ui/strings/android/site_settings.grdp
@@ -211,7 +211,7 @@
     You'll be signed out of all sites.
   </message>
   <message name="IDS_STORAGE_CLEAR_SITE_STORAGE_TITLE" desc="Title of clear storage dialogs used in a couple different places to confirm clearing site storage data. [CHAR-LIMIT=24]">
-    Clear site storage?
+    Clear site data?
   </message>
   <message name="IDS_STORAGE_CLEAR_DIALOG_CLEAR_STORAGE_OPTION" desc="Text of the button that will clear website storage.">
     Clear
diff --git a/components/browser_ui/strings/android/site_settings_grdp/IDS_STORAGE_CLEAR_SITE_STORAGE_TITLE.png.sha1 b/components/browser_ui/strings/android/site_settings_grdp/IDS_STORAGE_CLEAR_SITE_STORAGE_TITLE.png.sha1
new file mode 100644
index 0000000..91d626e
--- /dev/null
+++ b/components/browser_ui/strings/android/site_settings_grdp/IDS_STORAGE_CLEAR_SITE_STORAGE_TITLE.png.sha1
@@ -0,0 +1 @@
+870987174981c44e16485836e453676f4129b6dc
\ No newline at end of file
diff --git a/components/exo/data_offer.cc b/components/exo/data_offer.cc
index 9027d77c..cb558f5 100644
--- a/components/exo/data_offer.cc
+++ b/components/exo/data_offer.cc
@@ -31,6 +31,8 @@
 namespace exo {
 namespace {
 
+constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8";
+constexpr char kUtf8String[] = "UTF8_STRING";
 constexpr char kTextMimeTypeUtf16[] = "text/plain;charset=utf-16";
 constexpr char kTextHtmlMimeTypeUtf8[] = "text/html;charset=utf-8";
 constexpr char kTextHtmlMimeTypeUtf16[] = "text/html;charset=utf-16";
@@ -277,7 +279,7 @@
 
   base::string16 string_content;
   if (data.HasString() && data.GetString(&string_content)) {
-    const std::string utf8_mime_type = std::string(ui::kMimeTypeTextUtf8);
+    const std::string utf8_mime_type = std::string(kTextMimeTypeUtf8);
     data_.emplace(utf8_mime_type,
                   EncodeAsRefCountedString(string_content, kUTF8));
     delegate_->OnOffer(utf8_mime_type);
@@ -319,11 +321,10 @@
                              /* data_dst = */ nullptr)) {
     auto utf8_callback =
         base::BindRepeating(&ReadTextFromClipboard, std::string(kUTF8));
-    delegate_->OnOffer(std::string(ui::kMimeTypeTextUtf8));
-    data_callbacks_.emplace(std::string(ui::kMimeTypeTextUtf8), utf8_callback);
-    delegate_->OnOffer(std::string(ui::kMimeTypeLinuxUtf8String));
-    data_callbacks_.emplace(std::string(ui::kMimeTypeLinuxUtf8String),
-                            utf8_callback);
+    delegate_->OnOffer(std::string(kTextMimeTypeUtf8));
+    data_callbacks_.emplace(std::string(kTextMimeTypeUtf8), utf8_callback);
+    delegate_->OnOffer(std::string(kUtf8String));
+    data_callbacks_.emplace(std::string(kUtf8String), utf8_callback);
     delegate_->OnOffer(std::string(kTextMimeTypeUtf16));
     data_callbacks_.emplace(
         std::string(kTextMimeTypeUtf16),
diff --git a/components/exo/data_source.cc b/components/exo/data_source.cc
index e612082731..53765d91 100644
--- a/components/exo/data_source.cc
+++ b/components/exo/data_source.cc
@@ -22,7 +22,6 @@
 #include "net/base/mime_util.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 #include "third_party/icu/source/common/unicode/ucnv.h"
-#include "ui/base/clipboard/clipboard_constants.h"
 
 namespace exo {
 
@@ -249,7 +248,7 @@
 
   for (auto mime_type : mime_types_) {
     if (net::MatchesMimeType(std::string(kTextPlain), mime_type) ||
-        mime_type == ui::kMimeTypeLinuxUtf8String) {
+        mime_type == kEncodingUTF8Legacy) {
       if (text_reader.is_null())
         continue;
 
diff --git a/components/exo/mime_utils.cc b/components/exo/mime_utils.cc
index 40ae71f4..b988607 100644
--- a/components/exo/mime_utils.cc
+++ b/components/exo/mime_utils.cc
@@ -4,8 +4,6 @@
 
 #include "components/exo/mime_utils.h"
 
-#include "ui/base/clipboard/clipboard_constants.h"
-
 namespace {
 
 constexpr char kCharset[] = ";charset=";
@@ -18,7 +16,7 @@
 
 std::string GetCharset(const std::string& mime_type) {
   // We special case UTF8_STRING to provide minimal handling of X11 apps.
-  if (mime_type == ui::kMimeTypeLinuxUtf8String)
+  if (mime_type == kEncodingUTF8Legacy)
     return std::string(kEncodingUTF8Charset);
 
   auto pos = mime_type.find(kCharset);
diff --git a/components/exo/mime_utils.h b/components/exo/mime_utils.h
index 5905e17..8e2b4b5 100644
--- a/components/exo/mime_utils.h
+++ b/components/exo/mime_utils.h
@@ -9,6 +9,8 @@
 
 namespace exo {
 
+constexpr char kEncodingUTF8Legacy[] = "UTF8_STRING";
+
 // Takes a text/* mime type and returns the name of the character set specified
 // in the type. If no character set is specified, defaults to US-ASCII.
 std::string GetCharset(const std::string& mime_type);
diff --git a/components/management/resources/images/enterprise_icon.svg b/components/management/resources/images/enterprise_icon.svg
new file mode 100644
index 0000000..436a7d6
--- /dev/null
+++ b/components/management/resources/images/enterprise_icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#5f6368"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"/></svg>
\ No newline at end of file
diff --git a/components/management/resources/management.css b/components/management/resources/management.css
index 9243ca8..6f01ff0 100644
--- a/components/management/resources/management.css
+++ b/components/management/resources/management.css
@@ -3,14 +3,14 @@
  * found in the LICENSE file.
  */
 
-/* TODO(crbug.com/1108981): adjust the style when the design is finalized. */
 body {
   color: rgb(95, 99, 104);
   font-size: 2em;
-  margin: 10%;
+  line-height: 1.5;
+  margin: 5%;
   min-width: 500;  /* Wrap content to screen on mobile. */
   text-align: start;
-  width: 80%;
+  width: 90%;
 }
 
 a {
@@ -18,10 +18,20 @@
   text-decoration: none;
 }
 
-#main-message {
-  margin-bottom: 2em;
+img {
+  height: auto;
+  margin-top: 2em;
+  width: 15%;
+}
+
+#main-message,
+#unmanaged-info {
+  color: black;
+  font-size: 1.5em;
+  margin-bottom: 1em;
+  margin-top: 2em;
 }
 
 .hidden {
   display: none;
-}
\ No newline at end of file
+}
diff --git a/components/management/resources/management.html b/components/management/resources/management.html
index b5a8d96..168c8bbf 100644
--- a/components/management/resources/management.html
+++ b/components/management/resources/management.html
@@ -24,6 +24,7 @@
   </script>
 </head>
 <body>
+  <img src="images/enterprise_icon.svg">
   <div id="managed-info" class="hidden">
     <div id="main-message">$i18n{managementMessage}</div>
     <div id="secondary-message">$i18n{managedInfo}
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 8064739..618f750 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -275,8 +275,6 @@
     "location_bar_model_delegate.h",
     "location_bar_model_impl.cc",
     "location_bar_model_impl.h",
-    "location_bar_model_util.cc",
-    "location_bar_model_util.h",
   ]
 
   public_deps = [
diff --git a/components/omnibox/browser/autocomplete_classifier.cc b/components/omnibox/browser/autocomplete_classifier.cc
index 5ea9964..a7c54964 100644
--- a/components/omnibox/browser/autocomplete_classifier.cc
+++ b/components/omnibox/browser/autocomplete_classifier.cc
@@ -43,19 +43,9 @@
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
       // Custom search engines cannot be used on mobile.
       AutocompleteProvider::TYPE_KEYWORD |
-#endif
-#if defined(OS_ANDROID)
+#else
       AutocompleteProvider::TYPE_CLIPBOARD |
 #endif
-#if defined(OS_IOS)
-      // On iOS 14, a notification appears whenever the clipboard is accessed.
-      // The clipboard provider accesses the clipboard every time the omnibox is
-      // opened. Until a better solution is found, disable the clipboard
-      // provider temporarily. See crbug.com/1098722.
-      (!base::ios::IsRunningOnIOS14OrLater()
-           ? AutocompleteProvider::TYPE_CLIPBOARD
-           : 0) |
-#endif
       AutocompleteProvider::TYPE_ZERO_SUGGEST |
       AutocompleteProvider::TYPE_ZERO_SUGGEST_LOCAL_HISTORY |
       (base::FeatureList::IsEnabled(omnibox::kDocumentProvider)
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 345ff70..f66274cd 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -320,9 +320,10 @@
     // ClipboardRecentContent can be null in iOS tests.  For non-iOS, we
     // create a ClipboardRecentContent as above (for both Chrome and tests).
     if (ClipboardRecentContent::GetInstance()) {
-      providers_.push_back(new ClipboardProvider(
+      clipboard_provider_ = new ClipboardProvider(
           provider_client_.get(), this, history_url_provider_,
-          ClipboardRecentContent::GetInstance()));
+          ClipboardRecentContent::GetInstance());
+      providers_.push_back(clipboard_provider_);
     }
   }
 
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index 7b4f0ff..a2f127e 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -24,6 +24,7 @@
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/autocomplete_result.h"
 
+class ClipboardProvider;
 class DocumentProvider;
 class HistoryURLProvider;
 class KeywordProvider;
@@ -160,6 +161,7 @@
   }
   KeywordProvider* keyword_provider() const { return keyword_provider_; }
   SearchProvider* search_provider() const { return search_provider_; }
+  ClipboardProvider* clipboard_provider() const { return clipboard_provider_; }
 
   const AutocompleteInput& input() const { return input_; }
   const AutocompleteResult& result() const { return result_; }
@@ -294,6 +296,8 @@
 
   OnDeviceHeadProvider* on_device_head_provider_;
 
+  ClipboardProvider* clipboard_provider_;
+
   // Input passed to Start.
   AutocompleteInput input_;
 
diff --git a/components/omnibox/browser/clipboard_provider.cc b/components/omnibox/browser/clipboard_provider.cc
index d15b6c3..25b3b18 100644
--- a/components/omnibox/browser/clipboard_provider.cc
+++ b/components/omnibox/browser/clipboard_provider.cc
@@ -150,16 +150,37 @@
   if (CreateImageMatch(input))
     return;
 
-  base::Optional<AutocompleteMatch> optional_match = CreateURLMatch(input);
-  if (!optional_match)
-    optional_match = CreateTextMatch(input);
+  bool read_clipboard_content = false;
+  bool read_clipboard_url;
+  base::Optional<AutocompleteMatch> optional_match =
+      CreateURLMatch(input, &read_clipboard_url);
+  read_clipboard_content |= read_clipboard_url;
+  if (!optional_match) {
+    bool read_clipboard_text;
+    optional_match = CreateTextMatch(input, &read_clipboard_text);
+    read_clipboard_content |= read_clipboard_text;
+  }
 
-  // The clipboard does not contain any suggestions
-  if (!optional_match)
+  if (optional_match) {
+    AddCreatedMatchWithTracking(input, std::move(optional_match).value(),
+                                clipboard_content_->GetClipboardContentAge());
     return;
+  }
 
-  AddCreatedMatchWithTracking(input, std::move(optional_match).value(),
-                              clipboard_content_->GetClipboardContentAge());
+  // If there was clipboard content, but no match, don't proceed. There was
+  // some other reason for not creating a match (e.g. copied URL but the URL was
+  // the same as the current URL).
+  if (read_clipboard_content) {
+    return;
+  }
+
+  // On iOS 14, accessing the clipboard contents shows a notification to the
+  // user. To avoid this, all the methods above will not check the contents and
+  // will return false/base::nullopt. Instead, check the existence of content
+  // without accessing the actual content and create blank matches.
+  done_ = false;
+  // Image matched was kicked off asynchronously, so proceed when that ends.
+  CheckClipboardContent(input);
 }
 
 void ClipboardProvider::Stop(bool clear_cached_results,
@@ -226,7 +247,6 @@
     current_url_suggested_times_ = 1;
   }
 
-
   // If the omnibox is not empty, add a default match.
   // This match will be opened when the user presses "Enter".
   if (!input.text().empty()) {
@@ -247,14 +267,97 @@
   matches_.push_back(match);
 }
 
+bool ClipboardProvider::TemplateURLSupportsTextSearch() {
+  TemplateURLService* url_service = client_->GetTemplateURLService();
+  const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
+  if (!default_url)
+    return false;
+
+  DCHECK(!default_url->url().empty());
+  DCHECK(default_url->url_ref().IsValid(url_service->search_terms_data()));
+  return true;
+}
+
+bool ClipboardProvider::TemplateURLSupportsImageSearch() {
+  TemplateURLService* url_service = client_->GetTemplateURLService();
+  const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
+
+  return default_url && !default_url->image_url().empty() &&
+         default_url->image_url_ref().IsValid(url_service->search_terms_data());
+}
+
+void ClipboardProvider::CheckClipboardContent(const AutocompleteInput& input) {
+  std::set<ClipboardContentType> desired_types;
+  desired_types.insert(ClipboardContentType::URL);
+
+  if (TemplateURLSupportsTextSearch()) {
+    desired_types.insert(ClipboardContentType::Text);
+  }
+
+  if (base::FeatureList::IsEnabled(
+          omnibox::kEnableClipboardProviderImageSuggestions) &&
+      TemplateURLSupportsImageSearch()) {
+    desired_types.insert(ClipboardContentType::Image);
+  }
+
+  // We want to get the age here because the contents of the clipboard could
+  // change after this point. We want the age of the contents we actually use,
+  // not the age of whatever's on the clipboard when the histogram is created
+  // (i.e when the match is created).
+  base::TimeDelta clipboard_contents_age =
+      clipboard_content_->GetClipboardContentAge();
+  clipboard_content_->HasRecentContentFromClipboard(
+      desired_types,
+      base::BindOnce(&ClipboardProvider::OnReceiveClipboardContent,
+                     callback_weak_ptr_factory_.GetWeakPtr(), input,
+                     clipboard_contents_age));
+}
+
+void ClipboardProvider::OnReceiveClipboardContent(
+    const AutocompleteInput& input,
+    base::TimeDelta clipboard_contents_age,
+    std::set<ClipboardContentType> matched_types) {
+  if (matched_types.find(ClipboardContentType::Image) != matched_types.end()) {
+    // The image content will be added in later. If the image is large, encoding
+    // the image may take some time, so just be wary whenever that step happens
+    // (e.g OmniboxView::OpenMatch).
+    AutocompleteMatch match = NewBlankImageMatch();
+    field_trial_triggered_ = true;
+    field_trial_triggered_in_session_ = true;
+    // Some users may be in a counterfactual study arm in which we perform all
+    // necessary work but do not forward the autocomplete matches.
+    bool in_counterfactual_group = base::GetFieldTrialParamByFeatureAsBool(
+        omnibox::kEnableClipboardProviderImageSuggestions,
+        "ClipboardProviderImageSuggestionsCounterfactualArm", false);
+    if (!in_counterfactual_group) {
+      AddCreatedMatchWithTracking(input, match, clipboard_contents_age);
+      listener_->OnProviderUpdate(true);
+    }
+  } else if (matched_types.find(ClipboardContentType::URL) !=
+             matched_types.end()) {
+    AutocompleteMatch match = NewBlankURLMatch();
+    AddCreatedMatchWithTracking(input, match, clipboard_contents_age);
+    listener_->OnProviderUpdate(true);
+  } else if (matched_types.find(ClipboardContentType::Text) !=
+             matched_types.end()) {
+    AutocompleteMatch match = NewBlankTextMatch();
+    AddCreatedMatchWithTracking(input, match, clipboard_contents_age);
+    listener_->OnProviderUpdate(true);
+  }
+  done_ = true;
+}
+
 base::Optional<AutocompleteMatch> ClipboardProvider::CreateURLMatch(
-    const AutocompleteInput& input) {
+    const AutocompleteInput& input,
+    bool* read_clipboard_content) {
+  *read_clipboard_content = false;
   // The clipboard does not contain a URL worth suggesting.
   base::Optional<GURL> optional_gurl =
       clipboard_content_->GetRecentURLFromClipboard();
   if (!optional_gurl)
     return base::nullopt;
 
+  *read_clipboard_content = true;
   GURL url = std::move(optional_gurl).value();
 
   // The URL on the page is the same as the URL in the clipboard.  Don't
@@ -262,11 +365,103 @@
   if (url == input.current_url())
     return base::nullopt;
 
-  DCHECK(url.is_valid());
+  return NewClipboardURLMatch(url);
+}
 
+base::Optional<AutocompleteMatch> ClipboardProvider::CreateTextMatch(
+    const AutocompleteInput& input,
+    bool* read_clipboard_content) {
+  *read_clipboard_content = false;
+  base::Optional<base::string16> optional_text =
+      clipboard_content_->GetRecentTextFromClipboard();
+  if (!optional_text)
+    return base::nullopt;
+
+  *read_clipboard_content = true;
+  base::string16 text = std::move(optional_text).value();
+
+  // The clipboard can contain the empty string, which shouldn't be suggested.
+  if (text.empty())
+    return base::nullopt;
+
+  // The text in the clipboard is a url. We don't want to prompt the user to
+  // search for a url.
+  if (GURL(text).is_valid())
+    return base::nullopt;
+
+  return NewClipboardTextMatch(text);
+}
+
+bool ClipboardProvider::CreateImageMatch(const AutocompleteInput& input) {
+  // Only try image match if feature is enabled
+  if (!base::FeatureList::IsEnabled(
+          omnibox::kEnableClipboardProviderImageSuggestions)) {
+    return false;
+  }
+
+  if (!clipboard_content_->HasRecentImageFromClipboard()) {
+    return false;
+  }
+
+  if (!TemplateURLSupportsImageSearch()) {
+    return false;
+  }
+
+  done_ = false;
+
+  // We want to get the age here because the contents of the clipboard could
+  // change after this point. We want the age of the image we actually use, not
+  // the age of whatever's on the clipboard when the histogram is created (i.e
+  // when the match is created).
+  base::TimeDelta clipboard_contents_age =
+      clipboard_content_->GetClipboardContentAge();
+  clipboard_content_->GetRecentImageFromClipboard(base::BindOnce(
+      &ClipboardProvider::CreateImageMatchCallback,
+      callback_weak_ptr_factory_.GetWeakPtr(), input, clipboard_contents_age));
+  return true;
+}
+
+void ClipboardProvider::CreateImageMatchCallback(
+    const AutocompleteInput& input,
+    const base::TimeDelta clipboard_contents_age,
+    base::Optional<gfx::Image> optional_image) {
+  if (!optional_image) {
+    return;
+  }
+  NewClipboardImageMatch(
+      optional_image.value(),
+      base::BindOnce(&ClipboardProvider::AddImageMatchCallback,
+                     callback_weak_ptr_factory_.GetWeakPtr(), input,
+                     clipboard_contents_age));
+}
+void ClipboardProvider::AddImageMatchCallback(
+    const AutocompleteInput& input,
+    const base::TimeDelta clipboard_contents_age,
+    base::Optional<AutocompleteMatch> match) {
+  if (!match) {
+    return;
+  }
+  AddCreatedMatchWithTracking(input, match.value(), clipboard_contents_age);
+  listener_->OnProviderUpdate(true);
+  done_ = true;
+}
+
+AutocompleteMatch ClipboardProvider::NewBlankURLMatch() {
   AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
                           IsMatchDeletionEnabled(),
                           AutocompleteMatchType::CLIPBOARD_URL);
+
+  match.description.assign(l10n_util::GetStringUTF16(IDS_LINK_FROM_CLIPBOARD));
+  if (!match.description.empty())
+    match.description_class.push_back({0, ACMatchClassification::NONE});
+  return match;
+}
+
+AutocompleteMatch ClipboardProvider::NewClipboardURLMatch(GURL url) {
+  DCHECK(url.is_valid());
+
+  AutocompleteMatch match = NewBlankURLMatch();
+
   match.destination_url = url;
 
   // Because the user did not type a related input to get this clipboard
@@ -279,35 +474,30 @@
   match.fill_into_edit =
       AutocompleteInput::FormattedStringWithEquivalentMeaning(
           url, match.contents, client_->GetSchemeClassifier(), nullptr);
-
-  match.description.assign(l10n_util::GetStringUTF16(IDS_LINK_FROM_CLIPBOARD));
-  if (!match.description.empty())
-    match.description_class.push_back({0, ACMatchClassification::NONE});
-
   return match;
 }
 
-base::Optional<AutocompleteMatch> ClipboardProvider::CreateTextMatch(
-    const AutocompleteInput& input) {
-  base::Optional<base::string16> optional_text =
-      clipboard_content_->GetRecentTextFromClipboard();
-  if (!optional_text)
-    return base::nullopt;
+AutocompleteMatch ClipboardProvider::NewBlankTextMatch() {
+  AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
+                          IsMatchDeletionEnabled(),
+                          AutocompleteMatchType::CLIPBOARD_TEXT);
 
-  base::string16 text = std::move(optional_text).value();
+  match.description.assign(l10n_util::GetStringUTF16(IDS_TEXT_FROM_CLIPBOARD));
+  if (!match.description.empty())
+    match.description_class.push_back({0, ACMatchClassification::NONE});
 
-  // The clipboard can contain the empty string, which shouldn't be suggested.
-  if (text.empty())
-    return base::nullopt;
+  match.transition = ui::PAGE_TRANSITION_GENERATED;
+  return match;
+}
 
+base::Optional<AutocompleteMatch> ClipboardProvider::NewClipboardTextMatch(
+    base::string16 text) {
   // The text in the clipboard is a url. We don't want to prompt the user to
   // search for a url.
   if (GURL(text).is_valid())
     return base::nullopt;
 
-  AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
-                          IsMatchDeletionEnabled(),
-                          AutocompleteMatchType::CLIPBOARD_TEXT);
+  AutocompleteMatch match = NewBlankTextMatch();
   match.fill_into_edit = text;
 
   TemplateURLService* url_service = client_->GetTemplateURLService();
@@ -327,65 +517,51 @@
   if (!match.contents.empty())
     match.contents_class.push_back({0, ACMatchClassification::NONE});
 
-  match.description.assign(l10n_util::GetStringUTF16(IDS_TEXT_FROM_CLIPBOARD));
+  match.keyword = default_url->keyword();
+
+  return match;
+}
+
+AutocompleteMatch ClipboardProvider::NewBlankImageMatch() {
+  AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
+                          IsMatchDeletionEnabled(),
+                          AutocompleteMatchType::CLIPBOARD_IMAGE);
+
+  match.description.assign(l10n_util::GetStringUTF16(IDS_IMAGE_FROM_CLIPBOARD));
   if (!match.description.empty())
     match.description_class.push_back({0, ACMatchClassification::NONE});
 
-  match.keyword = default_url->keyword();
+  // This will end up being something like "Search for Copied Image." This may
+  // seem strange to use for |fill_into_edit|, but it is because iOS requires
+  // some text in the text field for the Enter key to work when using keyboard
+  // navigation.
+  match.fill_into_edit = match.description;
   match.transition = ui::PAGE_TRANSITION_GENERATED;
 
   return match;
 }
 
-bool ClipboardProvider::CreateImageMatch(const AutocompleteInput& input) {
-  // Only try image match if feature is enabled
-  if (!base::FeatureList::IsEnabled(
-          omnibox::kEnableClipboardProviderImageSuggestions)) {
-    return false;
-  }
-
-  if (!clipboard_content_->HasRecentImageFromClipboard()) {
-    return false;
-  }
-
-  // Make sure current provider supports image search
-  TemplateURLService* url_service = client_->GetTemplateURLService();
-  const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
-
-  if (!default_url || default_url->image_url().empty() ||
-      !default_url->image_url_ref().IsValid(url_service->search_terms_data())) {
-    return false;
-  }
-
-  // We want to get the age here because the contents of the clipboard could
-  // change after this point. We want the age of the image we actually use, not
-  // the age of whatever's on the clipboard when the histogram is created (i.e
-  // when the match is created).
-  base::TimeDelta clipboard_contents_age =
-      clipboard_content_->GetClipboardContentAge();
-  clipboard_content_->GetRecentImageFromClipboard(
-      base::BindOnce(&ClipboardProvider::OnReceiveImage,
-                     callback_weak_ptr_factory_.GetWeakPtr(), input,
-                     url_service, clipboard_contents_age));
-  return true;
+void ClipboardProvider::NewClipboardImageMatch(
+    gfx::Image image,
+    ClipboardImageMatchCallback callback) {
+  clipboard_content_->GetRecentImageFromClipboard(base::BindOnce(
+      &ClipboardProvider::OnReceiveImage,
+      callback_weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void ClipboardProvider::OnReceiveImage(
-    const AutocompleteInput& input,
-    TemplateURLService* url_service,
-    base::TimeDelta clipboard_contents_age,
+    ClipboardImageMatchCallback callback,
     base::Optional<gfx::Image> optional_image) {
   if (!optional_image)
-    return;
-  done_ = false;
+    std::move(callback).Run(base::nullopt);
   gfx::ImageSkia image_skia = *optional_image.value().ToImageSkia();
   image_skia.MakeThreadSafe();
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE,
       base::BindOnce(&ClipboardProvider::EncodeClipboardImage, image_skia),
       base::BindOnce(&ClipboardProvider::ConstructImageMatchCallback,
-                     callback_weak_ptr_factory_.GetWeakPtr(), input,
-                     url_service, clipboard_contents_age));
+                     callback_weak_ptr_factory_.GetWeakPtr(),
+                     std::move(callback)));
 }
 
 scoped_refptr<base::RefCountedMemory> ClipboardProvider::EncodeClipboardImage(
@@ -396,26 +572,13 @@
 }
 
 void ClipboardProvider::ConstructImageMatchCallback(
-    const AutocompleteInput& input,
-    TemplateURLService* url_service,
-    base::TimeDelta clipboard_contents_age,
+    ClipboardImageMatchCallback callback,
     scoped_refptr<base::RefCountedMemory> image_bytes) {
+  TemplateURLService* url_service = client_->GetTemplateURLService();
   const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
   DCHECK(default_url);
 
-  AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
-                          IsMatchDeletionEnabled(),
-                          AutocompleteMatchType::CLIPBOARD_IMAGE);
-
-  match.description.assign(l10n_util::GetStringUTF16(IDS_IMAGE_FROM_CLIPBOARD));
-  if (!match.description.empty())
-    match.description_class.push_back({0, ACMatchClassification::NONE});
-
-  // This will end up being something like "Search for Copied Image." This may
-  // seem strange to use for |fill_into_edit, but it is because iOS requires
-  // some text in the text field for the Enter key to work when using keyboard
-  // navigation.
-  match.fill_into_edit = match.description;
+  AutocompleteMatch match = NewBlankImageMatch();
 
   match.search_terms_args =
       std::make_unique<TemplateURLRef::SearchTermsArgs>(base::ASCIIToUTF16(""));
@@ -436,19 +599,5 @@
   match.post_content =
       std::make_unique<TemplateURLRef::PostContent>(post_content);
 
-  match.transition = ui::PAGE_TRANSITION_GENERATED;
-
-  field_trial_triggered_ = true;
-  field_trial_triggered_in_session_ = true;
-  done_ = true;
-
-  // Some users may be in a counterfactual study arm in which we perform all
-  // necessary work but do not forward the autocomplete matches.
-  bool in_counterfactual_group = base::GetFieldTrialParamByFeatureAsBool(
-      omnibox::kEnableClipboardProviderImageSuggestions,
-      "ClipboardProviderImageSuggestionsCounterfactualArm", false);
-  if (!in_counterfactual_group) {
-    AddCreatedMatchWithTracking(input, match, clipboard_contents_age);
-    listener_->OnProviderUpdate(true);
-  }
+  std::move(callback).Run(match);
 }
diff --git a/components/omnibox/browser/clipboard_provider.h b/components/omnibox/browser/clipboard_provider.h
index e75d819..20d3117 100644
--- a/components/omnibox/browser/clipboard_provider.h
+++ b/components/omnibox/browser/clipboard_provider.h
@@ -12,6 +12,7 @@
 class AutocompleteProviderClient;
 class ClipboardRecentContent;
 class HistoryURLProvider;
+enum class ClipboardContentType;
 
 // Autocomplete provider offering content based on the clipboard's content.
 class ClipboardProvider : public AutocompleteProvider {
@@ -24,6 +25,23 @@
   ClipboardProvider(const ClipboardProvider&) = delete;
   ClipboardProvider& operator=(const ClipboardProvider&) = delete;
 
+  // Returns a new AutocompleteMatch clipboard match that will navigate to the
+  // given copied url. Used to construct a match later when the URL is not
+  // available at match creation time (e.g. iOS 14).
+  AutocompleteMatch NewClipboardURLMatch(GURL url);
+  // Returns a new AutocompleteMatch clipboard match that will search for the
+  // given copied text. Used to construct a match later when the text is not
+  // available at match creation time (e.g. iOS 14).
+  base::Optional<AutocompleteMatch> NewClipboardTextMatch(base::string16 text);
+
+  using ClipboardImageMatchCallback =
+      base::OnceCallback<void(base::Optional<AutocompleteMatch>)>;
+  // Returns a new AutocompleteMatch clipboard match that will search for the
+  // given copied image. Used to construct a match later when the image is not
+  // available at match creation time (e.g. iOS 14).
+  void NewClipboardImageMatch(gfx::Image image,
+                              ClipboardImageMatchCallback callback);
+
   // AutocompleteProvider implementation.
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
   void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
@@ -43,23 +61,72 @@
       const AutocompleteMatch& match,
       const base::TimeDelta clipboard_contents_age);
 
-  // If there is a url copied to the clipboard, use it to create a match.
+  // Uses asynchronous clipboard APIs to check which content types have
+  // clipboard data without actually accessing the data. If any do, then one
+  // clipboard match is created. Calls back to |OnReceiveClipboardContent| with
+  // the result.
+  void CheckClipboardContent(const AutocompleteInput& input);
+  // Called when the clipboard data is returned from the asynchronous call.
+  void OnReceiveClipboardContent(const AutocompleteInput& input,
+                                 base::TimeDelta clipboard_contents_age,
+                                 std::set<ClipboardContentType> matched_types);
+
+  // Checks whether the current template url supports text searches.
+  bool TemplateURLSupportsTextSearch();
+  // Checks whether the current template url supports image searches.
+  bool TemplateURLSupportsImageSearch();
+
+  // Returns a URL match with no URL. This can be used if the clipboard content
+  // is inaccessible at match creation time (e.g. iOS 14).
+  AutocompleteMatch NewBlankURLMatch();
+
+  // Returns a text match with no text. This can be used if the clipboard
+  // content is inaccessible at match creation time (e.g. iOS 14).
+  AutocompleteMatch NewBlankTextMatch();
+
+  // Returns a image match with no attached image. This can be used if the
+  // clipboard content is inaccessible at match creation time (e.g. iOS 14).
+  AutocompleteMatch NewBlankImageMatch();
+
+  // If there is a url copied to the clipboard and accessing it will not show a
+  // clipboard access notification (e.g. iOS 14), use it to create a match.
+  // |read_clipboard_content| will be filled with false if the clipboard didn't
+  // have any content (either because there was none or because accessing it
+  // would have shown a clipboard access notification, and true if there was
+  // content.
   base::Optional<AutocompleteMatch> CreateURLMatch(
-      const AutocompleteInput& input);
-  // If there is text copied to the clipboard, use it to create a match.
+      const AutocompleteInput& input,
+      bool* read_clipboard_content);
+  // If there is text copied to the clipboard and accessing it will not show a
+  // clipboard access notification (e.g. iOS 14), use it to create a match.
+  // |read_clipboard_content| will be filled with false if the clipboard didn't
+  // have any content (either because there was none or because accessing it
+  // would have shown a clipboard access notification, and true if there was
+  // content.
   base::Optional<AutocompleteMatch> CreateTextMatch(
-      const AutocompleteInput& input);
-  // If there is an image copied to the clipboard, use it to create a match.
+      const AutocompleteInput& input,
+      bool* read_clipboard_content);
+  // If there is an image copied to the clipboard and accessing it will not show
+  // a clipboard access notification (e.g. iOS 14), use it to create a match.
   // The image match is asynchronous (because constructing the image post data
   // takes time), so instead of returning an optional match like the other
   // Create functions, it returns a boolean indicating whether there will be a
   // match.
   bool CreateImageMatch(const AutocompleteInput& input);
 
-  // Called when received image data from clipboard.
-  void OnReceiveImage(const AutocompleteInput& input,
-                      TemplateURLService* url_service,
-                      base::TimeDelta clipboard_contents_age,
+  // Handles the callback response from |CreateImageMatch| and turns the image
+  // into an AutocompleteMatch.
+  void CreateImageMatchCallback(const AutocompleteInput& input,
+                                const base::TimeDelta clipboard_contents_age,
+                                base::Optional<gfx::Image>);
+  // Handles the callback response from |CreateImageMatchCallback| and adds the
+  // created AutocompleteMatch to the matches list.
+  void AddImageMatchCallback(const AutocompleteInput& input,
+                             const base::TimeDelta clipboard_contents_age,
+                             base::Optional<AutocompleteMatch> match);
+
+  // Called when image data is received from clipboard.
+  void OnReceiveImage(ClipboardImageMatchCallback callback,
                       base::Optional<gfx::Image> optional_image);
 
   // Resize and encode the image data into bytes. This can take some time if the
@@ -69,9 +136,7 @@
   // Construct the actual image match once the image has been encoded into
   // bytes. This should be called back on the main thread.
   void ConstructImageMatchCallback(
-      const AutocompleteInput& input,
-      TemplateURLService* url_service,
-      base::TimeDelta clipboard_contents_age,
+      ClipboardImageMatchCallback callback,
       scoped_refptr<base::RefCountedMemory> image_bytes);
 
   AutocompleteProviderClient* client_;
diff --git a/components/omnibox/browser/clipboard_provider_unittest.cc b/components/omnibox/browser/clipboard_provider_unittest.cc
index dde3293..82ed6091 100644
--- a/components/omnibox/browser/clipboard_provider_unittest.cc
+++ b/components/omnibox/browser/clipboard_provider_unittest.cc
@@ -82,6 +82,10 @@
     return input;
   }
 
+  void MatchesImageCallback(base::Optional<AutocompleteMatch> match) {
+    matches_image_match_ = match;
+  }
+
  protected:
   // AutocompleteProviderListener:
   void OnProviderUpdate(bool updated_matches) override;
@@ -90,6 +94,8 @@
   FakeClipboardRecentContent clipboard_content_;
   std::unique_ptr<MockAutocompleteProviderClient> client_;
   scoped_refptr<ClipboardProvider> provider_;
+
+  base::Optional<AutocompleteMatch> matches_image_match_;
 };
 
 void ClipboardProviderTest::OnProviderUpdate(bool updated_matches) {
@@ -102,12 +108,18 @@
 }
 
 TEST_F(ClipboardProviderTest, EmptyClipboard) {
+  auto template_url_service = std::make_unique<TemplateURLService>(
+      /*initializers=*/nullptr, /*count=*/0);
+  client_->set_template_url_service(std::move(template_url_service));
   ClearClipboard();
   provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
   EXPECT_TRUE(provider_->matches().empty());
 }
 
 TEST_F(ClipboardProviderTest, ClipboardIsCurrentURL) {
+  auto template_url_service = std::make_unique<TemplateURLService>(
+      /*initializers=*/nullptr, /*count=*/0);
+  client_->set_template_url_service(std::move(template_url_service));
   SetClipboardUrl(GURL(kCurrentURL));
   provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
   EXPECT_TRUE(provider_->matches().empty());
@@ -152,20 +164,21 @@
   base::Feature imageFeature =
       omnibox::kEnableClipboardProviderImageSuggestions;
   feature_list.InitAndEnableFeature(imageFeature);
-  TemplateURLService template_url_service(/*initializers=*/nullptr,
-                                          /*count=*/0);
-  base::TimeDelta clipboard_age = base::TimeDelta::FromSeconds(5);
+  auto template_url_service =
+      std::make_unique<TemplateURLService>(/*initializers=*/nullptr,
+                                           /*count=*/0);
+  client_->set_template_url_service(std::move(template_url_service));
 
   gfx::Image test_image = gfx::test::CreateImage(/*height=*/10, /*width=*/10);
   scoped_refptr<base::RefCountedMemory> image_bytes =
       provider_->EncodeClipboardImage(*test_image.ToImageSkia());
   ASSERT_TRUE(image_bytes);
   provider_->ConstructImageMatchCallback(
-      CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS),
-      &template_url_service, clipboard_age, image_bytes);
-  ASSERT_GE(provider_->matches().size(), 1U);
-  EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_IMAGE,
-            provider_->matches().back().type);
+      base::BindOnce(&ClipboardProviderTest::MatchesImageCallback,
+                     base::Unretained(this)),
+      image_bytes);
+  ASSERT_TRUE(matches_image_match_);
+  EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_IMAGE, matches_image_match_->type);
 }
 
 TEST_F(ClipboardProviderTest, DeleteMatch) {
diff --git a/components/omnibox/browser/location_bar_model_impl.cc b/components/omnibox/browser/location_bar_model_impl.cc
index 4fe8d91..5270e37 100644
--- a/components/omnibox/browser/location_bar_model_impl.cc
+++ b/components/omnibox/browser/location_bar_model_impl.cc
@@ -15,7 +15,6 @@
 #include "components/dom_distiller/core/url_utils.h"
 #include "components/omnibox/browser/buildflags.h"
 #include "components/omnibox/browser/location_bar_model_delegate.h"
-#include "components/omnibox/browser/location_bar_model_util.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/security_state/core/security_state.h"
@@ -30,6 +29,7 @@
 
 #if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
 #include "components/omnibox/browser/vector_icons.h"  // nogncheck
+#include "components/vector_icons/vector_icons.h"     // nogncheck
 #endif
 
 using metrics::OmniboxEventProto;
@@ -205,9 +205,35 @@
 
   if (IsOfflinePage())
     return omnibox::kOfflinePinIcon;
-#endif
 
-  return location_bar_model::GetSecurityVectorIcon(GetSecurityLevel());
+  security_state::SecurityLevel security_level = GetSecurityLevel();
+  switch (security_level) {
+    case security_state::NONE:
+      return omnibox::kHttpIcon;
+    case security_state::WARNING:
+      // When kMarkHttpAsParameterDangerWarning is enabled, show a danger
+      // triangle icon.
+      if (security_state::ShouldShowDangerTriangleForWarningLevel()) {
+        return omnibox::kNotSecureWarningIcon;
+      }
+      return omnibox::kHttpIcon;
+    case security_state::SECURE:
+      return omnibox::kHttpsValidIcon;
+    case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
+      return vector_icons::kBusinessIcon;
+    case security_state::DANGEROUS:
+      return omnibox::kNotSecureWarningIcon;
+    case security_state::SECURITY_LEVEL_COUNT:
+      NOTREACHED();
+      return omnibox::kHttpIcon;
+  }
+  NOTREACHED();
+  return omnibox::kHttpIcon;
+#else
+  NOTREACHED();
+  static const gfx::VectorIcon dummy = {};
+  return dummy;
+#endif
 }
 
 base::string16 LocationBarModelImpl::GetSecureDisplayText() const {
diff --git a/components/omnibox/browser/location_bar_model_util.cc b/components/omnibox/browser/location_bar_model_util.cc
deleted file mode 100644
index 27c4ec8..0000000
--- a/components/omnibox/browser/location_bar_model_util.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/omnibox/browser/location_bar_model_util.h"
-
-#include "build/build_config.h"
-#include "components/omnibox/browser/buildflags.h"
-#include "ui/gfx/vector_icon_types.h"
-
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
-#include "components/omnibox/browser/vector_icons.h"  // nogncheck
-#include "components/vector_icons/vector_icons.h"     // nogncheck
-#endif
-
-namespace location_bar_model {
-
-const gfx::VectorIcon& GetSecurityVectorIcon(
-    security_state::SecurityLevel security_level) {
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
-  switch (security_level) {
-    case security_state::NONE:
-      return omnibox::kHttpIcon;
-    case security_state::WARNING:
-      // When kMarkHttpAsParameterDangerWarning is enabled, show a danger
-      // triangle icon.
-      if (security_state::ShouldShowDangerTriangleForWarningLevel()) {
-        return omnibox::kNotSecureWarningIcon;
-      }
-      return omnibox::kHttpIcon;
-    case security_state::SECURE:
-      return omnibox::kHttpsValidIcon;
-    case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
-      return vector_icons::kBusinessIcon;
-    case security_state::DANGEROUS:
-      return omnibox::kNotSecureWarningIcon;
-    case security_state::SECURITY_LEVEL_COUNT:
-      NOTREACHED();
-      return omnibox::kHttpIcon;
-  }
-  NOTREACHED();
-  return omnibox::kHttpIcon;
-#else
-  NOTREACHED();
-  static const gfx::VectorIcon dummy = {};
-  return dummy;
-#endif
-}
-
-}  // namespace location_bar_model
diff --git a/components/omnibox/browser/location_bar_model_util.h b/components/omnibox/browser/location_bar_model_util.h
deleted file mode 100644
index 00803fc..0000000
--- a/components/omnibox/browser/location_bar_model_util.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OMNIBOX_BROWSER_LOCATION_BAR_MODEL_UTIL_H_
-#define COMPONENTS_OMNIBOX_BROWSER_LOCATION_BAR_MODEL_UTIL_H_
-
-#include "components/security_state/core/security_state.h"
-
-namespace gfx {
-struct VectorIcon;
-}  // namespace gfx
-
-namespace location_bar_model {
-
-// Get the vector icon according to security level.
-// It indicates security state of the page.
-const gfx::VectorIcon& GetSecurityVectorIcon(
-    security_state::SecurityLevel security_level);
-
-}  // namespace location_bar_model
-
-#endif  // COMPONENTS_OMNIBOX_BROWSER_LOCATION_BAR_MODEL_UTIL_H_
diff --git a/components/open_from_clipboard/clipboard_recent_content.h b/components/open_from_clipboard/clipboard_recent_content.h
index 6b383bda..075a8ec 100644
--- a/components/open_from_clipboard/clipboard_recent_content.h
+++ b/components/open_from_clipboard/clipboard_recent_content.h
@@ -33,21 +33,17 @@
   static void SetInstance(std::unique_ptr<ClipboardRecentContent> new_instance);
 
   // Returns clipboard content as URL, if it has a compatible type,
-  // is recent enough and has not been suppressed.
+  // is recent enough, has not been suppressed and will not trigger a system
+  // notification that the clipboard has been accessed.
   virtual base::Optional<GURL> GetRecentURLFromClipboard() = 0;
 
   // Returns clipboard content as text, if it has a compatible type,
-  // is recent enough and has not been suppressed.
+  // is recent enough, has not been suppressed and will not trigger a system
+  // notification that the clipboard has been accessed.
   virtual base::Optional<base::string16> GetRecentTextFromClipboard() = 0;
 
-  using GetRecentImageCallback =
-      base::OnceCallback<void(base::Optional<gfx::Image>)>;
-
-  // Returns clipboard content as image to |GetRecentImageCallback|, if it has a
-  // compatible type, is recent enough and has not been suppressed.
-  virtual void GetRecentImageFromClipboard(GetRecentImageCallback callback) = 0;
-
-  // Return if system's clipboard contains an image.
+  // Return if system's clipboard contains an image that will not trigger a
+  // system notification that the clipboard has been accessed.
   virtual bool HasRecentImageFromClipboard() = 0;
 
   /*
@@ -59,19 +55,27 @@
   using GetRecentURLCallback = base::OnceCallback<void(base::Optional<GURL>)>;
   using GetRecentTextCallback =
       base::OnceCallback<void(base::Optional<base::string16>)>;
+  using GetRecentImageCallback =
+      base::OnceCallback<void(base::Optional<gfx::Image>)>;
 
   // Returns whether the clipboard contains a URL to |HasDataCallback| if it
   // is recent enough and has not been suppressed.
   virtual void HasRecentContentFromClipboard(
       std::set<ClipboardContentType> types,
       HasDataCallback callback) = 0;
+
   // Returns clipboard content as URL to |GetRecentURLCallback|, if it has a
   // compatible type, is recent enough and has not been suppressed.
   virtual void GetRecentURLFromClipboard(GetRecentURLCallback callback) = 0;
+
   // Returns clipboard content as a string to |GetRecentTextCallback|, if it has
   // a compatible type, is recent enough and has not been suppressed.
   virtual void GetRecentTextFromClipboard(GetRecentTextCallback callback) = 0;
 
+  // Returns clipboard content as image to |GetRecentImageCallback|, if it has a
+  // compatible type, is recent enough and has not been suppressed.
+  virtual void GetRecentImageFromClipboard(GetRecentImageCallback callback) = 0;
+
   // Returns how old the content of the clipboard is.
   virtual base::TimeDelta GetClipboardContentAge() const = 0;
 
diff --git a/components/open_from_clipboard/clipboard_recent_content_impl_ios.h b/components/open_from_clipboard/clipboard_recent_content_impl_ios.h
index 38f708b..a865a8b 100644
--- a/components/open_from_clipboard/clipboard_recent_content_impl_ios.h
+++ b/components/open_from_clipboard/clipboard_recent_content_impl_ios.h
@@ -39,15 +39,18 @@
 - (instancetype)init NS_UNAVAILABLE;
 
 // Returns the copied URL if the clipboard contains a recent URL that has not
-// been supressed. Otherwise, returns nil.
+// been suppressed and will not trigger a pasteboard access notification.
+// Otherwise, returns nil.
 - (NSURL*)recentURLFromClipboard;
 
 // Returns the copied string if the clipboard contains a recent string that has
-// not been suppresed. Otherwise, returns nil.
+// not been suppresed and will not trigger a pasteboard access notification.
+// Otherwise, returns nil.
 - (NSString*)recentTextFromClipboard;
 
 // Returns the copied image if the clipboard contains a recent image that has
-// not been suppressed. Otherwise, returns nil.
+// not been suppressed and will not trigger a pasteboard access notification.
+// Otherwise, returns nil.
 - (UIImage*)recentImageFromClipboard;
 
 // Uses the new iOS 14 pasteboard detection pattern API to asynchronously detect
diff --git a/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm b/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
index 20b1ce34..1ff61e1b 100644
--- a/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
+++ b/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
@@ -131,8 +131,13 @@
   if (![self shouldReturnValueOfClipboard])
     return nil;
 
-  if (!self.cachedURL) {
-    self.cachedURL = [self URLFromPasteboard];
+  if (@available(iOS 14, *)) {
+    // On iOS 14, don't actually access the pasteboard in this method. This
+    // prevents the pasteboard access notification from appearing.
+  } else {
+    if (!self.cachedURL) {
+      self.cachedURL = [self URLFromPasteboard];
+    }
   }
   return self.cachedURL;
 }
@@ -143,8 +148,13 @@
   if (![self shouldReturnValueOfClipboard])
     return nil;
 
-  if (!self.cachedText) {
-    self.cachedText = UIPasteboard.generalPasteboard.string;
+  if (@available(iOS 14, *)) {
+    // On iOS 14, don't actually access the pasteboard in this method. This
+    // prevents the pasteboard access notification from appearing.
+  } else {
+    if (!self.cachedText) {
+      self.cachedText = UIPasteboard.generalPasteboard.string;
+    }
   }
   return self.cachedText;
 }
@@ -155,8 +165,13 @@
   if (![self shouldReturnValueOfClipboard])
     return nil;
 
-  if (!self.cachedImage) {
-    self.cachedImage = UIPasteboard.generalPasteboard.image;
+  if (@available(iOS 14, *)) {
+    // On iOS 14, don't actually access the pasteboard in this method. This
+    // prevents the pasteboard access notification from appearing.
+  } else {
+    if (!self.cachedImage) {
+      self.cachedImage = UIPasteboard.generalPasteboard.image;
+    }
   }
 
   return self.cachedImage;
diff --git a/components/open_from_clipboard/clipboard_recent_content_ios.mm b/components/open_from_clipboard/clipboard_recent_content_ios.mm
index 3bec1c0..5bba010 100644
--- a/components/open_from_clipboard/clipboard_recent_content_ios.mm
+++ b/components/open_from_clipboard/clipboard_recent_content_ios.mm
@@ -14,6 +14,7 @@
 #include "base/stl_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/system/sys_info.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #import "components/open_from_clipboard/clipboard_recent_content_impl_ios.h"
 #import "net/base/mac/url_conversions.h"
 #include "url/gurl.h"
@@ -116,19 +117,6 @@
   return base::SysNSStringToUTF16(text_from_pasteboard);
 }
 
-void ClipboardRecentContentIOS::GetRecentImageFromClipboard(
-    GetRecentImageCallback callback) {
-  __block GetRecentImageCallback callback_for_block = std::move(callback);
-  [implementation_ recentImageFromClipboardAsync:^(UIImage* image) {
-    if (!image) {
-      std::move(callback_for_block).Run(base::nullopt);
-      return;
-    }
-
-    std::move(callback_for_block).Run(gfx::Image(image));
-  }];
-}
-
 bool ClipboardRecentContentIOS::HasRecentImageFromClipboard() {
   return GetRecentImageFromClipboardInternal().has_value();
 }
@@ -141,39 +129,93 @@
   for (ClipboardContentType type : types) {
     [ios_types addObject:ContentTypeFromClipboardContentType(type)];
   }
-  [implementation_ hasContentMatchingTypes:ios_types
-                         completionHandler:^(NSSet<ContentType>* results) {
-                           std::set<ClipboardContentType> matching_types;
-                           for (ContentType type in results) {
-                             matching_types.insert(
-                                 ClipboardContentTypeFromContentType(type));
-                           }
-                           std::move(callback_for_block).Run(matching_types);
-                         }];
+  // The iOS methods for checking clipboard content call their callbacks on an
+  // arbitrary thread. As Objective-C doesn't have very good thread-management
+  // techniques, make sure this method calls its callback on the same thread
+  // that it was called on.
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
+      base::SequencedTaskRunnerHandle::Get();
+  [implementation_
+      hasContentMatchingTypes:ios_types
+            completionHandler:^(NSSet<ContentType>* results) {
+              std::set<ClipboardContentType> matching_types;
+              for (ContentType type in results) {
+                matching_types.insert(
+                    ClipboardContentTypeFromContentType(type));
+              }
+              task_runner->PostTask(
+                  FROM_HERE, base::BindOnce(^{
+                    std::move(callback_for_block).Run(matching_types);
+                  }));
+            }];
 }
 
 void ClipboardRecentContentIOS::GetRecentURLFromClipboard(
     GetRecentURLCallback callback) {
   __block GetRecentURLCallback callback_for_block = std::move(callback);
+  // The iOS methods for checking clipboard content call their callbacks on an
+  // arbitrary thread. As Objective-C doesn't have very good thread-management
+  // techniques, make sure this method calls its callback on the same thread
+  // that it was called on.
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
+      base::SequencedTaskRunnerHandle::Get();
   [implementation_ recentURLFromClipboardAsync:^(NSURL* url) {
     GURL converted_url = net::GURLWithNSURL(url);
     if (!converted_url.is_valid()) {
-      std::move(callback_for_block).Run(base::nullopt);
+      task_runner->PostTask(FROM_HERE, base::BindOnce(^{
+                              std::move(callback_for_block).Run(base::nullopt);
+                            }));
       return;
     }
-    std::move(callback_for_block).Run(converted_url);
+    task_runner->PostTask(FROM_HERE, base::BindOnce(^{
+                            std::move(callback_for_block).Run(converted_url);
+                          }));
   }];
 }
 
 void ClipboardRecentContentIOS::GetRecentTextFromClipboard(
     GetRecentTextCallback callback) {
   __block GetRecentTextCallback callback_for_block = std::move(callback);
+  // The iOS methods for checking clipboard content call their callbacks on an
+  // arbitrary thread. As Objective-C doesn't have very good thread-management
+  // techniques, make sure this method calls its callback on the same thread
+  // that it was called on.
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
+      base::SequencedTaskRunnerHandle::Get();
   [implementation_ recentTextFromClipboardAsync:^(NSString* text) {
     if (!text) {
-      std::move(callback_for_block).Run(base::nullopt);
+      task_runner->PostTask(FROM_HERE, base::BindOnce(^{
+                              std::move(callback_for_block).Run(base::nullopt);
+                            }));
       return;
     }
-    std::move(callback_for_block).Run(base::SysNSStringToUTF16(text));
+    task_runner->PostTask(
+        FROM_HERE, base::BindOnce(^{
+          std::move(callback_for_block).Run(base::SysNSStringToUTF16(text));
+        }));
+  }];
+}
+
+void ClipboardRecentContentIOS::GetRecentImageFromClipboard(
+    GetRecentImageCallback callback) {
+  __block GetRecentImageCallback callback_for_block = std::move(callback);
+  // The iOS methods for checking clipboard content call their callbacks on an
+  // arbitrary thread. As Objective-C doesn't have very good thread-management
+  // techniques, make sure this method calls its callback on the same thread
+  // that it was called on.
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
+      base::SequencedTaskRunnerHandle::Get();
+  [implementation_ recentImageFromClipboardAsync:^(UIImage* image) {
+    if (!image) {
+      task_runner->PostTask(FROM_HERE, base::BindOnce(^{
+                              std::move(callback_for_block).Run(base::nullopt);
+                            }));
+      return;
+    }
+    task_runner->PostTask(
+        FROM_HERE, base::BindOnce(^{
+          std::move(callback_for_block).Run(gfx::Image(image));
+        }));
   }];
 }
 
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc
index 0acfce13..2aeadaf 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -915,6 +915,7 @@
   result->scheme = PasswordForm::Scheme::kHtml;
   result->blocked_by_user = false;
   result->type = PasswordForm::Type::kManual;
+  result->server_side_classification_successful = form_predictions.has_value();
   result->username_may_use_prefilled_placeholder =
       GetMayUsePrefilledPlaceholder(form_predictions, significant_fields);
   result->is_new_password_reliable =
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
index 65dca71..76c7d7e 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -90,6 +90,7 @@
   // null means no checking
   const autofill::ValueElementVector* all_possible_passwords = nullptr;
   const autofill::ValueElementVector* all_possible_usernames = nullptr;
+  bool server_side_classification_successful = true;
   bool username_may_use_prefilled_placeholder = false;
   base::Optional<FormDataParser::ReadonlyPasswordFields> readonly_status;
   base::Optional<FormDataParser::ReadonlyPasswordFields>
@@ -358,6 +359,8 @@
         EXPECT_EQ(PasswordForm::Scheme::kHtml, parsed_form->scheme);
         EXPECT_FALSE(parsed_form->blocked_by_user);
         EXPECT_EQ(PasswordForm::Type::kManual, parsed_form->type);
+        EXPECT_EQ(test_case.server_side_classification_successful,
+                  parsed_form->server_side_classification_successful);
         EXPECT_EQ(test_case.username_may_use_prefilled_placeholder,
                   parsed_form->username_may_use_prefilled_placeholder);
         EXPECT_EQ(test_case.submission_event, parsed_form->submission_event);
@@ -1243,6 +1246,7 @@
                    .prediction = {.type = autofill::PASSWORD,
                                   .may_use_prefilled_placeholder = true}},
               },
+          .server_side_classification_successful = true,
           .username_may_use_prefilled_placeholder = false,
       },
   });
@@ -1290,6 +1294,7 @@
                    .prediction = {.type = autofill::PASSWORD,
                                   .may_use_prefilled_placeholder = true}},
               },
+          .server_side_classification_successful = true,
           .username_may_use_prefilled_placeholder = true,
       },
       {
@@ -1327,6 +1332,22 @@
                    .form_control_type = "password"},
               },
       },
+      {
+          .description_for_logging = "Username not a placeholder",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS,
+                                  .may_use_prefilled_placeholder = false}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD,
+                                  .may_use_prefilled_placeholder = false}},
+              },
+          .server_side_classification_successful = true,
+          .username_may_use_prefilled_placeholder = false,
+      },
   });
 }
 
diff --git a/components/password_manager/core/browser/password_form_filling.cc b/components/password_manager/core/browser/password_form_filling.cc
index 5910626f..bcc720dc 100644
--- a/components/password_manager/core/browser/password_form_filling.cc
+++ b/components/password_manager/core/browser/password_form_filling.cc
@@ -179,6 +179,13 @@
   } else if (no_sign_in_form) {
     // If the parser did not find a current password element, don't fill.
     wait_for_username_reason = WaitForUsernameReason::kFormNotGoodForFilling;
+  } else if (observed_form.HasUsernameElement() &&
+             observed_form.HasNonEmptyPasswordValue() &&
+             observed_form.server_side_classification_successful &&
+             !observed_form.username_may_use_prefilled_placeholder) {
+    // Password is already filled in and we don't think the username is a
+    // placeholder, so don't overwrite.
+    wait_for_username_reason = WaitForUsernameReason::kPasswordPrefilled;
   } else if (!client->IsCommittedMainFrameSecure()) {
     wait_for_username_reason = WaitForUsernameReason::kInsecureOrigin;
   } else if (autofill::IsTouchToFillEnabled()) {
diff --git a/components/password_manager/core/browser/password_form_filling_unittest.cc b/components/password_manager/core/browser/password_form_filling_unittest.cc
index e7edb59..486384d 100644
--- a/components/password_manager/core/browser/password_form_filling_unittest.cc
+++ b/components/password_manager/core/browser/password_form_filling_unittest.cc
@@ -261,6 +261,67 @@
   }
 }
 
+// Test autofill when username and password are prefilled. Overwrite password
+// if server side classification thought the username was a placeholder or the
+// classification failed. Do not overwrite if username doesn't look like a
+// placeholder.
+// Skip for Android since it uses touch to fill (kAutofillTouchToFill), meaning
+// placeholders will never be overwritten.
+#if !defined(OS_ANDROID)
+TEST_F(PasswordFormFillingTest, TestFillOnLoadSuggestionWithPrefill) {
+  const struct {
+    const char* description;
+    bool username_may_use_prefilled_placeholder;
+    bool server_side_classification_successful;
+    LikelyFormFilling likely_form_filling;
+  } kTestCases[] = {
+      {
+          .description = "Username not placeholder",
+          .username_may_use_prefilled_placeholder = false,
+          .server_side_classification_successful = true,
+          .likely_form_filling = LikelyFormFilling::kFillOnAccountSelect,
+      },
+      {
+          .description = "Username is placeholder",
+          .username_may_use_prefilled_placeholder = true,
+          .server_side_classification_successful = true,
+          .likely_form_filling = LikelyFormFilling::kFillOnPageLoad,
+      },
+      {
+          .description = "No server classification",
+          .username_may_use_prefilled_placeholder = false,
+          .server_side_classification_successful = false,
+          .likely_form_filling = LikelyFormFilling::kFillOnPageLoad,
+      },
+  };
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.description);
+    PasswordForm preferred_match = saved_match_;
+    std::vector<const PasswordForm*> best_matches = {&preferred_match};
+
+    PasswordForm observed_form = observed_form_;
+    // Set username to match preferred match
+    observed_form.username_value = preferred_match.username_value;
+    // Set a different password than saved
+    observed_form.password_value = ASCIIToUTF16("New Passwd");
+    // Set classification results
+    observed_form.server_side_classification_successful =
+        test_case.server_side_classification_successful;
+    observed_form.username_may_use_prefilled_placeholder =
+        test_case.username_may_use_prefilled_placeholder;
+
+    EXPECT_CALL(driver_, FillPasswordForm);
+    EXPECT_CALL(client_, PasswordWasAutofilled);
+
+    LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
+        &client_, &driver_, observed_form, best_matches, federated_matches_,
+        &preferred_match, metrics_recorder_.get());
+
+    EXPECT_EQ(test_case.likely_form_filling, likely_form_filling);
+  }
+}
+#endif
+
 TEST_F(PasswordFormFillingTest, AutofillPSLMatch) {
   std::vector<const PasswordForm*> best_matches = {&psl_saved_match_};
 
@@ -535,6 +596,7 @@
   form_on_page.username_element = ASCIIToUTF16("username");
   form_on_page.password_element = ASCIIToUTF16("password");
   form_on_page.username_may_use_prefilled_placeholder = true;
+  form_on_page.server_side_classification_successful = true;
 
   // Create an exact match in the database.
   PasswordForm preferred_match = form_on_page;
@@ -638,4 +700,5 @@
           IsLogin(kDuplicateLocalUsername, kDuplicateLocalPassword, false),
           IsLogin(kSyncedUsername, kSyncedPassword, true)));
 }
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.h b/components/password_manager/core/browser/password_form_metrics_recorder.h
index 341e540..f51f5b1 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.h
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.h
@@ -226,7 +226,9 @@
     kFoasFeature = 6,
     // Re-authenticaion for filling passwords is required.
     kReauthRequired = 7,
-    kMaxValue = kReauthRequired,
+    // Password is already filled
+    kPasswordPrefilled = 8,
+    kMaxValue = kPasswordPrefilled,
   };
 
   // This metric records the user experience with the passwords filling. The
diff --git a/components/password_manager/core/browser/password_scripts_fetcher_impl.cc b/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
index fac8f5f8..89c8357 100644
--- a/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
+++ b/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
@@ -14,10 +14,60 @@
 #include "url/origin.h"
 
 namespace {
+
 constexpr int kCacheTimeoutInMinutes = 5;
 constexpr int kFetchTimeoutInSeconds = 3;
 
 constexpr int kMaxDownloadSizeInBytes = 10 * 1024;
+
+using ParsingResult =
+    password_manager::PasswordScriptsFetcherImpl::ParsingResult;
+
+// Extracts the domains for which password changes are supported and adds them
+// to |supported_domains|.
+// |script_config| is the dictionary passed for a domain, representig the
+// configuration data of one password change script.
+// For example, the fetched JSON might look like this:
+// {
+//   'example.com': {
+//     'domains': [ 'https://www.example.com', 'https://m.example.com' ]
+//   }
+// }
+// In this case |script_config| would represent the dictionary value
+// of 'example.com'.
+// Returns a set of warnings.
+// The function tries to be lax about errors and prefers to skip them
+// with warnings rather than bail the parsing. This is for forward
+// compatibility.
+base::flat_set<ParsingResult> ExtractPasswordDomains(
+    const base::Value& script_config,
+    base::flat_set<url::Origin>& supported_domains) {
+  if (!script_config.is_dict())
+    return {ParsingResult::kInvalidJson};
+
+  const base::Value* supported_domains_list =
+      script_config.FindListKey("domains");
+  if (!supported_domains_list || !supported_domains_list->is_list())
+    return {ParsingResult::kInvalidJson};
+
+  base::flat_set<ParsingResult> warnings;
+  for (const base::Value& domain : supported_domains_list->GetList()) {
+    if (!domain.is_string()) {
+      warnings.insert(ParsingResult::kInvalidJson);
+      continue;
+    }
+
+    GURL url(domain.GetString());
+    if (!url.is_valid()) {
+      warnings.insert(ParsingResult::kInvalidUrl);
+      continue;
+    }
+    supported_domains.insert(url::Origin::Create(url));
+  }
+
+  return warnings;
+}
+
 }  // namespace
 
 namespace password_manager {
@@ -139,9 +189,14 @@
   url_loader_.reset();
   last_fetch_timestamp_ = base::TimeTicks::Now();
 
-  ParsingResult parsing_result = ParseResponse(std::move(response_body));
-  base::UmaHistogramEnumeration(
-      "PasswordManager.PasswordScriptsFetcher.ParsingResult", parsing_result);
+  base::flat_set<ParsingResult> parsing_warnings =
+      ParseResponse(std::move(response_body));
+  if (parsing_warnings.empty())
+    parsing_warnings.insert(ParsingResult::kOk);
+  for (ParsingResult warning : parsing_warnings) {
+    base::UmaHistogramEnumeration(
+        "PasswordManager.PasswordScriptsFetcher.ParsingResult", warning);
+  }
 
   for (auto& callback : std::exchange(fetch_finished_callbacks_, {}))
     std::move(callback).Run();
@@ -149,33 +204,32 @@
     RunResponseCallback(std::move(callback.first), std::move(callback.second));
 }
 
-PasswordScriptsFetcherImpl::ParsingResult
-PasswordScriptsFetcherImpl::ParseResponse(
+base::flat_set<ParsingResult> PasswordScriptsFetcherImpl::ParseResponse(
     std::unique_ptr<std::string> response_body) {
   password_change_domains_.clear();
 
   if (!response_body)
-    return ParsingResult::kNoResponse;
+    return {ParsingResult::kNoResponse};
 
-  base::Optional<base::Value> data = base::JSONReader::Read(*response_body);
-  if (data == base::nullopt)
-    return ParsingResult::kNotJsonString;
-  if (!data->is_dict())
-    return ParsingResult::kNotDictionary;
+  base::JSONReader::ValueWithError data =
+      base::JSONReader::ReadAndReturnValueWithError(*response_body);
 
-  bool invalid_urls_found = false;
-  for (const auto& it : data->DictItems()) {
-    // |it.second| is not used at the moment and reserved for
-    // domain-specific parameters.
-    GURL url(it.first);
-    if (url.is_valid()) {
-      url::Origin origin = url::Origin::Create(url);
-      password_change_domains_.insert(origin);
-    } else {
-      invalid_urls_found = true;
-    }
+  if (data.value == base::nullopt) {
+    DVLOG(1) << "Parse error: " << data.error_message;
+    return {ParsingResult::kInvalidJson};
   }
-  return invalid_urls_found ? ParsingResult::kInvalidUrl : ParsingResult::kOk;
+  if (!data.value->is_dict())
+    return {ParsingResult::kInvalidJson};
+
+  base::flat_set<ParsingResult> warnings;
+  for (const auto& script_it : data.value->DictItems()) {
+    // |script_it.first| is an identifier that we don't care about.
+    // |script_it.second| provides domain-sepcific parameters.
+    base::flat_set<ParsingResult> warnings_for_script =
+        ExtractPasswordDomains(script_it.second, password_change_domains_);
+    warnings.insert(warnings_for_script.begin(), warnings_for_script.end());
+  }
+  return warnings;
 }
 
 bool PasswordScriptsFetcherImpl::IsCacheStale() const {
diff --git a/components/password_manager/core/browser/password_scripts_fetcher_impl.h b/components/password_manager/core/browser/password_scripts_fetcher_impl.h
index 159846f..1a97f66 100644
--- a/components/password_manager/core/browser/password_scripts_fetcher_impl.h
+++ b/components/password_manager/core/browser/password_scripts_fetcher_impl.h
@@ -47,15 +47,15 @@
   enum class ParsingResult {
     // No response from the server.
     kNoResponse = 0,
-    // Response is not a properly formed JSON string.
-    kNotJsonString = 1,
-    // Parsing result is not a dictionary.
-    kNotDictionary = 2,
     // There was at least one invalid URL.
     kInvalidUrl = 3,
     // No errors occurred.
     kOk = 4,
-    kMaxValue = kOk,
+    // Invalid JSON (either syntactically, e.g. ill-formed lists, dictionaries,
+    // strings, etc., or structurally, e.g. a dictionary that does not contain
+    // the expected keys).
+    kInvalidJson = 5,
+    kMaxValue = kInvalidJson,
   };
 
   explicit PasswordScriptsFetcherImpl(
@@ -84,8 +84,11 @@
                        std::unique_ptr<std::string> response_body);
   // Parses |response_body| and stores the result in |password_change_domains_|
   // (always overwrites the old list). Sets an empty list if |response_body| is
-  // invalid. Returns a parsing result for a histogram.
-  ParsingResult ParseResponse(std::unique_ptr<std::string> response_body);
+  // invalid. Returns a parsing result for a histogram. The function tries to be
+  // forgiving and rather return warnings and skip an entry than cancel the
+  // parsing.
+  base::flat_set<ParsingResult> ParseResponse(
+      std::unique_ptr<std::string> response_body);
   // Returns whether a re-fetch is needed.
   bool IsCacheStale() const;
   // Runs |callback| immediately with the script availability for |origin|.
diff --git a/components/password_manager/core/browser/password_scripts_fetcher_impl_unittests.cc b/components/password_manager/core/browser/password_scripts_fetcher_impl_unittests.cc
index 9530c96..9341c93 100644
--- a/components/password_manager/core/browser/password_scripts_fetcher_impl_unittests.cc
+++ b/components/password_manager/core/browser/password_scripts_fetcher_impl_unittests.cc
@@ -18,11 +18,24 @@
 namespace {
 
 constexpr char kOriginWithScript1[] = "https://example.com";
-constexpr char kOriginWithScript2[] = "https://test.com";
+constexpr char kOriginWithScript2[] = "https://mobile.example.com";
+constexpr char kOriginWithScript3[] = "https://test.com";
 constexpr char kOriginWithoutScript[] = "https://no-script.com";
 
 constexpr char kTestResponseContent[] =
-    "{\"https://example.com\" : {}, \"https://test.com\" : {}}";
+    R"json(
+      {
+        "example.com": {
+          "domains": [
+            "https://example.com",
+            "https://mobile.example.com"
+          ]
+        },
+        "test.com": {
+          "domains": ["https://test.com"]
+        }
+      }
+    )json";
 
 url::Origin GetOriginWithScript1() {
   return url::Origin::Create(GURL(kOriginWithScript1));
@@ -32,6 +45,10 @@
   return url::Origin::Create(GURL(kOriginWithScript2));
 }
 
+url::Origin GetOriginWithScript3() {
+  return url::Origin::Create(GURL(kOriginWithScript3));
+}
+
 url::Origin GetOriginWithoutScript() {
   return url::Origin::Create(GURL(kOriginWithoutScript));
 }
@@ -76,6 +93,7 @@
                        base::Unretained(this)));
     RequestSingleScriptAvailability(GetOriginWithScript1());
     RequestSingleScriptAvailability(GetOriginWithScript2());
+    RequestSingleScriptAvailability(GetOriginWithScript3());
     RequestSingleScriptAvailability(GetOriginWithoutScript());
   }
 
@@ -136,6 +154,7 @@
   EXPECT_THAT(recorded_responses(),
               UnorderedElementsAre(Pair(GetOriginWithScript1(), true),
                                    Pair(GetOriginWithScript2(), true),
+                                   Pair(GetOriginWithScript3(), true),
                                    Pair(GetOriginWithoutScript(), false)));
   EXPECT_EQ(0, GetNumberOfPendingRequests());
   histogram_tester->ExpectUniqueSample(
@@ -150,12 +169,14 @@
   StartBulkCheck();
   EXPECT_EQ(1, GetNumberOfPendingRequests());
   // OriginWithScript2 (test.com) is not available anymore.
-  SimulateResponseWithContent("{\"https://example.com\" : {}}");
+  SimulateResponseWithContent(
+      R"({"example.com": {"domains": ["https://example.com"]}})");
   base::RunLoop().RunUntilIdle();
 
   EXPECT_THAT(recorded_responses(),
               UnorderedElementsAre(Pair(GetOriginWithScript1(), true),
                                    Pair(GetOriginWithScript2(), false),
+                                   Pair(GetOriginWithScript3(), false),
                                    Pair(GetOriginWithoutScript(), false)));
   EXPECT_EQ(0, GetNumberOfPendingRequests());
   histogram_tester->ExpectUniqueSample(
@@ -186,6 +207,7 @@
   EXPECT_THAT(recorded_responses(),
               UnorderedElementsAre(Pair(GetOriginWithScript1(), true),
                                    Pair(GetOriginWithScript2(), true),
+                                   Pair(GetOriginWithScript3(), true),
                                    Pair(GetOriginWithoutScript(), false)));
   EXPECT_EQ(0, GetNumberOfPendingRequests());
 
@@ -202,16 +224,18 @@
       net::HttpStatusCode::HTTP_OK, 1u);
 }
 
-TEST_F(PasswordScriptsFetcherImplTest, InvalidJson) {
+TEST_F(PasswordScriptsFetcherImplTest, InvalidResponseBody) {
   const struct TestCase {
     const char* const response;
     PasswordScriptsFetcherImpl::ParsingResult histogram_value;
   } kTestCases[]{
-      {"", PasswordScriptsFetcherImpl::ParsingResult::kNotJsonString},
-      {"{{{", PasswordScriptsFetcherImpl::ParsingResult::kNotJsonString},
-      {"[\"1\", \"2\"]",
-       PasswordScriptsFetcherImpl::ParsingResult::kNotDictionary},
-      {"{ \"not-url.com\" : {}}",
+      {"", PasswordScriptsFetcherImpl::ParsingResult::kInvalidJson},
+      {"{{{", PasswordScriptsFetcherImpl::ParsingResult::kInvalidJson},
+      {R"(["1", "2"])",
+       PasswordScriptsFetcherImpl::ParsingResult::kInvalidJson},
+      {R"({"no-domains-attribute.com" : {}})",
+       PasswordScriptsFetcherImpl::ParsingResult::kInvalidJson},
+      {R"({"not-url.com" : {"domains": ["scheme-forgotten.com"]}})",
        PasswordScriptsFetcherImpl::ParsingResult::kInvalidUrl}};
   for (const auto& test_case : kTestCases) {
     SCOPED_TRACE(testing::Message() << "test_case=" << test_case.response);
@@ -227,6 +251,7 @@
     EXPECT_THAT(recorded_responses(),
                 UnorderedElementsAre(Pair(GetOriginWithScript1(), false),
                                      Pair(GetOriginWithScript2(), false),
+                                     Pair(GetOriginWithScript3(), false),
                                      Pair(GetOriginWithoutScript(), false)));
     histogram_tester.ExpectUniqueSample(
         "PasswordManager.PasswordScriptsFetcher.ParsingResult",
@@ -243,6 +268,7 @@
   EXPECT_THAT(recorded_responses(),
               UnorderedElementsAre(Pair(GetOriginWithScript1(), false),
                                    Pair(GetOriginWithScript2(), false),
+                                   Pair(GetOriginWithScript3(), false),
                                    Pair(GetOriginWithoutScript(), false)));
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.PasswordScriptsFetcher.ParsingResult",
@@ -259,6 +285,7 @@
   // default value (false).
   EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithScript1()));
   EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithScript2()));
+  EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithScript3()));
   EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithoutScript()));
   EXPECT_EQ(0, GetNumberOfPendingRequests());
 
@@ -273,12 +300,14 @@
   // Cache is ready.
   EXPECT_TRUE(fetcher()->IsScriptAvailable(GetOriginWithScript1()));
   EXPECT_TRUE(fetcher()->IsScriptAvailable(GetOriginWithScript2()));
+  EXPECT_TRUE(fetcher()->IsScriptAvailable(GetOriginWithScript3()));
   EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithoutScript()));
 
   // |IsScriptAvailable| does not trigger refetching and returns stale values.
   fetcher()->make_cache_stale_for_testing();
   EXPECT_TRUE(fetcher()->IsScriptAvailable(GetOriginWithScript1()));
   EXPECT_TRUE(fetcher()->IsScriptAvailable(GetOriginWithScript2()));
+  EXPECT_TRUE(fetcher()->IsScriptAvailable(GetOriginWithScript3()));
   EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithoutScript()));
   EXPECT_EQ(0, GetNumberOfPendingRequests());
 }
diff --git a/components/payments/core/features.cc b/components/payments/core/features.cc
index c4c9717..bda13aa 100644
--- a/components/payments/core/features.cc
+++ b/components/payments/core/features.cc
@@ -77,9 +77,6 @@
     "AllowJITInstallationWhenAppIconIsMissing",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kPaymentHandlerSecurityIcon{
-    "PaymentHandlerSecurityIcon", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kEnforceFullDelegation{"EnforceFullDelegation",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/payments/core/features.h b/components/payments/core/features.h
index b7716bff..b92ea8e 100644
--- a/components/payments/core/features.h
+++ b/components/payments/core/features.h
@@ -79,11 +79,6 @@
 // Used to test icon refetch for JIT installed apps with missing icons.
 extern const base::Feature kAllowJITInstallationWhenAppIconIsMissing;
 
-// Desktop only, if enabled the security icon would be showed next to the
-// payment handler's URL bar. It indicate that only secure content is
-// allowed inside the payment handler.
-extern const base::Feature kPaymentHandlerSecurityIcon;
-
 // Used to reject the apps with partial delegation.
 extern const base::Feature kEnforceFullDelegation;
 
diff --git a/components/performance_manager/OWNERS b/components/performance_manager/OWNERS
index 6726b9e..085920a 100644
--- a/components/performance_manager/OWNERS
+++ b/components/performance_manager/OWNERS
@@ -1,7 +1,7 @@
 chrisha@chromium.org
 fdoray@chromium.org
 siggi@chromium.org
-joenotcharles@google.com
+joenotcharles@chromium.org
 
 # For IPC security review
 per-file *.mojom=set noparent
diff --git a/components/policy/core/common/features.cc b/components/policy/core/common/features.cc
index 2890847c..c38e3d2c 100644
--- a/components/policy/core/common/features.cc
+++ b/components/policy/core/common/features.cc
@@ -8,9 +8,6 @@
 
 namespace features {
 
-const base::Feature kPolicyAtomicGroup{"PolicyAtomicGroup",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kCBCMPolicyInvalidations{"CBCMPolicyInvalidations",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/policy/core/common/features.h b/components/policy/core/common/features.h
index 65c42f0c..f098584 100644
--- a/components/policy/core/common/features.h
+++ b/components/policy/core/common/features.h
@@ -12,9 +12,6 @@
 namespace policy {
 namespace features {
 
-// TODO(994227) Remove references to this now unused feature.
-POLICY_EXPORT extern const base::Feature kPolicyAtomicGroup;
-
 #if defined(OS_APPLE)
 // Feature that controls whether the browser ignores sensitive policies on an
 // unmanaged Mac.
diff --git a/components/policy/tools/template_writers/writers/adm_writer.py b/components/policy/tools/template_writers/writers/adm_writer.py
index 3e38528..4b480c4 100755
--- a/components/policy/tools/template_writers/writers/adm_writer.py
+++ b/components/policy/tools/template_writers/writers/adm_writer.py
@@ -124,15 +124,15 @@
       builder.AddLine('VALUENAME "%s"' % policy['name'])
     if policy['type'] == 'int':
       # The default max for NUMERIC values is 9999 which is too small for us.
-      max = '2000000000'
-      min = '0'
+      max = 2000000000
+      min = 0
       if self.PolicyHasRestrictions(policy):
         schema = policy['schema']
         if 'minimum' in schema:
           min = schema['minimum']
         if 'maximum' in schema:
           max = schema['maximum']
-      builder.AddLine('MIN ' + str(min) + ' MAX ' + max)
+      builder.AddLine('MIN %d MAX %d' % (min, max))
     if policy['type'] in ('string', 'dict', 'external'):
       # The default max for EDITTEXT values is 1023, which is too small for
       # big JSON blobs and other string policies.
diff --git a/components/policy/tools/template_writers/writers/adm_writer_unittest.py b/components/policy/tools/template_writers/writers/adm_writer_unittest.py
index 92bbfbd..38389e0d 100755
--- a/components/policy/tools/template_writers/writers/adm_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/adm_writer_unittest.py
@@ -474,6 +474,76 @@
 ''')
     self.CompareOutputs(output, expected_output)
 
+  def testIntPolicyWithRange(self):
+    # Tests a policy group with a single policy of type 'int' with a min and
+    # max value.
+    policy_json = '''
+      {
+        'policy_definitions': [
+          {
+            'name': 'IntPolicy',
+            'type': 'int',
+            'schema': { 'type': 'integer', 'minimum': 5, 'maximum': 10 },
+            'caption': 'Caption of policy.',
+            'features': { 'can_be_recommended': True },
+            'desc': 'Description of policy.',
+            'supported_on': ['chrome.win:8-']
+          },
+        ],
+        'policy_atomic_group_definitions': [],
+        'placeholders': [],
+        'messages': %s
+      }''' % MESSAGES
+    output = self.GetOutput(policy_json, {'_chromium': '1'}, 'adm')
+    expected_output = self.ConstructOutput(['MACHINE', 'USER'], '''
+  CATEGORY !!chromium
+    KEYNAME "Software\\Policies\\Chromium"
+
+    POLICY !!IntPolicy_Policy
+      #if version >= 4
+        SUPPORTED !!SUPPORTED_WIN7
+      #endif
+      EXPLAIN !!IntPolicy_Explain
+
+      PART !!IntPolicy_Part  NUMERIC
+        VALUENAME "IntPolicy"
+        MIN 5 MAX 10
+      END PART
+    END POLICY
+
+  END CATEGORY
+
+  CATEGORY !!chromium_recommended
+    KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+    POLICY !!IntPolicy_Policy
+      #if version >= 4
+        SUPPORTED !!SUPPORTED_WIN7
+      #endif
+      EXPLAIN !!IntPolicy_Explain
+
+      PART !!IntPolicy_Part  NUMERIC
+        VALUENAME "IntPolicy"
+        MIN 5 MAX 10
+      END PART
+    END POLICY
+
+  END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WIN7="Microsoft Windows 7 or later"
+SUPPORTED_WIN7_ONLY="Microsoft Windows 7"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+IntPolicy_Policy="Caption of policy."
+IntPolicy_Explain="Description of policy.\\n\\n\
+Reference: \
+https://cloud.google.com/docs/chrome-enterprise/policies/?policy=IntPolicy"
+IntPolicy_Part="Caption of policy."
+''')
+    self.CompareOutputs(output, expected_output)
+
   def testIntEnumPolicy(self):
     # Tests a policy group with a single policy of type 'int-enum'.
     policy_json = '''
diff --git a/components/safety_check/safety_check.cc b/components/safety_check/safety_check.cc
index e9539b25..e09f9d7 100644
--- a/components/safety_check/safety_check.cc
+++ b/components/safety_check/safety_check.cc
@@ -18,13 +18,17 @@
 void SafetyCheck::CheckSafeBrowsing(PrefService* pref_service) {
   const PrefService::Preference* enabled_pref =
       pref_service->FindPreference(prefs::kSafeBrowsingEnabled);
-  bool enabled = pref_service->GetBoolean(prefs::kSafeBrowsingEnabled);
+  bool is_sb_enabled = pref_service->GetBoolean(prefs::kSafeBrowsingEnabled);
+  bool is_sb_managed = enabled_pref->IsManaged();
+
   SafeBrowsingStatus status;
-  if (enabled && pref_service->GetBoolean(prefs::kSafeBrowsingEnhanced)) {
+  if (is_sb_enabled && pref_service->GetBoolean(prefs::kSafeBrowsingEnhanced)) {
     status = SafeBrowsingStatus::kEnabledEnhanced;
-  } else if (enabled) {
+  } else if (is_sb_enabled && is_sb_managed) {
     status = SafeBrowsingStatus::kEnabledStandard;
-  } else if (enabled_pref->IsManaged()) {
+  } else if (is_sb_enabled && !is_sb_managed) {
+    status = SafeBrowsingStatus::kEnabledStandardAvailableEnhanced;
+  } else if (is_sb_managed) {
     status = SafeBrowsingStatus::kDisabledByAdmin;
   } else if (enabled_pref->IsExtensionControlled()) {
     status = SafeBrowsingStatus::kDisabledByExtension;
diff --git a/components/safety_check/safety_check.h b/components/safety_check/safety_check.h
index 2bd457a..37663d5 100644
--- a/components/safety_check/safety_check.h
+++ b/components/safety_check/safety_check.h
@@ -29,8 +29,9 @@
     kDisabledByExtension = 4,
     kEnabledStandard = 5,
     kEnabledEnhanced = 6,
+    kEnabledStandardAvailableEnhanced = 7,
     // New enum values must go above here.
-    kMaxValue = kEnabledEnhanced,
+    kMaxValue = kEnabledStandardAvailableEnhanced,
   };
 
   class SafetyCheckHandlerInterface {
diff --git a/components/services/storage/public/mojom/service_worker_storage_control.mojom b/components/services/storage/public/mojom/service_worker_storage_control.mojom
index f653334..0fa69076 100644
--- a/components/services/storage/public/mojom/service_worker_storage_control.mojom
+++ b/components/services/storage/public/mojom/service_worker_storage_control.mojom
@@ -93,7 +93,6 @@
 // lifetime of user data is tied up with the registration.
 // It will be deleted when the corresponding registration is deleted.
 struct ServiceWorkerUserData {
-  int64 registration_id;
   string key;
   string value;
 };
@@ -249,14 +248,14 @@
       (ServiceWorkerDatabaseStatus status);
 
   // Gets the user data from all registrations that have user data for |key|.
+  // Returns a map from registration IDs to their values.
   GetUserDataForAllRegistrations(string key) =>
-      (ServiceWorkerDatabaseStatus status,
-       array<ServiceWorkerUserData> values);
+      (ServiceWorkerDatabaseStatus status, map<int64, string> values);
   // Gets the user data from all registrations that have user data for
-  // |key_prefix| where |key_prefix| is a prefix of keys.
+  // |key_prefix| where |key_prefix| is a prefix of keys. Returns a map from
+  // registration IDs to their values.
   GetUserDataForAllRegistrationsByKeyPrefix(string key_prefix) =>
-      (ServiceWorkerDatabaseStatus status,
-       array<ServiceWorkerUserData> values);
+      (ServiceWorkerDatabaseStatus status, map<int64, string> values);
   // Clears the user data from all registrations using |key_prefix| as a prefix
   // of keys.
   ClearUserDataForAllRegistrationsByKeyPrefix(string key_prefix) =>
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
index d5eea0f4..25dd40b 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
@@ -36,18 +36,16 @@
   capabilities_.preserve_buffer_content = true;
   capabilities_.only_invalidates_damage_rect = false;
   capabilities_.number_of_buffers = 3;
-#if defined(OS_ANDROID)
   capabilities_.orientation_mode = OutputSurface::OrientationMode::kHardware;
-  // With vulkan and android surface control, if the chrome is launched in
-  // landscape mode, the chrome is always blank until chrome window is rotated
-  // once. Workaround this problem by using logic rotation mode.
+#if defined(OS_ANDROID)
+  // With vulkan, if the chrome is launched in landscape mode, the chrome is
+  // always blank until chrome window is rotated once. Workaround this problem
+  // by using logic rotation mode.
   // TODO(https://crbug.com/1115065): use hardware orientation mode for vulkan,
   if (dependency_->GetSharedContextState()->GrContextIsVulkan() &&
       base::FeatureList::GetFieldTrial(features::kVulkan)) {
     capabilities_.orientation_mode = OutputSurface::OrientationMode::kLogic;
   }
-#else
-  capabilities_.orientation_mode = OutputSurface::OrientationMode::kHardware;
 #endif
 
   // Force the number of max pending frames to one when the switch
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan.cc b/components/viz/service/display_embedder/skia_output_device_vulkan.cc
index 57154ac..d69c465 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan.cc
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan.cc
@@ -7,11 +7,13 @@
 #include <utility>
 
 #include "base/compiler_specific.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "build/build_config.h"
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "gpu/ipc/common/gpu_surface_lookup.h"
 #include "gpu/vulkan/vulkan_function_pointers.h"
 #include "gpu/vulkan/vulkan_implementation.h"
@@ -292,6 +294,14 @@
   capabilities_.output_surface_origin = gfx::SurfaceOrigin::kTopLeft;
   capabilities_.supports_post_sub_buffer = true;
   capabilities_.orientation_mode = OutputSurface::OrientationMode::kHardware;
+#if defined(OS_ANDROID)
+  // With vulkan, if the chrome is launched in landscape mode, the chrome is
+  // always blank until chrome window is rotated once. Workaround this problem
+  // by using logic rotation mode.
+  // TODO(https://crbug.com/1115065): use hardware orientation mode for vulkan,
+  if (base::FeatureList::GetFieldTrial(features::kVulkan))
+    capabilities_.orientation_mode = OutputSurface::OrientationMode::kLogic;
+#endif
   // We don't know the number of buffers until the VulkanSwapChain is
   // initialized, so set it to 0. Since |damage_area_from_skia_output_device| is
   // assigned to true, so |number_of_buffers| will not be used for tracking
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index b9cdd83..4afabfa 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -268,8 +268,6 @@
       return gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE;
     case OutputSurface::OrientationMode::kHardware:
       return display_transform_;
-    default:
-      NOTREACHED();
   }
 }
 
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 41fcc60..352a655 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -6599,6 +6599,8 @@
 IN_PROC_BROWSER_TEST_F(
     BackForwardCacheBrowserTest,
     ProcessKilledIfMessageReceivedOnAssociatedInterfaceWhileCached) {
+  base::HistogramTester histogram_tester;
+
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
@@ -6632,6 +6634,11 @@
               testing::Optional(
                   bad_message::RFH_RECEIVED_ASSOCIATED_MESSAGE_WHILE_BFCACHED));
   EXPECT_TRUE(delete_observer_rfh_a.deleted());
+  histogram_tester.ExpectBucketCount(
+      "BackForwardCache.UnexpectedRendererToBrowserMessage.InterfaceName",
+      base::HistogramBase::Sample(
+          static_cast<int32_t>(base::HashMetricName(mojom::Echo::Name_))),
+      1);
 }
 
 IN_PROC_BROWSER_TEST_F(
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 19ae61c..8ecb7872 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -408,6 +408,10 @@
         "KillIfInBackForwardCacheMessageFilter::WillDispatch bad_message",
         "interface_name", interface_name_, "message_name", message->name());
 
+    base::UmaHistogramSparse(
+        "BackForwardCache.UnexpectedRendererToBrowserMessage.InterfaceName",
+        static_cast<int32_t>(base::HashMetricName(interface_name_)));
+
     switch (policy_) {
       case BackForwardCacheImpl::kMessagePolicyNone:
       case BackForwardCacheImpl::kMessagePolicyLog:
@@ -1360,6 +1364,13 @@
   return it == portals_.end() ? nullptr : it->get();
 }
 
+std::vector<Portal*> RenderFrameHostImpl::GetPortals() const {
+  std::vector<Portal*> result;
+  for (const auto& portal : portals_)
+    result.push_back(portal.get());
+  return result;
+}
+
 void RenderFrameHostImpl::DestroyPortal(Portal* portal) {
   auto it = portals_.find(portal);
   CHECK(it != portals_.end());
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index ff48cc63..321131b1 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1191,6 +1191,9 @@
   // Look up a portal by its token (as received from the renderer process).
   Portal* FindPortalByToken(const blink::PortalToken& portal_token);
 
+  // Return portals owned by |this|.
+  std::vector<Portal*> GetPortals() const;
+
   // Called when a Portal needs to be destroyed.
   void DestroyPortal(Portal* portal);
 
diff --git a/content/browser/native_file_system/file_system_chooser_browsertest.cc b/content/browser/native_file_system/file_system_chooser_browsertest.cc
index 58a76ab5..356842d 100644
--- a/content/browser/native_file_system/file_system_chooser_browsertest.cc
+++ b/content/browser/native_file_system/file_system_chooser_browsertest.cc
@@ -179,6 +179,34 @@
   EXPECT_FALSE(IsFullscreen());
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
+                       OpenFile_BlockedPermission) {
+  const base::FilePath test_file = CreateTestFile("Save File");
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_file}, &dialog_params));
+
+  testing::StrictMock<MockNativeFileSystemPermissionContext> permission_context;
+  static_cast<NativeFileSystemManagerImpl*>(
+      BrowserContext::GetStoragePartition(
+          shell()->web_contents()->GetBrowserContext(),
+          shell()->web_contents()->GetSiteInstance())
+          ->GetNativeFileSystemEntryFactory())
+      ->SetPermissionContextForTesting(&permission_context);
+
+  EXPECT_CALL(permission_context,
+              CanObtainReadPermission(url::Origin::Create(
+                  embedded_test_server()->GetURL("/title1.html"))))
+      .WillOnce(testing::Return(false));
+
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  auto result = EvalJs(shell(), "self.showOpenFilePicker()");
+  EXPECT_TRUE(result.error.find("not allowed") != std::string::npos)
+      << result.error;
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_NONE, dialog_params.type);
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SaveFile_NonExistingFile) {
   const std::string file_contents = "file contents to write";
   const base::FilePath test_file = CreateTestFile("");
@@ -254,6 +282,10 @@
       ->SetPermissionContextForTesting(&permission_context);
 
   EXPECT_CALL(permission_context,
+              CanObtainReadPermission(url::Origin::Create(
+                  embedded_test_server()->GetURL("/title1.html"))))
+      .WillOnce(testing::Return(true));
+  EXPECT_CALL(permission_context,
               CanObtainWritePermission(url::Origin::Create(
                   embedded_test_server()->GetURL("/title1.html"))))
       .WillOnce(testing::Return(false));
@@ -355,6 +387,34 @@
   EXPECT_FALSE(IsFullscreen());
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
+                       OpenDirectory_BlockedPermission) {
+  base::FilePath test_dir = CreateTestDir();
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
+
+  testing::StrictMock<MockNativeFileSystemPermissionContext> permission_context;
+  static_cast<NativeFileSystemManagerImpl*>(
+      BrowserContext::GetStoragePartition(
+          shell()->web_contents()->GetBrowserContext(),
+          shell()->web_contents()->GetSiteInstance())
+          ->GetNativeFileSystemEntryFactory())
+      ->SetPermissionContextForTesting(&permission_context);
+
+  EXPECT_CALL(permission_context,
+              CanObtainReadPermission(url::Origin::Create(
+                  embedded_test_server()->GetURL("/title1.html"))))
+      .WillOnce(testing::Return(false));
+
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  auto result = EvalJs(shell(), "self.showDirectoryPicker()");
+  EXPECT_TRUE(result.error.find("not allowed") != std::string::npos)
+      << result.error;
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_NONE, dialog_params.type);
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenDirectory_DenyAccess) {
   base::FilePath test_dir = CreateTestDir();
   SelectFileDialogParams dialog_params;
@@ -375,6 +435,11 @@
       PermissionStatus::ASK);
 
   EXPECT_CALL(permission_context,
+              CanObtainReadPermission(url::Origin::Create(
+                  embedded_test_server()->GetURL("/title1.html"))))
+      .WillOnce(testing::Return(true));
+
+  EXPECT_CALL(permission_context,
               ConfirmSensitiveDirectoryAccess_(
                   testing::_, testing::_, testing::_, testing::_, testing::_))
       .WillOnce(RunOnceCallback<4>(SensitiveDirectoryResult::kAllowed));
@@ -437,6 +502,10 @@
       .WillOnce(RunOnceCallback<4>(SensitiveDirectoryResult::kAbort));
 
   EXPECT_CALL(permission_context,
+              CanObtainReadPermission(url::Origin::Create(
+                  embedded_test_server()->GetURL("/title1.html"))))
+      .WillOnce(testing::Return(true));
+  EXPECT_CALL(permission_context,
               CanObtainWritePermission(url::Origin::Create(
                   embedded_test_server()->GetURL("/title1.html"))))
       .WillOnce(testing::Return(true));
@@ -484,6 +553,10 @@
       .WillOnce(RunOnceCallback<4>(SensitiveDirectoryResult::kAbort));
 
   EXPECT_CALL(permission_context,
+              CanObtainReadPermission(url::Origin::Create(
+                  embedded_test_server()->GetURL("/title1.html"))))
+      .WillOnce(testing::Return(true));
+  EXPECT_CALL(permission_context,
               CanObtainWritePermission(url::Origin::Create(
                   embedded_test_server()->GetURL("/title1.html"))))
       .WillOnce(testing::Return(true));
diff --git a/content/browser/native_file_system/mock_native_file_system_permission_context.h b/content/browser/native_file_system/mock_native_file_system_permission_context.h
index 82fe8a2..79d5389 100644
--- a/content/browser/native_file_system/mock_native_file_system_permission_context.h
+++ b/content/browser/native_file_system/mock_native_file_system_permission_context.h
@@ -53,6 +53,7 @@
                     GlobalFrameRoutingId frame_id,
                     base::OnceCallback<void(AfterWriteCheckResult)>& callback));
 
+  MOCK_METHOD1(CanObtainReadPermission, bool(const url::Origin& origin));
   MOCK_METHOD1(CanObtainWritePermission, bool(const url::Origin& origin));
 };
 
diff --git a/content/browser/native_file_system/native_file_system_manager_impl.cc b/content/browser/native_file_system/native_file_system_manager_impl.cc
index e7630d2..03787ae 100644
--- a/content/browser/native_file_system/native_file_system_manager_impl.cc
+++ b/content/browser/native_file_system/native_file_system_manager_impl.cc
@@ -246,16 +246,17 @@
     return;
   }
 
-  // When site setting is block, it's better not to show file chooser for save.
-  if (type == blink::mojom::ChooseFileSystemEntryType::kSaveFile &&
-      permission_context_ &&
-      !permission_context_->CanObtainWritePermission(context.origin)) {
-    std::move(callback).Run(
-        native_file_system_error::FromStatus(
-            NativeFileSystemStatus::kPermissionDenied),
-        std::vector<blink::mojom::NativeFileSystemEntryPtr>());
-
-    return;
+  if (permission_context_) {
+    // When site setting is block, it's better not to show file chooser.
+    if (!permission_context_->CanObtainReadPermission(context.origin) ||
+        (type == blink::mojom::ChooseFileSystemEntryType::kSaveFile &&
+         !permission_context_->CanObtainWritePermission(context.origin))) {
+      std::move(callback).Run(
+          native_file_system_error::FromStatus(
+              NativeFileSystemStatus::kPermissionDenied),
+          std::vector<blink::mojom::NativeFileSystemEntryPtr>());
+      return;
+    }
   }
 
   RenderFrameHost* rfh = RenderFrameHost::FromID(context.frame_id);
diff --git a/content/browser/portal/portal.cc b/content/browser/portal/portal.cc
index 4da15e4..edf8114 100644
--- a/content/browser/portal/portal.cc
+++ b/content/browser/portal/portal.cc
@@ -307,169 +307,34 @@
 void Portal::Activate(blink::TransferableMessage data,
                       base::TimeTicks activation_time,
                       ActivateCallback callback) {
-  WebContentsImpl* outer_contents = GetPortalHostContents();
-
-  if (outer_contents->portal()) {
+  if (GetPortalHostContents()->portal()) {
     mojo::ReportBadMessage("Portal::Activate called on nested portal");
-    DestroySelf();  // Also deletes |this|.
+    DestroySelf();
     return;
   }
 
-  DCHECK(owner_render_frame_host_->IsCurrent())
-      << "The binding should have been closed when the portal's outer "
-         "FrameTreeNode was deleted due to swap out.";
-
-  DCHECK(portal_contents_);
-  NavigationControllerImpl& portal_controller =
-      portal_contents_->GetController();
-  NavigationControllerImpl& predecessor_controller =
-      outer_contents->GetController();
-
-  // If no navigation has yet committed in the portal, it cannot be activated as
-  // this would lead to an empty tab contents (without even an about:blank).
-  if (portal_controller.GetLastCommittedEntryIndex() < 0) {
-    std::move(callback).Run(
-        blink::mojom::PortalActivateResult::kRejectedDueToPortalNotReady);
-    return;
-  }
-  DCHECK(predecessor_controller.GetLastCommittedEntry());
-
-  // Error pages and interstitials may not host portals due to the HTTP(S)
-  // restriction.
-  DCHECK_EQ(PAGE_TYPE_NORMAL,
-            predecessor_controller.GetLastCommittedEntry()->GetPageType());
-
-  // If the portal is crashed or is showing an error page, reject activation.
-  if (portal_contents_->IsCrashed() ||
-      portal_controller.GetLastCommittedEntry()->GetPageType() !=
-          PAGE_TYPE_NORMAL) {
-    std::move(callback).Run(
-        blink::mojom::PortalActivateResult::kRejectedDueToErrorInPortal);
+  if (is_activating_) {
+    mojo::ReportBadMessage("Portal::Activate called twice on the same portal");
+    DestroySelf();
     return;
   }
 
-  // If a navigation in the main frame is occurring, stop it if possible and
-  // reject the activation if it's too late or if an ongoing navigation takes
-  // precedence. There are a few cases here:
-  // - a different RenderFrameHost has been assigned to the FrameTreeNode
-  // - the same RenderFrameHost is being used, but it is committing a navigation
-  // - the FrameTreeNode holds a navigation request that can't turn back but has
-  //   not yet been handed off to a RenderFrameHost
-  FrameTreeNode* outer_root_node = owner_render_frame_host_->frame_tree_node();
-  NavigationRequest* outer_navigation = outer_root_node->navigation_request();
-  const bool has_user_gesture =
-      owner_render_frame_host_->HasTransientUserActivation();
-
-  // WILL_PROCESS_RESPONSE is slightly early: it happens
-  // immediately before READY_TO_COMMIT (unless it's deferred), but
-  // WILL_PROCESS_RESPONSE is easier to hook for tests using a
-  // NavigationThrottle.
-  if (owner_render_frame_host_->HasPendingCommitNavigation() ||
-      (outer_navigation &&
-       outer_navigation->state() >= NavigationRequest::WILL_PROCESS_RESPONSE) ||
-      Navigator::ShouldIgnoreIncomingRendererRequest(outer_navigation,
-                                                     has_user_gesture)) {
-    std::move(callback).Run(blink::mojom::PortalActivateResult::
-                                kRejectedDueToPredecessorNavigation);
-    return;
-  }
-  outer_root_node->navigator().CancelNavigation(outer_root_node);
-
-  DCHECK(!is_closing_) << "Portal should not be shutting down when contents "
-                          "ownership is yielded";
-
-  WebContentsDelegate* delegate = outer_contents->GetDelegate();
-  std::unique_ptr<WebContents> successor_contents;
-
-  if (portal_contents_->GetOuterWebContents()) {
-    FrameTreeNode* outer_frame_tree_node = FrameTreeNode::GloballyFindByID(
-        portal_contents_->GetOuterDelegateFrameTreeNodeId());
-    outer_frame_tree_node->RemoveObserver(this);
-    successor_contents = portal_contents_->DetachFromOuterWebContents();
-    owner_render_frame_host_->RemoveChild(outer_frame_tree_node);
-  } else {
-    // Portals created for predecessor pages during activation may not be
-    // attached to an outer WebContents, and may not have an outer frame tree
-    // node created (i.e. CreateProxyAndAttachPortal isn't called). In this
-    // case, we can skip a few of the detachment steps above.
-    for (auto& render_view_host :
-         portal_contents_->GetFrameTree()->render_view_hosts()) {
-      CreatePortalRenderWidgetHostView(portal_contents_.get(),
-                                       render_view_host.second);
+  for (Portal* portal : owner_render_frame_host()->GetPortals()) {
+    if (portal != this && portal->is_activating_) {
+      mojo::ReportBadMessage(
+          "Portal::Activate called on portal whose owner RenderFrameHost has "
+          "another portal that is activating");
+      DestroySelf();
+      return;
     }
-    successor_contents = portal_contents_.ReleaseOwnership();
-  }
-  DCHECK(!portal_contents_.OwnsContents());
-
-  // This assumes that the delegate keeps the new contents alive long enough to
-  // notify it of activation, at least.
-  WebContentsImpl* successor_contents_raw =
-      static_cast<WebContentsImpl*>(successor_contents.get());
-
-  auto* outer_contents_main_frame_view = static_cast<RenderWidgetHostViewBase*>(
-      outer_contents->GetMainFrame()->GetView());
-  DCHECK(!outer_contents->GetPendingMainFrame());
-  auto* portal_contents_main_frame_view =
-      static_cast<RenderWidgetHostViewBase*>(
-          successor_contents_raw->GetMainFrame()->GetView());
-
-  std::vector<std::unique_ptr<ui::TouchEvent>> touch_events;
-
-  if (outer_contents_main_frame_view) {
-    // Take fallback contents from previous WebContents so that the activation
-    // is smooth without flashes.
-    portal_contents_main_frame_view->TakeFallbackContentFrom(
-        outer_contents_main_frame_view);
-    touch_events =
-        outer_contents_main_frame_view->ExtractAndCancelActiveTouches();
-    FlushTouchEventQueues(outer_contents_main_frame_view->host());
   }
 
-  TakeHistoryForActivation(successor_contents_raw, outer_contents);
-
-  devtools_instrumentation::PortalActivated(outer_contents->GetMainFrame());
-  successor_contents_raw->set_portal(nullptr);
-
-  std::unique_ptr<WebContents> predecessor_web_contents =
-      delegate->ActivatePortalWebContents(outer_contents,
-                                          std::move(successor_contents));
-  DCHECK_EQ(predecessor_web_contents.get(), outer_contents);
-
-  if (outer_contents_main_frame_view) {
-    portal_contents_main_frame_view->TransferTouches(touch_events);
-    // Takes ownership of SyntheticGestureController from the predecessor's
-    // RenderWidgetHost. This allows the controller to continue sending events
-    // to the new RenderWidgetHostView.
-    portal_contents_main_frame_view->host()->TakeSyntheticGestureController(
-        outer_contents_main_frame_view->host());
-    outer_contents_main_frame_view->Destroy();
-  }
-
-  // These pointers are cleared so that they don't dangle in the event this
-  // object isn't immediately deleted. It isn't done sooner because
-  // ActivatePortalWebContents misbehaves if the WebContents doesn't appear to
-  // be a portal at that time.
-  portal_contents_.Clear();
-
-  mojo::PendingAssociatedRemote<blink::mojom::Portal> pending_portal;
-  auto portal_receiver = pending_portal.InitWithNewEndpointAndPassReceiver();
-  mojo::PendingAssociatedRemote<blink::mojom::PortalClient> pending_client;
-  auto client_receiver = pending_client.InitWithNewEndpointAndPassReceiver();
-
-  RenderFrameHostImpl* successor_main_frame =
-      successor_contents_raw->GetMainFrame();
-  auto predecessor = std::make_unique<Portal>(
-      successor_main_frame, std::move(predecessor_web_contents));
-  predecessor->Bind(std::move(portal_receiver), std::move(pending_client));
-  successor_main_frame->OnPortalActivated(
-      std::move(predecessor), std::move(pending_portal),
-      std::move(client_receiver), std::move(data), std::move(callback));
-
-  // Notifying of activation happens later than ActivatePortalWebContents so
-  // that it is observed after predecessor_web_contents has been moved into a
-  // portal.
-  DCHECK(outer_contents->IsPortal());
-  successor_contents_raw->DidActivatePortal(outer_contents, activation_time);
+  is_activating_ = true;
+  WebContentsImpl* outer_contents = GetPortalHostContents();
+  outer_contents->GetDelegate()->UpdateInspectedWebContentsIfNecessary(
+      outer_contents, portal_contents_.get(),
+      base::BindOnce(&Portal::ActivateImpl, weak_factory_.GetWeakPtr(),
+                     std::move(data), activation_time, std::move(callback)));
 }
 
 void Portal::PostMessageToGuest(
@@ -574,6 +439,186 @@
       WebContents::FromRenderFrameHost(owner_render_frame_host_));
 }
 
+std::pair<bool, blink::mojom::PortalActivateResult> Portal::CanActivate() {
+  WebContentsImpl* outer_contents = GetPortalHostContents();
+
+  DCHECK(owner_render_frame_host_->IsCurrent())
+      << "The binding should have been closed when the portal's outer "
+         "FrameTreeNode was deleted due to swap out.";
+
+  DCHECK(portal_contents_);
+  NavigationControllerImpl& portal_controller =
+      portal_contents_->GetController();
+  NavigationControllerImpl& predecessor_controller =
+      outer_contents->GetController();
+
+  // If no navigation has yet committed in the portal, it cannot be activated as
+  // this would lead to an empty tab contents (without even an about:blank).
+  if (portal_controller.GetLastCommittedEntryIndex() < 0) {
+    return std::make_pair(
+        false,
+        blink::mojom::PortalActivateResult::kRejectedDueToPortalNotReady);
+  }
+  DCHECK(predecessor_controller.GetLastCommittedEntry());
+
+  // Error pages and interstitials may not host portals due to the HTTP(S)
+  // restriction.
+  DCHECK_EQ(PAGE_TYPE_NORMAL,
+            predecessor_controller.GetLastCommittedEntry()->GetPageType());
+
+  // If the portal is crashed or is showing an error page, reject activation.
+  if (portal_contents_->IsCrashed() ||
+      portal_controller.GetLastCommittedEntry()->GetPageType() !=
+          PAGE_TYPE_NORMAL) {
+    return std::make_pair(
+        false, blink::mojom::PortalActivateResult::kRejectedDueToErrorInPortal);
+  }
+
+  // If a navigation in the main frame is occurring, stop it if possible and
+  // reject the activation if it's too late or if an ongoing navigation takes
+  // precedence. There are a few cases here:
+  // - a different RenderFrameHost has been assigned to the FrameTreeNode
+  // - the same RenderFrameHost is being used, but it is committing a navigation
+  // - the FrameTreeNode holds a navigation request that can't turn back but has
+  //   not yet been handed off to a RenderFrameHost
+  FrameTreeNode* outer_root_node = owner_render_frame_host_->frame_tree_node();
+  NavigationRequest* outer_navigation = outer_root_node->navigation_request();
+  const bool has_user_gesture =
+      owner_render_frame_host_->HasTransientUserActivation();
+
+  // WILL_PROCESS_RESPONSE is slightly early: it happens
+  // immediately before READY_TO_COMMIT (unless it's deferred), but
+  // WILL_PROCESS_RESPONSE is easier to hook for tests using a
+  // NavigationThrottle.
+  if (owner_render_frame_host_->HasPendingCommitNavigation() ||
+      (outer_navigation &&
+       outer_navigation->state() >= NavigationRequest::WILL_PROCESS_RESPONSE) ||
+      Navigator::ShouldIgnoreIncomingRendererRequest(outer_navigation,
+                                                     has_user_gesture)) {
+    return std::make_pair(false, blink::mojom::PortalActivateResult::
+                                     kRejectedDueToPredecessorNavigation);
+  }
+  return std::make_pair(true,
+                        blink::mojom::PortalActivateResult::kAbortedDueToBug);
+}
+
+void Portal::ActivateImpl(blink::TransferableMessage data,
+                          base::TimeTicks activation_time,
+                          ActivateCallback callback) {
+  WebContentsImpl* outer_contents = GetPortalHostContents();
+  WebContentsDelegate* delegate = outer_contents->GetDelegate();
+
+  is_activating_ = false;
+
+  bool can_activate;
+  blink::mojom::PortalActivateResult activate_error;
+  std::tie(can_activate, activate_error) = CanActivate();
+  if (!can_activate) {
+    outer_contents->GetDelegate()->UpdateInspectedWebContentsIfNecessary(
+        portal_contents_.get(), outer_contents, base::DoNothing());
+    std::move(callback).Run(activate_error);
+    return;
+  }
+
+  FrameTreeNode* outer_root_node = owner_render_frame_host_->frame_tree_node();
+  outer_root_node->navigator().CancelNavigation(outer_root_node);
+
+  DCHECK(!is_closing_) << "Portal should not be shutting down when contents "
+                          "ownership is yielded";
+
+  std::unique_ptr<WebContents> successor_contents;
+
+  if (portal_contents_->GetOuterWebContents()) {
+    FrameTreeNode* outer_frame_tree_node = FrameTreeNode::GloballyFindByID(
+        portal_contents_->GetOuterDelegateFrameTreeNodeId());
+    outer_frame_tree_node->RemoveObserver(this);
+    successor_contents = portal_contents_->DetachFromOuterWebContents();
+    owner_render_frame_host_->RemoveChild(outer_frame_tree_node);
+  } else {
+    // Portals created for predecessor pages during activation may not be
+    // attached to an outer WebContents, and may not have an outer frame tree
+    // node created (i.e. CreateProxyAndAttachPortal isn't called). In this
+    // case, we can skip a few of the detachment steps above.
+    for (auto& render_view_host :
+         portal_contents_->GetFrameTree()->render_view_hosts()) {
+      CreatePortalRenderWidgetHostView(portal_contents_.get(),
+                                       render_view_host.second);
+    }
+    successor_contents = portal_contents_.ReleaseOwnership();
+  }
+  DCHECK(!portal_contents_.OwnsContents());
+
+  // This assumes that the delegate keeps the new contents alive long enough to
+  // notify it of activation, at least.
+  WebContentsImpl* successor_contents_raw =
+      static_cast<WebContentsImpl*>(successor_contents.get());
+
+  auto* outer_contents_main_frame_view = static_cast<RenderWidgetHostViewBase*>(
+      outer_contents->GetMainFrame()->GetView());
+  DCHECK(!outer_contents->GetPendingMainFrame());
+  auto* portal_contents_main_frame_view =
+      static_cast<RenderWidgetHostViewBase*>(
+          successor_contents_raw->GetMainFrame()->GetView());
+
+  std::vector<std::unique_ptr<ui::TouchEvent>> touch_events;
+
+  if (outer_contents_main_frame_view) {
+    // Take fallback contents from previous WebContents so that the activation
+    // is smooth without flashes.
+    portal_contents_main_frame_view->TakeFallbackContentFrom(
+        outer_contents_main_frame_view);
+    touch_events =
+        outer_contents_main_frame_view->ExtractAndCancelActiveTouches();
+    FlushTouchEventQueues(outer_contents_main_frame_view->host());
+  }
+
+  TakeHistoryForActivation(successor_contents_raw, outer_contents);
+
+  devtools_instrumentation::PortalActivated(outer_contents->GetMainFrame());
+  successor_contents_raw->set_portal(nullptr);
+
+  std::unique_ptr<WebContents> predecessor_web_contents =
+      delegate->ActivatePortalWebContents(outer_contents,
+                                          std::move(successor_contents));
+  DCHECK_EQ(predecessor_web_contents.get(), outer_contents);
+
+  if (outer_contents_main_frame_view) {
+    portal_contents_main_frame_view->TransferTouches(touch_events);
+    // Takes ownership of SyntheticGestureController from the predecessor's
+    // RenderWidgetHost. This allows the controller to continue sending events
+    // to the new RenderWidgetHostView.
+    portal_contents_main_frame_view->host()->TakeSyntheticGestureController(
+        outer_contents_main_frame_view->host());
+    outer_contents_main_frame_view->Destroy();
+  }
+
+  // These pointers are cleared so that they don't dangle in the event this
+  // object isn't immediately deleted. It isn't done sooner because
+  // ActivatePortalWebContents misbehaves if the WebContents doesn't appear to
+  // be a portal at that time.
+  portal_contents_.Clear();
+
+  mojo::PendingAssociatedRemote<blink::mojom::Portal> pending_portal;
+  auto portal_receiver = pending_portal.InitWithNewEndpointAndPassReceiver();
+  mojo::PendingAssociatedRemote<blink::mojom::PortalClient> pending_client;
+  auto client_receiver = pending_client.InitWithNewEndpointAndPassReceiver();
+
+  RenderFrameHostImpl* successor_main_frame =
+      successor_contents_raw->GetMainFrame();
+  auto predecessor = std::make_unique<Portal>(
+      successor_main_frame, std::move(predecessor_web_contents));
+  predecessor->Bind(std::move(portal_receiver), std::move(pending_client));
+  successor_main_frame->OnPortalActivated(
+      std::move(predecessor), std::move(pending_portal),
+      std::move(client_receiver), std::move(data), std::move(callback));
+
+  // Notifying of activation happens later than ActivatePortalWebContents so
+  // that it is observed after predecessor_web_contents has been moved into a
+  // portal.
+  DCHECK(outer_contents->IsPortal());
+  successor_contents_raw->DidActivatePortal(outer_contents, activation_time);
+}
+
 Portal::WebContentsHolder::WebContentsHolder(Portal* portal)
     : portal_(portal) {}
 
diff --git a/content/browser/portal/portal.h b/content/browser/portal/portal.h
index a5fbaa6..f6192f4 100644
--- a/content/browser/portal/portal.h
+++ b/content/browser/portal/portal.h
@@ -191,6 +191,11 @@
 
   void SetPortalContents(std::unique_ptr<WebContents> web_contents);
 
+  std::pair<bool, blink::mojom::PortalActivateResult> CanActivate();
+  void ActivateImpl(blink::TransferableMessage data,
+                    base::TimeTicks activation_time,
+                    ActivateCallback callback);
+
   RenderFrameHostImpl* owner_render_frame_host_;
 
   // Uniquely identifies the portal, this token is used by the browser process
@@ -214,9 +219,14 @@
   // Set when |Close| is called. Destruction will occur shortly thereafter.
   bool is_closing_ = false;
 
+  // Set when portal is activating.
+  bool is_activating_ = false;
+
   // Another implementation of blink::mojom::Portal to bind instead.
   // For use in testing only.
   std::unique_ptr<blink::mojom::Portal> interceptor_;
+
+  base::WeakPtrFactory<Portal> weak_factory_{this};
 };
 
 }  // namespace content
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index aa58c907..32654b1 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -2172,6 +2172,76 @@
   EXPECT_FALSE(download_observer.AwaitDownload());
 }
 
+IN_PROC_BROWSER_TEST_F(PortalBrowserTest, CallActivateOnTwoPortals) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
+  WebContentsImpl* web_contents_impl =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
+  shell()->ShowDevTools();
+
+  GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
+  Portal* portal_a = CreatePortalToUrl(web_contents_impl, url_a);
+  GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
+  Portal* portal_b = CreatePortalToUrl(web_contents_impl, url_b);
+
+  PortalInterceptorForTesting* portal_interceptor =
+      PortalInterceptorForTesting::From(portal_a);
+  // Hijacks navigate request and calls Activate on both portals.
+  portal_interceptor->SetNavigateCallback(base::BindRepeating(
+      [](Portal* portal_a, Portal* portal_b, const GURL&,
+         blink::mojom::ReferrerPtr,
+         blink::mojom::Portal::NavigateCallback callback) {
+        portal_a->Activate(blink::TransferableMessage(), base::TimeTicks::Now(),
+                           base::DoNothing());
+        portal_b->Activate(blink::TransferableMessage(), base::TimeTicks::Now(),
+                           base::DoNothing());
+        std::move(callback).Run();
+      },
+      portal_a, portal_b));
+
+  RenderProcessHostBadIpcMessageWaiter rph_kill_waiter(
+      main_frame->GetProcess());
+  GURL dummy_url = embedded_test_server()->GetURL("c.com", "/title1.html");
+  ExecuteScriptAsync(
+      main_frame,
+      JsReplace("document.querySelector('portal').src = $1", dummy_url));
+  EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, rph_kill_waiter.Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(PortalBrowserTest, CallActivateTwice) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
+  WebContentsImpl* web_contents_impl =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
+  shell()->ShowDevTools();
+
+  GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
+  Portal* portal = CreatePortalToUrl(web_contents_impl, url);
+  PortalInterceptorForTesting* portal_interceptor =
+      PortalInterceptorForTesting::From(portal);
+  // Hijacks navigate request and calls Activate twice instead.
+  portal_interceptor->SetNavigateCallback(base::BindRepeating(
+      [](Portal* portal, const GURL&, blink::mojom::ReferrerPtr,
+         blink::mojom::Portal::NavigateCallback callback) {
+        portal->Activate(blink::TransferableMessage(), base::TimeTicks::Now(),
+                         base::DoNothing());
+        portal->Activate(blink::TransferableMessage(), base::TimeTicks::Now(),
+                         base::DoNothing());
+        std::move(callback).Run();
+      },
+      portal));
+
+  RenderProcessHostBadIpcMessageWaiter rph_kill_waiter(
+      main_frame->GetProcess());
+  GURL dummy_url = embedded_test_server()->GetURL("b.com", "/title1.html");
+  ExecuteScriptAsync(
+      main_frame,
+      JsReplace("document.querySelector('portal').src = $1", dummy_url));
+  EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, rph_kill_waiter.Wait());
+}
+
 namespace {
 
 static constexpr struct {
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index ee8379b..82aeb50 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -1030,8 +1030,6 @@
 
   if (!using_browser_compositor_) {
     SynchronousCopyContents(src_subrect, output_size, std::move(callback));
-    UMA_HISTOGRAM_TIMES("Compositing.CopyFromSurfaceTimeSynchronous",
-                        base::TimeTicks::Now() - start_time);
     return;
   }
 
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc
index 01a2eaf..38f3457 100644
--- a/content/browser/service_worker/service_worker_database.cc
+++ b/content/browser/service_worker/service_worker_database.cc
@@ -1111,7 +1111,7 @@
 ServiceWorkerDatabase::Status
 ServiceWorkerDatabase::ReadUserDataForAllRegistrations(
     const std::string& user_data_name,
-    std::vector<storage::mojom::ServiceWorkerUserDataPtr>* user_data) {
+    std::vector<std::pair<int64_t, std::string>>* user_data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(user_data->empty());
 
@@ -1153,8 +1153,7 @@
         user_data->clear();
         break;
       }
-      user_data->emplace_back(storage::mojom::ServiceWorkerUserData::New(
-          registration_id, user_data_name, value));
+      user_data->push_back(std::make_pair(registration_id, value));
     }
   }
 
@@ -1165,7 +1164,7 @@
 ServiceWorkerDatabase::Status
 ServiceWorkerDatabase::ReadUserDataForAllRegistrationsByKeyPrefix(
     const std::string& user_data_name_prefix,
-    std::vector<storage::mojom::ServiceWorkerUserDataPtr>* user_data) {
+    std::vector<std::pair<int64_t, std::string>>* user_data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(user_data->empty());
 
@@ -1222,8 +1221,7 @@
         user_data->clear();
         break;
       }
-      user_data->push_back(storage::mojom::ServiceWorkerUserData::New(
-          registration_id, parts[0], value));
+      user_data->push_back(std::make_pair(registration_id, value));
     }
   }
 
diff --git a/content/browser/service_worker/service_worker_database.h b/content/browser/service_worker/service_worker_database.h
index 49f9cd9..3ee09fdd 100644
--- a/content/browser/service_worker/service_worker_database.h
+++ b/content/browser/service_worker/service_worker_database.h
@@ -217,14 +217,14 @@
   // from the database. Returns OK if they are successfully read or not found.
   Status ReadUserDataForAllRegistrations(
       const std::string& user_data_name,
-      std::vector<storage::mojom::ServiceWorkerUserDataPtr>* user_data);
+      std::vector<std::pair<int64_t, std::string>>* user_data);
 
   // Reads user data for all registrations that have data with
   // |user_data_name_prefix| from the database. Returns OK if they are
   // successfully read or not found.
   Status ReadUserDataForAllRegistrationsByKeyPrefix(
       const std::string& user_data_name_prefix,
-      std::vector<storage::mojom::ServiceWorkerUserDataPtr>* user_data);
+      std::vector<std::pair<int64_t, std::string>>* user_data);
 
   // Deletes user data for all registrations that have data with
   // |user_data_name_prefix| from the database. Returns OK if all are
diff --git a/content/browser/service_worker/service_worker_database_unittest.cc b/content/browser/service_worker/service_worker_database_unittest.cc
index 0fa08fe..c5caaa2 100644
--- a/content/browser/service_worker/service_worker_database_unittest.cc
+++ b/content/browser/service_worker/service_worker_database_unittest.cc
@@ -106,12 +106,11 @@
 }
 
 std::vector<storage::mojom::ServiceWorkerUserDataPtr> CreateUserData(
-    int64_t registration_id,
     const std::vector<std::pair<std::string, std::string>>& key_value_pairs) {
   std::vector<storage::mojom::ServiceWorkerUserDataPtr> out;
   for (auto& kv : key_value_pairs) {
-    out.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id, kv.first, kv.second));
+    out.push_back(
+        storage::mojom::ServiceWorkerUserData::New(kv.first, kv.second));
   }
   return out;
 }
@@ -1084,9 +1083,8 @@
   // Write user data associated with the stored registration.
   std::vector<std::string> user_data_out;
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data.registration_id, kOrigin,
-                CreateUserData(data.registration_id, {{"key1", "data"}})));
+            database->WriteUserData(data.registration_id, kOrigin,
+                                    CreateUserData({{"key1", "data"}})));
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data.registration_id, {"key1"}, &user_data_out));
@@ -1096,15 +1094,13 @@
   // Writing user data not associated with the stored registration should be
   // failed.
   EXPECT_EQ(ServiceWorkerDatabase::Status::kErrorNotFound,
-            database->WriteUserData(
-                300, kOrigin,
-                CreateUserData(data.registration_id, {{"key1", "data"}})));
+            database->WriteUserData(300, kOrigin,
+                                    CreateUserData({{"key1", "data"}})));
 
   // Write empty user data for a different key.
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(data.registration_id, kOrigin,
-                                    CreateUserData(data.registration_id,
-                                                   {{"key2", std::string()}})));
+                                    CreateUserData({{"key2", std::string()}})));
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data.registration_id, {"key2"}, &user_data_out));
@@ -1118,9 +1114,8 @@
 
   // Overwrite the existing user data.
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data.registration_id, kOrigin,
-                CreateUserData(data.registration_id, {{"key1", "overwrite"}})));
+            database->WriteUserData(data.registration_id, kOrigin,
+                                    CreateUserData({{"key1", "overwrite"}})));
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data.registration_id, {"key1"}, &user_data_out));
@@ -1142,11 +1137,10 @@
 
   // Write/overwrite multiple user data keys.
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data.registration_id, kOrigin,
-                CreateUserData(data.registration_id, {{"key2", "overwrite2"},
-                                                      {"key3", "data3"},
-                                                      {"key4", "data4"}})));
+            database->WriteUserData(data.registration_id, kOrigin,
+                                    CreateUserData({{"key2", "overwrite2"},
+                                                    {"key3", "data3"},
+                                                    {"key4", "data4"}})));
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kErrorNotFound,
       database->ReadUserData(data.registration_id, {"key1"}, &user_data_out));
@@ -1218,63 +1212,51 @@
   ASSERT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->WriteUserData(data1.registration_id, kOrigin,
-                              CreateUserData(data1.registration_id,
-                                             {{"key_prefix:key1", "value1"}})));
+                              CreateUserData({{"key_prefix:key1", "value1"}})));
   ASSERT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->WriteUserData(data1.registration_id, kOrigin,
-                              CreateUserData(data1.registration_id,
-                                             {{"key_prefix:key2", "value2"}})));
+                              CreateUserData({{"key_prefix:key2", "value2"}})));
   ASSERT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->WriteUserData(data1.registration_id, kOrigin,
-                              CreateUserData(data1.registration_id,
-                                             {{"key_prefix:key3", "value3"}})));
+                              CreateUserData({{"key_prefix:key3", "value3"}})));
 
   // Write user data associated with the registration2.
   ASSERT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->WriteUserData(data2.registration_id, kOrigin,
-                              CreateUserData(data2.registration_id,
-                                             {{"key_prefix:key1", "value1"}})));
+                              CreateUserData({{"key_prefix:key1", "value1"}})));
   ASSERT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->WriteUserData(data2.registration_id, kOrigin,
-                              CreateUserData(data2.registration_id,
-                                             {{"key_prefix:key2", "value2"}})));
+                              CreateUserData({{"key_prefix:key2", "value2"}})));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(
                 data2.registration_id, kOrigin,
-                CreateUserData(data2.registration_id,
-                               {{"another_key_prefix:key1", "value1"}})));
+                CreateUserData({{"another_key_prefix:key1", "value1"}})));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(
                 data2.registration_id, kOrigin,
-                CreateUserData(data2.registration_id,
-                               {{"another_key_prefix:key2", "value2"}})));
+                CreateUserData({{"another_key_prefix:key2", "value2"}})));
 
   // Get all registrations with user data by key prefix.
-  std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data_list;
+  std::vector<std::pair<int64_t, std::string>> user_data_list;
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->ReadUserDataForAllRegistrationsByKeyPrefix(
                 "key_prefix:", &user_data_list));
   ASSERT_EQ(5u, user_data_list.size());
 
-  EXPECT_EQ(data1.registration_id, user_data_list[0]->registration_id);
-  EXPECT_EQ("key_prefix:key1", user_data_list[0]->key);
-  EXPECT_EQ("value1", user_data_list[0]->value);
-  EXPECT_EQ(data2.registration_id, user_data_list[1]->registration_id);
-  EXPECT_EQ("key_prefix:key1", user_data_list[1]->key);
-  EXPECT_EQ("value1", user_data_list[1]->value);
-  EXPECT_EQ(data1.registration_id, user_data_list[2]->registration_id);
-  EXPECT_EQ("key_prefix:key2", user_data_list[2]->key);
-  EXPECT_EQ("value2", user_data_list[2]->value);
-  EXPECT_EQ(data2.registration_id, user_data_list[3]->registration_id);
-  EXPECT_EQ("key_prefix:key2", user_data_list[3]->key);
-  EXPECT_EQ("value2", user_data_list[3]->value);
-  EXPECT_EQ(data1.registration_id, user_data_list[4]->registration_id);
-  EXPECT_EQ("key_prefix:key3", user_data_list[4]->key);
-  EXPECT_EQ("value3", user_data_list[4]->value);
+  EXPECT_EQ(data1.registration_id, user_data_list[0].first);
+  EXPECT_EQ("value1", user_data_list[0].second);
+  EXPECT_EQ(data2.registration_id, user_data_list[1].first);
+  EXPECT_EQ("value1", user_data_list[1].second);
+  EXPECT_EQ(data1.registration_id, user_data_list[2].first);
+  EXPECT_EQ("value2", user_data_list[2].second);
+  EXPECT_EQ(data2.registration_id, user_data_list[3].first);
+  EXPECT_EQ("value2", user_data_list[3].second);
+  EXPECT_EQ(data1.registration_id, user_data_list[4].first);
+  EXPECT_EQ("value3", user_data_list[4].second);
 }
 
 TEST(ServiceWorkerDatabaseTest, ReadUserDataByKeyPrefix) {
@@ -1298,8 +1280,7 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(
                 data.registration_id, kOrigin,
-                CreateUserData(data.registration_id,
-                               {{"key_prefix:key1", "value_c1"},
+                CreateUserData({{"key_prefix:key1", "value_c1"},
                                 {"key_prefix:key2", "value_c2"},
                                 {"other_key_prefix:k1", "value_d1"},
                                 {"other_key_prefix:k2", "value_d2"}})));
@@ -1344,8 +1325,7 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(
                 data.registration_id, kOrigin,
-                CreateUserData(data.registration_id,
-                               {{"key_prefix:key1", "value_c1"},
+                CreateUserData({{"key_prefix:key1", "value_c1"},
                                 {"key_prefix:key2", "value_c2"},
                                 {"other_key_prefix:k1", "value_d1"},
                                 {"other_key_prefix:k2", "value_d2"}})));
@@ -1407,8 +1387,7 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(
                 data1.registration_id, kOrigin,
-                CreateUserData(data1.registration_id,
-                               {{"key_prefix:key1", "value_a1"},
+                CreateUserData({{"key_prefix:key1", "value_a1"},
                                 {"key_prefix:key2", "value_a2"},
                                 {"key_prefix:key3", "value_a3"},
                                 {"kept_key_prefix:key1", "value_b1"}})));
@@ -1417,8 +1396,7 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(
                 data2.registration_id, kOrigin,
-                CreateUserData(data2.registration_id,
-                               {{"key_prefix:key1", "value_c1"},
+                CreateUserData({{"key_prefix:key1", "value_c1"},
                                 {"key_prefix:key2", "value_c2"},
                                 {"other_key_prefix:key1", "value_d1"},
                                 {"other_key_prefix:key2", "value_d2"},
@@ -1440,20 +1418,17 @@
                 {"key_prefix:", "other_key_prefix:", "not_found_key_prefix:"}));
 
   // User data with deleted "key_prefix:" should only remain for registration 1.
-  std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data_list;
+  std::vector<std::pair<int64_t, std::string>> user_data_list;
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->ReadUserDataForAllRegistrationsByKeyPrefix(
                 "key_prefix:", &user_data_list));
   ASSERT_EQ(3u, user_data_list.size());
-  EXPECT_EQ(data1.registration_id, user_data_list[0]->registration_id);
-  EXPECT_EQ("key_prefix:key1", user_data_list[0]->key);
-  EXPECT_EQ("value_a1", user_data_list[0]->value);
-  EXPECT_EQ(data1.registration_id, user_data_list[1]->registration_id);
-  EXPECT_EQ("key_prefix:key2", user_data_list[1]->key);
-  EXPECT_EQ("value_a2", user_data_list[1]->value);
-  EXPECT_EQ(data1.registration_id, user_data_list[2]->registration_id);
-  EXPECT_EQ("key_prefix:key3", user_data_list[2]->key);
-  EXPECT_EQ("value_a3", user_data_list[2]->value);
+  EXPECT_EQ(data1.registration_id, user_data_list[0].first);
+  EXPECT_EQ("value_a1", user_data_list[0].second);
+  EXPECT_EQ(data1.registration_id, user_data_list[1].first);
+  EXPECT_EQ("value_a2", user_data_list[1].second);
+  EXPECT_EQ(data1.registration_id, user_data_list[2].first);
+  EXPECT_EQ("value_a3", user_data_list[2].second);
 
   // User data for second deleted key prefix should also have been deleted.
   user_data_list.clear();
@@ -1469,15 +1444,12 @@
             database->ReadUserDataForAllRegistrationsByKeyPrefix(
                 "kept_key_prefix:", &user_data_list));
   ASSERT_EQ(3u, user_data_list.size());
-  EXPECT_EQ(data1.registration_id, user_data_list[0]->registration_id);
-  EXPECT_EQ("kept_key_prefix:key1", user_data_list[0]->key);
-  EXPECT_EQ("value_b1", user_data_list[0]->value);
-  EXPECT_EQ(data2.registration_id, user_data_list[1]->registration_id);
-  EXPECT_EQ("kept_key_prefix:key1", user_data_list[1]->key);
-  EXPECT_EQ("value_e1", user_data_list[1]->value);
-  EXPECT_EQ(data2.registration_id, user_data_list[2]->registration_id);
-  EXPECT_EQ("kept_key_prefix:key2", user_data_list[2]->key);
-  EXPECT_EQ("value_e2", user_data_list[2]->value);
+  EXPECT_EQ(data1.registration_id, user_data_list[0].first);
+  EXPECT_EQ("value_b1", user_data_list[0].second);
+  EXPECT_EQ(data2.registration_id, user_data_list[1].first);
+  EXPECT_EQ("value_e1", user_data_list[1].second);
+  EXPECT_EQ(data2.registration_id, user_data_list[2].first);
+  EXPECT_EQ("value_e2", user_data_list[2].second);
 }
 
 TEST(ServiceWorkerDatabaseTest,
@@ -1515,8 +1487,7 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(
                 data1.registration_id, kOrigin,
-                CreateUserData(data1.registration_id,
-                               {{"key_prefix:key1", "value_a1"},
+                CreateUserData({{"key_prefix:key1", "value_a1"},
                                 {"key_prefix:key2", "value_a2"},
                                 {"key_prefix:key3", "value_a3"},
                                 {"kept_key_prefix:key1", "value_b1"}})));
@@ -1525,8 +1496,7 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteUserData(
                 data2.registration_id, kOrigin,
-                CreateUserData(data2.registration_id,
-                               {{"key_prefix:key1", "value_c1"},
+                CreateUserData({{"key_prefix:key1", "value_c1"},
                                 {"key_prefix:key2", "value_c2"},
                                 {"kept_key_prefix:key1", "value_d1"},
                                 {"kept_key_prefix:key2", "value_d2"}})));
@@ -1544,7 +1514,7 @@
       database->DeleteUserDataForAllRegistrationsByKeyPrefix("key_prefix:"));
 
   // User data with deleted "key_prefix:" should be deleted.
-  std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data_list;
+  std::vector<std::pair<int64_t, std::string>> user_data_list;
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->ReadUserDataForAllRegistrationsByKeyPrefix(
                 "key_prefix:", &user_data_list));
@@ -1557,15 +1527,12 @@
                 "kept_key_prefix:", &user_data_list));
   ASSERT_EQ(3u, user_data_list.size());
 
-  EXPECT_EQ(data1.registration_id, user_data_list[0]->registration_id);
-  EXPECT_EQ("kept_key_prefix:key1", user_data_list[0]->key);
-  EXPECT_EQ("value_b1", user_data_list[0]->value);
-  EXPECT_EQ(data2.registration_id, user_data_list[1]->registration_id);
-  EXPECT_EQ("kept_key_prefix:key1", user_data_list[1]->key);
-  EXPECT_EQ("value_d1", user_data_list[1]->value);
-  EXPECT_EQ(data2.registration_id, user_data_list[2]->registration_id);
-  EXPECT_EQ("kept_key_prefix:key2", user_data_list[2]->key);
-  EXPECT_EQ("value_d2", user_data_list[2]->value);
+  EXPECT_EQ(data1.registration_id, user_data_list[0].first);
+  EXPECT_EQ("value_b1", user_data_list[0].second);
+  EXPECT_EQ(data2.registration_id, user_data_list[1].first);
+  EXPECT_EQ("value_d1", user_data_list[1].second);
+  EXPECT_EQ(data2.registration_id, user_data_list[2].first);
+  EXPECT_EQ("value_d2", user_data_list[2].second);
 }
 
 TEST(ServiceWorkerDatabaseTest, UserData_DataIsolation) {
@@ -1602,14 +1569,13 @@
   // Write user data associated with the registration1.
   std::vector<std::string> user_data_out;
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data1.registration_id, kOrigin,
-                CreateUserData(data1.registration_id, {{"key", "value1"}})));
+            database->WriteUserData(data1.registration_id, kOrigin,
+                                    CreateUserData({{"key", "data1"}})));
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data1.registration_id, {"key"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  EXPECT_EQ("value1", user_data_out[0]);
+  EXPECT_EQ("data1", user_data_out[0]);
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kErrorNotFound,
       database->ReadUserData(data2.registration_id, {"key"}, &user_data_out));
@@ -1617,31 +1583,28 @@
   // Write user data associated with the registration2. This shouldn't overwrite
   // the data associated with registration1.
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data2.registration_id, kOrigin,
-                CreateUserData(data2.registration_id, {{"key", "value2"}})));
+            database->WriteUserData(data2.registration_id, kOrigin,
+                                    CreateUserData({{"key", "data2"}})));
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data1.registration_id, {"key"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  EXPECT_EQ("value1", user_data_out[0]);
+  EXPECT_EQ("data1", user_data_out[0]);
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data2.registration_id, {"key"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  EXPECT_EQ("value2", user_data_out[0]);
+  EXPECT_EQ("data2", user_data_out[0]);
 
   // Get all registrations with user data.
-  std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data_list;
+  std::vector<std::pair<int64_t, std::string>> user_data_list;
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->ReadUserDataForAllRegistrations("key", &user_data_list));
   EXPECT_EQ(2u, user_data_list.size());
-  EXPECT_EQ(data1.registration_id, user_data_list[0]->registration_id);
-  EXPECT_EQ("key", user_data_list[0]->key);
-  EXPECT_EQ("value1", user_data_list[0]->value);
-  EXPECT_EQ(data2.registration_id, user_data_list[1]->registration_id);
-  EXPECT_EQ("key", user_data_list[1]->key);
-  EXPECT_EQ("value2", user_data_list[1]->value);
+  EXPECT_EQ(data1.registration_id, user_data_list[0].first);
+  EXPECT_EQ("data1", user_data_list[0].second);
+  EXPECT_EQ(data2.registration_id, user_data_list[1].first);
+  EXPECT_EQ("data2", user_data_list[1].second);
 
   // Delete the data associated with the registration2. This shouldn't delete
   // the data associated with registration1.
@@ -1651,7 +1614,7 @@
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data1.registration_id, {"key"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  EXPECT_EQ("value1", user_data_out[0]);
+  EXPECT_EQ("data1", user_data_out[0]);
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kErrorNotFound,
       database->ReadUserData(data2.registration_id, {"key"}, &user_data_out));
@@ -1661,9 +1624,8 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->ReadUserDataForAllRegistrations("key", &user_data_list));
   EXPECT_EQ(1u, user_data_list.size());
-  EXPECT_EQ(data1.registration_id, user_data_list[0]->registration_id);
-  EXPECT_EQ("key", user_data_list[0]->key);
-  EXPECT_EQ("value1", user_data_list[0]->value);
+  EXPECT_EQ(data1.registration_id, user_data_list[0].first);
+  EXPECT_EQ("data1", user_data_list[0].second);
 }
 
 TEST(ServiceWorkerDatabaseTest, UserData_DeleteRegistration) {
@@ -1699,34 +1661,31 @@
   // Write user data associated with the registration1.
   std::vector<std::string> user_data_out;
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data1.registration_id, kOrigin,
-                CreateUserData(data1.registration_id, {{"key1", "value1"}})));
+            database->WriteUserData(data1.registration_id, kOrigin,
+                                    CreateUserData({{"key1", "data1"}})));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data1.registration_id, kOrigin,
-                CreateUserData(data1.registration_id, {{"key2", "value2"}})));
+            database->WriteUserData(data1.registration_id, kOrigin,
+                                    CreateUserData({{"key2", "data2"}})));
   ASSERT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data1.registration_id, {"key1"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  ASSERT_EQ("value1", user_data_out[0]);
+  ASSERT_EQ("data1", user_data_out[0]);
   ASSERT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data1.registration_id, {"key2"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  ASSERT_EQ("value2", user_data_out[0]);
+  ASSERT_EQ("data2", user_data_out[0]);
 
   // Write user data associated with the registration2.
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data2.registration_id, kOrigin,
-                CreateUserData(data2.registration_id, {{"key3", "value3"}})));
+            database->WriteUserData(data2.registration_id, kOrigin,
+                                    CreateUserData({{"key3", "data3"}})));
   ASSERT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data2.registration_id, {"key3"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  ASSERT_EQ("value3", user_data_out[0]);
+  ASSERT_EQ("data3", user_data_out[0]);
 
   // Delete all data associated with the registration1. This shouldn't delete
   // the data associated with registration2.
@@ -1743,7 +1702,7 @@
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data2.registration_id, {"key3"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  EXPECT_EQ("value3", user_data_out[0]);
+  EXPECT_EQ("data3", user_data_out[0]);
 }
 
 TEST(ServiceWorkerDatabaseTest, UserData_UninitializedDatabase) {
@@ -1756,9 +1715,9 @@
             database->ReadUserData(100, {"key"}, &user_data_out));
 
   // Should be failed because the associated registration does not exist.
-  EXPECT_EQ(ServiceWorkerDatabase::Status::kErrorNotFound,
-            database->WriteUserData(100, kOrigin,
-                                    CreateUserData(100, {{"key", "value"}})));
+  EXPECT_EQ(
+      ServiceWorkerDatabase::Status::kErrorNotFound,
+      database->WriteUserData(100, kOrigin, CreateUserData({{"key", "data"}})));
 
   // Deleting non-existent entry should succeed.
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -1772,9 +1731,9 @@
             database->state_);
   EXPECT_EQ(ServiceWorkerDatabase::Status::kErrorNotFound,
             database->ReadUserData(100, {"key"}, &user_data_out));
-  EXPECT_EQ(ServiceWorkerDatabase::Status::kErrorNotFound,
-            database->WriteUserData(100, kOrigin,
-                                    CreateUserData(100, {{"key", "value"}})));
+  EXPECT_EQ(
+      ServiceWorkerDatabase::Status::kErrorNotFound,
+      database->WriteUserData(100, kOrigin, CreateUserData({{"key", "data"}})));
 
   // Deleting non-existent entry should succeed.
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -1967,13 +1926,11 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteRegistration(data1, resources1, &deleted_version));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data1.registration_id, origin1,
-                CreateUserData(data1.registration_id, {{"key1", "value1"}})));
+            database->WriteUserData(data1.registration_id, origin1,
+                                    CreateUserData({{"key1", "data1"}})));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data1.registration_id, origin1,
-                CreateUserData(data1.registration_id, {{"key2", "value2"}})));
+            database->WriteUserData(data1.registration_id, origin1,
+                                    CreateUserData({{"key2", "data2"}})));
 
   RegistrationData data2;
   data2.registration_id = 11;
@@ -1988,13 +1945,11 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteRegistration(data2, resources2, &deleted_version));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data2.registration_id, origin1,
-                CreateUserData(data2.registration_id, {{"key3", "value3"}})));
+            database->WriteUserData(data2.registration_id, origin1,
+                                    CreateUserData({{"key3", "data3"}})));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data2.registration_id, origin1,
-                CreateUserData(data2.registration_id, {{"key4", "value4"}})));
+            database->WriteUserData(data2.registration_id, origin1,
+                                    CreateUserData({{"key4", "data4"}})));
 
   // |origin2| has one registration (registration3).
   RegistrationData data3;
@@ -2010,13 +1965,11 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteRegistration(data3, resources3, &deleted_version));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data3.registration_id, origin2,
-                CreateUserData(data3.registration_id, {{"key5", "value5"}})));
+            database->WriteUserData(data3.registration_id, origin2,
+                                    CreateUserData({{"key5", "data5"}})));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->WriteUserData(
-                data3.registration_id, origin2,
-                CreateUserData(data3.registration_id, {{"key6", "value6"}})));
+            database->WriteUserData(data3.registration_id, origin2,
+                                    CreateUserData({{"key6", "data6"}})));
 
   std::set<GURL> origins_to_delete;
   std::vector<int64_t> newly_purgeable_resources;
@@ -2086,12 +2039,12 @@
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data3.registration_id, {"key5"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  EXPECT_EQ("value5", user_data_out[0]);
+  EXPECT_EQ("data5", user_data_out[0]);
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadUserData(data3.registration_id, {"key6"}, &user_data_out));
   ASSERT_EQ(1u, user_data_out.size());
-  EXPECT_EQ("value6", user_data_out[0]);
+  EXPECT_EQ("data6", user_data_out[0]);
 }
 
 TEST(ServiceWorkerDatabaseTest, DestroyDatabase) {
diff --git a/content/browser/service_worker/service_worker_registry.cc b/content/browser/service_worker/service_worker_registry.cc
index cb2a54b..6908880 100644
--- a/content/browser/service_worker/service_worker_registry.cc
+++ b/content/browser/service_worker/service_worker_registry.cc
@@ -616,8 +616,8 @@
                              blink::ServiceWorkerStatusCode::kErrorFailed));
       return;
     }
-    user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id, kv.first, kv.second));
+    user_data.push_back(
+        storage::mojom::ServiceWorkerUserData::New(kv.first, kv.second));
   }
 
   GetRemoteStorageControl()->StoreUserData(
@@ -708,7 +708,7 @@
     return;
   }
 
-  GetRemoteStorageControl()->GetUserDataForAllRegistrations(
+  storage()->GetUserDataForAllRegistrations(
       key,
       base::BindOnce(&ServiceWorkerRegistry::DidGetUserDataForAllRegistrations,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
@@ -726,7 +726,7 @@
     return;
   }
 
-  GetRemoteStorageControl()->GetUserDataForAllRegistrationsByKeyPrefix(
+  storage()->GetUserDataForAllRegistrationsByKeyPrefix(
       key_prefix,
       base::BindOnce(&ServiceWorkerRegistry::DidGetUserDataForAllRegistrations,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
@@ -1287,18 +1287,11 @@
 
 void ServiceWorkerRegistry::DidGetUserDataForAllRegistrations(
     GetUserDataForAllRegistrationsCallback callback,
-    storage::mojom::ServiceWorkerDatabaseStatus status,
-    std::vector<storage::mojom::ServiceWorkerUserDataPtr> entries) {
+    const std::vector<std::pair<int64_t, std::string>>& user_data,
+    storage::mojom::ServiceWorkerDatabaseStatus status) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  // TODO(crbug.com/1055677): Update call sites of
-  // GetUserDataForAllRegistrations so that we can avoid converting mojo struct
-  // to a pair.
-  std::vector<std::pair<int64_t, std::string>> user_data;
   if (status != storage::mojom::ServiceWorkerDatabaseStatus::kOk)
     ScheduleDeleteAndStartOver();
-  for (auto& entry : entries) {
-    user_data.emplace_back(entry->registration_id, entry->value);
-  }
   std::move(callback).Run(user_data, DatabaseStatusToStatusCode(status));
 }
 
diff --git a/content/browser/service_worker/service_worker_registry.h b/content/browser/service_worker/service_worker_registry.h
index f6afb1e..2349bbdd 100644
--- a/content/browser/service_worker/service_worker_registry.h
+++ b/content/browser/service_worker/service_worker_registry.h
@@ -337,8 +337,8 @@
                         storage::mojom::ServiceWorkerDatabaseStatus status);
   void DidGetUserDataForAllRegistrations(
       GetUserDataForAllRegistrationsCallback callback,
-      storage::mojom::ServiceWorkerDatabaseStatus status,
-      std::vector<storage::mojom::ServiceWorkerUserDataPtr> entries);
+      const std::vector<std::pair<int64_t, std::string>>& user_data,
+      storage::mojom::ServiceWorkerDatabaseStatus status);
 
   void DidGetNewRegistrationId(
       blink::mojom::ServiceWorkerRegistrationOptions options,
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index c536cea..0fb0a953 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -770,10 +770,9 @@
   switch (state_) {
     case STORAGE_STATE_DISABLED:
       RunSoon(FROM_HERE,
-              base::BindOnce(
-                  std::move(callback),
-                  ServiceWorkerDatabase::Status::kErrorDisabled,
-                  std::vector<storage::mojom::ServiceWorkerUserDataPtr>()));
+              base::BindOnce(std::move(callback),
+                             std::vector<std::pair<int64_t, std::string>>(),
+                             ServiceWorkerDatabase::Status::kErrorDisabled));
       return;
     case STORAGE_STATE_INITIALIZING:  // Fall-through.
     case STORAGE_STATE_UNINITIALIZED:
@@ -802,10 +801,9 @@
   switch (state_) {
     case STORAGE_STATE_DISABLED:
       RunSoon(FROM_HERE,
-              base::BindOnce(
-                  std::move(callback),
-                  ServiceWorkerDatabase::Status::kErrorDisabled,
-                  std::vector<storage::mojom::ServiceWorkerUserDataPtr>()));
+              base::BindOnce(std::move(callback),
+                             std::vector<std::pair<int64_t, std::string>>(),
+                             ServiceWorkerDatabase::Status::kErrorDisabled));
       return;
     case STORAGE_STATE_INITIALIZING:  // Fall-through.
     case STORAGE_STATE_UNINITIALIZED:
@@ -1593,12 +1591,11 @@
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
     const std::string& key,
     GetUserDataForAllRegistrationsInDBCallback callback) {
-  std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data;
+  std::vector<std::pair<int64_t, std::string>> user_data;
   ServiceWorkerDatabase::Status status =
       database->ReadUserDataForAllRegistrations(key, &user_data);
   original_task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(callback), status, std::move(user_data)));
+      FROM_HERE, base::BindOnce(std::move(callback), user_data, status));
 }
 
 void ServiceWorkerStorage::GetUserDataForAllRegistrationsByKeyPrefixInDB(
@@ -1606,13 +1603,12 @@
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
     const std::string& key_prefix,
     GetUserDataForAllRegistrationsInDBCallback callback) {
-  std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data;
+  std::vector<std::pair<int64_t, std::string>> user_data;
   ServiceWorkerDatabase::Status status =
       database->ReadUserDataForAllRegistrationsByKeyPrefix(key_prefix,
                                                            &user_data);
   original_task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(callback), status, std::move(user_data)));
+      FROM_HERE, base::BindOnce(std::move(callback), user_data, status));
 }
 
 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index 4e0541c..532ad1e 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -107,8 +107,8 @@
   using GetUserKeysAndDataInDBCallback = storage::mojom::
       ServiceWorkerStorageControl::GetUserKeysAndDataByKeyPrefixCallback;
   using GetUserDataForAllRegistrationsInDBCallback = base::OnceCallback<void(
-      ServiceWorkerDatabase::Status,
-      std::vector<storage::mojom::ServiceWorkerUserDataPtr>)>;
+      const std::vector<std::pair<int64_t, std::string>>& user_data,
+      ServiceWorkerDatabase::Status)>;
 
   ~ServiceWorkerStorage();
 
diff --git a/content/browser/service_worker/service_worker_storage_control_impl.cc b/content/browser/service_worker/service_worker_storage_control_impl.cc
index 81c094d..8d68e304 100644
--- a/content/browser/service_worker/service_worker_storage_control_impl.cc
+++ b/content/browser/service_worker/service_worker_storage_control_impl.cc
@@ -13,6 +13,20 @@
 
 namespace {
 
+void DidGetUserDataForAllRegistrations(
+    ServiceWorkerStorageControlImpl::GetUserDataForAllRegistrationsCallback
+        callback,
+    const std::vector<std::pair<int64_t, std::string>>& user_data,
+    storage::mojom::ServiceWorkerDatabaseStatus status) {
+  // TODO(bashi): Change ServiceWorkerStorage::GetUserDataForAllRegistrations()
+  // to return base::flat_map.
+  base::flat_map<int64_t, std::string> values;
+  for (auto& entry : user_data) {
+    values[entry.first] = entry.second;
+  }
+  std::move(callback).Run(status, std::move(values));
+}
+
 void DidGetAllRegistrations(
     ServiceWorkerStorageControlImpl::GetAllRegistrationsDeprecatedCallback
         callback,
@@ -335,14 +349,17 @@
 void ServiceWorkerStorageControlImpl::GetUserDataForAllRegistrations(
     const std::string& key,
     GetUserDataForAllRegistrationsCallback callback) {
-  storage_->GetUserDataForAllRegistrations(key, std::move(callback));
+  storage_->GetUserDataForAllRegistrations(
+      key,
+      base::BindOnce(&DidGetUserDataForAllRegistrations, std::move(callback)));
 }
 
 void ServiceWorkerStorageControlImpl::GetUserDataForAllRegistrationsByKeyPrefix(
     const std::string& key_prefix,
     GetUserDataForAllRegistrationsByKeyPrefixCallback callback) {
-  storage_->GetUserDataForAllRegistrationsByKeyPrefix(key_prefix,
-                                                      std::move(callback));
+  storage_->GetUserDataForAllRegistrationsByKeyPrefix(
+      key_prefix,
+      base::BindOnce(&DidGetUserDataForAllRegistrations, std::move(callback)));
 }
 
 void ServiceWorkerStorageControlImpl::
diff --git a/content/browser/service_worker/service_worker_storage_control_impl_unittest.cc b/content/browser/service_worker/service_worker_storage_control_impl_unittest.cc
index ca11258..cb8fb095 100644
--- a/content/browser/service_worker/service_worker_storage_control_impl_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_control_impl_unittest.cc
@@ -84,7 +84,7 @@
 
 struct GetUserDataForAllRegistrationsResult {
   DatabaseStatus status;
-  std::vector<storage::mojom::ServiceWorkerUserDataPtr> values;
+  base::flat_map<int64_t, std::string> values;
 };
 
 ReadResponseHeadResult ReadResponseHead(
@@ -536,14 +536,13 @@
     GetUserDataForAllRegistrationsResult result;
     base::RunLoop loop;
     storage()->GetUserDataForAllRegistrations(
-        key,
-        base::BindLambdaForTesting(
-            [&](DatabaseStatus status,
-                std::vector<storage::mojom::ServiceWorkerUserDataPtr> values) {
-              result.status = status;
-              result.values = std::move(values);
-              loop.Quit();
-            }));
+        key, base::BindLambdaForTesting(
+                 [&](DatabaseStatus status,
+                     const base::flat_map<int64_t, std::string>& values) {
+                   result.status = status;
+                   result.values = values;
+                   loop.Quit();
+                 }));
     loop.Run();
     return result;
   }
@@ -556,9 +555,9 @@
         key_prefix,
         base::BindLambdaForTesting(
             [&](DatabaseStatus status,
-                std::vector<storage::mojom::ServiceWorkerUserDataPtr> values) {
+                const base::flat_map<int64_t, std::string>& values) {
               result.status = status;
-              result.values = std::move(values);
+              result.values = values;
               loop.Quit();
             }));
     loop.Run();
@@ -1163,10 +1162,10 @@
   // Store user data with two entries.
   {
     std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data;
-    user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id, "key1", "value1"));
-    user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id, "key2", "value2"));
+    user_data.push_back(
+        storage::mojom::ServiceWorkerUserData::New("key1", "value1"));
+    user_data.push_back(
+        storage::mojom::ServiceWorkerUserData::New("key2", "value2"));
 
     status = StoreUserData(registration_id, kScope.GetOrigin(),
                            std::move(user_data));
@@ -1253,14 +1252,14 @@
 
   // Store some user data with prefixes.
   std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data;
-  user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-      registration_id, "prefixA", "value1"));
-  user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-      registration_id, "prefixA2", "value2"));
-  user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-      registration_id, "prefixB", "value3"));
-  user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-      registration_id, "prefixC", "value4"));
+  user_data.push_back(
+      storage::mojom::ServiceWorkerUserData::New("prefixA", "value1"));
+  user_data.push_back(
+      storage::mojom::ServiceWorkerUserData::New("prefixA2", "value2"));
+  user_data.push_back(
+      storage::mojom::ServiceWorkerUserData::New("prefixB", "value3"));
+  user_data.push_back(
+      storage::mojom::ServiceWorkerUserData::New("prefixC", "value4"));
   status =
       StoreUserData(registration_id, kScope.GetOrigin(), std::move(user_data));
   ASSERT_EQ(status, DatabaseStatus::kOk);
@@ -1341,11 +1340,11 @@
   {
     std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data;
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id1, "key1", "registration1_value1"));
+        "key1", "registration1_value1"));
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id1, "key2", "registration1_value2"));
+        "key2", "registration1_value2"));
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id1, "prefix1", "registration1_prefix_value1"));
+        "prefix1", "registration1_prefix_value1"));
     status = StoreUserData(registration_id1, kScope1.GetOrigin(),
                            std::move(user_data));
     ASSERT_EQ(status, DatabaseStatus::kOk);
@@ -1353,11 +1352,11 @@
   {
     std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data;
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id1, "key1", "registration2_value1"));
+        "key1", "registration2_value1"));
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id1, "key3", "registration2_value3"));
+        "key3", "registration2_value3"));
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
-        registration_id1, "prefix2", "registration2_prefix_value2"));
+        "prefix2", "registration2_prefix_value2"));
     status = StoreUserData(registration_id2, kScope2.GetOrigin(),
                            std::move(user_data));
     ASSERT_EQ(status, DatabaseStatus::kOk);
@@ -1368,23 +1367,19 @@
   result = GetUserDataForAllRegistrations("key1");
   ASSERT_EQ(result.status, DatabaseStatus::kOk);
   ASSERT_EQ(result.values.size(), 2UL);
-  EXPECT_EQ(result.values[0]->registration_id, registration_id1);
-  EXPECT_EQ(result.values[0]->value, "registration1_value1");
-  EXPECT_EQ(result.values[1]->registration_id, registration_id2);
-  EXPECT_EQ(result.values[1]->value, "registration2_value1");
+  EXPECT_EQ(result.values[registration_id1], "registration1_value1");
+  EXPECT_EQ(result.values[registration_id2], "registration2_value1");
 
   // Get uncommon user data.
   result = GetUserDataForAllRegistrations("key2");
   ASSERT_EQ(result.status, DatabaseStatus::kOk);
   ASSERT_EQ(result.values.size(), 1UL);
-  EXPECT_EQ(result.values[0]->registration_id, registration_id1);
-  EXPECT_EQ(result.values[0]->value, "registration1_value2");
+  EXPECT_EQ(result.values[registration_id1], "registration1_value2");
 
   result = GetUserDataForAllRegistrations("key3");
   ASSERT_EQ(result.status, DatabaseStatus::kOk);
   ASSERT_EQ(result.values.size(), 1UL);
-  EXPECT_EQ(result.values[0]->registration_id, registration_id2);
-  EXPECT_EQ(result.values[0]->value, "registration2_value3");
+  EXPECT_EQ(result.values[registration_id2], "registration2_value3");
 
   // Getting unknown key succeeds but returns an empty value.
   // TODO(bashi): Make sure this is an intentional behavior. The existing
@@ -1400,17 +1395,14 @@
   result = GetUserDataForAllRegistrations("key1");
   ASSERT_EQ(result.status, DatabaseStatus::kOk);
   ASSERT_EQ(result.values.size(), 1UL);
-  EXPECT_EQ(result.values[0]->registration_id, registration_id2);
-  EXPECT_EQ(result.values[0]->value, "registration2_value1");
+  EXPECT_EQ(result.values[registration_id2], "registration2_value1");
 
   // Get prefixed user data.
   result = GetUserDataForAllRegistrationsByKeyPrefix("prefix");
   ASSERT_EQ(result.status, DatabaseStatus::kOk);
   ASSERT_EQ(result.values.size(), 2UL);
-  EXPECT_EQ(result.values[0]->registration_id, registration_id1);
-  EXPECT_EQ(result.values[0]->value, "registration1_prefix_value1");
-  EXPECT_EQ(result.values[1]->registration_id, registration_id2);
-  EXPECT_EQ(result.values[1]->value, "registration2_prefix_value2");
+  EXPECT_EQ(result.values[registration_id1], "registration1_prefix_value1");
+  EXPECT_EQ(result.values[registration_id2], "registration2_prefix_value2");
 
   // Clear prefixed user data.
   status = ClearUserDataForAllRegistrationsByKeyPrefix("prefix");
diff --git a/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java b/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java
index 7916f055..e09218b 100644
--- a/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java
@@ -10,6 +10,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.content.browser.remoteobjects.RemoteObjectInjector;
 import org.chromium.content.browser.webcontents.WebContentsImpl;
 import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory;
 import org.chromium.content_public.browser.JavascriptInjector;
@@ -34,20 +35,37 @@
     private final Set<Object> mRetainedObjects = new HashSet<>();
     private final Map<String, Pair<Object, Class>> mInjectedObjects = new HashMap<>();
     private long mNativePtr;
+    private RemoteObjectInjector mInjector;
+    private Boolean mUseMojo;
 
     /**
      * @param webContents {@link WebContents} object.
+     * @param useMojo Whether to use {@link RemoteObjectInjector} methods
      * @return {@link JavascriptInjector} object used for the give WebContents.
      *         Creates one if not present.
      */
-    public static JavascriptInjector fromWebContents(WebContents webContents) {
-        return ((WebContentsImpl) webContents)
-                .getOrSetUserData(JavascriptInjectorImpl.class, UserDataFactoryLazyHolder.INSTANCE);
+    public static JavascriptInjector fromWebContents(WebContents webContents, boolean useMojo) {
+        JavascriptInjectorImpl javascriptInjector =
+                ((WebContentsImpl) webContents)
+                        .getOrSetUserData(
+                                JavascriptInjectorImpl.class, UserDataFactoryLazyHolder.INSTANCE);
+        javascriptInjector.setUseMojo(useMojo);
+        return javascriptInjector;
     }
 
     public JavascriptInjectorImpl(WebContents webContents) {
         mNativePtr = JavascriptInjectorImplJni.get().init(
                 JavascriptInjectorImpl.this, webContents, mRetainedObjects);
+        mInjector = new RemoteObjectInjector(webContents);
+        webContents.addObserver(mInjector);
+    }
+
+    public void setUseMojo(boolean useMojo) {
+        if (mUseMojo == null) {
+            mUseMojo = useMojo;
+        } else {
+            assert mUseMojo == useMojo;
+        }
     }
 
     @CalledByNative
@@ -62,15 +80,21 @@
 
     @Override
     public void setAllowInspection(boolean allow) {
-        if (mNativePtr != 0)
+        if (mNativePtr != 0) {
             JavascriptInjectorImplJni.get().setAllowInspection(
                     mNativePtr, JavascriptInjectorImpl.this, allow);
+        }
     }
 
     @Override
     public void addPossiblyUnsafeInterface(
             Object object, String name, Class<? extends Annotation> requiredAnnotation) {
-        if (mNativePtr != 0 && object != null) {
+        if (object == null) return;
+
+        assert mUseMojo != null;
+        if (mUseMojo) {
+            mInjector.addInterface(object, name);
+        } else if (mNativePtr != 0) {
             mInjectedObjects.put(name, new Pair<Object, Class>(object, requiredAnnotation));
             JavascriptInjectorImplJni.get().addInterface(
                     mNativePtr, JavascriptInjectorImpl.this, object, name, requiredAnnotation);
@@ -80,9 +104,10 @@
     @Override
     public void removeInterface(String name) {
         mInjectedObjects.remove(name);
-        if (mNativePtr != 0)
+        if (mNativePtr != 0) {
             JavascriptInjectorImplJni.get().removeInterface(
                     mNativePtr, JavascriptInjectorImpl.this, name);
+        }
     }
 
     @NativeMethods
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/JavascriptInjector.java b/content/public/android/java/src/org/chromium/content_public/browser/JavascriptInjector.java
index 9d37f1ac..8f71faf7 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/JavascriptInjector.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/JavascriptInjector.java
@@ -22,7 +22,17 @@
      *         Creates one if not present.
      */
     static JavascriptInjector fromWebContents(WebContents webContents) {
-        return JavascriptInjectorImpl.fromWebContents(webContents);
+        return fromWebContents(webContents, false);
+    }
+
+    /**
+     * @param webContents {@link WebContents} object.
+     * @param useMojo Whether to use {@link RemoteObjectInjector} methods
+     * @return {@link JavascriptInjector} object used for the give WebContents.
+     *         Creates one if not present.
+     */
+    static JavascriptInjector fromWebContents(WebContents webContents, boolean useMojo) {
+        return JavascriptInjectorImpl.fromWebContents(webContents, useMojo);
     }
 
     /**
diff --git a/content/public/browser/native_file_system_permission_context.h b/content/public/browser/native_file_system_permission_context.h
index af40f0e..15653e24 100644
--- a/content/public/browser/native_file_system_permission_context.h
+++ b/content/public/browser/native_file_system_permission_context.h
@@ -24,8 +24,9 @@
   // use to automatically grant write access to the path.
   enum class UserAction {
     // The path for which a permission grant is requested was the result of a
-    // "open" dialog, and as such the grant should probably not start out as
-    // granted.
+    // "open" dialog. As such, only read access to files should be automatically
+    // granted, but read access to directories as well as write access to files
+    // or directories should not be granted without needing to request it.
     kOpen,
     // The path for which a permission grant is requested was the result of a
     // "save" dialog, and as such it could make sense to return a grant that
@@ -35,6 +36,10 @@
     // loading a handle from storage. As such the grant should not start out
     // as granted, even for read access.
     kLoadFromStorage,
+    // The path for which a permission grant is requested was the result of a
+    // drag&drop operation. Read access should start out granted, but write
+    // access will require a prompt.
+    kDragAndDrop,
   };
 
   // This enum helps distinguish between file or directory Native File System
@@ -87,6 +92,11 @@
       GlobalFrameRoutingId frame_id,
       base::OnceCallback<void(AfterWriteCheckResult)> callback) = 0;
 
+  // Returns whether the give |origin| already allows read permission, or it is
+  // possible to request one. This is used to block file dialogs from being
+  // shown if permission won't be granted anyway.
+  virtual bool CanObtainReadPermission(const url::Origin& origin) = 0;
+
   // Returns whether the give |origin| already allows write permission, or it is
   // possible to request one. This is used to block save file dialogs from being
   // shown if there is no need to ask for it.
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 5631ce9..3c009a7 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -341,6 +341,13 @@
   return portal_contents;
 }
 
+void WebContentsDelegate::UpdateInspectedWebContentsIfNecessary(
+    WebContents* old_contents,
+    WebContents* new_contents,
+    base::OnceCallback<void()> callback) {
+  std::move(callback).Run();
+}
+
 bool WebContentsDelegate::ShouldShowStaleContentOnEviction(
     WebContents* source) {
   return false;
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 61ed37a5..61f786b 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -712,6 +712,15 @@
       WebContents* predecessor_contents,
       std::unique_ptr<WebContents> portal_contents);
 
+  // If |old_contents| is being inspected by a DevTools window, it updates the
+  // window to inspect |new_contents| instead and calls |callback| after it
+  // finishes asynchronously. If no window is present, or no update is
+  // necessary, |callback| is run synchronously (immediately on the same stack).
+  virtual void UpdateInspectedWebContentsIfNecessary(
+      WebContents* old_contents,
+      WebContents* new_contents,
+      base::OnceCallback<void()> callback);
+
   // Returns true if the widget's frame content needs to be stored before
   // eviction and displayed until a new frame is generated. If false, a white
   // solid color is displayed instead.
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index b2ea0f0..88fa09bd 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -600,11 +600,6 @@
   DCHECK_EQ(predecessor_contents, web_contents_.get());
   portal_contents->SetDelegate(this);
   web_contents_->SetDelegate(nullptr);
-  for (auto* shell_devtools_bindings :
-       ShellDevToolsBindings::GetInstancesForWebContents(
-           predecessor_contents)) {
-    shell_devtools_bindings->UpdateInspectedWebContents(portal_contents.get());
-  }
   std::swap(web_contents_, portal_contents);
   g_platform->SetContents(this);
   g_platform->SetAddressBarURL(this, web_contents_->GetVisibleURL());
@@ -612,6 +607,34 @@
   return portal_contents;
 }
 
+namespace {
+class PendingCallback : public base::RefCounted<PendingCallback> {
+ public:
+  explicit PendingCallback(base::OnceCallback<void()> cb)
+      : callback_(std::move(cb)) {}
+
+ private:
+  friend class base::RefCounted<PendingCallback>;
+  ~PendingCallback() { std::move(callback_).Run(); }
+  base::OnceCallback<void()> callback_;
+};
+}  // namespace
+
+void Shell::UpdateInspectedWebContentsIfNecessary(
+    content::WebContents* old_contents,
+    content::WebContents* new_contents,
+    base::OnceCallback<void()> callback) {
+  scoped_refptr<PendingCallback> pending_callback =
+      base::MakeRefCounted<PendingCallback>(std::move(callback));
+  for (auto* shell_devtools_bindings :
+       ShellDevToolsBindings::GetInstancesForWebContents(old_contents)) {
+    shell_devtools_bindings->UpdateInspectedWebContents(
+        new_contents,
+        base::BindOnce(base::DoNothing::Once<scoped_refptr<PendingCallback>>(),
+                       pending_callback));
+  }
+}
+
 bool Shell::ShouldAllowRunningInsecureContent(WebContents* web_contents,
                                               bool allowed_per_prefs,
                                               const url::Origin& origin,
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h
index 1c0ce7d5..871f642f 100644
--- a/content/shell/browser/shell.h
+++ b/content/shell/browser/shell.h
@@ -161,9 +161,14 @@
       RenderWidgetHost* render_widget_host,
       base::RepeatingClosure hang_monitor_restarter) override;
   void ActivateContents(WebContents* contents) override;
+
   std::unique_ptr<content::WebContents> ActivatePortalWebContents(
       content::WebContents* predecessor_contents,
       std::unique_ptr<content::WebContents> portal_contents) override;
+  void UpdateInspectedWebContentsIfNecessary(
+      content::WebContents* old_contents,
+      content::WebContents* new_contents,
+      base::OnceCallback<void()> callback) override;
   bool ShouldAllowRunningInsecureContent(content::WebContents* web_contents,
                                          bool allowed_per_prefs,
                                          const url::Origin& origin,
diff --git a/content/shell/browser/shell_devtools_bindings.cc b/content/shell/browser/shell_devtools_bindings.cc
index e52411eb..d939f35 100644
--- a/content/shell/browser/shell_devtools_bindings.cc
+++ b/content/shell/browser/shell_devtools_bindings.cc
@@ -234,13 +234,17 @@
 }
 
 void ShellDevToolsBindings::UpdateInspectedWebContents(
-    WebContents* new_contents) {
+    WebContents* new_contents,
+    base::OnceCallback<void()> callback) {
   inspected_contents_ = new_contents;
   if (!agent_host_)
     return;
   AttachInternal();
-  CallClientFunction("DevToolsAPI.reattachMainTarget", nullptr, nullptr,
-                     nullptr);
+  CallClientFunction(
+      "DevToolsAPI.reattachMainTarget", nullptr, nullptr, nullptr,
+      base::BindOnce([](base::OnceCallback<void()> callback,
+                        base::Value) { std::move(callback).Run(); },
+                     std::move(callback)));
 }
 
 void ShellDevToolsBindings::WebContentsDestroyed() {
@@ -405,10 +409,12 @@
   }
 }
 
-void ShellDevToolsBindings::CallClientFunction(const std::string& function_name,
-                                               const base::Value* arg1,
-                                               const base::Value* arg2,
-                                               const base::Value* arg3) {
+void ShellDevToolsBindings::CallClientFunction(
+    const std::string& function_name,
+    const base::Value* arg1,
+    const base::Value* arg2,
+    const base::Value* arg3,
+    base::OnceCallback<void(base::Value)> cb) {
   std::string javascript = function_name + "(";
   if (arg1) {
     std::string json;
@@ -425,7 +431,7 @@
   }
   javascript.append(");");
   web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::UTF8ToUTF16(javascript), base::NullCallback());
+      base::UTF8ToUTF16(javascript), std::move(cb));
 }
 
 void ShellDevToolsBindings::SendMessageAck(int request_id,
diff --git a/content/shell/browser/shell_devtools_bindings.h b/content/shell/browser/shell_devtools_bindings.h
index 09c3b6f..6d9c5394 100644
--- a/content/shell/browser/shell_devtools_bindings.h
+++ b/content/shell/browser/shell_devtools_bindings.h
@@ -51,12 +51,15 @@
 
   void InspectElementAt(int x, int y);
   virtual void Attach();
-  void UpdateInspectedWebContents(WebContents* new_contents);
+  void UpdateInspectedWebContents(WebContents* new_contents,
+                                  base::OnceCallback<void()> callback);
 
-  void CallClientFunction(const std::string& function_name,
-                          const base::Value* arg1,
-                          const base::Value* arg2,
-                          const base::Value* arg3);
+  void CallClientFunction(
+      const std::string& function_name,
+      const base::Value* arg1,
+      const base::Value* arg2,
+      const base::Value* arg3,
+      base::OnceCallback<void(base::Value)> cb = base::NullCallback());
   ~ShellDevToolsBindings() override;
 
   WebContents* inspected_contents() { return inspected_contents_; }
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index dc5b3c1..f5791db 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -11620,6 +11620,34 @@
       }
     }
     builders {
+      name: "win-omaha-builder-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builder:win-omaha-builder-rel"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows-10"
+      dimensions: "pool:luci.chromium.ci"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+      }
+    }
+    builders {
       name: "win-pixel-builder-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -11936,6 +11964,34 @@
       }
     }
     builders {
+      name: "win10-omaha-tester-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builder:win10-omaha-tester-rel"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows-10"
+      dimensions: "pool:luci.chromium.ci"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"mastername\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+      }
+    }
+    builders {
       name: "win32-archive-dbg"
       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 24b6ae9..911222b81 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -8295,6 +8295,10 @@
     category: "win10"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/win-omaha-builder-rel"
+    category: "win10"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/win-pixel-builder-rel"
     category: "win10"
   }
@@ -8303,6 +8307,10 @@
     category: "win10"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/win10-omaha-tester-rel"
+    category: "win10"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Win10 Tests x64 1803"
     category: "win10|1803"
   }
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index 59e3dc78..64b4b5ba 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -11224,6 +11224,16 @@
   }
 }
 job {
+  id: "ci-win-omaha-builder-rel"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "win-omaha-builder-rel"
+  }
+}
+job {
   id: "ci-win-pixel-builder-rel"
   realm: "ci"
   acl_sets: "ci"
@@ -11328,6 +11338,20 @@
   }
 }
 job {
+  id: "ci-win10-omaha-tester-rel"
+  realm: "ci"
+  acls {
+    role: TRIGGERER
+    granted_to: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+  }
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "win10-omaha-tester-rel"
+  }
+}
+job {
   id: "ci-win32-archive-dbg"
   realm: "ci"
   acl_sets: "ci"
@@ -12325,6 +12349,16 @@
   }
 }
 job {
+  id: "win-omaha-builder-rel"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "win-omaha-builder-rel"
+  }
+}
+job {
   id: "win-pixel-builder-rel"
   realm: "ci"
   acl_sets: "ci"
@@ -12440,6 +12474,20 @@
   }
 }
 job {
+  id: "win10-omaha-tester-rel"
+  realm: "ci"
+  acls {
+    role: TRIGGERER
+    granted_to: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+  }
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "win10-omaha-tester-rel"
+  }
+}
+job {
   id: "win32-archive-dbg"
   realm: "ci"
   acl_sets: "ci"
@@ -13020,6 +13068,7 @@
   triggers: "win-archive-rel"
   triggers: "win-asan"
   triggers: "win-official"
+  triggers: "win-omaha-builder-rel"
   triggers: "win-pixel-builder-rel"
   triggers: "win-swangle-chromium-x86"
   triggers: "win-swangle-tot-angle-x64"
diff --git a/infra/config/subprojects/chromium/master-only/ci.star b/infra/config/subprojects/chromium/master-only/ci.star
index 2ff67c92..ea2728d 100644
--- a/infra/config/subprojects/chromium/master-only/ci.star
+++ b/infra/config/subprojects/chromium/master-only/ci.star
@@ -1621,6 +1621,24 @@
 )
 
 ci.fyi_builder(
+    name = "win-omaha-builder-rel",
+    console_view_entry = ci.console_view_entry(
+        category = "win10",
+    ),
+    os = os.WINDOWS_DEFAULT,
+    cpu = cpu.X86_64,
+)
+
+ci.fyi_builder(
+    name = "win10-omaha-tester-rel",
+    console_view_entry = ci.console_view_entry(
+        category = "win10",
+    ),
+    os = os.WINDOWS_10,
+    triggered_by = ["win-omaha-builder-rel"],
+)
+
+ci.fyi_builder(
     name = "win-pixel-builder-rel",
     console_view_entry = ci.console_view_entry(
         category = "win10",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 7924e9dc..c17988f 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -708,6 +708,9 @@
                                     web::BuildMobileUserAgent(product));
   }
 
+  // Shared policy dictionary for all enterprise experimental flags.
+  NSMutableDictionary* testing_policies = [[NSMutableDictionary alloc] init];
+
   // Set some sample policy values for testing if EnableSamplePolicies is
   // enabled.
   if ([defaults boolForKey:@"EnableSamplePolicies"]) {
@@ -726,7 +729,7 @@
     ];
 
     // Define sample policies to enable.
-    NSDictionary* testing_policies = @{
+    [testing_policies addEntriesFromDictionary:@{
       base::SysUTF8ToNSString(policy::key::kEnableExperimentalPolicies) :
           experimental_policies,
 
@@ -751,7 +754,22 @@
       base::SysUTF8ToNSString(policy::key::kPasswordManagerEnabled) : @NO,
 
       base::SysUTF8ToNSString(policy::key::kTranslateEnabled) : @NO,
-    };
+    }];
+  }
+
+  // If a CBCM enrollment token is provided, force Chrome Browser Cloud
+  // Management to enabled and add the token to the list of policies.
+  NSString* token_key =
+      base::SysUTF8ToNSString(policy::key::kCloudManagementEnrollmentToken);
+  NSString* token = [defaults stringForKey:token_key];
+
+  if ([token length] > 0) {
+    command_line->AppendSwitch(switches::kEnableChromeBrowserCloudManagement);
+    [testing_policies setValue:token forKey:token_key];
+  }
+
+  // If some policies were set, commit them to the app's registration defaults.
+  if ([testing_policies count] > 0) {
     NSDictionary* registration_defaults =
         @{kPolicyLoaderIOSConfigurationKey : testing_policies};
     [defaults registerDefaults:registration_defaults];
diff --git a/ios/chrome/browser/metrics/ukm_egtest.mm b/ios/chrome/browser/metrics/ukm_egtest.mm
index b8045ba4..3162de7 100644
--- a/ios/chrome/browser/metrics/ukm_egtest.mm
+++ b/ios/chrome/browser/metrics/ukm_egtest.mm
@@ -6,7 +6,6 @@
 #include "base/macros.h"
 #import "ios/chrome/browser/metrics/metrics_app_interface.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
-#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -93,9 +92,7 @@
   // Note: URL-keyed anonymized data collection is turned off as part of the
   // flow to Sign out of Chrome and Turn sync off. This matches the main user
   // flow that disables UKM.
-  if (![SigninEarlGreyAppInterface isSignedOut]) {
-    [SigninEarlGreyAppInterface signOut];
-  }
+  [SigninEarlGrey signOut];
 
   [ChromeEarlGrey waitForSyncInitialized:NO
                              syncTimeout:syncher::kSyncUKMOperationsTimeout];
@@ -274,7 +271,7 @@
 // Corresponds to ConsentAddedButNoSyncCheck in //chrome/browser/metrics/
 // ukm_browsertest.cc.
 - (void)testConsentAddedButNoSync {
-  [SigninEarlGreyAppInterface signOut];
+  [SigninEarlGrey signOut];
   [MetricsAppInterface setMetricsAndCrashReportingForTesting:NO];
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:NO],
              @"Failed to assert that UKM was not enabled.");
@@ -347,7 +344,7 @@
 #endif
   const uint64_t clientID1 = [MetricsAppInterface UKMClientID];
 
-  [SigninEarlGreyAppInterface signOut];
+  [SigninEarlGrey signOut];
 
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:NO],
              @"Failed to assert that UKM was not enabled.");
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/save_password_infobar_banner_overlay.mm b/ios/chrome/browser/overlays/public/infobar_banner/save_password_infobar_banner_overlay.mm
index 2ffe3ec..c746a681 100644
--- a/ios/chrome/browser/overlays/public/infobar_banner/save_password_infobar_banner_overlay.mm
+++ b/ios/chrome/browser/overlays/public/infobar_banner/save_password_infobar_banner_overlay.mm
@@ -19,7 +19,7 @@
 
 namespace {
 // The name of the icon image for the save passwords banner.
-NSString* const kIconImageName = @"infobar_passwords_icon";
+NSString* const kIconImageName = @"password_key";
 }
 
 OVERLAY_USER_DATA_SETUP_IMPL(SavePasswordInfobarBannerOverlayRequestConfig);
diff --git a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
index eb674c8..eb67d4e5 100644
--- a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
+++ b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
@@ -132,6 +132,12 @@
 		</dict>
 		<dict>
 			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+			<key>Title</key>
+			<string>Enterprise Settings</string>
+		</dict>
+		<dict>
+			<key>Type</key>
 			<string>PSToggleSwitchSpecifier</string>
 			<key>Title</key>
 			<string>Enable Sample Policies</string>
@@ -142,6 +148,18 @@
 		</dict>
 		<dict>
 			<key>Type</key>
+			<string>PSTextFieldSpecifier</string>
+			<key>Title</key>
+			<string>CBCM Enrollment Token</string>
+			<key>Key</key>
+			<string>CloudManagementEnrollmentToken</string>
+			<key>DefaultValue</key>
+			<string></string>
+			<key>AutocorrectionType</key>
+			<string>No</string>
+		</dict>
+		<dict>
+			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 			<key>Title</key>
 			<string>Google App Ecosystem</string>
diff --git a/ios/chrome/browser/translate/BUILD.gn b/ios/chrome/browser/translate/BUILD.gn
index 003e6fb..3ddba5a 100644
--- a/ios/chrome/browser/translate/BUILD.gn
+++ b/ios/chrome/browser/translate/BUILD.gn
@@ -108,38 +108,6 @@
   ]
 }
 
-source_set("eg2_tests") {
-  defines = [ "CHROME_EARL_GREY_2" ]
-  configs += [
-    "//build/config/compiler:enable_arc",
-    "//build/config/ios:xctest_config",
-  ]
-  testonly = true
-  sources = [ "translate_egtest.mm" ]
-  deps = [
-    ":eg_test_support+eg2",
-    "//base/test:test_support",
-    "//components/strings:components_strings_grit",
-    "//components/translate/core/browser:browser",
-    "//components/translate/core/common:common",
-    "//ios/chrome/app/strings:ios_strings_grit",
-    "//ios/chrome/browser:chrome_url_constants",
-    "//ios/chrome/browser/ui/infobars:feature_flags",
-    "//ios/chrome/browser/ui/popup_menu:constants",
-    "//ios/chrome/browser/ui/translate:legacy_translate_constants",
-    "//ios/chrome/browser/ui/translate:translate_ui_constants",
-    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
-    "//ios/components/webui:url_constants",
-    "//ios/testing/earl_grey:eg_test_support+eg2",
-    "//ios/third_party/earl_grey2:test_lib",
-    "//ios/web/public:public",
-    "//ios/web/public/test/http_server:http_server",
-    "//ui/base:base",
-    "//url:url",
-  ]
-  frameworks = [ "UIKit.framework" ]
-}
-
 source_set("test_support") {
   testonly = true
   sources = [
diff --git a/ios/chrome/browser/translate/translate_egtest.mm b/ios/chrome/browser/translate/translate_egtest.mm
deleted file mode 100644
index 67dbff28..0000000
--- a/ios/chrome/browser/translate/translate_egtest.mm
+++ /dev/null
@@ -1,1750 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import <XCTest/XCTest.h>
-
-#include <memory>
-#include <string>
-
-#include "base/strings/stringprintf.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#import "base/test/ios/wait_util.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/translate/core/browser/translate_pref_names.h"
-#include "components/translate/core/common/translate_constants.h"
-#include "ios/chrome/browser/chrome_url_constants.h"
-#import "ios/chrome/browser/translate/translate_app_interface.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
-#import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
-#import "ios/chrome/browser/ui/translate/legacy_translate_infobar_constants.h"
-#import "ios/chrome/browser/ui/translate/translate_infobar_view_constants.h"
-#include "ios/chrome/grit/ios_strings.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_matchers.h"
-#import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#include "ios/components/webui/web_ui_url_constants.h"
-#import "ios/testing/earl_grey/earl_grey_test.h"
-#include "ios/web/public/test/http_server/data_response_provider.h"
-#import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
-#include "net/base/url_util.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-#include "url/gurl.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using base::test::ios::WaitUntilConditionOrTimeout;
-using base::test::ios::kWaitForJSCompletionTimeout;
-using base::test::ios::kWaitForUIElementTimeout;
-using chrome_test_util::ButtonWithAccessibilityLabel;
-using chrome_test_util::ButtonWithAccessibilityLabelId;
-using chrome_test_util::CloseButton;
-using chrome_test_util::ToolsMenuView;
-using chrome_test_util::WebStateScrollViewMatcher;
-
-namespace {
-
-// Paragraph height height for test pages. This must be large enough to trigger
-// the fullscreen mode.
-const int kParagraphHeightEM = 200;
-
-// Some text in English language.
-const char kEnglishText[] =
-    "After flying to an altitude of 39,045 meters (128,100 feet) in a "
-    "helium-filled balloon, Felix Baumgartner completed a record breaking jump "
-    "for the ages from the edge of space, exactly 65 years after Chuck Yeager "
-    "first broke the sound barrier flying in an experimental rocket-powered "
-    "airplane. Felix reached a maximum of speed of 1,342.8 km/h (833mph) "
-    "through the near vacuum of the stratosphere before being slowed by the "
-    "atmosphere later during his 4:20 minute long freefall. The 43-year-old "
-    "Austrian skydiving expert also broke two other world records (highest "
-    "freefall, highest manned balloon flight), leaving the one for the longest "
-    "freefall to project mentor Col. Joe Kittinger.";
-
-// Some text in French language.
-const char kFrenchText[] =
-    "Des yeux qui font baisser les miens. Un rire qui se perd sur sa bouche."
-    "Voilà le portrait sans retouches de l'homme auquel j'appartiens "
-    "Quand il me prend dans ses bras Il me parle tout bas "
-    "Je vois la vie en rose Il me dit des mots d'amour "
-    "Des mots de tous les jours Et ça me fait quelque chose "
-    "Il est entré dans mon cœur Une part de bonheur Dont je connais la cause "
-    "C'est lui pour moi, moi pour lui, dans la vie "
-    "Il me l'a dit, l'a juré, pour la vie Et dès que je l'aperçois "
-    "Alors je sens en moi, Mon cœur qui bat Des nuits d'amour à plus finir "
-    "Un grand bonheur qui prend sa place Les ennuis, les chagrins s'effacent "
-    "Heureux, heureux à en mourir Quand il me prend dans ses bras "
-    "Il me parle tout bas Je vois la vie en rose Il me dit des mots d'amour "
-    "Des mots de tous les jours Et ça me fait quelque chose "
-    "Il est entré dans mon cœur Une part de bonheur Dont je connais la cause "
-    "C'est toi pour moi, moi pour toi, dans la vie "
-    "Tu me l'as dit, l'as juré, pour la vie Et dès que je t'aperçois "
-    "Alors je sens en moi Mon cœur qui bat";
-
-// Various HTML tags.
-const char kHtmlAttribute[] = "<html>";
-const char kHtmlAttributeWithDeLang[] = "<html lang=\"de\">";
-const char kMetaNotranslateContent[] =
-    "<meta name=\"google\" content=\"notranslate\">";
-const char kMetaNotranslateValue[] =
-    "<meta name=\"google\" value=\"notranslate\">";
-const char kMetaItContentLanguage[] =
-    "<meta http-equiv=\"content-language\" content=\"it\">";
-
-// Various link components.
-// TODO(crbug.com/729195): Re-write the hardcoded address.
-const char kHttpServerDomain[] = "127.0.0.1";
-const char kLanguagePath[] = "/languagepath/";
-const char kLinkPath[] = "/linkpath/";
-const char kSubresourcePath[] = "/subresourcepath/";
-const char kSomeLanguageUrl[] = "http://languagepath/?http=es";
-const char kFrenchPagePath[] = "/frenchpage/";
-const char kFrenchPageWithLinkPath[] = "/frenchpagewithlink/";
-const char kFrenchPageNoTranslateContent[] = "/frenchpagenotranslatecontent/";
-const char kFrenchPageNoTranslateValue[] = "/frenchpagenotranslatevalue/";
-const char kTranslateScriptPath[] = "/translatescript/";
-const char kTranslateScript[] = "Fake Translate Script";
-
-// Body text for /languagepath/.
-const char kLanguagePathText[] = "Some text here.";
-
-// Builds a HTML document with a French text and the given |html| and |meta|
-// tags.
-std::string GetFrenchPageHtml(const std::string& html_tag,
-                              const std::string& meta_tags) {
-  return html_tag + meta_tags + "<body>" +
-         base::StringPrintf("<p style='height:%dem'>%s</p>", kParagraphHeightEM,
-                            kFrenchText) +
-         "</body></html>";
-}
-
-// Returns a matcher for the translate infobar view.
-id<GREYMatcher> TranslateInfobar() {
-  return grey_accessibilityID(kTranslateInfobarViewId);
-}
-
-// Returns a matcher for the translate infobar's options button.
-id<GREYMatcher> OptionsButton() {
-  return ButtonWithAccessibilityLabelId(
-      IDS_IOS_TRANSLATE_INFOBAR_OPTIONS_ACCESSIBILITY_LABEL);
-}
-
-// Returns a matcher for the translate options menu.
-id<GREYMatcher> OptionsMenu() {
-  return grey_accessibilityID(kTranslateOptionsPopupMenuId);
-}
-
-// Returns a matcher for the language selection menu.
-id<GREYMatcher> LanguagesMenu() {
-  return grey_accessibilityID(kLanguageSelectorPopupMenuId);
-}
-
-// Returns a matcher for the "More Languages" entry in translate options menu.
-id<GREYMatcher> MoreLanguages() {
-  return ButtonWithAccessibilityLabelId(
-      IDS_TRANSLATE_INFOBAR_OPTIONS_MORE_LANGUAGE);
-}
-
-// Returns a matcher for the "Always translate ..." entry in translate options
-// menu.
-id<GREYMatcher> AlwaysTranslate(NSString* language) {
-  return ButtonWithAccessibilityLabel(
-      l10n_util::GetNSStringF(IDS_TRANSLATE_INFOBAR_OPTIONS_ALWAYS,
-                              base::SysNSStringToUTF16(language)));
-}
-
-// Returns a matcher for the "Never translate ..." entry in translate options
-// menu.
-id<GREYMatcher> NeverTranslate(NSString* language) {
-  return ButtonWithAccessibilityLabel(l10n_util::GetNSStringF(
-      IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_LANG,
-      base::SysNSStringToUTF16(language)));
-}
-
-// Returns a matcher for the "Never translate this site" entry in translate
-// options menu.
-id<GREYMatcher> NeverTranslateSite() {
-  return ButtonWithAccessibilityLabel(l10n_util::GetNSString(
-      IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_SITE));
-}
-
-// Returns a matcher for the "Page not in ..." entry in translate options menu.
-id<GREYMatcher> PageNotIn(NSString* language) {
-  return ButtonWithAccessibilityLabel(
-      l10n_util::GetNSStringF(IDS_TRANSLATE_INFOBAR_OPTIONS_NOT_SOURCE_LANGUAGE,
-                              base::SysNSStringToUTF16(language)));
-}
-
-// Returns a matcher for the notification snackbar's "UNDO" button.
-id<GREYMatcher> UndoButton() {
-  return ButtonWithAccessibilityLabelId(IDS_TRANSLATE_NOTIFICATION_UNDO);
-}
-
-// Returns a matcher for the Translate manual trigger button in the tools menu.
-id<GREYMatcher> toolsMenuTranslateButton() {
-  return grey_allOf(grey_accessibilityID(kToolsMenuTranslateId),
-                    grey_interactable(), nil);
-}
-
-// Returns a matcher for an element with or without the
-// UIAccessibilityTraitSelected accessibility trait depending on |selected|.
-id<GREYMatcher> ElementIsSelected(BOOL selected) {
-  return grey_allOf(
-      grey_sufficientlyVisible(),
-      selected
-          ? grey_accessibilityTrait(UIAccessibilityTraitSelected)
-          : grey_not(grey_accessibilityTrait(UIAccessibilityTraitSelected)),
-      nil);
-}
-
-#pragma mark - TestResponseProvider
-
-// A ResponseProvider that provides html responses of texts in different
-// languages or links.
-class TestResponseProvider : public web::DataResponseProvider {
- public:
-  // TestResponseProvider implementation.
-  bool CanHandleRequest(const Request& request) override;
-  void GetResponseHeadersAndBody(
-      const Request& request,
-      scoped_refptr<net::HttpResponseHeaders>* headers,
-      std::string* response_body) override;
-
- private:
-  // Generates a page with a HTTP "Content-Language" header and "httpEquiv" meta
-  // tag.
-  // The URL in |request| has two parameters, "http" and "meta", that can be
-  // used to set the values of the header and the meta tag. For example:
-  // http://someurl?http=en&meta=fr generates a page with a "en" HTTP header and
-  // a "fr" meta tag.
-  void GetLanguageResponse(const Request& request,
-                           scoped_refptr<net::HttpResponseHeaders>* headers,
-                           std::string* response_body);
-};
-
-bool TestResponseProvider::CanHandleRequest(const Request& request) {
-  const GURL& url = request.url;
-  return (url.host() == kHttpServerDomain &&
-          (url.path() == kLanguagePath || url.path() == kLinkPath ||
-           url.path() == kSubresourcePath || url.path() == kFrenchPagePath ||
-           url.path() == kFrenchPageWithLinkPath ||
-           url.path() == kFrenchPageNoTranslateContent ||
-           url.path() == kFrenchPageNoTranslateValue ||
-           url.path() == kTranslateScriptPath)) ||
-         url.SchemeIs(kChromeUIScheme);
-}
-
-void TestResponseProvider::GetResponseHeadersAndBody(
-    const Request& request,
-    scoped_refptr<net::HttpResponseHeaders>* headers,
-    std::string* response_body) {
-  const GURL& url = request.url;
-  *headers = web::ResponseProvider::GetDefaultResponseHeaders();
-  if (url.SchemeIs(kChromeUIScheme)) {
-    *response_body = url.spec();
-    return;
-  } else if (url.path() == kLanguagePath) {
-    // HTTP header and meta tag read from parameters.
-    return GetLanguageResponse(request, headers, response_body);
-  } else if (url.path() == kSubresourcePath) {
-    // Different "Content-Language" headers in the main page and subresource.
-    (*headers)->AddHeader("Content-Language", "fr");
-    *response_body = base::StringPrintf(
-        "<html><body><img src=%s></body></html>", kSomeLanguageUrl);
-    return;
-  } else if (url.path() == kLinkPath) {
-    // Link to a page with "Content Language" headers.
-    GURL url = web::test::HttpServer::MakeUrl(kSomeLanguageUrl);
-    *response_body = base::StringPrintf(
-        "<html><body><a href='%s' id='click'>Click</a></body></html>",
-        url.spec().c_str());
-    return;
-  } else if (url.path() == kFrenchPagePath) {
-    *response_body = GetFrenchPageHtml(kHtmlAttribute, "");
-    return;
-  } else if (url.path() == kFrenchPageWithLinkPath) {
-    GURL page_path_url = web::test::HttpServer::MakeUrl(
-        base::StringPrintf("http://%s", kFrenchPagePath));
-    *response_body = base::StringPrintf(
-        "<html><body>%s<br/><a href='%s' id='link'>link</a></body></html>",
-        kFrenchText, page_path_url.spec().c_str());
-    return;
-  } else if (url.path() == kFrenchPageNoTranslateContent) {
-    GURL page_path_url = web::test::HttpServer::MakeUrl(
-        base::StringPrintf("http://%s", kFrenchPagePath));
-    // A page with French text and a 'content' attribute with "notranslate".
-    *response_body = GetFrenchPageHtml(kHtmlAttribute, kMetaNotranslateContent);
-    return;
-  } else if (url.path() == kFrenchPageNoTranslateValue) {
-    GURL page_path_url = web::test::HttpServer::MakeUrl(
-        base::StringPrintf("http://%s", kFrenchPagePath));
-    // A page with French text and a 'value' attribute with "notranslate".
-    *response_body = GetFrenchPageHtml(kHtmlAttribute, kMetaNotranslateValue);
-    return;
-  } else if (url.path() == kTranslateScriptPath) {
-    *response_body = kTranslateScript;
-    return;
-  }
-  NOTREACHED();
-}
-
-void TestResponseProvider::GetLanguageResponse(
-    const Request& request,
-    scoped_refptr<net::HttpResponseHeaders>* headers,
-    std::string* response_body) {
-  const GURL& url = request.url;
-  // HTTP headers.
-  std::string http;
-  net::GetValueForKeyInQuery(url, "http", &http);
-  if (!http.empty())
-    (*headers)->AddHeader("Content-Language", http);
-  // Response body.
-  std::string meta;
-  net::GetValueForKeyInQuery(url, "meta", &meta);
-  *response_body = "<html>";
-  if (!meta.empty()) {
-    *response_body +=
-        "<head>"
-        "<meta http-equiv='content-language' content='" +
-        meta +
-        "'>"
-        "</head>";
-  }
-  *response_body +=
-      base::StringPrintf("<html><body>%s</body></html>", kLanguagePathText);
-}
-
-}  // namespace
-
-#pragma mark - TranslateTestCase
-
-// Tests for translate.
-@interface TranslateTestCase : ChromeTestCase
-@end
-
-@implementation TranslateTestCase
-
-- (void)setUp {
-  [super setUp];
-
-  // Set up the fake URL for the translate script to hit the mock HTTP server.
-  GURL translateScriptURL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kTranslateScriptPath));
-  NSString* translateScriptSwitchValue =
-      base::SysUTF8ToNSString(translateScriptURL.spec());
-  [TranslateAppInterface setUpWithScriptServer:translateScriptSwitchValue];
-}
-
-- (void)tearDown {
-  [TranslateAppInterface tearDown];
-  [super tearDown];
-}
-
-#pragma mark - Test Cases
-
-// Tests that different language signals are detected correcty.
-- (void)testLanguageDetection {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  const GURL URL =
-      web::test::HttpServer::MakeUrl("http://scenarioLanguageDetection");
-  std::map<GURL, std::string> responses;
-  // A page with French text, German "lang" attribute and Italian content
-  // language.
-  responses[URL] =
-      GetFrenchPageHtml(kHtmlAttributeWithDeLang, kMetaItContentLanguage);
-  web::test::SetUpSimpleHttpServer(responses);
-
-  [ChromeEarlGrey loadURL:URL];
-  [self assertContentLanguage:@"it"
-             htmlRootLanguage:@"de"
-              adoptedLanguage:base::SysUTF8ToNSString(
-                                  translate::kUnknownLanguageCode)];
-}
-
-// Tests that hidden text is not considered during detection.
-- (void)testLanguageDetectionIgnoreHiddenText {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  const GURL URL = web::test::HttpServer::MakeUrl(
-      "http://scenarioLanguageDetectionIgnoreHiddenText");
-  std::map<GURL, std::string> responses;
-  // A page with French text that's hidden via CSS.
-  responses[URL] = base::StringPrintf(
-      "<html><body style='display:none'>%s</body></html>", kFrenchText);
-  web::test::SetUpSimpleHttpServer(responses);
-
-  [ChromeEarlGrey loadURL:URL];
-  // Check for no language detected.
-  [self assertContentLanguage:@""
-             htmlRootLanguage:@""
-              adoptedLanguage:base::SysUTF8ToNSString(
-                                  translate::kUnknownLanguageCode)];
-}
-
-// Tests that language detection is not performed when the page specifies that
-// it should not be translated.
-- (void)testLanguageDetectionNoTranslate {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  const GURL noTranslateContentURL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPageNoTranslateContent));
-  const GURL noTranslateValueURL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPageNoTranslateValue));
-
-  // Load some french page with |content="notranslate"| meta tag.
-  [ChromeEarlGrey loadURL:noTranslateContentURL];
-
-  // Check that no language has been detected.
-  GREYAssertFalse([self waitForLanguageDetection],
-                  @"A language has been detected");
-
-  // Load some french page with |value="notranslate"| meta tag.
-  [ChromeEarlGrey loadURL:noTranslateValueURL];
-
-  // Check that no language has been detected.
-  GREYAssertFalse([self waitForLanguageDetection],
-                  @"A language has been detected");
-}
-
-// Tests that history.pushState triggers a new detection.
-- (void)testLanguageDetectionWithPushState {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  const GURL URL = web::test::HttpServer::MakeUrl(
-      "http://scenarioLanguageDetectionPushState");
-  std::map<GURL, std::string> responses;
-  // Page without meaningful text, language should be undefined ("und").
-  responses[URL] = "<html><body>Blahrg :)</body></html>";
-  web::test::SetUpSimpleHttpServer(responses);
-
-  [ChromeEarlGrey loadURL:URL];
-  // Check for no language detected.
-  [self assertContentLanguage:@""
-             htmlRootLanguage:@""
-              adoptedLanguage:base::SysUTF8ToNSString(
-                                  translate::kUnknownLanguageCode)];
-
-  // Resets state before triggering a new round of language detection.
-  [TranslateAppInterface resetLanguageDetectionTabHelperObserver];
-  // Change the text of the page.
-  NSError* error = nil;
-  [ChromeEarlGreyAppInterface
-      executeJavaScript:[NSString stringWithFormat:@"document.write('%s');",
-                                                   kEnglishText]
-                  error:&error];
-  // Trigger a new detection with pushState.
-  error = nil;
-  [ChromeEarlGreyAppInterface
-      executeJavaScript:@"history.pushState(null, null, null);"
-                  error:&error];
-  // Check that the new language has been detected.
-  [self assertContentLanguage:@"" htmlRootLanguage:@"" adoptedLanguage:@"en"];
-}
-
-// Tests that language detection is performed on hash changes.
-- (void)testLanguageDetectionWithHashChange {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Generate a page with French text and a button that changes the text to
-  // English and triggers a hash change.
-  std::string html = base::StringPrintf(
-      "<html><head><script>"
-      "function hash() {"
-      "  document.getElementById('text').innerHTML = '%s';"
-      "  location.href='#1';"
-      "}"
-      "</script></head><body>"
-      "<input type='button' value='Hash' id='Hash' onclick='hash()'>"
-      "<div id='text'>%s</div>"
-      "</body></html>",
-      kEnglishText, kFrenchText);
-
-  // Set up the mock server.
-  std::map<GURL, std::string> responses;
-  const GURL URL =
-      web::test::HttpServer::MakeUrl("http://hashChangeLanguageDetected");
-  responses[URL] = html;
-  web::test::SetUpSimpleHttpServer(responses);
-
-  [ChromeEarlGrey loadURL:URL];
-  // Check that language has been detected.
-  [self assertContentLanguage:@"" htmlRootLanguage:@"" adoptedLanguage:@"fr"];
-
-  // Resets state before triggering a new round of language detection.
-  [TranslateAppInterface resetLanguageDetectionTabHelperObserver];
-  // Trigger the hash change.
-  [ChromeEarlGrey tapWebStateElementWithID:@"Hash"];
-  // Check that language detection has been re-run.
-  [self assertContentLanguage:@"" htmlRootLanguage:@"" adoptedLanguage:@"en"];
-}
-
-// Tests that language in http content is detected.
-- (void)testLanguageDetectionHttpContentLanguage {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // The HTTP header is detected.
-  GURL URL = web::test::HttpServer::MakeUrl(std::string("http://") +
-                                            kLanguagePath + "?http=fr");
-  [ChromeEarlGrey loadURL:URL];
-  [self assertContentLanguage:@"fr" htmlRootLanguage:@"" adoptedLanguage:@"fr"];
-
-  // Resets state before triggering a new round of language detection.
-  [TranslateAppInterface resetLanguageDetectionTabHelperObserver];
-  // Everything after the comma is truncated.
-  URL = web::test::HttpServer::MakeUrl(std::string("http://") + kLanguagePath +
-                                       "?http=fr,ornot");
-  [ChromeEarlGrey loadURL:URL];
-  [self assertContentLanguage:@"fr" htmlRootLanguage:@"" adoptedLanguage:@"fr"];
-
-  // Resets state before triggering a new round of language detection.
-  [TranslateAppInterface resetLanguageDetectionTabHelperObserver];
-  // The HTTP header is overriden by meta tag.
-  URL = web::test::HttpServer::MakeUrl(std::string("http://") + kLanguagePath +
-                                       "?http=fr&meta=it");
-  [ChromeEarlGrey loadURL:URL];
-  [self assertContentLanguage:@"it" htmlRootLanguage:@"" adoptedLanguage:@"it"];
-
-  // Resets state before triggering a new round of language detection.
-  [TranslateAppInterface resetLanguageDetectionTabHelperObserver];
-  // Only the header of the main page is detected.
-  URL =
-      web::test::HttpServer::MakeUrl(std::string("http://") + kSubresourcePath);
-  [ChromeEarlGrey loadURL:URL];
-  [self assertContentLanguage:@"fr" htmlRootLanguage:@"" adoptedLanguage:@"fr"];
-}
-
-// Tests that language in http content is detected when navigating to a link.
-- (void)testLanguageDetectionHttpContentLanguageBehindLink {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Detection works when clicking on a link.
-  GURL URL = web::test::HttpServer::MakeUrl(std::string("http://") + kLinkPath);
-  GURL someLanguageURL = web::test::HttpServer::MakeUrl(kSomeLanguageUrl);
-  [ChromeEarlGrey loadURL:URL];
-  [ChromeEarlGrey tapWebStateElementWithID:@"click"];
-  [ChromeEarlGrey waitForWebStateContainingText:kLanguagePathText];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
-                                          someLanguageURL.GetContent())]
-      assertWithMatcher:grey_notNil()];
-  [self assertContentLanguage:@"es" htmlRootLanguage:@"" adoptedLanguage:@"es"];
-}
-
-// Tests that language detection still happens when a very large quantity of
-// text is present on the page.
-- (void)testLanguageDetectionLargePage {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Generate very large page.
-  std::string html = "<html lang='fr'><body>";
-  NSUInteger targetSize = 1024 * 1024;  // More than 1 MB of page content.
-  while (html.length() < targetSize) {
-    html.append("<p>");
-    html.append(kFrenchText);
-    html.append("</p>");
-  }
-  html.append("</body></html>");
-
-  // Create map of canned responses and set up the test HTML server.
-  std::map<GURL, std::string> responses;
-  const GURL URL =
-      web::test::HttpServer::MakeUrl("http://languageDetectionLargePage");
-  responses[URL] = html;
-  web::test::SetUpSimpleHttpServer(responses);
-  [ChromeEarlGrey loadURL:URL];
-
-  // Check that language has been detected.
-  [self assertContentLanguage:@"" htmlRootLanguage:@"fr" adoptedLanguage:@"fr"];
-}
-
-// Tests that language detection is not performed when translate is disabled.
-- (void)testLanguageDetectionDisabled {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  const GURL URL = web::test::HttpServer::MakeUrl(
-      "http://scenarioLanguageDetectionDisabled");
-  std::map<GURL, std::string> responses;
-  // A page with some text.
-  responses[URL] = "<html><body>Hello world!</body></html>";
-  web::test::SetUpSimpleHttpServer(responses);
-
-  // Disable translate.
-  [ChromeEarlGreyAppInterface
-      setBoolValue:NO
-       forUserPref:base::SysUTF8ToNSString(prefs::kOfferTranslateEnabled)];
-
-  // Open some webpage.
-  [ChromeEarlGrey loadURL:URL];
-  // Check that no language has been detected.
-  GREYAssertFalse([self waitForLanguageDetection],
-                  @"A language has been detected");
-
-  // Enable translate.
-  [ChromeEarlGreyAppInterface
-      setBoolValue:YES
-       forUserPref:base::SysUTF8ToNSString(prefs::kOfferTranslateEnabled)];
-}
-
-// Tests that the infobar hides/shows as the browser enters/exits the fullscreen
-// mode as well as it can be dimissed.
-- (void)testInfobarShowHideDismiss {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Scroll down to enter the fullscreen mode.
-  [[EarlGrey selectElementWithMatcher:WebStateScrollViewMatcher()]
-      performAction:grey_swipeFastInDirection(kGREYDirectionUp)];
-
-  // Expect the translate infobar to be hidden.
-  [[EarlGrey selectElementWithMatcher:TranslateInfobar()]
-      assertWithMatcher:grey_notVisible()];
-
-  // Scroll up to exit the fullscreen mode.
-  [[EarlGrey selectElementWithMatcher:WebStateScrollViewMatcher()]
-      performAction:grey_swipeFastInDirection(kGREYDirectionDown)];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Dismiss the translate infobar.
-  [[EarlGrey selectElementWithMatcher:CloseButton()] performAction:grey_tap()];
-
-  // Wait until the translate infobar disappears.
-  ConditionBlock condition = ^{
-    NSError* error = nil;
-    [[EarlGrey selectElementWithMatcher:TranslateInfobar()]
-        assertWithMatcher:grey_nil()
-                    error:&error];
-    return error == nil;
-  };
-  GREYAssert(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition),
-             @"Translate infobar failed to disappear.");
-}
-
-// Tests that the infobar's popup menu can be dimissed.
-- (void)testInfobarDismissPopupMenu {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Expect the translate options menu to have appeared.
-  [[EarlGrey selectElementWithMatcher:OptionsMenu()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  // When the popup menu is visible, a scrim covers the whole window and tapping
-  // it dismisses the popup menu. The options button is outside of the bounds of
-  // the popup menu and is a convenient place to tap to activate the scrim.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Expect the translate options menu to have disappeared.
-  [[EarlGrey selectElementWithMatcher:OptionsMenu()]
-      assertWithMatcher:grey_nil()];
-}
-
-// Tests that the page can be translated and that translation can be reverted
-// using the source and the target language tabs.
-- (void)testInfobarTranslateRevert {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self translateThenRevert];
-}
-
-// Tests that the page can be translated and that translation can be reverted
-// using the source and the target language tabs in incognito mode.
-- (void)testInfobarTranslateRevertIncognito {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text in an incognito tab.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  // Stop observing the current IOSLanguageDetectionTabHelper before opening the
-  // incognito tab.
-  [TranslateAppInterface tearDownLanguageDetectionTabHelperObserver];
-  [ChromeEarlGrey openNewIncognitoTab];
-  [ChromeEarlGrey loadURL:URL];
-
-  // Since current web state has changed to Incognito, a new fake translation
-  // manager has to be set up for the rest of this test.
-  [TranslateAppInterface setUpFakeJSTranslateManagerInCurrentTab];
-
-  [self translateThenRevert];
-}
-
-// Translates the page and reverts the translation using the language tabs.
-- (void)translateThenRevert {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure the page is not translated.
-  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
-
-  // The source language tab must be selected and the target language tab must
-  // not. Translate the page by tapping the target language tab.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(YES)];
-  [[[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
-
-  // Make sure the page is translated.
-  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
-
-  // The target language tab must be selected and the source language tab must
-  // not. Revert the translation by tapping the source language tab.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(YES)];
-  [[[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
-
-  // Make sure the translation is reverted.
-  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
-
-  // The source language tab must be selected and the target language tab must
-  // not.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(YES)];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(NO)];
-}
-
-// Tests that translation occurs automatically on second navigation to an
-// already translated page.
-- (void)testInfobarAutoTranslate {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text and a link.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPageWithLinkPath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure the page is not translated.
-  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
-
-  // The target language tab must not be selected. Translate the page by
-  // tapping the target language tab.
-  [[[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
-
-  // Make sure the page is translated.
-  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
-
-  // Click on the link.
-  [ChromeEarlGrey tapWebStateElementWithID:@"link"];
-
-  // Make sure the navigation is completed.
-  GURL frenchPagePathURL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
-                                          frenchPagePathURL.GetContent())]
-      assertWithMatcher:grey_notNil()];
-
-  // Make sure the page is automatically translated.
-  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
-}
-
-// Tests that the source and the target languages can be changed.
-- (void)testInfobarChangeLanguages {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // The source language tab must be selected and the target language tab must
-  // not.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(YES)];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(NO)];
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Expect the translate options menu to have appeared.
-  [[EarlGrey selectElementWithMatcher:OptionsMenu()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  // Select the "More Languages" entry in the options popup menu.
-  [[EarlGrey selectElementWithMatcher:MoreLanguages()]
-      performAction:grey_tap()];
-
-  // Expect the translate options menu to have disappeared.
-  [[EarlGrey selectElementWithMatcher:OptionsMenu()]
-      assertWithMatcher:grey_nil()];
-
-  // Expect the language selection menu to have appeared.
-  [[EarlGrey selectElementWithMatcher:LanguagesMenu()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  // Select "Dutch" from the the popup menu.
-  [[[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"Dutch")]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
-      onElementWithMatcher:LanguagesMenu()] performAction:grey_tap()];
-
-  // Expect the language selection menu to have disappeared.
-  [[EarlGrey selectElementWithMatcher:LanguagesMenu()]
-      assertWithMatcher:grey_nil()];
-
-  // Make sure the page is translated.
-  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
-
-  // Make sure the target language changes to "Dutch". The target language
-  // tab must be selected and the source language tab must not. Revert the
-  // translation by tapping the source language tab.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"Dutch")]
-      assertWithMatcher:ElementIsSelected(YES)];
-  [[[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
-
-  // Make sure the translation is reverted.
-  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Select the "Page not in French" entry in the options popup menu.
-  [[EarlGrey selectElementWithMatcher:PageNotIn(@"French")]
-      performAction:grey_tap()];
-
-  // Expect the translate options menu to have disappeared.
-  [[EarlGrey selectElementWithMatcher:OptionsMenu()]
-      assertWithMatcher:grey_nil()];
-
-  // Expect the language selection menu to have appeared.
-  [[EarlGrey selectElementWithMatcher:LanguagesMenu()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  // Select "English" from the the popup menu.
-  [[[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
-      onElementWithMatcher:LanguagesMenu()] performAction:grey_tap()];
-
-  // Make sure the page is translated.
-  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
-
-  // Make sure the source language changes to "English". The target language
-  // tab must be selected and the source language tab must not.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(NO)];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"Dutch")]
-      assertWithMatcher:ElementIsSelected(YES)];
-}
-
-// Tests that the "Always Translate" options can be toggled and the prefs are
-// updated accordingly.
-- (void)testInfobarAlwaysTranslate {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure that French to English translation is not automatic.
-  GREYAssert(![TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                          toLanguage:@"en"],
-             @"French to English translation is automatic");
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Make sure the "Always Translate French" entry is not selected and tap it.
-  [[[EarlGrey selectElementWithMatcher:AlwaysTranslate(@"French")]
-      assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
-
-  // Expect the translate options menu to have disappeared.
-  [[EarlGrey selectElementWithMatcher:OptionsMenu()]
-      assertWithMatcher:grey_nil()];
-
-  // Make sure the page is not translated yet.
-  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
-
-  // Make sure that French to English translation is not automatic yet.
-  GREYAssert(![TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                          toLanguage:@"en"],
-             @"French to English translation is automatic");
-
-  // Tap the notification snackbar to dismiss it.
-  NSString* snackbarTitle =
-      l10n_util::GetNSStringF(IDS_TRANSLATE_NOTIFICATION_ALWAYS_TRANSLATE,
-                              base::SysNSStringToUTF16(@"French"),
-                              base::SysNSStringToUTF16(@"English"));
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarTitle)]
-      performAction:grey_tap()];
-
-  // Make sure the page is translated after the snackbar is dismissed.
-  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
-
-  // Make sure that French to English translation is automatic after the
-  // snackbar is dismissed.
-  GREYAssert([TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                         toLanguage:@"en"],
-             @"French to English translation is not automatic");
-
-  // Reload the page.
-  [ChromeEarlGrey reload];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure the page is translated.
-  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
-
-  // The target language tab must be selected and the source language tab must
-  // not.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(NO)];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(YES)];
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Make sure the "Always Translate French" entry is now selected and tap it.
-  [[[EarlGrey selectElementWithMatcher:AlwaysTranslate(@"French")]
-      assertWithMatcher:ElementIsSelected(YES)] performAction:grey_tap()];
-
-  // Make sure that French to English translation is no longer automatic.
-  GREYAssert(![TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                          toLanguage:@"en"],
-             @"French to English translation is automatic");
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Make sure the "Always Translate French" entry is not selected and tap it.
-  [[[EarlGrey selectElementWithMatcher:AlwaysTranslate(@"French")]
-      assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
-
-  // Tap the notification snackbar's "UNDO" button.
-  [[EarlGrey selectElementWithMatcher:UndoButton()] performAction:grey_tap()];
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Make sure the "Always Translate French" entry is still not selected.
-  [[EarlGrey selectElementWithMatcher:AlwaysTranslate(@"French")]
-      assertWithMatcher:ElementIsSelected(NO)];
-
-  // Make sure that French to English translation is not automatic.
-  GREYAssert(![TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                          toLanguage:@"en"],
-             @"French to English translation is automatic");
-}
-
-// Tests that "Always Translate" is automatically triggered after a minimum
-// number of translate attempts by the user.
-- (void)testInfobarAutoAlwaysTranslate {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure that French to English translation is not automatic.
-  GREYAssert(![TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                          toLanguage:@"en"],
-             @"French to English translation is automatic");
-
-  // Translate the page by tapping the target language tab until
-  // "Always Translate" is automatically triggered.
-  for (int i = 0; i <= [TranslateAppInterface infobarAutoAlwaysThreshold];
-       i++) {
-    [[EarlGrey
-        selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-        performAction:grey_tap()];
-    [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-        performAction:grey_tap()];
-  }
-
-  // Make sure that French to English translation is not automatic yet.
-  GREYAssert(![TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                          toLanguage:@"en"],
-             @"French to English translation is automatic");
-
-  // Tap the notification snackbar to dismiss it.
-  NSString* snackbarTitle =
-      l10n_util::GetNSStringF(IDS_TRANSLATE_NOTIFICATION_ALWAYS_TRANSLATE,
-                              base::SysNSStringToUTF16(@"French"),
-                              base::SysNSStringToUTF16(@"English"));
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarTitle)]
-      performAction:grey_tap()];
-
-  // Make sure that French to English translation is automatic after the
-  // snackbar is dismissed.
-  GREYAssert([TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                         toLanguage:@"en"],
-             @"French to English translation is not automatic");
-}
-
-// Tests that "Always Translate" is automatically triggered only for a maximum
-// number of times if refused by the user.
-- (void)testInfobarAutoAlwaysTranslateMaxTries {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure that French to English translation is not automatic.
-  GREYAssert(![TranslateAppInterface shouldAutoTranslateFromLanguage:@"fr"
-                                                          toLanguage:@"en"],
-             @"French to English translation is automatic");
-
-  // Trigger and refuse the auto "Always Translate".
-  for (int i = 0; i < [TranslateAppInterface infobarMaximumNumberOfAutoAlways];
-       i++) {
-    // Translate the page by tapping the target language tab until
-    // "Always Translate" is automatically triggered.
-    for (int j = 0; j <= [TranslateAppInterface infobarAutoAlwaysThreshold];
-         j++) {
-      [[EarlGrey
-          selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-          performAction:grey_tap()];
-      [[EarlGrey
-          selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-          performAction:grey_tap()];
-    }
-    // Tap the notification snackbar's "UNDO" button.
-    [[EarlGrey selectElementWithMatcher:UndoButton()] performAction:grey_tap()];
-  }
-
-  // Translate the page by tapping the target language tab in order to
-  // automatically trigger "Always Translate".
-  for (int i = 0; i <= [TranslateAppInterface infobarAutoAlwaysThreshold];
-       i++) {
-    [[EarlGrey
-        selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-        performAction:grey_tap()];
-  }
-
-  // Make sure "Always Translate" is not triggered.
-  NSString* snackbarTitle =
-      l10n_util::GetNSStringF(IDS_TRANSLATE_NOTIFICATION_ALWAYS_TRANSLATE,
-                              base::SysNSStringToUTF16(@"French"),
-                              base::SysNSStringToUTF16(@"English"));
-  GREYAssertFalse([self waitForElementToAppearOrTimeout:grey_accessibilityLabel(
-                                                            snackbarTitle)],
-                  @"Always Translate was triggered.");
-}
-
-// Tests that the "Never Translate ..." options dismisses the infobar and
-// updates the prefs accordingly.
-- (void)testInfobarNeverTranslate {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure that translation from French is not blocked.
-  GREYAssert(![TranslateAppInterface isBlockedLanguage:@"fr"],
-             @"Translation from French is blocked");
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Tap the "Never Translate French" entry.
-  [[EarlGrey selectElementWithMatcher:NeverTranslate(@"French")]
-      performAction:grey_tap()];
-
-  // Expect the translate options menu to have disappeared.
-  [[EarlGrey selectElementWithMatcher:OptionsMenu()]
-      assertWithMatcher:grey_nil()];
-
-  // Tap the notification snackbar's "UNDO" button.
-  [[EarlGrey selectElementWithMatcher:UndoButton()] performAction:grey_tap()];
-
-  // Make sure that translation from French is still not blocked.
-  GREYAssert(![TranslateAppInterface isBlockedLanguage:@"fr"],
-             @"Translation from French is blocked");
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Tap the "Never Translate French" entry.
-  [[EarlGrey selectElementWithMatcher:NeverTranslate(@"French")]
-      performAction:grey_tap()];
-
-  // Make sure that translation from French is not blocked yet.
-  GREYAssert(![TranslateAppInterface isBlockedLanguage:@"fr"],
-             @"Translation from French is blocked");
-
-  // Tap the notification snackbar to dismiss it.
-  NSString* snackbarTitle =
-      l10n_util::GetNSStringF(IDS_TRANSLATE_NOTIFICATION_LANGUAGE_NEVER,
-                              base::SysNSStringToUTF16(@"French"));
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarTitle)]
-      performAction:grey_tap()];
-
-  // Wait until the translate infobar disappears.
-  GREYAssert([self waitForElementToDisappearOrTimeout:TranslateInfobar()],
-             @"Translate infobar failed to disappear.");
-
-  // Make sure that translation from French is blocked after the snackbar is
-  // dismissed.
-  GREYAssert([TranslateAppInterface isBlockedLanguage:@"fr"],
-             @"Translation from French is not blocked");
-
-  // Reload the page.
-  [ChromeEarlGrey reload];
-
-  // Make sure the translate infobar does not appear.
-  GREYAssertFalse([self waitForElementToAppearOrTimeout:TranslateInfobar()],
-                  @"Translate infobar appeared.");
-}
-
-// Tests that "Never Translate ..." is automatically triggered after a minimum
-// number of translate infobar dismissals by the user.
-- (void)testInfobarAutoNeverTranslate {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure that translation from French is not blocked.
-  GREYAssert(![TranslateAppInterface isBlockedLanguage:@"fr"],
-             @"Translation from French is blocked");
-
-  // Dismiss the translate infobar until "Never Translate ..." is automatically
-  // triggered.
-  for (int i = 0; i < [TranslateAppInterface infobarAutoNeverThreshold]; i++) {
-    // Reload the page.
-    [ChromeEarlGrey reload];
-
-    [self assertTranslateInfobarIsVisible];
-
-    // Dismiss the translate infobar.
-    [[EarlGrey selectElementWithMatcher:CloseButton()]
-        performAction:grey_tap()];
-  }
-
-  // Make sure that translation from French is not blocked yet.
-  GREYAssert(![TranslateAppInterface isBlockedLanguage:@"fr"],
-             @"Translation from French is blocked");
-
-  // Tap the notification snackbar to dismiss it.
-  NSString* snackbarTitle =
-      l10n_util::GetNSStringF(IDS_TRANSLATE_NOTIFICATION_LANGUAGE_NEVER,
-                              base::SysNSStringToUTF16(@"French"));
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarTitle)]
-      performAction:grey_tap()];
-
-  // Wait until the translate infobar disappears.
-  GREYAssert([self waitForElementToDisappearOrTimeout:TranslateInfobar()],
-             @"Translate infobar failed to disappear.");
-
-  // Make sure that translation from French is blocked after the snackbar is
-  // dismissed.
-  GREYAssert([TranslateAppInterface isBlockedLanguage:@"fr"],
-             @"Translation from French is not blocked");
-}
-
-// Tests that "Never Translate ..." is automatically triggered only for a
-// maximum number of times if refused by the user.
-// TODO(crbug.com/945118): Re-enable when fixed.
-- (void)DISABLED_testInfobarAutoNeverTranslateMaxTries {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Make sure that translation from French is not blocked.
-  GREYAssert(![TranslateAppInterface isBlockedLanguage:@"fr"],
-             @"Translation from French is blocked");
-
-  // Trigger and refuse the auto "Never Translate ...".
-  for (int i = 0; i < [TranslateAppInterface infobarMaximumNumberOfAutoNever];
-       i++) {
-    // Dismiss the translate infobar until "Never Translate ..." is
-    // automatically triggered.
-    for (int j = 0; j < [TranslateAppInterface infobarAutoNeverThreshold];
-         j++) {
-      // Reload the page.
-      [ChromeEarlGrey reload];
-
-      [self assertTranslateInfobarIsVisible];
-
-      // Dismiss the translate infobar.
-      [[EarlGrey selectElementWithMatcher:CloseButton()]
-          performAction:grey_tap()];
-    }
-    // Tap the notification snackbar's "UNDO" button.
-    [[EarlGrey selectElementWithMatcher:UndoButton()] performAction:grey_tap()];
-
-    // Wait until the translate infobar disappears.
-    GREYAssert([self waitForElementToDisappearOrTimeout:TranslateInfobar()],
-               @"Translate infobar failed to disappear.");
-  }
-
-  // Dismiss the translate infobar in order to automatically trigger
-  // "Never Translate ...".
-  for (int i = 0; i < [TranslateAppInterface infobarAutoNeverThreshold]; i++) {
-    // Reload the page.
-    [ChromeEarlGrey reload];
-
-    [self assertTranslateInfobarIsVisible];
-
-    // Dismiss the translate infobar.
-    [[EarlGrey selectElementWithMatcher:CloseButton()]
-        performAction:grey_tap()];
-  }
-
-  // Make sure "Never Translate ..." is not triggered.
-  NSString* snackbarTitle =
-      l10n_util::GetNSStringF(IDS_TRANSLATE_NOTIFICATION_LANGUAGE_NEVER,
-                              base::SysNSStringToUTF16(@"French"));
-  GREYAssertFalse([self waitForElementToAppearOrTimeout:grey_accessibilityLabel(
-                                                            snackbarTitle)],
-                  @"Never Translate French was triggered.");
-}
-
-// Tests that the "Never Translate this site" option dismisses the infobar and
-// updates the prefs accordingly.
-- (void)testInfobarNeverTranslateSite {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  NSString* URLHost = base::SysUTF8ToNSString(URL.HostNoBrackets());
-  // Make sure that translation for the site is not blocked.
-  GREYAssert(![TranslateAppInterface isBlockedSite:URLHost],
-             @"Translation is blocked for the site");
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Tap the "Never Translate this site" entry.
-  [[EarlGrey selectElementWithMatcher:NeverTranslateSite()]
-      performAction:grey_tap()];
-
-  // Expect the translate options menu to have disappeared.
-  [[EarlGrey selectElementWithMatcher:OptionsMenu()]
-      assertWithMatcher:grey_nil()];
-
-  // Tap the notification snackbar's "UNDO" button.
-  [[EarlGrey selectElementWithMatcher:UndoButton()] performAction:grey_tap()];
-
-  // Make sure that translation for the site is still not blocked.
-  GREYAssert(![TranslateAppInterface isBlockedSite:URLHost],
-             @"Translation is blocked for the site");
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Tap the "Never Translate this site" entry.
-  [[EarlGrey selectElementWithMatcher:NeverTranslateSite()]
-      performAction:grey_tap()];
-
-  // Make sure that translation for the site is not blocked yet.
-  GREYAssert(![TranslateAppInterface isBlockedSite:URLHost],
-             @"Translation is blocked for the site");
-
-  // Tap the notification snackbar to dismiss it.
-  NSString* snackbarTitle =
-      l10n_util::GetNSString(IDS_TRANSLATE_NOTIFICATION_SITE_NEVER);
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarTitle)]
-      performAction:grey_tap()];
-
-  // Make sure that translation for the site is blocked after the snackbar is
-  // dismissed.
-  GREYAssert([TranslateAppInterface isBlockedSite:URLHost],
-             @"Translation is not blocked for the site");
-
-  // Wait until the translate infobar disappears.
-  GREYAssert([self waitForElementToDisappearOrTimeout:TranslateInfobar()],
-             @"Translate infobar failed to disappear.");
-
-  // Reload the page.
-  [ChromeEarlGrey reload];
-
-  // Make sure the translate infobar does not appear.
-  GREYAssertFalse([self waitForElementToAppearOrTimeout:TranslateInfobar()],
-                  @"Translate infobar appeared.");
-}
-
-// Tests that the "Translate..." button in the tools menu is enabled if
-// translate is available and it brings up the Translate infobar and translates
-// the page when tapped. If the page is already translated the infobar should
-// appear in "after translate" state.
-- (void)testTranslateManualTrigger {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // The source language tab must be selected and the target language tab must
-  // not.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(YES)];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(NO)];
-
-  // Dismiss the translate infobar.
-  [[EarlGrey selectElementWithMatcher:CloseButton()] performAction:grey_tap()];
-
-  // Wait until the translate infobar disappears.
-  ConditionBlock condition = ^{
-    NSError* error = nil;
-    [[EarlGrey selectElementWithMatcher:TranslateInfobar()]
-        assertWithMatcher:grey_nil()
-                    error:&error];
-    return error == nil;
-  };
-  GREYAssert(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition),
-             @"Translate infobar failed to disappear.");
-
-  // Make sure the Translate manual trigger button is enabled and tap it.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[[EarlGrey selectElementWithMatcher:toolsMenuTranslateButton()]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 250)
-      onElementWithMatcher:ToolsMenuView()]
-      assertWithMatcher:grey_not(grey_accessibilityTrait(
-                            UIAccessibilityTraitNotEnabled))]
-      performAction:grey_tap()];
-
-  // Make sure the infobar reappears.
-  [self assertTranslateInfobarIsVisible];
-
-  // The target language tab must be selected and the source language tab must
-  // not.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(YES)];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(NO)];
-
-  // Make sure the page is translated.
-  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
-
-  // Dismiss the translate infobar.
-  [[EarlGrey selectElementWithMatcher:CloseButton()] performAction:grey_tap()];
-
-  // Make sure the Translate manual trigger button is enabled and tap it.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[[EarlGrey selectElementWithMatcher:toolsMenuTranslateButton()]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 250)
-      onElementWithMatcher:ToolsMenuView()]
-      assertWithMatcher:grey_not(grey_accessibilityTrait(
-                            UIAccessibilityTraitNotEnabled))]
-      performAction:grey_tap()];
-
-  // Make sure the infobar reappears.
-  [self assertTranslateInfobarIsVisible];
-
-  // The target language tab must be selected and the source language tab must
-  // not.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:ElementIsSelected(YES)];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:ElementIsSelected(NO)];
-}
-
-// Tests that the "Translate..." button in the tools menu brings up the
-// Translate infobar even if user has previously selected not to translate the
-// the source language.
-- (void)testTranslateManualTriggerNeverTranslate {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Tap the "Never Translate French" entry.
-  [[EarlGrey selectElementWithMatcher:NeverTranslate(@"French")]
-      performAction:grey_tap()];
-
-  // Tap the notification snackbar to dismiss it.
-  NSString* snackbarTitle =
-      l10n_util::GetNSStringF(IDS_TRANSLATE_NOTIFICATION_LANGUAGE_NEVER,
-                              base::SysNSStringToUTF16(@"French"));
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarTitle)]
-      performAction:grey_tap()];
-
-  // Wait until the translate infobar disappears.
-  GREYAssert([self waitForElementToDisappearOrTimeout:TranslateInfobar()],
-             @"Translate infobar failed to disappear.");
-
-  // Reload the page.
-  [ChromeEarlGrey reload];
-
-  // Make sure the translate infobar does not appear.
-  GREYAssertFalse([self waitForElementToAppearOrTimeout:TranslateInfobar()],
-                  @"Translate infobar appeared.");
-
-  // Tap the Translate manual trigger button.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey selectElementWithMatcher:toolsMenuTranslateButton()]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 250)
-      onElementWithMatcher:ToolsMenuView()] performAction:grey_tap()];
-
-  // Make sure the infobar reappears.
-  [self assertTranslateInfobarIsVisible];
-}
-
-// Tests that the "Translate..." button in the tools menu brings up the
-// Translate infobar even if user has previously selected not to translate the
-// the site.
-- (void)testTranslateManualTriggerNeverTranslateSite {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text.
-  GURL URL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPagePath));
-  [ChromeEarlGrey loadURL:URL];
-
-  [self assertTranslateInfobarIsVisible];
-
-  // Open the translate options menu.
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      performAction:grey_tap()];
-
-  // Tap the "Never Translate this site" entry.
-  [[EarlGrey selectElementWithMatcher:NeverTranslateSite()]
-      performAction:grey_tap()];
-
-  // Tap the notification snackbar to dismiss it.
-  NSString* snackbarTitle =
-      l10n_util::GetNSString(IDS_TRANSLATE_NOTIFICATION_SITE_NEVER);
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarTitle)]
-      performAction:grey_tap()];
-
-  // Wait until the translate infobar disappears.
-  GREYAssert([self waitForElementToDisappearOrTimeout:TranslateInfobar()],
-             @"Translate infobar failed to disappear.");
-
-  // Reload the page.
-  [ChromeEarlGrey reload];
-
-  // Make sure the translate infobar does not appear.
-  GREYAssertFalse([self waitForElementToAppearOrTimeout:TranslateInfobar()],
-                  @"Translate infobar appeared.");
-
-  // Tap the Translate manual trigger button.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey selectElementWithMatcher:toolsMenuTranslateButton()]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 250)
-      onElementWithMatcher:ToolsMenuView()] performAction:grey_tap()];
-
-  // Make sure the infobar reappears.
-  [self assertTranslateInfobarIsVisible];
-}
-
-// Tests that the "Translate..." button in the tools menu is disabled if
-// translate is not available.
-- (void)testTranslateManualTriggerNotEnabled {
-  if (IsInfobarUIRebootEnabled()) {
-    EARL_GREY_TEST_DISABLED(@"Legacy Test.");
-  }
-  // Start the HTTP server.
-  std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load a page with French text with |content="notranslate"| meta tag.
-  GURL noTranslateContentURL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPageNoTranslateContent));
-  [ChromeEarlGrey loadURL:noTranslateContentURL];
-
-  // Make sure no language has been detected.
-  GREYAssertFalse([self waitForLanguageDetection],
-                  @"A language has been detected");
-
-  // Make sure the Translate manual trigger button is not enabled.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey selectElementWithMatcher:toolsMenuTranslateButton()]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
-      onElementWithMatcher:ToolsMenuView()]
-      assertWithMatcher:grey_accessibilityTrait(
-                            UIAccessibilityTraitNotEnabled)];
-  // Close the tools menu.
-  [ChromeTestCase removeAnyOpenMenusAndInfoBars];
-
-  // Load a page with French text with |value="notranslate"| meta tag.
-  GURL noTranslateValueURL = web::test::HttpServer::MakeUrl(
-      base::StringPrintf("http://%s", kFrenchPageNoTranslateValue));
-  [ChromeEarlGrey loadURL:noTranslateValueURL];
-
-  // Make sure no language has been detected.
-  GREYAssertFalse([self waitForLanguageDetection],
-                  @"A language has been detected");
-
-  // Make sure the Translate manual trigger button is not enabled.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey selectElementWithMatcher:toolsMenuTranslateButton()]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
-      onElementWithMatcher:ToolsMenuView()]
-      assertWithMatcher:grey_accessibilityTrait(
-                            UIAccessibilityTraitNotEnabled)];
-  // Close the tools menu.
-  [ChromeTestCase removeAnyOpenMenusAndInfoBars];
-
-  // Load a chrome:// page.
-  GURL URL = web::test::HttpServer::MakeUrl("chrome://something-internal");
-  [ChromeEarlGrey loadURL:URL];
-
-  // Make sure the Translate manual trigger button is not enabled.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey selectElementWithMatcher:toolsMenuTranslateButton()]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
-      onElementWithMatcher:ToolsMenuView()]
-      assertWithMatcher:grey_accessibilityTrait(
-                            UIAccessibilityTraitNotEnabled)];
-  // Close the tools menu.
-  [ChromeTestCase removeAnyOpenMenusAndInfoBars];
-}
-
-#pragma mark - Utility methods
-
-- (void)assertTranslateInfobarIsVisible {
-  // Wait until the translate infobar becomes visible.
-  GREYAssert([self waitForElementToAppearOrTimeout:TranslateInfobar()],
-             @"Translate infobar failed to show.");
-
-  // Check that the translate infobar is fully visible.
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"French")]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:OptionsButton()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:CloseButton()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-- (BOOL)waitForElementToAppearOrTimeout:(id<GREYMatcher>)matcher {
-  ConditionBlock condition = ^{
-    NSError* error = nil;
-    [[EarlGrey selectElementWithMatcher:matcher] assertWithMatcher:grey_notNil()
-                                                             error:&error];
-    return error == nil;
-  };
-  return WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition);
-}
-
-- (BOOL)waitForElementToDisappearOrTimeout:(id<GREYMatcher>)matcher {
-  ConditionBlock condition = ^{
-    NSError* error = nil;
-    [[EarlGrey selectElementWithMatcher:matcher] assertWithMatcher:grey_nil()
-                                                             error:&error];
-    return error == nil;
-  };
-  return WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition);
-}
-
-// Returns whether a language has been detected on the current page. Returns
-// false if a timeout was detected while waiting for language detection.
-- (BOOL)waitForLanguageDetection {
-  bool detected = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
-    return [TranslateAppInterface isLanguageDetected];
-  });
-  return detected;
-}
-
-// Waits until language details have been detected then verifies them.
-// Checks expectation for Content-Language, HTML root element language, and
-// detected language on page. Use @"" for expected values that are expected
-// to be not set.
-- (void)assertContentLanguage:(NSString*)expectedContentLanguage
-             htmlRootLanguage:(NSString*)expectedHtmlRootLanguage
-              adoptedLanguage:(NSString*)expectedAdoptedLanguage {
-  GREYAssert([self waitForLanguageDetection], @"Language not detected");
-
-  NSString* contentLanguage = [TranslateAppInterface contentLanguage];
-  NSString* contentLanguageError =
-      [NSString stringWithFormat:@"Wrong content-language: %@ (expected %@)",
-                                 contentLanguage, expectedContentLanguage];
-  GREYAssertEqualObjects(expectedContentLanguage, contentLanguage,
-                         contentLanguageError);
-
-  NSString* htmlRootLanguage = [TranslateAppInterface htmlRootLanguage];
-  NSString* htmlRootLanguageError =
-      [NSString stringWithFormat:@"Wrong html root language: %@ (expected %@)",
-                                 htmlRootLanguage, expectedHtmlRootLanguage];
-  GREYAssertEqualObjects(expectedHtmlRootLanguage, htmlRootLanguage,
-                         htmlRootLanguageError);
-
-  NSString* adoptedLanguage = [TranslateAppInterface adoptedLanguage];
-  NSString* adoptedLanguageError =
-      [NSString stringWithFormat:@"Wrong adopted language: %@ (expected %@)",
-                                 adoptedLanguage, expectedAdoptedLanguage];
-  GREYAssertEqualObjects(expectedAdoptedLanguage, adoptedLanguage,
-                         adoptedLanguageError);
-}
-
-@end
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index d9c54b1..49ff4e43 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -147,6 +147,7 @@
     "signin_earl_grey_app_interface.h",
     "signin_earl_grey_app_interface.mm",
   ]
+  visibility = [ "//ios/chrome/test/earl_grey:eg_app_support+eg2" ]
   deps = [
     ":authentication",
     "unified_consent",
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm b/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
index 463279b8..e561194a 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
@@ -101,7 +101,7 @@
       performAction:grey_tap()];
 
   // Check the signed-in user did change.
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity2];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity2];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -133,7 +133,7 @@
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Check |fakeIdentity| is signed-in.
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
 }
 
 // Tests signing in with one account, switching sync account to a second and
@@ -174,7 +174,7 @@
       signOutWithSignOutConfirmation:SignOutConfirmationManagedUser];
 
   // Check that there is no signed in user.
-  [SigninEarlGrey checkSignedOut];
+  [SigninEarlGrey verifySignedOut];
 }
 
 // Tests that signing in, tapping the Settings link on the confirmation screen
@@ -196,7 +196,7 @@
   [[EarlGrey selectElementWithMatcher:settings_matcher]
       assertWithMatcher:grey_sufficientlyVisible()];
   // Test the user is signed in.
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
 }
 
 // Opens the sign in screen and then cancel it by opening a new tab. Ensures
@@ -290,7 +290,7 @@
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
-  [SigninEarlGrey checkSignedOut];
+  [SigninEarlGrey verifySignedOut];
 }
 
 // Opens the sign in screen from the bookmarks and then cancel it by tapping on
@@ -420,7 +420,7 @@
           ButtonWithAccessibilityLabel(GetNSString(
               IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_CANCEL_SYNC_BUTTON))]
       performAction:grey_tap()];
-  [SigninEarlGrey checkSignedOut];
+  [SigninEarlGrey verifySignedOut];
 }
 
 #pragma mark - Utils
@@ -482,10 +482,10 @@
 
   if (tapSettingsLink) {
     // Should be signed in.
-    [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
+    [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
   } else {
     // Should be not signed in.
-    [SigninEarlGrey checkSignedOut];
+    [SigninEarlGrey verifySignedOut];
   }
 }
 
@@ -493,7 +493,7 @@
 // onscreen.
 - (void)assertFakeSSOScreenIsVisible {
   // Check for the fake SSO screen.
-  [SigninEarlGrey
+  [ChromeEarlGrey
       waitForMatcher:grey_accessibilityID(kFakeAddAccountViewIdentifier)];
   // Close the SSO view controller.
   id<GREYMatcher> matcher =
@@ -540,7 +540,7 @@
   // Open the identity chooser.
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [SigninEarlGrey waitForMatcher:identityChooserButtonMatcherWithEmail(
+  [ChromeEarlGrey waitForMatcher:identityChooserButtonMatcherWithEmail(
                                      fakeIdentity.userEmail)];
 
   // Remove the fake identity.
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey.h b/ios/chrome/browser/ui/authentication/signin_earl_grey.h
index ba2004c..4d5e07b 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey.h
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey.h
@@ -36,15 +36,19 @@
 // simulate identity removal from the device.
 - (void)forgetFakeIdentity:(FakeChromeIdentity*)fakeIdentity;
 
+// Signs the user out of the primary account. Induces a GREYAssert if the
+// app fails to sign out.
+- (void)signOut;
+
 // Induces a GREYAssert if |fakeIdentity| is not signed in to the active
 // profile.
-- (void)checkSignedInWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity;
+- (void)verifySignedInWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity;
 
 // Induces a GREYAssert if an identity is signed in.
-- (void)checkSignedOut;
+- (void)verifySignedOut;
 
-// Wait until |matcher| is accessible (not nil).
-- (void)waitForMatcher:(id<GREYMatcher>)matcher;
+// Induces a GREYAssert if there are no signed-in identities.
+- (void)verifyAuthenticated;
 
 @end
 
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey.mm
index 6c6763b..72ff0411 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey.mm
@@ -35,7 +35,12 @@
   [SigninEarlGreyAppInterface forgetFakeIdentity:fakeIdentity];
 }
 
-- (void)checkSignedInWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
+- (void)signOut {
+  [SigninEarlGreyAppInterface signOut];
+  [self verifySignedOut];
+}
+
+- (void)verifySignedInWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
   BOOL fakeIdentityIsNonNil = fakeIdentity != nil;
   EG_TEST_HELPER_ASSERT_TRUE(fakeIdentityIsNonNil, @"Need to give an identity");
 
@@ -63,7 +68,7 @@
       [fakeIdentity.gaiaID isEqualToString:primaryAccountGaiaID], errorStr);
 }
 
-- (void)checkSignedOut {
+- (void)verifySignedOut {
   // Required to avoid any problem since the following test is not dependant to
   // UI, and the previous action has to be totally finished before going through
   // the assert.
@@ -73,16 +78,9 @@
                              @"Unexpected signed in user");
 }
 
-- (void)waitForMatcher:(id<GREYMatcher>)matcher {
-  ConditionBlock condition = ^{
-    NSError* error = nil;
-    [[EarlGrey selectElementWithMatcher:matcher] assertWithMatcher:grey_notNil()
-                                                             error:&error];
-    return error == nil;
-  };
-  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
-                 base::test::ios::kWaitForUIElementTimeout, condition),
-             @"Waiting for matcher %@ failed.", matcher);
+- (void)verifyAuthenticated {
+  EG_TEST_HELPER_ASSERT_TRUE([SigninEarlGreyAppInterface isAuthenticated],
+                             @"User is not signed in");
 }
 
 @end
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
index 648ba391..830242a 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
@@ -11,6 +11,7 @@
 #import "ios/chrome/browser/ui/authentication/unified_consent/unified_consent_constants.h"
 #import "ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers_app_interface.h"
@@ -49,7 +50,7 @@
   }
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
 }
 
 + (void)selectIdentityWithEmail:(NSString*)userEmail {
@@ -74,7 +75,7 @@
   ScopedSynchronizationDisabler disabler;
   id<GREYMatcher> acceptButton = [ChromeMatchersAppInterface
       buttonWithAccessibilityLabelID:IDS_IOS_MANAGED_SIGNIN_ACCEPT_BUTTON];
-  [SigninEarlGrey waitForMatcher:acceptButton];
+  [ChromeEarlGrey waitForMatcher:acceptButton];
   [[EarlGrey selectElementWithMatcher:acceptButton] performAction:grey_tap()];
 }
 
@@ -224,7 +225,7 @@
   [ChromeEarlGreyUI waitForAppToIdle];
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
-  [SigninEarlGrey checkSignedOut];
+  [SigninEarlGrey verifySignedOut];
 }
 
 + (void)tapRemoveAccountFromDeviceWithFakeIdentity:
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
index 00abbdf..55ecb67 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
@@ -87,7 +87,15 @@
 
 // Tests that the addresses view controller contains the "Manage Addresses..."
 // action.
-- (void)testAddressesViewControllerContainsManageAddressesAction {
+// TODO(crbug.com/1116043): Flaky on ios simulator.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testAddressesViewControllerContainsManageAddressesAction \
+  DISABLED_testAddressesViewControllerContainsManageAddressesAction
+#else
+#define MAYBE_testAddressesViewControllerContainsManageAddressesAction \
+  testAddressesViewControllerContainsManageAddressesAction
+#endif
+- (void)MAYBE_testAddressesViewControllerContainsManageAddressesAction {
   // Bring up the keyboard.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormElementName)];
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
index e22026e..886523b 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
@@ -355,8 +355,7 @@
   // When keyboard is split, icons are not visible, so we rely on timeout before
   // docking again, because EarlGrey synchronization isn't working properly with
   // the keyboard.
-  [self waitForMatcherToBeVisible:ManualFallbackProfilesIconMatcher()
-                          timeout:base::test::ios::kWaitForUIElementTimeout];
+  [ChromeEarlGrey waitForMatcher:ManualFallbackProfilesIconMatcher()];
 
   DockKeyboard();
 
@@ -432,8 +431,7 @@
   // When keyboard is split, icons are not visible, so we rely on timeout before
   // docking again, because EarlGrey synchronization isn't working properly with
   // the keyboard.
-  [self waitForMatcherToBeVisible:ManualFallbackProfilesIconMatcher()
-                          timeout:base::test::ios::kWaitForUIElementTimeout];
+  [ChromeEarlGrey waitForMatcher:ManualFallbackProfilesIconMatcher()];
 
   DockKeyboard();
 
@@ -605,22 +603,4 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-#pragma mark - Utilities
-
-// Waits for the passed matcher to be visible with a given timeout.
-- (void)waitForMatcherToBeVisible:(id<GREYMatcher>)matcher
-                          timeout:(CFTimeInterval)timeout {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-result"
-  [[GREYCondition conditionWithName:@"Wait for visible matcher condition"
-                              block:^BOOL {
-                                NSError* error;
-                                [[EarlGrey selectElementWithMatcher:matcher]
-                                    assertWithMatcher:grey_sufficientlyVisible()
-                                                error:&error];
-                                return error == nil;
-                              }] waitWithTimeout:timeout];
-#pragma clang diagnostic pop
-}
-
 @end
diff --git a/ios/chrome/browser/ui/badges/badge_button_factory.mm b/ios/chrome/browser/ui/badges/badge_button_factory.mm
index b0c29f6..b4113627 100644
--- a/ios/chrome/browser/ui/badges/badge_button_factory.mm
+++ b/ios/chrome/browser/ui/badges/badge_button_factory.mm
@@ -47,7 +47,7 @@
 - (BadgeButton*)passwordsSaveBadgeButton {
   BadgeButton* button =
       [self createButtonForType:BadgeType::kBadgeTypePasswordSave
-                     imageNamed:@"infobar_passwords_icon"
+                     imageNamed:@"password_key"
                   renderingMode:UIImageRenderingModeAlwaysTemplate];
   [button addTarget:self.delegate
                 action:@selector(passwordsBadgeButtonTapped:)
@@ -62,7 +62,7 @@
 - (BadgeButton*)passwordsUpdateBadgeButton {
   BadgeButton* button =
       [self createButtonForType:BadgeType::kBadgeTypePasswordUpdate
-                     imageNamed:@"infobar_passwords_icon"
+                     imageNamed:@"password_key"
                   renderingMode:UIImageRenderingModeAlwaysTemplate];
   [button addTarget:self.delegate
                 action:@selector(passwordsBadgeButtonTapped:)
diff --git a/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm b/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
index e80626a..12f2e80 100644
--- a/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
+++ b/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
@@ -93,7 +93,7 @@
   switch (self.badgeType) {
     case BadgeType::kBadgeTypePasswordSave:
     case BadgeType::kBadgeTypePasswordUpdate:
-      badgeImage = [[UIImage imageNamed:@"infobar_passwords_icon"]
+      badgeImage = [[UIImage imageNamed:@"password_key"]
           imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
       break;
     case BadgeType::kBadgeTypeSaveCard:
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index 5c1247a0..a7193da 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -2267,6 +2267,29 @@
 
       return [UIMenu menuWithTitle:@"" children:menuElements];
     };
+  } else if (node->is_folder()) {
+    actionProvider = ^(NSArray<UIMenuElement*>* suggestedActions) {
+      RecordMenuShown(MenuScenario::kBookmarkFolder);
+
+      ActionFactory* actionFactory =
+          [[ActionFactory alloc] initWithBrowser:self.browser
+                                        scenario:MenuScenario::kBookmarkFolder];
+
+      NSMutableArray<UIMenuElement*>* menuElements =
+          [[NSMutableArray alloc] init];
+
+      [menuElements addObject:[actionFactory actionToEditWithBlock:^{
+                      [self editNode:node];
+                    }]];
+
+      [menuElements addObject:[actionFactory actionToMoveFolderWithBlock:^{
+                      std::set<const BookmarkNode*> nodes;
+                      nodes.insert(node);
+                      [self moveNodes:nodes];
+                    }]];
+
+      return [UIMenu menuWithTitle:@"" children:menuElements];
+    };
   }
 
   return
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index bd39910..a42ffbd0 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -283,6 +283,7 @@
     ":constants",
     ":content_suggestions_constant",
     ":eg_test_support+eg2",
+    ":feature_flags",
     "//base",
     "//base/test:test_support",
     "//components/strings",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
index 5852eb61..4528577 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
@@ -14,6 +14,7 @@
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -141,6 +142,9 @@
 // Tests that the additional items (when more is pressed) are kept when
 // switching tabs.
 - (void)testAdditionalItemsKept {
+  if (IsDiscoverFeedEnabled()) {
+    EARL_GREY_TEST_DISABLED(@"Legacy Feed Test.");
+  }
   // Set server up.
   self.testServer->RegisterRequestHandler(base::Bind(&StandardResponse));
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
@@ -175,6 +179,9 @@
 // Tests that when the page is reloaded using the tools menu, the suggestions
 // are updated.
 - (void)testReloadPage {
+  if (IsDiscoverFeedEnabled()) {
+    EARL_GREY_TEST_DISABLED(@"Legacy Feed Test.");
+  }
   // Add 2 suggestions, persisted accross page loads.
   [ContentSuggestionsAppInterface addNumberOfSuggestions:2
                                 additionalSuggestionsURL:nil];
@@ -200,6 +207,9 @@
 // disposition of the collection takes into account the previous scroll, even
 // when more is tapped.
 - (void)testOpenPageAndGoBackWithMoreContent {
+  if (IsDiscoverFeedEnabled()) {
+    EARL_GREY_TEST_DISABLED(@"Legacy Feed Test.");
+  }
   // Set server up.
   self.testServer->RegisterRequestHandler(base::Bind(&StandardResponse));
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
@@ -248,6 +258,9 @@
 // Tests that the "Learn More" cell is present only if there is a suggestion in
 // the section.
 - (void)testLearnMore {
+  if (IsDiscoverFeedEnabled()) {
+    EARL_GREY_TEST_DISABLED(@"Legacy Feed Test.");
+  }
   id<GREYAction> action =
       grey_scrollInDirectionWithStartPoint(kGREYDirectionDown, 200, 0.5, 0.5);
   [[[EarlGrey
diff --git a/ios/chrome/browser/ui/first_run/first_run_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
index 1d15030..172a446 100644
--- a/ios/chrome/browser/ui/first_run/first_run_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
@@ -140,7 +140,7 @@
   [[EarlGrey selectElementWithMatcher:SyncSettingsConfirmButton()]
       performAction:grey_tap()];
 
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
 
   GREYAssertTrue([FirstRunAppInterface isSyncFirstSetupComplete],
                  @"Sync should have finished its original setup");
diff --git a/ios/chrome/browser/ui/infobars/BUILD.gn b/ios/chrome/browser/ui/infobars/BUILD.gn
index 820563c..d7af0e60 100644
--- a/ios/chrome/browser/ui/infobars/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/BUILD.gn
@@ -76,7 +76,6 @@
     ":public",
     "resources:infobar_downloading",
     "resources:infobar_hide_password_icon",
-    "resources:infobar_passwords_icon",
     "resources:infobar_popup_blocker",
     "resources:infobar_reveal_password_icon",
     "resources:infobar_save_card_icon",
@@ -94,6 +93,7 @@
     "//ios/chrome/browser/ui/fancy_ui",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/browser/ui/fullscreen:ui",
+    "//ios/chrome/browser/ui/resources:password_key",
     "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common/ui/colors",
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
index 46ee8927..76acd1f 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
@@ -86,8 +86,7 @@
     self.bannerViewController.buttonText =
         base::SysUTF16ToNSString(self.passwordInfoBarDelegate->GetButtonLabel(
             ConfirmInfoBarDelegate::BUTTON_OK));
-    self.bannerViewController.iconImage =
-        [UIImage imageNamed:@"infobar_passwords_icon"];
+    self.bannerViewController.iconImage = [UIImage imageNamed:@"password_key"];
     NSString* hiddenPasswordText =
         l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_HIDDEN_LABEL);
     [self.bannerViewController
diff --git a/ios/chrome/browser/ui/infobars/resources/BUILD.gn b/ios/chrome/browser/ui/infobars/resources/BUILD.gn
index 8966145..60b4ef7 100644
--- a/ios/chrome/browser/ui/infobars/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/resources/BUILD.gn
@@ -31,15 +31,6 @@
   ]
 }
 
-imageset("infobar_passwords_icon") {
-  sources = [
-    "infobar_passwords_icon.imageset/Contents.json",
-    "infobar_passwords_icon.imageset/infobar_passwords_icon.png",
-    "infobar_passwords_icon.imageset/infobar_passwords_icon@2x.png",
-    "infobar_passwords_icon.imageset/infobar_passwords_icon@3x.png",
-  ]
-}
-
 imageset("infobar_reveal_password_icon") {
   sources = [
     "infobar_reveal_password_icon.imageset/Contents.json",
diff --git a/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/Contents.json b/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/Contents.json
deleted file mode 100644
index 7be13cb..0000000
--- a/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "infobar_passwords_icon.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "infobar_passwords_icon@2x.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "3x",
-            "filename": "infobar_passwords_icon@3x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon.png b/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon.png
deleted file mode 100644
index ccc86e8..0000000
--- a/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon@2x.png b/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon@2x.png
deleted file mode 100644
index 0935d37..0000000
--- a/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon@3x.png b/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon@3x.png
deleted file mode 100644
index 6151d22..0000000
--- a/ios/chrome/browser/ui/infobars/resources/infobar_passwords_icon.imageset/infobar_passwords_icon@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm b/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
index 845f7897..73213c5 100644
--- a/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
+++ b/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
@@ -740,6 +740,11 @@
 // Tests that the target language can be changed. TODO(crbug.com/1046629):
 // implement test for changing source langauge.
 - (void)testInfobarChangeTargetLanguage {
+  // TODO(crbug.com/1116012): This test is failing flaky on iOS14.
+  if (@available(iOS 14, *)) {
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS14.");
+  }
+
   // Start the HTTP server.
   std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
   web::test::SetUpHttpServer(std::move(provider));
diff --git a/ios/chrome/browser/ui/menu/BUILD.gn b/ios/chrome/browser/ui/menu/BUILD.gn
index ab8d181..f690200a 100644
--- a/ios/chrome/browser/ui/menu/BUILD.gn
+++ b/ios/chrome/browser/ui/menu/BUILD.gn
@@ -15,6 +15,7 @@
     "resources:copy_link_url",
     "resources:delete",
     "resources:edit",
+    "resources:move_folder",
     "resources:open_in_incognito",
     "resources:open_in_new_tab",
     "resources:open_new_window",
@@ -41,6 +42,7 @@
     "resources:copy_link_url",
     "resources:delete",
     "resources:edit",
+    "resources:move_folder",
     "resources:open_in_incognito",
     "resources:open_in_new_tab",
     "resources:open_new_window",
diff --git a/ios/chrome/browser/ui/menu/action_factory.h b/ios/chrome/browser/ui/menu/action_factory.h
index e39cbec9..7d89887 100644
--- a/ios/chrome/browser/ui/menu/action_factory.h
+++ b/ios/chrome/browser/ui/menu/action_factory.h
@@ -90,6 +90,10 @@
 // the given hiding |block| when executed.
 - (UIAction*)actionToHideWithBlock:(ProceduralBlock)block;
 
+// Creates a UIAction instance configured for moving a folder which will invoke
+// the given |block| when executed.
+- (UIAction*)actionToMoveFolderWithBlock:(ProceduralBlock)block;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_MENU_ACTION_FACTORY_H_
diff --git a/ios/chrome/browser/ui/menu/action_factory.mm b/ios/chrome/browser/ui/menu/action_factory.mm
index 822b6f5..d323892 100644
--- a/ios/chrome/browser/ui/menu/action_factory.mm
+++ b/ios/chrome/browser/ui/menu/action_factory.mm
@@ -183,4 +183,12 @@
   return action;
 }
 
+- (UIAction*)actionToMoveFolderWithBlock:(ProceduralBlock)block {
+  return [self
+      actionWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_MENU_MOVE)
+                image:[UIImage imageNamed:@"move_folder"]
+                 type:MenuActionType::Move
+                block:block];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/menu/action_factory_unittest.mm b/ios/chrome/browser/ui/menu/action_factory_unittest.mm
index e82c646..f60976c 100644
--- a/ios/chrome/browser/ui/menu/action_factory_unittest.mm
+++ b/ios/chrome/browser/ui/menu/action_factory_unittest.mm
@@ -296,4 +296,24 @@
   }
 }
 
+// Tests that the Move Folder action has the right title and image.
+TEST_F(ActionFactoryTest, MoveFolderAction) {
+  if (@available(iOS 13.0, *)) {
+    ActionFactory* factory =
+        [[ActionFactory alloc] initWithBrowser:test_browser_.get()
+                                      scenario:kTestMenuScenario];
+
+    UIImage* expectedImage = [UIImage imageNamed:@"move_folder"];
+
+    NSString* expectedTitle =
+        l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_MENU_MOVE);
+
+    UIAction* action = [factory actionToMoveFolderWithBlock:^{
+    }];
+
+    EXPECT_TRUE([expectedTitle isEqualToString:action.title]);
+    EXPECT_EQ(expectedImage, action.image);
+  }
+}
+
 #endif  // defined(__IPHONE_13_0)
diff --git a/ios/chrome/browser/ui/menu/menu_action_type.h b/ios/chrome/browser/ui/menu/menu_action_type.h
index 9a19fa2..825fde5f 100644
--- a/ios/chrome/browser/ui/menu/menu_action_type.h
+++ b/ios/chrome/browser/ui/menu/menu_action_type.h
@@ -19,7 +19,8 @@
   Share = 7,
   OpenAllInNewTabs = 8,
   Hide = 9,
-  kMaxValue = Hide
+  Move = 10,
+  kMaxValue = Move
 };
 
 #endif  // IOS_CHROME_BROWSER_UI_MENU_MENU_ACTION_TYPE_H_
diff --git a/ios/chrome/browser/ui/menu/menu_histograms.h b/ios/chrome/browser/ui/menu/menu_histograms.h
index b7c1ef5..f951aa4 100644
--- a/ios/chrome/browser/ui/menu/menu_histograms.h
+++ b/ios/chrome/browser/ui/menu/menu_histograms.h
@@ -15,7 +15,8 @@
   kRecentTabsEntry = 3,
   kContentSuggestionsEntry = 4,
   kRecentTabsHeader = 5,
-  kMaxValue = kRecentTabsHeader
+  kBookmarkFolder = 6,
+  kMaxValue = kBookmarkFolder
 };
 
 // Records a menu shown histogram metric for the |scenario|.
diff --git a/ios/chrome/browser/ui/menu/menu_histograms.mm b/ios/chrome/browser/ui/menu/menu_histograms.mm
index 47a5d289..cb649f7 100644
--- a/ios/chrome/browser/ui/menu/menu_histograms.mm
+++ b/ios/chrome/browser/ui/menu/menu_histograms.mm
@@ -19,14 +19,16 @@
     "Mobile.ContextMenu.HistoryEntry.Actions";
 const char kBookmarkEntryActionsHistogram[] =
     "Mobile.ContextMenu.BookmarkEntry.Actions";
-const char ReadingListEntryActionsHistogram[] =
+const char kReadingListEntryActionsHistogram[] =
     "Mobile.ContextMenu.ReadingListEntry.Actions";
-const char RecentTabsEntryActionsHistogram[] =
+const char kRecentTabsEntryActionsHistogram[] =
     "Mobile.ContextMenu.RecentTabsEntry.Actions";
-const char RecentTabsHeaderActionsHistogram[] =
+const char kRecentTabsHeaderActionsHistogram[] =
     "Mobile.ContextMenu.RecentTabsHeader.Actions";
-const char ContentSuggestionsEntryActionsHistogram[] =
+const char kContentSuggestionsEntryActionsHistogram[] =
     "Mobile.ContextMenu.ContentSuggestionsEntry.Actions";
+const char kBookmarkFolderActionsHistogram[] =
+    "Mobile.ContextMenu.BookmarkFolder.Actions";
 }  // namespace
 
 void RecordMenuShown(MenuScenario scenario) {
@@ -40,12 +42,14 @@
     case MenuScenario::kBookmarkEntry:
       return kBookmarkEntryActionsHistogram;
     case MenuScenario::kReadingListEntry:
-      return ReadingListEntryActionsHistogram;
+      return kReadingListEntryActionsHistogram;
     case MenuScenario::kRecentTabsEntry:
-      return RecentTabsEntryActionsHistogram;
+      return kRecentTabsEntryActionsHistogram;
     case MenuScenario::kRecentTabsHeader:
-      return RecentTabsHeaderActionsHistogram;
+      return kRecentTabsHeaderActionsHistogram;
     case MenuScenario::kContentSuggestionsEntry:
-      return ContentSuggestionsEntryActionsHistogram;
+      return kContentSuggestionsEntryActionsHistogram;
+    case MenuScenario::kBookmarkFolder:
+      return kBookmarkFolderActionsHistogram;
   }
 }
diff --git a/ios/chrome/browser/ui/menu/resources/BUILD.gn b/ios/chrome/browser/ui/menu/resources/BUILD.gn
index f490f62..e5bbe0b 100644
--- a/ios/chrome/browser/ui/menu/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/menu/resources/BUILD.gn
@@ -67,3 +67,11 @@
     "share.imageset/share@3x.png",
   ]
 }
+
+imageset("move_folder") {
+  sources = [
+    "move_folder.imageset/Contents.json",
+    "move_folder.imageset/move_folder@2x.png",
+    "move_folder.imageset/move_folder@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/menu/resources/move_folder.imageset/Contents.json b/ios/chrome/browser/ui/menu/resources/move_folder.imageset/Contents.json
new file mode 100644
index 0000000..08ac2ef
--- /dev/null
+++ b/ios/chrome/browser/ui/menu/resources/move_folder.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+  "images": [
+    {
+      "idiom": "universal",
+      "filename": "move_folder@2x.png",
+      "scale": "2x"
+    },
+    {
+      "idiom": "universal",
+      "filename": "move_folder@3x.png",
+      "scale": "3x"
+    }
+  ],
+  "info": {
+    "author": "xcode",
+    "version": 1
+  },
+  "properties": {
+    "template-rendering-intent": "template"
+  }
+}
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/menu/resources/move_folder.imageset/move_folder@2x.png b/ios/chrome/browser/ui/menu/resources/move_folder.imageset/move_folder@2x.png
new file mode 100644
index 0000000..c62530cd
--- /dev/null
+++ b/ios/chrome/browser/ui/menu/resources/move_folder.imageset/move_folder@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/menu/resources/move_folder.imageset/move_folder@3x.png b/ios/chrome/browser/ui/menu/resources/move_folder.imageset/move_folder@3x.png
new file mode 100644
index 0000000..ef3716a35
--- /dev/null
+++ b/ios/chrome/browser/ui/menu/resources/move_folder.imageset/move_folder@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
index af53275..feedb0a 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
@@ -614,8 +614,8 @@
   }
 
   // If there is pasteboard content, show paste.
-  if (UIPasteboard.generalPasteboard.string.length > 0 && action == @selector
-                                                              (paste:)) {
+  if (UIPasteboard.generalPasteboard.hasStrings && action == @selector
+                                                       (paste:)) {
     return YES;
   }
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
index ae25572c..fa14adf 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
@@ -39,6 +39,8 @@
                  ChromeBrowserState* browser_state,
                  id<OmniboxCommands> omnibox_focuser);
 
+  ~OmniboxViewIOS() override;
+
   void SetPopupProvider(OmniboxPopupProvider* provider) {
     popup_provider_ = provider;
   }
@@ -49,6 +51,41 @@
       security_state::SecurityLevel security_level,
       bool in_dark_mode);
 
+  void OnReceiveClipboardURLForOpenMatch(
+      const AutocompleteMatch& match,
+      WindowOpenDisposition disposition,
+      const GURL& alternate_nav_url,
+      const base::string16& pasted_text,
+      size_t selected_line,
+      base::TimeTicks match_selection_timestamp,
+      base::Optional<GURL> optional_gurl);
+
+  void OnReceiveClipboardTextForOpenMatch(
+      const AutocompleteMatch& match,
+      WindowOpenDisposition disposition,
+      const GURL& alternate_nav_url,
+      const base::string16& pasted_text,
+      size_t selected_line,
+      base::TimeTicks match_selection_timestamp,
+      base::Optional<base::string16> optional_text);
+
+  void OnReceiveClipboardImageForOpenMatch(
+      const AutocompleteMatch& match,
+      WindowOpenDisposition disposition,
+      const GURL& alternate_nav_url,
+      const base::string16& pasted_text,
+      size_t selected_line,
+      base::TimeTicks match_selection_timestamp,
+      base::Optional<gfx::Image> optional_image);
+
+  void OnReceiveImageMatchForOpenMatch(
+      WindowOpenDisposition disposition,
+      const GURL& alternate_nav_url,
+      const base::string16& pasted_text,
+      size_t selected_line,
+      base::TimeTicks match_selection_timestamp,
+      base::Optional<AutocompleteMatch> optional_match);
+
   // OmniboxView implementation.
   void OpenMatch(const AutocompleteMatch& match,
                  WindowOpenDisposition disposition,
@@ -199,6 +236,9 @@
   NSMutableAttributedString* attributing_display_string_;
 
   OmniboxPopupProvider* popup_provider_;  // weak
+
+  // Used to cancel clipboard callbacks if this is deallocated;
+  base::WeakPtrFactory<OmniboxViewIOS> weak_ptr_factory_{this};
 };
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_VIEW_IOS_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
index 03a259ff..4725004a 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
@@ -17,10 +17,12 @@
 #include "base/strings/sys_string_conversions.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/clipboard_provider.h"
 #include "components/omnibox/browser/location_bar_model.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/omnibox/browser/omnibox_popup_model.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
+#include "components/open_from_clipboard/clipboard_recent_content.h"
 #include "ios/chrome/browser/autocomplete/autocomplete_scheme_classifier_impl.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/ui/commands/omnibox_commands.h"
@@ -95,6 +97,8 @@
                                   !base::ios::IsRunningOnOrLater(11, 2, 0);
 }
 
+OmniboxViewIOS::~OmniboxViewIOS() = default;
+
 void OmniboxViewIOS::OpenMatch(const AutocompleteMatch& match,
                                WindowOpenDisposition disposition,
                                const GURL& alternate_nav_url,
@@ -106,10 +110,125 @@
     return;
   }
 
+  // Fill in clipboard matches if they don't have a destination URL.
+  if (match.destination_url.is_empty()) {
+    if (match.type == AutocompleteMatchType::CLIPBOARD_URL) {
+      ClipboardRecentContent* clipboard_recent_content =
+          ClipboardRecentContent::GetInstance();
+      clipboard_recent_content->GetRecentURLFromClipboard(base::BindOnce(
+          &OmniboxViewIOS::OnReceiveClipboardURLForOpenMatch,
+          weak_ptr_factory_.GetWeakPtr(), match, disposition, alternate_nav_url,
+          pasted_text, selected_line, match_selection_timestamp));
+      return;
+    } else if (match.type == AutocompleteMatchType::CLIPBOARD_TEXT) {
+      ClipboardRecentContent* clipboard_recent_content =
+          ClipboardRecentContent::GetInstance();
+      clipboard_recent_content->GetRecentTextFromClipboard(base::BindOnce(
+          &OmniboxViewIOS::OnReceiveClipboardTextForOpenMatch,
+          weak_ptr_factory_.GetWeakPtr(), match, disposition, alternate_nav_url,
+          pasted_text, selected_line, match_selection_timestamp));
+      return;
+    } else if (match.type == AutocompleteMatchType::CLIPBOARD_IMAGE) {
+      ClipboardRecentContent* clipboard_recent_content =
+          ClipboardRecentContent::GetInstance();
+      clipboard_recent_content->GetRecentImageFromClipboard(base::BindOnce(
+          &OmniboxViewIOS::OnReceiveClipboardImageForOpenMatch,
+          weak_ptr_factory_.GetWeakPtr(), match, disposition, alternate_nav_url,
+          pasted_text, selected_line, match_selection_timestamp));
+      return;
+    }
+  }
+
   OmniboxView::OpenMatch(match, disposition, alternate_nav_url, pasted_text,
                          selected_line, match_selection_timestamp);
 }
 
+void OmniboxViewIOS::OnReceiveClipboardURLForOpenMatch(
+    const AutocompleteMatch& match,
+    WindowOpenDisposition disposition,
+    const GURL& alternate_nav_url,
+    const base::string16& pasted_text,
+    size_t selected_line,
+    base::TimeTicks match_selection_timestamp,
+    base::Optional<GURL> optional_gurl) {
+  if (!optional_gurl) {
+    return;
+  }
+
+  GURL url = std::move(optional_gurl).value();
+
+  ClipboardProvider* clipboard_provider =
+      model()->autocomplete_controller()->clipboard_provider();
+  AutocompleteMatch new_match = clipboard_provider->NewClipboardURLMatch(url);
+
+  OmniboxView::OpenMatch(new_match, disposition, alternate_nav_url, pasted_text,
+                         selected_line, match_selection_timestamp);
+}
+
+void OmniboxViewIOS::OnReceiveClipboardTextForOpenMatch(
+    const AutocompleteMatch& match,
+    WindowOpenDisposition disposition,
+    const GURL& alternate_nav_url,
+    const base::string16& pasted_text,
+    size_t selected_line,
+    base::TimeTicks match_selection_timestamp,
+    base::Optional<base::string16> optional_text) {
+  if (!optional_text) {
+    return;
+  }
+
+  base::string16 text = std::move(optional_text).value();
+
+  ClipboardProvider* clipboard_provider =
+      model()->autocomplete_controller()->clipboard_provider();
+  base::Optional<AutocompleteMatch> new_match =
+      clipboard_provider->NewClipboardTextMatch(text);
+
+  if (!new_match) {
+    return;
+  }
+
+  OmniboxView::OpenMatch(new_match.value(), disposition, alternate_nav_url,
+                         pasted_text, selected_line, match_selection_timestamp);
+}
+
+void OmniboxViewIOS::OnReceiveClipboardImageForOpenMatch(
+    const AutocompleteMatch& match,
+    WindowOpenDisposition disposition,
+    const GURL& alternate_nav_url,
+    const base::string16& pasted_text,
+    size_t selected_line,
+    base::TimeTicks match_selection_timestamp,
+    base::Optional<gfx::Image> optional_image) {
+  if (!optional_image) {
+    return;
+  }
+
+  gfx::Image image = std::move(optional_image).value();
+
+  ClipboardProvider* clipboard_provider =
+      model()->autocomplete_controller()->clipboard_provider();
+  clipboard_provider->NewClipboardImageMatch(
+      image, base::BindOnce(&OmniboxViewIOS::OnReceiveImageMatchForOpenMatch,
+                            weak_ptr_factory_.GetWeakPtr(), disposition,
+                            alternate_nav_url, pasted_text, selected_line,
+                            match_selection_timestamp));
+}
+
+void OmniboxViewIOS::OnReceiveImageMatchForOpenMatch(
+    WindowOpenDisposition disposition,
+    const GURL& alternate_nav_url,
+    const base::string16& pasted_text,
+    size_t selected_line,
+    base::TimeTicks match_selection_timestamp,
+    base::Optional<AutocompleteMatch> optional_match) {
+  if (!optional_match) {
+    return;
+  }
+  OmniboxView::OpenMatch(optional_match.value(), disposition, alternate_nav_url,
+                         pasted_text, selected_line, match_selection_timestamp);
+}
+
 base::string16 OmniboxViewIOS::GetText() const {
   return base::SysNSStringToUTF16([field_ displayedText]);
 }
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/confirm/confirm_infobar_banner_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_banner/confirm/confirm_infobar_banner_overlay_mediator.mm
index 2bd473e..2c31c4a0 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/confirm/confirm_infobar_banner_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/confirm/confirm_infobar_banner_overlay_mediator.mm
@@ -53,8 +53,6 @@
   if (!self.consumer || !config)
     return;
 
-  [self.consumer setBannerAccessibilityLabel:base::SysUTF16ToNSString(
-                                                 config->button_label_text())];
   [self.consumer
       setButtonText:base::SysUTF16ToNSString(config->button_label_text())];
   if (!config->icon_image().IsEmpty())
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/passwords/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_banner/passwords/BUILD.gn
index 199a1f9..059618a 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/passwords/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/passwords/BUILD.gn
@@ -46,6 +46,7 @@
     "//ios/chrome/browser/ui/infobars/banners/test",
     "//ios/chrome/browser/ui/infobars/test",
     "//ios/chrome/browser/ui/overlays/test",
+    "//ios/chrome/browser/ui/resources:password_key",
     "//testing/gmock",
     "//testing/gtest",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/passwords/save_password_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/passwords/save_password_infobar_banner_overlay_mediator_unittest.mm
index d28d9c8..95fca1b 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/passwords/save_password_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/passwords/save_password_infobar_banner_overlay_mediator_unittest.mm
@@ -84,7 +84,6 @@
               consumer.buttonText);
   EXPECT_NSEQ(title, consumer.titleText);
   EXPECT_NSEQ(subtitle, consumer.subtitleText);
-  EXPECT_NSEQ([UIImage imageNamed:@"infobar_passwords_icon"],
-              consumer.iconImage);
+  EXPECT_NSEQ([UIImage imageNamed:@"password_key"], consumer.iconImage);
   EXPECT_TRUE(consumer.presentsModal);
 }
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm
index c34954d6..0d72d5d1 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm
@@ -73,8 +73,6 @@
   if (!self.consumer || !config)
     return;
 
-  [self.consumer setBannerAccessibilityLabel:base::SysUTF16ToNSString(
-                                                 config->button_label_text())];
   [self.consumer
       setButtonText:base::SysUTF16ToNSString(self.config->button_label_text())];
   [self.consumer setIconImage:[UIImage imageNamed:config->icon_image_name()]];
diff --git a/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm b/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
index bbd872e..d52f9a17 100644
--- a/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
@@ -87,13 +87,14 @@
 
   CGPoint originPresentationCoordinates = [self.presentationProvider
       convertToPresentationCoordinatesForOrigin:self.originPoint];
+  // TODO(crbug.com/1045047): Use HandlerForProtocol() when BrowserCommands is
+  // broken up.
   self.pageInfoViewController = [[LegacyPageInfoViewController alloc]
              initWithModel:config
                sourcePoint:originPresentationCoordinates
       presentationProvider:self.presentationProvider
-                   handler:HandlerForProtocol(
-                               self.browser->GetCommandDispatcher(),
-                               BrowserCommands)];
+                   handler:static_cast<id<BrowserCommands>>(
+                               self.browser->GetCommandDispatcher())];
 }
 
 - (void)stop {
diff --git a/ios/chrome/browser/ui/resources/BUILD.gn b/ios/chrome/browser/ui/resources/BUILD.gn
index f22869b..ac7c768 100644
--- a/ios/chrome/browser/ui/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/resources/BUILD.gn
@@ -12,3 +12,11 @@
     "menu_shadow.imageset/menu_shadow@3x.png",
   ]
 }
+
+imageset("password_key") {
+  sources = [
+    "password_key.imageset/Contents.json",
+    "password_key.imageset/password_key@2x.png",
+    "password_key.imageset/password_key@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/Contents.json b/ios/chrome/browser/ui/resources/password_key.imageset/Contents.json
similarity index 67%
rename from ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/Contents.json
rename to ios/chrome/browser/ui/resources/password_key.imageset/Contents.json
index a31b671..fc983739 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/Contents.json
+++ b/ios/chrome/browser/ui/resources/password_key.imageset/Contents.json
@@ -3,12 +3,12 @@
         {
             "idiom": "universal",
             "scale": "2x",
-            "filename": "clear_browsing_data_passwords@2x.png"
+            "filename": "password_key@2x.png"
         },
         {
             "idiom": "universal",
             "scale": "3x",
-            "filename": "clear_browsing_data_passwords@3x.png"
+            "filename": "password_key@3x.png"
         }
     ],
     "info": {
diff --git a/ios/chrome/browser/ui/resources/password_key.imageset/password_key@2x.png b/ios/chrome/browser/ui/resources/password_key.imageset/password_key@2x.png
new file mode 100644
index 0000000..cc90103
--- /dev/null
+++ b/ios/chrome/browser/ui/resources/password_key.imageset/password_key@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/resources/password_key.imageset/password_key@3x.png b/ios/chrome/browser/ui/resources/password_key.imageset/password_key@3x.png
new file mode 100644
index 0000000..5f1fd300
--- /dev/null
+++ b/ios/chrome/browser/ui/resources/password_key.imageset/password_key@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 2ab85ca..44f3d49 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -88,7 +88,9 @@
     "resources:settings_passwords",
     "resources:settings_payment_methods",
     "resources:settings_privacy",
+    "resources:settings_safe_browsing",
     "resources:settings_safe_state",
+    "resources:settings_safety_check",
     "resources:settings_search_engine",
     "resources:settings_unsafe_state",
     "resources:settings_voice_search",
@@ -153,6 +155,7 @@
     "//ios/chrome/browser/ui/icons",
     "//ios/chrome/browser/ui/keyboard",
     "//ios/chrome/browser/ui/list_model",
+    "//ios/chrome/browser/ui/resources:password_key",
     "//ios/chrome/browser/ui/settings/autofill",
     "//ios/chrome/browser/ui/settings/cells",
     "//ios/chrome/browser/ui/settings/cells:public",
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/BUILD.gn b/ios/chrome/browser/ui/settings/clear_browsing_data/BUILD.gn
index c8fabea..0506d18 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/BUILD.gn
@@ -23,7 +23,6 @@
     "resources:clear_browsing_data_cached_images",
     "resources:clear_browsing_data_cookies",
     "resources:clear_browsing_data_history",
-    "resources:clear_browsing_data_passwords",
     "//components/browsing_data/core",
     "//components/feature_engagement/public",
     "//components/history/core/browser",
@@ -48,6 +47,7 @@
     "//ios/chrome/browser/ui/elements:elements_internal",
     "//ios/chrome/browser/ui/icons",
     "//ios/chrome/browser/ui/list_model",
+    "//ios/chrome/browser/ui/resources:password_key",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/cells",
     "//ios/chrome/browser/ui/table_view",
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
index 1e52c5a..47c7af9 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
@@ -90,8 +90,7 @@
       @"clear_browsing_data_cookies",
   [NSNumber numberWithInteger:ItemTypeDataTypeCache] :
       @"clear_browsing_data_cached_images",
-  [NSNumber numberWithInteger:ItemTypeDataTypeSavedPasswords] :
-      @"clear_browsing_data_passwords",
+  [NSNumber numberWithInteger:ItemTypeDataTypeSavedPasswords] : @"password_key",
   [NSNumber numberWithInteger:ItemTypeDataTypeAutofill] :
       @"clear_browsing_data_autofill",
 };
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/BUILD.gn b/ios/chrome/browser/ui/settings/clear_browsing_data/resources/BUILD.gn
index 7b2c9c50c..e6640f9 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/resources/BUILD.gn
@@ -35,11 +35,3 @@
     "clear_browsing_data_history.imageset/clear_browsing_data_history@3x.png",
   ]
 }
-
-imageset("clear_browsing_data_passwords") {
-  sources = [
-    "clear_browsing_data_passwords.imageset/Contents.json",
-    "clear_browsing_data_passwords.imageset/clear_browsing_data_passwords@2x.png",
-    "clear_browsing_data_passwords.imageset/clear_browsing_data_passwords@3x.png",
-  ]
-}
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/clear_browsing_data_passwords@2x.png b/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/clear_browsing_data_passwords@2x.png
deleted file mode 100644
index 5128543..0000000
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/clear_browsing_data_passwords@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/clear_browsing_data_passwords@3x.png b/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/clear_browsing_data_passwords@3x.png
deleted file mode 100644
index d78f4c77..0000000
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/clear_browsing_data_passwords@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
index d774aea7..2999a8c 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
@@ -89,7 +89,7 @@
 
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGrey checkSignedOut];
+  [SigninEarlGrey verifySignedOut];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -113,7 +113,7 @@
 
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGrey checkSignedOut];
+  [SigninEarlGrey verifySignedOut];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -148,7 +148,7 @@
                                               fakeIdentity2.userEmail),
                                           grey_sufficientlyVisible(), nil)]
       assertWithMatcher:grey_nil()];
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity1];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity1];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -177,7 +177,7 @@
   // Check that the user is signed out and the Main Settings screen is shown.
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGrey checkSignedOut];
+  [SigninEarlGrey verifySignedOut];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -290,7 +290,7 @@
   [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                           SettingsAccountsCollectionView()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
index 1e87a1b..88e7a8e9 100644
--- a/ios/chrome/browser/ui/settings/password/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -5,9 +5,9 @@
 source_set("password") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "password_details_table_view_controller.h",
-    "password_details_table_view_controller.mm",
-    "password_details_table_view_controller_delegate.h",
+    "legacy_password_details_table_view_controller.h",
+    "legacy_password_details_table_view_controller.mm",
+    "legacy_password_details_table_view_controller_delegate.h",
     "password_exporter.h",
     "password_exporter.mm",
     "password_issue_with_form.h",
@@ -111,7 +111,7 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
-    "password_details_table_view_controller+testing.h",
+    "legacy_password_details_table_view_controller+testing.h",
     "password_exporter_for_testing.h",
   ]
   deps = [ ":password" ]
@@ -121,7 +121,7 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
-    "password_details_table_view_controller_unittest.mm",
+    "legacy_password_details_table_view_controller_unittest.mm",
     "password_exporter_unittest.mm",
     "password_issues_mediator_unittest.mm",
     "password_issues_table_view_controller_unittest.mm",
diff --git a/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller+testing.h b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller+testing.h
new file mode 100644
index 0000000..be2d212c
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller+testing.h
@@ -0,0 +1,22 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
+
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h"
+
+// TODO(crbug.com/943523): Refactor the PasswordTableViewController and
+// PasswordsSettingsTestCase to remove this Category file.
+@interface LegacyPasswordDetailsTableViewController (Testing)
+
+// Allows to replace a |reauthenticationModule| for a fake one in integration
+// tests, where the testing code cannot control the creation of the
+// controller.
+- (void)setReauthenticationModule:
+    (id<ReauthenticationProtocol>)reauthenticationModule;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
diff --git a/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h
new file mode 100644
index 0000000..965066db
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
+
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
+
+namespace autofill {
+struct PasswordForm;
+}  // namespace autofill
+
+@protocol ReauthenticationProtocol;
+
+// TODO(crbug.com/1096986): Delete this view controller after Password Check
+// launch.
+
+// Displays details of a password item, including URL of the site, username and
+// password in masked state as default. User can copy the URL and username,
+// pass the iOS security check to see and copy the password , or delete the
+// password item.
+@interface LegacyPasswordDetailsTableViewController
+    : SettingsRootTableViewController
+
+// The designated initializer.
+- (nullable instancetype)
+      initWithPasswordForm:(const autofill::PasswordForm&)passwordForm
+                  delegate:
+                      (nonnull
+                           id<LegacyPasswordDetailsTableViewControllerDelegate>)
+                          delegate
+    reauthenticationModule:
+        (nonnull id<ReauthenticationProtocol>)reauthenticationModule
+    NS_DESIGNATED_INITIALIZER;
+
+- (nullable instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.mm
similarity index 93%
rename from ios/chrome/browser/ui/settings/password/password_details_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.mm
index be4593d..8469733 100644
--- a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h"
 
 #import <UIKit/UIKit.h>
 
@@ -66,7 +66,7 @@
 
 }  // namespace
 
-@interface PasswordDetailsTableViewController () {
+@interface LegacyPasswordDetailsTableViewController () {
   // The username to which the saved password belongs.
   NSString* _username;
   // The saved password.
@@ -92,21 +92,22 @@
 
 // Instance of the parent view controller needed in order to update the
 // password list when a password is deleted.
-@property(nonatomic, weak) id<PasswordDetailsTableViewControllerDelegate>
+@property(nonatomic, weak) id<LegacyPasswordDetailsTableViewControllerDelegate>
     delegate;
 
 @end
 
-@implementation PasswordDetailsTableViewController
+@implementation LegacyPasswordDetailsTableViewController
 
 @synthesize deleteConfirmation = _deleteConfirmation;
 
-- (instancetype)initWithPasswordForm:(const autofill::PasswordForm&)passwordForm
-                            delegate:
-                                (id<PasswordDetailsTableViewControllerDelegate>)
-                                    delegate
-              reauthenticationModule:
-                  (id<ReauthenticationProtocol>)reauthenticationModule {
+- (instancetype)
+      initWithPasswordForm:(const autofill::PasswordForm&)passwordForm
+                  delegate:
+                      (id<LegacyPasswordDetailsTableViewControllerDelegate>)
+                          delegate
+    reauthenticationModule:
+        (id<ReauthenticationProtocol>)reauthenticationModule {
   DCHECK(delegate);
   DCHECK(reauthenticationModule);
   UITableViewStyle style = base::FeatureList::IsEnabled(kSettingsRefresh)
@@ -349,25 +350,25 @@
   }
 
   if ([_weakReauthenticationModule canAttemptReauth]) {
-    __weak PasswordDetailsTableViewController* weakSelf = self;
-    void (^showPasswordHandler)(ReauthenticationResult) = ^(
-        ReauthenticationResult result) {
-      PasswordDetailsTableViewController* strongSelf = weakSelf;
-      if (!strongSelf)
-        return;
-      [strongSelf logPasswordSettingsReauthResult:result];
-      if (result == ReauthenticationResult::kFailure)
-        return;
-      TableViewTextItem* passwordItem = strongSelf->_passwordItem;
-      passwordItem.masked = NO;
-      [strongSelf reconfigureCellsForItems:@[ passwordItem ]];
-      strongSelf->_plainTextPasswordShown = YES;
-      [strongSelf toggleShowHideButton];
-      UMA_HISTOGRAM_ENUMERATION(
-          "PasswordManager.AccessPasswordInSettings",
-          password_manager::metrics_util::ACCESS_PASSWORD_VIEWED,
-          password_manager::metrics_util::ACCESS_PASSWORD_COUNT);
-    };
+    __weak LegacyPasswordDetailsTableViewController* weakSelf = self;
+    void (^showPasswordHandler)(ReauthenticationResult) =
+        ^(ReauthenticationResult result) {
+          LegacyPasswordDetailsTableViewController* strongSelf = weakSelf;
+          if (!strongSelf)
+            return;
+          [strongSelf logPasswordSettingsReauthResult:result];
+          if (result == ReauthenticationResult::kFailure)
+            return;
+          TableViewTextItem* passwordItem = strongSelf->_passwordItem;
+          passwordItem.masked = NO;
+          [strongSelf reconfigureCellsForItems:@[ passwordItem ]];
+          strongSelf->_plainTextPasswordShown = YES;
+          [strongSelf toggleShowHideButton];
+          UMA_HISTOGRAM_ENUMERATION(
+              "PasswordManager.AccessPasswordInSettings",
+              password_manager::metrics_util::ACCESS_PASSWORD_VIEWED,
+              password_manager::metrics_util::ACCESS_PASSWORD_COUNT);
+        };
 
     [_weakReauthenticationModule
         attemptReauthWithLocalizedReason:
@@ -409,10 +410,10 @@
     password_manager::metrics_util::LogPasswordSettingsReauthResult(
         password_manager::metrics_util::ReauthResult::kSkipped);
   } else if ([_weakReauthenticationModule canAttemptReauth]) {
-    __weak PasswordDetailsTableViewController* weakSelf = self;
+    __weak LegacyPasswordDetailsTableViewController* weakSelf = self;
     void (^copyPasswordHandler)(ReauthenticationResult) = ^(
         ReauthenticationResult result) {
-      PasswordDetailsTableViewController* strongSelf = weakSelf;
+      LegacyPasswordDetailsTableViewController* strongSelf = weakSelf;
       if (!strongSelf)
         return;
       [strongSelf logPasswordSettingsReauthResult:result];
@@ -491,7 +492,7 @@
 
 // Deletes the password with a deletion confirmation alert.
 - (void)deletePassword {
-  __weak PasswordDetailsTableViewController* weakSelf = self;
+  __weak LegacyPasswordDetailsTableViewController* weakSelf = self;
 
   self.deleteConfirmation = [UIAlertController
       alertControllerWithTitle:nil
diff --git a/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h
new file mode 100644
index 0000000..d9c5e1a36
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h
@@ -0,0 +1,27 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+namespace autofill {
+struct PasswordForm;
+}  // namespace autofill
+
+@class LegacyPasswordDetailsTableViewController;
+
+// PasswordDetailsTableViewController uses this protocol to interact with higher
+// level password controller.
+@protocol LegacyPasswordDetailsTableViewControllerDelegate
+
+- (void)passwordDetailsTableViewController:
+            (LegacyPasswordDetailsTableViewController*)controller
+                            deletePassword:
+                                (const autofill::PasswordForm&)passwordForm;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_LEGACY_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_unittest.mm
similarity index 91%
rename from ios/chrome/browser/ui/settings/password/password_details_table_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_unittest.mm
index 5f49f10..77b64ade 100644
--- a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h"
 
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
@@ -47,10 +47,10 @@
 const int kDeleteSection = 3;
 const int kDeleteButtonItem = 0;
 
-class PasswordDetailsTableViewControllerTest
+class LegacyPasswordDetailsTableViewControllerTest
     : public ChromeTableViewControllerTest {
  protected:
-  PasswordDetailsTableViewControllerTest() {
+  LegacyPasswordDetailsTableViewControllerTest() {
     origin_ = kSite;
     form_.username_value = base::SysNSStringToUTF16(kUsername);
     form_.password_value = base::SysNSStringToUTF16(kPassword);
@@ -65,10 +65,11 @@
   }
 
   ChromeTableViewController* InstantiateController() override {
-    return [[PasswordDetailsTableViewController alloc]
+    return [[LegacyPasswordDetailsTableViewController alloc]
           initWithPasswordForm:form_
-                      delegate:OCMProtocolMock(@protocol(
-                                   PasswordDetailsTableViewControllerDelegate))
+                      delegate:
+                          OCMProtocolMock(@protocol(
+                              LegacyPasswordDetailsTableViewControllerDelegate))
         reauthenticationModule:reauthentication_module_];
   }
 
@@ -78,7 +79,7 @@
   autofill::PasswordForm form_;
 };
 
-TEST_F(PasswordDetailsTableViewControllerTest,
+TEST_F(LegacyPasswordDetailsTableViewControllerTest,
        TestInitialization_NormalPassword) {
   CreateController();
   CheckController();
@@ -119,7 +120,8 @@
                           kDeleteSection, kDeleteButtonItem);
 }
 
-TEST_F(PasswordDetailsTableViewControllerTest, TestInitialization_Blocked) {
+TEST_F(LegacyPasswordDetailsTableViewControllerTest,
+       TestInitialization_Blocked) {
   constexpr int kBlockedSiteSection = 0;
   constexpr int kBlockedSiteItem = 0;
   constexpr int kBlockedCopySiteButtonItem = 1;
@@ -149,7 +151,8 @@
                           kBlockedDeleteSection, kBlockedDeleteButtonItem);
 }
 
-TEST_F(PasswordDetailsTableViewControllerTest, TestInitialization_Federated) {
+TEST_F(LegacyPasswordDetailsTableViewControllerTest,
+       TestInitialization_Federated) {
   constexpr int kFederatedSiteSection = 0;
   constexpr int kFederatedSiteItem = 0;
   constexpr int kFederatedCopySiteButtonItem = 1;
@@ -210,7 +213,7 @@
   NSString* expectedSimplifiedOrigin;
 };
 
-TEST_F(PasswordDetailsTableViewControllerTest, SimplifyOrigin) {
+TEST_F(LegacyPasswordDetailsTableViewControllerTest, SimplifyOrigin) {
   SimplifyOriginTestData test_data[] = {
       {GURL("http://test.com/index.php"), @"test.com"},
       {GURL("https://example.com/index.php"), @"example.com"},
@@ -230,7 +233,7 @@
   }
 }
 
-TEST_F(PasswordDetailsTableViewControllerTest, CopySite) {
+TEST_F(LegacyPasswordDetailsTableViewControllerTest, CopySite) {
   CreateController();
   [controller() tableView:[controller() tableView]
       didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:kCopySiteButtonItem
@@ -239,7 +242,7 @@
   EXPECT_NSEQ(origin_, generalPasteboard.string);
 }
 
-TEST_F(PasswordDetailsTableViewControllerTest, CopyUsername) {
+TEST_F(LegacyPasswordDetailsTableViewControllerTest, CopyUsername) {
   CreateController();
   [controller() tableView:[controller() tableView]
       didSelectRowAtIndexPath:[NSIndexPath
@@ -249,7 +252,7 @@
   EXPECT_NSEQ(kUsername, generalPasteboard.string);
 }
 
-TEST_F(PasswordDetailsTableViewControllerTest, ShowPassword) {
+TEST_F(LegacyPasswordDetailsTableViewControllerTest, ShowPassword) {
   CreateController();
   [controller() tableView:[controller() tableView]
       didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:kShowHideButtonItem
@@ -265,7 +268,7 @@
                           kPasswordSection, kShowHideButtonItem);
 }
 
-TEST_F(PasswordDetailsTableViewControllerTest, HidePassword) {
+TEST_F(LegacyPasswordDetailsTableViewControllerTest, HidePassword) {
   CreateController();
   // First show the password.
   [controller() tableView:[controller() tableView]
@@ -283,7 +286,7 @@
                           kPasswordSection, kShowHideButtonItem);
 }
 
-TEST_F(PasswordDetailsTableViewControllerTest, CopyPassword) {
+TEST_F(LegacyPasswordDetailsTableViewControllerTest, CopyPassword) {
   CreateController();
   [controller() tableView:[controller() tableView]
       didSelectRowAtIndexPath:[NSIndexPath
diff --git a/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn b/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
index 4a56a48..27c7271 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
@@ -43,9 +43,9 @@
     "password_details_handler.h",
     "password_details_table_view_constants.h",
     "password_details_table_view_constants.mm",
-    "password_details_view_controller.h",
-    "password_details_view_controller.mm",
-    "password_details_view_controller_delegate.h",
+    "password_details_table_view_controller.h",
+    "password_details_table_view_controller.mm",
+    "password_details_table_view_controller_delegate.h",
   ]
   deps = [
     "//base",
@@ -75,7 +75,7 @@
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
-  sources = [ "password_details_view_controller_unittest.mm" ]
+  sources = [ "password_details_table_view_controller_unittest.mm" ]
   deps = [
     ":password_details",
     ":password_details_ui",
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.mm
index eccaf2c..1326a0b 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.mm
@@ -17,7 +17,7 @@
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_handler.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.h"
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -35,7 +35,7 @@
 }
 
 // Main view controller for this coordinator.
-@property(nonatomic, strong) PasswordDetailsViewController* viewController;
+@property(nonatomic, strong) PasswordDetailsTableViewController* viewController;
 
 // Main mediator for this coordinator.
 @property(nonatomic, strong) PasswordDetailsMediator* mediator;
@@ -83,7 +83,7 @@
                                : UITableViewStyleGrouped;
 
   self.viewController =
-      [[PasswordDetailsViewController alloc] initWithStyle:style];
+      [[PasswordDetailsTableViewController alloc] initWithStyle:style];
 
   self.mediator = [[PasswordDetailsMediator alloc] initWithPassword:_password
                                                passwordCheckManager:_manager];
@@ -105,7 +105,7 @@
 
 #pragma mark - PasswordDetailsHandler
 
-- (void)passwordDetailsViewControllerDidDisappear {
+- (void)passwordDetailsTableViewControllerDidDisappear {
   [self.delegate passwordDetailsCoordinatorDidRemove:self];
 }
 
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_handler.h b/ios/chrome/browser/ui/settings/password/password_details/password_details_handler.h
index c276ca9..4245539c 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_handler.h
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_handler.h
@@ -9,7 +9,7 @@
 @protocol PasswordDetailsHandler
 
 // Called when the view controller was dismissed.
-- (void)passwordDetailsViewControllerDidDisappear;
+- (void)passwordDetailsTableViewControllerDidDisappear;
 
 // Shows a dialog offering the user to set a passcode in order to see the
 // password.
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.h b/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.h
index 73341f3..60aefde 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.h
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.h
@@ -7,7 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_delegate.h"
 
 class IOSChromePasswordCheckManager;
 @protocol PasswordDetailsConsumer;
@@ -18,7 +18,7 @@
 
 // This mediator fetches and organises the credentials for its consumer.
 @interface PasswordDetailsMediator
-    : NSObject <PasswordDetailsViewControllerDelegate>
+    : NSObject <PasswordDetailsTableViewControllerDelegate>
 
 // PasswordForm is converted to the PasswordDetails and passed to a consumer.
 - (instancetype)initWithPassword:(const autofill::PasswordForm&)passwordForm
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm
index 7c75595..0e940ea 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm
@@ -9,7 +9,7 @@
 #include "ios/chrome/browser/passwords/password_check_observer_bridge.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_consumer.h"
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_delegate.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -18,8 +18,9 @@
 using CompromisedCredentialsView =
     password_manager::CompromisedCredentialsManager::CredentialsView;
 
-@interface PasswordDetailsMediator () <PasswordCheckObserver,
-                                       PasswordDetailsViewControllerDelegate> {
+@interface PasswordDetailsMediator () <
+    PasswordCheckObserver,
+    PasswordDetailsTableViewControllerDelegate> {
   // Password Check manager.
   IOSChromePasswordCheckManager* _manager;
 
@@ -55,9 +56,10 @@
   _manager->RemoveObserver(_passwordCheckObserver.get());
 }
 
-#pragma mark - PasswordDetailsViewControllerDelegate
+#pragma mark - PasswordDetailsTableViewControllerDelegate
+
 - (void)passwordDetailsViewController:
-            (PasswordDetailsViewController*)viewController
+            (PasswordDetailsTableViewController*)viewController
                didEditPasswordDetails:(PasswordDetails*)password {
   if ([password.password length] != 0) {
     password.compromised
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.h b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.h
similarity index 80%
rename from ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.h
rename to ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.h
index 763ab1d0..ae34cc9 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.h
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.h
@@ -2,26 +2,27 @@
 // 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_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
 
 #import "ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_consumer.h"
 
 @protocol ApplicationCommands;
 @protocol PasswordDetailsHandler;
-@protocol PasswordDetailsViewControllerDelegate;
+@protocol PasswordDetailsTableViewControllerDelegate;
 @protocol ReauthenticationProtocol;
 
 // Screen which shows password details and allows to edit it.
-@interface PasswordDetailsViewController
+@interface PasswordDetailsTableViewController
     : AutofillEditTableViewController <PasswordDetailsConsumer>
 
 // Handler for PasswordDetails related actions.
 @property(nonatomic, weak) id<PasswordDetailsHandler> handler;
 
 // Delegate for PasswordDetails related actions e.g. Password editing.
-@property(nonatomic, weak) id<PasswordDetailsViewControllerDelegate> delegate;
+@property(nonatomic, weak) id<PasswordDetailsTableViewControllerDelegate>
+    delegate;
 
 // Dispatcher for this ViewController.
 @property(nonatomic, weak) id<ApplicationCommands> commandsDispatcher;
@@ -35,4 +36,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
similarity index 97%
rename from ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.mm
rename to ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
index 03e9570f3..8c50090 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.h"
 
 #include "base/ios/ios_util.h"
 #include "base/mac/foundation_util.h"
@@ -16,7 +16,7 @@
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_consumer.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_handler.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_constants.h"
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
@@ -61,7 +61,7 @@
 
 }  // namespace
 
-@interface PasswordDetailsViewController ()
+@interface PasswordDetailsTableViewController ()
 
 // Password which is shown on the screen.
 @property(nonatomic, strong) PasswordDetails* password;
@@ -74,7 +74,7 @@
 
 @end
 
-@implementation PasswordDetailsViewController
+@implementation PasswordDetailsTableViewController
 
 #pragma mark - UIViewController
 
@@ -92,7 +92,7 @@
 }
 
 - (void)viewDidDisappear:(BOOL)animated {
-  [self.handler passwordDetailsViewControllerDidDisappear];
+  [self.handler passwordDetailsTableViewControllerDidDisappear];
   [super viewDidDisappear:animated];
 }
 
@@ -349,7 +349,7 @@
     __weak __typeof(self) weakSelf = self;
     void (^showPasswordHandler)(ReauthenticationResult) =
         ^(ReauthenticationResult result) {
-          PasswordDetailsViewController* strongSelf = weakSelf;
+          PasswordDetailsTableViewController* strongSelf = weakSelf;
           if (!strongSelf)
             return;
           [strongSelf logPasswordSettingsReauthResult:result];
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_delegate.h b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_delegate.h
similarity index 63%
rename from ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_delegate.h
rename to ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_delegate.h
index 85e8a17..4567e4f7 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_delegate.h
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_delegate.h
@@ -2,19 +2,19 @@
 // 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_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_VIEW_CONTROLLER_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_VIEW_CONTROLLER_DELEGATE_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
 
 @class PasswordDetails;
-@class PasswordDetailsViewController;
+@class PasswordDetailsTableViewController;
 
-@protocol PasswordDetailsViewControllerDelegate
+@protocol PasswordDetailsTableViewControllerDelegate
 
 // Called when user finished editing a password.
 - (void)passwordDetailsViewController:
-            (PasswordDetailsViewController*)viewController
+            (PasswordDetailsTableViewController*)viewController
                didEditPasswordDetails:(PasswordDetails*)password;
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_VIEW_CONTROLLER_DELEGATE_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_unittest.mm
similarity index 81%
rename from ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_unittest.mm
index 7d91e77..7ed767f 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.h"
 
 #include <memory>
 
@@ -15,7 +15,7 @@
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_consumer.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_handler.h"
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h"
@@ -44,8 +44,7 @@
 
 @implementation FakePasswordDetailsHandler
 
-// Called when the view controller was dismissed.
-- (void)passwordDetailsViewControllerDidDisappear {
+- (void)passwordDetailsTableViewControllerDidDisappear {
 }
 
 - (void)showPasscodeDialog {
@@ -64,7 +63,7 @@
 // Test class that conforms to PasswordDetailsViewControllerDelegate in order to
 // test the delegate methods are called correctly.
 @interface FakePasswordDetailsDelegate
-    : NSObject <PasswordDetailsViewControllerDelegate>
+    : NSObject <PasswordDetailsTableViewControllerDelegate>
 
 @property(nonatomic, strong) PasswordDetails* password;
 
@@ -73,7 +72,7 @@
 @implementation FakePasswordDetailsDelegate
 
 - (void)passwordDetailsViewController:
-            (PasswordDetailsViewController*)viewController
+            (PasswordDetailsTableViewController*)viewController
                didEditPasswordDetails:(PasswordDetails*)password {
   self.password = password;
 }
@@ -81,9 +80,10 @@
 @end
 
 // Unit tests for PasswordIssuesTableViewController.
-class PasswordDetailsViewControllerTest : public ChromeTableViewControllerTest {
+class PasswordDetailsTableViewControllerTest
+    : public ChromeTableViewControllerTest {
  protected:
-  PasswordDetailsViewControllerTest() {
+  PasswordDetailsTableViewControllerTest() {
     handler_ = [[FakePasswordDetailsHandler alloc] init];
     delegate_ = [[FakePasswordDetailsDelegate alloc] init];
     reauthentication_module_ = [[MockReauthenticationModule alloc] init];
@@ -91,8 +91,8 @@
   }
 
   ChromeTableViewController* InstantiateController() override {
-    PasswordDetailsViewController* controller =
-        [[PasswordDetailsViewController alloc]
+    PasswordDetailsTableViewController* controller =
+        [[PasswordDetailsTableViewController alloc]
             initWithStyle:UITableViewStylePlain];
     controller.handler = handler_;
     controller.delegate = delegate_;
@@ -115,8 +115,8 @@
         [[PasswordDetails alloc] initWithPasswordForm:form];
     password.compromised = isCompromised;
 
-    PasswordDetailsViewController* passwords_controller =
-        static_cast<PasswordDetailsViewController*>(controller());
+    PasswordDetailsTableViewController* passwords_controller =
+        static_cast<PasswordDetailsTableViewController*>(controller());
     [passwords_controller setPassword:password];
   }
 
@@ -146,9 +146,9 @@
   MockReauthenticationModule* reauthentication_module_;
 };
 
-// Tests PasswordDetailsViewController is set up with appropriate items
+// Tests PasswordDetailsTableViewController is set up with appropriate items
 // and sections.
-TEST_F(PasswordDetailsViewControllerTest, TestModel) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestModel) {
   CreateController();
   CheckController();
   EXPECT_EQ(1, NumberOfSections());
@@ -157,7 +157,7 @@
 }
 
 // Tests that password is displayed properly.
-TEST_F(PasswordDetailsViewControllerTest, TestPassword) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestPassword) {
   SetPassword();
   EXPECT_EQ(1, NumberOfSections());
   EXPECT_EQ(3, NumberOfItemsInSection(0));
@@ -168,7 +168,7 @@
 }
 
 // Tests that compromised password is displayed properly.
-TEST_F(PasswordDetailsViewControllerTest, TestCompromisedPassword) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestCompromisedPassword) {
   SetPassword(true);
   EXPECT_EQ(2, NumberOfSections());
   EXPECT_EQ(3, NumberOfItemsInSection(0));
@@ -184,7 +184,7 @@
 }
 
 // Tests that password is shown/hidden.
-TEST_F(PasswordDetailsViewControllerTest, TestShowHidePassword) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestShowHidePassword) {
   SetPassword();
   CheckEditCellText(kMaskedPassword, 0, 2);
 
@@ -208,7 +208,7 @@
 }
 
 // Tests that passwords was not shown in case reauth failed.
-TEST_F(PasswordDetailsViewControllerTest, TestShowPasswordReauthFailed) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestShowPasswordReauthFailed) {
   SetPassword();
 
   CheckEditCellText(kMaskedPassword, 0, 2);
@@ -227,12 +227,13 @@
 }
 
 // Tests that password was revealed during editing.
-TEST_F(PasswordDetailsViewControllerTest, TestPasswordShownDuringEditing) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestPasswordShownDuringEditing) {
   SetPassword();
   CheckEditCellText(kMaskedPassword, 0, 2);
 
-  PasswordDetailsViewController* passwordDetails =
-      base::mac::ObjCCastStrict<PasswordDetailsViewController>(controller());
+  PasswordDetailsTableViewController* passwordDetails =
+      base::mac::ObjCCastStrict<PasswordDetailsTableViewController>(
+          controller());
   [passwordDetails editButtonPressed];
   EXPECT_TRUE(passwordDetails.tableView.editing);
   CheckEditCellText(@"test", 0, 2);
@@ -243,25 +244,27 @@
 }
 
 // Tests that editing mode was not entered because reauth failed.
-TEST_F(PasswordDetailsViewControllerTest, TestEditingReauthFailed) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestEditingReauthFailed) {
   SetPassword();
   CheckEditCellText(kMaskedPassword, 0, 2);
 
   reauth().expectedResult = ReauthenticationResult::kFailure;
-  PasswordDetailsViewController* passwordDetails =
-      base::mac::ObjCCastStrict<PasswordDetailsViewController>(controller());
+  PasswordDetailsTableViewController* passwordDetails =
+      base::mac::ObjCCastStrict<PasswordDetailsTableViewController>(
+          controller());
   [passwordDetails editButtonPressed];
   EXPECT_FALSE(passwordDetails.tableView.editing);
   CheckEditCellText(kMaskedPassword, 0, 2);
 }
 
 // Tests that delete button trigger showing password delete dialog.
-TEST_F(PasswordDetailsViewControllerTest, TestPasswordDelete) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestPasswordDelete) {
   SetPassword();
 
   EXPECT_FALSE(handler().deletionCalled);
-  PasswordDetailsViewController* passwordDetails =
-      base::mac::ObjCCastStrict<PasswordDetailsViewController>(controller());
+  PasswordDetailsTableViewController* passwordDetails =
+      base::mac::ObjCCastStrict<PasswordDetailsTableViewController>(
+          controller());
   [passwordDetails editButtonPressed];
   [[UIApplication sharedApplication]
       sendAction:passwordDetails.deleteButton.action
@@ -272,11 +275,12 @@
 }
 
 // Tests password editing. User confirmed this action.
-TEST_F(PasswordDetailsViewControllerTest, TestEditPasswordConfirmed) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestEditPasswordConfirmed) {
   SetPassword();
 
-  PasswordDetailsViewController* passwordDetails =
-      base::mac::ObjCCastStrict<PasswordDetailsViewController>(controller());
+  PasswordDetailsTableViewController* passwordDetails =
+      base::mac::ObjCCastStrict<PasswordDetailsTableViewController>(
+          controller());
   [passwordDetails editButtonPressed];
   EXPECT_FALSE(handler().editingCalled);
   EXPECT_FALSE(delegate().password);
@@ -297,11 +301,12 @@
 }
 
 // Tests  password editing. User cancelled this action.
-TEST_F(PasswordDetailsViewControllerTest, TestEditPasswordCancel) {
+TEST_F(PasswordDetailsTableViewControllerTest, TestEditPasswordCancel) {
   SetPassword();
 
-  PasswordDetailsViewController* passwordDetails =
-      base::mac::ObjCCastStrict<PasswordDetailsViewController>(controller());
+  PasswordDetailsTableViewController* passwordDetails =
+      base::mac::ObjCCastStrict<PasswordDetailsTableViewController>(
+          controller());
   [passwordDetails editButtonPressed];
   EXPECT_FALSE(delegate().password);
   EXPECT_TRUE(passwordDetails.tableView.editing);
diff --git a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller+testing.h b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller+testing.h
deleted file mode 100644
index c8c1386b..0000000
--- a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller+testing.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
-
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
-
-// TODO(crbug.com/943523): Refactor the PasswordTableViewController and
-// PasswordsSettingsTestCase to remove this Category file.
-@interface PasswordDetailsTableViewController (Testing)
-
-// Allows to replace a |reauthenticationModule| for a fake one in integration
-// tests, where the testing code cannot control the creation of the
-// controller.
-- (void)setReauthenticationModule:
-    (id<ReauthenticationProtocol>)reauthenticationModule;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_TESTING_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h
deleted file mode 100644
index 160d538d..0000000
--- a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
-
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h"
-#import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
-
-namespace autofill {
-struct PasswordForm;
-}  // namespace autofill
-
-@protocol ReauthenticationProtocol;
-
-// Displays details of a password item, including URL of the site, username and
-// password in masked state as default. User can copy the URL and username,
-// pass the iOS security check to see and copy the password , or delete the
-// password item.
-@interface PasswordDetailsTableViewController : SettingsRootTableViewController
-
-// The designated initializer.
-- (nullable instancetype)
-      initWithPasswordForm:(const autofill::PasswordForm&)passwordForm
-                  delegate:
-                      (nonnull id<PasswordDetailsTableViewControllerDelegate>)
-                          delegate
-    reauthenticationModule:
-        (nonnull id<ReauthenticationProtocol>)reauthenticationModule
-    NS_DESIGNATED_INITIALIZER;
-
-- (nullable instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h b/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h
deleted file mode 100644
index 4373925..0000000
--- a/ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
-
-#import <Foundation/Foundation.h>
-
-namespace autofill {
-struct PasswordForm;
-}  // namespace autofill
-
-@class PasswordDetailsTableViewController;
-
-// PasswordDetailsTableViewController uses this protocal to interact with higher
-// level password controller.
-@protocol PasswordDetailsTableViewControllerDelegate
-
-- (void)passwordDetailsTableViewController:
-            (PasswordDetailsTableViewController*)controller
-                            deletePassword:
-                                (const autofill::PasswordForm&)passwordForm;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
index f53420e..e5edaac 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
@@ -5,7 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
 #define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
 
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/settings/settings_controller_protocol.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
@@ -28,7 +28,7 @@
 @end
 
 @interface PasswordsTableViewController (Testing) <
-    PasswordDetailsTableViewControllerDelegate>
+    LegacyPasswordDetailsTableViewControllerDelegate>
 
 // Initializes the password exporter with a (fake) |reauthenticationModule|.
 - (void)setReauthenticationModuleForExporter:
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
index 1c289c8..0c2a5a6 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
@@ -49,8 +49,8 @@
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
 #import "ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/settings/password/password_exporter.h"
 #import "ios/chrome/browser/ui/settings/password/password_issues_coordinator.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
@@ -177,7 +177,7 @@
 @interface PasswordsTableViewController () <
     BooleanObserver,
     ChromeIdentityServiceObserver,
-    PasswordDetailsTableViewControllerDelegate,
+    LegacyPasswordDetailsTableViewControllerDelegate,
     PasswordExporterDelegate,
     PasswordExportActivityViewControllerDelegate,
     PasswordsConsumer,
@@ -1208,8 +1208,8 @@
 }
 
 - (void)openDetailedViewForForm:(const autofill::PasswordForm&)form {
-  PasswordDetailsTableViewController* controller =
-      [[PasswordDetailsTableViewController alloc]
+  LegacyPasswordDetailsTableViewController* controller =
+      [[LegacyPasswordDetailsTableViewController alloc]
             initWithPasswordForm:form
                         delegate:self
           reauthenticationModule:_reauthenticationModule];
@@ -1462,10 +1462,10 @@
   return cell;
 }
 
-#pragma mark PasswordDetailsTableViewControllerDelegate
+#pragma mark LegacyPasswordDetailsTableViewControllerDelegate
 
 - (void)passwordDetailsTableViewController:
-            (PasswordDetailsTableViewController*)controller
+            (LegacyPasswordDetailsTableViewController*)controller
                             deletePassword:(const autofill::PasswordForm&)form {
   _passwordStore->RemoveLogin(form);
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
index e1ecbf0..2c3479d 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
@@ -25,7 +25,7 @@
 #include "ios/chrome/browser/passwords/password_check_observer_bridge.h"
 #include "ios/chrome/browser/passwords/save_passwords_consumer.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_check_item.h"
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/password/password_issues_coordinator.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
diff --git a/ios/chrome/browser/ui/settings/resources/BUILD.gn b/ios/chrome/browser/ui/settings/resources/BUILD.gn
index 9c89d8e..43bca9f 100644
--- a/ios/chrome/browser/ui/settings/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/resources/BUILD.gn
@@ -128,6 +128,14 @@
   ]
 }
 
+imageset("settings_safe_browsing") {
+  sources = [
+    "settings_safe_browsing.imageset/Contents.json",
+    "settings_safe_browsing.imageset/settings_safe_browsing@2x.png",
+    "settings_safe_browsing.imageset/settings_safe_browsing@3x.png",
+  ]
+}
+
 imageset("settings_search_engine") {
   sources = [
     "settings_search_engine.imageset/Contents.json",
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/Contents.json b/ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/Contents.json
similarity index 67%
copy from ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/Contents.json
copy to ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/Contents.json
index a31b671..8f859ab 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/resources/clear_browsing_data_passwords.imageset/Contents.json
+++ b/ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/Contents.json
@@ -3,12 +3,12 @@
         {
             "idiom": "universal",
             "scale": "2x",
-            "filename": "clear_browsing_data_passwords@2x.png"
+            "filename": "settings_safe_browsing@2x.png"
         },
         {
             "idiom": "universal",
             "scale": "3x",
-            "filename": "clear_browsing_data_passwords@3x.png"
+            "filename": "settings_safe_browsing@3x.png"
         }
     ],
     "info": {
diff --git a/ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/settings_safe_browsing@2x.png b/ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/settings_safe_browsing@2x.png
new file mode 100644
index 0000000..62209991a
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/settings_safe_browsing@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/settings_safe_browsing@3x.png b/ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/settings_safe_browsing@3x.png
new file mode 100644
index 0000000..26d037e
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/settings_safe_browsing.imageset/settings_safe_browsing@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
index ff65023..0cae299 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
@@ -206,18 +206,33 @@
     _updateCheckItem = [[SettingsCheckItem alloc] initWithType:UpdateItemType];
     _updateCheckItem.text =
         l10n_util::GetNSString(IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_TITLE);
+    UIImage* updateCheckIcon = [[UIImage imageNamed:@"settings_info"]
+        imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+    _updateCheckItem.leadingImage = updateCheckIcon;
+    _updateCheckItem.leadingImageTintColor = [UIColor colorNamed:kGrey400Color];
 
     _passwordCheckRowState = PasswordCheckRowStateDefault;
     _passwordCheckItem =
         [[SettingsCheckItem alloc] initWithType:PasswordItemType];
     _passwordCheckItem.text =
         l10n_util::GetNSString(IDS_IOS_SETTINGS_SAFETY_CHECK_PASSWORDS_TITLE);
+    UIImage* passwordCheckIcon = [[UIImage imageNamed:@"password_key"]
+        imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+    _passwordCheckItem.leadingImage = passwordCheckIcon;
+    _passwordCheckItem.leadingImageTintColor =
+        [UIColor colorNamed:kGrey400Color];
 
     _safeBrowsingCheckRowState = SafeBrowsingCheckRowStateDefault;
     _safeBrowsingCheckItem =
         [[SettingsCheckItem alloc] initWithType:SafeBrowsingItemType];
     _safeBrowsingCheckItem.text = l10n_util::GetNSString(
         IDS_IOS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_TITLE);
+    UIImage* safeBrowsingCheckIcon =
+        [[UIImage imageNamed:@"settings_safe_browsing"]
+            imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+    _safeBrowsingCheckItem.leadingImage = safeBrowsingCheckIcon;
+    _safeBrowsingCheckItem.leadingImageTintColor =
+        [UIColor colorNamed:kGrey400Color];
 
     _checkStartState = CheckStartStateDefault;
     _checkStartItem =
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 0a99154a..916d3b88 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -111,6 +111,7 @@
     @"settings_payment_methods";
 NSString* const kSettingsAutofillProfileImageName = @"settings_addresses";
 NSString* const kSettingsVoiceSearchImageName = @"settings_voice_search";
+NSString* const kSettingsSafetyCheckImageName = @"settings_safety_check";
 NSString* const kSettingsPrivacyImageName = @"settings_privacy";
 NSString* const kSettingsLanguageSettingsImageName =
     @"settings_language_settings";
@@ -635,7 +636,7 @@
   return [self detailItemWithType:ItemTypeSafetyCheck
                              text:safetyCheckTitle
                        detailText:nil
-                    iconImageName:kSettingsPrivacyImageName
+                    iconImageName:kSettingsSafetyCheckImageName
           accessibilityIdentifier:nil];
 }
 
diff --git a/ios/chrome/browser/ui/settings/signin_settings_egtest.mm b/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
index 95a46c7..d3fea67 100644
--- a/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
@@ -65,7 +65,7 @@
   [SigninEarlGreyUI confirmSigninConfirmationDialog];
 
   // User signed in.
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI checkSigninPromoNotVisible];
   [[EarlGrey selectElementWithMatcher:SettingsAccountButton()]
       assertWithMatcher:grey_interactable()];
@@ -84,7 +84,7 @@
   [SigninEarlGreyUI confirmSigninConfirmationDialog];
 
   // User signed in.
-  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI checkSigninPromoNotVisible];
   [[EarlGrey selectElementWithMatcher:SettingsAccountButton()]
       assertWithMatcher:grey_interactable()];
diff --git a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
index 7763918..a01c525 100644
--- a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
+++ b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
@@ -4,7 +4,7 @@
 
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -69,8 +69,8 @@
   [BookmarkEarlGrey addBookmarkWithTitle:@"foo" URL:@"https://www.foo.com"];
 
   // Sign in to sync, after a bookmark has been added.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Assert that the correct number of bookmarks have been synced.
@@ -80,8 +80,8 @@
 
 // Tests that a bookmark added on the client is uploaded to the Sync server.
 - (void)testSyncUploadBookmark {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Add a bookmark after sync is initialized.
@@ -98,8 +98,8 @@
   [ChromeEarlGrey addFakeSyncServerBookmarkWithURL:URL title:"hoo"];
 
   // Sign in to sync, after a bookmark has been injected in the sync server.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
   [BookmarkEarlGrey verifyBookmarksWithTitle:@"hoo" expectedCount:1];
@@ -108,8 +108,8 @@
 // Tests that the local cache guid does not change when sync is restarted.
 - (void)testSyncCheckSameCacheGuid_SyncRestarted {
   // Sign in the fake identity.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
@@ -129,15 +129,14 @@
 // signs back in with the same account.
 - (void)testSyncCheckDifferentCacheGuid_SignOutAndSignIn {
   // Sign in a fake identity, and store the initial sync guid.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
   std::string original_guid = [ChromeEarlGrey syncCacheGUID];
 
-  GREYAssert([SigninEarlGreyAppInterface isAuthenticated],
-             @"User is not signed in.");
-  [SigninEarlGreyAppInterface signOut];
+  [SigninEarlGrey verifyAuthenticated];
+  [SigninEarlGrey signOut];
   [ChromeEarlGrey waitForSyncInitialized:NO syncTimeout:kSyncOperationTimeout];
 
   // Sign the user back in, and verify the guid has changed.
@@ -153,14 +152,13 @@
 // Test for http://crbug.com/413611 .
 - (void)testSyncCheckSameCacheGuid_SyncRestartedAfterSignOutAndSignIn {
   // Sign in a fake idenitty.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
-  GREYAssert([SigninEarlGreyAppInterface isAuthenticated],
-             @"User is not signed in.");
-  [SigninEarlGreyAppInterface signOut];
+  [SigninEarlGrey verifyAuthenticated];
+  [SigninEarlGrey signOut];
   [ChromeEarlGrey waitForSyncInitialized:NO syncTimeout:kSyncOperationTimeout];
 
   // Sign the user back in.
@@ -193,8 +191,8 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded.
@@ -221,8 +219,8 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded.
@@ -266,8 +264,8 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded
@@ -306,8 +304,8 @@
   [ChromeEarlGrey loadURL:URL2];
 
   // Sign in to sync, after opening two tabs.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify the sessions on the sync server.
@@ -333,8 +331,8 @@
   [ChromeEarlGrey addHistoryServiceTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -360,8 +358,8 @@
   [ChromeEarlGrey addFakeSyncServerTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -386,8 +384,8 @@
   [ChromeEarlGrey addFakeSyncServerTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -420,8 +418,8 @@
   [ChromeEarlGrey addHistoryServiceTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -462,8 +460,8 @@
                    originator_client_item_id:"1"];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
-  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
diff --git a/ios/chrome/test/app/password_test_util.mm b/ios/chrome/test/app/password_test_util.mm
index 187a0c0..370884e 100644
--- a/ios/chrome/test/app/password_test_util.mm
+++ b/ios/chrome/test/app/password_test_util.mm
@@ -5,7 +5,7 @@
 #include "ios/chrome/test/app/password_test_util.h"
 
 #include "base/mac/foundation_util.h"
-#import "ios/chrome/browser/ui/settings/password/password_details_table_view_controller+testing.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller+testing.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/util/top_view_controller.h"
@@ -53,9 +53,10 @@
   SettingsNavigationController* settings_navigation_controller =
       base::mac::ObjCCastStrict<SettingsNavigationController>(
           top_view_controller::TopPresentedViewController());
-  PasswordDetailsTableViewController* password_details_table_view_controller =
-      base::mac::ObjCCastStrict<PasswordDetailsTableViewController>(
-          settings_navigation_controller.topViewController);
+  LegacyPasswordDetailsTableViewController*
+      password_details_table_view_controller =
+          base::mac::ObjCCastStrict<LegacyPasswordDetailsTableViewController>(
+              settings_navigation_controller.topViewController);
   [password_details_table_view_controller
       setReauthenticationModule:mock_reauthentication_module];
   return mock_reauthentication_module;
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 0699e90..19ff4a7f 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -39,6 +39,11 @@
 // will properly synchronize the UI for Earl Grey tests.
 @interface ChromeEarlGreyImpl : BaseEGTestHelperImpl
 
+#pragma mark - Test Utilities
+
+// Wait until |matcher| is accessible (not nil) on the device.
+- (void)waitForMatcher:(id<GREYMatcher>)matcher;
+
 #pragma mark - Device Utilities
 
 // Simulate the user action to rotate the device to a certain orientation.
@@ -348,11 +353,6 @@
 // the operation fails.
 - (void)signOutAndClearIdentities;
 
-// Same as signOutAndClearIdentities.
-//
-// DEPRECATED in favor of signOutAndClearIdentities
-- (void)signOutAndClearAccounts;
-
 #pragma mark - Sync Utilities (EG2)
 
 // Waits for sync to be initialized or not. If not succeeded a GREYAssert is
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 8b10d7d..281972b 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -72,6 +72,23 @@
 
 @implementation ChromeEarlGreyImpl
 
+#pragma mark - Test Utilities
+
+- (void)waitForMatcher:(id<GREYMatcher>)matcher {
+  ConditionBlock condition = ^{
+    NSError* error = nil;
+    [[EarlGrey selectElementWithMatcher:matcher] assertWithMatcher:grey_notNil()
+                                                             error:&error];
+    return error == nil;
+  };
+  NSString* errorString =
+      [NSString stringWithFormat:@"Waiting for matcher %@ failed.", matcher];
+  EG_TEST_HELPER_ASSERT_TRUE(
+      base::test::ios::WaitUntilConditionOrTimeout(
+          base::test::ios::kWaitForUIElementTimeout, condition),
+      errorString);
+}
+
 #pragma mark - Device Utilities
 
 - (void)rotateDeviceToOrientation:(UIDeviceOrientation)deviceOrientation
@@ -833,16 +850,12 @@
                              @"Failed waiting for identities to be cleared");
 }
 
-- (void)signOutAndClearAccounts {
-  [self signOutAndClearIdentities];
-}
+#pragma mark - Bookmarks Utilities (EG2)
 
 - (void)addBookmarkWithSyncPassphrase:(NSString*)syncPassphrase {
   [ChromeEarlGreyAppInterface addBookmarkWithSyncPassphrase:syncPassphrase];
 }
 
-#pragma mark - Bookmarks Utilities (EG2)
-
 - (void)waitForBookmarksToFinishLoading {
   EG_TEST_HELPER_ASSERT_NO_ERROR(
       [ChromeEarlGreyAppInterface waitForBookmarksToFinishinLoading]);
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index b878822..0efc635 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -84,7 +84,6 @@
     "//ios/chrome/browser/policy_url_blocking:eg2_tests",
     "//ios/chrome/browser/prerender:eg2_tests",
     "//ios/chrome/browser/safe_browsing:eg2_tests",
-    "//ios/chrome/browser/translate:eg2_tests",
     "//ios/chrome/browser/ui/autofill:eg2_tests",
     "//ios/chrome/browser/ui/autofill/manual_fill:eg2_tests",
     "//ios/chrome/browser/ui/content_suggestions:eg2_tests",
diff --git a/media/gpu/vaapi/h264_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/h264_vaapi_video_decoder_delegate.cc
index 05427a7..0a23c7bb 100644
--- a/media/gpu/vaapi/h264_vaapi_video_decoder_delegate.cc
+++ b/media/gpu/vaapi/h264_vaapi_video_decoder_delegate.cc
@@ -7,6 +7,7 @@
 #include <va/va.h>
 
 #include "base/stl_util.h"
+#include "base/trace_event/trace_event.h"
 #include "media/gpu/decode_surface_handler.h"
 #include "media/gpu/h264_dpb.h"
 #include "media/gpu/macros.h"
@@ -67,6 +68,8 @@
     const H264Picture::Vector& ref_pic_listb1,
     scoped_refptr<H264Picture> pic) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("media,gpu",
+               "H264VaapiVideoDecoderDelegate::SubmitFrameMetadata");
   VAPictureParameterBufferH264 pic_param;
   memset(&pic_param, 0, sizeof(pic_param));
 
@@ -183,6 +186,7 @@
     size_t size,
     const std::vector<SubsampleEntry>& subsamples) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("media,gpu", "H264VaapiVideoDecoderDelegate::SubmitSlice");
   VASliceParameterBufferH264 slice_param;
   memset(&slice_param, 0, sizeof(slice_param));
 
@@ -282,6 +286,7 @@
 DecodeStatus H264VaapiVideoDecoderDelegate::SubmitDecode(
     scoped_refptr<H264Picture> pic) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("media,gpu", "H264VaapiVideoDecoderDelegate::SubmitDecode");
 
   const bool success = vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
       pic->AsVaapiH264Picture()->va_surface()->id());
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index 39a64a5..4818406 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -83,7 +83,7 @@
       buffer_id_to_timestamp_(kTimestampCacheSize),
       weak_this_factory_(this) {
   VLOGF(2);
-  DCHECK(decoder_task_runner->RunsTasksInCurrentSequence());
+  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   weak_this_ = weak_this_factory_.GetWeakPtr();
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index c17b7b8..5c7441ba 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -1692,9 +1692,13 @@
   TRACE_EVENT0("media,gpu", "VaapiWrapper::SubmitBufferLocked");
 
   VABufferID buffer_id;
-  VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_, va_buffer_type,
-                                   size, 1, nullptr, &buffer_id);
-  VA_SUCCESS_OR_RETURN(va_res, "vaCreateBuffer", false);
+  {
+    TRACE_EVENT0("media,gpu", "VaapiWrapper::SubmitBuffer_vaCreateBuffer");
+    const VAStatus va_res =
+        vaCreateBuffer(va_display_, va_context_id_, va_buffer_type, size, 1,
+                       nullptr, &buffer_id);
+    VA_SUCCESS_OR_RETURN(va_res, "vaCreateBuffer", false);
+  }
 
   ScopedVABufferMapping mapping(
       va_lock_, va_display_, buffer_id,
@@ -1706,18 +1710,7 @@
   // such as libyuv::CopyX and family.
   memcpy(mapping.data(), buffer, size);
 
-  switch (va_buffer_type) {
-    case VASliceParameterBufferType:
-    case VASliceDataBufferType:
-    case VAEncSliceParameterBufferType:
-      pending_slice_bufs_.push_back(buffer_id);
-      break;
-
-    default:
-      pending_va_bufs_.push_back(buffer_id);
-      break;
-  }
-
+  pending_va_buffers_.push_back(buffer_id);
   return true;
 }
 
@@ -1743,7 +1736,7 @@
   params->type = misc_param_type;
   memcpy(params->data, buffer, size);
 
-  pending_va_bufs_.push_back(buffer_id);
+  pending_va_buffers_.push_back(buffer_id);
   return true;
 }
 
@@ -1756,18 +1749,11 @@
 void VaapiWrapper::DestroyPendingBuffers_Locked() {
   TRACE_EVENT0("media,gpu", "VaapiWrapper::DestroyPendingBuffers_Locked");
   va_lock_->AssertAcquired();
-  for (const auto& pending_va_buf : pending_va_bufs_) {
+  for (const auto& pending_va_buf : pending_va_buffers_) {
     VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf);
     VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer");
   }
-
-  for (const auto& pending_slice_buf : pending_slice_bufs_) {
-    VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf);
-    VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer");
-  }
-
-  pending_va_bufs_.clear();
-  pending_slice_bufs_.clear();
+  pending_va_buffers_.clear();
 }
 
 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
@@ -2376,8 +2362,7 @@
   TRACE_EVENT0("media,gpu", "VaapiWrapper::Execute_Locked");
   va_lock_->AssertAcquired();
 
-  DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
-  DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
+  DVLOG(4) << "Pending VA bufs to commit: " << pending_va_buffers_.size();
   DVLOG(4) << "Target VA surface " << va_surface_id;
   const auto decode_start_time = base::TimeTicks::Now();
 
@@ -2385,20 +2370,12 @@
   VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, va_surface_id);
   VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture", false);
 
-  if (pending_va_bufs_.size() > 0) {
+  if (pending_va_buffers_.size() > 0) {
     // Commit parameter and slice buffers.
-    va_res = vaRenderPicture(va_display_, va_context_id_, &pending_va_bufs_[0],
-                             pending_va_bufs_.size());
-    VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for |pending_va_bufs_|",
-                         false);
-  }
-
-  if (pending_slice_bufs_.size() > 0) {
     va_res =
-        vaRenderPicture(va_display_, va_context_id_, &pending_slice_bufs_[0],
-                        pending_slice_bufs_.size());
-    VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for |pending_slice_bufs_|",
-                         false);
+        vaRenderPicture(va_display_, va_context_id_, &pending_va_buffers_[0],
+                        pending_va_buffers_.size());
+    VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture", false);
   }
 
   // Instruct HW codec to start processing the submitted commands. In theory,
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index c4d005b..ea451e5 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -456,8 +456,7 @@
   VAEntrypoint va_entrypoint_;
 
   // Data queued up for HW codec, to be committed on next execution.
-  std::vector<VABufferID> pending_slice_bufs_;
-  std::vector<VABufferID> pending_va_bufs_;
+  std::vector<VABufferID> pending_va_buffers_;
 
   // Buffers for kEncode or kVideoProcess.
   std::set<VABufferID> va_buffers_;
diff --git a/media/gpu/vaapi/vp8_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/vp8_vaapi_video_decoder_delegate.cc
index bc75725f..c68ca3b 100644
--- a/media/gpu/vaapi/vp8_vaapi_video_decoder_delegate.cc
+++ b/media/gpu/vaapi/vp8_vaapi_video_decoder_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "media/gpu/vaapi/vp8_vaapi_video_decoder_delegate.h"
 
+#include "base/trace_event/trace_event.h"
 #include "media/gpu/decode_surface_handler.h"
 #include "media/gpu/vaapi/va_surface.h"
 #include "media/gpu/vaapi/vaapi_common.h"
@@ -31,6 +32,7 @@
 bool VP8VaapiVideoDecoderDelegate::SubmitDecode(
     scoped_refptr<VP8Picture> pic,
     const Vp8ReferenceFrameVector& reference_frames) {
+  TRACE_EVENT0("media,gpu", "VP8VaapiVideoDecoderDelegate::SubmitDecode");
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   auto va_surface_id = pic->AsVaapiVP8Picture()->va_surface()->id();
diff --git a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
index dca0183..6d6089b 100644
--- a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
+++ b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
@@ -7,6 +7,7 @@
 #include <type_traits>
 
 #include "base/stl_util.h"
+#include "base/trace_event/trace_event.h"
 #include "media/gpu/decode_surface_handler.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/vaapi/va_surface.h"
@@ -37,6 +38,7 @@
     const Vp9LoopFilterParams& lf,
     const Vp9ReferenceFrameVector& ref_frames,
     base::OnceClosure done_cb) {
+  TRACE_EVENT0("media,gpu", "VP9VaapiVideoDecoderDelegate::SubmitDecode");
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // |done_cb| should be null as we return false from IsFrameContextRequired().
   DCHECK(!done_cb);
diff --git a/media/test/data/test-25fps.vp9_2.json b/media/test/data/test-25fps.vp9_2.json
index 8d3036d..fc38be3 100644
--- a/media/test/data/test-25fps.vp9_2.json
+++ b/media/test/data/test-25fps.vp9_2.json
@@ -6,256 +6,256 @@
   "num_frames": 250,
   "num_fragments": 250,
   "md5_checksums": [
-    "9c8d2359e9a386af75805e18ec985961",
-    "57c1de8f7c8c712098acc78d88ff4b3e",
-    "10d2824b4b5224bbe483e4030f937d87",
-    "44a157abbe00572269ee6cd308e11329",
-    "6c668f802d3945b5df04f12fdcbadd58",
-    "50828e75f435c4950d89ab54a4c2cb57",
-    "707a97e75ceacedfb644b1c600dcce0f",
-    "51b8a2d6d19d72a66c35c1b16ccf6d23",
-    "8efadd6cddd4498b985d22a0f5a087e5",
-    "445d78cf7bdfbd258819bfbbc284dbac",
-    "7e43e1cbb16030027400ddcde5ac4b1f",
-    "dc72a49f119a10e358eb91a74bcca0a7",
-    "0744410b6182cbdecc7b52a75bb8a519",
-    "87f572a1ccb96b70e7cfe49b7c1be9ab",
-    "55f2802a408417972c7af23c391b8c44",
-    "34978d638ea2a137926b801130c4f18d",
-    "fb16afccef3fd0a2542d3f886e3bc76e",
-    "22f838188988d72cc3c82aadc6c1de85",
-    "d825769ed309c32356711b8ea7976a98",
-    "eec173a8c7979145f8af625857e5a446",
-    "964dc0e71ac4b0acab184c499f69933d",
-    "f7a2b524ef638dfbb1a8a8151ec668ac",
-    "5acdec2e3b9b4df67fbefe41c43b55eb",
-    "04e7d42e6b2843bdf4be46cdd984250a",
-    "d9e2695475faace4124c19f219d192b4",
-    "82a477bc93bff493eeef4419dc795364",
-    "7f6a1012b842badf22a96302e52d1c9f",
-    "72e9217e59d03dd9e3c7ceb44d390222",
-    "7ec11e9f20c7e7842c8b1f65be6a7f79",
-    "f650b9cf36b221b9371c2b373620261c",
-    "1be04f17c85fffb9059e909db472df17",
-    "f64acfd484eb6ab1289b806c78103243",
-    "75ee71fa45d8a5f4e551d9d4c087d715",
-    "6092e2320de1858170f4e94f370c3cbc",
-    "ecae6e91a65706b74214ea17e65982e3",
-    "489cb0430901612ba6cfb54b415cad86",
-    "416c0bfbe0fb303d2ebb677e0cebc2c3",
-    "450b55553f5d4ccee41d6bafddc8d66f",
-    "31a08830592579be51c54a36eed02ca5",
-    "c3c7fb2eccc757436c17e041a56265db",
-    "96797f2fd84a8b072ea81f06fd36b161",
-    "44c4f28fe56481e49841d8a1196aca73",
-    "27fbdac1ac2d3172893b3a372be1dcaf",
-    "8dd0e468258e6a7641cc00bce5933a3a",
-    "d1a0c5d9ae55512e24147e4b0b6bd2d0",
-    "2e6415fc0a171e65af7687c57a869707",
-    "707e29961513add6f8bb0ba96e773c23",
-    "faf3fdf60e84ece33c7d85b4a73d2e4b",
-    "1c689c78dbbf0f4656cadc83438db77b",
-    "353e37243a25f5bc18de5a9c895e761d",
-    "088f6f072527e0ce0fd2289a90822b79",
-    "9199724ac90a61857be49aba874d2b05",
-    "b0fef1e0e18a9397dd95f40d0366b14b",
-    "2d1f148dc258f311e4e78c069fc8279a",
-    "a1844ba1b7022418fa660ac07978565b",
-    "275b8950b6db868f8ef6daab76751f26",
-    "dd2611b54ba903d22c490248796c0770",
-    "34e1af274cceae16c1af581b74a2bfed",
-    "2c2631e1e7f986f9fb314a8fccda3416",
-    "54eb141d09babc58ab1d4fae1f3fabb6",
-    "b8a36207caca157b80bbdfe0b3bab84c",
-    "dca5651dbf37f9e68daae11bc379c9c3",
-    "981d282c2ca643c67d7a5d3e8fae083b",
-    "072b62e0fea29d0554ffae727bad28aa",
-    "fbf200c751dd34f12201d649b3c9761f",
-    "741793c713d9ea7e1280c3c54dbed3ab",
-    "da0f631880f9d1f977df3685039bfb36",
-    "753f23fdf2e24cdec879a78bf99e45cd",
-    "d6bae48cec01deeef406850a8766c394",
-    "197cb360c601ca6fbec8c48d7601814a",
-    "299c8aa6ad085707da9a8c6ce9313d66",
-    "c3da551786a43fcfaa015fedecefc6e4",
-    "7f2290dcec74191321dc751becb660e5",
-    "36ce0d47afec42e276ae58121011ce19",
-    "4436670d6ff987dac66ef725e7b738be",
-    "f4b1de84c1e1d243fb84fd8533a5034d",
-    "4eb8547b6f5959897c17ca3d6e5095ce",
-    "0df7d40e75b61421275903f49d5bf433",
-    "e247fbd3fb4165e9671dc2e3b58f4330",
-    "17b70d99ae075eba2604038e73b78f7e",
-    "476efb80455eb9a910415558d20e678f",
-    "c7e4ae096e9c01fa4fb309d8d55ebaa9",
-    "534f701fdb032a4a8f1ffeac6ca7e382",
-    "eeaa58a6e51bd05b7f5992ddaa98cebe",
-    "1ab34252d9333a6b90614763e6292ecf",
-    "f38232d49c9a0bceeaa8a4fc70cdff53",
-    "f430c6a6eaf0bca74510af92f1fb0285",
-    "94c70343e8d247cc76aca65a9d970579",
-    "225c5c349d1b9f3285f600c5faab2696",
-    "971b496ac2f6d77e89e9324457edaf02",
-    "78d83325ba3ae1fb78253edbbed9411e",
-    "ad49ed47dcf40f2093f74fb3a022c06a",
-    "b1630fe2eca7dc580ba195cb59551050",
-    "6dc6bd1491344192a34628ac76eeb9a0",
-    "2d942bf3670f1a64ce22d79f48061a42",
-    "882184c577f9ca601cea95381be975ec",
-    "f898aac1c4dca650d07b16fedee014e5",
-    "c2f3f2a63489984ff8b6a971914330b4",
-    "ee355a981b5a42ca3b515a49bf380dba",
-    "d7a61e44eec316815897cd86994b1235",
-    "68139f52ccb8705dd22ebe4bb9524ee3",
-    "2e7a0870e9f8f4a1ec4204b0c06218ec",
-    "be548ed3a0c64356c14457ceae01dc38",
-    "04e1f67e239f0f73da7c232c9294072e",
-    "403bc2f26f8a9de3ba63ad9e19815670",
-    "27c5a416fa50602b028b4767d6ca6313",
-    "33855b39710bc55f06d8d3a7cd6187d9",
-    "acbcdf202836c60b42e666a7fba81d09",
-    "02368ff27221dd277557bc82470d6367",
-    "865328ba8e13bde83a50b46b85e1b076",
-    "a973b4473645a6634800d14f6dda1d97",
-    "e4e72d4d40860ccf2ea0267af5b72489",
-    "5ed66a2fe444483a300bf4c87996d708",
-    "0362687d3a2ce08a13d9a0af351ae7e5",
-    "294a0202e60f468a4a0111b0dac74545",
-    "113aa715f07345eb183a84e7f5d1c14d",
-    "076072a893a559dc51c4ead9b41b67e2",
-    "96fc3ac5086234bb89332a72d4a3f05b",
-    "2d9a85feb30528560700ecc75077b34a",
-    "5b4ff61881281f2cdcd7ec68994bf000",
-    "1703491c88136f11432095be8c8cd9ec",
-    "aa90c0ff07e92edee5cd9388533d862a",
-    "8959ac9fdc0d5cf54b2f4b73ec8ad269",
-    "b8cf1d36559b4e18a3668af2746bcfd7",
-    "79117f25d8fabc44b77c841f53138c41",
-    "dbcb468a686d34a27e7bf79af5087046",
-    "b3df5e803085d66d573a000b03bb5768",
-    "b8beb470fb3e6670d46402823554915d",
-    "bfc550bc5ddba1c3dba83edb301e3ee4",
-    "5f6d0d25dddbca7e0c6bb42d4dba89b6",
-    "d98738374520a5bb7e1d21fdb5e87407",
-    "c355489f09f82d736cdf5bb2ccc82261",
-    "304d7bc869ba553e3523de59009997bc",
-    "fc9728b3bc1bd368763336a00fab9e54",
-    "310bf813ca94275ca55db36f64c94229",
-    "2be2f6385fcc9547b93e242ae6c6d0f0",
-    "1d44e33800065aa5e07e0c14f5fde547",
-    "8b0be65793c3d03cf77e0c90dbb046e6",
-    "9e3e6c1399a81f26b53d9ea5462a0ff4",
-    "65368dac9c1d08eadb56a76e9c8daa3b",
-    "210ab55b98e741ad8fa44ebf8ce1643d",
-    "9dec552fac1955003119c487c3c374ab",
-    "b806750076ddf0b11f8ebb693ada10aa",
-    "2ed5d34e65c1658f79543dc90ce04f7e",
-    "31caf9b589d6b18ac08bc12d99c53dcb",
-    "77445ea1f386da761abb9df0a5df2931",
-    "c6cefb579b056dca6cbec2eadb479367",
-    "d62b2fd426a1ba28e7653fbe4539cdc4",
-    "ead3d369c77c0313e5b26fb3e3f30ff4",
-    "4ba709ab0ff256366ba1a583ef8e9d5f",
-    "e3ada41c1ac6045871b5cc0b4271e53c",
-    "a1ca36927d9f19613d40fcdbf1e54e1c",
-    "9317cf6eb772e225fd74b4bbe6aac481",
-    "44fcf37d4c6b9e902de29a41f6549d4b",
-    "ee97e17e6e8522d60ad29b171a511d13",
-    "980295287a80c6d6751504f396ded0a6",
-    "3ee20ecc430e0c09ca9ac22ae14b5d44",
-    "49f9b05ab4a9ff7cf0984d259b6c328f",
-    "e60dcf6053ef6be7b18fb92888dd56ea",
-    "9b74df9ab4615375faf17398f98b6faa",
-    "11fcad52840b47e8115d39b320494d48",
-    "88a4a8a095a5bb138ce98e39e5ec136c",
-    "2a02e8ea5207d99a36104b5c87ad373f",
-    "2287ed564f063b811f897baca2c5eea6",
-    "bc8aa50f126350ce32a8755b229eab63",
-    "c5520093169c46ea2c0ed299f29f6f56",
-    "10a53939eba7b29d0687db5a01f5bfd3",
-    "b519a65817702c79e4ee81341f238b17",
-    "44f637d4e72a64ca67764e1ab4e4e775",
-    "3f55610f9057a187556e68691c5aa25c",
-    "e27044a23e24e93e96c3331ac1ae89c3",
-    "fa986c7410f3c7164716783169715b8a",
-    "aaba77fcfc5c380c0178c0e5d423fa73",
-    "e01e7089da41fb47bc7289e7eaddef91",
-    "ff092fb21d050e46134dec7d522a3d12",
-    "82fd84dc3c20fc315fc924a036636ddb",
-    "17a986aaa4cb079c5e772b28ab7ea4a0",
-    "25fcb0f1ea2dcc658cc941a5f7e1a368",
-    "9177407682d5d3add1d2cd28d111835c",
-    "059337dc93f280591c3940b2384c6b68",
-    "0bece31e5dec6a8376187d7674919676",
-    "9e7ef48e848a8585be3711f376f05b23",
-    "3e204cbe59b95226d0f21fc25219920d",
-    "1faa664937e7afaf408e5e0b3fef0377",
-    "cdcf11c4d5434d6ba4dcf2cabb50b401",
-    "bf8dece1593a2d3d557f4a1ca947e89b",
-    "71f41d00990f902c9d300a4cd36e7f55",
-    "c2f2cb98a370dd1906f3e2d0a9345bd5",
-    "54fe13e1aa4442fbe3115faedec60dd7",
-    "1397873cd572460c90009dffa5fa6418",
-    "54cda5502c1c6c639f88a8d056198d41",
-    "dd995b9ad824d8bcca8d2d1df52a945f",
-    "63288ceb960a870a9272be3684851240",
-    "eac81c0630ff9c9e66e56335acc0f883",
-    "3ae643b7245d997d3c6ca7dd65b40e42",
-    "9deeff4a6ccd87c9c8f402cd0fe7c112",
-    "15b5fa016db2ea9a0c552276116d29d8",
-    "b1e4f7b117972c131d04f5b7577f5356",
-    "f4ef1489090d8ac2a2a9a097b9468fbc",
-    "dba800c799a259615c5cb85ddd075189",
-    "a3c74b815cf875c1895b7af24d2d9d1a",
-    "53557ad9a04fec9ce85a797dabf5f416",
-    "10242f9b71a44fa79094b26165436b9f",
-    "6a703674d91480bc24fb8e066158d53b",
-    "d925c0dc0e19a676b01490d050b7152e",
-    "357f045ec25d88e4473c8c9bb1271163",
-    "26d9053c54b31cd616d4711d0e49158e",
-    "d8fbb4d8bf9db88d6cfe7c2354ea03ef",
-    "58a976cc6cf903121aa1c29925aae94a",
-    "e77ec312b75326e7868f8f938e6c3195",
-    "423b2ff3e7d99a6997866b5a4583496c",
-    "9a5578dcf1b53b5e624f4c750df4916d",
-    "5e2cde9533a57da707aa0360674a6810",
-    "ba45f35c5dd9881bbd4cfe3a712fd045",
-    "5c4754d7610a6f8831a53dd30f1340cf",
-    "25ba552ca55e8961311fe067c1078291",
-    "57f4073e98ecd969730a83d7e54dc1ab",
-    "76956a1564ebedaffd76814402b4647a",
-    "7558b862412061a5ce82a0b0c7bf56ae",
-    "85013cf9653d384f1de2f186767ec933",
-    "0b9fd790fdfef148d5e95583ff1a0258",
-    "1edc6486684000f16a880d4522b77882",
-    "a4dd0e3f6362b5848f5a5655e8df5e7a",
-    "5d8d566682d44334c8af1bf9ab70d68b",
-    "fb4f617b09fd9f3df670612fb6d4e087",
-    "4e8f3a63d536b59a44dbf1bd64758052",
-    "bd4f9f8d74989ad67835b6f134c4ca14",
-    "c2334251188056cb45e2627b5a953ba3",
-    "83e5706f503558a5645e567430bf2670",
-    "6c1e98da202f4a7d2c40a6e9e848e494",
-    "ea088ac8d9fd7e0af2ab557c5f005db9",
-    "f70fb5f97ff9c763d18f27da53003d08",
-    "fb2d4cd555540acaada343faba6e2271",
-    "fd3dad9490b627f226595cf2cac3a172",
-    "15cab446131cc42616317e4e256a5625",
-    "e322972f6fd72c72c46000fee4e68c91",
-    "aeb53d75520cac68e37c5743b0644f9a",
-    "19893ca56b715a1abe2167077e1485ed",
-    "4f8985ce61045e203e2095effa1058ef",
-    "6758ad39404c9ae4966550c9699d27c5",
-    "2ad303311377498de85fdbc0db007259",
-    "518d296c6b2d1ffa7bb6a8e05ae24521",
-    "6183d06dd93892d2619c55cc733a35f0",
-    "c3b81884db7c5ad2a63f184aa9979c14",
-    "7e9103458073436993f05254c42e2f33",
-    "ce7133eb15db513ada44e1637bf2dc13",
-    "ab9bf0d935045d25050b3da7016af3a4",
-    "70c3771a7abcebfb0e318e71704f1230",
-    "4a2f3dfed38e13d939a84d80c02e1726",
-    "33b05476d513c7a1d2de6bae77031685"
+    "0f3489c0340ccdcd4fb457621caf6918",
+    "fd5db934b5acbfb178b4c829f982d29b",
+    "201461a4256783d5776df7b403435c99",
+    "dad4bc94e234a55cd87dd936a2f70b5e",
+    "604fa6fc46744bb01713e0c74c889f6b",
+    "7eb563e7b078bb04d2853701a8eea29c",
+    "c073d8876d177b6129b947a486280a8f",
+    "6195248b702eb41bd20b4495bacdebbf",
+    "6c9404ec3cdd61f78204e280140371ce",
+    "611b4b9c318423386affacb9150cb7ac",
+    "bbaf4acd466b835f47ae96b107b336ad",
+    "a1d80a407f30aaa9d9f720f1cbd93d92",
+    "a5068703a449fdcbf90bd0da894bd6ad",
+    "81e1227d81e55cd6b3b8b5a358390748",
+    "8d7f30b985c2895ec087344b7b45f8f6",
+    "8a9a8b717c3591698691bd70a80d9508",
+    "e4d2762a4032b9b2fdb8b4cbae0882c1",
+    "27a20c25cd80a5fc60072b394f2d09cd",
+    "292efafb2f6eeb49e92180bcc72a2b28",
+    "83aee86ca149819318e03ce370be7ac5",
+    "ecaa285f3e1227583165280ce21b597d",
+    "da29f499d0bb7e81751c067aba59b537",
+    "3e1929c6d28f90d89d1f6b61c980b692",
+    "d6d2048316efb031c138a708b98c57b8",
+    "77284b06772bd77ed805bb096845f164",
+    "558e39a75ae807ec73015504f54634e4",
+    "c875be23f1e777ebaf24387ff68ca229",
+    "69db7ca5a69326e8b021ccfd2c75cdea",
+    "0a8dfade147d2e881632427220a13252",
+    "990b3fbf36dfde95b7ceb7acb082f831",
+    "a5297a9c0c863cd1518763a185fbdf94",
+    "3f8463cd1fc3e487d6d1f5d216b4c0f3",
+    "f2fc958318e91979ea320f6aa3bfd065",
+    "d158530e239435344a84760450a90242",
+    "8f186d82e75f056500b7a749d2931132",
+    "e078418bae9910822b7b7b107c8e9243",
+    "eec9c070873d93ac2a58e3e790e184af",
+    "aae8fee06da9e72fefbc2cf1f3c1e7af",
+    "aa7f79dc4ebe43325c16c47e53797aeb",
+    "5f65c81f6dec5596ef4cecbc9dd02129",
+    "aeb650ae40a2f64b6bdf061851b0d43c",
+    "b72f17167237f5f2c1a6936ced0654f9",
+    "c32c2785d616aac7ed1a579c7d3b4323",
+    "28d8d8a4a10710b2c7b97348b59117a5",
+    "1e13f2ded8cd48c0966f255e6c8df27d",
+    "84aee7d099bf370c6ec2feb9d306487e",
+    "d3a9615e7b36ad3364750a8028af3752",
+    "93d0c1173013f9c4ce4dedfb6a36a3ba",
+    "2c0a44e3adcea3d29591c16a17ddbd62",
+    "e23bde64aa4f57dd37c1063fa685a185",
+    "a1857a186ca7a80c10e4d0f53cebe5b4",
+    "632dcc8d6139f5b37853df3135d8893d",
+    "31e2c6995b1bef1e61251535e74d5aed",
+    "5a741ce3fe3cfbb37ee49ee18b370c97",
+    "d950225312a18feb644e185d46aa0821",
+    "7a283d81f5cc2dfe00103718d49222da",
+    "33d4dcc6c8df9dba9eedd10f7bcdfc81",
+    "e347cb6b05771f547a5d29ab6a4813aa",
+    "9cfc800c6185730ba881ba0c17038a72",
+    "4092590ff97327fe2a5fba8d7e81221f",
+    "0a84e993b89b11926120dbd5553a2692",
+    "a8e2672bbeeac54c04b0f645d0e51c69",
+    "efebc22877ba88144a807f85f8e0532b",
+    "60c8176ea51763f6e79831ed9e98eb15",
+    "bae091d7986a906a00a2c77bd81e913d",
+    "1d6701a28d686cb4a49b27e258932914",
+    "c3783425e90d64bdaaaadf375152d8b1",
+    "5460b46e999cc31fa08346960b647d5f",
+    "fe0f689aa615812ae88ca0fe5881f9b5",
+    "364effe8252a50aff00c098ff7b9dbc7",
+    "80d20971450192b92f8b975e673041f0",
+    "2eb1fdba702b2696769c988853c8ab62",
+    "96f39fa9b0556fbe686658ea76de3fc9",
+    "62656259ce12eb78e998a57c0504cf6c",
+    "60b402c81cde258a5b0ec7b7dc23e5c6",
+    "021dedc5b91c40449ab4d666ebd1390a",
+    "abc63ada241672284d538ec187c4d095",
+    "2697e649e2ac772566d9914f68b4d9e3",
+    "e08f68efb197aa11b82e6c5c1f5fe594",
+    "7afa749293be296fccb15ba5d4bad014",
+    "9b94bedc7e233a38cba241de620b03ce",
+    "c065e45486502ac97d5075a8255ead47",
+    "571c9aefdfdf2d63dc3d8a1c7c53f0d3",
+    "d8057bd424ef3270d21093a240b64b69",
+    "6515d657e051654f1bd2598a94fc629c",
+    "ba438c1a28cbc1c97e44c1e11ef0d5da",
+    "1c2609c77764bad37678deb82214e787",
+    "711b39c69640fd5322a179465e41800a",
+    "9c00cbf308c9e68eddf5dcf601837ec4",
+    "847a6885565416bbdb5cc10e3e3f56ad",
+    "9d6c4b5b1e85fe5b482b085bf59dbe08",
+    "47eb3a8661e1339441c99e45cab4cef2",
+    "8ac09ac3ee2cd9a69698b4a947905065",
+    "131497b7046103209b27d3a96ee058e5",
+    "cc257659350a6882101622f71f3bda17",
+    "06851634f7f93caefcb0bf3b7d2d9d62",
+    "79c2638d1284951d454c8a76b8925e5d",
+    "2973bb8b5db306f43b7fedd60a408322",
+    "128cdebd0276eb5649f47b6629b0d94a",
+    "ab3e86d0e05ed3d51ff8831f597cf1c8",
+    "b7604da3da48464e6171d3d00d64e99b",
+    "eef8db4bb3c03db811142de97cddd270",
+    "9376c4f9de5adc65afdd3f2054643370",
+    "2c4a96ce061ae939b12e91248e936557",
+    "0b30d5a80f23b0d5a07c5ac381fa22e5",
+    "fc4865f313d1e5993782787b347372e2",
+    "16155944c6c14fd6b1b83bc892a7267d",
+    "cc2d9e3a55a4bbd3ee8b58e0ec86b491",
+    "3035fc1cd983c4bcf983a65619847aa4",
+    "c7e2d818fea2eae3a95fc0af7674b7b4",
+    "56e2048b6b9569c77a559cc9402ee897",
+    "26dafb151f10dfe4bb58396b3ffd32c4",
+    "78eb1ff731e1d6d1747223231a7d32b8",
+    "395cceaaa3b5932f9235663808cfd4e9",
+    "9ab5f8bd2e01bf4ddcaadffd3d787714",
+    "d82d2f4418dcce19890bba694cad7d5f",
+    "4659660690a5eba2f5200c085c244111",
+    "318f4f6a746880f8d6a86a02fd6de2d8",
+    "286535a6f7a724afa2249bf424b30c92",
+    "c035911ccf6e970c79f0928cd58b1214",
+    "130b1e7d213c33726d51155774fda125",
+    "0cf5ac85baae39d43f0d8f88e50069c1",
+    "2804279d43189e6b6251fe4d791617e5",
+    "d672530e9c18ed0b862224760eab80d1",
+    "e0909510e4c51736d79c41c9b0bb8380",
+    "64b7380b87a84131eac615bc5e3a804e",
+    "f9d8703cd5b2dcdf363c5eb30a484e90",
+    "f230aea12bcd311bd4903c15949a49d4",
+    "7a0bdd68e197a0a104264a92ebd0b318",
+    "991fd7cd93a82ba301475430607f8b48",
+    "5f2cb6e7de63f6796756c327fc40f6ef",
+    "c4252228508d6a28deab3acf71309cb2",
+    "a6d2d02593f3087bab61109295937179",
+    "23b21446852c850b8bca5589ce1ea2c0",
+    "52d3032d6c9cd04e2f6425663d3da1b2",
+    "8b7bdc2dc4e6dfeb73ca7478faf1d9b3",
+    "c8d120e273be1dd2166e476b5ebd213a",
+    "ae7a5ab883aee28e543885432ae08363",
+    "d918c8c3cc0752379edc1bc571132458",
+    "0e82b56e2a3d4d86f92e5ce7ddf34e28",
+    "8a582d932d92f1b4cf714c4cef49e5f6",
+    "df74150037a5bf28850e9f69b9f625b4",
+    "3e63178b48ca6c4fa48548b406a0bbd5",
+    "77c0bf1195bb67e71e82bb63fb71e0aa",
+    "031a593b74a0bc536c14620c53a4d1f3",
+    "3199b239cf03dcf90161176fff02df60",
+    "8c34bdf2a2916f2da6ca47ef1f9bde00",
+    "fe621468c2d24b94ed96248c145ad5b1",
+    "bd27d6e49d96390e25f9510dbab86c90",
+    "1fe1a0ec743fa0f7029e4fd6f4d9ace6",
+    "38299f8d19e46e55cf5e889fb9299004",
+    "fd3b8e5df584881116033bf970ef4d77",
+    "5c715e6075e4acf384b318bf8fee3102",
+    "32f5f0d56f8ad79cf7850affc0c1fd48",
+    "47261104af6afec10bc1dc7541832fc9",
+    "de766d1080f625a50e8a7d797f2a9bb8",
+    "cd9ea735cf1a403136e08b3bd2b7e54d",
+    "9ccc88d0df2d7bb73cfc9de65332419d",
+    "13b9b29df7319c6604cbc9e2d02fc961",
+    "a5ebd7e17783f1dd92f6fcd3d78a2518",
+    "0705294fcdc639fefdc8c2117aeb5a9a",
+    "789e52b1c006ed9b3506b9a2915280c4",
+    "d94771050db81f2d9246743d658f6eed",
+    "7d6ceed6dee010932ed1e3e39f0f6587",
+    "a70f44135f31052f0c8deb81bd3a4196",
+    "b6fa23ee33a3d79e0247168f35e61562",
+    "687b3ef6600561f638c7db70f6e144be",
+    "9e1ca84bf1dec1226687bff5e6e00ef6",
+    "3e6c30b7dd84657ea47e16d195974379",
+    "7470f69d16208cb64defd7575d58e9a2",
+    "26600e013e452ad8432a6540d37e4297",
+    "e75066cc80c00185149eb905ce9e2d1d",
+    "76f83db132a075ab97a9aa8e8bdcdb58",
+    "332d38fab44ac872721b8fd874f5a6af",
+    "ff633252c0e9771e98cccb9011bc29fa",
+    "3d7b9ca6d8162be2b9fa5814ed5b41be",
+    "f840034ca0f19aeb460c0eef9416ac07",
+    "acca06f0f82cd219e4bfb1dd26d4e264",
+    "e1fc461818170a3a8a16baa4137a1e2a",
+    "28aebeb6642a6468a640a5d5cee9df80",
+    "56c6b2af5b2ffb60b9ee925fc26a00ed",
+    "9d840473ef0459f211c9aa2539712692",
+    "00b430c3560cac9d92b5bd6506f0c622",
+    "d6c4e17016a130925ef237a3555678f6",
+    "88ae296bae40574eac053af329da81cc",
+    "17533fd613286b431415e0c7dbb79061",
+    "cc60f6d82fea281cf5181f5e171b77d0",
+    "13443591288f6a25b76ce4cf5297177f",
+    "a01d2cb7efa082a6c96425c887f1b5e5",
+    "b3b7254e6816b8186fb496b4b664302a",
+    "47122ad5f33d09659fcfbb7c037587d7",
+    "2ae3f2f3fa05f333d2949c17ceeac8b8",
+    "cbcc16c6cd32662a78514c1da87ec3ab",
+    "c610f59aaceff811fb0f1dafb9210f19",
+    "1979b26d6234a0edfbeb15bb89c3270f",
+    "c4e5b0d94056ac1cb37196676efd39e6",
+    "c137cdc306a9dfef82be3b523e822548",
+    "bec6394e185db543b1fb840cddc8bf1c",
+    "d77bc4d3a7b9d450c4209a4dad33bee1",
+    "36ec9a9d7e9844c2bb8834804b92666b",
+    "9ef62d46f1d58ac606b09a4b2964b892",
+    "3c2ecc69e6d170a344e82f68070929fa",
+    "8ced6945737b30202c7436b4bb4ffe16",
+    "1164f3265cb50988131b857a1a95bf24",
+    "1ffd7e6b4cf6c03eb76de9c5731683e6",
+    "e3dc292fe56fdef69ac37551b4e6b7d1",
+    "2a8929041005f39e37ac12313c04858e",
+    "beb8660893cad60b217d316ec53df5f1",
+    "cdba1d861af5802cdbdc5110a2bb0b0e",
+    "51e64a56ed1647d76f5c4742f3e097ed",
+    "39410553bc6a4f7a5bf052fddade2dbe",
+    "53a26137a12e990598bdd2435cbc53a2",
+    "0be23327fd1d3bb06f5f5c7b172bc0b1",
+    "8fee322f97127596a32b078a2bdb0fed",
+    "2d979904bcbb32c4f8150fb5f8bc1520",
+    "277bfef37afe4927026088332cfa93ac",
+    "ba9748dfc3d5dc6666d7cca405ee9b10",
+    "2b712e5d6aaaa5270981e04fa54ab552",
+    "43ab8642b2a305aa0c3a750a8caeb8be",
+    "d3a82966ccf04c8428eaa1cf53cbcc7d",
+    "895b009ce5e7e783953d11ae7d26b0c7",
+    "9a3b11600f2cdf585f91c50694f95483",
+    "b9086011edce02c3359e7935c607f51f",
+    "1ed778f001666554b4325a1780325e1d",
+    "c8ff0c1c8a68461e0a5d54ce70349d77",
+    "e2cdd8639d5e44f87cb40e8d3c2d8388",
+    "9884eed47f96e40ef99b9e56cceffcea",
+    "c9666735fe424feb2b01dae39a73ec1a",
+    "f6cfe3cd15a2f412d803868d4a2c443d",
+    "028407903ad54460111d0b2f424ba293",
+    "1881557cabc055d8b82c79b6a6ec9a46",
+    "d4441c4cc53125fee0c5b8184efb825d",
+    "4b7aae7bc87b4fdf38099dc745422fdd",
+    "82d2179e3f27a822a532977ef8689c89",
+    "e1b0fa3da0f08963ccb94855720be942",
+    "d3cdb020ddaa3f4231584385324da03a",
+    "89033e7d8999713f959a0cb5a5bc6e25",
+    "6cf4a3194b8c5f9b6e5d72b80f8d6e58",
+    "44496a268163bdefc0f24bb3b0735298",
+    "3a9cf4e4bcc2fae4889342527a95f447",
+    "676016517bc7a1536c72b20c94531424",
+    "f7b03d19d41ec57d3162b3d40a6196d0",
+    "1bfce8405b703068f4ce063c219f80c6",
+    "5a4a33a778b8689d3f71d0dda49eb15e",
+    "5451241f22cdd192e4719585d2614e17",
+    "bfdf52ca5639550dbfa6c83d68cf68e7",
+    "1b9e832447082d52fcc6024880a4c0d7",
+    "2beaf5a356d5fd00568bc38774babb93",
+    "f4effc0d0d64f080cc3b4eaeb13a2ab8",
+    "fcd59329e6f64603be8672d62e1c08d1"
   ],
   "thumbnail_checksums": [
     "# Intel",
diff --git a/remoting/codec/webrtc_video_encoder_selector.cc b/remoting/codec/webrtc_video_encoder_selector.cc
index 1de9f855e..aae403a 100644
--- a/remoting/codec/webrtc_video_encoder_selector.cc
+++ b/remoting/codec/webrtc_video_encoder_selector.cc
@@ -57,6 +57,16 @@
   DCHECK_GE(codec, 0);
   DCHECK_LT(codec, static_cast<int>(encoders_.size()));
   preferred_codec_ = codec;
+
+  // Reset so that the next call to CreateEncoder() creates an encoder which
+  // matches the one negotiated over SDP. Otherwise, repeated calls to
+  // CreateEncoder() would cycle through every codec that could encode at the
+  // current resolution, even when they do not match the codec "created" by
+  // WebRTC via the DummyVideoEncoderFactory.
+
+  // TODO(crbug.com/1115789): Review the CreateEncoder() logic to ensure it only
+  // creates encoders that are compatible with the SDP-selected codec.
+  last_codec_ = -1;
 }
 
 int WebrtcVideoEncoderSelector::RegisterEncoder(
diff --git a/remoting/codec/webrtc_video_encoder_selector.h b/remoting/codec/webrtc_video_encoder_selector.h
index 82e28328..071fc807 100644
--- a/remoting/codec/webrtc_video_encoder_selector.h
+++ b/remoting/codec/webrtc_video_encoder_selector.h
@@ -35,6 +35,8 @@
 // 6. If the encoder failed, go back to 3 with the failed DesktopFrame.
 //
 // Selector will return nullptr if there is no more codec supporting |profile_|.
+// Calling SetPreferredCodec() again will reset the cycle, so that the next call
+// to CreateEncoder() will start with the codec from SDP.
 class WebrtcVideoEncoderSelector final {
  public:
   struct Profile {
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc
index a05b067..e96df4c3 100644
--- a/remoting/protocol/webrtc_video_stream.cc
+++ b/remoting/protocol/webrtc_video_stream.cc
@@ -351,6 +351,16 @@
     webrtc::VideoCodecType codec_type,
     const webrtc::SdpVideoFormat::Parameters& parameters) {
   DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Reset the encoder in case a previous one was being used and SDP
+  // re-negotiation selected a different one. The proper codec will be
+  // created after the next frame is captured.
+  // An optimization would be to reset only if the new encoder is different
+  // from the current one. However, SDP renegotiation is expected to occur
+  // infrequently (only when the user changes a setting), and should typically
+  // not cause the same codec to be repeatedly selected.
+  encoder_.reset();
+
   // The preferred codec id depends on the order of
   // |encoder_selector_|.RegisterEncoder().
   if (codec_type == webrtc::kVideoCodecVP8) {
diff --git a/sandbox/policy/mac/gpu_v2.sb b/sandbox/policy/mac/gpu_v2.sb
index c1ee3ca..543a2724 100644
--- a/sandbox/policy/mac/gpu_v2.sb
+++ b/sandbox/policy/mac/gpu_v2.sb
@@ -14,6 +14,7 @@
 ; Allow communication between the GPU process and the UI server.
 (allow mach-lookup
   (global-name "com.apple.bsd.dirhelper")
+  (global-name "com.apple.CARenderServer")
   (global-name "com.apple.cfprefsd.agent")
   (global-name "com.apple.cfprefsd.daemon")
   (global-name "com.apple.CoreServices.coreservicesd")
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index bf17e62..0b5e702bb 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -4455,7 +4455,6 @@
         "args": [
           "--gtest_filter=-NetExportFileWriterTest*"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4483,7 +4482,6 @@
         "args": [
           "--gtest_filter=-OutOfProcessPPAPITest.TrueTypeFont"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4591,7 +4589,6 @@
         "args": [
           "--gtest_filter=-BluetoothShellApiTest.ApiSanityCheck:BluetoothSocketApiTest.Listen:BluetoothSocketApiTest.PermissionDenied"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4608,7 +4605,6 @@
           "--enable-features=StorageServiceOutOfProcess",
           "--gtest_filter=-BluetoothShellApiTest.ApiSanityCheck:BluetoothSocketApiTest.Listen:BluetoothSocketApiTest.PermissionDenied"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5049,7 +5045,6 @@
         "args": [
           "--gtest_filter=-SingleClientSessionsSyncTestWithFaviconTestServer.ShouldDeleteOnDemandIconsOnSessionsDisabled"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json
index fa762a6..dfbb7145 100644
--- a/testing/buildbot/chromium.ci.json
+++ b/testing/buildbot/chromium.ci.json
@@ -19067,24 +19067,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -20756,24 +20738,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -115796,18 +115760,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -117035,18 +116987,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -118240,18 +118180,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -119995,24 +119923,6 @@
           ],
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -121818,24 +121728,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -123641,24 +123533,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -124953,18 +124827,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -126192,18 +126054,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -127431,18 +127281,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -128670,18 +128508,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -133093,17 +132919,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -134275,18 +134090,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -145404,24 +145207,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -147533,24 +147318,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -149588,24 +149355,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-17134"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -151743,24 +151492,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-18363"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -158180,18 +157911,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -159654,18 +159373,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -255700,7 +255407,6 @@
         "args": [
           "--gtest_filter=-NetExportFileWriterTest*"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -255728,7 +255434,6 @@
         "args": [
           "--gtest_filter=-OutOfProcessPPAPITest.TrueTypeFont"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -255836,7 +255541,6 @@
         "args": [
           "--gtest_filter=-BluetoothShellApiTest.ApiSanityCheck:BluetoothSocketApiTest.Listen:BluetoothSocketApiTest.PermissionDenied"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -255853,7 +255557,6 @@
           "--enable-features=StorageServiceOutOfProcess",
           "--gtest_filter=-BluetoothShellApiTest.ApiSanityCheck:BluetoothSocketApiTest.Listen:BluetoothSocketApiTest.PermissionDenied"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -256294,7 +255997,6 @@
         "args": [
           "--gtest_filter=-SingleClientSessionsSyncTestWithFaviconTestServer.ShouldDeleteOnDemandIconsOnSessionsDisabled"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -261318,23 +261020,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -267450,23 +267135,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -270205,19 +269873,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -270340,6 +269995,27 @@
       }
     ]
   },
+  "win10-omaha-tester-rel": {
+    "gtest_tests": [
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "integrity": "high"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "updater_tests",
+        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
+      }
+    ]
+  },
   "win32-archive-dbg": {
     "additional_compile_targets": [
       "all"
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index d484e682..615b8f6 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -3060,24 +3060,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -4749,24 +4731,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -27160,18 +27124,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -28399,18 +28351,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -29604,18 +29544,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -31359,24 +31287,6 @@
           ],
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -33182,24 +33092,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -35005,24 +34897,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -36317,18 +36191,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -37556,18 +37418,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -38795,18 +38645,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -40034,18 +39872,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -44691,23 +44517,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index b982281..e25acd85 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1667,17 +1667,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -3393,24 +3382,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-17134"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -5548,24 +5519,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-18363"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -65576,19 +65529,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -65711,6 +65651,27 @@
       }
     ]
   },
+  "win10-omaha-tester-rel": {
+    "gtest_tests": [
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "integrity": "high"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "updater_tests",
+        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
+      }
+    ]
+  },
   "win7-blink-rel-dummy": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index fbb1703..4a3d7501 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -19157,23 +19157,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 6444471..9f4bc8c1 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -1097,18 +1097,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -3226,24 +3214,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -5355,24 +5325,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -6962,18 +6914,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
@@ -8436,18 +8376,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "url_unittests",
         "test_id_prefix": "ninja://url:url_unittests/"
       },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index daeca87f..31b7635 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -837,7 +837,6 @@
       },
       # https://crbug.com/1111979,
       'linux-lacros-tester-rel': {
-        'experiment_percentage': 100,
         'args': [
           '--gtest_filter=-NetExportFileWriterTest*',
         ],
@@ -976,7 +975,6 @@
       },
       # https://crbug.com/1111979,
       'linux-lacros-tester-rel': {
-        'experiment_percentage': 100,
         'args': [
           '--gtest_filter=-OutOfProcessPPAPITest.TrueTypeFont',
         ],
@@ -1155,7 +1153,6 @@
       },
       # https://crbug.com/1111979,
       'linux-lacros-tester-rel': {
-        'experiment_percentage': 100,
         'args': [
           '--gtest_filter=-BluetoothShellApiTest.ApiSanityCheck:BluetoothSocketApiTest.Listen:BluetoothSocketApiTest.PermissionDenied',
         ],
@@ -2365,7 +2362,6 @@
       },
       # https://crbug.com/1111979,
       'linux-lacros-tester-rel': {
-        'experiment_percentage': 100,
         'args': [
           '--gtest_filter=-BluetoothShellApiTest.ApiSanityCheck:BluetoothSocketApiTest.Listen:BluetoothSocketApiTest.PermissionDenied',
         ],
@@ -2442,7 +2438,6 @@
       },
       # https://crbug.com/1111979,
       'linux-lacros-tester-rel': {
-        'experiment_percentage': 100,
         'args': [
           '--gtest_filter=-SingleClientSessionsSyncTestWithFaviconTestServer.ShouldDeleteOnDemandIconsOnSessionsDisabled',
         ],
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 2897562..cb927226 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3686,6 +3686,10 @@
       },
     },
 
+    'omaha_gtests': {
+      'updater_tests': {},
+    },
+
     'opus_tests': {
       'opus_tests': {
         'args': [
@@ -4360,7 +4364,6 @@
           ],
         },
       },
-      'updater_tests': {},
       'zucchini_unittests': {},
     },
 
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 6c0565a..13373af 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2472,6 +2472,18 @@
           'gtest_tests': 'chromium_win_gtests',
         },
       },
+      'win10-omaha-tester-rel': {
+        'test_suites': {
+          'gtest_tests': 'omaha_gtests',
+        },
+        'swarming': {
+          'dimension_sets': [
+            {
+              'integrity': 'high',
+            }
+          ]
+        },
+      },
       'win7-blink-rel-dummy': {
         'swarming': {
           'dimension_sets': [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 15a5123..041a5991 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1675,6 +1675,25 @@
             ]
         }
     ],
+    "CompositingOptimizations": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "CompositingOptimizations"
+                    ]
+                }
+            ]
+        }
+    ],
     "ContentCapture": [
         {
             "platforms": [
@@ -3545,6 +3564,21 @@
             ]
         }
     ],
+    "IOSMessagesOverlay": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "InfobarOverlayUI"
+                    ]
+                }
+            ]
+        }
+    ],
     "IOSPageInfoRefactoring": [
         {
             "platforms": [
@@ -5156,26 +5190,6 @@
             ]
         }
     ],
-    "PaintHolding": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "PaintHolding"
-                    ]
-                }
-            ]
-        }
-    ],
     "PaintHoldingCrossOrigin": [
         {
             "platforms": [
@@ -8375,6 +8389,21 @@
             ]
         }
     ],
+    "WebViewHeaderInjection": [
+        {
+            "platforms": [
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "SameDomainOnly",
+                    "enable_features": [
+                        "WebViewExtraHeadersSameDomainOnly"
+                    ]
+                }
+            ]
+        }
+    ],
     "WebViewOriginCheckForStreamReader": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/00-bug_report.md b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/00-bug_report.md
new file mode 100644
index 0000000..1edf3de0
--- /dev/null
+++ b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/00-bug_report.md
@@ -0,0 +1,41 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: 'bug'
+assignees: ''
+---
+
+**Describe the bug**
+
+Include a clear and concise description of what the problem is, including what
+you expected to happen, and what actually happened.
+
+**Steps to reproduce the bug**
+
+It's important that we are able to reproduce the problem that you are
+experiencing. Please provide all code and relevant steps to reproduce the
+problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links
+to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the
+problem are also helpful.
+
+**What version of Abseil are you using?**
+
+**What operating system and version are you using**
+
+If you are using a Linux distribution please include the name and version of the
+distribution as well.
+
+**What compiler and version are you using?**
+
+Please include the output of `gcc -v` or `clang -v`, or the equivalent for your
+compiler.
+
+**What build system are you using?**
+
+Please include the output of `bazel --version` or `cmake --version`, or the
+equivalent for your build system.
+
+**Additional context**
+
+Add any other context about the problem here.
diff --git a/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/90-question.md b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/90-question.md
new file mode 100644
index 0000000..84cf349
--- /dev/null
+++ b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/90-question.md
@@ -0,0 +1,7 @@
+---
+name: Question
+about: Have a question? Ask us anything! :-)
+title: ''
+labels: 'question'
+assignees: ''
+---
diff --git a/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/config.yml b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..9794ae1
--- /dev/null
+++ b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1 @@
+blank_issues_enables: true
diff --git a/third_party/abseil-cpp/CMake/AbseilDll.cmake b/third_party/abseil-cpp/CMake/AbseilDll.cmake
index c001621..af7c004 100644
--- a/third_party/abseil-cpp/CMake/AbseilDll.cmake
+++ b/third_party/abseil-cpp/CMake/AbseilDll.cmake
@@ -138,7 +138,6 @@
   "random/internal/distribution_caller.h"
   "random/internal/fastmath.h"
   "random/internal/fast_uniform_bits.h"
-  "random/internal/gaussian_distribution_gentables.cc"
   "random/internal/generate_real.h"
   "random/internal/iostream_state_saver.h"
   "random/internal/mock_helpers.h"
diff --git a/third_party/abseil-cpp/README.chromium b/third_party/abseil-cpp/README.chromium
index bf23d0a..edf51b4 100644
--- a/third_party/abseil-cpp/README.chromium
+++ b/third_party/abseil-cpp/README.chromium
@@ -4,7 +4,7 @@
 License: Apache 2.0
 License File: LICENSE
 Version: 0
-Revision: f66bc749282dd7cffc68b641f527740e95e90cfa
+Revision: 1beb3191c20ea315186dc761540d25c4939f1892
 Security Critical: yes
 
 Description:
diff --git a/third_party/abseil-cpp/absl/base/internal/bits.h b/third_party/abseil-cpp/absl/base/internal/bits.h
index 14c51d8b..81648e2 100644
--- a/third_party/abseil-cpp/absl/base/internal/bits.h
+++ b/third_party/abseil-cpp/absl/base/internal/bits.h
@@ -83,10 +83,11 @@
 #elif defined(_MSC_VER) && !defined(__clang__)
   // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
   unsigned long result = 0;  // NOLINT(runtime/int)
-  if ((n >> 32) && _BitScanReverse(&result, n >> 32)) {
+  if ((n >> 32) &&
+      _BitScanReverse(&result, static_cast<unsigned long>(n >> 32))) {
     return 31 - result;
   }
-  if (_BitScanReverse(&result, n)) {
+  if (_BitScanReverse(&result, static_cast<unsigned long>(n))) {
     return 63 - result;
   }
   return 64;
@@ -170,10 +171,10 @@
 #elif defined(_MSC_VER) && !defined(__clang__)
   unsigned long result = 0;  // NOLINT(runtime/int)
   if (static_cast<uint32_t>(n) == 0) {
-    _BitScanForward(&result, n >> 32);
+    _BitScanForward(&result, static_cast<unsigned long>(n >> 32));
     return result + 32;
   }
-  _BitScanForward(&result, n);
+  _BitScanForward(&result, static_cast<unsigned long>(n));
   return result;
 #elif defined(__GNUC__) || defined(__clang__)
   static_assert(sizeof(unsigned long long) == sizeof(n),  // NOLINT(runtime/int)
diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
index 1f304b6..48a1a8c 100644
--- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
+++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
@@ -523,7 +523,8 @@
 // if they are equal, false if they are not. If two keys compare equal, then
 // their hash values as defined by Hash MUST be equal.
 //
-// Allocator: an Allocator [https://devdocs.io/cpp/concept/allocator] with which
+// Allocator: an Allocator
+// [https://en.cppreference.com/w/cpp/named_req/Allocator] with which
 // the storage of the hashtable will be allocated and the elements will be
 // constructed and destroyed.
 template <class Policy, class Hash, class Eq, class Alloc>
diff --git a/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake b/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake
index 935eb6d..97bd283e 100644
--- a/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake
+++ b/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake
@@ -89,6 +89,7 @@
     "-Wwrite-strings"
     "-Wno-missing-field-initializers"
     "-Wno-sign-compare"
+    "-DNOMINMAX"
 )
 
 list(APPEND ABSL_GCC_TEST_FLAGS
@@ -146,6 +147,7 @@
     "-Wobjc-literal-conversion"
     "-Wno-sign-conversion"
     "-Wstring-conversion"
+    "-DNOMINMAX"
 )
 
 list(APPEND ABSL_LLVM_TEST_FLAGS
diff --git a/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl b/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl
index dad5b28..bcdd61e 100644
--- a/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl
+++ b/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl
@@ -90,6 +90,7 @@
     "-Wwrite-strings",
     "-Wno-missing-field-initializers",
     "-Wno-sign-compare",
+    "-DNOMINMAX",
 ]
 
 ABSL_GCC_TEST_FLAGS = [
@@ -147,6 +148,7 @@
     "-Wobjc-literal-conversion",
     "-Wno-sign-conversion",
     "-Wstring-conversion",
+    "-DNOMINMAX",
 ]
 
 ABSL_LLVM_TEST_FLAGS = [
diff --git a/third_party/abseil-cpp/absl/copts/copts.py b/third_party/abseil-cpp/absl/copts/copts.py
index 7c3edb6..a3437c1 100644
--- a/third_party/abseil-cpp/absl/copts/copts.py
+++ b/third_party/abseil-cpp/absl/copts/copts.py
@@ -141,6 +141,8 @@
         # Google style does not use unsigned integers, though STL containers
         # have unsigned types.
         "-Wno-sign-compare",
+        # Don't define min and max macros (Build on Windows using gcc)
+        "-DNOMINMAX",
     ],
     "ABSL_GCC_TEST_FLAGS": [
         "-Wno-conversion-null",
@@ -152,7 +154,10 @@
         "-Wno-unused-private-field",
     ],
     "ABSL_LLVM_FLAGS":
-        LLVM_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS,
+        LLVM_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS + [
+            # Don't define min and max macros (Build on Windows using clang)
+            "-DNOMINMAX",
+        ],
     "ABSL_LLVM_TEST_FLAGS":
         LLVM_TEST_DISABLE_WARNINGS_FLAGS,
     "ABSL_CLANG_CL_FLAGS":
diff --git a/third_party/abseil-cpp/absl/flags/BUILD.bazel b/third_party/abseil-cpp/absl/flags/BUILD.bazel
index 524e702..92bd4f15 100644
--- a/third_party/abseil-cpp/absl/flags/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/flags/BUILD.bazel
@@ -465,6 +465,7 @@
         ":marshalling",
         ":reflection",
         "//absl/memory",
+        "//absl/strings",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/third_party/abseil-cpp/absl/flags/BUILD.gn b/third_party/abseil-cpp/absl/flags/BUILD.gn
index a9fbbcb1..99c61ca 100644
--- a/third_party/abseil-cpp/absl/flags/BUILD.gn
+++ b/third_party/abseil-cpp/absl/flags/BUILD.gn
@@ -4,13 +4,7 @@
 
 import("//third_party/abseil-cpp/absl.gni")
 
-# Build targets in this module are marked as "testonly" because it is not clear
-# how ABSL_FLAG will interact with //base/command_line.h.
-# If this is a problem, feel free to remove "testonly" and use "assert_no_deps"
-# on the main Chrome binary.
-
 absl_source_set("path_util") {
-  testonly = true
   public = [ "internal/path_util.h" ]
   deps = [
     "//third_party/abseil-cpp/absl/base:config",
@@ -20,7 +14,6 @@
 }
 
 absl_source_set("program_name") {
-  testonly = true
   sources = [ "internal/program_name.cc" ]
   public = [ "internal/program_name.h" ]
   deps = [
@@ -34,7 +27,6 @@
 }
 
 absl_source_set("config") {
-  testonly = true
   sources = [ "usage_config.cc" ]
   public = [
     "config.h",
@@ -51,7 +43,6 @@
 }
 
 absl_source_set("marshalling") {
-  testonly = true
   sources = [ "marshalling.cc" ]
   public = [ "marshalling.h" ]
   deps = [
@@ -64,7 +55,6 @@
 }
 
 absl_source_set("commandlineflag_internal") {
-  testonly = true
   public = [ "internal/commandlineflag.h" ]
   sources = [ "internal/commandlineflag.cc" ]
   deps = [
@@ -75,7 +65,6 @@
 }
 
 absl_source_set("commandlineflag") {
-  testonly = true
   sources = [ "commandlineflag.cc" ]
   public = [ "commandlineflag.h" ]
   deps = [
@@ -88,7 +77,6 @@
 }
 
 absl_source_set("private_handle_accessor") {
-  testonly = true
   sources = [ "internal/private_handle_accessor.cc" ]
   public = [ "internal/private_handle_accessor.h" ]
   deps = [
@@ -101,7 +89,6 @@
 }
 
 absl_source_set("reflection") {
-  testonly = true
   sources = [ "reflection.cc" ]
   public = [
     "internal/registry.h",
@@ -120,7 +107,6 @@
 }
 
 absl_source_set("flag_internal") {
-  testonly = true
   sources = [ "internal/flag.cc" ]
   public = [ "internal/flag.h" ]
   deps = [
@@ -145,7 +131,6 @@
 }
 
 absl_source_set("flag") {
-  testonly = true
   sources = [ "flag.cc" ]
   public = [
     "declare.h",
@@ -163,7 +148,6 @@
 }
 
 absl_source_set("usage_internal") {
-  testonly = true
   sources = [ "internal/usage.cc" ]
   public = [ "internal/usage.h" ]
   deps = [
@@ -183,7 +167,6 @@
 }
 
 absl_source_set("usage") {
-  testonly = true
   sources = [ "usage.cc" ]
   public = [ "usage.h" ]
   deps = [
@@ -196,7 +179,6 @@
 }
 
 absl_source_set("parse") {
-  testonly = true
   sources = [ "parse.cc" ]
   public = [
     "internal/parse.h",
diff --git a/third_party/abseil-cpp/absl/flags/reflection_test.cc b/third_party/abseil-cpp/absl/flags/reflection_test.cc
index 9781e59..2da0a0e 100644
--- a/third_party/abseil-cpp/absl/flags/reflection_test.cc
+++ b/third_party/abseil-cpp/absl/flags/reflection_test.cc
@@ -23,6 +23,8 @@
 #include "absl/flags/internal/commandlineflag.h"
 #include "absl/flags/marshalling.h"
 #include "absl/memory/memory.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_split.h"
 
 ABSL_FLAG(int, int_flag, 1, "int_flag help");
 ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help");
@@ -57,4 +59,183 @@
   EXPECT_NE(handle, nullptr);
 }
 
+// --------------------------------------------------------------------
+
+struct CustomUDT {
+  CustomUDT() : a(1), b(1) {}
+  CustomUDT(int a_, int b_) : a(a_), b(b_) {}
+
+  friend bool operator==(const CustomUDT& f1, const CustomUDT& f2) {
+    return f1.a == f2.a && f1.b == f2.b;
+  }
+
+  int a;
+  int b;
+};
+bool AbslParseFlag(absl::string_view in, CustomUDT* f, std::string*) {
+  std::vector<absl::string_view> parts =
+      absl::StrSplit(in, ':', absl::SkipWhitespace());
+
+  if (parts.size() != 2) return false;
+
+  if (!absl::SimpleAtoi(parts[0], &f->a)) return false;
+
+  if (!absl::SimpleAtoi(parts[1], &f->b)) return false;
+
+  return true;
+}
+std::string AbslUnparseFlag(const CustomUDT& f) {
+  return absl::StrCat(f.a, ":", f.b);
+}
+
+}  // namespace
+
+// --------------------------------------------------------------------
+
+ABSL_FLAG(bool, test_flag_01, true, "");
+ABSL_FLAG(int, test_flag_02, 1234, "");
+ABSL_FLAG(int16_t, test_flag_03, -34, "");
+ABSL_FLAG(uint16_t, test_flag_04, 189, "");
+ABSL_FLAG(int32_t, test_flag_05, 10765, "");
+ABSL_FLAG(uint32_t, test_flag_06, 40000, "");
+ABSL_FLAG(int64_t, test_flag_07, -1234567, "");
+ABSL_FLAG(uint64_t, test_flag_08, 9876543, "");
+ABSL_FLAG(double, test_flag_09, -9.876e-50, "");
+ABSL_FLAG(float, test_flag_10, 1.234e12f, "");
+ABSL_FLAG(std::string, test_flag_11, "", "");
+ABSL_FLAG(absl::Duration, test_flag_12, absl::Minutes(10), "");
+static int counter = 0;
+ABSL_FLAG(int, test_flag_13, 200, "").OnUpdate([]() { counter++; });
+ABSL_FLAG(CustomUDT, test_flag_14, {}, "");
+
+namespace {
+
+TEST_F(ReflectionTest, TestFlagSaverInScope) {
+  {
+    absl::FlagSaver s;
+    counter = 0;
+    absl::SetFlag(&FLAGS_test_flag_01, false);
+    absl::SetFlag(&FLAGS_test_flag_02, -1021);
+    absl::SetFlag(&FLAGS_test_flag_03, 6009);
+    absl::SetFlag(&FLAGS_test_flag_04, 44);
+    absl::SetFlag(&FLAGS_test_flag_05, +800);
+    absl::SetFlag(&FLAGS_test_flag_06, -40978756);
+    absl::SetFlag(&FLAGS_test_flag_07, 23405);
+    absl::SetFlag(&FLAGS_test_flag_08, 975310);
+    absl::SetFlag(&FLAGS_test_flag_09, 1.00001);
+    absl::SetFlag(&FLAGS_test_flag_10, -3.54f);
+    absl::SetFlag(&FLAGS_test_flag_11, "asdf");
+    absl::SetFlag(&FLAGS_test_flag_12, absl::Hours(20));
+    absl::SetFlag(&FLAGS_test_flag_13, 4);
+    absl::SetFlag(&FLAGS_test_flag_14, CustomUDT{-1, -2});
+  }
+
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543);
+  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55);
+  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "");
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10));
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), 200);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_14), CustomUDT{});
+  EXPECT_EQ(counter, 2);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ReflectionTest, TestFlagSaverVsUpdateViaReflection) {
+  {
+    absl::FlagSaver s;
+    counter = 0;
+    std::string error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_01")->ParseFrom("false", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_02")->ParseFrom("-4536", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_03")->ParseFrom("111", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_04")->ParseFrom("909", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_05")->ParseFrom("-2004", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_06")->ParseFrom("1000023", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_07")->ParseFrom("69305", &error))
+        << error;
+    EXPECT_TRUE(absl::FindCommandLineFlag("test_flag_08")
+                    ->ParseFrom("1000000001", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_09")->ParseFrom("2.09021", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_10")->ParseFrom("-33.1", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_11")->ParseFrom("ADD_FOO", &error))
+        << error;
+    EXPECT_TRUE(absl::FindCommandLineFlag("test_flag_12")
+                    ->ParseFrom("3h11m16s", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_13")->ParseFrom("0", &error))
+        << error;
+    EXPECT_TRUE(
+        absl::FindCommandLineFlag("test_flag_14")->ParseFrom("10:1", &error))
+        << error;
+  }
+
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543);
+  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55);
+  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "");
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10));
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), 200);
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_14), CustomUDT{});
+  EXPECT_EQ(counter, 2);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ReflectionTest, TestMultipleFlagSaversInEnclosedScopes) {
+  {
+    absl::FlagSaver s;
+    absl::SetFlag(&FLAGS_test_flag_08, 10);
+    EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 10);
+    {
+      absl::FlagSaver s;
+      absl::SetFlag(&FLAGS_test_flag_08, 20);
+      EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 20);
+      {
+        absl::FlagSaver s;
+        absl::SetFlag(&FLAGS_test_flag_08, -200);
+        EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), -200);
+      }
+      EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 20);
+    }
+    EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 10);
+  }
+  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543);
+}
+
 }  // namespace
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc
index 3fcc75b..a14982a93 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -153,7 +153,8 @@
   std::tm tm;
   while (lo + 1 != hi) {
     const std::time_t mid = lo + (hi - lo) / 2;
-    if (std::tm* tmp = local_time(&mid, &tm)) {
+    std::tm* tmp = local_time(&mid, &tm);
+    if (tmp != nullptr) {
       if (tm_gmtoff(*tmp) == offset) {
         hi = mid;
       } else {
@@ -163,7 +164,8 @@
       // If std::tm cannot hold some result we resort to a linear search,
       // ignoring all failed conversions.  Slow, but never really happens.
       while (++lo != hi) {
-        if (std::tm* tmp = local_time(&lo, &tm)) {
+        tmp = local_time(&lo, &tm);
+        if (tmp != nullptr) {
           if (tm_gmtoff(*tmp) == offset) break;
         }
       }
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index 8f7ab15..53641bfe 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -1004,13 +1004,13 @@
 #if defined(_WIN32) || defined(_WIN64)
     // localtime_s() and gmtime_s() don't believe in years outside [1970:3000].
 #else
-    const time_zone utc = LoadZone("libc:UTC");
+    const time_zone cut = LoadZone("libc:UTC");
     const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900;
-    tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), utc);
-    EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, utc));
+    tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut);
+    EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, cut));
     const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
-    tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), utc);
-    EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, utc));
+    tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
+    EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
 #endif
   }
 }
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index c6d8ff0..35406b21 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2706,6 +2706,7 @@
   kV8RTCRtpTransceiver_Stop_Method = 3375,
   kSecurePaymentConfirmation = 3376,
   kCSSInvalidVariableUnset = 3377,
+  kElementInternalsShadowRoot = 3378,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
index 37f6ed6..6fdce97 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
@@ -395,7 +395,7 @@
     const QualifiedName& content_attribute,
     const char* interface_name,
     const char* attribute_name) {
-  PerformAttributeSetCEReactionsReflect<IDLStringV2, AtomicString,
+  PerformAttributeSetCEReactionsReflect<IDLStringV2, const AtomicString&,
                                         &Element::setAttribute>(
       info, content_attribute, interface_name, attribute_name);
 }
@@ -406,7 +406,8 @@
     const char* interface_name,
     const char* attribute_name) {
   PerformAttributeSetCEReactionsReflect<IDLStringTreatNullAsEmptyStringV2,
-                                        AtomicString, &Element::setAttribute>(
+                                        const AtomicString&,
+                                        &Element::setAttribute>(
       info, content_attribute, interface_name, attribute_name);
 }
 
@@ -415,8 +416,8 @@
     const QualifiedName& content_attribute,
     const char* interface_name,
     const char* attribute_name) {
-  PerformAttributeSetCEReactionsReflect<IDLNullable<IDLStringV2>, AtomicString,
-                                        &Element::setAttribute>(
+  PerformAttributeSetCEReactionsReflect<
+      IDLNullable<IDLStringV2>, const AtomicString&, &Element::setAttribute>(
       info, content_attribute, interface_name, attribute_name);
 }
 
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 69023367..6fb6c65 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -639,6 +639,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_save_file_picker_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_options.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_request.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_request.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_sensor_error_event_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_sensor_error_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_sensor_options.cc",
@@ -1022,6 +1024,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_screen_idle_state.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_sdp_semantics.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_sdp_semantics.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_action.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_action.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_service_worker_state.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_service_worker_state.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_service_worker_update_via_cache.cc",
@@ -2140,6 +2144,8 @@
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_serial_port_request_options.h",
   ]
   generated_enumeration_sources_in_modules += [
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_flow_control_type.cc",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_flow_control_type.h",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_parity_type.cc",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_parity_type.h",
   ]
diff --git a/third_party/blink/renderer/core/css/style_change_reason.cc b/third_party/blink/renderer/core/css/style_change_reason.cc
index 9a660dd1..28a564ce 100644
--- a/third_party/blink/renderer/core/css/style_change_reason.cc
+++ b/third_party/blink/renderer/core/css/style_change_reason.cc
@@ -19,9 +19,7 @@
 const char kDeclarativeContent[] = "Extension declarativeContent.css";
 const char kDesignMode[] = "DesignMode";
 const char kDisplayLock[] = "DisplayLock";
-const char kFindInvisible[] = "FindInvisible";
 const char kFlatTreeChange[] = "FlatTreeChange";
-const char kFontSizeChange[] = "FontSizeChange";
 const char kFonts[] = "Fonts";
 const char kFrame[] = "Frame";
 const char kFullscreen[] = "Fullscreen";
@@ -31,24 +29,18 @@
     "Inline CSS style declaration was mutated";
 const char kInspector[] = "Inspector";
 const char kLanguage[] = "Language";
-const char kInvisibleChange[] = "InvisibleChange";
-const char kLazyReattach[] = "LazyReattach";
 const char kLinkColorChange[] = "LinkColorChange";
 const char kPlatformColorChange[] = "PlatformColorChange";
 const char kPluginChanged[] = "Plugin Changed";
-const char kPolicyViolation[] = "Feature Policy Violation";
 const char kPropertyRegistration[] = "PropertyRegistration";
-const char kPropertyUnregistration[] = "PropertyUnregistration";
 const char kPseudoClass[] = "PseudoClass";
 const char kSVGContainerSizeChange[] = "SVGContainerSizeChange";
-const char kSVGCursor[] = "SVGCursor";
 const char kSettings[] = "Settings";
 const char kShadow[] = "Shadow";
 const char kStyleInvalidator[] = "StyleInvalidator";
 const char kStyleSheetChange[] = "StyleSheetChange";
 const char kUseFallback[] = "UseFallback";
 const char kViewportUnits[] = "ViewportUnits";
-const char kVisitedLink[] = "VisitedLink";
 const char kVisuallyOrdered[] = "VisuallyOrdered";
 const char kWritingModeChange[] = "WritingModeChange";
 const char kZoom[] = "Zoom";
diff --git a/third_party/blink/renderer/core/css/style_change_reason.h b/third_party/blink/renderer/core/css/style_change_reason.h
index bece22e6..4415e46294 100644
--- a/third_party/blink/renderer/core/css/style_change_reason.h
+++ b/third_party/blink/renderer/core/css/style_change_reason.h
@@ -24,32 +24,24 @@
 extern const char kDisplayLock[];
 extern const char kFrame[];
 extern const char kFlatTreeChange[];
-extern const char kFontSizeChange[];
 extern const char kFonts[];
 extern const char kFullscreen[];
-extern const char kFindInvisible[];
 extern const char kInheritedStyleChangeFromParentFrame[];
 extern const char kInlineCSSStyleMutated[];
 extern const char kInspector[];
-extern const char kInvisibleChange[];
 extern const char kLanguage[];
-extern const char kLazyReattach[];
 extern const char kLinkColorChange[];
 extern const char kPlatformColorChange[];
 extern const char kPluginChanged[];
-extern const char kPolicyViolation[];
 extern const char kPropertyRegistration[];
-extern const char kPropertyUnregistration[];
 extern const char kPseudoClass[];
 extern const char kSVGContainerSizeChange[];
-extern const char kSVGCursor[];
 extern const char kSettings[];
 extern const char kShadow[];
 extern const char kStyleInvalidator[];
 extern const char kStyleSheetChange[];
 extern const char kUseFallback[];
 extern const char kViewportUnits[];
-extern const char kVisitedLink[];
 extern const char kVisuallyOrdered[];
 extern const char kWritingModeChange[];
 extern const char kZoom[];
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
index b8b7a5ef..43c72ec3 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
@@ -856,24 +856,6 @@
   return is_allowed;
 }
 
-bool ContentSecurityPolicy::AllowAncestors(
-    LocalFrame* frame,
-    const KURL& url,
-    ReportingDisposition reporting_disposition) const {
-  bool is_allowed = true;
-  for (const auto& policy : policies_)
-    is_allowed &= policy->AllowAncestors(frame, url, reporting_disposition);
-  return is_allowed;
-}
-
-bool ContentSecurityPolicy::IsFrameAncestorsEnforced() const {
-  for (const auto& policy : policies_) {
-    if (policy->IsFrameAncestorsEnforced())
-      return true;
-  }
-  return false;
-}
-
 bool ContentSecurityPolicy::AllowTrustedTypeAssignmentFailure(
     const String& message,
     const String& sample,
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.h b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
index 3d820757..1ffbbe5c 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.h
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
@@ -311,18 +311,6 @@
 
   static bool IsScriptInlineType(InlineType);
 
-  // |allowAncestors| does not need to know whether the resource was a
-  // result of a redirect. After a redirect, source paths are usually
-  // ignored to stop a page from learning the path to which the
-  // request was redirected, but this is not a concern for ancestors,
-  // because a child frame can't manipulate the URL of a cross-origin
-  // parent.
-  bool AllowAncestors(
-      LocalFrame*,
-      const KURL&,
-      ReportingDisposition = ReportingDisposition::kReport) const;
-  bool IsFrameAncestorsEnforced() const;
-
   // TODO(crbug.com/889751): Remove "mojom::RequestContextType" once
   // all the code migrates.
   bool AllowRequestWithoutIntegrity(
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
index 86c23184..797e4179 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
@@ -189,23 +189,6 @@
                                      ReportingDisposition::kSuppressReporting));
 }
 
-TEST_F(ContentSecurityPolicyTest, IsFrameAncestorsEnforced) {
-  csp->DidReceiveHeader("script-src 'none';",
-                        ContentSecurityPolicyType::kEnforce,
-                        ContentSecurityPolicySource::kHTTP);
-  EXPECT_FALSE(csp->IsFrameAncestorsEnforced());
-
-  csp->DidReceiveHeader("frame-ancestors 'self'",
-                        ContentSecurityPolicyType::kReport,
-                        ContentSecurityPolicySource::kHTTP);
-  EXPECT_FALSE(csp->IsFrameAncestorsEnforced());
-
-  csp->DidReceiveHeader("frame-ancestors 'self'",
-                        ContentSecurityPolicyType::kEnforce,
-                        ContentSecurityPolicySource::kHTTP);
-  EXPECT_TRUE(csp->IsFrameAncestorsEnforced());
-}
-
 TEST_F(ContentSecurityPolicyTest, IsActiveForConnectionsWithConnectSrc) {
   EXPECT_FALSE(csp->IsActiveForConnections());
   csp->DidReceiveHeader("connect-src 'none';",
@@ -222,20 +205,6 @@
   EXPECT_TRUE(csp->IsActiveForConnections());
 }
 
-// Tests that frame-ancestors directives are discarded from policies
-// delivered in <meta> elements.
-TEST_F(ContentSecurityPolicyTest, FrameAncestorsInMeta) {
-  csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
-  csp->DidReceiveHeader("frame-ancestors 'none';",
-                        ContentSecurityPolicyType::kEnforce,
-                        ContentSecurityPolicySource::kMeta);
-  EXPECT_FALSE(csp->IsFrameAncestorsEnforced());
-  csp->DidReceiveHeader("frame-ancestors 'none';",
-                        ContentSecurityPolicyType::kEnforce,
-                        ContentSecurityPolicySource::kHTTP);
-  EXPECT_TRUE(csp->IsFrameAncestorsEnforced());
-}
-
 // Tests that sandbox directives are discarded from policies
 // delivered in <meta> elements.
 TEST_F(ContentSecurityPolicyTest, SandboxInMeta) {
@@ -1364,8 +1333,6 @@
   EXPECT_TRUE(csp->AllowInline(ContentSecurityPolicy::InlineType::kStyle,
                                element, source, nonce, context_url,
                                ordinal_number));
-  EXPECT_TRUE(csp->AllowAncestors(document->GetFrame(), example_url));
-  EXPECT_FALSE(csp->IsFrameAncestorsEnforced());
   EXPECT_TRUE(csp->AllowRequest(mojom::RequestContextType::SCRIPT,
                                 network::mojom::RequestDestination::kScript,
                                 example_url, nonce, IntegrityMetadataSet(),
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index 3581ff0..5aec2ca 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -358,27 +358,6 @@
              redirect_status);
 }
 
-bool CSPDirectiveList::CheckAncestors(SourceListDirective* directive,
-                                      LocalFrame* frame) const {
-  if (!frame || !directive)
-    return true;
-
-  for (Frame* current = frame->Tree().Parent(); current;
-       current = current->Tree().Parent()) {
-    // The |current| frame might be a remote frame which has no URL, so use
-    // its origin instead.  This should suffice for this check since it
-    // doesn't do path comparisons.  See https://crbug.com/582544.
-    //
-    // TODO(mkwst): Move this check up into the browser process.  See
-    // https://crbug.com/555418.
-    KURL url(NullURL(),
-             current->GetSecurityContext()->GetSecurityOrigin()->ToString());
-    if (!directive->Allows(url, ResourceRequest::RedirectStatus::kNoRedirect))
-      return false;
-  }
-  return true;
-}
-
 bool CSPDirectiveList::CheckMediaType(MediaListDirective* directive,
                                       const String& type,
                                       const String& type_attribute) const {
@@ -585,25 +564,6 @@
   return DenyIfEnforcingPolicy();
 }
 
-bool CSPDirectiveList::CheckAncestorsAndReportViolation(
-    SourceListDirective* directive,
-    LocalFrame* frame,
-    const KURL& url) const {
-  if (CheckAncestors(directive, frame))
-    return true;
-
-  ReportViolationWithFrame(
-      directive->GetText(),
-      ContentSecurityPolicy::DirectiveType::kFrameAncestors,
-      "Refused to display '" + url.ElidedString() +
-          "' in a frame because an ancestor violates the "
-          "following Content Security Policy directive: "
-          "\"" +
-          directive->GetText() + "\".",
-      url, frame);
-  return DenyIfEnforcingPolicy();
-}
-
 bool CSPDirectiveList::AllowInline(
     ContentSecurityPolicy::InlineType inline_type,
     Element* element,
@@ -819,21 +779,6 @@
   return DenyIfEnforcingPolicy();
 }
 
-bool CSPDirectiveList::AllowAncestors(
-    LocalFrame* frame,
-    const KURL& url,
-    ReportingDisposition reporting_disposition) const {
-  return reporting_disposition == ReportingDisposition::kReport
-             ? CheckAncestorsAndReportViolation(
-                   OperativeDirective(
-                       ContentSecurityPolicy::DirectiveType::kFrameAncestors),
-                   frame, url)
-             : CheckAncestors(
-                   OperativeDirective(
-                       ContentSecurityPolicy::DirectiveType::kFrameAncestors),
-                   frame);
-}
-
 bool CSPDirectiveList::AllowHash(
     const CSPHashValue& hash_value,
     const ContentSecurityPolicy::InlineType inline_type) const {
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
index a6c45b9..7185ce0 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
@@ -89,13 +89,6 @@
   bool AllowTrustedTypePolicy(const String& policy_name,
                               bool is_duplicate) const;
 
-  // |allowAncestors| does not need to know whether the resource was a
-  // result of a redirect. After a redirect, source paths are usually
-  // ignored to stop a page from learning the path to which the
-  // request was redirected, but this is not a concern for ancestors,
-  // because a child frame can't manipulate the URL of a cross-origin
-  // parent.
-  bool AllowAncestors(LocalFrame*, const KURL&, ReportingDisposition) const;
   bool AllowDynamic(ContentSecurityPolicy::DirectiveType) const;
   bool AllowDynamicWorker() const;
 
@@ -127,9 +120,6 @@
   }
   const Vector<String>& ReportEndpoints() const { return report_endpoints_; }
   bool UseReportingApi() const { return use_reporting_api_; }
-  bool IsFrameAncestorsEnforced() const {
-    return frame_ancestors_.Get() && !IsReportOnly();
-  }
 
   // Used to copy plugin-types into a plugin document in a nested
   // browsing context.
@@ -257,7 +247,6 @@
   bool CheckMediaType(MediaListDirective*,
                       const String& type,
                       const String& type_attribute) const;
-  bool CheckAncestors(SourceListDirective*, LocalFrame*) const;
 
   void SetEvalDisabledErrorMessage(const String& error_message) {
     eval_disabled_error_message_ = error_message;
@@ -291,9 +280,6 @@
                                         const String& type,
                                         const String& type_attribute,
                                         const String& console_message) const;
-  bool CheckAncestorsAndReportViolation(SourceListDirective*,
-                                        LocalFrame*,
-                                        const KURL&) const;
 
   bool DenyIfEnforcingPolicy() const { return IsReportOnly(); }
 
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 69c97eac..12f94d4 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -814,7 +814,7 @@
   if (!context_ && !OffscreenCanvasFrame())
     return;
 
-  if (HasResourceProvider() && !canvas_is_clear_)
+  if (!canvas_is_clear_)
     PaintTiming::From(GetDocument()).MarkFirstContentfulPaint();
 
   // If the canvas is gpu composited, it has another way of getting to screen
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.cc b/third_party/blink/renderer/core/html/custom/element_internals.cc
index e879e1f..70f3934 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.cc
+++ b/third_party/blink/renderer/core/html/custom/element_internals.cc
@@ -238,6 +238,10 @@
   return custom_states_ && custom_states_->contains(state);
 }
 
+ShadowRoot* ElementInternals::shadowRoot() const {
+  return Target().AuthorShadowRoot();
+}
+
 const AtomicString& ElementInternals::FastGetAttribute(
     const QualifiedName& attribute) const {
   return accessibility_semantics_map_.at(attribute);
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.h b/third_party/blink/renderer/core/html/custom/element_internals.h
index 35e9b9354..695e1e8 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.h
+++ b/third_party/blink/renderer/core/html/custom/element_internals.h
@@ -54,6 +54,8 @@
 
   bool HasState(const AtomicString& state) const;
 
+  ShadowRoot* shadowRoot() const;
+
   // We need these functions because we are reflecting ARIA attributes.
   // See dom/aria_attributes.idl.
   const AtomicString& FastGetAttribute(const QualifiedName&) const;
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.idl b/third_party/blink/renderer/core/html/custom/element_internals.idl
index 2364642..1734883f 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.idl
+++ b/third_party/blink/renderer/core/html/custom/element_internals.idl
@@ -27,5 +27,9 @@
   // Custom state
   // https://github.com/w3c/webcomponents/blob/gh-pages/proposals/custom-states-and-state-pseudo-class.md
   [RuntimeEnabled=CustomStatePseudoClass] readonly attribute DOMTokenList states;
+
+  // Access to shadowRoot from custom elements. See crbug.com/1042130 and
+  // https://github.com/w3c/webcomponents/issues/871#issuecomment-672082936
+  [RuntimeEnabled=DeclarativeShadowDOM, MeasureAs=ElementInternalsShadowRoot] readonly attribute ShadowRoot? shadowRoot;
 };
 
diff --git a/third_party/blink/renderer/core/html/media/media_source_attachment.cc b/third_party/blink/renderer/core/html/media/media_source_attachment.cc
index ec3a128..4a571900 100644
--- a/third_party/blink/renderer/core/html/media/media_source_attachment.cc
+++ b/third_party/blink/renderer/core/html/media/media_source_attachment.cc
@@ -24,7 +24,7 @@
   // The only expected caller is an HTMLMediaElement on the main thread.
   DCHECK(IsMainThread());
 
-  if (!registry_)
+  if (!registry_ || url.IsEmpty())
     return nullptr;
 
   // This cast is safe because the only setter of |registry_| is SetRegistry().
diff --git a/third_party/blink/renderer/core/html/media/media_source_registry.h b/third_party/blink/renderer/core/html/media/media_source_registry.h
index d578df7..528131e 100644
--- a/third_party/blink/renderer/core/html/media/media_source_registry.h
+++ b/third_party/blink/renderer/core/html/media/media_source_registry.h
@@ -18,6 +18,10 @@
 // scoped_refptr.
 class CORE_EXPORT MediaSourceRegistry : public URLRegistry {
  public:
+  // Finds the attachment, if any, registered with |url| in the
+  // MediaSourceRegistry implementation. |url| must be non-empty. If such an
+  // active registration for |url| is not found, returns an unset
+  // scoped_refptr<MediaSourceAttachment>.
   virtual scoped_refptr<MediaSourceAttachment> LookupMediaSource(
       const String& url) = 0;
 };
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index dd30039..76a6ca2 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -718,6 +718,13 @@
   return result;
 }
 
+LayoutUnit GetColumnPosition(const LayoutGrid* layout_grid, size_t index) {
+  LayoutUnit position = layout_grid->ColumnPositions().at(index);
+  return layout_grid->StyleRef().IsLeftToRightDirection()
+             ? position
+             : layout_grid->TranslateRTLCoordinate(position);
+}
+
 std::unique_ptr<protocol::DictionaryValue> BuildGridInfo(
     Node* node,
     const InspectorGridHighlightConfig& grid_highlight_config,
@@ -796,21 +803,27 @@
   PathBuilder column_gap_builder;
   LayoutUnit column_top = rows.front();
   LayoutUnit column_height = rows.back() - rows.front();
+  bool is_ltr = layout_grid->StyleRef().IsLeftToRightDirection();
   for (size_t i = 1; i < columns.size(); ++i) {
-    PhysicalOffset position(columns.at(i - 1), column_top);
     PhysicalSize size(columns.at(i) - columns.at(i - 1), column_height);
     if (i != columns.size() - 1)
       size.width -= column_gap;
+    LayoutUnit line_left =
+        is_ltr ? GetColumnPosition(layout_grid, i - 1)
+               : GetColumnPosition(layout_grid, i - 1) - size.width;
+    PhysicalOffset position(line_left, column_top);
     PhysicalRect column(position, size);
     FloatQuad column_quad = layout_grid->LocalRectToAbsoluteQuad(column);
     FrameQuadToViewport(containing_view, column_quad);
+    bool draw_end_line = is_ltr ? i == columns.size() - 1 : i == 1;
     column_builder.AppendPath(
-        ColumnQuadToPath(column_quad,
-                         i == columns.size() - 1 || column_gap > 0),
-        scale);
+        ColumnQuadToPath(column_quad, draw_end_line || column_gap > 0), scale);
     // Column Gaps
     if (i != columns.size() - 1) {
-      PhysicalOffset gap_position(columns.at(i) - column_gap, column_top);
+      LayoutUnit gap_left = is_ltr
+                                ? GetColumnPosition(layout_grid, i) - column_gap
+                                : GetColumnPosition(layout_grid, i);
+      PhysicalOffset gap_position(gap_left, column_top);
       PhysicalSize gap_size(column_gap, column_height);
       PhysicalRect gap(gap_position, gap_size);
       FloatQuad gap_quad = layout_grid->LocalRectToAbsoluteQuad(gap);
diff --git a/third_party/blink/renderer/core/layout/layout_grid.h b/third_party/blink/renderer/core/layout/layout_grid.h
index 6599f923..e925619 100644
--- a/third_party/blink/renderer/core/layout/layout_grid.h
+++ b/third_party/blink/renderer/core/layout/layout_grid.h
@@ -92,6 +92,8 @@
     return grid_->ExplicitGridStart(direction);
   }
 
+  LayoutUnit TranslateRTLCoordinate(LayoutUnit) const;
+
   LayoutUnit TranslateOutOfFlowRTLCoordinate(const LayoutBox&,
                                              LayoutUnit) const;
 
@@ -297,8 +299,6 @@
   size_t NonCollapsedTracks(GridTrackSizingDirection) const;
   size_t NumTracks(GridTrackSizingDirection, const Grid&) const;
 
-  LayoutUnit TranslateRTLCoordinate(LayoutUnit) const;
-
   static LayoutUnit OverrideContainingBlockContentSizeForChild(
       const LayoutBox& child,
       GridTrackSizingDirection);
diff --git a/third_party/blink/renderer/core/paint/html_canvas_painter.cc b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
index 990ef25..2694fe78 100644
--- a/third_party/blink/renderer/core/paint/html_canvas_painter.cc
+++ b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/layout/layout_html_canvas.h"
 #include "third_party/blink/renderer/core/paint/box_painter.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
+#include "third_party/blink/renderer/core/paint/paint_timing.h"
 #include "third_party/blink/renderer/platform/geometry/layout_point.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
@@ -44,6 +45,8 @@
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
       !flatten_composited_layers) {
     if (auto* layer = canvas->ContentsCcLayer()) {
+      PaintTiming::From(layout_html_canvas_.GetDocument())
+          .MarkFirstContentfulPaint();
       IntRect pixel_snapped_rect = PixelSnappedIntRect(paint_rect);
       layer->SetBounds(gfx::Size(pixel_snapped_rect.Size()));
       layer->SetIsDrawable(true);
diff --git a/third_party/blink/renderer/core/paint/image_painter.cc b/third_party/blink/renderer/core/paint/image_painter.cc
index 4f068f6..54bae23 100644
--- a/third_party/blink/renderer/core/paint/image_painter.cc
+++ b/third_party/blink/renderer/core/paint/image_painter.cc
@@ -50,11 +50,16 @@
   if (layout_size.IsEmpty() || image_size.IsEmpty())
     return false;
 
-  double dpr = layout_image.GetDocument().GetFrame()->DevicePixelRatio();
-  double downscale_ratio_width =
-      image_size.Width() / (dpr * layout_size.Width());
-  double downscale_ratio_height =
-      image_size.Height() / (dpr * layout_size.Height());
+  // Note: Do not use frame->GetDevicePixelRatio() here, because it
+  // leads to different behaviour on MacOS platform. https://crbug.com/716231.
+  // virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases.html
+  // verifies the behaviour.
+  const double dsf =
+      layout_image.GetDocument().GetPage()->DeviceScaleFactorDeprecated();
+  const double downscale_ratio_width =
+      image_size.Width() / layout_size.Width() / dsf;
+  const double downscale_ratio_height =
+      image_size.Height() / layout_size.Height() / dsf;
 
   const LayoutImageResource* image_resource = layout_image.ImageResource();
   const ImageResourceContent* cached_image =
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 2a36de59..6106ec1b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -3968,6 +3968,7 @@
     case ax::mojom::blink::Role::kLineBreak:
     case ax::mojom::blink::Role::kLink:
     case ax::mojom::blink::Role::kListBoxOption:
+    case ax::mojom::blink::Role::kMenuButton:
     case ax::mojom::blink::Role::kMenuItem:
     case ax::mojom::blink::Role::kMenuItemCheckBox:
     case ax::mojom::blink::Role::kMenuItemRadio:
@@ -4072,8 +4073,6 @@
     case ax::mojom::blink::Role::kMenuListPopup:
     case ax::mojom::blink::Role::kMenu:
     case ax::mojom::blink::Role::kMenuBar:
-    case ax::mojom::blink::Role::kMenuButton:  // Only value from content, not
-                                               // name.
     case ax::mojom::blink::Role::kMeter:
     case ax::mojom::blink::Role::kNavigation:
     case ax::mojom::blink::Role::kNote:
diff --git a/third_party/blink/renderer/modules/mediasource/media_source_registry_impl.cc b/third_party/blink/renderer/modules/mediasource/media_source_registry_impl.cc
index 65181d602..2cda907 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source_registry_impl.cc
+++ b/third_party/blink/renderer/modules/mediasource/media_source_registry_impl.cc
@@ -47,6 +47,7 @@
                                           URLRegistrable* registrable) {
   DCHECK(IsMainThread());
   DCHECK_EQ(&registrable->Registry(), this);
+  DCHECK(!url.IsEmpty());  // Caller of interface should already enforce this.
 
   DVLOG(1) << __func__ << " url=" << url;
 
@@ -58,6 +59,7 @@
 void MediaSourceRegistryImpl::UnregisterURL(const KURL& url) {
   DVLOG(1) << __func__ << " url=" << url;
   DCHECK(IsMainThread());
+  DCHECK(!url.IsEmpty());  // Caller of interface should already enforce this.
 
   auto iter = media_sources_.find(url.GetString());
   if (iter == media_sources_.end())
@@ -71,8 +73,8 @@
 scoped_refptr<MediaSourceAttachment> MediaSourceRegistryImpl::LookupMediaSource(
     const String& url) {
   DCHECK(IsMainThread());
-  return url.IsNull() ? scoped_refptr<MediaSourceAttachment>()
-                      : media_sources_.at(url);
+  DCHECK(!url.IsEmpty());
+  return media_sources_.at(url);
 }
 
 MediaSourceRegistryImpl::MediaSourceRegistryImpl() {
diff --git a/third_party/blink/renderer/modules/mediasource/media_source_registry_impl.h b/third_party/blink/renderer/modules/mediasource/media_source_registry_impl.h
index c38ab9b..49e19f6 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source_registry_impl.h
+++ b/third_party/blink/renderer/modules/mediasource/media_source_registry_impl.h
@@ -39,7 +39,7 @@
 
   // MediaSourceRegistry override that finds |url| in |media_sources_| and
   // returns the corresponding scoped_refptr if found. Otherwise, returns an
-  // unset scoped_refptr.
+  // unset scoped_refptr. |url| must be non-empty.
   scoped_refptr<MediaSourceAttachment> LookupMediaSource(
       const String& url) override;
 
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream.cc b/third_party/blink/renderer/modules/mediastream/media_stream.cc
index 293efe7e..f73c1d14 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream.cc
@@ -148,7 +148,10 @@
   }
 
   if (number_of_video_tracks == 0) {
-    std::move(media_stream_initialized_callback_).Run(this);
+    context->GetTaskRunner(TaskType::kInternalMedia)
+        ->PostTask(FROM_HERE,
+                   WTF::Bind(std::move(media_stream_initialized_callback_),
+                             WrapPersistent(this)));
   }
 }
 
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
index c9add9fd..6604b5e 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
@@ -256,7 +256,8 @@
     image_capture_ = MakeGarbageCollected<ImageCapture>(
         context, this, pan_tilt_zoom_allowed, std::move(callback));
   } else {
-    std::move(callback).Run();
+    execution_context_->GetTaskRunner(TaskType::kInternalMedia)
+        ->PostTask(FROM_HERE, std::move(callback));
   }
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.idl b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.idl
index 0972dfa..4756e6bb 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.idl
@@ -9,7 +9,7 @@
     readonly attribute RTCDtlsTransport? transport;
     readonly attribute RTCDtlsTransport? rtcpTransport;
     static RTCRtpCapabilities? getCapabilities(DOMString kind);
-    [CallWith=ScriptState] Promise<void> setParameters(optional RTCRtpSendParameters parameters = {});
+    [CallWith=ScriptState] Promise<void> setParameters(RTCRtpSendParameters parameters);
     RTCRtpSendParameters getParameters();
     [Measure, CallWith=ScriptState] Promise<void> replaceTrack(MediaStreamTrack? withTrack);
     [Measure] readonly attribute RTCDTMFSender? dtmf;
diff --git a/third_party/blink/renderer/modules/plugins/navigator_plugins.cc b/third_party/blink/renderer/modules/plugins/navigator_plugins.cc
index 6ea9da60..3a21ecc 100644
--- a/third_party/blink/renderer/modules/plugins/navigator_plugins.cc
+++ b/third_party/blink/renderer/modules/plugins/navigator_plugins.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/plugins/navigator_plugins.h"
 
 #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/navigator.h"
@@ -56,7 +57,7 @@
     plugins_ = MakeGarbageCollected<DOMPluginArray>(frame);
 
   DOMPluginArray* result = plugins_.Get();
-  if (!frame)
+  if (!IdentifiabilityStudySettings::Get()->IsActive() || !frame)
     return result;
   Document* document = frame->GetDocument();
   if (!document)
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
index ba12555..9da92f6 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/fetch/body_stream_buffer.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/modules/service_worker/cross_origin_resource_policy_checker.h"
+#include "third_party/blink/renderer/modules/service_worker/fetch_event.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/modules/service_worker/wait_until_observer.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -213,8 +214,7 @@
   service_worker_global_scope->RespondToFetchEvent(
       event_id_, request_url_, std::move(response), event_dispatch_time_,
       base::TimeTicks::Now());
-  service_worker_global_scope->RejectFetchEventHandledPromise(event_id_,
-                                                              error_message);
+  event_->RejectHandledPromise(error_message);
 }
 
 void FetchRespondWithObserver::OnResponseFulfilled(
@@ -337,7 +337,7 @@
       service_worker_global_scope->RespondToFetchEvent(
           event_id_, request_url_, std::move(fetch_api_response),
           event_dispatch_time_, base::TimeTicks::Now());
-      service_worker_global_scope->ResolveFetchEventHandledPromise(event_id_);
+      event_->ResolveHandledPromise();
       return;
     }
 
@@ -364,13 +364,13 @@
     service_worker_global_scope->RespondToFetchEventWithResponseStream(
         event_id_, request_url_, std::move(fetch_api_response),
         std::move(stream_handle), event_dispatch_time_, base::TimeTicks::Now());
-    service_worker_global_scope->ResolveFetchEventHandledPromise(event_id_);
+    event_->ResolveHandledPromise();
     return;
   }
   service_worker_global_scope->RespondToFetchEvent(
       event_id_, request_url_, std::move(fetch_api_response),
       event_dispatch_time_, base::TimeTicks::Now());
-  service_worker_global_scope->ResolveFetchEventHandledPromise(event_id_);
+  event_->ResolveHandledPromise();
 }
 
 void FetchRespondWithObserver::OnNoResponse() {
@@ -379,7 +379,7 @@
       To<ServiceWorkerGlobalScope>(GetExecutionContext());
   service_worker_global_scope->RespondToFetchEventWithNoResponse(
       event_id_, request_url_, event_dispatch_time_, base::TimeTicks::Now());
-  service_worker_global_scope->ResolveFetchEventHandledPromise(event_id_);
+  event_->ResolveHandledPromise();
 }
 
 FetchRespondWithObserver::FetchRespondWithObserver(
@@ -398,6 +398,7 @@
       task_runner_(context->GetTaskRunner(TaskType::kNetworking)) {}
 
 void FetchRespondWithObserver::Trace(Visitor* visitor) const {
+  visitor->Trace(event_);
   RespondWithObserver::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h
index 808cfe0..2de7884 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h
+++ b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h
@@ -17,6 +17,7 @@
 
 class CrossOriginResourcePolicyChecker;
 class ExecutionContext;
+class FetchEvent;
 class ScriptValue;
 class WaitUntilObserver;
 
@@ -46,6 +47,11 @@
                            const char* property_name) override;
   void OnNoResponse() override;
 
+  void SetEvent(FetchEvent* event) {
+    DCHECK(!event_);
+    event_ = event;
+  }
+
   void Trace(Visitor*) const override;
 
  private:
@@ -54,6 +60,7 @@
   const network::mojom::RedirectMode redirect_mode_;
   const mojom::RequestContextFrameType frame_type_;
   const network::mojom::RequestDestination request_destination_;
+  Member<FetchEvent> event_;
   base::WeakPtr<CrossOriginResourcePolicyChecker> corp_checker_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 };
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index 84f2915..dd1c4d9 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -808,7 +808,6 @@
   visitor->Trace(payment_response_callbacks_);
   visitor->Trace(fetch_response_callbacks_);
   visitor->Trace(pending_preload_fetch_events_);
-  visitor->Trace(pending_fetch_events_);
   visitor->Trace(controller_receivers_);
   WorkerGlobalScope::Trace(visitor);
 }
@@ -823,20 +822,6 @@
   return features::kInstallingServiceWorkerOutstandingThrottledLimit.Get();
 }
 
-void ServiceWorkerGlobalScope::ResolveFetchEventHandledPromise(int event_id) {
-  FetchEvent* fetch_event = pending_fetch_events_.Take(event_id);
-  DCHECK(fetch_event);
-  fetch_event->ResolveHandledPromise();
-}
-
-void ServiceWorkerGlobalScope::RejectFetchEventHandledPromise(
-    int event_id,
-    const String& error_message) {
-  FetchEvent* fetch_event = pending_fetch_events_.Take(event_id);
-  DCHECK(fetch_event);
-  fetch_event->RejectHandledPromise(error_message);
-}
-
 void ServiceWorkerGlobalScope::importScripts(const Vector<String>& urls,
                                              ExceptionState& exception_state) {
   for (const String& string_url : urls) {
@@ -1537,14 +1522,14 @@
       mojo::PendingRemote<mojom::blink::WorkerTimingContainer>(
           std::move(params->worker_timing_remote)),
       navigation_preload_sent);
+  respond_with_observer->SetEvent(fetch_event);
+
   if (navigation_preload_sent) {
     // Keep |fetchEvent| until OnNavigationPreloadComplete() or
     // onNavigationPreloadError() will be called.
     pending_preload_fetch_events_.insert(event_id, fetch_event);
   }
 
-  pending_fetch_events_.insert(event_id, fetch_event);
-
   NoteNewFetchEvent(request->url());
 
   DispatchExtendableEventWithRespondWith(fetch_event, wait_until_observer,
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
index 57beea8..e4ae567 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
@@ -316,9 +316,6 @@
   int GetOutstandingThrottledLimit() const override;
 
   const ServiceWorkerToken& token() const { return token_; }
-  void ResolveFetchEventHandledPromise(int event_id);
-  void RejectFetchEventHandledPromise(int event_id,
-                                      const String& error_message);
 
  protected:
   // EventTarget
@@ -691,10 +688,6 @@
 
   HeapHashMap<int, Member<FetchEvent>> pending_preload_fetch_events_;
 
-  // Fetch events that are being handled are stored here and will be removed
-  // after being handled.
-  HeapHashMap<int, Member<FetchEvent>> pending_fetch_events_;
-
   // Track outstanding FetchEvent objects still waiting for a response by
   // request URL.  This information can be used as a hint that cache_storage
   // or fetch requests to the same URL is likely to be used to satisfy a
diff --git a/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc b/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc
index 23a1e4f67..354fdbd 100644
--- a/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc
+++ b/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc
@@ -7,6 +7,7 @@
 #include "services/metrics/public/cpp/metrics_utils.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_token.h"
 #include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
 
@@ -41,7 +42,9 @@
       identifiability_metrics_timer_(
           task_runner,
           this,
-          &FontMatchingMetrics::IdentifiabilityMetricsTimerFired) {
+          &FontMatchingMetrics::IdentifiabilityMetricsTimerFired),
+      identifiability_study_enabled_(
+          IdentifiabilityStudySettings::Get()->IsActive()) {
   // Estimate of average page font use from anecdotal browsing session.
   constexpr unsigned kEstimatedFontCount = 7;
   local_fonts_succeeded_.ReserveCapacityForSize(kEstimatedFontCount);
@@ -84,6 +87,9 @@
     LocalFontLookupType check_type,
     SimpleFontData* resulting_font_data,
     bool is_loading_fallback) {
+  if (!identifiability_study_enabled_) {
+    return;
+  }
   OnFontLookup();
   uint64_t hash = GetHashForFontData(resulting_font_data);
   LocalFontLookupKey key(name, font_description.GetFontSelectionRequest());
@@ -96,6 +102,9 @@
     const FontDescription& font_description,
     LocalFontLookupType check_type,
     SimpleFontData* resulting_font_data) {
+  if (!identifiability_study_enabled_) {
+    return;
+  }
   OnFontLookup();
   uint64_t hash = GetHashForFontData(resulting_font_data);
   LocalFontLookupKey key(fallback_character,
@@ -109,6 +118,9 @@
     const FontDescription& font_description,
     LocalFontLookupType check_type,
     SimpleFontData* resulting_font_data) {
+  if (!identifiability_study_enabled_) {
+    return;
+  }
   OnFontLookup();
   uint64_t hash = GetHashForFontData(resulting_font_data);
   LocalFontLookupKey key(font_description.GetFontSelectionRequest());
@@ -122,6 +134,9 @@
     UScriptCode script,
     FontDescription::GenericFamilyType generic_family_type,
     const AtomicString& resulting_font_name) {
+  if (!identifiability_study_enabled_) {
+    return;
+  }
   OnFontLookup();
   GenericFontLookupKey key(generic_font_family_name, script,
                            generic_family_type);
@@ -130,6 +145,8 @@
 }
 
 void FontMatchingMetrics::PublishIdentifiabilityMetrics() {
+  DCHECK(identifiability_study_enabled_);
+
   IdentifiabilityMetricBuilder builder(source_id_);
 
   for (const auto& entry : font_lookups_) {
@@ -188,6 +205,7 @@
 }
 
 void FontMatchingMetrics::OnFontLookup() {
+  DCHECK(identifiability_study_enabled_);
   if (!identifiability_metrics_timer_.IsActive()) {
     identifiability_metrics_timer_.StartOneShot(base::TimeDelta::FromMinutes(1),
                                                 FROM_HERE);
@@ -199,7 +217,9 @@
 }
 
 void FontMatchingMetrics::PublishAllMetrics() {
-  PublishIdentifiabilityMetrics();
+  if (identifiability_study_enabled_) {
+    PublishIdentifiabilityMetrics();
+  }
   PublishUkmMetrics();
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/font_matching_metrics.h b/third_party/blink/renderer/platform/fonts/font_matching_metrics.h
index f7333631..4e9f7c2 100644
--- a/third_party/blink/renderer/platform/fonts/font_matching_metrics.h
+++ b/third_party/blink/renderer/platform/fonts/font_matching_metrics.h
@@ -277,6 +277,8 @@
   const ukm::SourceId source_id_;
 
   TaskRunnerTimer<FontMatchingMetrics> identifiability_metrics_timer_;
+
+  const bool identifiability_study_enabled_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.cc b/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.cc
index 51fa85b..c923687 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.cc
@@ -226,12 +226,21 @@
       SetWaitingForInput(true);
 
     main_frames_.insert(&frame_scheduler);
-    main_frames_waiting_for_signal_.insert(&frame_scheduler);
+
+    // Only add ordinary page frames to the set of waiting frames, as
+    // non-ordinary ones don't report any signals.
+    if (frame_scheduler.IsOrdinary())
+      main_frames_waiting_for_signal_.insert(&frame_scheduler);
 
     return ShouldUpdatePolicy::kYes;
   }
 
   bool ShouldAffectQueue(const MainThreadTaskQueue& task_queue) const {
+    // Queues that don't have a frame scheduler are, by definition, not
+    // associated with a frame (or agent).
+    if (!task_queue.GetFrameScheduler())
+      return false;
+
     if (affected_queue_types_ == PerAgentAffectedQueues::kTimerQueues &&
         task_queue.GetPrioritisationType() !=
             PrioritisationType::kJavaScriptTimer) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy_unittest.cc
index d5e6c54..ed6d6d3 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy_unittest.cc
@@ -30,6 +30,7 @@
 using ::base::test::ScopedFeatureList;
 using ::testing::_;
 using ::testing::NiceMock;
+using ::testing::Return;
 using ::testing::ReturnRef;
 using ::testing::Test;
 
@@ -43,9 +44,9 @@
                base::TimeDelta delay));
 };
 
-class MockFrameDelegate : public FrameScheduler::Delegate {
+class MockFrameSchedulerDelegate : public FrameScheduler::Delegate {
  public:
-  MockFrameDelegate() {
+  MockFrameSchedulerDelegate() {
     ON_CALL(*this, GetAgentClusterId)
         .WillByDefault(ReturnRef(agent_cluster_id_));
   }
@@ -70,10 +71,14 @@
                            /*parent_page_scheduler=*/nullptr,
                            /*delegate=*/&delegate_,
                            /*blame_context=*/nullptr,
-                           /*frame_type=*/frame_type) {}
+                           /*frame_type=*/frame_type) {
+    ON_CALL(*this, IsOrdinary).WillByDefault(Return(true));
+  }
+
+  MOCK_METHOD(bool, IsOrdinary, (), (const));
 
  private:
-  NiceMock<MockFrameDelegate> delegate_;
+  NiceMock<MockFrameSchedulerDelegate> delegate_;
 };
 
 }  // namespace
@@ -494,5 +499,29 @@
   EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
 }
 
+class PerAgentNonOrdinaryPageTest : public PerAgentSchedulingBaseTest {
+ public:
+  PerAgentNonOrdinaryPageTest()
+      : PerAgentSchedulingBaseTest({{"queues", "timer-queues"},
+                                    {"method", "disable"},
+                                    {"signal", "onload"}}) {
+    ON_CALL(non_ordinary_frame_scheduler_, IsOrdinary)
+        .WillByDefault(Return(false));
+  }
+
+ protected:
+  NiceMock<MockFrameScheduler> non_ordinary_frame_scheduler_{
+      FrameScheduler::FrameType::kMainFrame};
+};
+
+TEST_F(PerAgentNonOrdinaryPageTest, DoesntWaitForNonOrdinaryFrames) {
+  EXPECT_EQ(strategy_->OnFrameAdded(non_ordinary_frame_scheduler_),
+            ShouldUpdatePolicy::kYes);
+  EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+  EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+  EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+  EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index e22f8234e..9bd2928 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -941,6 +941,12 @@
   return waiting_for_meaningful_paint_;
 }
 
+bool FrameSchedulerImpl::IsOrdinary() const {
+  if (!parent_page_scheduler_)
+    return true;
+  return parent_page_scheduler_->IsOrdinary();
+}
+
 bool FrameSchedulerImpl::ShouldThrottleTaskQueues() const {
   // TODO(crbug.com/1078387): Convert the CHECK to a DCHECK once enough time has
   // passed to confirm that it is correct. (November 2020).
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
index 6bae895a..18808184 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -125,6 +125,11 @@
   bool IsWaitingForContentfulPaint() const;
   bool IsWaitingForMeaningfulPaint() const;
 
+  // An "ordinary" FrameScheduler is responsible for a frame whose parent page
+  // is a fully-featured page owned by a web view (as opposed to, e.g.: a Page
+  // created by an SVGImage). Virtual for testing.
+  virtual bool IsOrdinary() const;
+
   void AsValueInto(base::trace_event::TracedValue* state) const;
   bool IsExemptFromBudgetBasedThrottling() const override;
   std::unique_ptr<blink::mojom::blink::PauseSubresourceLoadingHandle>
diff --git a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
index a1c47bb5..475b86c 100644
--- a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
+++ b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
@@ -29,13 +29,11 @@
 
 import copy
 import logging
-import itertools
 import re
 
 from collections import defaultdict
 from collections import OrderedDict
 
-from blinkpy.common import path_finder
 from blinkpy.common.memoized import memoized
 from blinkpy.web_tests.models import typ_types
 
@@ -46,8 +44,7 @@
 SPECIAL_PREFIXES = ('# tags:', '# results:', '# conflicts_allowed:')
 
 _PLATFORM_TOKENS_LIST = [
-    'Android', 'Fuchsia', 'IOS', 'IOS12.2', 'IOS13.0', 'Linux', 'Mac',
-    'Mac10.10', 'Mac10.11', 'Retina', 'Mac10.12', 'Mac10.13', 'Mac10.14',
+    'Android', 'Fuchsia', 'Linux', 'Mac', 'Mac10.12', 'Mac10.13', 'Mac10.14',
     'Win', 'Win7', 'Win10'
 ]
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index e4ad2a8..c1411025 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -139,17 +139,13 @@
         ('win10', 'x86'),
         ('trusty', 'x86_64'),
         ('fuchsia', 'x86_64'),
-        ('ios12.2', 'x86_64'),
-        ('ios13.0', 'x86_64'),
     )
 
     CONFIGURATION_SPECIFIER_MACROS = {
-        'mac':
-        ['mac10.12', 'mac10.13', 'mac10.14', 'mac10.15'],
+        'mac': ['mac10.12', 'mac10.13', 'mac10.14', 'mac10.15'],
         'win': ['win7', 'win10'],
         'linux': ['trusty'],
         'fuschia': ['fuchsia'],
-        'ios': ['ios12.2', 'ios13.0'],
     }
 
     # List of ports open on the host that the tests will connect to. When tests
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/factory.py b/third_party/blink/tools/blinkpy/web_tests/port/factory.py
index 50691180..2345de24 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/factory.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/factory.py
@@ -39,7 +39,6 @@
     PORT_CLASSES = (
         'android.AndroidPort',
         'fuchsia.FuchsiaPort',
-        'ios.IOSPort',
         'linux.LinuxPort',
         'mac.MacPort',
         'mock_drt.MockDRTPort',
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/ios.py b/third_party/blink/tools/blinkpy/web_tests/port/ios.py
deleted file mode 100644
index e496d70..0000000
--- a/third_party/blink/tools/blinkpy/web_tests/port/ios.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 2019 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Chromium iOS implementation of the Port interface."""
-
-import logging
-
-from blinkpy.web_tests.port import base
-
-_log = logging.getLogger(__name__)
-
-
-class IOSPort(base.Port):
-    SUPPORTED_VERSIONS = ('ios12.2', 'ios13.0')
-    port_name = 'ios'
-
-    FALLBACK_PATHS = {}
-
-    FALLBACK_PATHS['ios13.0'] = ['ios']
-    FALLBACK_PATHS['ios12.2'] = ['ios12.2', 'ios']
-
-    def __init__(self, host, port_name, **kwargs):
-        super(IOSPort, self).__init__(host, port_name, **kwargs)
-        self._architecture = 'x86_64'
-        self._version = port_name[port_name.index('ios-') + len('ios-'):]
-        assert self._version in self.SUPPORTED_VERSIONS
-
-    def operating_system(self):
-        return 'ios'
diff --git a/third_party/blink/web_tests/ASANExpectations b/third_party/blink/web_tests/ASANExpectations
index fb252f6..d0c2d43 100644
--- a/third_party/blink/web_tests/ASANExpectations
+++ b/third_party/blink/web_tests/ASANExpectations
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/composite-after-paint b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
index 804625a..68db8417 100644
--- a/third_party/blink/web_tests/FlagExpectations/composite-after-paint
+++ b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-features=OutOfBlinkCors b/third_party/blink/web_tests/FlagExpectations/disable-features=OutOfBlinkCors
index 4bc9f017..b39d97dd 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-features=OutOfBlinkCors
+++ b/third_party/blink/web_tests/FlagExpectations/disable-features=OutOfBlinkCors
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index f1bc5ce..541b07e 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
index 0ff1e97..fb0f5082 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
+++ b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-features=BackForwardCache b/third_party/blink/web_tests/FlagExpectations/enable-features=BackForwardCache
index 6f7748a..d8ff56256 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-features=BackForwardCache
+++ b/third_party/blink/web_tests/FlagExpectations/enable-features=BackForwardCache
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer b/third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer
index cdbe7c03..2bf7dd1 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer
+++ b/third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization b/third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization
index 4653d6e..28e5246 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization
+++ b/third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-threaded-compositing b/third_party/blink/web_tests/FlagExpectations/enable-threaded-compositing
index 109097a..24c6388 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-threaded-compositing
+++ b/third_party/blink/web_tests/FlagExpectations/enable-threaded-compositing
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item b/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
index e4641c23..153fc6d 100644
--- a/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
+++ b/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 # tags: [ Release Debug ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/use-gl=any b/third_party/blink/web_tests/FlagExpectations/use-gl=any
index 360deb1..7a1b8612 100644
--- a/third_party/blink/web_tests/FlagExpectations/use-gl=any
+++ b/third_party/blink/web_tests/FlagExpectations/use-gl=any
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/use-vulkan=native b/third_party/blink/web_tests/FlagExpectations/use-vulkan=native
index 1f466b8..433b19e 100644
--- a/third_party/blink/web_tests/FlagExpectations/use-vulkan=native
+++ b/third_party/blink/web_tests/FlagExpectations/use-vulkan=native
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader b/third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader
index 2c9d2de3..40d99841 100644
--- a/third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader
+++ b/third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 3decd13..5f0929d 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index f8a7256..58b3a2f 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index b171cd2..12fc1e1 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Mac10.14 Mac10.15 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Mac10.14 Mac10.15 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Skip ]
 
@@ -3324,8 +3324,6 @@
 crbug.com/626703 [ Win10 ] external/wpt/fetch/api/redirect/redirect-count.any.html [ Timeout ]
 
 crbug.com/626703 external/wpt/css/cssom-view/scroll-behavior-smooth.html [ Timeout Crash ]
-crbug.com/626703 [ IOS ] external/wpt/websockets/Create-Secure-extensions-empty.any.html [ Timeout ]
-crbug.com/626703 [ IOS ] external/wpt/websockets/Create-Secure-extensions-empty.any.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/html/browsers/history/joint-session-history/joint-session-history-remove-iframe.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index f1ed4ad..4f55295 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -203,7 +203,8 @@
               "external/wpt/css/filter-effects",
               "external/wpt/largest-contentful-paint",
               "fast/hidpi/static",
-              "http/tests/csspaint/hidpi"],
+              "http/tests/csspaint/hidpi",
+              "http/tests/images/document-policy"],
     "args": ["--force-device-scale-factor=2"]
   },
   {
diff --git a/third_party/blink/web_tests/W3CImportExpectations b/third_party/blink/web_tests/W3CImportExpectations
index 3d5f8a0..d653d5999 100644
--- a/third_party/blink/web_tests/W3CImportExpectations
+++ b/third_party/blink/web_tests/W3CImportExpectations
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Skip ]
 
diff --git a/third_party/blink/web_tests/WPTOverrideExpectations b/third_party/blink/web_tests/WPTOverrideExpectations
index 4174419..4d74e90 100644
--- a/third_party/blink/web_tests/WPTOverrideExpectations
+++ b/third_party/blink/web_tests/WPTOverrideExpectations
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/WebDriverExpectations b/third_party/blink/web_tests/WebDriverExpectations
index b6d5928..a589dbf 100644
--- a/third_party/blink/web_tests/WebDriverExpectations
+++ b/third_party/blink/web_tests/WebDriverExpectations
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index 2d41eb87..a2f21c95 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -1,4 +1,4 @@
-# tags: [ Android Fuchsia IOS IOS12.2 IOS13.0 Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
+# tags: [ Android Fuchsia Linux Mac Mac10.12 Mac10.13 Win Win7 Win10 ]
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip RetryOnFailure ]
 
diff --git a/third_party/blink/web_tests/accessibility/computed-name-expected.txt b/third_party/blink/web_tests/accessibility/computed-name-expected.txt
index c46aa329..f635db5 100644
--- a/third_party/blink/web_tests/accessibility/computed-name-expected.txt
+++ b/third_party/blink/web_tests/accessibility/computed-name-expected.txt
@@ -60,6 +60,8 @@
 PASS name is "menubar name"
 PASS name is "menuitem name"
 PASS name is "menuitem name"
+PASS name is "group name"
+PASS name is "menuitem name"
 PASS name is "menuitemcheckbox name"
 PASS name is "menuitemcheckbox name"
 PASS name is "menuitemradio name"
diff --git a/third_party/blink/web_tests/accessibility/computed-name.html b/third_party/blink/web_tests/accessibility/computed-name.html
index af775adf..7e1fe99 100644
--- a/third_party/blink/web_tests/accessibility/computed-name.html
+++ b/third_party/blink/web_tests/accessibility/computed-name.html
@@ -118,6 +118,9 @@
 
       <div role="menuitem">menuitem name</div>
       <div role="menuitem" aria-label="menuitem name">This is a menuitem</div>
+      <div role="group" aria-label="group name">
+        <div role="menuitem">menuitem name</div>
+      </div>
       <menu type="popup">
         <menuitem command="doSomething" data-role="menuitem" aria-label="implicit menuitem name" data-knownFailure>This is a menuitem</menuitem>
       </menu>
diff --git a/third_party/blink/web_tests/android/ChromiumWPTExpectations b/third_party/blink/web_tests/android/ChromiumWPTExpectations
index 59ab398..5e7e13a 100644
--- a/third_party/blink/web_tests/android/ChromiumWPTExpectations
+++ b/third_party/blink/web_tests/android/ChromiumWPTExpectations
@@ -3709,7 +3709,6 @@
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-cors-xhr.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-error.https.html [ Failure ]
-crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-handled.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-redirect.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-referrer-policy.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-respond-with-custom-response.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/android/WeblayerWPTExpectations b/third_party/blink/web_tests/android/WeblayerWPTExpectations
index 0fd8a017..fc11743 100644
--- a/third_party/blink/web_tests/android/WeblayerWPTExpectations
+++ b/third_party/blink/web_tests/android/WeblayerWPTExpectations
@@ -3747,7 +3747,6 @@
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-with-range-request.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-error.https.html [ Failure ]
-crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-handled.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-redirect.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-referrer-policy.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-respond-with-custom-response.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index 51fb9a8..4f3105f 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -3984,7 +3984,6 @@
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-with-range-request.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-error.https.html [ Failure ]
-crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-handled.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-redirect.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-referrer-policy.https.html [ Failure ]
 crbug.com/1050754 external/wpt/service-workers/service-worker/fetch-event-respond-with-custom-response.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/paint-timing/first-contentful-canvas-none.html b/third_party/blink/web_tests/external/wpt/paint-timing/first-contentful-canvas-none.html
new file mode 100644
index 0000000..33a4352
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/paint-timing/first-contentful-canvas-none.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+
+<head>
+  <title>Performance Paint Timing Test: FCP due to canvas</title>
+</head>
+
+<body>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="resources/utils.js"></script>
+  <canvas id="canvas" width="200" height="200"></canvas>
+
+  <script>
+    setup({"hide_test_state": true});
+    promise_test(async t => {
+        assert_implements(window.PerformancePaintTiming, "Paint Timing isn't supported.");
+        await new Promise(r => window.addEventListener('load', r));
+        await assertNoFirstContentfulPaint(t);
+    }, 'First contentful paint should not fire for canvas type none');
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/paint-timing/first-contentful-canvas-webgl2.html b/third_party/blink/web_tests/external/wpt/paint-timing/first-contentful-canvas-webgl2.html
new file mode 100644
index 0000000..f7c5f50
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/paint-timing/first-contentful-canvas-webgl2.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+
+<head>
+  <title>Performance Paint Timing Test: FCP due to canvas</title>
+</head>
+
+<body>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <canvas id="canvas" width="200" height="200"></canvas>
+
+  <script>
+    setup({ "hide_test_state": true });
+    async_test(function (t) {
+      assert_implements(window.PerformancePaintTiming, "Paint Timing isn't supported.");
+      const canvas = document.getElementById("canvas");
+      const context = canvas.getContext("webgl2");
+      if (!context) {
+        assert_implements_optional(context, "WebGL 2 Canvas isn't supported.")
+      }
+      context.clearColor(0.3, 0.3, 0.3, 1);
+      context.clear(context.COLOR_BUFFER_BIT);
+      function testPaintEntries() {
+        const bufferedEntries = performance.getEntriesByType('paint');
+        if (bufferedEntries.length < 2) {
+          t.step_timeout(function () {
+            testPaintEntries();
+          }, 20);
+          return;
+        }
+        t.step(function () {
+          assert_equals(bufferedEntries.length, 2, "There should be two paint timing instances.");
+          assert_equals(bufferedEntries[0].entryType, "paint");
+          assert_equals(bufferedEntries[0].name, "first-paint");
+          assert_equals(bufferedEntries[1].entryType, "paint");
+          assert_equals(bufferedEntries[1].name, "first-contentful-paint");
+          t.done();
+        });
+      };
+      t.step(function () {
+        testPaintEntries();
+      });
+    }, "First contentful paint fires due to webgl2 canvas render.");
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/element-internals-shadowroot.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/element-internals-shadowroot.tentative.html
new file mode 100644
index 0000000..0f01cc4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/element-internals-shadowroot.tentative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>ElementInternals.shadowRoot</title>
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://github.com/w3c/webcomponents/issues/871">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+
+test(() => {
+  let constructed = false;
+  customElements.define('custom-open', class extends HTMLElement {
+    constructor() {
+      super();
+      const elementInternals = this.attachInternals();
+      assert_equals(elementInternals.shadowRoot, null);
+      const shadow = this.attachShadow({mode: 'open'});
+      assert_equals(elementInternals.shadowRoot, shadow);
+      constructed = true;
+    }
+  });
+  const element = document.createElement('custom-open');
+  assert_true(constructed);
+}, 'ElementInternals.shadowRoot allows access to open shadow root');
+
+test(() => {
+  let constructed = false;
+  customElements.define('custom-closed', class extends HTMLElement {
+    constructor() {
+      super();
+      const elementInternals = this.attachInternals();
+      assert_equals(elementInternals.shadowRoot, null);
+      const shadow = this.attachShadow({mode: 'closed'});
+      assert_equals(elementInternals.shadowRoot, shadow);
+      assert_equals(this.shadowRoot, null);
+      constructed = true;
+    }
+  });
+  const element = document.createElement('custom-closed');
+  assert_true(constructed);
+}, 'ElementInternals.shadowRoot allows access to closed shadow root');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
index 8b39ad8..222565c 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 496 tests; 479 PASS, 17 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 496 tests; 480 PASS, 16 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Test driver for asyncInitCertificate
@@ -235,7 +235,7 @@
 PASS RTCRtpSender interface: attribute track
 PASS RTCRtpSender interface: attribute transport
 PASS RTCRtpSender interface: operation getCapabilities(DOMString)
-FAIL RTCRtpSender interface: operation setParameters(RTCRtpSendParameters) assert_equals: property has wrong .length expected 1 but got 0
+PASS RTCRtpSender interface: operation setParameters(RTCRtpSendParameters)
 PASS RTCRtpSender interface: operation getParameters()
 PASS RTCRtpSender interface: operation replaceTrack(MediaStreamTrack?)
 PASS RTCRtpSender interface: operation setStreams(MediaStream...)
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-direction-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-direction-expected.txt
new file mode 100644
index 0000000..0cfe4961
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-direction-expected.txt
@@ -0,0 +1,1051 @@
+This test verifies that grids with direction rtl and ltr are correctly highlighted.
+
+ltrGrid{
+  "paths": [
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "ltrGrid",
+    "className": ".grid.ltr-dir",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutGrid",
+    "showAccessibilityInfo": true
+  },
+  "gridInfo": [
+    {
+      "rotationAngle": 0,
+      "columnTrackSizes": [
+        {
+          "x": 18,
+          "y": 44,
+          "computedSize": 20,
+          "authoredSize": "20px"
+        },
+        {
+          "x": 53,
+          "y": 44,
+          "computedSize": 50,
+          "authoredSize": "50px"
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "x": 8,
+          "y": 94,
+          "computedSize": 100,
+          "authoredSize": "100px"
+        }
+      ],
+      "rows": [
+        "M",
+        8,
+        44,
+        "L",
+        78,
+        44,
+        "M",
+        78,
+        144,
+        "L",
+        8,
+        144
+      ],
+      "rowGaps": [],
+      "columns": [
+        "M",
+        8,
+        44,
+        "L",
+        8,
+        144,
+        "M",
+        28,
+        44,
+        "L",
+        28,
+        144,
+        "M",
+        78,
+        144,
+        "L",
+        78,
+        44
+      ],
+      "columnGaps": [
+        "M",
+        28,
+        44,
+        "L",
+        28,
+        44,
+        "L",
+        28,
+        144,
+        "L",
+        28,
+        144,
+        "Z"
+      ],
+      "positiveRowLineNumberPositions": [
+        {
+          "x": 8,
+          "y": 44
+        },
+        {
+          "x": 8,
+          "y": 144
+        }
+      ],
+      "positiveColumnLineNumberPositions": [
+        {
+          "x": 8,
+          "y": 44
+        },
+        {
+          "x": 28,
+          "y": 44
+        },
+        {
+          "x": 78,
+          "y": 44
+        }
+      ],
+      "negativeRowLineNumberPositions": [
+        {
+          "x": 78,
+          "y": 44
+        },
+        {
+          "x": 78,
+          "y": 144
+        }
+      ],
+      "negativeColumnLineNumberPositions": [
+        {
+          "x": 8,
+          "y": 144
+        },
+        {
+          "x": 28,
+          "y": 144
+        },
+        {
+          "x": 78,
+          "y": 144
+        }
+      ],
+      "areaNames": {},
+      "rowLineNameOffsets": [],
+      "columnLineNameOffsets": [],
+      "gridBorder": [
+        "M",
+        8,
+        44,
+        "L",
+        78,
+        44,
+        "L",
+        78,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "gridHighlightConfig": {
+        "gridBorderDash": false,
+        "rowLineDash": true,
+        "columnLineDash": true,
+        "showGridExtensionLines": true,
+        "showPositiveLineNumbers": true,
+        "showNegativeLineNumbers": true,
+        "showAreaNames": true,
+        "showLineNames": true,
+        "gridBorderColor": "rgba(255, 0, 0, 0)",
+        "rowLineColor": "rgba(128, 0, 0, 0)",
+        "columnLineColor": "rgba(128, 0, 0, 0)",
+        "rowGapColor": "rgba(0, 255, 0, 0)",
+        "columnGapColor": "rgba(0, 0, 255, 0)",
+        "rowHatchColor": "rgba(255, 255, 255, 0)",
+        "columnHatchColor": "rgba(128, 128, 128, 0)",
+        "areaBorderColor": "rgba(255, 0, 0, 0)"
+      },
+      "isPrimaryGrid": true
+    }
+  ]
+}
+rtlGrid{
+  "paths": [
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "rtlGrid",
+    "className": ".grid.rtl-dir",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutGrid",
+    "showAccessibilityInfo": true
+  },
+  "gridInfo": [
+    {
+      "rotationAngle": 0,
+      "columnTrackSizes": [
+        {
+          "x": 48,
+          "y": 44,
+          "computedSize": 20,
+          "authoredSize": "20px"
+        },
+        {
+          "x": 83,
+          "y": 44,
+          "computedSize": 50,
+          "authoredSize": "50px"
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "x": 38,
+          "y": 94,
+          "computedSize": 100,
+          "authoredSize": "100px"
+        }
+      ],
+      "rows": [
+        "M",
+        38,
+        44,
+        "L",
+        108,
+        44,
+        "M",
+        108,
+        144,
+        "L",
+        38,
+        144
+      ],
+      "rowGaps": [],
+      "columns": [
+        "M",
+        88,
+        44,
+        "L",
+        88,
+        144,
+        "M",
+        108,
+        144,
+        "L",
+        108,
+        44,
+        "M",
+        38,
+        44,
+        "L",
+        38,
+        144
+      ],
+      "columnGaps": [
+        "M",
+        88,
+        44,
+        "L",
+        88,
+        44,
+        "L",
+        88,
+        144,
+        "L",
+        88,
+        144,
+        "Z"
+      ],
+      "positiveRowLineNumberPositions": [
+        {
+          "x": 38,
+          "y": 44
+        },
+        {
+          "x": 38,
+          "y": 144
+        }
+      ],
+      "positiveColumnLineNumberPositions": [
+        {
+          "x": 38,
+          "y": 44
+        },
+        {
+          "x": 58,
+          "y": 44
+        },
+        {
+          "x": 108,
+          "y": 44
+        }
+      ],
+      "negativeRowLineNumberPositions": [
+        {
+          "x": 108,
+          "y": 44
+        },
+        {
+          "x": 108,
+          "y": 144
+        }
+      ],
+      "negativeColumnLineNumberPositions": [
+        {
+          "x": 38,
+          "y": 144
+        },
+        {
+          "x": 58,
+          "y": 144
+        },
+        {
+          "x": 108,
+          "y": 144
+        }
+      ],
+      "areaNames": {},
+      "rowLineNameOffsets": [],
+      "columnLineNameOffsets": [],
+      "gridBorder": [
+        "M",
+        38,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        38,
+        144,
+        "Z"
+      ],
+      "gridHighlightConfig": {
+        "gridBorderDash": false,
+        "rowLineDash": true,
+        "columnLineDash": true,
+        "showGridExtensionLines": true,
+        "showPositiveLineNumbers": true,
+        "showNegativeLineNumbers": true,
+        "showAreaNames": true,
+        "showLineNames": true,
+        "gridBorderColor": "rgba(255, 0, 0, 0)",
+        "rowLineColor": "rgba(128, 0, 0, 0)",
+        "columnLineColor": "rgba(128, 0, 0, 0)",
+        "rowGapColor": "rgba(0, 255, 0, 0)",
+        "columnGapColor": "rgba(0, 0, 255, 0)",
+        "rowHatchColor": "rgba(255, 255, 255, 0)",
+        "columnHatchColor": "rgba(128, 128, 128, 0)",
+        "areaBorderColor": "rgba(255, 0, 0, 0)"
+      },
+      "isPrimaryGrid": true
+    }
+  ]
+}
+ltrGridGap{
+  "paths": [
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "ltrGridGap",
+    "className": ".grid.ltr-dir.with-gap",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutGrid",
+    "showAccessibilityInfo": true
+  },
+  "gridInfo": [
+    {
+      "rotationAngle": 0,
+      "columnTrackSizes": [
+        {
+          "x": 18,
+          "y": 44,
+          "computedSize": 20,
+          "authoredSize": "20px"
+        },
+        {
+          "x": 69,
+          "y": 44,
+          "computedSize": 50,
+          "authoredSize": "50px"
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "x": 8,
+          "y": 94,
+          "computedSize": 100,
+          "authoredSize": "100px"
+        }
+      ],
+      "rows": [
+        "M",
+        8,
+        44,
+        "L",
+        94,
+        44,
+        "M",
+        94,
+        144,
+        "L",
+        8,
+        144
+      ],
+      "rowGaps": [],
+      "columns": [
+        "M",
+        8,
+        44,
+        "L",
+        8,
+        144,
+        "M",
+        28,
+        144,
+        "L",
+        28,
+        44,
+        "M",
+        44,
+        44,
+        "L",
+        44,
+        144,
+        "M",
+        94,
+        144,
+        "L",
+        94,
+        44
+      ],
+      "columnGaps": [
+        "M",
+        28,
+        44,
+        "L",
+        44,
+        44,
+        "L",
+        44,
+        144,
+        "L",
+        28,
+        144,
+        "Z"
+      ],
+      "positiveRowLineNumberPositions": [
+        {
+          "x": 8,
+          "y": 44
+        },
+        {
+          "x": 8,
+          "y": 144
+        }
+      ],
+      "positiveColumnLineNumberPositions": [
+        {
+          "x": 8,
+          "y": 44
+        },
+        {
+          "x": 36,
+          "y": 44
+        },
+        {
+          "x": 94,
+          "y": 44
+        }
+      ],
+      "negativeRowLineNumberPositions": [
+        {
+          "x": 94,
+          "y": 44
+        },
+        {
+          "x": 94,
+          "y": 144
+        }
+      ],
+      "negativeColumnLineNumberPositions": [
+        {
+          "x": 8,
+          "y": 144
+        },
+        {
+          "x": 36,
+          "y": 144
+        },
+        {
+          "x": 94,
+          "y": 144
+        }
+      ],
+      "areaNames": {},
+      "rowLineNameOffsets": [],
+      "columnLineNameOffsets": [],
+      "gridBorder": [
+        "M",
+        8,
+        44,
+        "L",
+        94,
+        44,
+        "L",
+        94,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "gridHighlightConfig": {
+        "gridBorderDash": false,
+        "rowLineDash": true,
+        "columnLineDash": true,
+        "showGridExtensionLines": true,
+        "showPositiveLineNumbers": true,
+        "showNegativeLineNumbers": true,
+        "showAreaNames": true,
+        "showLineNames": true,
+        "gridBorderColor": "rgba(255, 0, 0, 0)",
+        "rowLineColor": "rgba(128, 0, 0, 0)",
+        "columnLineColor": "rgba(128, 0, 0, 0)",
+        "rowGapColor": "rgba(0, 255, 0, 0)",
+        "columnGapColor": "rgba(0, 0, 255, 0)",
+        "rowHatchColor": "rgba(255, 255, 255, 0)",
+        "columnHatchColor": "rgba(128, 128, 128, 0)",
+        "areaBorderColor": "rgba(255, 0, 0, 0)"
+      },
+      "isPrimaryGrid": true
+    }
+  ]
+}
+rtlGridGap{
+  "paths": [
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        8,
+        144,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "rtlGridGap",
+    "className": ".grid.rtl-dir.with-gap",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutGrid",
+    "showAccessibilityInfo": true
+  },
+  "gridInfo": [
+    {
+      "rotationAngle": 0,
+      "columnTrackSizes": [
+        {
+          "x": 32,
+          "y": 44,
+          "computedSize": 20,
+          "authoredSize": "20px"
+        },
+        {
+          "x": 83,
+          "y": 44,
+          "computedSize": 50,
+          "authoredSize": "50px"
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "x": 22,
+          "y": 94,
+          "computedSize": 100,
+          "authoredSize": "100px"
+        }
+      ],
+      "rows": [
+        "M",
+        22,
+        44,
+        "L",
+        108,
+        44,
+        "M",
+        108,
+        144,
+        "L",
+        22,
+        144
+      ],
+      "rowGaps": [],
+      "columns": [
+        "M",
+        88,
+        44,
+        "L",
+        88,
+        144,
+        "M",
+        108,
+        144,
+        "L",
+        108,
+        44,
+        "M",
+        22,
+        44,
+        "L",
+        22,
+        144,
+        "M",
+        72,
+        144,
+        "L",
+        72,
+        44
+      ],
+      "columnGaps": [
+        "M",
+        72,
+        44,
+        "L",
+        88,
+        44,
+        "L",
+        88,
+        144,
+        "L",
+        72,
+        144,
+        "Z"
+      ],
+      "positiveRowLineNumberPositions": [
+        {
+          "x": 22,
+          "y": 44
+        },
+        {
+          "x": 22,
+          "y": 144
+        }
+      ],
+      "positiveColumnLineNumberPositions": [
+        {
+          "x": 22,
+          "y": 44
+        },
+        {
+          "x": 50,
+          "y": 44
+        },
+        {
+          "x": 108,
+          "y": 44
+        }
+      ],
+      "negativeRowLineNumberPositions": [
+        {
+          "x": 108,
+          "y": 44
+        },
+        {
+          "x": 108,
+          "y": 144
+        }
+      ],
+      "negativeColumnLineNumberPositions": [
+        {
+          "x": 22,
+          "y": 144
+        },
+        {
+          "x": 50,
+          "y": 144
+        },
+        {
+          "x": 108,
+          "y": 144
+        }
+      ],
+      "areaNames": {},
+      "rowLineNameOffsets": [],
+      "columnLineNameOffsets": [],
+      "gridBorder": [
+        "M",
+        22,
+        44,
+        "L",
+        108,
+        44,
+        "L",
+        108,
+        144,
+        "L",
+        22,
+        144,
+        "Z"
+      ],
+      "gridHighlightConfig": {
+        "gridBorderDash": false,
+        "rowLineDash": true,
+        "columnLineDash": true,
+        "showGridExtensionLines": true,
+        "showPositiveLineNumbers": true,
+        "showNegativeLineNumbers": true,
+        "showAreaNames": true,
+        "showLineNames": true,
+        "gridBorderColor": "rgba(255, 0, 0, 0)",
+        "rowLineColor": "rgba(128, 0, 0, 0)",
+        "columnLineColor": "rgba(128, 0, 0, 0)",
+        "rowGapColor": "rgba(0, 255, 0, 0)",
+        "columnGapColor": "rgba(0, 0, 255, 0)",
+        "rowHatchColor": "rgba(255, 255, 255, 0)",
+        "columnHatchColor": "rgba(128, 128, 128, 0)",
+        "areaBorderColor": "rgba(255, 0, 0, 0)"
+      },
+      "isPrimaryGrid": true
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-direction.js b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-direction.js
new file mode 100644
index 0000000..443a3c6
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-direction.js
@@ -0,0 +1,61 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`This test verifies that grids with direction rtl and ltr are correctly highlighted.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      .grid {
+        position: absolute;
+	top: 44px;
+	left: 8px;
+        grid-template-columns: 20px 50px;
+        grid-template-rows: 100px;
+        width: 100px;
+        display: grid;
+      }
+      .ltr-dir {
+        direction: ltr;
+      }
+      .rtl-dir {
+        direction: rtl;
+      }
+      .with-gap {
+        grid-gap: 1em;
+      }
+      </style>
+
+      <p id="description">This test verifies that grids with direction rtl and ltr are correctly highlighted.</p>
+      <div>
+          <div class="grid ltr-dir" id="ltrGrid">
+              <div style="background: burlywood"></div>
+              <div style="background: cadetblue"></div>
+          </div>
+          <div class="grid rtl-dir" id="rtlGrid">
+              <div style="background: burlywood"></div>
+              <div style="background: cadetblue"></div>
+          </div>
+          <div class="grid ltr-dir with-gap" id="ltrGridGap">
+              <div style="background: burlywood"></div>
+              <div style="background: cadetblue"></div>
+          </div>
+          <div class="grid rtl-dir with-gap" id="rtlGridGap">
+              <div style="background: burlywood"></div>
+              <div style="background: cadetblue"></div>
+          </div>
+      </div>
+`);
+  function dumpGridHighlight(id) {
+    return new Promise(resolve => ElementsTestRunner.dumpInspectorHighlightJSON(id, resolve));
+  }
+
+  await dumpGridHighlight('ltrGrid');
+  await dumpGridHighlight('rtlGrid');
+  await dumpGridHighlight('ltrGridGap');
+  await dumpGridHighlight('rtlGridGap');
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/portals/portals-sources-activate-expected.txt b/third_party/blink/web_tests/http/tests/devtools/portals/portals-sources-activate-expected.txt
new file mode 100644
index 0000000..b5ce4e2
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/portals/portals-sources-activate-expected.txt
@@ -0,0 +1,7 @@
+Checks that breakpoint set inside a portalactivate event handler is hit on activation
+error: Item to refresh is not present
+Script execution paused.
+Call stack:
+    0) window.onportalactivate (append-predecessor.html:5)
+Script execution resumed.
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/portals/portals-sources-activate.js b/third_party/blink/web_tests/http/tests/devtools/portals/portals-sources-activate.js
new file mode 100644
index 0000000..bd98d8a2
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/portals/portals-sources-activate.js
@@ -0,0 +1,20 @@
+// 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.
+
+
+(async function () {
+  TestRunner.addResult(`Checks that breakpoint set inside a portalactivate event handler is hit on activation`);
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.showPanel('sources');
+
+  await TestRunner.navigatePromise('resources/append-predecessor-host.html');
+
+  await SourcesTestRunner.startDebuggerTestPromise();
+  const sourceFrame = await SourcesTestRunner.showScriptSourcePromise('append-predecessor.html');
+  await SourcesTestRunner.toggleBreakpoint(sourceFrame, 4);
+  TestRunner.evaluateInPage(`setTimeout(() => document.querySelector('portal').activate());`);
+  const callFrames = await SourcesTestRunner.waitUntilPausedPromise();
+  await SourcesTestRunner.captureStackTrace(callFrames);
+  SourcesTestRunner.completeDebuggerTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.txt b/third_party/blink/web_tests/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.txt
new file mode 100644
index 0000000..5e471995
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.txt
@@ -0,0 +1,4 @@
+CONSOLE ERROR: Document policy violation: oversized-images is not allowed in this document.
+CONSOLE ERROR: Document policy violation: oversized-images is not allowed in this document.
+CONSOLE ERROR: Document policy violation: oversized-images is not allowed in this document.
+CONSOLE ERROR: Document policy violation: oversized-images is not allowed in this document.
diff --git a/third_party/blink/web_tests/http/tests/images/document-policy-oversized-images-edge-cases.html b/third_party/blink/web_tests/http/tests/images/document-policy/document-policy-oversized-images-edge-cases.html
similarity index 100%
rename from third_party/blink/web_tests/http/tests/images/document-policy-oversized-images-edge-cases.html
rename to third_party/blink/web_tests/http/tests/images/document-policy/document-policy-oversized-images-edge-cases.html
diff --git a/third_party/blink/web_tests/http/tests/images/document-policy/resources/frame-with-oversized-images-edge-cases.php b/third_party/blink/web_tests/http/tests/images/document-policy/resources/frame-with-oversized-images-edge-cases.php
new file mode 100644
index 0000000..a9b796a
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/images/document-policy/resources/frame-with-oversized-images-edge-cases.php
@@ -0,0 +1,24 @@
+<?php
+header("Document-Policy: oversized-images=2.0");
+?>
+<!DOCTYPE html>
+
+<!--
+  Images should be replaced by placeholders if there are considered
+  oversized, i.e. having
+  image_size / (container_size * pixel_ratio) > threshold.
+  Threshold is set by the document policy header(2.0 in this test).
+-->
+<body>
+  <img src="green-256x256.jpg">
+  <!-- Following cases are for device pixel ratio = 1.0 -->
+  <!-- Image with size < 128 should all be replaced with placeholders -->
+  <img src="green-256x256.jpg" width="128" height="128">
+  <img src="green-256x256.jpg" width="127" height="127">
+  <img src="green-256x256.jpg" width="129" height="129">
+  <!-- Following cases are for device pixel ratio = 2.0 -->
+  <!-- Image with size < 64 should all be replaced with placeholders -->
+  <img src="green-256x256.jpg" width="64" height="64">
+  <img src="green-256x256.jpg" width="63" height="63">
+  <img src="green-256x256.jpg" width="65" height="65">
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/images/document-policy/resources/green-256x256.jpg b/third_party/blink/web_tests/http/tests/images/document-policy/resources/green-256x256.jpg
new file mode 100644
index 0000000..497ed770
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/images/document-policy/resources/green-256x256.jpg
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/images/resources/frame-with-oversized-images-edge-cases.php b/third_party/blink/web_tests/http/tests/images/resources/frame-with-oversized-images-edge-cases.php
deleted file mode 100644
index b43e2c5..0000000
--- a/third_party/blink/web_tests/http/tests/images/resources/frame-with-oversized-images-edge-cases.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-header("Document-Policy: oversized-images=2.0");
-?>
-<!DOCTYPE html>
-<body>
-<img src="green-256x256.jpg">
-<img src="green-256x256.jpg" width="128" height="128" >
-<img src="green-256x256.jpg" width="127" height="127" >
-<img src="green-256x256.jpg" width="129" height="129" >
-</body>
diff --git a/third_party/blink/web_tests/platform/linux/http/tests/images/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/linux/http/tests/images/document-policy-oversized-images-edge-cases-expected.png
deleted file mode 100644
index e8dd3dc6..0000000
--- a/third_party/blink/web_tests/platform/linux/http/tests/images/document-policy-oversized-images-edge-cases-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/linux/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
new file mode 100644
index 0000000..d97f50a3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
new file mode 100644
index 0000000..8ab930aa
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/http/tests/images/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/mac/http/tests/images/document-policy-oversized-images-edge-cases-expected.png
deleted file mode 100644
index 66bfd22..0000000
--- a/third_party/blink/web_tests/platform/mac/http/tests/images/document-policy-oversized-images-edge-cases-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/mac/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
new file mode 100644
index 0000000..38b9ed5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
new file mode 100644
index 0000000..8ab930aa
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/http/tests/images/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/win/http/tests/images/document-policy-oversized-images-edge-cases-expected.png
deleted file mode 100644
index fbf990d..0000000
--- a/third_party/blink/web_tests/platform/win/http/tests/images/document-policy-oversized-images-edge-cases-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/win/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
new file mode 100644
index 0000000..44c0c0d7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
new file mode 100644
index 0000000..dc7f6ad0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/http/tests/images/document-policy/README.txt b/third_party/blink/web_tests/virtual/scalefactor200/http/tests/images/document-policy/README.txt
new file mode 100644
index 0000000..3879583e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/http/tests/images/document-policy/README.txt
@@ -0,0 +1 @@
+# This suite runs tests in http/tests/images/document-policy with --force-device-scale-factor=2
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/images/document-policy-oversized-images-edge-cases-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/http/tests/images/document-policy-oversized-images-edge-cases-expected.txt
rename to third_party/blink/web_tests/virtual/scalefactor200/http/tests/images/document-policy/document-policy-oversized-images-edge-cases-expected.txt
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
index a48e141..a343612 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 496 tests; 411 PASS, 85 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 496 tests; 412 PASS, 84 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Test driver for asyncInitCertificate
@@ -235,7 +235,7 @@
 PASS RTCRtpSender interface: attribute track
 PASS RTCRtpSender interface: attribute transport
 PASS RTCRtpSender interface: operation getCapabilities(DOMString)
-FAIL RTCRtpSender interface: operation setParameters(RTCRtpSendParameters) assert_equals: property has wrong .length expected 1 but got 0
+PASS RTCRtpSender interface: operation setParameters(RTCRtpSendParameters)
 PASS RTCRtpSender interface: operation getParameters()
 PASS RTCRtpSender interface: operation replaceTrack(MediaStreamTrack?)
 PASS RTCRtpSender interface: operation setStreams(MediaStream...)
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 1610113b..1f6f3e5 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -2245,6 +2245,7 @@
     getter form
     getter labels
     getter role
+    getter shadowRoot
     getter states
     getter validationMessage
     getter validity
diff --git a/third_party/shell-encryption/src/int256_test.cc b/third_party/shell-encryption/src/int256_test.cc
index 01e8927..b1a88056 100644
--- a/third_party/shell-encryption/src/int256_test.cc
+++ b/third_party/shell-encryption/src/int256_test.cc
@@ -368,22 +368,6 @@
   EXPECT_EQ(x4, x3);
 }
 
-TEST(Int256, DivideByZeroCheckFails) {
-  uint256 a = 0;
-  uint256 b = 0;
-  EXPECT_DEATH(a / b, "Division or mod by zero:");
-  a = 123;
-  EXPECT_DEATH(a / b, "Division or mod by zero:");
-}
-
-TEST(Int256, ModByZeroCheckFails) {
-  uint256 a = 0;
-  uint256 b = 0;
-  EXPECT_DEATH(a % b, "Division or mod by zero:");
-  a = 123;
-  EXPECT_DEATH(a % b, "Division or mod by zero:");
-}
-
 TEST(Int256, DivideAndMod) {
   // a := q * b + r
   uint256 a, b, q, r;
diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
index 17c73a67..a7adbe95a 100644
--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
+++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
@@ -1,10 +1,9 @@
 #include <zxcvbn/frequency_lists.hpp>
 
 #include <unordered_map>
+#include <utility>
 
 #include "base/no_destructor.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_piece.h"
 
 namespace zxcvbn {
 
@@ -16,16 +15,10 @@
   return *ranked_dicts;
 }
 
-}
+}  // namespace
 
-bool ParseRankedDictionary(DictionaryTag tag, base::StringPiece str) {
-  RankedDict& dict = ranked_dicts()[tag];
-  if (!dict.empty())
-    return false;
-
-  dict = build_ranked_dict(base::SplitStringPiece(
-      str, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY));
-  return true;
+void SetRankedDicts(std::unordered_map<DictionaryTag, RankedDict> dicts) {
+  ranked_dicts() = std::move(dicts);
 }
 
 RankedDicts convert_to_ranked_dicts(std::unordered_map<DictionaryTag, RankedDict> & ranked_dicts) {
diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
index 191842fb..33da027 100644
--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
+++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
@@ -38,7 +38,7 @@
 
 using RankedDicts = std::unordered_map<DictionaryTag, const RankedDict &>;
 
-bool ParseRankedDictionary(DictionaryTag tag, base::StringPiece str);
+void SetRankedDicts(std::unordered_map<DictionaryTag, RankedDict> dicts);
 
 RankedDicts convert_to_ranked_dicts(std::unordered_map<DictionaryTag, RankedDict> & ranked_dicts);
 RankedDicts default_ranked_dicts();
diff --git a/third_party/zxcvbn-cpp/patches/dictionary_component.diff b/third_party/zxcvbn-cpp/patches/dictionary_component.diff
index dd2195e1..b597bae 100644
--- a/third_party/zxcvbn-cpp/patches/dictionary_component.diff
+++ b/third_party/zxcvbn-cpp/patches/dictionary_component.diff
@@ -1,5 +1,5 @@
 diff --git a/third_party/zxcvbn-cpp/data-scripts/build_frequency_lists.py b/third_party/zxcvbn-cpp/data-scripts/build_frequency_lists.py
-index d5504e073cb3..9be1391c5aa9 100755
+index d5504e0..9be1391 100755
 --- a/third_party/zxcvbn-cpp/data-scripts/build_frequency_lists.py
 +++ b/third_party/zxcvbn-cpp/data-scripts/build_frequency_lists.py
 @@ -122,8 +122,7 @@ def to_kv(lst, lst_name):
@@ -37,20 +37,19 @@
          output_fn = output_coffee
  
 diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
-index 4e4b72756e34..17c73a67bd0a 100644
+index 4e4b727..a7adbe9 100644
 --- a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
 +++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
-@@ -1,11 +1,33 @@
+@@ -1,11 +1,26 @@
  #include <zxcvbn/frequency_lists.hpp>
  
 -#include <zxcvbn/_frequency_lists.hpp>
 -
  #include <unordered_map>
- 
-+#include "base/no_destructor.h"
-+#include "base/strings/string_split.h"
-+#include "base/strings/string_piece.h"
++#include <utility>
 +
++#include "base/no_destructor.h"
+ 
  namespace zxcvbn {
  
 +namespace {
@@ -61,22 +60,16 @@
 +  return *ranked_dicts;
 +}
 +
-+}
++}  // namespace
 +
-+bool ParseRankedDictionary(DictionaryTag tag, base::StringPiece str) {
-+  RankedDict& dict = ranked_dicts()[tag];
-+  if (!dict.empty())
-+    return false;
-+
-+  dict = build_ranked_dict(base::SplitStringPiece(
-+      str, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY));
-+  return true;
++void SetRankedDicts(std::unordered_map<DictionaryTag, RankedDict> dicts) {
++  ranked_dicts() = std::move(dicts);
 +}
 +
  RankedDicts convert_to_ranked_dicts(std::unordered_map<DictionaryTag, RankedDict> & ranked_dicts) {
    RankedDicts build;
  
-@@ -17,8 +39,7 @@ RankedDicts convert_to_ranked_dicts(std::unordered_map<DictionaryTag, RankedDict
+@@ -17,8 +32,7 @@ RankedDicts convert_to_ranked_dicts(std::unordered_map<DictionaryTag, RankedDict
  }
  
  RankedDicts default_ranked_dicts() {
@@ -87,10 +80,10 @@
 -
  }
 diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
-index c75ea30d5a91..191842fb530c 100644
+index c75ea30..634ecb2 100644
 --- a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
 +++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
-@@ -2,15 +2,24 @@
+@@ -2,7 +2,6 @@
  #define __ZXCVBN__FREQUENCY_LISTS_HPP
  
  #include <zxcvbn/frequency_lists_common.hpp>
@@ -98,10 +91,8 @@
  
  #include <unordered_map>
  
- #include <cstdint>
+@@ -10,7 +9,15 @@
  
-+#include "base/strings/string_piece.h"
-+
  namespace zxcvbn {
  
 -using DictionaryTag = _frequency_lists::DictionaryTag;
@@ -117,11 +108,11 @@
  
  }
  
-@@ -29,6 +38,8 @@ namespace zxcvbn {
+@@ -29,6 +36,8 @@ namespace zxcvbn {
  
  using RankedDicts = std::unordered_map<DictionaryTag, const RankedDict &>;
  
-+bool ParseRankedDictionary(DictionaryTag tag, base::StringPiece str);
++void SetRankedDicts(std::unordered_map<DictionaryTag, RankedDict> dicts);
 +
  RankedDicts convert_to_ranked_dicts(std::unordered_map<DictionaryTag, RankedDict> & ranked_dicts);
  RankedDicts default_ranked_dicts();
diff --git a/third_party/zxcvbn-cpp/test/matching_unittest.cc b/third_party/zxcvbn-cpp/test/matching_unittest.cc
index 4def546..07a4913 100644
--- a/third_party/zxcvbn-cpp/test/matching_unittest.cc
+++ b/third_party/zxcvbn-cpp/test/matching_unittest.cc
@@ -304,7 +304,7 @@
 
   {
     // default dictionaries
-    ParseRankedDictionary(DictionaryTag::US_TV_AND_FILM, "wow");
+    SetRankedDicts({{DictionaryTag::US_TV_AND_FILM, {{"wow", 1}}}});
     std::vector<Match> matches =
         dictionary_match("wow", default_ranked_dicts());
     EXPECT_THAT(matches, ElementsAre(ExpectedDictionaryMatch{
@@ -319,7 +319,7 @@
 
   {
     // matches with provided user input dictionary
-    ParseRankedDictionary(DictionaryTag::USER_INPUTS, "foo\nbar");
+    SetRankedDicts({{DictionaryTag::USER_INPUTS, {{"foo", 1}, {"bar", 2}}}});
     std::vector<Match> matches =
         dictionary_match("foobar", default_ranked_dicts());
     EXPECT_THAT(matches, ElementsAre(
@@ -1004,7 +1004,8 @@
 TEST(ZxcvbnTest, Omnimatch) {
   EXPECT_THAT(omnimatch(""), IsEmpty());
 
-  ParseRankedDictionary(DictionaryTag::ENGLISH_WIKIPEDIA, "rosebud\nmaelstrom");
+  SetRankedDicts(
+      {{DictionaryTag::ENGLISH_WIKIPEDIA, {{"rosebud", 1}, {"maelstrom", 2}}}});
   std::string password = "r0sebudmaelstrom11/20/91aaaa";
   std::vector<Match> matches = omnimatch(password);
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index e761565..c08c2b1d 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -94,15 +94,15 @@
       'android-cronet-x86-dbg': 'android_cronet_debug_static_bot_x86',
       'android-cronet-x86-rel': 'android_cronet_release_bot_minimal_symbols_x86',
       'android-incremental-dbg': 'android_incremental_debug_bot',
-      'android-lollipop-arm-rel': 'android_release_bot_minimal_symbols_webview_google',
+      'android-lollipop-arm-rel': 'android_release_bot_minimal_symbols_fastbuild_webview_google',
 
       # This bot must use the gpu_tests mixin to match 'Android FYI Release (Nexus 5X)'
       # on the chromium.gpu waterfall, which it mirrors via trybots.pyl.
-      'android-marshmallow-arm64-rel': 'gpu_tests_android_release_bot_minimal_symbols_arm64',
+      'android-marshmallow-arm64-rel': 'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild',
 
       'android-marshmallow-x86-rel': 'android_release_bot_minimal_symbols_x86_fastbuild_resource_whitelisting_webview_google',
       'android-nougat-arm64-rel': 'android_release_bot_minimal_symbols_arm64_fastbuild_webview_google',
-      'android-pie-arm64-rel': 'android_release_bot_minimal_symbols_arm64_webview_google_fastbuild',
+      'android-pie-arm64-rel': 'android_release_bot_minimal_symbols_arm64_webview_google',
       'android-pie-x86-rel': 'android_release_bot_minimal_symbols_x86_fastbuild_webview_google',
       'android-10-arm64-rel': 'android_release_bot_minimal_symbols_arm64_fastbuild_webview_google',
     },
@@ -302,6 +302,7 @@
       'mac-upload-perfetto': 'release_bot',
       'win-annotator-rel': 'release_bot',
       'win-celab-builder-rel': 'release_bot_minimal_symbols',
+      'win-omaha-builder-rel': 'omaha_release_bot',
       'win-pixel-builder-rel': 'release_bot',
       'win-upload-perfetto': 'release_bot',
       'win10-code-coverage': 'clang_code_coverage',
@@ -550,10 +551,10 @@
 
     'chromium.dev': {
       # This should be the same with 'android-lollipop-arm-rel'.
-      'android-lollipop-arm-rel-swarming': 'android_release_bot_minimal_symbols_webview_google',
+      'android-lollipop-arm-rel-swarming': 'android_release_bot_minimal_symbols_fastbuild_webview_google',
 
       # This should be the same with 'android-marshmallow-arm64-rel'.
-      'android-marshmallow-arm64-rel-swarming':  'gpu_tests_android_release_bot_minimal_symbols_arm64',
+      'android-marshmallow-arm64-rel-swarming':  'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild',
 
       # These should be the same with 'Linux Builder'.
       'linux-rel-swarming': 'gpu_tests_release_bot',
@@ -707,7 +708,7 @@
       # TODO(crbug/597596): Switch this back to debug_trybot when cronet's
       # shared library loading is fixed.
       'android-cronet-arm-dbg': 'android_cronet_debug_static_bot_arm_no_neon',
-      'android-lollipop-arm-rel': 'android_release_trybot_webview_google',
+      'android-lollipop-arm-rel': 'android_release_trybot_fastbuild_webview_google',
       'android-marshmallow-arm64-rel': 'gpu_tests_android_release_trybot_arm64_resource_whitelisting_fastbuild_java_coverage',
       'android-marshmallow-x86-fyi-rel': 'android_release_trybot_x86_fastbuild_resource_whitelisting_webview_google',
       'android-marshmallow-x86-rel': 'android_release_trybot_x86_fastbuild_resource_whitelisting_webview_google',
@@ -1259,15 +1260,9 @@
       'strip_debug_info', 'webview_google',
     ],
 
-    'android_release_bot_minimal_symbols_arm64_webview_google_fastbuild': [
-      'android', 'release_bot', 'minimal_symbols', 'arm64',
-      'strip_debug_info', 'webview_google', 'android_fastbuild',
-      'android_no_proguard',
-    ],
-
-    'android_release_bot_minimal_symbols_webview_google': [
+    'android_release_bot_minimal_symbols_fastbuild_webview_google': [
       'android', 'release_bot', 'minimal_symbols',
-      'strip_debug_info', 'webview_google',
+      'strip_debug_info', 'android_fastbuild', 'webview_google',
     ],
 
     'android_release_bot_minimal_symbols_x86_fastbuild_resource_whitelisting_webview_google': [
@@ -1309,9 +1304,9 @@
       'use_clang_coverage', 'partial_code_coverage_instrumentation',
     ],
 
-    'android_release_trybot_webview_google': [
-      'android', 'release_trybot', 'strip_debug_info', 'android_no_proguard',
-      'webview_google',
+    'android_release_trybot_fastbuild_webview_google': [
+      'android', 'release_trybot', 'strip_debug_info', 'android_fastbuild',
+      'android_no_proguard', 'webview_google',
     ],
 
     'android_release_trybot_x86_fastbuild_resource_whitelisting_webview_google': [
@@ -1820,9 +1815,10 @@
       'gpu_fyi_tests', 'release_trybot', 'x86',
     ],
 
-    'gpu_tests_android_release_bot_minimal_symbols_arm64': [
+    'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild': [
       'gpu_tests', 'android', 'release_bot', 'minimal_symbols', 'arm64',
-      'resource_whitelisting', 'static_angle', 'webview_google',
+      'resource_whitelisting', 'static_angle', 'android_fastbuild', 'webview_google',
+      'android_no_proguard',
     ],
 
     'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild_java_coverage': [
@@ -1854,6 +1850,7 @@
     'gpu_tests_android_release_trybot_arm64_resource_whitelisting_fastbuild_java_coverage': [
       'gpu_tests', 'android', 'release_trybot', 'arm64', 'static_angle',
       'resource_whitelisting', 'android_fastbuild', 'webview_google',
+      'android_no_proguard',
       'use_java_coverage', 'partial_code_coverage_instrumentation',
     ],
 
@@ -2189,6 +2186,10 @@
       'official_optimize_goma_trybot',
     ],
 
+    'omaha_release_bot': [
+      'omaha_on_win_mac', 'release',
+    ],
+
     'ozone_linux_release_bot': [
       'ozone_linux', 'release_bot',
     ],
@@ -2861,6 +2862,10 @@
       'mixins': ['official_optimize_goma', 'minimal_symbols', 'dcheck_always_on'],
     },
 
+    'omaha_on_win_mac': {
+      'gn_args': 'is_google_branded=true',
+    },
+
     'optimize_for_fuzzing': {
       'gn_args': 'optimize_for_fuzzing=true',
     },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 48373a8..0afa40d7 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -28713,6 +28713,7 @@
   <int value="3375" label="V8RTCRtpTransceiver_Stop_Method"/>
   <int value="3376" label="SecurePaymentConfirmation"/>
   <int value="3377" label="CSSInvalidVariableUnset"/>
+  <int value="3378" label="ElementInternalsShadowRoot"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -37309,6 +37310,7 @@
   <int value="7" label="Share"/>
   <int value="8" label="Open all in New Tabs"/>
   <int value="9" label="Hide"/>
+  <int value="10" label="Move"/>
 </enum>
 
 <enum name="IOSMenuScenario">
@@ -37318,6 +37320,7 @@
   <int value="3" label="RecentTabs Entry"/>
   <int value="4" label="ContentSuggestions Entry"/>
   <int value="5" label="RecentTabs Header"/>
+  <int value="6" label="Bookmark Folder"/>
 </enum>
 
 <enum name="IOSNTPImpression">
@@ -47318,6 +47321,16 @@
   <int value="2" label="ntdll.dll"/>
 </enum>
 
+<enum name="MojoInterfaceName">
+  <int value="-1403116593" label="content.mojom.FrameHost"/>
+  <int value="-1370606112" label="blink.mojom.LocalFrameHost"/>
+  <int value="-1324568928" label="blink.mojom.LocalMainFrameHost"/>
+  <int value="-1001040796" label="blink.mojom.ManifestUrlChangeObserver"/>
+  <int value="163955581" label="blink.mojom.PortalHost"/>
+  <int value="374886440" label="content.mojom.Echo"/>
+  <int value="1755985492" label="content.mojom.RenderAccessibilityHost"/>
+</enum>
+
 <enum name="MojoMachPortRelayBrokerError">
   <int value="0" label="SUCCESS">
     The Mach port was successfully sent or received.
@@ -54487,6 +54500,9 @@
   <int value="7" label="Reauth required">
     Re-authenticaion for filling passwords is required.
   </int>
+  <int value="8" label="Password is already filled">
+    Password field is already filled in and it doesn't look like a placeholder.
+  </int>
 </enum>
 
 <enum name="PasswordManagerHttpCredentialType">
@@ -54976,10 +54992,11 @@
 
 <enum name="PasswordScriptsFetcherParsingResult">
   <int value="0" label="No response from server"/>
-  <int value="1" label="Invalid JSON string"/>
-  <int value="2" label="Not a JSON dictionary"/>
+  <int value="1" label="Invalid JSON string - deprecated"/>
+  <int value="2" label="Not a JSON dictionary - deprecated"/>
   <int value="3" label="Invalid URLs found"/>
   <int value="4" label="OK"/>
+  <int value="5" label="Invalid JSON (either syntactically or structurally)"/>
 </enum>
 
 <enum name="PasswordStoreChange">
@@ -57990,6 +58007,7 @@
   <int value="15" label="Add new profile button"/>
   <int value="16" label="Sync settings(sync on) button"/>
   <int value="17" label="Edit profile button"/>
+  <int value="18" label="Create incognito shortcut button"/>
 </enum>
 
 <enum name="ProfileNameState">
@@ -61645,6 +61663,7 @@
   <int value="4" label="DISABLED_BY_EXTENSION"/>
   <int value="5" label="ENABLED_STANDARD"/>
   <int value="6" label="ENABLED_ENHANCED"/>
+  <int value="7" label="ENABLED_STANDARD_AVAILABLE_ENHANCED"/>
 </enum>
 
 <enum name="SafetyCheckUpdateStatus">
@@ -65101,11 +65120,15 @@
 
 <enum name="SigninSSOIdentityListRequestCacheState">
   <int value="0"
-      label="SSO identity cache is stale, the identity list is synchronously
-             fetched."/>
+      label="SSO identity cache is stale, M85: the identity list is
+             synchronously fetched and then returned, M86: the stale identity
+             list is returned."/>
   <int value="1"
-      label="SSO identity cache is valid, the identity list from the cache is
+      label="SSO identity cache is valid, the valid identity list is
              returned."/>
+  <int value="2"
+      label="SSO identity cache is not populated, the identity list is
+             synchronously fetched and then returned."/>
 </enum>
 
 <enum name="SigninSSOWKWebViewGetAllCookiesRequest">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 9a99973..1bb1737 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -17749,6 +17749,27 @@
   </summary>
 </histogram>
 
+<histogram
+    name="BackForwardCache.UnexpectedRendererToBrowserMessage.InterfaceName"
+    enum="MojoInterfaceName" expires_after="2021-01-01">
+  <owner>carlscab@google.com</owner>
+  <owner>bfcache-dev@chromium.org</owner>
+  <summary>
+    Hash (base::HashMetricName) of a mojo interface name.
+
+    A sample is recorded for every message the browser receives from a renderer
+    for a page that is in the back-forward cache. Logging can be disabled via
+    the back-forward cache feature parameter
+    &quot;message_handling_when_cached&quot;.
+
+    Renderers should not be sending messages back to the browser for pages that
+    are in the back-forward cache. Eventually we will be killing such renderers
+    (as we could assume that it is misbehaving because it got compromised). But
+    first we must make sure that we are not sending messages by mistake (i.e.
+    bugs in the renderer).
+  </summary>
+</histogram>
+
 <histogram base="true" name="BackgroundFetch.EventDispatchFailure.Dispatch"
     enum="ServiceWorkerStatusCode" expires_after="2021-06-30">
 <!-- Name completed by histogram_suffixes name="BackgroundFetchEvents" -->
@@ -28465,7 +28486,7 @@
 <histogram name="Compositing.CopyFromSurfaceTimeSynchronous" units="ms"
     expires_after="2016-04-29">
   <obsolete>
-    Removed 04/2016 as doesn't have data nor owner.
+    Made obsolete 04/2016. Logging removed 08/2020.
   </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
@@ -214478,6 +214499,8 @@
 
 <histogram_suffixes name="SigninSSOIdentityListRequestDurationCacheState"
     separator=".">
+  <suffix name="NotPopulated"
+      label="The SSOAuth identity list is not populated."/>
   <suffix name="Stale" label="The SSOAuth identity list is stale."/>
   <suffix name="Valid" label="The SSOAuth identity list is valid."/>
   <affected-histogram name="Signin.SSOIdentityListRequest.Duration"/>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 6c44d5ce..c116905 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,12 +5,12 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/win/dbee93ac203d9269fe93c1ff1e48eba6148e542c/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "27f63277ace856aad218ce2806fcd2a3d5ca0839",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/dbee93ac203d9269fe93c1ff1e48eba6148e542c/trace_processor_shell"
+            "hash": "5653df7850d5d9418d72896ee3b06ecc5d10a5f5",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/e1360ddcec9e7e29e6d0739fbe360374ab66b712/trace_processor_shell"
         },
         "linux": {
-            "hash": "d1080003a68b2a808256bed9d034529030f1aca9",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/dbee93ac203d9269fe93c1ff1e48eba6148e542c/trace_processor_shell"
+            "hash": "35f5be0660c5b16796877db5a64765d7b99a3daf",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/e1360ddcec9e7e29e6d0739fbe360374ab66b712/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/clipboard/clipboard_constants.cc b/ui/base/clipboard/clipboard_constants.cc
index e435dd2..3ea7842 100644
--- a/ui/base/clipboard/clipboard_constants.cc
+++ b/ui/base/clipboard/clipboard_constants.cc
@@ -10,12 +10,15 @@
 const char kMimeTypeTextUtf8[] = "text/plain;charset=utf-8";
 const char kMimeTypeURIList[] = "text/uri-list";
 const char kMimeTypeMozillaURL[] = "text/x-moz-url";
+// Unstandardized format for downloading files  after drop events. Now only
+// works in Windows, but used to also work in Linux and MacOS.
+// See https://crbug.com/860557 and https://crbug.com/425170.
 const char kMimeTypeDownloadURL[] = "downloadurl";
 const char kMimeTypeHTML[] = "text/html";
 const char kMimeTypeRTF[] = "text/rtf";
 const char kMimeTypePNG[] = "image/png";
-
 #if !defined(OS_APPLE)
+// TODO(dcheng): This name is temporary. See crbug.com/106449.
 const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data";
 const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
 const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data";
diff --git a/ui/base/clipboard/clipboard_constants.h b/ui/base/clipboard/clipboard_constants.h
index bdc32ecba..d749751 100644
--- a/ui/base/clipboard/clipboard_constants.h
+++ b/ui/base/clipboard/clipboard_constants.h
@@ -24,9 +24,6 @@
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeText[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeTextUtf8[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeURIList[];
-// Unstandardized format for downloading files  after drop events. Now only
-// works in Windows, but used to also work in Linux and MacOS.
-// See https://crbug.com/860557 and https://crbug.com/425170.
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
 extern const char kMimeTypeDownloadURL[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
@@ -34,19 +31,7 @@
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeHTML[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeRTF[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypePNG[];
-
-// Linux-specific MIME type constants (also used in Fuchsia).
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
-COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
-constexpr char kMimeTypeLinuxUtf8String[] = "UTF8_STRING";
-COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
-constexpr char kMimeTypeLinuxString[] = "STRING";
-COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
-constexpr char kMimeTypeLinuxText[] = "TEXT";
-#endif  // defined(OS_LINUX) || defined(OS_FUCHSIA)
-
 #if !defined(OS_APPLE)
-// TODO(dcheng): This name is temporary. See crbug.com/106449.
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
 extern const char kMimeTypeWebCustomData[];
 COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
diff --git a/ui/base/clipboard/clipboard_ozone.cc b/ui/base/clipboard/clipboard_ozone.cc
index 5f46a62..4db9efa6 100644
--- a/ui/base/clipboard/clipboard_ozone.cc
+++ b/ui/base/clipboard/clipboard_ozone.cc
@@ -38,6 +38,12 @@
 
 namespace {
 
+// TODO(crbug.com/1105892): those three constants can be found in a few other
+// places.  Perhaps it would be worth finding some common place for them?
+constexpr char kMimeTypeX11String[] = "STRING";
+constexpr char kMimeTypeX11Text[] = "TEXT";
+constexpr char kMimeTypeX11Utf8String[] = "UTF8_STRING";
+
 // The amount of time to wait for a request to complete before aborting it.
 constexpr base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10);
 
@@ -558,8 +564,8 @@
 void ClipboardOzone::WriteText(const char* text_data, size_t text_len) {
   std::vector<uint8_t> data(text_data, text_data + text_len);
   async_clipboard_ozone_->InsertData(
-      std::move(data), {kMimeTypeText, kMimeTypeLinuxText, kMimeTypeLinuxString,
-                        kMimeTypeTextUtf8, kMimeTypeLinuxUtf8String});
+      std::move(data), {kMimeTypeText, kMimeTypeX11Text, kMimeTypeX11String,
+                        kMimeTypeTextUtf8, kMimeTypeX11Utf8String});
 }
 
 void ClipboardOzone::WriteHTML(const char* markup_data,
diff --git a/ui/base/clipboard/clipboard_test_template.h b/ui/base/clipboard/clipboard_test_template.h
index f0cf758..61b7dfa 100644
--- a/ui/base/clipboard/clipboard_test_template.h
+++ b/ui/base/clipboard/clipboard_test_template.h
@@ -27,7 +27,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "build/chromecast_buildflags.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -168,11 +167,6 @@
 
   EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
               Contains(ASCIIToUTF16(kMimeTypeText)));
-#if defined(USE_OZONE) && !defined(OS_CHROMEOS) && !defined(OS_FUCHSIA) && \
-    !BUILDFLAG(IS_CHROMECAST)
-  EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
-              Contains(ASCIIToUTF16(kMimeTypeTextUtf8)));
-#endif
   EXPECT_TRUE(this->clipboard().IsFormatAvailable(
       ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste,
       /* data_dst = */ nullptr));
@@ -248,13 +242,10 @@
 }
 #endif  // !defined(OS_ANDROID)
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+// TODO(msisov, tonikitoo): Enable test once ClipboardOzone implements
+// selection support. https://crbug.com/911992
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(USE_OZONE)
 TYPED_TEST(ClipboardTest, MultipleBufferTest) {
-#if defined(USE_OZONE)
-  if (!this->clipboard().IsSelectionBufferAvailable())
-    return;
-#endif
-
   base::string16 text(ASCIIToUTF16("Standard")), text_result;
   base::string16 markup(ASCIIToUTF16("<string>Selection</string>"));
   std::string url("http://www.example.com/"), url_result;
diff --git a/ui/base/clipboard/clipboard_x11.cc b/ui/base/clipboard/clipboard_x11.cc
index fff2b73e..f5119b6 100644
--- a/ui/base/clipboard/clipboard_x11.cc
+++ b/ui/base/clipboard/clipboard_x11.cc
@@ -758,9 +758,9 @@
       base::RefCountedString::TakeString(&str));
 
   x11_details_->InsertMapping(kMimeTypeText, mem);
-  x11_details_->InsertMapping(kMimeTypeLinuxText, mem);
-  x11_details_->InsertMapping(kMimeTypeLinuxString, mem);
-  x11_details_->InsertMapping(kMimeTypeLinuxUtf8String, mem);
+  x11_details_->InsertMapping(kText, mem);
+  x11_details_->InsertMapping(kString, mem);
+  x11_details_->InsertMapping(kUtf8String, mem);
 }
 
 void ClipboardX11::WriteHTML(const char* markup_data,
diff --git a/ui/base/x/selection_utils.cc b/ui/base/x/selection_utils.cc
index 72e02bc..34533b9 100644
--- a/ui/base/x/selection_utils.cc
+++ b/ui/base/x/selection_utils.cc
@@ -19,13 +19,19 @@
 
 namespace ui {
 
+const char kString[] = "STRING";
+const char kText[] = "TEXT";
+const char kTextPlain[] = "text/plain";
+const char kTextPlainUtf8[] = "text/plain;charset=utf-8";
+const char kUtf8String[] = "UTF8_STRING";
+
 std::vector<x11::Atom> GetTextAtomsFrom() {
   std::vector<x11::Atom> atoms;
-  atoms.push_back(gfx::GetAtom(kMimeTypeLinuxUtf8String));
-  atoms.push_back(gfx::GetAtom(kMimeTypeLinuxString));
-  atoms.push_back(gfx::GetAtom(kMimeTypeLinuxText));
-  atoms.push_back(gfx::GetAtom(kMimeTypeText));
-  atoms.push_back(gfx::GetAtom(kMimeTypeTextUtf8));
+  atoms.push_back(gfx::GetAtom(kUtf8String));
+  atoms.push_back(gfx::GetAtom(kString));
+  atoms.push_back(gfx::GetAtom(kText));
+  atoms.push_back(gfx::GetAtom(kTextPlain));
+  atoms.push_back(gfx::GetAtom(kTextPlainUtf8));
   return atoms;
 }
 
@@ -170,12 +176,11 @@
 }
 
 std::string SelectionData::GetText() const {
-  if (type_ == gfx::GetAtom(kMimeTypeLinuxUtf8String) ||
-      type_ == gfx::GetAtom(kMimeTypeLinuxText) ||
-      type_ == gfx::GetAtom(kMimeTypeTextUtf8)) {
+  if (type_ == gfx::GetAtom(kUtf8String) || type_ == gfx::GetAtom(kText) ||
+      type_ == gfx::GetAtom(kTextPlainUtf8)) {
     return RefCountedMemoryToString(memory_);
-  } else if (type_ == gfx::GetAtom(kMimeTypeLinuxString) ||
-             type_ == gfx::GetAtom(kMimeTypeText)) {
+  } else if (type_ == gfx::GetAtom(kString) ||
+             type_ == gfx::GetAtom(kTextPlain)) {
     std::string result;
     base::ConvertToUtf8AndNormalize(RefCountedMemoryToString(memory_),
                                     base::kCodepageLatin1, &result);
diff --git a/ui/base/x/selection_utils.h b/ui/base/x/selection_utils.h
index 3e90ed3..9ca25b3 100644
--- a/ui/base/x/selection_utils.h
+++ b/ui/base/x/selection_utils.h
@@ -15,6 +15,10 @@
 namespace ui {
 class SelectionData;
 
+COMPONENT_EXPORT(UI_BASE_X) extern const char kString[];
+COMPONENT_EXPORT(UI_BASE_X) extern const char kText[];
+COMPONENT_EXPORT(UI_BASE_X) extern const char kUtf8String[];
+
 // Returns a list of all text atoms that we handle.
 COMPONENT_EXPORT(UI_BASE_X) std::vector<x11::Atom> GetTextAtomsFrom();
 
diff --git a/ui/base/x/x11_os_exchange_data_provider.cc b/ui/base/x/x11_os_exchange_data_provider.cc
index ef6463f..e7a374f 100644
--- a/ui/base/x/x11_os_exchange_data_provider.cc
+++ b/ui/base/x/x11_os_exchange_data_provider.cc
@@ -97,9 +97,9 @@
       base::RefCountedString::TakeString(&utf8));
 
   format_map_.Insert(gfx::GetAtom(kMimeTypeText), mem);
-  format_map_.Insert(gfx::GetAtom(kMimeTypeLinuxText), mem);
-  format_map_.Insert(gfx::GetAtom(kMimeTypeLinuxString), mem);
-  format_map_.Insert(gfx::GetAtom(kMimeTypeLinuxUtf8String), mem);
+  format_map_.Insert(gfx::GetAtom(kText), mem);
+  format_map_.Insert(gfx::GetAtom(kString), mem);
+  format_map_.Insert(gfx::GetAtom(kUtf8String), mem);
 }
 
 void XOSExchangeDataProvider::SetURL(const GURL& url,
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
index 339cf87..c60c35e 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
@@ -807,9 +807,18 @@
   TRACE_EVENT0("wayland", "WaylandBufferManagerHost::CommitOverlays");
 
   DCHECK(error_message_.empty());
+
+  if (widget == gfx::kNullAcceleratedWidget) {
+    error_message_ = "Invalid widget.";
+    TerminateGpuProcess();
+  }
   WaylandWindow* window =
       connection_->wayland_window_manager()->GetWindow(widget);
-  DCHECK(window);
+  // In tab dragging, window may have been destroyed when buffers reach here. We
+  // omit buffer commits and OnSubmission, because the corresponding buffer
+  // queue in gpu process should be destroyed soon.
+  if (!window)
+    return;
 
   window->CommitOverlays(overlays);
 }
diff --git a/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc b/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc
index cbdd82a..8656f41 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc
@@ -9,6 +9,14 @@
 
 namespace ui {
 
+namespace {
+
+const char kString[] = "STRING";
+const char kText[] = "TEXT";
+const char kUtf8String[] = "UTF8_STRING";
+
+}  // namespace
+
 WaylandDataOfferBase::WaylandDataOfferBase() = default;
 WaylandDataOfferBase::~WaylandDataOfferBase() = default;
 
@@ -18,10 +26,9 @@
 
   if (std::any_of(mime_types_.begin(), mime_types_.end(),
                   [](const std::string& mime_type) {
-                    return mime_type == kMimeTypeLinuxString ||
-                           mime_type == kMimeTypeLinuxText ||
+                    return mime_type == kString || mime_type == kText ||
                            mime_type == kMimeTypeTextUtf8 ||
-                           mime_type == kMimeTypeLinuxUtf8String;
+                           mime_type == kUtf8String;
                   })) {
     mime_types_.push_back(kMimeTypeText);
     text_plain_mime_type_inserted_ = true;
diff --git a/ui/ozone/platform/x11/x11_clipboard_ozone.cc b/ui/ozone/platform/x11/x11_clipboard_ozone.cc
index 16a0476..d4fed35 100644
--- a/ui/ozone/platform/x11/x11_clipboard_ozone.cc
+++ b/ui/ozone/platform/x11/x11_clipboard_ozone.cc
@@ -25,21 +25,23 @@
 
 const char kChromeSelection[] = "CHROME_SELECTION";
 const char kClipboard[] = "CLIPBOARD";
+const char kString[] = "STRING";
 const char kTargets[] = "TARGETS";
 const char kTimestamp[] = "TIMESTAMP";
+const char kUtf8String[] = "UTF8_STRING";
 
 // Helps to allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING.
 void ExpandTypes(std::vector<std::string>* list) {
   bool has_mime_type_text = Contains(*list, ui::kMimeTypeText);
-  bool has_string = Contains(*list, kMimeTypeLinuxString);
+  bool has_string = Contains(*list, kString);
   bool has_mime_type_utf8 = Contains(*list, kMimeTypeTextUtf8);
-  bool has_utf8_string = Contains(*list, kMimeTypeLinuxUtf8String);
+  bool has_utf8_string = Contains(*list, kUtf8String);
   if (has_mime_type_text && !has_string)
-    list->push_back(kMimeTypeLinuxString);
+    list->push_back(kString);
   if (has_string && !has_mime_type_text)
     list->push_back(ui::kMimeTypeText);
   if (has_mime_type_utf8 && !has_utf8_string)
-    list->push_back(kMimeTypeLinuxUtf8String);
+    list->push_back(kUtf8String);
   if (has_utf8_string && !has_mime_type_utf8)
     list->push_back(kMimeTypeTextUtf8);
 }
@@ -161,11 +163,9 @@
 
     std::string key = target_name;
     // Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING.
-    if (key == kMimeTypeLinuxUtf8String &&
-        !Contains(offer_data_map, kMimeTypeLinuxUtf8String)) {
+    if (key == kUtf8String && !Contains(offer_data_map, kUtf8String)) {
       key = kMimeTypeTextUtf8;
-    } else if (key == kMimeTypeLinuxString &&
-               !Contains(offer_data_map, kMimeTypeLinuxString)) {
+    } else if (key == kString && !Contains(offer_data_map, kString)) {
       key = kMimeTypeText;
     }
     auto it = offer_data_map.find(key);
@@ -314,9 +314,9 @@
   std::string target = selection_state.data_mime_type;
   if (!Contains(selection_state.mime_types, target)) {
     if (target == kMimeTypeText) {
-      target = kMimeTypeLinuxString;
+      target = kString;
     } else if (target == kMimeTypeTextUtf8) {
-      target = kMimeTypeLinuxUtf8String;
+      target = kUtf8String;
     }
   }