diff --git a/DEPS b/DEPS
index 9ec7af0..9a3fe95 100644
--- a/DEPS
+++ b/DEPS
@@ -199,7 +199,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'bb4817544d7ce327fc432a8229efa213952ca979',
+  'v8_revision': 'ff50ca34d46076a54b805f4f916cdd3e5805ccba',
   # 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.
@@ -258,7 +258,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '534924ed9ee5671b616f716581d21e765555f0a5',
+  'catapult_revision': '93d0ecf3d428c85a75c41fea97ea37c5891f0d49',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # 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': 'bc83da97d26c4f5f78ed5f4514b845e669720d3c',
+  'devtools_frontend_revision': '8b1a5e7995a5f5152b78c8ded3b4973f32f92be0',
   # 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.
@@ -1249,7 +1249,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b0a548c7e6b64fe0a6a140d133815123f5b3b112',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b189aac831322c28c0d3d3f41cab7c0214dd8c9b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1471,7 +1471,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '34f5ab82feaebb6ad66bc6f9fd3d4f91fa517636',
+    Var('webrtc_git') + '/src.git' + '@' + 'c6dbc5ee80273699789bf384faeffeffe292a59f',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1543,7 +1543,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@68241d4f5af5f2e08963fe6f02f8255a1b3677b2',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9d6bf6eee8b548c0fbdb856b2ca44c455b605c37',
     'condition': 'checkout_src_internal',
   },
 
@@ -1551,7 +1551,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'Efy7gVoSk1mOpec4spFNgvpGxdfwB3frPpVKkwksne8C',
+        'version': '7-Fyv71mIOgLZ5UT2rzvAWdbDHOekNFf1CVgl0mHSkkC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1562,7 +1562,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'tbdCdSmxCSExSm3_J4ZD3I0NKirjX4F7MA1Lr38MjDcC',
+        'version': 'tmWwsSISDU41lQ4kf4xfmJyf0_fQYQlzfq_1EKE6E5MC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index a816c15..8593cfb 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -330,7 +330,7 @@
       <message name="IDS_ASH_STATUS_TRAY_CAST_CAST_DESKTOP" desc="The label used in the tray popup to tell the user we are casting the desktop.">
         Casting screen to <ph name="RECEIVER_NAME">$1<ex>Living Room</ex></ph>
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_DARK_THEME" desc="The label used as the header in the dark theme popup. [CHAR_LIMIT=17">
+      <message name="IDS_ASH_STATUS_TRAY_DARK_THEME" desc="The label used as the header in the dark theme popup. [CHAR_LIMIT=19">
         Dark theme
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DARK_THEME_TOGGLE_TOOLTIP" desc="The tooltip text used for the button in the status tray to toggle the Dark theme feature on or off.">
diff --git a/ash/clipboard/clipboard_history_menu_model_adapter.cc b/ash/clipboard/clipboard_history_menu_model_adapter.cc
index d005ebaa..a2afdbb 100644
--- a/ash/clipboard/clipboard_history_menu_model_adapter.cc
+++ b/ash/clipboard/clipboard_history_menu_model_adapter.cc
@@ -130,7 +130,7 @@
 
   std::unique_ptr<ClipboardHistoryItemView> item_view =
       ClipboardHistoryItemView::CreateFromClipboardHistoryItem(
-          GetItemFromCommandId(command_id), *resource_manager_, container);
+          GetItemFromCommandId(command_id), resource_manager_, container);
   item_view->Init();
   container->AddChildView(std::move(item_view));
 
diff --git a/ash/clipboard/clipboard_history_resource_manager.cc b/ash/clipboard/clipboard_history_resource_manager.cc
index 0ae81ff..e5cf325 100644
--- a/ash/clipboard/clipboard_history_resource_manager.cc
+++ b/ash/clipboard/clipboard_history_resource_manager.cc
@@ -160,6 +160,14 @@
   }
 }
 
+void ClipboardHistoryResourceManager::AddObserver(Observer* observer) const {
+  observers_.AddObserver(observer);
+}
+
+void ClipboardHistoryResourceManager::RemoveObserver(Observer* observer) const {
+  observers_.RemoveObserver(observer);
+}
+
 ClipboardHistoryResourceManager::CachedImageModel::CachedImageModel() = default;
 
 ClipboardHistoryResourceManager::CachedImageModel::CachedImageModel(
@@ -177,8 +185,15 @@
     ui::ImageModel image_model) {
   auto cached_image_model = base::ConstCastIterator(
       cached_image_models_, FindCachedImageModelForId(id));
-  if (cached_image_model != cached_image_models_.end())
-    cached_image_model->image_model = std::move(image_model);
+  if (cached_image_model == cached_image_models_.end())
+    return;
+
+  cached_image_model->image_model = std::move(image_model);
+
+  for (auto& observer : observers_) {
+    observer.OnCachedImageModelUpdated(
+        cached_image_model->clipboard_history_item_ids);
+  }
 }
 
 std::vector<ClipboardHistoryResourceManager::CachedImageModel>::const_iterator
diff --git a/ash/clipboard/clipboard_history_resource_manager.h b/ash/clipboard/clipboard_history_resource_manager.h
index 7c70b0a5..53c5c5d 100644
--- a/ash/clipboard/clipboard_history_resource_manager.h
+++ b/ash/clipboard/clipboard_history_resource_manager.h
@@ -10,6 +10,8 @@
 #include "ash/ash_export.h"
 #include "ash/clipboard/clipboard_history.h"
 #include "ash/clipboard/clipboard_history_item.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "base/strings/string16.h"
 #include "base/unguessable_token.h"
 #include "ui/base/models/image_model.h"
@@ -19,6 +21,14 @@
 class ASH_EXPORT ClipboardHistoryResourceManager
     : public ClipboardHistory::Observer {
  public:
+  class Observer : public base::CheckedObserver {
+   public:
+    // Called when the CachedImageModel that corresponds with 'menu_item_ids'
+    // has been updated.
+    virtual void OnCachedImageModelUpdated(
+        const std::vector<base::UnguessableToken>& menu_item_ids) = 0;
+  };
+
   explicit ClipboardHistoryResourceManager(
       const ClipboardHistory* clipboard_history);
   ClipboardHistoryResourceManager(const ClipboardHistoryResourceManager&) =
@@ -33,10 +43,8 @@
   // Returns the label to display for the specified clipboard history |item|.
   base::string16 GetLabel(const ClipboardHistoryItem& item) const;
 
-  // Returns the main format of the specified clipboard history |item|. Note
-  // that one `ClipboardHistoryItem` instance may own multiple formats.
-  ui::ClipboardInternalFormat CalculateMainFormat(
-      const ClipboardHistoryItem& item) const;
+  void AddObserver(Observer* observer) const;
+  void RemoveObserver(Observer* observer) const;
 
  private:
   struct CachedImageModel {
@@ -80,6 +88,10 @@
   // Image used when the cached ImageModel has not yet been generated.
   ui::ImageModel placeholder_image_model_;
 
+  // Mutable to allow adding/removing from |observers_| through a const
+  // ClipboardHistoryResourceManager.
+  mutable base::ObserverList<Observer> observers_;
+
   base::WeakPtrFactory<ClipboardHistoryResourceManager> weak_factory_{this};
 };
 
diff --git a/ash/clipboard/views/clipboard_history_bitmap_item_view.cc b/ash/clipboard/views/clipboard_history_bitmap_item_view.cc
index 17546ab..15db5ac 100644
--- a/ash/clipboard/views/clipboard_history_bitmap_item_view.cc
+++ b/ash/clipboard/views/clipboard_history_bitmap_item_view.cc
@@ -5,13 +5,21 @@
 #include "ash/clipboard/views/clipboard_history_bitmap_item_view.h"
 
 #include "ash/clipboard/clipboard_history_item.h"
+#include "ash/clipboard/clipboard_history_resource_manager.h"
+#include "ash/clipboard/clipboard_history_util.h"
+#include "base/time/time.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/view_class_properties.h"
 
+namespace ash {
+
 namespace {
 
 // The preferred height for the bitmap.
@@ -20,9 +28,108 @@
 // The margins of the delete button.
 constexpr gfx::Insets kDeleteButtonMargins =
     gfx::Insets(/*top=*/4, /*left=*/0, /*bottom=*/0, /*right=*/4);
-}  // namespace
 
-namespace ash {
+// The duration of the fade out animation for transitioning the placeholder
+// image to rendered HTML.
+constexpr base::TimeDelta kFadeOutDurationMs =
+    base::TimeDelta::FromMilliseconds(60);
+
+// The duration of the fade in animation for transitioning the placeholder image
+// to rendered HTML.
+constexpr base::TimeDelta kFadeInDurationMs =
+    base::TimeDelta::FromMilliseconds(200);
+
+////////////////////////////////////////////////////////////////////////////////
+// FadeImageView
+// An ImageView which reacts to updates from ClipboardHistoryResourceManager by
+// fading out the old image, and fading in the new image. Used when HTML is done
+// rendering. Only expected to transition once in its lifetime.
+class FadeImageView : public views::ImageView,
+                      public ui::ImplicitAnimationObserver,
+                      public ClipboardHistoryResourceManager::Observer {
+ public:
+  FadeImageView(const ClipboardHistoryItem& clipboard_history_item,
+                const ClipboardHistoryResourceManager* resource_manager)
+      : views::ImageView(),
+        resource_manager_(resource_manager),
+        clipboard_history_item_(clipboard_history_item) {
+    resource_manager_->AddObserver(this);
+    SetImage(*(resource_manager_->GetImageModel(clipboard_history_item_)
+                   .GetImage()
+                   .ToImageSkia()));
+  }
+
+  FadeImageView(const FadeImageView& rhs) = delete;
+
+  FadeImageView& operator=(const FadeImageView& rhs) = delete;
+
+  ~FadeImageView() override {
+    StopObservingImplicitAnimations();
+    resource_manager_->RemoveObserver(this);
+  }
+
+  // ClipboardHistoryResourceManager::Observer:
+  void OnCachedImageModelUpdated(
+      const std::vector<base::UnguessableToken>& item_ids) override {
+    if (!base::Contains(item_ids, clipboard_history_item_.id()))
+      return;
+
+    // Fade the old image out, then swap in the new image.
+    DCHECK_EQ(FadeAnimationState::kNoFadeAnimation, animation_state_);
+    SetPaintToLayer();
+    animation_state_ = FadeAnimationState::kFadeOut;
+
+    ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
+    settings.SetTransitionDuration(kFadeOutDurationMs);
+    settings.AddObserver(this);
+    layer()->SetOpacity(0.0f);
+  }
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override {
+    switch (animation_state_) {
+      case FadeAnimationState::kNoFadeAnimation:
+        NOTREACHED();
+        return;
+      case FadeAnimationState::kFadeOut:
+        DCHECK_EQ(0.0f, layer()->opacity());
+        animation_state_ = FadeAnimationState::kFadeIn;
+        SetImage(*(resource_manager_->GetImageModel(clipboard_history_item_)
+                       .GetImage()
+                       .ToImageSkia()));
+        {
+          ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
+          settings.AddObserver(this);
+          settings.SetTransitionDuration(kFadeInDurationMs);
+          layer()->SetOpacity(1.0f);
+        }
+        return;
+      case FadeAnimationState::kFadeIn:
+        DestroyLayer();
+        animation_state_ = FadeAnimationState::kNoFadeAnimation;
+        return;
+    }
+  }
+
+  // The different animation states possible when transitioning from one
+  // gfx::ImageSkia to the next.
+  enum class FadeAnimationState {
+    kNoFadeAnimation,
+    kFadeOut,
+    kFadeIn,
+  };
+
+  // The current animation state.
+  FadeAnimationState animation_state_ = FadeAnimationState::kNoFadeAnimation;
+
+  // The resource manager, owned by ClipboardHistoryController.
+  const ClipboardHistoryResourceManager* const resource_manager_;
+
+  // The ClipboardHistoryItem represented by this class.
+  const ClipboardHistoryItem clipboard_history_item_;
+};
+
+}  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 // ClipboardHistoryBitmapItemView::BitmapContentsView
@@ -62,9 +169,12 @@
 // ClipboardHistoryBitmapItemView
 
 ClipboardHistoryBitmapItemView::ClipboardHistoryBitmapItemView(
-    const gfx::ImageSkia& image_skia,
+    const ClipboardHistoryItem& clipboard_history_item,
+    const ClipboardHistoryResourceManager* resource_manager,
     views::MenuItemView* container)
-    : ClipboardHistoryItemView(container), original_image_(image_skia) {}
+    : ClipboardHistoryItemView(container),
+      resource_manager_(resource_manager),
+      clipboard_history_item_(clipboard_history_item) {}
 
 ClipboardHistoryBitmapItemView::~ClipboardHistoryBitmapItemView() = default;
 
@@ -77,11 +187,9 @@
   auto contents_view = std::make_unique<BitmapContentsView>(this);
   contents_view->SetLayoutManager(std::make_unique<views::FillLayout>());
 
-  auto image_view = std::make_unique<views::ImageView>();
-  image_view->SetImage(original_image_);
+  auto image_view = BuildImageView();
   image_view->SetPreferredSize(gfx::Size(INT_MAX, kBitmapHeight));
   image_view_ = contents_view->AddChildView(std::move(image_view));
-
   contents_view->InstallDeleteButton();
   return contents_view;
 }
@@ -91,6 +199,26 @@
   image_view_->SetImageSize(CalculateTargetImageSize());
 }
 
+std::unique_ptr<views::ImageView>
+ClipboardHistoryBitmapItemView::BuildImageView() {
+  switch (
+      ClipboardHistoryUtil::CalculateMainFormat(clipboard_history_item_.data())
+          .value()) {
+    case ui::ClipboardInternalFormat::kHtml:
+      return std::make_unique<FadeImageView>(clipboard_history_item_,
+                                             resource_manager_);
+    case ui::ClipboardInternalFormat::kBitmap: {
+      auto image_view = std::make_unique<views::ImageView>();
+      image_view->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(
+          clipboard_history_item_.data().bitmap()));
+      return image_view;
+    }
+    default:
+      NOTREACHED();
+      return std::make_unique<views::ImageView>();
+  }
+}
+
 gfx::Size ClipboardHistoryBitmapItemView::CalculateTargetImageSize() const {
   const gfx::Size image_size = image_view_->GetImage().size();
   const double width_ratio = image_size.width() / double(width());
diff --git a/ash/clipboard/views/clipboard_history_bitmap_item_view.h b/ash/clipboard/views/clipboard_history_bitmap_item_view.h
index bb65025..8c73bf7c 100644
--- a/ash/clipboard/views/clipboard_history_bitmap_item_view.h
+++ b/ash/clipboard/views/clipboard_history_bitmap_item_view.h
@@ -5,23 +5,24 @@
 #ifndef ASH_CLIPBOARD_VIEWS_CLIPBOARD_HISTORY_BITMAP_ITEM_VIEW_H_
 #define ASH_CLIPBOARD_VIEWS_CLIPBOARD_HISTORY_BITMAP_ITEM_VIEW_H_
 
+#include "ash/clipboard/clipboard_history_item.h"
 #include "ash/clipboard/views/clipboard_history_item_view.h"
-
-namespace gfx {
-class ImageSkia;
-}  // namespace gfx
+#include "ui/base/clipboard/clipboard_data.h"
 
 namespace views {
 class ImageView;
 }  // namespace views
 
 namespace ash {
+class ClipboardHistoryResourceManager;
 
-// The menu item showing the bitmap.
+// The menu item showing a bitmap.
 class ClipboardHistoryBitmapItemView : public ClipboardHistoryItemView {
  public:
-  ClipboardHistoryBitmapItemView(const gfx::ImageSkia& image_skia,
-                                 views::MenuItemView* container);
+  ClipboardHistoryBitmapItemView(
+      const ClipboardHistoryItem& clipboard_history_item,
+      const ClipboardHistoryResourceManager* resource_manager,
+      views::MenuItemView* container);
   ClipboardHistoryBitmapItemView(const ClipboardHistoryBitmapItemView& rhs) =
       delete;
   ClipboardHistoryBitmapItemView& operator=(
@@ -36,14 +37,20 @@
   std::unique_ptr<ContentsView> CreateContentsView() override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
 
+  // Builds `image_view_`.
+  std::unique_ptr<views::ImageView> BuildImageView();
+
   // Calculates the target size of the image to show.
   gfx::Size CalculateTargetImageSize() const;
 
-  // The image from the bitmap which is stored in the clipboard data.
-  const gfx::ImageSkia original_image_;
-
   // Owned by view hierarchy.
   views::ImageView* image_view_ = nullptr;
+
+  // Owned by ClipboardHistoryController.
+  const ClipboardHistoryResourceManager* const resource_manager_;
+
+  // The ClipboardHistoryItem represented by this view.
+  const ClipboardHistoryItem clipboard_history_item_;
 };
 
 }  // namespace ash
diff --git a/ash/clipboard/views/clipboard_history_item_view.cc b/ash/clipboard/views/clipboard_history_item_view.cc
index 2a2ebc64..281b19d 100644
--- a/ash/clipboard/views/clipboard_history_item_view.cc
+++ b/ash/clipboard/views/clipboard_history_item_view.cc
@@ -138,16 +138,13 @@
 std::unique_ptr<ClipboardHistoryItemView>
 ClipboardHistoryItemView::CreateFromClipboardHistoryItem(
     const ClipboardHistoryItem& item,
-    const ClipboardHistoryResourceManager& resource_manager,
+    const ClipboardHistoryResourceManager* resource_manager,
     views::MenuItemView* container) {
   switch (ClipboardHistoryUtil::CalculateMainFormat(item.data()).value()) {
     case ui::ClipboardInternalFormat::kBitmap:
-      return std::make_unique<ClipboardHistoryBitmapItemView>(
-          gfx::ImageSkia::CreateFrom1xBitmap(item.data().bitmap()), container);
     case ui::ClipboardInternalFormat::kHtml:
       return std::make_unique<ClipboardHistoryBitmapItemView>(
-          *(resource_manager.GetImageModel(item).GetImage().ToImageSkia()),
-          container);
+          item, resource_manager, container);
     case ui::ClipboardInternalFormat::kText:
     case ui::ClipboardInternalFormat::kSvg:
     case ui::ClipboardInternalFormat::kRtf:
diff --git a/ash/clipboard/views/clipboard_history_item_view.h b/ash/clipboard/views/clipboard_history_item_view.h
index 5439837..c7434ea 100644
--- a/ash/clipboard/views/clipboard_history_item_view.h
+++ b/ash/clipboard/views/clipboard_history_item_view.h
@@ -23,7 +23,7 @@
   static std::unique_ptr<ClipboardHistoryItemView>
   CreateFromClipboardHistoryItem(
       const ClipboardHistoryItem& item,
-      const ClipboardHistoryResourceManager& resource_manager,
+      const ClipboardHistoryResourceManager* resource_manager,
       views::MenuItemView* container);
 
   ClipboardHistoryItemView(const ClipboardHistoryItemView& rhs) = delete;
diff --git a/ash/power/hid_battery_listener_unittest.cc b/ash/power/hid_battery_listener_unittest.cc
index e5bfe77..05c5f75 100644
--- a/ash/power/hid_battery_listener_unittest.cc
+++ b/ash/power/hid_battery_listener_unittest.cc
@@ -122,13 +122,13 @@
 
   // Simulate DeviceAdded observer event.
   chromeos::FakePowerManagerClient::Get()->set_peripheral_battery_refresh_level(
-      60);
+      kAddress, 60);
   adapter_observer_->DeviceAdded(mock_adapter_.get(), mock_device_.get());
   EXPECT_EQ(60, mock_device_->battery_percentage());
 
   // Simulate DeviceAdded observer event with a different battery value.
   chromeos::FakePowerManagerClient::Get()->set_peripheral_battery_refresh_level(
-      50);
+      kAddress, 50);
   adapter_observer_->DeviceAdded(mock_adapter_.get(), mock_device_.get());
   EXPECT_EQ(50, mock_device_->battery_percentage());
 }
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index d4f644b..2b1ba6f 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "ash/focus_cycler.h"
+#include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "ash/lock_screen_action/lock_screen_action_background_state.h"
 #include "ash/login/login_screen_controller.h"
 #include "ash/login/ui/lock_screen.h"
@@ -248,6 +249,12 @@
     SchedulePaint();
   }
 
+  void OnFocus() override {
+    auto* const keyboard_controller = keyboard::KeyboardUIController::Get();
+    keyboard_controller->set_keyboard_locked(false /*lock*/);
+    keyboard_controller->HideKeyboardImplicitlyByUser();
+  }
+
  private:
   const int text_resource_id_;
   const gfx::VectorIcon& icon_;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index dbd7ab6..25bdb95 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3572,10 +3572,8 @@
       "android/java/src/org/chromium/base/UserDataHost.java",
       "android/java/src/org/chromium/base/annotations/AccessedByNative.java",
       "android/java/src/org/chromium/base/annotations/CalledByNative.java",
-      "android/java/src/org/chromium/base/annotations/CalledByNativeJavaTest.java",
       "android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java",
       "android/java/src/org/chromium/base/annotations/CheckDiscard.java",
-      "android/java/src/org/chromium/base/annotations/DisabledCalledByNativeJavaTest.java",
       "android/java/src/org/chromium/base/annotations/DoNotInline.java",
       "android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java",
       "android/java/src/org/chromium/base/annotations/JNINamespace.java",
@@ -3583,7 +3581,6 @@
       "android/java/src/org/chromium/base/annotations/MainDex.java",
       "android/java/src/org/chromium/base/annotations/MockedInTests.java",
       "android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java",
-      "android/java/src/org/chromium/base/annotations/NativeJavaTestFeatures.java",
       "android/java/src/org/chromium/base/annotations/RemovableInRelease.java",
       "android/java/src/org/chromium/base/annotations/UsedByReflection.java",
       "android/java/src/org/chromium/base/annotations/VerifiesOnLollipop.java",
diff --git a/base/android/java/src/org/chromium/base/annotations/CalledByNativeJavaTest.java b/base/android/java/src/org/chromium/base/annotations/CalledByNativeJavaTest.java
deleted file mode 100644
index d17c1f2..0000000
--- a/base/android/java/src/org/chromium/base/annotations/CalledByNativeJavaTest.java
+++ /dev/null
@@ -1,24 +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.
-
-package org.chromium.base.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * @CalledByNativeJavaTest is used by the JNI generator to create the necessary JNI
- * bindings, expose this method to native code, and generate a native test proxy
- * method for this Java Test method.
- */
-@Target({ElementType.METHOD})
-@Retention(RetentionPolicy.CLASS)
-public @interface CalledByNativeJavaTest {
-    /*
-     *  If present, tells which inner class the method belongs to.
-     */
-    public String value() default "";
-}
diff --git a/base/android/java/src/org/chromium/base/annotations/DisabledCalledByNativeJavaTest.java b/base/android/java/src/org/chromium/base/annotations/DisabledCalledByNativeJavaTest.java
deleted file mode 100644
index 52fa44f..0000000
--- a/base/android/java/src/org/chromium/base/annotations/DisabledCalledByNativeJavaTest.java
+++ /dev/null
@@ -1,22 +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.
-
-package org.chromium.base.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * See @CalledByNativeJavaTest. This annotation generates a disabled test instead.
- */
-@Target({ElementType.METHOD})
-@Retention(RetentionPolicy.CLASS)
-public @interface DisabledCalledByNativeJavaTest {
-    /*
-     *  If present, tells which inner class the method belongs to.
-     */
-    public String value() default "";
-}
diff --git a/base/android/java/src/org/chromium/base/annotations/NativeJavaTestFeatures.java b/base/android/java/src/org/chromium/base/annotations/NativeJavaTestFeatures.java
deleted file mode 100644
index 59e3724..0000000
--- a/base/android/java/src/org/chromium/base/annotations/NativeJavaTestFeatures.java
+++ /dev/null
@@ -1,35 +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.
-
-package org.chromium.base.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * NativeJavaTestFeatures is used by the JNI generator in combination with @CalledByNativeJavaTest
- * to generate a native test method that enabled or disables the specified feature for the scope
- * of the test.
- */
-public class NativeJavaTestFeatures {
-    @Target({ElementType.METHOD})
-    @Retention(RetentionPolicy.CLASS)
-    public static @interface Enable {
-        /**
-         *  If present, specifies which features to enable.
-         */
-        public String[] value() default {};
-    }
-
-    @Target({ElementType.METHOD})
-    @Retention(RetentionPolicy.CLASS)
-    public static @interface Disable {
-        /**
-         *  If present, specifies which features to disable.
-         */
-        public String[] value() default {};
-    }
-}
diff --git a/base/android/jni_generator/README.md b/base/android/jni_generator/README.md
index 9bc74181..facd1de 100644
--- a/base/android/jni_generator/README.md
+++ b/base/android/jni_generator/README.md
@@ -209,57 +209,6 @@
  * `JavaParamRef<>` - Use to accept any of the above as a parameter to a
    function without creating a redundant registration.
 
-### Native Java Unittests and @CalledByNativeJavaTest
-
-Native Java Unittests are Java unit tests that run on Android (not on the host
-machine). Unlike junit and robolectric, these tests can use the native library,
-and real Android APIs. Unlike unit tests in ChromePublicTestApk and similar, the
-Activity is not restarted between tests, so the tests run much faster (and you
-must be careful not to leave state behind). Example tests may be found in
-chrome/android/native_java_unittests/.
-
-The @CalledByNativeJavaTest annotation causes the JNI generator to generate
-C++ test methods that simply call out to the Java test methods.
-
-For Example:
-```java
-class FooTest {
-  @CalledByNative public FooTest() {}
-  @CalledByNativeJavaTest public void testFoo() { ... }
-  @CalledByNativeJavaTest public void testOtherFoo() { ... }
-}
-```
-```c++
-class FooTest : public ::testing::Test {
-  FooTest() : j_test_(Java_FooTest_Constructor(AttachCurrentThread())) {}
-  const ScopedJavaGlobalRef<jobject>& j_test() { return j_test_; }
- private:
-  ScopedJavaGlobalRef<jobject> j_test_;
-}
-JAVA_TESTS(AndroidPaymentAppFinderUnitTest, j_test())
-```
-
-In some cases you may want to run custom C++ code before running the Java test,
-in which case, use CalledByNative instead of CalledByNativeJavaTest and call the
-test method yourself. eg. Java_FooTest_testFancyFoo(env, j_test());
-
-#### Disabling @CalledByNativeJavaTest tests
-In order to disabled a Native Java Unittest, replace the @CalledByNativeJavaTest
-annotation with @DisabledCalledByNativeJavaTest. This will generate a native
-test prefixed with DISABLED_.
-
-Please note that unlike @DisabledTest, you should not provide a message in the
-annotation as to why the test is disabled, as this will be interpreted as the
-inner class name by the CalledByNative jni generator.
-
-#### Feature flag support in @CalledByNativeJavaTest tests
-Features may be enabled/disabled for the Java test methods by using the
-@NativeJavaTestFeatures.Enabled or @NativeJavaTestFeatures.Disabled annotations.
-
-Note that these annotations will re-initialize the feature list given they
-initialize through the command line approach, and so will clear any features
-set by, say, test setup.
-
 ### Additional Guidelines / Advice
 
 Minimize the surface API between the two sides. Rather than calling multiple
diff --git a/base/android/jni_generator/TestSampleFeatureList.java b/base/android/jni_generator/TestSampleFeatureList.java
deleted file mode 100644
index 736fb34..0000000
--- a/base/android/jni_generator/TestSampleFeatureList.java
+++ /dev/null
@@ -1,12 +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.
-
-package foo;
-
-class TestFeatureList {
-    public static final String MY_FEATURE = "MyFeature";
-    public static final String MY_FEATURE_WITH_A_REALLY_REALLY_ABSURDLY_LONG_NAME =
-            "MyFeatureWithAReallyReallyAbsurdlyLongName";
-    public static final String BAD_FEATURE = "BadFeature";
-}
diff --git a/base/android/jni_generator/golden/HashedSampleForAnnotationProcessor_jni.golden b/base/android/jni_generator/golden/HashedSampleForAnnotationProcessor_jni.golden
index 8592458..ce5b642 100644
--- a/base/android/jni_generator/golden/HashedSampleForAnnotationProcessor_jni.golden
+++ b/base/android/jni_generator/golden/HashedSampleForAnnotationProcessor_jni.golden
@@ -262,7 +262,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_example_jni_generator_SampleForAnnotationProcessor_JNI
diff --git a/base/android/jni_generator/golden/SampleForAnnotationProcessor_jni.golden b/base/android/jni_generator/golden/SampleForAnnotationProcessor_jni.golden
index 38324a2..313766fe 100644
--- a/base/android/jni_generator/golden/SampleForAnnotationProcessor_jni.golden
+++ b/base/android/jni_generator/golden/SampleForAnnotationProcessor_jni.golden
@@ -280,7 +280,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_example_jni_generator_SampleForAnnotationProcessor_JNI
diff --git a/base/android/jni_generator/golden/SampleForTests_jni.golden b/base/android/jni_generator/golden/SampleForTests_jni.golden
index d023191..2efae836 100644
--- a/base/android/jni_generator/golden/SampleForTests_jni.golden
+++ b/base/android/jni_generator/golden/SampleForTests_jni.golden
@@ -524,7 +524,4 @@
 }  // namespace android
 }  // namespace base
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_generator/golden/testCalledByNativeJavaTest.golden b/base/android/jni_generator/golden/testCalledByNativeJavaTest.golden
deleted file mode 100644
index fad2849..0000000
--- a/base/android/jni_generator/golden/testCalledByNativeJavaTest.golden
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-// This file is autogenerated by
-//     base/android/jni_generator/jni_generator.py
-// For
-//     org/chromium/TestJni
-
-#ifndef org_chromium_TestJni_JNI
-#define org_chromium_TestJni_JNI
-
-#include <jni.h>
-
-#include "base/android/jni_generator/jni_generator_helper.h"
-
-
-// Step 1: Forward declarations.
-
-JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni[];
-const char kClassPath_org_chromium_TestJni[] = "org/chromium/TestJni";
-
-JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni_00024MyInnerClass[];
-const char kClassPath_org_chromium_TestJni_00024MyInnerClass[] =
-    "org/chromium/TestJni$MyInnerClass";
-// Leaking this jclass as we cannot use LazyInstance from some threads.
-JNI_REGISTRATION_EXPORT std::atomic<jclass> g_org_chromium_TestJni_clazz(nullptr);
-#ifndef org_chromium_TestJni_clazz_defined
-#define org_chromium_TestJni_clazz_defined
-inline jclass org_chromium_TestJni_clazz(JNIEnv* env) {
-  return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni,
-      &g_org_chromium_TestJni_clazz);
-}
-#endif
-// Leaking this jclass as we cannot use LazyInstance from some threads.
-JNI_REGISTRATION_EXPORT std::atomic<jclass> g_org_chromium_TestJni_00024MyInnerClass_clazz(nullptr);
-#ifndef org_chromium_TestJni_00024MyInnerClass_clazz_defined
-#define org_chromium_TestJni_00024MyInnerClass_clazz_defined
-inline jclass org_chromium_TestJni_00024MyInnerClass_clazz(JNIEnv* env) {
-  return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni_00024MyInnerClass,
-      &g_org_chromium_TestJni_00024MyInnerClass_clazz);
-}
-#endif
-
-
-// Step 2: Constants (optional).
-
-
-// Step 3: Method stubs.
-
-static std::atomic<jmethodID> g_org_chromium_TestJni_Constructor(nullptr);
-static base::android::ScopedJavaLocalRef<jobject> Java_TestJni_Constructor(JNIEnv* env) {
-  jclass clazz = org_chromium_TestJni_clazz(env);
-  CHECK_CLAZZ(env, clazz,
-      org_chromium_TestJni_clazz(env), NULL);
-
-  jni_generator::JniJavaCallContextChecked call_context;
-  call_context.Init<
-      base::android::MethodID::TYPE_INSTANCE>(
-          env,
-          clazz,
-          "<init>",
-          "()V",
-          &g_org_chromium_TestJni_Constructor);
-
-  jobject ret =
-      env->NewObject(clazz,
-          call_context.base.method_id);
-  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
-}
-
-static std::atomic<jmethodID> g_org_chromium_TestJni_testFoo(nullptr);
-static jint Java_TestJni_testFoo(JNIEnv* env, const base::android::JavaRef<jobject>& obj) {
-  jclass clazz = org_chromium_TestJni_clazz(env);
-  CHECK_CLAZZ(env, obj.obj(),
-      org_chromium_TestJni_clazz(env), 0);
-
-  jni_generator::JniJavaCallContextChecked call_context;
-  call_context.Init<
-      base::android::MethodID::TYPE_INSTANCE>(
-          env,
-          clazz,
-          "testFoo",
-          "()I",
-          &g_org_chromium_TestJni_testFoo);
-
-  jint ret =
-      env->CallIntMethod(obj.obj(),
-          call_context.base.method_id);
-  return ret;
-}
-
-static std::atomic<jmethodID> g_org_chromium_TestJni_testFeatures(nullptr);
-static void Java_TestJni_testFeatures(JNIEnv* env, const base::android::JavaRef<jobject>& obj) {
-  jclass clazz = org_chromium_TestJni_clazz(env);
-  CHECK_CLAZZ(env, obj.obj(),
-      org_chromium_TestJni_clazz(env));
-
-  jni_generator::JniJavaCallContextChecked call_context;
-  call_context.Init<
-      base::android::MethodID::TYPE_INSTANCE>(
-          env,
-          clazz,
-          "testFeatures",
-          "()V",
-          &g_org_chromium_TestJni_testFeatures);
-
-     env->CallVoidMethod(obj.obj(),
-          call_context.base.method_id);
-}
-
-static std::atomic<jmethodID> g_org_chromium_TestJni_testOtherFeatures(nullptr);
-static void Java_TestJni_testOtherFeatures(JNIEnv* env, const base::android::JavaRef<jobject>& obj)
-    {
-  jclass clazz = org_chromium_TestJni_clazz(env);
-  CHECK_CLAZZ(env, obj.obj(),
-      org_chromium_TestJni_clazz(env));
-
-  jni_generator::JniJavaCallContextChecked call_context;
-  call_context.Init<
-      base::android::MethodID::TYPE_INSTANCE>(
-          env,
-          clazz,
-          "testOtherFeatures",
-          "()V",
-          &g_org_chromium_TestJni_testOtherFeatures);
-
-     env->CallVoidMethod(obj.obj(),
-          call_context.base.method_id);
-}
-
-static std::atomic<jmethodID> g_org_chromium_TestJni_testDisabledFoo(nullptr);
-static void Java_TestJni_testDisabledFoo(JNIEnv* env, const base::android::JavaRef<jobject>& obj) {
-  jclass clazz = org_chromium_TestJni_clazz(env);
-  CHECK_CLAZZ(env, obj.obj(),
-      org_chromium_TestJni_clazz(env));
-
-  jni_generator::JniJavaCallContextChecked call_context;
-  call_context.Init<
-      base::android::MethodID::TYPE_INSTANCE>(
-          env,
-          clazz,
-          "testDisabledFoo",
-          "()V",
-          &g_org_chromium_TestJni_testDisabledFoo);
-
-     env->CallVoidMethod(obj.obj(),
-          call_context.base.method_id);
-}
-
-static std::atomic<jmethodID>
-    g_org_chromium_TestJni_testLongNameActionServiceModelProducerDelegateProxyObserverMediatorFactoryConsumerImplForTesting(nullptr);
-static void
-    Java_TestJni_testLongNameActionServiceModelProducerDelegateProxyObserverMediatorFactoryConsumerImplForTesting(JNIEnv*
-    env, const base::android::JavaRef<jobject>& obj) {
-  jclass clazz = org_chromium_TestJni_clazz(env);
-  CHECK_CLAZZ(env, obj.obj(),
-      org_chromium_TestJni_clazz(env));
-
-  jni_generator::JniJavaCallContextChecked call_context;
-  call_context.Init<
-      base::android::MethodID::TYPE_INSTANCE>(
-          env,
-          clazz,
-"testLongNameActionServiceModelProducerDelegateProxyObserverMediatorFactoryConsumerImplForTesting",
-          "()V",
-&g_org_chromium_TestJni_testLongNameActionServiceModelProducerDelegateProxyObserverMediatorFactoryConsumerImplForTesting);
-
-     env->CallVoidMethod(obj.obj(),
-          call_context.base.method_id);
-}
-
-static std::atomic<jmethodID> g_org_chromium_TestJni_00024MyInnerClass_testInnerFoo(nullptr);
-static void Java_MyInnerClass_testInnerFoo(JNIEnv* env, const base::android::JavaRef<jobject>& obj)
-    {
-  jclass clazz = org_chromium_TestJni_00024MyInnerClass_clazz(env);
-  CHECK_CLAZZ(env, obj.obj(),
-      org_chromium_TestJni_00024MyInnerClass_clazz(env));
-
-  jni_generator::JniJavaCallContextChecked call_context;
-  call_context.Init<
-      base::android::MethodID::TYPE_INSTANCE>(
-          env,
-          clazz,
-          "testInnerFoo",
-          "()V",
-          &g_org_chromium_TestJni_00024MyInnerClass_testInnerFoo);
-
-     env->CallVoidMethod(obj.obj(),
-          call_context.base.method_id);
-}
-
-static std::atomic<jmethodID> g_org_chromium_TestJni_testOneLine(nullptr);
-static void Java_TestJni_testOneLine(JNIEnv* env, const base::android::JavaRef<jobject>& obj) {
-  jclass clazz = org_chromium_TestJni_clazz(env);
-  CHECK_CLAZZ(env, obj.obj(),
-      org_chromium_TestJni_clazz(env));
-
-  jni_generator::JniJavaCallContextChecked call_context;
-  call_context.Init<
-      base::android::MethodID::TYPE_INSTANCE>(
-          env,
-          clazz,
-          "testOneLine",
-          "()V",
-          &g_org_chromium_TestJni_testOneLine);
-
-     env->CallVoidMethod(obj.obj(),
-          call_context.base.method_id);
-}
-
-// Step 4: Generated test functions (optional).
-#define JAVA_TESTS(test_fixture, java_test_object)\
-  TEST_F(test_fixture, TestFoo) { \
-    JNIEnv* env = base::android::AttachCurrentThread(); \
-    Java_TestJni_testFoo(\
-        env, java_test_object); \
-  }\
-  TEST_F(test_fixture, TestFeatures) { \
-    base::test::ScopedFeatureList feature_list; \
-    feature_list.InitFromCommandLine( \
-        "MyFeature,MyFeatureWithAReallyReallyAbsurdlyLongName", "BadFeature"); \
-    JNIEnv* env = base::android::AttachCurrentThread(); \
-    Java_TestJni_testFeatures(\
-        env, java_test_object); \
-  }\
-  TEST_F(test_fixture, TestOtherFeatures) { \
-    JNIEnv* env = base::android::AttachCurrentThread(); \
-    Java_TestJni_testOtherFeatures(\
-        env, java_test_object); \
-  }\
-  TEST_F(test_fixture, DISABLED_TestDisabledFoo) { \
-    JNIEnv* env = base::android::AttachCurrentThread(); \
-    Java_TestJni_testDisabledFoo(\
-        env, java_test_object); \
-  }\
-  TEST_F(test_fixture, TestLongNameActionServiceModelProducerDelegateProxyObserverMediatorFactoryConsumerImplForTesting) { \
-    JNIEnv* env = base::android::AttachCurrentThread(); \
-    Java_TestJni_testLongNameActionServiceModelProducerDelegateProxyObserverMediatorFactoryConsumerImplForTesting(\
-        env, java_test_object); \
-  }\
-  TEST_F(test_fixture, TestInnerFoo) { \
-    JNIEnv* env = base::android::AttachCurrentThread(); \
-    Java_MyInnerClass_testInnerFoo(\
-        env, java_test_object); \
-  }\
-  TEST_F(test_fixture, TestOneLine) { \
-    base::test::ScopedFeatureList feature_list; \
-    feature_list.InitFromCommandLine( \
-        "MyFeature", ""); \
-    JNIEnv* env = base::android::AttachCurrentThread(); \
-    Java_TestJni_testOneLine(\
-        env, java_test_object); \
-  }
-
-#endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testCalledByNatives.golden b/base/android/jni_generator/golden/testCalledByNatives.golden
index 8e82504a..6249bd6 100644
--- a/base/android/jni_generator/golden/testCalledByNatives.golden
+++ b/base/android/jni_generator/golden/testCalledByNatives.golden
@@ -475,7 +475,4 @@
   return base::android::ScopedJavaLocalRef<jobject>(env, ret);
 }
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testConstantsFromJavaP.golden b/base/android/jni_generator/golden/testConstantsFromJavaP.golden
index 292b2d1..b0364515 100644
--- a/base/android/jni_generator/golden/testConstantsFromJavaP.golden
+++ b/base/android/jni_generator/golden/testConstantsFromJavaP.golden
@@ -2317,7 +2317,4 @@
 
 }  // namespace JNI_MotionEvent
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // android_view_MotionEvent_JNI
diff --git a/base/android/jni_generator/golden/testFromJavaP.golden b/base/android/jni_generator/golden/testFromJavaP.golden
index c824864..e5f9467 100644
--- a/base/android/jni_generator/golden/testFromJavaP.golden
+++ b/base/android/jni_generator/golden/testFromJavaP.golden
@@ -273,7 +273,4 @@
 
 }  // namespace JNI_InputStream
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // java_io_InputStream_JNI
diff --git a/base/android/jni_generator/golden/testFromJavaPGenerics.golden b/base/android/jni_generator/golden/testFromJavaPGenerics.golden
index c9b845c3..9c34dfc 100644
--- a/base/android/jni_generator/golden/testFromJavaPGenerics.golden
+++ b/base/android/jni_generator/golden/testFromJavaPGenerics.golden
@@ -84,7 +84,4 @@
 
 }  // namespace JNI_HashSet
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // java_util_HashSet_JNI
diff --git a/base/android/jni_generator/golden/testInnerClassNatives.golden b/base/android/jni_generator/golden/testInnerClassNatives.golden
index 2046e09c..6cad2b0 100644
--- a/base/android/jni_generator/golden/testInnerClassNatives.golden
+++ b/base/android/jni_generator/golden/testInnerClassNatives.golden
@@ -57,7 +57,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuter.golden b/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuter.golden
index 03d3463..5b695409 100644
--- a/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuter.golden
+++ b/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuter.golden
@@ -67,7 +67,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testInnerClassNativesMultiple.golden b/base/android/jni_generator/golden/testInnerClassNativesMultiple.golden
index c6c0ba1..983fb1f 100644
--- a/base/android/jni_generator/golden/testInnerClassNativesMultiple.golden
+++ b/base/android/jni_generator/golden/testInnerClassNativesMultiple.golden
@@ -80,7 +80,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testMultipleJNIAdditionalImport.golden b/base/android/jni_generator/golden/testMultipleJNIAdditionalImport.golden
index 2c0180a0..38cca86 100644
--- a/base/android/jni_generator/golden/testMultipleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/golden/testMultipleJNIAdditionalImport.golden
@@ -68,7 +68,4 @@
           call_context.base.method_id, callback1.obj(), callback2.obj());
 }
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/golden/testNativeExportsOnlyOption.golden b/base/android/jni_generator/golden/testNativeExportsOnlyOption.golden
index adbe95e5695..95ee8824 100644
--- a/base/android/jni_generator/golden/testNativeExportsOnlyOption.golden
+++ b/base/android/jni_generator/golden/testNativeExportsOnlyOption.golden
@@ -226,7 +226,4 @@
   return base::android::ScopedJavaLocalRef<jstring>(env, ret);
 }
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_generator/golden/testNatives.golden b/base/android/jni_generator/golden/testNatives.golden
index a2ceed2..98b2568 100644
--- a/base/android/jni_generator/golden/testNatives.golden
+++ b/base/android/jni_generator/golden/testNatives.golden
@@ -213,7 +213,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testNativesLong.golden b/base/android/jni_generator/golden/testNativesLong.golden
index a0811ea..52169dcf 100644
--- a/base/android/jni_generator/golden/testNativesLong.golden
+++ b/base/android/jni_generator/golden/testNativesLong.golden
@@ -46,7 +46,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testProxyNatives.golden b/base/android/jni_generator/golden/testProxyNatives.golden
index f768289..a6bd014 100644
--- a/base/android/jni_generator/golden/testProxyNatives.golden
+++ b/base/android/jni_generator/golden/testProxyNatives.golden
@@ -60,7 +60,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_example_SampleProxyJni_JNI
diff --git a/base/android/jni_generator/golden/testProxyNativesWithNatives.golden b/base/android/jni_generator/golden/testProxyNativesWithNatives.golden
index b1a6737..728d604 100644
--- a/base/android/jni_generator/golden/testProxyNativesWithNatives.golden
+++ b/base/android/jni_generator/golden/testProxyNativesWithNatives.golden
@@ -114,7 +114,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/golden/testREForNatives.golden b/base/android/jni_generator/golden/testREForNatives.golden
index e4a1e3b7..c4b3c14 100644
--- a/base/android/jni_generator/golden/testREForNatives.golden
+++ b/base/android/jni_generator/golden/testREForNatives.golden
@@ -46,7 +46,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // foo_bar_JNI
diff --git a/base/android/jni_generator/golden/testSingleJNIAdditionalImport.golden b/base/android/jni_generator/golden/testSingleJNIAdditionalImport.golden
index e32bba7..13fb55f3 100644
--- a/base/android/jni_generator/golden/testSingleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/golden/testSingleJNIAdditionalImport.golden
@@ -64,7 +64,4 @@
           call_context.base.method_id, callback.obj());
 }
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/golden/testStaticBindingCaller.golden b/base/android/jni_generator/golden/testStaticBindingCaller.golden
index fa94bc1..df55ec4 100644
--- a/base/android/jni_generator/golden/testStaticBindingCaller.golden
+++ b/base/android/jni_generator/golden/testStaticBindingCaller.golden
@@ -97,7 +97,4 @@
 }
 
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/golden/testTracing.golden b/base/android/jni_generator/golden/testTracing.golden
index 6d414451..5ebe728 100644
--- a/base/android/jni_generator/golden/testTracing.golden
+++ b/base/android/jni_generator/golden/testTracing.golden
@@ -103,7 +103,4 @@
 }  // namespace chromium_foo
 }  // namespace org
 
-// Step 4: Generated test functions (optional).
-
-
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index ea93216..0fa50eb 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -175,10 +175,6 @@
     self.env_call = GetEnvCall(self.is_constructor, self.static,
                                self.return_type)
     self.static_cast = GetStaticCastForReturnType(self.return_type)
-    self.gen_test_method = kwargs.get('gen_test_method', False)
-    self.test_disabled = kwargs.get('test_disabled', False)
-    self.enabled_features = kwargs.get('enabled_features', None)
-    self.disabled_features = kwargs.get('disabled_features', None)
 
 
 class ConstantField(object):
@@ -670,100 +666,31 @@
 # Regex to match the JNI types that should be wrapped in a JavaRef.
 RE_SCOPED_JNI_TYPES = re.compile('jobject|jclass|jstring|jthrowable|.*Array')
 
-# Regex to match a string like "@Annotation public void foo(int bar)".
+# Regex to match a string like "@CalledByNative public void foo(int bar)".
 RE_CALLED_BY_NATIVE = re.compile(
-    # Capture all annotations.
-    r'(?P<annotations>'
-    # Optimization: Match the first annotation that could be relevant to
-    # CalledByNative.
-    r'(?:@(?:(?:Disabled)?(?:CalledByNative|NativeJavaTestFeatures|Features\.)'
-    r'[^\(\n ]*(?:\([^\)]*\)[\n ]|[\n ])\s*)'
-    # Match the rest of the annotations.
-    r'(?:@[^\(\n ]+(?:\([^\)]*\)[\n ]|[\n ])\s*)*)'
-    r')'
-    r'(?P<prefix>(?:'
-    r'(?:private|protected|public|static|abstract|final|default|synchronized)'
+    r'@CalledByNative(?P<Unchecked>(?:Unchecked)?)(?:\("(?P<annotation>.*)"\))?'
+    r'(?:\s+@\w+(?:\(.*\))?)*'  # Ignore any other annotations.
+    r'\s+(?P<prefix>('
+    r'(private|protected|public|static|abstract|final|default|synchronized)'
     r'\s*)*)'
     r'(?:\s*@\w+)?'  # Ignore annotations in return types.
     r'\s*(?P<return_type>\S*?)'
     r'\s*(?P<name>\w+)'
     r'\s*\((?P<params>[^\)]*)\)')
 
-# Regex to match a string like "CalledByNative".
-RE_CALLED_BY_NATIVE_ANNOTATION = re.compile(
-    r'(?P<Disabled>(?:Disabled)?)CalledByNative'
-    r'(?P<Unchecked>(?:Unchecked)?)(?P<JavaTest>(?:JavaTest)?)'
-    r'(?:\("(?P<annotation>.*)"\))?')
-
-RE_ENABLED_FEATURES = re.compile(
-    r'NativeJavaTestFeatures\.Enable\((?P<features>[^\)]*)\)')
-
-RE_DISABLED_FEATURES = re.compile(
-    r'NativeJavaTestFeatures\.Disable\((?P<features>[^\)]*)\)')
-
-RE_BANNED_FEATURES = re.compile(r'^Features\.')
-
-RE_BANNED_IMPORTS = re.compile(r'(import (?:'
-                               r'org\.junit\.Rule|'
-                               r'org\.junit\.Test|'
-                               r'org\.junit\.runner|'
-                               r'org\.junit\.Before|'
-                               r'org\.junit\.After|'
-                               r'org\.robolectric))')
-
-RE_FEATURE_STRING = r'[^"]*"(.*)";'
 
 # Removes empty lines that are indented (i.e. start with 2x spaces).
 def RemoveIndentedEmptyLines(string):
   return re.sub('^(?: {2})+$\n', '', string, flags=re.MULTILINE)
 
 
-def _StripChars(chars, string):
-  ret = string
-  for char in chars:
-    ret = ret.replace(char, '')
-  return ret
-
-
-def _GetFeaturesString(features_match, feature_list_file):
-  if not features_match:
-    return None
-  features = features_match.group('features')
-  features_list = _StripChars(" {}\n", features)
-  if not features_list:
-    return None
-  if not feature_list_file:
-    raise ParseError('Your generate_jni target must specify a feature_list_file'
-                     ' in order to support feature annotations.')
-
-  with open(feature_list_file) as f:
-    contents = f.read()
-  features_split = features_list.split(',')
-  feature_strings = []
-  for feature in features_split:
-    regex = feature.split('.')[-1] + RE_FEATURE_STRING
-    match = re.search(re.compile(regex), contents)
-    if not match:
-      raise ParseError('Feature not found in {}.'.format(feature_list_file),
-                       feature)
-    feature_strings.append(match.group(1))
-
-  return (',').join(feature_strings)
-
-
-def ExtractCalledByNatives(jni_params,
-                           contents,
-                           always_mangle,
-                           feature_list_file=None,
-                           fully_qualified_class=""):
+def ExtractCalledByNatives(jni_params, contents, always_mangle):
   """Parses all methods annotated with @CalledByNative.
 
   Args:
     jni_params: JniParams object.
     contents: the contents of the java file.
     always_mangle: See MangleCalledByNatives.
-    feature_list_file: A file containing the feature constants used by the build
-        target.
 
   Returns:
     A list of dict with information about the annotated methods.
@@ -773,44 +700,7 @@
     ParseError: if unable to parse.
   """
   called_by_natives = []
-  checked_banned_imports = False
   for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
-    cbn = None
-    enabled_features = None
-    disabled_features = None
-    for value in match.group('annotations').split('@'):
-      if not cbn:
-        cbn = re.match(RE_CALLED_BY_NATIVE_ANNOTATION, value)
-      if not enabled_features:
-        enabled_features = re.match(RE_ENABLED_FEATURES, value)
-      if not disabled_features:
-        disabled_features = re.match(RE_DISABLED_FEATURES, value)
-      if re.match(RE_BANNED_FEATURES, value):
-        raise ParseError(
-            '@Features.EnableFeatures will not work, please use '
-            '@NativeJavaTestFeatures.Enable("MyFeature") instead.',
-            fully_qualified_class, value)
-    if not cbn:
-      continue
-    java_test = 'JavaTest' in cbn.group('JavaTest')
-    if java_test and not checked_banned_imports:
-      checked_banned_imports = True
-      imports = re.search(RE_BANNED_IMPORTS, contents)
-      if imports:
-        raise ParseError(
-            'Most junit and robolectric features do not work for native java '
-            'unittests (asserts are okay).', fully_qualified_class,
-            imports.group(1))
-
-
-    enabled_string = _GetFeaturesString(enabled_features, feature_list_file)
-    disabled_string = _GetFeaturesString(disabled_features, feature_list_file)
-
-    if not java_test and (enabled_string or disabled_string):
-      raise ParseError(
-          '@NativeJavaTestFeatures requires @CalledByNativeJavaTest to work.',
-          fully_qualified_class, match.group('annotations'))
-
     return_type = match.group('return_type')
     name = match.group('name')
     if not return_type:
@@ -821,26 +711,21 @@
       is_constructor = False
 
     called_by_natives += [
-        CalledByNative(
-            system_class=False,
-            unchecked='Unchecked' in cbn.group('Unchecked'),
-            static='static' in match.group('prefix'),
-            java_class_name=cbn.group('annotation') or '',
-            return_type=return_type,
-            name=name,
-            is_constructor=is_constructor,
-            params=JniParams.Parse(match.group('params')),
-            gen_test_method=java_test,
-            test_disabled='Disabled' in cbn.group('Disabled'),
-            enabled_features=enabled_string,
-            disabled_features=disabled_string),
+        CalledByNative(system_class=False,
+                       unchecked='Unchecked' in match.group('Unchecked'),
+                       static='static' in match.group('prefix'),
+                       java_class_name=match.group('annotation') or '',
+                       return_type=return_type,
+                       name=name,
+                       is_constructor=is_constructor,
+                       params=JniParams.Parse(match.group('params')))
     ]
   # Check for any @CalledByNative occurrences that weren't matched.
   unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
   for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
     if '@CalledByNative' in line1:
       raise ParseError('could not parse @CalledByNative method signature',
-                       fully_qualified_class, line1, line2)
+                       line1, line2)
   return MangleCalledByNatives(jni_params, called_by_natives, always_mangle)
 
 
@@ -1052,9 +937,8 @@
     self.jni_params.ExtractImportsAndInnerClasses(contents)
     jni_namespace = ExtractJNINamespace(contents) or options.namespace
     natives = ExtractNatives(contents, options.ptr_type)
-    called_by_natives = ExtractCalledByNatives(
-        self.jni_params, contents, options.always_mangle,
-        options.feature_list_file, fully_qualified_class)
+    called_by_natives = ExtractCalledByNatives(self.jni_params, contents,
+                                               options.always_mangle)
 
     natives += ProxyHelpers.ExtractStaticProxyNatives(
         fully_qualified_class, contents, options.ptr_type,
@@ -1230,9 +1114,6 @@
 // Step 3: Method stubs.
 $METHOD_STUBS
 
-// Step 4: Generated test functions (optional).
-$TEST_METHODS
-
 #endif  // ${HEADER_GUARD}
 """)
     values = {
@@ -1243,7 +1124,6 @@
         'METHOD_STUBS': self.GetMethodStubsString(),
         'HEADER_GUARD': self.header_guard,
         'INCLUDES': self.GetIncludesString(),
-        'TEST_METHODS': self.GetTestMethodsString(),
     }
     open_namespace = self.GetOpenNamespaceString()
     if open_namespace:
@@ -1286,17 +1166,6 @@
         for called_by_native in self.called_by_natives
     ]
 
-  def GetTestMethodsString(self):
-    strings = []
-    for called_by_native in self.called_by_natives:
-      if not called_by_native.gen_test_method:
-        continue
-      strings += [self.GetTestMethodString(called_by_native)]
-    if strings:
-      strings.insert(0, "#define JAVA_TESTS(test_fixture, java_test_object)\\")
-    ret = '\n'.join(strings)
-    return ret[:-1]  # Drop trailing '\'.
-
   def GetIncludesString(self):
     if not self.options.includes:
       return ''
@@ -1577,40 +1446,6 @@
       values['TRACE_EVENT'] = ''
     return RemoveIndentedEmptyLines(template.substitute(values))
 
-  def GetTestMethodString(self, called_by_native):
-    method_template = Template("""\
-  TEST_F(test_fixture, ${DISABLED}${METHOD_ID_VAR_NAME_UPPERCASE}) { \\
-${FEATURES}    JNIEnv* env = base::android::AttachCurrentThread(); \\
-    Java_${JAVA_CLASS_ONLY}_${METHOD_ID_VAR_NAME}(\\
-        env, java_test_object); \\
-  }\\""")
-
-    features_template = Template("""\
-    base::test::ScopedFeatureList feature_list; \\
-    feature_list.InitFromCommandLine( \\
-        \"${ENABLED}\", \"${DISABLED}\"); \\
-""")
-    values = self.GetCalledByNativeValues(called_by_native)
-
-    if called_by_native.enabled_features or called_by_native.disabled_features:
-      features_values = {
-          'ENABLED': called_by_native.enabled_features or '',
-          'DISABLED': called_by_native.disabled_features or ''
-      }
-      values['FEATURES'] = features_template.substitute(features_values)
-    else:
-      values['FEATURES'] = ''
-
-    method_name = values['METHOD_ID_VAR_NAME']
-    values['METHOD_ID_VAR_NAME_UPPERCASE'] = method_name[0].upper(
-    ) + method_name[1:]
-    if called_by_native.test_disabled:
-      values['DISABLED'] = 'DISABLED_'
-    else:
-      values['DISABLED'] = ''
-    return RemoveIndentedEmptyLines(method_template.substitute(values))
-
-
   def GetTraceEventForNameTemplate(self, name_template, values):
     name = Template(name_template).substitute(values)
     return '  TRACE_EVENT0("jni", "%s");\n' % name
@@ -1618,16 +1453,10 @@
 
 def WrapOutput(output):
   ret = []
-  is_preprocessor_directive = False
   for line in output.splitlines():
-    if line and line[0] == '#':
-      is_preprocessor_directive = True
     # Do not wrap preprocessor directives or comments.
-    if (len(line) < _WRAP_LINE_LENGTH or is_preprocessor_directive
-        or line.startswith('//')):
+    if len(line) < _WRAP_LINE_LENGTH or line[0] == '#' or line.startswith('//'):
       ret.append(line)
-      if not line or line[-1] != '\\':
-        is_preprocessor_directive = False
     else:
       # Assumes that the line is not already indented as a continuation line,
       # which is not always true (oh well).
@@ -1747,11 +1576,6 @@
       action='store_true',
       help='Hashes the native declaration of methods used '
       'in @JniNatives interface.')
-  parser.add_argument(
-      '--feature_list_file',
-      default='',
-      help='Optional path to a Java file containing the list of possible '
-      'features to be used for validation.')
   args = parser.parse_args()
   input_files = args.input_files
   output_files = args.output_files
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index 842b2fa..fbaf9fb 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -52,12 +52,6 @@
     self.enable_tracing = False
     self.use_proxy_hash = False
     self.always_mangle = False
-    self.feature_list_file = ''
-
-
-def _FeatureListFile():
-  dir_name = os.path.dirname(os.path.realpath(__file__))
-  return dir_name + '/TestSampleFeatureList.java'
 
 
 class BaseTest(unittest.TestCase):
@@ -818,268 +812,7 @@
           always_mangle=False)
       self.fail('Expected a ParseError')
     except jni_generator.ParseError as e:
-      self.assertEqual(('', '@CalledByNative', 'scooby doo'), e.context_lines)
-
-  def testCalledByNativeJavaTestImportErrors(self):
-    # Using banned imports
-    try:
-      jni_params = jni_generator.JniParams('')
-      jni_generator.ExtractCalledByNatives(
-          jni_params,
-          """
-import org.junit.Rule;
-
-class MyClass {
-    @Rule
-    public JniMocker mocker = new JniMocker();
-
-    @CalledByNativeJavaTest
-    public void testStuff() {}
-}
-""",
-          always_mangle=False,
-          feature_list_file=_FeatureListFile())
-      self.fail('Expected a ParseError')
-    except jni_generator.ParseError as e:
-      self.assertEqual(('', 'import org.junit.Rule'), e.context_lines)
-
-  def testCalledByNativeJavaTestFeatureParseErrors(self):
-    # Using banned Features.Enable/Disable
-    try:
-      jni_params = jni_generator.JniParams('')
-      jni_generator.ExtractCalledByNatives(
-          jni_params,
-          """
-class MyClass {
-    @Features.Disable({ChromeFeatureList.SOME_FEATURE})
-    @CalledByNativeJavaTest
-    public void testMoreFeatures() {}
-}
-""",
-          always_mangle=False,
-          feature_list_file=_FeatureListFile())
-      self.fail('Expected a ParseError')
-    except jni_generator.ParseError as e:
-      self.assertEqual(
-          ('', 'Features.Disable({ChromeFeatureList.SOME_FEATURE})\n    '),
-          e.context_lines)
-
-    # Using NativeJavaTestFeatures outside of a test.
-    try:
-      jni_params = jni_generator.JniParams('')
-      jni_generator.ExtractCalledByNatives(
-          jni_params,
-          """
-class MyClass {
-    @CalledByNative @NativeJavaTestFeatures.Enable(TestFeatureList.MY_FEATURE) \
-public void testNotActuallyATest() {}
-}
-""",
-          always_mangle=False,
-          feature_list_file=_FeatureListFile())
-      self.fail('Expected a ParseError')
-    except jni_generator.ParseError as e:
-      self.assertEqual((
-          '',
-          '@CalledByNative @NativeJavaTestFeatures.Enable('
-          'TestFeatureList.MY_FEATURE) ',
-      ), e.context_lines)
-
-    # Not specifying a feature_list_file.
-    try:
-      jni_params = jni_generator.JniParams('')
-      jni_generator.ExtractCalledByNatives(
-          jni_params,
-          """
-class MyClass {
-    @CalledByNativeJavaTest
-    @NativeJavaTestFeatures.Disable(TestFeatureList.MY_FEATURE)
-    public void testMoreFeatures() {}
-}
-""",
-          always_mangle=False,
-          feature_list_file=None)
-      self.fail('Expected a ParseError')
-    except jni_generator.ParseError as e:
-      self.assertEqual(
-          'Your generate_jni target must specify a feature_list_file in order '
-          'to support feature annotations.', e.description)
-
-    # Specifying a feature that doesn't exist.
-    try:
-      jni_params = jni_generator.JniParams('')
-      jni_generator.ExtractCalledByNatives(
-          jni_params,
-          """
-class MyClass {
-    @CalledByNativeJavaTest
-    @NativeJavaTestFeatures.Disable(TestFeatureList.NOT_A_FEATURE)
-    public void testMoreFeatures() {}
-}
-""",
-          always_mangle=False,
-          feature_list_file=_FeatureListFile())
-      self.fail('Expected a ParseError')
-    except jni_generator.ParseError as e:
-      self.assertEqual(('TestFeatureList.NOT_A_FEATURE', ), e.context_lines)
-
-  def testCalledByNativeJavaTest(self):
-    test_data = """
-    class MyOuterClass {
-      @CalledByNative
-      public MyOuterClass() {}
-
-      @CalledByNativeJavaTest
-      public int testFoo() {}
-
-      @NativeJavaTestFeatures.Enable({TestFeatureList.MY_FEATURE,
-          TestFeatureList.MY_FEATURE_WITH_A_REALLY_REALLY_ABSURDLY_LONG_NAME})
-      @CalledByNativeJavaTest
-      @NativeJavaTestFeatures.Disable(TestFeatureList.BAD_FEATURE)
-      public void testFeatures() {}
-
-      @CalledByNativeJavaTest
-      @NativeJavaTestFeatures.Enable({})
-      @NativeJavaTestFeatures.Disable({})
-      public void testOtherFeatures() {}
-
-      @DisabledCalledByNativeJavaTest
-      public void testDisabledFoo() {}
-
-      @CalledByNativeJavaTest
-      public void testLongNameActionServiceModelProducerDelegateProxyObserver\
-MediatorFactoryConsumerImplForTesting() {}
-
-      class MyInnerClass {
-        @CalledByNativeJavaTest("MyInnerClass")
-        public void testInnerFoo() {}
-      }
-
-      @CalledByNativeJavaTest @NativeJavaTestFeatures.Enable(\
-TestFeatureList.MY_FEATURE) public void testOneLine() {}
-    }
-    """
-    jni_params = jni_generator.JniParams('org/chromium/Foo')
-    jni_params.ExtractImportsAndInnerClasses(test_data)
-    called_by_natives = jni_generator.ExtractCalledByNatives(
-        jni_params,
-        test_data,
-        always_mangle=False,
-        feature_list_file=_FeatureListFile())
-    golden_called_by_natives = [
-        CalledByNative(
-            return_type='MyOuterClass',
-            system_class=False,
-            static=False,
-            name='Constructor',
-            method_id_var_name='Constructor',
-            java_class_name='',
-            params=[],
-            env_call=('Void', ''),
-            unchecked=False,
-            gen_test_method=False,
-            is_constructor=True,
-        ),
-        CalledByNative(
-            return_type='int',
-            system_class=False,
-            static=False,
-            name='testFoo',
-            method_id_var_name='testFoo',
-            java_class_name='',
-            params=[],
-            env_call=('Void', ''),
-            unchecked=False,
-            gen_test_method=True,
-        ),
-        CalledByNative(
-            return_type='void',
-            system_class=False,
-            static=False,
-            name='testFeatures',
-            method_id_var_name='testFeatures',
-            java_class_name='',
-            params=[],
-            env_call=('Void', ''),
-            unchecked=False,
-            gen_test_method=True,
-            enabled_features=
-            'MyFeature,MyFeatureWithAReallyReallyAbsurdlyLongName',
-            disabled_features='BadFeature',
-        ),
-        CalledByNative(
-            return_type='void',
-            system_class=False,
-            static=False,
-            name='testOtherFeatures',
-            method_id_var_name='testOtherFeatures',
-            java_class_name='',
-            params=[],
-            env_call=('Void', ''),
-            unchecked=False,
-            gen_test_method=True,
-            enabled_features=None,
-            disabled_features=None,
-        ),
-        CalledByNative(
-            return_type='void',
-            system_class=False,
-            static=False,
-            name='testDisabledFoo',
-            method_id_var_name='testDisabledFoo',
-            java_class_name='',
-            params=[],
-            env_call=('Void', ''),
-            unchecked=False,
-            gen_test_method=True,
-            test_disabled=True,
-        ),
-        CalledByNative(
-            return_type='void',
-            system_class=False,
-            static=False,
-            name=
-            'testLongNameActionServiceModelProducerDelegateProxyObserverMediatorFactoryConsumerImplForTesting',
-            method_id_var_name=
-            'testLongNameActionServiceModelProducerDelegateProxyObserverMediatorFactoryConsumerImplForTesting',
-            java_class_name='',
-            params=[],
-            env_call=('Void', ''),
-            unchecked=False,
-            gen_test_method=True,
-        ),
-        CalledByNative(
-            return_type='void',
-            system_class=False,
-            static=False,
-            name='testInnerFoo',
-            method_id_var_name='testInnerFoo',
-            java_class_name='MyInnerClass',
-            params=[],
-            env_call=('Void', ''),
-            unchecked=False,
-            gen_test_method=True,
-        ),
-        CalledByNative(
-            return_type='void',
-            system_class=False,
-            static=False,
-            name='testOneLine',
-            method_id_var_name='testOneLine',
-            java_class_name='',
-            params=[],
-            env_call=('Void', ''),
-            unchecked=False,
-            gen_test_method=True,
-            enabled_features='MyFeature',
-            disabled_features=None,
-        ),
-    ]
-    self.AssertListEquals(golden_called_by_natives, called_by_natives)
-    h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', [],
-                                             called_by_natives, [], jni_params,
-                                             TestOptions())
-    self.AssertGoldenTextEquals(h.GetContent())
+      self.assertEqual(('@CalledByNative', 'scooby doo'), e.context_lines)
 
   def testFullyQualifiedClassName(self):
     contents = """
diff --git a/base/pending_task.h b/base/pending_task.h
index e7648c2..fa534ab 100644
--- a/base/pending_task.h
+++ b/base/pending_task.h
@@ -61,12 +61,12 @@
   // The context of the IPC message that was being handled when this task was
   // posted. This is a hash of the IPC message name that is set within the scope
   // of an IPC handler and when symbolized uniquely identifies the message being
-  // processed. This property is also propagated from one PendingTask to the
+  // processed. This property is not propagated from one PendingTask to the
   // next. For example, if pending task A was posted while handling an IPC,
   // and pending task B was posted from within pending task A, then pending task
-  // B will inherit the |ipc_hash| of pending task A. In some sense this can be
-  // interpreted as a "root" task backtrace frame.
+  // B will not inherit the |ipc_hash| of pending task A.
   uint32_t ipc_hash = 0;
+  const char* ipc_interface_name = nullptr;
 
   // Secondary sort key for run time.
   int sequence_num = 0;
diff --git a/base/task/common/task_annotator.cc b/base/task/common/task_annotator.cc
index 505a55b..fe3c1e8 100644
--- a/base/task/common/task_annotator.cc
+++ b/base/task/common/task_annotator.cc
@@ -6,9 +6,12 @@
 
 #include <array>
 
+#include "base/check_op.h"
 #include "base/debug/activity_tracker.h"
 #include "base/debug/alias.h"
+#include "base/hash/md5.h"
 #include "base/no_destructor.h"
+#include "base/sys_byteorder.h"
 #include "base/threading/thread_local.h"
 #include "base/trace_event/base_tracing.h"
 
@@ -30,6 +33,17 @@
   return instance.get();
 }
 
+// Returns the TLS slot that stores scoped IPC-related data (IPC hash and/or
+// IPC interface name). IPC hash or interface name can be known before the
+// associated task object is created; store in the TLS so that this data can be
+// affixed to the associated task.
+ThreadLocalPointer<TaskAnnotator::ScopedSetIpcHash>*
+GetTLSForCurrentScopedIpcHash() {
+  static NoDestructor<ThreadLocalPointer<TaskAnnotator::ScopedSetIpcHash>>
+      instance;
+  return instance.get();
+}
+
 // Determines whether or not the given |task| is a dummy pending task that has
 // been injected by ScopedSetIpcHash solely for the purposes of
 // tracking IPC context.
@@ -49,7 +63,7 @@
 
   // Don't return "dummy" current tasks that are only used for storing IPC
   // context.
-  if (current_task && IsDummyPendingTask(current_task))
+  if (!current_task || (current_task && IsDummyPendingTask(current_task)))
     return nullptr;
   return current_task;
 }
@@ -74,11 +88,18 @@
   if (pending_task->task_backtrace[0])
     return;
 
+  DCHECK(!pending_task->ipc_interface_name);
+  DCHECK(!pending_task->ipc_hash);
+  auto* current_ipc_hash = GetTLSForCurrentScopedIpcHash()->Get();
+  if (current_ipc_hash) {
+    pending_task->ipc_interface_name = current_ipc_hash->GetIpcInterfaceName();
+    pending_task->ipc_hash = current_ipc_hash->GetIpcHash();
+  }
+
   const auto* parent_task = CurrentTaskForThread();
   if (!parent_task)
     return;
 
-  pending_task->ipc_hash = parent_task->ipc_hash;
   pending_task->task_backtrace[0] = parent_task->posted_from.program_counter();
   std::copy(parent_task->task_backtrace.begin(),
             parent_task->task_backtrace.end() - 1,
@@ -170,31 +191,42 @@
   g_task_annotator_observer = nullptr;
 }
 
-TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash(uint32_t ipc_hash) {
-  // We store the IPC context in the currently running task. If there is none
-  // then introduce a dummy task.
-  auto* tls = GetTLSForCurrentPendingTask();
-  auto* current_task = tls->Get();
-  if (!current_task) {
-    dummy_pending_task_ = std::make_unique<PendingTask>();
-    dummy_pending_task_->sequence_num = kSentinelSequenceNum;
-    current_task = dummy_pending_task_.get();
-    tls->Set(current_task);
-  }
+TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash(uint32_t ipc_hash)
+    : ScopedSetIpcHash(ipc_hash, nullptr) {}
 
-  old_ipc_hash_ = current_task->ipc_hash;
-  current_task->ipc_hash = ipc_hash;
+TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash(
+    const char* ipc_interface_name)
+    : ScopedSetIpcHash(0, ipc_interface_name) {}
+
+TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash(
+    uint32_t ipc_hash,
+    const char* ipc_interface_name) {
+  TRACE_EVENT_BEGIN2("base", "ScopedSetIpcHash", "ipc_hash", ipc_hash,
+                     "ipc_interface_name", ipc_interface_name);
+  auto* tls_ipc_hash = GetTLSForCurrentScopedIpcHash();
+  auto* current_ipc_hash = tls_ipc_hash->Get();
+  old_scoped_ipc_hash_ = current_ipc_hash;
+  ipc_hash_ = ipc_hash;
+  ipc_interface_name_ = ipc_interface_name;
+  tls_ipc_hash->Set(this);
+}
+
+// Static
+uint32_t TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName(
+    base::StringPiece name) {
+  base::MD5Digest digest;
+  base::MD5Sum(name.data(), name.size(), &digest);
+  uint32_t value;
+  DCHECK_GE(sizeof(digest.a), sizeof(value));
+  memcpy(&value, digest.a, sizeof(value));
+  return base::NetToHost32(value);
 }
 
 TaskAnnotator::ScopedSetIpcHash::~ScopedSetIpcHash() {
-  auto* tls = GetTLSForCurrentPendingTask();
-  auto* current_task = tls->Get();
-  DCHECK(current_task);
-  if (current_task == dummy_pending_task_.get()) {
-    tls->Set(nullptr);
-  } else {
-    current_task->ipc_hash = old_ipc_hash_;
-  }
+  auto* tls_ipc_hash = GetTLSForCurrentScopedIpcHash();
+  DCHECK_EQ(this, tls_ipc_hash->Get());
+  tls_ipc_hash->Set(old_scoped_ipc_hash_);
+  TRACE_EVENT_END0("base", "ScopedSetIpcHash");
 }
 
 }  // namespace base
diff --git a/base/task/common/task_annotator.h b/base/task/common/task_annotator.h
index 30d18b1..55a3ab2 100644
--- a/base/task/common/task_annotator.h
+++ b/base/task/common/task_annotator.h
@@ -13,6 +13,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/pending_task.h"
+#include "base/strings/string_piece.h"
 
 namespace base {
 
@@ -71,11 +72,22 @@
 class BASE_EXPORT TaskAnnotator::ScopedSetIpcHash {
  public:
   explicit ScopedSetIpcHash(uint32_t ipc_hash);
+
+  // Compile-time-const string identifying the current IPC context. Not always
+  // available due to binary size constraints, so IPC hash might be set instead.
+  explicit ScopedSetIpcHash(const char* ipc_interface_name);
   ~ScopedSetIpcHash();
 
+  uint32_t GetIpcHash() const { return ipc_hash_; }
+  const char* GetIpcInterfaceName() const { return ipc_interface_name_; }
+
+  static uint32_t MD5HashMetricName(base::StringPiece name);
+
  private:
-  std::unique_ptr<PendingTask> dummy_pending_task_;
-  uint32_t old_ipc_hash_ = 0;
+  ScopedSetIpcHash(uint32_t ipc_hash, const char* ipc_interface_name);
+  ScopedSetIpcHash* old_scoped_ipc_hash_ = nullptr;
+  uint32_t ipc_hash_ = 0;
+  const char* ipc_interface_name_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedSetIpcHash);
 };
diff --git a/base/task/common/task_annotator_unittest.cc b/base/task/common/task_annotator_unittest.cc
index edac760..64be6aa 100644
--- a/base/task/common/task_annotator_unittest.cc
+++ b/base/task/common/task_annotator_unittest.cc
@@ -165,7 +165,8 @@
   RunLoop run_loop;
 
   // Task 0 executes with no IPC context. Task 1 executes under an explicitly
-  // set IPC context, and tasks 2-5 inherit that context.
+  // set IPC context. Tasks 2-5 don't necessarily inherit that context, as
+  // IPCs may spawn subtasks that aren't necessarily IPCs themselves.
 
   // Task 5 has tasks 4/3/2/1 as parents (task 0 isn't visible as only the
   // last 4 parents are kept).
@@ -174,7 +175,7 @@
       Unretained(this), ThreadTaskRunnerHandle::Get(), location5, FROM_HERE,
       ExpectedTrace({location4.program_counter(), location3.program_counter(),
                      location2.program_counter(), location1.program_counter()}),
-      dummy_ipc_hash, run_loop.QuitClosure());
+      0, run_loop.QuitClosure());
 
   // Task i=4/3/2/1/0 have tasks [0,i) as parents.
   OnceClosure task4 = BindOnce(
@@ -182,13 +183,13 @@
       Unretained(this), ThreadTaskRunnerHandle::Get(), location4, location5,
       ExpectedTrace({location3.program_counter(), location2.program_counter(),
                      location1.program_counter(), location0.program_counter()}),
-      dummy_ipc_hash, std::move(task5));
+      0, std::move(task5));
   OnceClosure task3 = BindOnce(
       &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
       Unretained(this), ThreadTaskRunnerHandle::Get(), location3, location4,
       ExpectedTrace({location2.program_counter(), location1.program_counter(),
                      location0.program_counter()}),
-      dummy_ipc_hash, std::move(task4));
+      0, std::move(task4));
   OnceClosure task2 = BindOnce(
       &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
       Unretained(this), ThreadTaskRunnerHandle::Get(), location2, location3,
@@ -359,19 +360,19 @@
       Unretained(this), ThreadTaskRunnerHandle::Get(), location5, FROM_HERE,
       ExpectedTrace({location4.program_counter(), location3.program_counter(),
                      location2.program_counter(), location1.program_counter()}),
-      dummy_ipc_hash, run_loop.QuitClosure());
+      0, run_loop.QuitClosure());
   OnceClosure task4 = BindOnce(
       &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
       Unretained(this), ThreadTaskRunnerHandle::Get(), location4, location5,
       ExpectedTrace({location3.program_counter(), location2.program_counter(),
                      location1.program_counter(), location0.program_counter()}),
-      dummy_ipc_hash, std::move(task5));
+      0, std::move(task5));
   OnceClosure task3 = BindOnce(
       &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
       Unretained(this), ThreadTaskRunnerHandle::Get(), location3, location4,
       ExpectedTrace({location2.program_counter(), location1.program_counter(),
                      location0.program_counter()}),
-      dummy_ipc_hash, std::move(task4));
+      0, std::move(task4));
 
   OnceClosure run_task_3_then_quit_nested_loop1 =
       BindOnce(&TaskAnnotatorBacktraceIntegrationTest::RunTwo, std::move(task3),
@@ -381,7 +382,7 @@
       &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
       Unretained(this), ThreadTaskRunnerHandle::Get(), location2, location3,
       ExpectedTrace({location1.program_counter(), location0.program_counter()}),
-      dummy_ipc_hash, std::move(run_task_3_then_quit_nested_loop1));
+      0, std::move(run_task_3_then_quit_nested_loop1));
 
   // Task 1 is custom. It enters another nested RunLoop, has it do work and exit
   // before posting the next task. This confirms that |task1| is restored as the
diff --git a/base/task/thread_pool/job_task_source.cc b/base/task/thread_pool/job_task_source.cc
index 221e4e4..cb9ec96 100644
--- a/base/task/thread_pool/job_task_source.cc
+++ b/base/task/thread_pool/job_task_source.cc
@@ -95,7 +95,7 @@
             self->worker_task_.Run(&job_delegate);
           },
           base::Unretained(this))),
-      queue_time_(TimeTicks::Now()),
+      ready_time_(TimeTicks::Now()),
       delegate_(delegate) {
   DCHECK(delegate_);
 }
@@ -348,7 +348,8 @@
 }
 
 TaskSourceSortKey JobTaskSource::GetSortKey() const {
-  return TaskSourceSortKey(traits_.priority(), queue_time_);
+  return TaskSourceSortKey(priority_racy(), ready_time_,
+                           TS_UNCHECKED_READ(state_).Load().worker_count());
 }
 
 Task JobTaskSource::Clear(TaskSource::Transaction* transaction) {
diff --git a/base/task/thread_pool/job_task_source.h b/base/task/thread_pool/job_task_source.h
index b47b220..a1ce0905 100644
--- a/base/task/thread_pool/job_task_source.h
+++ b/base/task/thread_pool/job_task_source.h
@@ -68,6 +68,7 @@
   // TaskSource:
   ExecutionEnvironment GetExecutionEnvironment() override;
   size_t GetRemainingConcurrency() const override;
+  TaskSourceSortKey GetSortKey() const override;
 
   bool IsCompleted() const;
   size_t GetWorkerCount() const;
@@ -182,7 +183,6 @@
   Task TakeTask(TaskSource::Transaction* transaction) override;
   Task Clear(TaskSource::Transaction* transaction) override;
   bool DidProcessTask(TaskSource::Transaction* transaction) override;
-  TaskSourceSortKey GetSortKey() const override;
 
   // Synchronizes access to workers state.
   mutable CheckedLock worker_lock_{UniversalSuccessor()};
@@ -207,7 +207,7 @@
   // Task returned from TakeTask(), that calls |worker_task_| internally.
   RepeatingClosure primary_task_;
 
-  const TimeTicks queue_time_;
+  const TimeTicks ready_time_;
   PooledTaskRunnerDelegate* delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(JobTaskSource);
diff --git a/base/task/thread_pool/job_task_source_unittest.cc b/base/task/thread_pool/job_task_source_unittest.cc
index 6a61e7c..e8e98b0 100644
--- a/base/task/thread_pool/job_task_source_unittest.cc
+++ b/base/task/thread_pool/job_task_source_unittest.cc
@@ -203,6 +203,7 @@
   EXPECT_EQ(registered_task_source_a.WillRunTask(),
             TaskSource::RunStatus::kAllowedNotSaturated);
   EXPECT_EQ(1U, task_source->GetWorkerCount());
+  EXPECT_EQ(1U, task_source->GetSortKey().worker_count());
   auto task_a = registered_task_source_a.TakeTask();
 
   auto registered_task_source_b =
@@ -210,6 +211,7 @@
   EXPECT_EQ(registered_task_source_b.WillRunTask(),
             TaskSource::RunStatus::kAllowedSaturated);
   EXPECT_EQ(2U, task_source->GetWorkerCount());
+  EXPECT_EQ(2U, task_source->GetSortKey().worker_count());
   auto task_b = registered_task_source_b.TakeTask();
 
   // WillRunTask() should return a null RunStatus once the max concurrency is
@@ -222,9 +224,11 @@
   // source to re-enqueue.
   job_task->SetNumTasksToRun(2);
   EXPECT_TRUE(registered_task_source_a.DidProcessTask());
+  EXPECT_EQ(1U, task_source->GetSortKey().worker_count());
 
   std::move(task_b.task).Run();
   EXPECT_TRUE(registered_task_source_b.DidProcessTask());
+  EXPECT_EQ(0U, task_source->GetSortKey().worker_count());
 
   EXPECT_EQ(0U, task_source->GetWorkerCount());
 
diff --git a/base/task/thread_pool/priority_queue.cc b/base/task/thread_pool/priority_queue.cc
index 3e8e4d0..6b992f19 100644
--- a/base/task/thread_pool/priority_queue.cc
+++ b/base/task/thread_pool/priority_queue.cc
@@ -100,7 +100,7 @@
 void PriorityQueue::Push(
     TransactionWithRegisteredTaskSource transaction_with_task_source) {
   auto task_source_sort_key =
-      transaction_with_task_source.transaction.GetSortKey();
+      transaction_with_task_source.task_source->GetSortKey();
   container_.insert(
       TaskSourceAndSortKey(std::move(transaction_with_task_source.task_source),
                            task_source_sort_key));
@@ -160,29 +160,27 @@
   return registered_task_source;
 }
 
-void PriorityQueue::UpdateSortKey(TaskSource::Transaction transaction) {
-  DCHECK(transaction);
-
+void PriorityQueue::UpdateSortKey(const TaskSource& task_source,
+                                  TaskSourceSortKey sort_key) {
   if (IsEmpty())
     return;
 
-  const HeapHandle heap_handle = transaction.task_source()->heap_handle();
+  const HeapHandle heap_handle = task_source.heap_handle();
   if (!heap_handle.IsValid())
     return;
 
   auto old_sort_key = container_.at(heap_handle).sort_key();
-  auto new_sort_key = transaction.GetSortKey();
   auto registered_task_source =
       const_cast<PriorityQueue::TaskSourceAndSortKey&>(
           container_.at(heap_handle))
           .take_task_source();
 
   DecrementNumTaskSourcesForPriority(old_sort_key.priority());
-  IncrementNumTaskSourcesForPriority(new_sort_key.priority());
+  IncrementNumTaskSourcesForPriority(sort_key.priority());
 
   container_.ChangeKey(
       heap_handle,
-      TaskSourceAndSortKey(std::move(registered_task_source), new_sort_key));
+      TaskSourceAndSortKey(std::move(registered_task_source), sort_key));
 }
 
 bool PriorityQueue::IsEmpty() const {
diff --git a/base/task/thread_pool/priority_queue.h b/base/task/thread_pool/priority_queue.h
index f7381cb..b5ae708 100644
--- a/base/task/thread_pool/priority_queue.h
+++ b/base/task/thread_pool/priority_queue.h
@@ -51,10 +51,10 @@
   // empty.
   RegisteredTaskSource RemoveTaskSource(const TaskSource& task_source);
 
-  // Updates the sort key of the TaskSource in |transaction| to
-  // match its current traits. No-ops if the TaskSource is not in the
-  // PriorityQueue or the PriorityQueue is empty.
-  void UpdateSortKey(TaskSource::Transaction transaction);
+  // Updates the sort key of |task_source| to |sort_key|, reordering
+  // |task_source| in the queue if necessary. No-ops if the TaskSource is not in
+  // the PriorityQueue or the PriorityQueue is empty.
+  void UpdateSortKey(const TaskSource& task_source, TaskSourceSortKey sort_key);
 
   // Returns true if the PriorityQueue is empty.
   bool IsEmpty() const;
diff --git a/base/task/thread_pool/priority_queue_unittest.cc b/base/task/thread_pool/priority_queue_unittest.cc
index 1ddf981..ace537b 100644
--- a/base/task/thread_pool/priority_queue_unittest.cc
+++ b/base/task/thread_pool/priority_queue_unittest.cc
@@ -59,19 +59,19 @@
 
   scoped_refptr<TaskSource> sequence_a =
       MakeSequenceWithTraitsAndTask(TaskTraits(TaskPriority::USER_VISIBLE));
-  TaskSourceSortKey sort_key_a = sequence_a->BeginTransaction().GetSortKey();
+  TaskSourceSortKey sort_key_a = sequence_a->GetSortKey();
 
   scoped_refptr<TaskSource> sequence_b =
       MakeSequenceWithTraitsAndTask(TaskTraits(TaskPriority::USER_BLOCKING));
-  TaskSourceSortKey sort_key_b = sequence_b->BeginTransaction().GetSortKey();
+  TaskSourceSortKey sort_key_b = sequence_b->GetSortKey();
 
   scoped_refptr<TaskSource> sequence_c =
       MakeSequenceWithTraitsAndTask(TaskTraits(TaskPriority::USER_BLOCKING));
-  TaskSourceSortKey sort_key_c = sequence_c->BeginTransaction().GetSortKey();
+  TaskSourceSortKey sort_key_c = sequence_c->GetSortKey();
 
   scoped_refptr<TaskSource> sequence_d =
       MakeSequenceWithTraitsAndTask(TaskTraits(TaskPriority::BEST_EFFORT));
-  TaskSourceSortKey sort_key_d = sequence_d->BeginTransaction().GetSortKey();
+  TaskSourceSortKey sort_key_d = sequence_d->GetSortKey();
 
   PriorityQueue pq;
 };
@@ -193,7 +193,7 @@
     auto sequence_b_transaction = sequence_b->BeginTransaction();
     sequence_b_transaction.UpdatePriority(TaskPriority::BEST_EFFORT);
 
-    pq.UpdateSortKey(std::move(sequence_b_transaction));
+    pq.UpdateSortKey(*sequence_b, sequence_b->GetSortKey());
     EXPECT_EQ(sort_key_c, pq.PeekSortKey());
     ExpectNumSequences(2U, 1U, 1U);
   }
@@ -205,7 +205,7 @@
     auto sequence_c_transaction = sequence_c->BeginTransaction();
     sequence_c_transaction.UpdatePriority(TaskPriority::USER_BLOCKING);
 
-    pq.UpdateSortKey(std::move(sequence_c_transaction));
+    pq.UpdateSortKey(*sequence_c, sequence_c->GetSortKey());
     ExpectNumSequences(2U, 1U, 1U);
 
     // Note: |sequence_c| is popped for comparison as |sort_key_c| becomes
@@ -222,7 +222,7 @@
     auto sequence_d_and_transaction = sequence_d->BeginTransaction();
     sequence_d_and_transaction.UpdatePriority(TaskPriority::USER_BLOCKING);
 
-    pq.UpdateSortKey(std::move(sequence_d_and_transaction));
+    pq.UpdateSortKey(*sequence_d, sequence_d->GetSortKey());
     ExpectNumSequences(1U, 1U, 1U);
 
     // Note: |sequence_d| is popped for comparison as |sort_key_d| becomes
@@ -235,7 +235,7 @@
   }
 
   {
-    pq.UpdateSortKey(sequence_d->BeginTransaction());
+    pq.UpdateSortKey(*sequence_d, sequence_d->GetSortKey());
     ExpectNumSequences(1U, 1U, 0U);
     EXPECT_EQ(sequence_a, pq.PopTaskSource().Unregister());
     ExpectNumSequences(1U, 0U, 0U);
@@ -245,7 +245,7 @@
 
   {
     // No-op if UpdateSortKey() is called on an empty PriorityQueue.
-    pq.UpdateSortKey(sequence_b->BeginTransaction());
+    pq.UpdateSortKey(*sequence_b, sequence_b->GetSortKey());
     EXPECT_TRUE(pq.IsEmpty());
     ExpectNumSequences(0U, 0U, 0U);
   }
diff --git a/base/task/thread_pool/sequence.cc b/base/task/thread_pool/sequence.cc
index 8388c38..ae5cab7 100644
--- a/base/task/thread_pool/sequence.cc
+++ b/base/task/thread_pool/sequence.cc
@@ -49,6 +49,8 @@
                                         std::move(task.task))
                   : std::move(task.task);
 
+  if (sequence()->queue_.empty())
+    sequence()->ready_time_.store(task.queue_time, std::memory_order_relaxed);
   sequence()->queue_.push(std::move(task));
 
   // AddRef() matched by manual Release() when the sequence has no more tasks
@@ -83,6 +85,9 @@
 
   auto next_task = std::move(queue_.front());
   queue_.pop();
+  if (!queue_.empty()) {
+    ready_time_.store(queue_.front().queue_time, std::memory_order_relaxed);
+  }
   return next_task;
 }
 
@@ -104,8 +109,8 @@
 }
 
 TaskSourceSortKey Sequence::GetSortKey() const {
-  DCHECK(!queue_.empty());
-  return TaskSourceSortKey(traits_.priority(), queue_.front().queue_time);
+  return TaskSourceSortKey(priority_racy(),
+                           ready_time_.load(std::memory_order_relaxed));
 }
 
 Task Sequence::Clear(TaskSource::Transaction* transaction) {
diff --git a/base/task/thread_pool/sequence.h b/base/task/thread_pool/sequence.h
index ace78e1..9e81424 100644
--- a/base/task/thread_pool/sequence.h
+++ b/base/task/thread_pool/sequence.h
@@ -86,6 +86,7 @@
   // TaskSource:
   ExecutionEnvironment GetExecutionEnvironment() override;
   size_t GetRemainingConcurrency() const override;
+  TaskSourceSortKey GetSortKey() const override;
 
   // Returns a token that uniquely identifies this Sequence.
   const SequenceToken& token() const { return token_; }
@@ -102,7 +103,6 @@
   Task TakeTask(TaskSource::Transaction* transaction) override;
   Task Clear(TaskSource::Transaction* transaction) override;
   bool DidProcessTask(TaskSource::Transaction* transaction) override;
-  TaskSourceSortKey GetSortKey() const override;
 
   // Releases reference to TaskRunner.
   void ReleaseTaskRunner();
@@ -112,6 +112,8 @@
   // Queue of tasks to execute.
   base::queue<Task> queue_;
 
+  std::atomic<TimeTicks> ready_time_{TimeTicks()};
+
   // True if a worker is currently associated with a Task from this Sequence.
   bool has_worker_ = false;
 
diff --git a/base/task/thread_pool/sequence_unittest.cc b/base/task/thread_pool/sequence_unittest.cc
index 39ebfc4..b5f1e8241 100644
--- a/base/task/thread_pool/sequence_unittest.cc
+++ b/base/task/thread_pool/sequence_unittest.cc
@@ -128,7 +128,7 @@
 
   // Get the sort key.
   const TaskSourceSortKey best_effort_sort_key =
-      best_effort_sequence_transaction.GetSortKey();
+      best_effort_sequence->GetSortKey();
 
   // Take the task from the sequence, so that its sequenced time is available
   // for the check below.
@@ -141,7 +141,7 @@
   // Verify the sort key.
   EXPECT_EQ(TaskPriority::BEST_EFFORT, best_effort_sort_key.priority());
   EXPECT_EQ(take_best_effort_task.queue_time,
-            best_effort_sort_key.next_task_sequenced_time());
+            best_effort_sort_key.ready_time());
 
   // DidProcessTask for correctness.
   best_effort_registered_task_source.DidProcessTask(
@@ -162,7 +162,7 @@
 
   // Get the sort key.
   const TaskSourceSortKey foreground_sort_key =
-      foreground_sequence_transaction.GetSortKey();
+      foreground_sequence->GetSortKey();
 
   // Take the task from the sequence, so that its sequenced time is available
   // for the check below.
@@ -174,8 +174,7 @@
 
   // Verify the sort key.
   EXPECT_EQ(TaskPriority::USER_VISIBLE, foreground_sort_key.priority());
-  EXPECT_EQ(take_foreground_task.queue_time,
-            foreground_sort_key.next_task_sequenced_time());
+  EXPECT_EQ(take_foreground_task.queue_time, foreground_sort_key.ready_time());
 
   // DidProcessTask for correctness.
   foreground_registered_task_source.DidProcessTask(
diff --git a/base/task/thread_pool/task_source.cc b/base/task/thread_pool/task_source.cc
index e5d2f907..1b08a321 100644
--- a/base/task/thread_pool/task_source.cc
+++ b/base/task/thread_pool/task_source.cc
@@ -32,10 +32,6 @@
   }
 }
 
-TaskSourceSortKey TaskSource::Transaction::GetSortKey() const {
-  return task_source_->GetSortKey();
-}
-
 void TaskSource::Transaction::UpdatePriority(TaskPriority priority) {
   if (FeatureList::IsEnabled(kAllTasksUserBlocking))
     return;
diff --git a/base/task/thread_pool/task_source.h b/base/task/thread_pool/task_source.h
index 6368e7c9..3c14131 100644
--- a/base/task/thread_pool/task_source.h
+++ b/base/task/thread_pool/task_source.h
@@ -105,10 +105,6 @@
 
     operator bool() const { return !!task_source_; }
 
-    // Returns a TaskSourceSortKey representing the priority of the TaskSource.
-    // Cannot be called on an empty TaskSource.
-    TaskSourceSortKey GetSortKey() const;
-
     // Sets TaskSource priority to |priority|.
     void UpdatePriority(TaskPriority priority);
 
@@ -148,6 +144,9 @@
   // are needed. This may be called on an empty task source.
   virtual size_t GetRemainingConcurrency() const = 0;
 
+  // Returns a TaskSourceSortKey representing the priority of the TaskSource.
+  virtual TaskSourceSortKey GetSortKey() const = 0;
+
   // Support for IntrusiveHeap.
   void SetHeapHandle(const HeapHandle& handle);
   void ClearHeapHandle();
@@ -193,8 +192,6 @@
   // are concurrently ready.
   virtual Task Clear(TaskSource::Transaction* transaction) = 0;
 
-  virtual TaskSourceSortKey GetSortKey() const = 0;
-
   // Sets TaskSource priority to |priority|.
   void UpdatePriority(TaskPriority priority);
 
diff --git a/base/task/thread_pool/task_source_sort_key.cc b/base/task/thread_pool/task_source_sort_key.cc
index 3885b8b..e334401c 100644
--- a/base/task/thread_pool/task_source_sort_key.cc
+++ b/base/task/thread_pool/task_source_sort_key.cc
@@ -7,14 +7,20 @@
 namespace base {
 namespace internal {
 
+static_assert(sizeof(TaskSourceSortKey) <= 2 * sizeof(uint64_t),
+              "Members in TaskSourceSortKey should be ordered to be compact.");
+
 TaskSourceSortKey::TaskSourceSortKey(TaskPriority priority,
-                                     TimeTicks next_task_sequenced_time)
+                                     TimeTicks ready_time,
+                                     uint8_t worker_count)
     : priority_(priority),
-      next_task_sequenced_time_(next_task_sequenced_time) {}
+      worker_count_(worker_count),
+      ready_time_(ready_time) {}
 
 bool TaskSourceSortKey::operator<=(const TaskSourceSortKey& other) const {
   // This TaskSourceSortKey is considered more important than |other| if it has
-  // a higher priority or if it has the same priority but its next task was
+  // a higher priority or if it has the same priority but fewer workers, or if
+  // it has the same priority and same worker count but its next task was
   // posted sooner than |other|'s.
   const int priority_diff =
       static_cast<int>(priority_) - static_cast<int>(other.priority_);
@@ -22,7 +28,11 @@
     return true;
   if (priority_diff < 0)
     return false;
-  return next_task_sequenced_time_ <= other.next_task_sequenced_time_;
+  if (worker_count_ < other.worker_count_)
+    return true;
+  if (worker_count_ > other.worker_count_)
+    return false;
+  return ready_time_ <= other.ready_time_;
 }
 
 }  // namespace internal
diff --git a/base/task/thread_pool/task_source_sort_key.h b/base/task/thread_pool/task_source_sort_key.h
index 32be1c8c..44092fb 100644
--- a/base/task/thread_pool/task_source_sort_key.h
+++ b/base/task/thread_pool/task_source_sort_key.h
@@ -16,19 +16,21 @@
 class BASE_EXPORT TaskSourceSortKey final {
  public:
   TaskSourceSortKey() = default;
-  TaskSourceSortKey(TaskPriority priority, TimeTicks next_task_sequenced_time);
+  TaskSourceSortKey(TaskPriority priority,
+                    TimeTicks ready_time,
+                    uint8_t worker_count = 0);
 
   TaskPriority priority() const { return priority_; }
-  TimeTicks next_task_sequenced_time() const {
-    return next_task_sequenced_time_;
-  }
+  uint8_t worker_count() const { return worker_count_; }
+  TimeTicks ready_time() const { return ready_time_; }
 
   // Lower sort key means more important.
   bool operator<=(const TaskSourceSortKey& other) const;
 
   bool operator==(const TaskSourceSortKey& other) const {
     return priority_ == other.priority_ &&
-           next_task_sequenced_time_ == other.next_task_sequenced_time_;
+           worker_count_ == other.worker_count_ &&
+           ready_time_ == other.ready_time_;
   }
   bool operator!=(const TaskSourceSortKey& other) const {
     return !(other == *this);
@@ -42,9 +44,13 @@
   // created.
   TaskPriority priority_;
 
-  // Sequenced time of the next task to run in the sequence at the time this
-  // sort key was created.
-  TimeTicks next_task_sequenced_time_;
+  // Number of workers running the task source, used as secondary sort key
+  // prioritizing task sources with fewer workers.
+  uint8_t worker_count_;
+
+  // Time since the task source has been ready to run upcoming work, used as
+  // secondary sort key after |worker_count| prioritizing older task sources.
+  TimeTicks ready_time_;
 };
 
 }  // namespace internal
diff --git a/base/task/thread_pool/task_source_sort_key_unittest.cc b/base/task/thread_pool/task_source_sort_key_unittest.cc
index be78aad3..84c4d5f 100644
--- a/base/task/thread_pool/task_source_sort_key_unittest.cc
+++ b/base/task/thread_pool/task_source_sort_key_unittest.cc
@@ -20,9 +20,13 @@
                           TimeTicks::FromInternalValue(1000));
   TaskSourceSortKey key_d(TaskPriority::USER_VISIBLE,
                           TimeTicks::FromInternalValue(2000));
-  TaskSourceSortKey key_e(TaskPriority::BEST_EFFORT,
+  TaskSourceSortKey key_e(TaskPriority::USER_VISIBLE,
+                          TimeTicks::FromInternalValue(1000), 1);
+  TaskSourceSortKey key_f(TaskPriority::USER_VISIBLE,
+                          TimeTicks::FromInternalValue(2000), 1);
+  TaskSourceSortKey key_g(TaskPriority::BEST_EFFORT,
                           TimeTicks::FromInternalValue(1000));
-  TaskSourceSortKey key_f(TaskPriority::BEST_EFFORT,
+  TaskSourceSortKey key_h(TaskPriority::BEST_EFFORT,
                           TimeTicks::FromInternalValue(2000));
 
   EXPECT_LE(key_a, key_a);
@@ -31,6 +35,8 @@
   EXPECT_FALSE(key_d <= key_a);
   EXPECT_FALSE(key_e <= key_a);
   EXPECT_FALSE(key_f <= key_a);
+  EXPECT_FALSE(key_g <= key_a);
+  EXPECT_FALSE(key_h <= key_a);
 
   EXPECT_LE(key_a, key_b);
   EXPECT_LE(key_b, key_b);
@@ -38,6 +44,8 @@
   EXPECT_FALSE(key_d <= key_b);
   EXPECT_FALSE(key_e <= key_b);
   EXPECT_FALSE(key_f <= key_b);
+  EXPECT_FALSE(key_g <= key_b);
+  EXPECT_FALSE(key_h <= key_b);
 
   EXPECT_LE(key_a, key_c);
   EXPECT_LE(key_b, key_c);
@@ -45,6 +53,8 @@
   EXPECT_FALSE(key_d <= key_c);
   EXPECT_FALSE(key_e <= key_c);
   EXPECT_FALSE(key_f <= key_c);
+  EXPECT_FALSE(key_g <= key_c);
+  EXPECT_FALSE(key_h <= key_c);
 
   EXPECT_LE(key_a, key_d);
   EXPECT_LE(key_b, key_d);
@@ -52,6 +62,8 @@
   EXPECT_LE(key_d, key_d);
   EXPECT_FALSE(key_e <= key_d);
   EXPECT_FALSE(key_f <= key_d);
+  EXPECT_FALSE(key_g <= key_d);
+  EXPECT_FALSE(key_h <= key_d);
 
   EXPECT_LE(key_a, key_e);
   EXPECT_LE(key_b, key_e);
@@ -59,6 +71,8 @@
   EXPECT_LE(key_d, key_e);
   EXPECT_LE(key_e, key_e);
   EXPECT_FALSE(key_f <= key_e);
+  EXPECT_FALSE(key_g <= key_e);
+  EXPECT_FALSE(key_h <= key_e);
 
   EXPECT_LE(key_a, key_f);
   EXPECT_LE(key_b, key_f);
@@ -66,6 +80,26 @@
   EXPECT_LE(key_d, key_f);
   EXPECT_LE(key_e, key_f);
   EXPECT_LE(key_f, key_f);
+  EXPECT_FALSE(key_g <= key_f);
+  EXPECT_FALSE(key_h <= key_f);
+
+  EXPECT_LE(key_a, key_g);
+  EXPECT_LE(key_b, key_g);
+  EXPECT_LE(key_c, key_g);
+  EXPECT_LE(key_d, key_g);
+  EXPECT_LE(key_e, key_g);
+  EXPECT_LE(key_f, key_g);
+  EXPECT_LE(key_g, key_g);
+  EXPECT_FALSE(key_h <= key_g);
+
+  EXPECT_LE(key_a, key_h);
+  EXPECT_LE(key_b, key_h);
+  EXPECT_LE(key_c, key_h);
+  EXPECT_LE(key_d, key_h);
+  EXPECT_LE(key_e, key_h);
+  EXPECT_LE(key_f, key_h);
+  EXPECT_LE(key_g, key_h);
+  EXPECT_LE(key_h, key_h);
 }
 
 TEST(TaskSourceSortKeyTest, OperatorEqual) {
@@ -77,9 +111,13 @@
                           TimeTicks::FromInternalValue(1000));
   TaskSourceSortKey key_d(TaskPriority::USER_VISIBLE,
                           TimeTicks::FromInternalValue(2000));
-  TaskSourceSortKey key_e(TaskPriority::BEST_EFFORT,
+  TaskSourceSortKey key_e(TaskPriority::USER_VISIBLE,
+                          TimeTicks::FromInternalValue(1000), 1);
+  TaskSourceSortKey key_f(TaskPriority::USER_VISIBLE,
+                          TimeTicks::FromInternalValue(2000), 1);
+  TaskSourceSortKey key_g(TaskPriority::BEST_EFFORT,
                           TimeTicks::FromInternalValue(1000));
-  TaskSourceSortKey key_f(TaskPriority::BEST_EFFORT,
+  TaskSourceSortKey key_h(TaskPriority::BEST_EFFORT,
                           TimeTicks::FromInternalValue(2000));
 
   EXPECT_EQ(key_a, key_a);
@@ -88,6 +126,8 @@
   EXPECT_FALSE(key_d == key_a);
   EXPECT_FALSE(key_e == key_a);
   EXPECT_FALSE(key_f == key_a);
+  EXPECT_FALSE(key_g == key_a);
+  EXPECT_FALSE(key_h == key_a);
 
   EXPECT_FALSE(key_a == key_b);
   EXPECT_EQ(key_b, key_b);
@@ -95,6 +135,8 @@
   EXPECT_FALSE(key_d == key_b);
   EXPECT_FALSE(key_e == key_b);
   EXPECT_FALSE(key_f == key_b);
+  EXPECT_FALSE(key_g == key_b);
+  EXPECT_FALSE(key_h == key_b);
 
   EXPECT_FALSE(key_a == key_c);
   EXPECT_FALSE(key_b == key_c);
@@ -102,6 +144,8 @@
   EXPECT_FALSE(key_d == key_c);
   EXPECT_FALSE(key_e == key_c);
   EXPECT_FALSE(key_f == key_c);
+  EXPECT_FALSE(key_g == key_c);
+  EXPECT_FALSE(key_h == key_c);
 
   EXPECT_FALSE(key_a == key_d);
   EXPECT_FALSE(key_b == key_d);
@@ -109,6 +153,8 @@
   EXPECT_EQ(key_d, key_d);
   EXPECT_FALSE(key_e == key_d);
   EXPECT_FALSE(key_f == key_d);
+  EXPECT_FALSE(key_g == key_d);
+  EXPECT_FALSE(key_h == key_d);
 
   EXPECT_FALSE(key_a == key_e);
   EXPECT_FALSE(key_b == key_e);
@@ -116,6 +162,8 @@
   EXPECT_FALSE(key_d == key_e);
   EXPECT_EQ(key_e, key_e);
   EXPECT_FALSE(key_f == key_e);
+  EXPECT_FALSE(key_g == key_e);
+  EXPECT_FALSE(key_h == key_e);
 
   EXPECT_FALSE(key_a == key_f);
   EXPECT_FALSE(key_b == key_f);
@@ -123,6 +171,26 @@
   EXPECT_FALSE(key_d == key_f);
   EXPECT_FALSE(key_e == key_f);
   EXPECT_EQ(key_f, key_f);
+  EXPECT_FALSE(key_g == key_f);
+  EXPECT_FALSE(key_h == key_f);
+
+  EXPECT_FALSE(key_a == key_g);
+  EXPECT_FALSE(key_b == key_g);
+  EXPECT_FALSE(key_c == key_g);
+  EXPECT_FALSE(key_d == key_g);
+  EXPECT_FALSE(key_e == key_g);
+  EXPECT_FALSE(key_f == key_g);
+  EXPECT_EQ(key_g, key_g);
+  EXPECT_FALSE(key_h == key_g);
+
+  EXPECT_FALSE(key_a == key_h);
+  EXPECT_FALSE(key_b == key_h);
+  EXPECT_FALSE(key_c == key_h);
+  EXPECT_FALSE(key_d == key_h);
+  EXPECT_FALSE(key_e == key_h);
+  EXPECT_FALSE(key_f == key_h);
+  EXPECT_FALSE(key_g == key_h);
+  EXPECT_EQ(key_h, key_h);
 }
 
 TEST(TaskSourceSortKeyTest, OperatorNotEqual) {
@@ -134,9 +202,13 @@
                           TimeTicks::FromInternalValue(1000));
   TaskSourceSortKey key_d(TaskPriority::USER_VISIBLE,
                           TimeTicks::FromInternalValue(2000));
-  TaskSourceSortKey key_e(TaskPriority::BEST_EFFORT,
+  TaskSourceSortKey key_e(TaskPriority::USER_VISIBLE,
+                          TimeTicks::FromInternalValue(1000), 1);
+  TaskSourceSortKey key_f(TaskPriority::USER_VISIBLE,
+                          TimeTicks::FromInternalValue(2000), 1);
+  TaskSourceSortKey key_g(TaskPriority::BEST_EFFORT,
                           TimeTicks::FromInternalValue(1000));
-  TaskSourceSortKey key_f(TaskPriority::BEST_EFFORT,
+  TaskSourceSortKey key_h(TaskPriority::BEST_EFFORT,
                           TimeTicks::FromInternalValue(2000));
 
   EXPECT_FALSE(key_a != key_a);
@@ -145,6 +217,8 @@
   EXPECT_NE(key_d, key_a);
   EXPECT_NE(key_e, key_a);
   EXPECT_NE(key_f, key_a);
+  EXPECT_NE(key_g, key_a);
+  EXPECT_NE(key_h, key_a);
 
   EXPECT_NE(key_a, key_b);
   EXPECT_FALSE(key_b != key_b);
@@ -152,6 +226,8 @@
   EXPECT_NE(key_d, key_b);
   EXPECT_NE(key_e, key_b);
   EXPECT_NE(key_f, key_b);
+  EXPECT_NE(key_g, key_b);
+  EXPECT_NE(key_h, key_b);
 
   EXPECT_NE(key_a, key_c);
   EXPECT_NE(key_b, key_c);
@@ -159,6 +235,8 @@
   EXPECT_NE(key_d, key_c);
   EXPECT_NE(key_e, key_c);
   EXPECT_NE(key_f, key_c);
+  EXPECT_NE(key_g, key_c);
+  EXPECT_NE(key_h, key_c);
 
   EXPECT_NE(key_a, key_d);
   EXPECT_NE(key_b, key_d);
@@ -166,6 +244,8 @@
   EXPECT_FALSE(key_d != key_d);
   EXPECT_NE(key_e, key_d);
   EXPECT_NE(key_f, key_d);
+  EXPECT_NE(key_g, key_d);
+  EXPECT_NE(key_h, key_d);
 
   EXPECT_NE(key_a, key_e);
   EXPECT_NE(key_b, key_e);
@@ -173,6 +253,8 @@
   EXPECT_NE(key_d, key_e);
   EXPECT_FALSE(key_e != key_e);
   EXPECT_NE(key_f, key_e);
+  EXPECT_NE(key_g, key_e);
+  EXPECT_NE(key_h, key_e);
 
   EXPECT_NE(key_a, key_f);
   EXPECT_NE(key_b, key_f);
@@ -180,6 +262,26 @@
   EXPECT_NE(key_d, key_f);
   EXPECT_NE(key_e, key_f);
   EXPECT_FALSE(key_f != key_f);
+  EXPECT_NE(key_g, key_f);
+  EXPECT_NE(key_h, key_f);
+
+  EXPECT_NE(key_a, key_g);
+  EXPECT_NE(key_b, key_g);
+  EXPECT_NE(key_c, key_g);
+  EXPECT_NE(key_d, key_g);
+  EXPECT_NE(key_e, key_g);
+  EXPECT_NE(key_f, key_g);
+  EXPECT_FALSE(key_g != key_g);
+  EXPECT_NE(key_h, key_g);
+
+  EXPECT_NE(key_a, key_h);
+  EXPECT_NE(key_b, key_h);
+  EXPECT_NE(key_c, key_h);
+  EXPECT_NE(key_d, key_h);
+  EXPECT_NE(key_e, key_h);
+  EXPECT_NE(key_f, key_h);
+  EXPECT_NE(key_g, key_h);
+  EXPECT_FALSE(key_h != key_h);
 }
 
 }  // namespace internal
diff --git a/base/task/thread_pool/thread_group.cc b/base/task/thread_pool/thread_group.cc
index f8573dd..69f2412 100644
--- a/base/task/thread_pool/thread_group.cc
+++ b/base/task/thread_pool/thread_group.cc
@@ -194,7 +194,7 @@
   // If the TaskSource isn't saturated, check whether TaskTracker allows it to
   // remain in the PriorityQueue.
   // The canonical way of doing this is to pop the task source to return, call
-  // WillQueueTaskSource() to get an additional RegisteredTaskSource, and
+  // RegisterTaskSource() to get an additional RegisteredTaskSource, and
   // reenqueue that task source if valid. Instead, it is cheaper and equivalent
   // to peek the task source, call RegisterTaskSource() to get an additional
   // RegisteredTaskSource to replace if valid, and only pop |priority_queue_|
@@ -203,14 +203,17 @@
       task_tracker_->RegisterTaskSource(priority_queue_.PeekTaskSource().get());
   if (!task_source)
     return priority_queue_.PopTaskSource();
-  return std::exchange(priority_queue_.PeekTaskSource(),
-                       std::move(task_source));
+  // Replace the top task_source and then update the queue.
+  std::swap(priority_queue_.PeekTaskSource(), task_source);
+  priority_queue_.UpdateSortKey(*task_source.get(), task_source->GetSortKey());
+  return task_source;
 }
 
 void ThreadGroup::UpdateSortKeyImpl(BaseScopedCommandsExecutor* executor,
                                     TaskSource::Transaction transaction) {
   CheckedAutoLock auto_lock(lock_);
-  priority_queue_.UpdateSortKey(std::move(transaction));
+  priority_queue_.UpdateSortKey(*transaction.task_source(),
+                                transaction.task_source()->GetSortKey());
   EnsureEnoughWorkersLockRequired(executor);
 }
 
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java b/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
index aa2edbe..17425613 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
@@ -44,7 +44,7 @@
      * Unit tests must be careful not to persist any changes to global state, or flakes are likely
      * to occur.
      *
-     * An exception to this is loading Chrome's native library (eg. using NativeLibraryTestRule).
+     * An exception to this is loading Chrome's native library (eg. using NativeLibraryTestUtils).
      * Your unit tests must assume that the native library may have already been loaded by another
      * test.
      */
diff --git a/base/util/memory_pressure/system_memory_pressure_evaluator.cc b/base/util/memory_pressure/system_memory_pressure_evaluator.cc
index babb4f8..4a9963b 100644
--- a/base/util/memory_pressure/system_memory_pressure_evaluator.cc
+++ b/base/util/memory_pressure/system_memory_pressure_evaluator.cc
@@ -46,7 +46,7 @@
   }
   return evaluator;
 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  return std::make_unique<util::linux::SystemMemoryPressureEvaluator>(
+  return std::make_unique<util::os_linux::SystemMemoryPressureEvaluator>(
       monitor->CreateVoter());
 #endif
   return nullptr;
diff --git a/base/util/memory_pressure/system_memory_pressure_evaluator_linux.cc b/base/util/memory_pressure/system_memory_pressure_evaluator_linux.cc
index 258d983..1878d88 100644
--- a/base/util/memory_pressure/system_memory_pressure_evaluator_linux.cc
+++ b/base/util/memory_pressure/system_memory_pressure_evaluator_linux.cc
@@ -32,7 +32,7 @@
 }  // namespace
 
 namespace util {
-namespace linux {
+namespace os_linux {
 
 const base::TimeDelta SystemMemoryPressureEvaluator::kModeratePressureCooldown =
     base::TimeDelta::FromSeconds(10);
@@ -153,5 +153,5 @@
   return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
 }
 
-}  // namespace linux
+}  // namespace os_linux
 }  // namespace util
diff --git a/base/util/memory_pressure/system_memory_pressure_evaluator_linux.h b/base/util/memory_pressure/system_memory_pressure_evaluator_linux.h
index 0f5c43d..3a24427 100644
--- a/base/util/memory_pressure/system_memory_pressure_evaluator_linux.h
+++ b/base/util/memory_pressure/system_memory_pressure_evaluator_linux.h
@@ -13,7 +13,7 @@
 #include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
 
 namespace util {
-namespace linux {
+namespace os_linux {
 
 // Linux memory pressure voter. Because there is no OS provided signal this
 // polls at a low frequency, and applies internal hysteresis.
@@ -106,7 +106,7 @@
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
-}  // namespace linux
+}  // namespace os_linux
 }  // namespace util
 
 #endif  // BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_LINUX_H_
diff --git a/base/util/memory_pressure/system_memory_pressure_evaluator_linux_unittest.cc b/base/util/memory_pressure/system_memory_pressure_evaluator_linux_unittest.cc
index e5db67e..dff871eb 100644
--- a/base/util/memory_pressure/system_memory_pressure_evaluator_linux_unittest.cc
+++ b/base/util/memory_pressure/system_memory_pressure_evaluator_linux_unittest.cc
@@ -13,7 +13,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace util {
-namespace linux {
+namespace os_linux {
 
 namespace {
 
@@ -257,5 +257,5 @@
   testing::Mock::VerifyAndClearExpectations(&evaluator);
 }
 
-}  // namespace linux
+}  // namespace os_linux
 }  // namespace util
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 85d76f8..cd97b298 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -220,10 +220,6 @@
       if (enable_jni_tracing) {
         args += [ "--enable_tracing" ]
       }
-      if (defined(invoker.feature_list_file)) {
-        file_path = rebase_path(invoker.feature_list_file, root_build_dir)
-        args += [ "--feature_list_file=$file_path" ]
-      }
     }
   }
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 16af91c..18fa20d 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200915.0.1
+0.20200915.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 16af91c..18fa20d 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200915.0.1
+0.20200915.1.1
diff --git a/build/lacros/test_runner.py b/build/lacros/test_runner.py
index 80f6ea57..1665f59 100755
--- a/build/lacros/test_runner.py
+++ b/build/lacros/test_runner.py
@@ -70,6 +70,9 @@
 _PREBUILT_ASH_CHROME_DIR = os.path.join(os.path.dirname(__file__),
                                         'prebuilt_ash_chrome')
 
+# Number of seconds to wait for ash-chrome to start.
+ASH_CHROME_TIMEOUT_SECONDS = 10
+
 # List of targets that require ash-chrome as a Wayland server in order to run.
 _TARGETS_REQUIRE_ASH_CHROME = [
     'app_shell_unittests',
@@ -227,11 +230,49 @@
       return f.read().strip()
 
 
+def _WaitForAshChromeToStart(tmp_xdg_dir, lacros_mojo_socket_file,
+                             is_lacros_chrome_browsertests):
+  """Waits for Ash-Chrome to be up and running and returns a boolean indicator.
+
+  Determine whether ash-chrome is up and running by checking whether two files
+  (lock file + socket) have been created in the |XDG_RUNTIME_DIR| and the lacros
+  mojo socket file has been created if running lacros_chrome_browsertests.
+  TODO(crbug.com/1107966): Figure out a more reliable hook to determine the
+  status of ash-chrome, likely through mojo connection.
+
+  Args:
+    tmp_xdg_dir (str): Path to the XDG_RUNTIME_DIR.
+    lacros_mojo_socket_file (str): Path to the lacros mojo socket file.
+    is_lacros_chrome_browsertests (bool): is running lacros_chrome_browsertests.
+
+  Returns:
+    A boolean indicating whether Ash-chrome is up and running.
+  """
+
+  def IsAshChromeReady(tmp_xdg_dir, lacros_mojo_socket_file,
+                       is_lacros_chrome_browsertests):
+    return (len(os.listdir(tmp_xdg_dir)) >= 2
+            and (not is_lacros_chrome_browsertests
+                 or os.path.exists(lacros_mojo_socket_file)))
+
+  time_counter = 0
+  while not IsAshChromeReady(tmp_xdg_dir, lacros_mojo_socket_file,
+                             is_lacros_chrome_browsertests):
+    time.sleep(0.5)
+    time_counter += 0.5
+    if time_counter > ASH_CHROME_TIMEOUT_SECONDS:
+      break
+
+  return IsAshChromeReady(tmp_xdg_dir, lacros_mojo_socket_file,
+                          is_lacros_chrome_browsertests)
+
+
 def _RunTestWithAshChrome(args, forward_args):
   """Runs tests with ash-chrome.
 
-  args (dict): Args for this script.
-  forward_args (dict): Args to be forwarded to the test command.
+  Args:
+    args (dict): Args for this script.
+    forward_args (dict): Args to be forwarded to the test command.
   """
   ash_chrome_version = args.ash_chrome_version or _GetLatestVersionOfAshChrome()
   _DownloadAshChromeIfNecessary(ash_chrome_version)
@@ -240,10 +281,19 @@
   ash_chrome_file = os.path.join(_GetAshChromeDirPath(ash_chrome_version),
                                  'chrome')
   try:
+    # Starts Ash-Chrome.
     tmp_xdg_dir_name = tempfile.mkdtemp()
     tmp_ash_data_dir_name = tempfile.mkdtemp()
-    ash_process = None
 
+    # Please refer to below file for how mojo connection is set up in testing.
+    # //chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h
+    lacros_mojo_socket_file = '%s/lacros.sock' % tmp_ash_data_dir_name
+    lacros_mojo_socket_arg = ('--lacros-mojo-socket-for-testing=%s' %
+                              lacros_mojo_socket_file)
+    is_lacros_chrome_browsertests = (os.path.basename(
+        args.command) == 'lacros_chrome_browsertests')
+
+    ash_process = None
     ash_env = os.environ.copy()
     ash_env['XDG_RUNTIME_DIR'] = tmp_xdg_dir_name
     ash_cmd = [
@@ -252,6 +302,8 @@
         '--enable-wayland-server',
         '--no-startup-window',
     ]
+    if is_lacros_chrome_browsertests:
+      ash_cmd.append(lacros_mojo_socket_arg)
 
     ash_process_has_started = False
     total_tries = 3
@@ -259,24 +311,14 @@
     while not ash_process_has_started and num_tries < total_tries:
       num_tries += 1
       ash_process = subprocess.Popen(ash_cmd, env=ash_env)
-
-      # Determine whether ash-chrome is up and running by checking whether two
-      # files (lock file + socket) have been created in the |XDG_RUNTIME_DIR|.
-      # TODO(crbug.com/1107966): Figure out a more reliable hook to determine
-      # the status of ash-chrome, likely through mojo connection.
-      time_to_wait = 10
-      time_counter = 0
-      while len(os.listdir(tmp_xdg_dir_name)) < 2:
-        time.sleep(0.5)
-        time_counter += 0.5
-        if time_counter > time_to_wait:
-          break
-
-      if len(os.listdir(tmp_xdg_dir_name)) >= 2:
-        ash_process_has_started = True
+      ash_process_has_started = _WaitForAshChromeToStart(
+          tmp_xdg_dir_name, lacros_mojo_socket_file,
+          is_lacros_chrome_browsertests)
+      if ash_process_has_started:
         break
 
-      logging.warning('Starting ash-chrome timed out after %ds', time_to_wait)
+      logging.warning('Starting ash-chrome timed out after %ds',
+                      ASH_CHROME_TIMEOUT_SECONDS)
       logging.warning('Printing the output of "ps aux" for debugging:')
       subprocess.call(['ps', 'aux'])
       if ash_process and ash_process.poll() is None:
@@ -285,6 +327,26 @@
     if not ash_process_has_started:
       raise RuntimeError('Timed out waiting for ash-chrome to start')
 
+    # Starts tests.
+    if is_lacros_chrome_browsertests:
+      forward_args.append(lacros_mojo_socket_arg)
+
+      reason_of_jobs_1 = (
+          'multiple clients crosapi is not supported yet (crbug.com/1124490), '
+          'lacros_chrome_browsertests has to run tests serially')
+
+      if any('--test-launcher-jobs' in arg for arg in forward_args):
+        raise RuntimeError(
+            'Specifying "--test-launcher-jobs" is not allowed because %s. '
+            'Please remove it and this script will automatically append '
+            '"--test-launcher-jobs=1"' % reason_of_jobs_1)
+
+      # TODO(crbug.com/1124490): Run lacros_chrome_browsertests in parallel once
+      # the bug is fixed.
+      logging.warning('Appending "--test-launcher-jobs=1" because %s',
+                      reason_of_jobs_1)
+      forward_args.append('--test-launcher-jobs=1')
+
     test_env = os.environ.copy()
     test_env['EGL_PLATFORM'] = 'surfaceless'
     test_env['XDG_RUNTIME_DIR'] = tmp_xdg_dir_name
diff --git a/build/lacros/test_runner_test.py b/build/lacros/test_runner_test.py
index 7e59fc67..4685d8d 100755
--- a/build/lacros/test_runner_test.py
+++ b/build/lacros/test_runner_test.py
@@ -59,6 +59,7 @@
       'browser_tests',
       'components_browsertests',
       'content_browsertests',
+      'lacros_chrome_browsertests',
   ])
   @mock.patch.object(os,
                      'listdir',
@@ -67,6 +68,7 @@
                      'mkdtemp',
                      side_effect=['/tmp/xdg', '/tmp/ash-data'])
   @mock.patch.object(os.environ, 'copy', side_effect=[{}, {}])
+  @mock.patch.object(os.path, 'exists', return_value=True)
   @mock.patch.object(os.path, 'isfile', return_value=True)
   @mock.patch.object(test_runner,
                      '_GetLatestVersionOfAshChrome',
@@ -85,15 +87,28 @@
       ash_chrome_args = mock_popen.call_args_list[0][0][0]
       self.assertTrue(ash_chrome_args[0].endswith(
           'build/lacros/prebuilt_ash_chrome/793554/chrome'))
-      self.assertListEqual([
-          '--user-data-dir=/tmp/ash-data', '--enable-wayland-server',
-          '--no-startup-window'
-      ], ash_chrome_args[1:])
+      expected_ash_chrome_args = [
+          '--user-data-dir=/tmp/ash-data',
+          '--enable-wayland-server',
+          '--no-startup-window',
+      ]
+      if command == 'lacros_chrome_browsertests':
+        expected_ash_chrome_args.append(
+            '--lacros-mojo-socket-for-testing=/tmp/ash-data/lacros.sock')
+      self.assertListEqual(expected_ash_chrome_args, ash_chrome_args[1:])
       ash_chrome_env = mock_popen.call_args_list[0][1].get('env', {})
       self.assertDictEqual({'XDG_RUNTIME_DIR': '/tmp/xdg'}, ash_chrome_env)
 
       test_args = mock_popen.call_args_list[1][0][0]
-      self.assertListEqual([command], test_args)
+      if command == 'lacros_chrome_browsertests':
+        self.assertListEqual([
+            command,
+            '--lacros-mojo-socket-for-testing=/tmp/ash-data/lacros.sock',
+            '--test-launcher-jobs=1'
+        ], test_args)
+      else:
+        self.assertListEqual([command], test_args)
+
       test_env = mock_popen.call_args_list[1][1].get('env', {})
       self.assertDictEqual(
           {
@@ -101,9 +116,11 @@
               'EGL_PLATFORM': 'surfaceless'
           }, test_env)
 
+
   @mock.patch.object(os,
                      'listdir',
                      return_value=['wayland-0', 'wayland-0.lock'])
+  @mock.patch.object(os.path, 'exists', return_value=True)
   @mock.patch.object(os.path, 'isfile', return_value=True)
   @mock.patch.object(test_runner,
                      '_GetLatestVersionOfAshChrome',
@@ -123,6 +140,7 @@
   @mock.patch.object(os,
                      'listdir',
                      return_value=['wayland-0', 'wayland-0.lock'])
+  @mock.patch.object(os.path, 'exists', return_value=True)
   @mock.patch.object(os.path, 'isfile', return_value=True)
   @mock.patch.object(test_runner, '_DownloadAshChromeIfNecessary')
   @mock.patch.object(subprocess, 'Popen', return_value=mock.Mock())
diff --git a/build/toolchain/gcc_solink_wrapper.py b/build/toolchain/gcc_solink_wrapper.py
index b86cf3f..c62a24e 100755
--- a/build/toolchain/gcc_solink_wrapper.py
+++ b/build/toolchain/gcc_solink_wrapper.py
@@ -124,15 +124,6 @@
   collect_inputs_only = InterceptFlag('--collect-inputs-only', args.command)
   generate_dwp = InterceptFlag('--generate-dwp', args.command)
 
-  # First, run the actual link.
-  command = wrapper_utils.CommandToRun(args.command)
-  result = wrapper_utils.RunLinkWithOptionalMapFile(command,
-                                                    env=fast_env,
-                                                    map_file=args.map_file)
-
-  if result != 0:
-    return result
-
   # If only linking, we are likely generating a partitioned .so that will be
   # split apart later. In that case:
   #
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index a5641c1..d11cb60f 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -14,6 +14,8 @@
 #include <set>
 #include <utility>
 
+#include "base/logging.h"
+
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/numerics/ranges.h"
@@ -810,20 +812,15 @@
   }
 }
 
-bool PictureLayerImpl::UpdateCanUseLCDText(
+void PictureLayerImpl::UpdateCanUseLCDText(
     bool raster_translation_aligns_pixels) {
   // If we have pending/active trees, the active tree doesn't update lcd text
   // status but copies it from the pending tree.
   if (!layer_tree_impl()->IsSyncTree())
-    return false;
+    return;
 
-  auto new_lcd_text_disallowed_reason =
+  lcd_text_disallowed_reason_ =
       ComputeLCDTextDisallowedReason(raster_translation_aligns_pixels);
-  if (lcd_text_disallowed_reason_ == new_lcd_text_disallowed_reason)
-    return false;
-
-  lcd_text_disallowed_reason_ = new_lcd_text_disallowed_reason;
-  return true;
 }
 
 bool PictureLayerImpl::HasWillChangeTransformHint() const {
@@ -1200,6 +1197,9 @@
   // Also avoid re-rasterization during pinch-zoom.
   if (layer_tree_impl()->PinchGestureActive())
     return false;
+  // Keep the current LCD text and raster translation if there is no text.
+  if (lcd_text_disallowed_reason_ == LCDTextDisallowedReason::kNoText)
+    return false;
   return true;
 }
 
@@ -1211,11 +1211,12 @@
   gfx::Vector2dF raster_translation;
   bool raster_translation_aligns_pixels =
       CalculateRasterTranslation(raster_translation);
-  bool can_use_lcd_text_changed =
-      UpdateCanUseLCDText(raster_translation_aligns_pixels);
+  UpdateCanUseLCDText(raster_translation_aligns_pixels);
   if (high_res) {
     bool raster_translation_is_not_ideal =
         high_res->raster_transform().translation() != raster_translation;
+    bool can_use_lcd_text_changed =
+        high_res->can_use_lcd_text() != can_use_lcd_text();
     bool should_recreate_high_res =
         (raster_translation_is_not_ideal || can_use_lcd_text_changed) &&
         CanRecreateHighResTilingForLCDTextAndRasterTranslation(*high_res);
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 935281ee..ea8bcab 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -222,8 +222,7 @@
 
   LCDTextDisallowedReason ComputeLCDTextDisallowedReason(
       bool raster_translation_aligns_pixels) const;
-  // Returns true if the LCD state changed.
-  bool UpdateCanUseLCDText(bool raster_translation_aligns_pixels);
+  void UpdateCanUseLCDText(bool raster_translation_aligns_pixels);
 
   // TODO(crbug.com/1114504): For now this checks the immediate transform node
   // only. The callers may actually want to know if this layer or ancestor has
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index b59d5c98..781ad45 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -5066,8 +5066,20 @@
   for (Tile* tile : pending_layer()->HighResTiling()->AllTilesForTesting())
     EXPECT_FALSE(tile->can_use_lcd_text());
 
+  // Change of the specific LCD text disallowed reason should not invalidate
+  // tilings.
   pending_layer()->SetContentsOpaque(true);
-  pending_layer()->UpdateTiles();
+  FilterOperations blur_filter;
+  blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f));
+  SetFilter(pending_layer(), blur_filter);
+  UpdateDrawProperties(host_impl()->pending_tree());
+  EXPECT_FALSE(pending_layer()->can_use_lcd_text());
+  EXPECT_TRUE(pending_layer()->HighResTiling()->has_tiles());
+  for (Tile* tile : pending_layer()->HighResTiling()->AllTilesForTesting())
+    EXPECT_FALSE(tile->can_use_lcd_text());
+
+  SetFilter(pending_layer(), FilterOperations());
+  UpdateDrawProperties(host_impl()->pending_tree());
   EXPECT_TRUE(pending_layer()->can_use_lcd_text());
   EXPECT_TRUE(pending_layer()->HighResTiling()->has_tiles());
   for (Tile* tile : pending_layer()->HighResTiling()->AllTilesForTesting())
diff --git a/cc/metrics/frame_sequence_metrics.cc b/cc/metrics/frame_sequence_metrics.cc
index 0a688f5..67d182b 100644
--- a/cc/metrics/frame_sequence_metrics.cc
+++ b/cc/metrics/frame_sequence_metrics.cc
@@ -124,7 +124,6 @@
   scrolling_thread_ = scrolling_thread;
 
   DCHECK(!jank_reporter_);
-  DCHECK_NE(scrolling_thread, ThreadType::kSlower);
   DCHECK_NE(scrolling_thread, ThreadType::kUnknown);
   jank_reporter_ = std::make_unique<JankMetrics>(type_, scrolling_thread);
 }
@@ -232,21 +231,6 @@
       GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_),
       main_throughput_);
 
-  // Report for the 'slower thread' for the metrics where it makes sense.
-  bool should_report_slower_thread = IsInteractionType(type_);
-  base::Optional<int> aggregated_throughput_percent;
-  if (should_report_slower_thread) {
-    aggregated_throughput_percent = ThroughputData::ReportHistogram(
-        this, ThreadType::kSlower,
-        GetIndexForMetric(FrameSequenceMetrics::ThreadType::kSlower, type_),
-        aggregated_throughput_);
-    if (aggregated_throughput_percent.has_value() && throughput_ukm_reporter_) {
-      throughput_ukm_reporter_->ReportThroughputUkm(
-          aggregated_throughput_percent, impl_throughput_percent,
-          main_throughput_percent, type_);
-    }
-  }
-
   // Report for the 'scrolling thread' for the scrolling interactions.
   if (scrolling_thread_ != ThreadType::kUnknown) {
     base::Optional<int> scrolling_thread_throughput;
@@ -257,7 +241,6 @@
       case ThreadType::kMain:
         scrolling_thread_throughput = main_throughput_percent;
         break;
-      case ThreadType::kSlower:
       case ThreadType::kUnknown:
         NOTREACHED();
         break;
diff --git a/cc/metrics/frame_sequence_metrics.h b/cc/metrics/frame_sequence_metrics.h
index 323dec4..17a8285 100644
--- a/cc/metrics/frame_sequence_metrics.h
+++ b/cc/metrics/frame_sequence_metrics.h
@@ -41,7 +41,7 @@
   FrameSequenceMetrics(const FrameSequenceMetrics&) = delete;
   FrameSequenceMetrics& operator=(const FrameSequenceMetrics&) = delete;
 
-  enum class ThreadType { kMain, kCompositor, kSlower, kUnknown };
+  enum class ThreadType { kMain, kCompositor, kUnknown };
 
   struct ThroughputData {
     static std::unique_ptr<base::trace_event::TracedValue> ToTracedValue(
diff --git a/cc/metrics/frame_sequence_tracker_collection.cc b/cc/metrics/frame_sequence_tracker_collection.cc
index 1519c45..a30d44b 100644
--- a/cc/metrics/frame_sequence_tracker_collection.cc
+++ b/cc/metrics/frame_sequence_tracker_collection.cc
@@ -67,22 +67,22 @@
     metrics->SetScrollingThread(scrolling_thread);
   }
 
-    if (metrics->GetEffectiveThread() == ThreadType::kCompositor) {
-      if (compositor_frame_reporting_controller_ &&
-          compositor_thread_driving_smoothness_ == 0) {
-        compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-            ThreadType::kCompositor, true);
-      }
-      ++compositor_thread_driving_smoothness_;
-    } else {
-      DCHECK_EQ(metrics->GetEffectiveThread(), ThreadType::kMain);
-      if (compositor_frame_reporting_controller_ &&
-          main_thread_driving_smoothness_ == 0) {
-        compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-            ThreadType::kMain, true);
-      }
-      ++main_thread_driving_smoothness_;
+  if (metrics->GetEffectiveThread() == ThreadType::kCompositor) {
+    if (compositor_frame_reporting_controller_ &&
+        compositor_thread_driving_smoothness_ == 0) {
+      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
+          ThreadType::kCompositor, true);
     }
+    ++compositor_thread_driving_smoothness_;
+  } else {
+    DCHECK_EQ(metrics->GetEffectiveThread(), ThreadType::kMain);
+    if (compositor_frame_reporting_controller_ &&
+        main_thread_driving_smoothness_ == 0) {
+      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
+          ThreadType::kMain, true);
+    }
+    ++main_thread_driving_smoothness_;
+  }
   return frame_trackers_[key].get();
 }
 
@@ -131,23 +131,23 @@
         tracker->type());
   }
 
-    if (tracker->metrics()->GetEffectiveThread() == ThreadType::kCompositor) {
-      DCHECK_GT(compositor_thread_driving_smoothness_, 0u);
-      --compositor_thread_driving_smoothness_;
-      if (compositor_frame_reporting_controller_ &&
-          compositor_thread_driving_smoothness_ == 0) {
-        compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-            ThreadType::kCompositor, false);
-      }
-    } else {
-      DCHECK_GT(main_thread_driving_smoothness_, 0u);
-      --main_thread_driving_smoothness_;
-      if (compositor_frame_reporting_controller_ &&
-          main_thread_driving_smoothness_ == 0) {
-        compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-            ThreadType::kMain, false);
-      }
+  if (tracker->metrics()->GetEffectiveThread() == ThreadType::kCompositor) {
+    DCHECK_GT(compositor_thread_driving_smoothness_, 0u);
+    --compositor_thread_driving_smoothness_;
+    if (compositor_frame_reporting_controller_ &&
+        compositor_thread_driving_smoothness_ == 0) {
+      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
+          ThreadType::kCompositor, false);
     }
+  } else {
+    DCHECK_GT(main_thread_driving_smoothness_, 0u);
+    --main_thread_driving_smoothness_;
+    if (compositor_frame_reporting_controller_ &&
+        main_thread_driving_smoothness_ == 0) {
+      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
+          ThreadType::kMain, false);
+    }
+  }
 
   frame_trackers_.erase(key);
   tracker->ScheduleTerminate();
diff --git a/cc/metrics/throughput_ukm_reporter.cc b/cc/metrics/throughput_ukm_reporter.cc
index e384da5..b89a100 100644
--- a/cc/metrics/throughput_ukm_reporter.cc
+++ b/cc/metrics/throughput_ukm_reporter.cc
@@ -45,9 +45,6 @@
                                         FrameSequenceMetrics::ThreadType::kMain,
                                         main_throughput_percent.value());
     }
-    ukm_manager_->RecordThroughputUKM(type,
-                                      FrameSequenceMetrics::ThreadType::kSlower,
-                                      slower_throughput_percent.value());
   }
   DCHECK_GT(samples_to_next_event_[static_cast<int>(type)], 0u);
   samples_to_next_event_[static_cast<int>(type)]--;
diff --git a/cc/test/test_layer_tree_host_base.cc b/cc/test/test_layer_tree_host_base.cc
index 1067606..96b1b7a 100644
--- a/cc/test/test_layer_tree_host_base.cc
+++ b/cc/test/test_layer_tree_host_base.cc
@@ -75,9 +75,9 @@
 
 void TestLayerTreeHostBase::SetupDefaultTrees(const gfx::Size& layer_bounds) {
   scoped_refptr<FakeRasterSource> pending_raster_source =
-      FakeRasterSource::CreateFilled(layer_bounds);
+      FakeRasterSource::CreateFilledWithText(layer_bounds);
   scoped_refptr<FakeRasterSource> active_raster_source =
-      FakeRasterSource::CreateFilled(layer_bounds);
+      FakeRasterSource::CreateFilledWithText(layer_bounds);
 
   SetupTrees(std::move(pending_raster_source), std::move(active_raster_source));
 }
diff --git a/cc/tiles/picture_layer_tiling.cc b/cc/tiles/picture_layer_tiling.cc
index a887995c..25c0e843 100644
--- a/cc/tiles/picture_layer_tiling.cc
+++ b/cc/tiles/picture_layer_tiling.cc
@@ -345,6 +345,14 @@
   if (!TilingMatchesTileIndices(active_twin))
     return true;
 
+  // If our settings don't match the active twin, it means that the active
+  // tiles will all be removed when we activate. So we need all the tiles on the
+  // pending tree to be created. See
+  // PictureLayerTilingSet::CopyTilingsAndPropertiesFromPendingTwin.
+  if (can_use_lcd_text() != active_twin->can_use_lcd_text() ||
+      raster_transform() != active_twin->raster_transform())
+    return true;
+
   // If the active tree can't create a tile, because of its raster source, then
   // the pending tree should create one.
   if (!active_twin->raster_source()->CoversRect(info.enclosing_layer_rect,
diff --git a/cc/tiles/picture_layer_tiling_set_unittest.cc b/cc/tiles/picture_layer_tiling_set_unittest.cc
index 833fd681..5313a1b9 100644
--- a/cc/tiles/picture_layer_tiling_set_unittest.cc
+++ b/cc/tiles/picture_layer_tiling_set_unittest.cc
@@ -1106,5 +1106,58 @@
   EXPECT_EQ(1u, active_set->tiling_at(0)->AllTilesForTesting().size());
 }
 
+TEST(PictureLayerTilingSetTest, LcdChanges) {
+  gfx::Size tile_size(64, 64);
+  FakePictureLayerTilingClient pending_client;
+  FakePictureLayerTilingClient active_client;
+  pending_client.SetTileSize(tile_size);
+  active_client.SetTileSize(tile_size);
+  std::unique_ptr<PictureLayerTilingSet> pending_set =
+      PictureLayerTilingSet::Create(PENDING_TREE, &pending_client, 0, 1.f, 0,
+                                    0.f);
+  std::unique_ptr<PictureLayerTilingSet> active_set =
+      PictureLayerTilingSet::Create(ACTIVE_TREE, &active_client, 0, 1.f, 0,
+                                    0.f);
+  active_client.set_twin_tiling_set(pending_set.get());
+  pending_client.set_twin_tiling_set(active_set.get());
+
+  gfx::Size layer_bounds(100, 100);
+  scoped_refptr<FakeRasterSource> raster_source =
+      FakeRasterSource::CreateFilled(layer_bounds);
+
+  const bool with_lcd_text = true;
+  const bool without_lcd_text = false;
+
+  gfx::AxisTransform2d raster_transform(1.f, gfx::Vector2dF());
+  pending_set->AddTiling(raster_transform, raster_source, with_lcd_text);
+  pending_set->tiling_at(0)->set_resolution(HIGH_RESOLUTION);
+
+  // Set a priority rect so we get tiles.
+  pending_set->UpdateTilePriorities(gfx::Rect(layer_bounds), 1.f, 1.0,
+                                    Occlusion(), false);
+
+  // Make sure all tiles are generated.
+  EXPECT_EQ(4u, pending_set->tiling_at(0)->AllTilesForTesting().size());
+
+  // Clone from the pending to the active tree.
+  active_set->UpdateTilingsToCurrentRasterSourceForActivation(
+      raster_source.get(), pending_set.get(), Region(), 1.f, 1.f);
+
+  // Verifies active tree cloned the tiling correctly.
+  ASSERT_EQ(1u, active_set->num_tilings());
+  EXPECT_EQ(4u, active_set->tiling_at(0)->AllTilesForTesting().size());
+
+  // Change LCD state on the pending tree
+  pending_set->RemoveAllTilings();
+  pending_set->AddTiling(raster_transform, raster_source, without_lcd_text);
+  pending_set->tiling_at(0)->set_resolution(HIGH_RESOLUTION);
+
+  // Set a priority rect so we get tiles.
+  pending_set->UpdateTilePriorities(gfx::Rect(layer_bounds), 1.f, 1.0,
+                                    Occlusion(), false);
+  // We should have created all tiles because lcd state changed.
+  EXPECT_EQ(4u, pending_set->tiling_at(0)->AllTilesForTesting().size());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/tiles/tile_manager_unittest.cc b/cc/tiles/tile_manager_unittest.cc
index 13703e7..48f5c11 100644
--- a/cc/tiles/tile_manager_unittest.cc
+++ b/cc/tiles/tile_manager_unittest.cc
@@ -843,7 +843,7 @@
   host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
 
   scoped_refptr<FakeRasterSource> pending_raster_source =
-      FakeRasterSource::CreateFilled(layer_bounds);
+      FakeRasterSource::CreateFilledWithText(layer_bounds);
   SetupPendingTree(pending_raster_source);
 
   auto* pending_child_layer = AddLayer<FakePictureLayerImpl>(
diff --git a/cc/trees/ukm_manager.cc b/cc/trees/ukm_manager.cc
index a41e299a..348e4b6 100644
--- a/cc/trees/ukm_manager.cc
+++ b/cc/trees/ukm_manager.cc
@@ -147,28 +147,7 @@
       break;
     }
 
-    case FrameSequenceMetrics::ThreadType::kSlower: {
-      switch (tracker_type) {
-#define CASE_FOR_SLOWER_THREAD_TRACKER(name)    \
-  case FrameSequenceTrackerType::k##name:       \
-    builder.SetSlowerThread_##name(throughput); \
-    break;
-        CASE_FOR_SLOWER_THREAD_TRACKER(CompositorAnimation);
-        CASE_FOR_SLOWER_THREAD_TRACKER(MainThreadAnimation);
-        CASE_FOR_SLOWER_THREAD_TRACKER(PinchZoom);
-        CASE_FOR_SLOWER_THREAD_TRACKER(RAF);
-        CASE_FOR_SLOWER_THREAD_TRACKER(ScrollbarScroll);
-        CASE_FOR_SLOWER_THREAD_TRACKER(TouchScroll);
-        CASE_FOR_SLOWER_THREAD_TRACKER(Video);
-        CASE_FOR_SLOWER_THREAD_TRACKER(WheelScroll);
-#undef CASE_FOR_SLOWER_THREAD_TRACKER
-        default:
-          NOTREACHED();
-          break;
-      }
-      break;
-    }
-    default:
+    case FrameSequenceMetrics::ThreadType::kUnknown:
       NOTREACHED();
       break;
   }
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 597ba0d..34c5a046 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -184,6 +184,7 @@
     "//components/autofill/android:autofill_java_resources",
     "//components/blocked_content/android:java_resources",
     "//components/browser_ui/android/bottomsheet:java_resources",
+    "//components/browser_ui/contacts_picker/android:java_resources",
     "//components/browser_ui/http_auth/android:java_resources",
     "//components/browser_ui/modaldialog/android:java_resources",
     "//components/browser_ui/settings/android:java_resources",
@@ -770,6 +771,7 @@
     "//chrome/browser/browser_controls/android:junit",
     "//chrome/browser/download/android:java",
     "//chrome/browser/download/android:junit_tests",
+    "//chrome/browser/endpoint_fetcher:java",
     "//chrome/browser/enterprise/util:java",
     "//chrome/browser/flags:flags_junit_tests",
     "//chrome/browser/flags:java",
@@ -1125,6 +1127,7 @@
     "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
     "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/android_deps:androidx_viewpager_viewpager_java",
+    "//url:gurl_javatests",
     "//url:origin_java",
 
     # TODO (bjoyce): Remove recyclerview_v7 when espresso tests are migrated
@@ -1893,6 +1896,7 @@
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
     "//third_party/junit",
+    "//url:gurl_android_test_helper_java",
   ]
 }
 
@@ -1915,6 +1919,7 @@
     "//components/query_tiles/test:test_support",
     "//content/test:test_support",
     "//net:test_support",
+    "//url:gurl_android_test_helper",
   ]
 }
 
@@ -2945,8 +2950,6 @@
     "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java",
     "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java",
     "java/src/org/chromium/chrome/browser/complex_tasks/TaskTabHelper.java",
-    "java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointFetcher.java",
-    "java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointResponse.java",
     "java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java",
     "java/src/org/chromium/chrome/browser/compositor/CompositorView.java",
     "java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java",
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index a12cad4c..0382272 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -149,6 +149,7 @@
   "java/res/drawable-ldrtl-mdpi-v17/btn_toolbar_reload.png",
   "java/res/drawable-ldrtl-mdpi-v17/ic_share_white_24dp.png",
   "java/res/drawable-ldrtl-mdpi-v17/ic_suggestion_magnifier.png",
+  "java/res/drawable-ldrtl-sw600dp-night-xhdpi/toolbar_background.9.png",
   "java/res/drawable-ldrtl-sw600dp-xhdpi-v17/toolbar_background.9.png",
   "java/res/drawable-ldrtl-v17/btn_back.xml",
   "java/res/drawable-ldrtl-v17/btn_forward.xml",
@@ -289,6 +290,7 @@
   "java/res/drawable-nodpi/widget_preview.png",
   "java/res/drawable-sw600dp-hdpi/google_logo.png",
   "java/res/drawable-sw600dp-mdpi/google_logo.png",
+  "java/res/drawable-sw600dp-night-xhdpi/toolbar_background.9.png",
   "java/res/drawable-sw600dp-xhdpi/google_logo.png",
   "java/res/drawable-sw600dp-xhdpi/toolbar_background.9.png",
   "java/res/drawable-sw600dp-xxhdpi/google_logo.png",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 30fe463..7341810 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -237,8 +237,6 @@
   "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java",
   "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java",
   "java/src/org/chromium/chrome/browser/complex_tasks/TaskTabHelper.java",
-  "java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointFetcher.java",
-  "java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointResponse.java",
   "java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java",
   "java/src/org/chromium/chrome/browser/component_updater/UpdateTask.java",
   "java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java",
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml
index e238d6b..7d2baca 100644
--- a/chrome/android/expectations/lint-baseline.xml
+++ b/chrome/android/expectations/lint-baseline.xml
@@ -466,39 +466,6 @@
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="        boolean isRouteInRecord = mRoutes.containsKey(routeId);"
-        errorLine2="                                  ~~~~~~~">
-        <location
-            file="../../chrome/android/features/media_router/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java"
-            line="86"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        if (!mRoutes.containsKey(routeId)) {"
-        errorLine2="             ~~~~~~~">
-        <location
-            file="../../chrome/android/features/media_router/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java"
-            line="105"
-            column="14"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        if (!mRoutes.containsKey(routeId)) return null;"
-        errorLine2="             ~~~~~~~">
-        <location
-            file="../../chrome/android/features/media_router/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafRemotingMediaRouteProvider.java"
-            line="66"
-            column="14"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="        mPartnerBrowserRefreshNeeded = !PartnerBrowserCustomizations.getInstance().isInitialized();"
         errorLine2="                                                                                   ~~~~~~~~~~~~~">
         <location
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
index 7a72412..a7dbdc2c 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
@@ -335,15 +335,16 @@
                     mPropertyModel.get(FEED_SURFACE_COORDINATOR);
             if (feedSurfaceCoordinator != null) {
                 Stream feedStream = feedSurfaceCoordinator.getStream();
-                assert feedStream != null;
-                feedStream.addOnContentChangedListener(() -> {
-                    int firstCardDensity = feedStream.getFirstCardDensity();
-                    if (firstCardDensity != Stream.FeedFirstCardDensity.UNKNOWN) {
-                        StartSurfaceConfiguration.setFeedPlaceholderDense(
-                                feedStream.getFirstCardDensity()
-                                == Stream.FeedFirstCardDensity.DENSE);
-                    }
-                });
+                if (feedStream != null) {
+                    feedStream.addOnContentChangedListener(() -> {
+                        int firstCardDensity = feedStream.getFirstCardDensity();
+                        if (firstCardDensity != Stream.FeedFirstCardDensity.UNKNOWN) {
+                            StartSurfaceConfiguration.setFeedPlaceholderDense(
+                                    feedStream.getFirstCardDensity()
+                                    == Stream.FeedFirstCardDensity.DENSE);
+                        }
+                    });
+                }
             }
         }
 
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 188e7a86..08ca38f 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -183,6 +183,7 @@
     "//chrome/app:java_strings_grd",
     "//chrome/browser/android/lifecycle:java",
     "//chrome/browser/browser_controls/android:java",
+    "//chrome/browser/endpoint_fetcher:java",
     "//chrome/browser/flags:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/share:java",
diff --git a/chrome/android/features/tab_ui/DEPS b/chrome/android/features/tab_ui/DEPS
index 95b36d9..42cf86e 100644
--- a/chrome/android/features/tab_ui/DEPS
+++ b/chrome/android/features/tab_ui/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
  "+chrome/browser/android/lifecycle",
+ "+chrome/browser/endpoint_fetcher",
  "+chrome/browser/profiles/android/java",
  "+chrome/browser/tab/java",
  "+chrome/browser/tabmodel/android/java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TrendyTermsCache.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TrendyTermsCache.java
index 22bb7cc..4828225 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TrendyTermsCache.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TrendyTermsCache.java
@@ -18,8 +18,8 @@
 import org.chromium.base.Log;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointFetcherJni;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointResponse;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointResponse;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsServerFetcher.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsServerFetcher.java
index 746d313e..26db9fdb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsServerFetcher.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsServerFetcher.java
@@ -12,8 +12,8 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointFetcher;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointResponse;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointResponse;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/TrendyTermsCacheUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/TrendyTermsCacheUnitTest.java
index 6af52d5..870608b 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/TrendyTermsCacheUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/TrendyTermsCacheUnitTest.java
@@ -29,9 +29,9 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointFetcher;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointFetcherJni;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointResponse;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointResponse;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
 import org.chromium.chrome.test.util.browser.Features;
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsServerFetcherUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsServerFetcherUnitTest.java
index af23024..60270cf 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsServerFetcherUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsServerFetcherUnitTest.java
@@ -29,9 +29,9 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointFetcher;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointFetcherJni;
-import org.chromium.chrome.browser.complex_tasks.endpoint_fetcher.EndpointResponse;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointResponse;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 
diff --git a/chrome/android/java/res/drawable-ldrtl-sw600dp-night-xhdpi/toolbar_background.9.png b/chrome/android/java/res/drawable-ldrtl-sw600dp-night-xhdpi/toolbar_background.9.png
new file mode 100644
index 0000000..30e3a72
--- /dev/null
+++ b/chrome/android/java/res/drawable-ldrtl-sw600dp-night-xhdpi/toolbar_background.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-sw600dp-night-xhdpi/toolbar_background.9.png b/chrome/android/java/res/drawable-sw600dp-night-xhdpi/toolbar_background.9.png
new file mode 100644
index 0000000..1dc1d27
--- /dev/null
+++ b/chrome/android/java/res/drawable-sw600dp-night-xhdpi/toolbar_background.9.png
Binary files differ
diff --git a/chrome/android/java/res/layout/custom_tabs_toolbar.xml b/chrome/android/java/res/layout/custom_tabs_toolbar.xml
index a3b998b..36f574ae 100644
--- a/chrome/android/java/res/layout/custom_tabs_toolbar.xml
+++ b/chrome/android/java/res/layout/custom_tabs_toolbar.xml
@@ -103,9 +103,6 @@
         android:layout_gravity="center_vertical|end"
         android:layout_width="42dp"
         android:paddingEnd="4dp"
-        android:clickable="true"
-        android:focusable="true"
-        android:contentDescription="@string/accessibility_toolbar_btn_menu"
         android:id="@+id/menu_button_wrapper">
 
         <org.chromium.ui.widget.ChromeImageButton
@@ -113,7 +110,9 @@
             style="@style/ToolbarButton"
             android:src="@drawable/ic_more_vert_24dp"
             android:layout_gravity="center"
-            android:importantForAccessibility="no"
+            android:clickable="true"
+            android:focusable="true"
+            android:contentDescription="@string/accessibility_toolbar_btn_menu"
             android:background="@null"
             app:tint="@color/default_icon_color_tint_list" />
 
diff --git a/chrome/android/java/res/layout/new_tab_page_feed_v2_expandable_header.xml b/chrome/android/java/res/layout/new_tab_page_feed_v2_expandable_header.xml
index b359ad77..0e6b49c 100644
--- a/chrome/android/java/res/layout/new_tab_page_feed_v2_expandable_header.xml
+++ b/chrome/android/java/res/layout/new_tab_page_feed_v2_expandable_header.xml
@@ -40,5 +40,5 @@
         android:src="@drawable/ic_settings_gear_24dp"
         android:contentDescription="@string/accessibility_ntp_feed_menu_button"
         app:menuMaxWidth="@dimen/feed_header_menu_max_width"
-        app:tint="@color/default_icon_color_tint_list" />
+        app:tint="@null" />
 </org.chromium.chrome.browser.ntp.snippets.SectionHeaderView>
diff --git a/chrome/android/java/res/layout/new_tab_page_snippets_expandable_header_with_menu.xml b/chrome/android/java/res/layout/new_tab_page_snippets_expandable_header_with_menu.xml
index 68b777e..94aa3d4 100644
--- a/chrome/android/java/res/layout/new_tab_page_snippets_expandable_header_with_menu.xml
+++ b/chrome/android/java/res/layout/new_tab_page_snippets_expandable_header_with_menu.xml
@@ -37,5 +37,5 @@
         android:src="@drawable/ic_settings_gear_24dp"
         android:contentDescription="@string/accessibility_ntp_feed_menu_button"
         app:menuMaxWidth="@dimen/feed_header_menu_max_width"
-        app:tint="@color/default_icon_color_tint_list" />
+        app:tint="@null" />
 </org.chromium.chrome.browser.ntp.snippets.SectionHeaderView>
diff --git a/chrome/android/java/res/layout/toolbar_tablet.xml b/chrome/android/java/res/layout/toolbar_tablet.xml
index b754783..2fd51c5a 100644
--- a/chrome/android/java/res/layout/toolbar_tablet.xml
+++ b/chrome/android/java/res/layout/toolbar_tablet.xml
@@ -14,7 +14,7 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/toolbar_height_no_shadow"
     android:layout_marginTop="@dimen/tab_strip_height"
-    android:background="@color/default_bg_color"
+    android:background="@color/toolbar_background_primary"
     android:paddingStart="@dimen/tablet_toolbar_start_padding" >
 
     <LinearLayout
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index baf7b75..3a70fe204 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -441,9 +441,6 @@
     <dimen name="custom_tabs_screenshot_height">300dp</dimen>
     <dimen name="custom_tabs_screenshot_width">190dp</dimen>
 
-    <!-- Contact Picker dimensions -->
-    <dimen name="contact_picker_icon_size">36dp</dimen>
-
     <!-- Account chooser dialog dimensions -->
     <dimen name="account_chooser_dialog_margin">24dp</dimen>
     <dimen name="account_chooser_dialog_item_margin">24dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ChromePickerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ChromePickerAdapter.java
index 5ab10951..e1d0332 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ChromePickerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ChromePickerAdapter.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.browser.signin.ProfileDataCache;
 import org.chromium.components.browser_ui.contacts_picker.ContactDetails;
 import org.chromium.components.browser_ui.contacts_picker.PickerAdapter;
-import org.chromium.components.browser_ui.contacts_picker.PickerCategoryView;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.base.CoreAccountInfo;
@@ -46,7 +45,10 @@
     // Whether owner info is being fetched asynchronously.
     private boolean mWaitingOnOwnerInfo;
 
-    public ChromePickerAdapter() {}
+    public ChromePickerAdapter(Context context) {
+        mProfileDataCache = new ProfileDataCache(context,
+                context.getResources().getDimensionPixelSize(R.dimen.contact_picker_icon_size));
+    }
 
     // Adapter:
 
@@ -99,13 +101,6 @@
 
     // PickerAdapter:
 
-    @Override
-    public void init(PickerCategoryView categoryView, Context context, String formattedOrigin) {
-        mProfileDataCache = new ProfileDataCache(context,
-                context.getResources().getDimensionPixelSize(R.dimen.contact_picker_icon_size));
-        super.init(categoryView, context, formattedOrigin);
-    }
-
     /**
      * Returns the email for the currently signed-in user. If that is not available, return the
      * first Google account associated with this phone instead.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
index 7c1262b0..faaaece 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
@@ -77,6 +77,7 @@
                 mView.invalidateItemDecorations();
             }
         };
+        mView.setId(R.id.download_home_recycler_view);
         mView.setHasFixedSize(true);
         ((DefaultItemAnimator) mView.getItemAnimator()).setSupportsChangeAnimations(false);
         mView.getItemAnimator().setMoveDuration(0);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index a844f2c..0b690edf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -248,10 +248,10 @@
                         boolean allowMultiple, boolean includeNames, boolean includeEmails,
                         boolean includeTel, boolean includeAddresses, boolean includeIcons,
                         String formattedOrigin) -> {
-                    ContactsPickerDialog dialog =
-                            new ContactsPickerDialog(windowAndroid, new ChromePickerAdapter(),
-                                    listener, allowMultiple, includeNames, includeEmails,
-                                    includeTel, includeAddresses, includeIcons, formattedOrigin);
+                    ContactsPickerDialog dialog = new ContactsPickerDialog(windowAndroid,
+                            new ChromePickerAdapter(windowAndroid.getContext().get()), listener,
+                            allowMultiple, includeNames, includeEmails, includeTel,
+                            includeAddresses, includeIcons, formattedOrigin);
                     dialog.getWindow().getAttributes().windowAnimations =
                             R.style.PickerDialogAnimation;
                     dialog.show();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderView.java
index 37d0dd8..cb489c13 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderView.java
@@ -140,6 +140,8 @@
                 return result;
             }
         };
+        int yInsetPx =
+                getResources().getDimensionPixelOffset(R.dimen.text_bubble_menu_anchor_y_inset);
         helper.requestShowIPH(new IPHCommandBuilder(mMenuView.getContext().getResources(),
                 FeatureConstants.FEED_HEADER_MENU_FEATURE, R.string.ntp_feed_menu_iph,
                 R.string.accessibility_ntp_feed_menu_iph)
@@ -147,7 +149,7 @@
                                       .setCircleHighlight(true)
                                       .setShouldHighlight(true)
                                       .setDismissOnTouch(false)
-                                      .setInsetRect(new Rect(0, 0, 0, 0))
+                                      .setInsetRect(new Rect(0, 0, 0, -yInsetPx))
                                       .setAutoDismissTimeout(5 * 1000)
                                       .setViewRectProvider(rectProvider)
                                       .build());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
index 97e20736..d23418f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
@@ -32,7 +32,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.signin.ProfileDataCache;
+import org.chromium.components.browser_ui.util.AvatarGenerator;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.widget.Toast;
@@ -267,7 +267,7 @@
         if (mIsDestroyed) return;
         assert index >= 0 && index < mCredentials.length;
         assert mCredentials[index] != null;
-        Drawable avatar = ProfileDataCache.makeRoundAvatar(
+        Drawable avatar = AvatarGenerator.makeRoundAvatar(
                 mContext.getResources(), avatarBitmap, avatarBitmap.getHeight());
         mCredentials[index].setAvatar(avatar);
         ListView view = mDialog.getListView();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/printing/TabPrinter.java b/chrome/android/java/src/org/chromium/chrome/browser/printing/TabPrinter.java
index f6e1419c..0bdd0e6a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/printing/TabPrinter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/printing/TabPrinter.java
@@ -55,7 +55,7 @@
     @Override
     public String getTitle() {
         Tab tab = mTab.get();
-        if (tab == null) return mDefaultTitle;
+        if (tab == null || !tab.isInitialized()) return mDefaultTitle;
 
         String title = tab.getTitle();
         if (!TextUtils.isEmpty(title)) return title;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDataCache.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDataCache.java
index e37b468..69cc4b4d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDataCache.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDataCache.java
@@ -13,9 +13,7 @@
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
-import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 
@@ -29,6 +27,7 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.R;
+import org.chromium.components.browser_ui.util.AvatarGenerator;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.ProfileDataSource;
 
@@ -272,28 +271,6 @@
     }
 
     /**
-     * Rescales avatar image and crops it into a circle.
-     */
-    public static Drawable makeRoundAvatar(Resources resources, Bitmap bitmap, int imageSize) {
-        if (bitmap == null) return null;
-
-        Bitmap output = Bitmap.createBitmap(imageSize, imageSize, Config.ARGB_8888);
-        Canvas canvas = new Canvas(output);
-        // Fill the canvas with transparent color.
-        canvas.drawColor(Color.TRANSPARENT);
-        // Draw a white circle.
-        float radius = (float) imageSize / 2;
-        Paint paint = new Paint();
-        paint.setAntiAlias(true);
-        paint.setColor(Color.WHITE);
-        canvas.drawCircle(radius, radius, radius, paint);
-        // Use SRC_IN so white circle acts as a mask while drawing the avatar.
-        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
-        canvas.drawBitmap(bitmap, null, new Rect(0, 0, imageSize, imageSize), paint);
-        return new BitmapDrawable(resources, output);
-    }
-
-    /**
      * Returns a profile data cache object without a badge.The badge is put with respect to
      * R.dimen.user_picture_size. So this method only works with the user avatar of this size.
      * @param context Context of the application to extract resources from
@@ -338,7 +315,7 @@
 
     private Drawable prepareAvatar(Bitmap bitmap) {
         Drawable croppedAvatar = bitmap != null
-                ? makeRoundAvatar(mContext.getResources(), bitmap, mImageSize)
+                ? AvatarGenerator.makeRoundAvatar(mContext.getResources(), bitmap, mImageSize)
                 : mPlaceholderImage;
         if (mBadgeConfig == null || mBadgeConfig.getBadge() == null) {
             return croppedAvatar;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
index f30a327..87899693 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
@@ -20,7 +20,6 @@
 import org.chromium.components.signin.AccountsChangeObserver;
 import org.chromium.components.signin.base.CoreAccountId;
 import org.chromium.components.signin.base.CoreAccountInfo;
-import org.chromium.components.signin.base.GoogleServiceAuthError;
 import org.chromium.components.signin.base.GoogleServiceAuthError.State;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -185,33 +184,36 @@
      * {@link AccountPickerBottomSheetProperties#ON_CONTINUE_AS_CLICKED}.
      */
     private void onContinueAsClicked() {
-        if (mSelectedAccountName == null) {
+        @ViewState
+        int viewState = mModel.get(AccountPickerBottomSheetProperties.VIEW_STATE);
+        if (viewState == ViewState.COLLAPSED_ACCOUNT_LIST
+                || viewState == ViewState.SIGNIN_GENERAL_ERROR) {
+            signIn();
+        } else if (viewState == ViewState.NO_ACCOUNTS) {
             addAccount();
-        } else {
-            mModel.set(AccountPickerBottomSheetProperties.VIEW_STATE, ViewState.SIGNIN_IN_PROGRESS);
-            new AsyncTask<String>() {
-                @Override
-                protected String doInBackground() {
-                    return mAccountManagerFacade.getAccountGaiaId(mSelectedAccountName);
-                }
-
-                @Override
-                protected void onPostExecute(String accountGaiaId) {
-                    CoreAccountInfo coreAccountInfo = new CoreAccountInfo(
-                            new CoreAccountId(accountGaiaId), mSelectedAccountName, accountGaiaId);
-                    mAccountPickerDelegate.signIn(
-                            coreAccountInfo, AccountPickerBottomSheetMediator.this::onSignInError);
-                }
-            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
         }
     }
 
-    private void onSignInError(GoogleServiceAuthError error) {
-        if (error.getState() == State.INVALID_GAIA_CREDENTIALS) {
-            mModel.set(AccountPickerBottomSheetProperties.VIEW_STATE, ViewState.SIGNIN_AUTH_ERROR);
-        } else {
-            mModel.set(
-                    AccountPickerBottomSheetProperties.VIEW_STATE, ViewState.SIGNIN_GENERAL_ERROR);
-        }
+    private void signIn() {
+        mModel.set(AccountPickerBottomSheetProperties.VIEW_STATE, ViewState.SIGNIN_IN_PROGRESS);
+        new AsyncTask<String>() {
+            @Override
+            protected String doInBackground() {
+                return mAccountManagerFacade.getAccountGaiaId(mSelectedAccountName);
+            }
+
+            @Override
+            protected void onPostExecute(String accountGaiaId) {
+                CoreAccountInfo coreAccountInfo = new CoreAccountInfo(
+                        new CoreAccountId(accountGaiaId), mSelectedAccountName, accountGaiaId);
+                mAccountPickerDelegate.signIn(coreAccountInfo, error -> {
+                    @ViewState
+                    int newViewState = error.getState() == State.INVALID_GAIA_CREDENTIALS
+                            ? ViewState.SIGNIN_AUTH_ERROR
+                            : ViewState.SIGNIN_GENERAL_ERROR;
+                    mModel.set(AccountPickerBottomSheetProperties.VIEW_STATE, newViewState);
+                });
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index e6d7af1..4412be55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -131,7 +131,7 @@
         }
 
         // TODO(yusufo) : Consider using this for all calls from getTab() for accessing url.
-        if (!hasTab()) return "";
+        if (!hasTab() || !getTab().isInitialized()) return "";
 
         // Tab.getUrl() returns empty string if it does not have a URL.
         return getTab().getUrlString().trim();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButton.java
index 45f8bfb..d4949291 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButton.java
@@ -298,6 +298,7 @@
     public void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
         mThemeColorProvider = themeColorProvider;
         mThemeColorProvider.addTintObserver(this);
+        onTintChanged(themeColorProvider.getTint(), themeColorProvider.useLight());
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
index 394bdad6..ad8386b1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
@@ -43,6 +43,7 @@
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.InfoBarUtil;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.download.DownloadState;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.DOMUtils;
@@ -54,7 +55,9 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Tests Chrome download feature by attempting to download some files.
@@ -105,6 +108,18 @@
         }
     }
 
+    static class TestDownloadInfoBarController extends DownloadInfoBarController {
+        public TestDownloadInfoBarController() {
+            super(false);
+        }
+
+        @Override
+        protected void showInfoBar(
+                @DownloadInfoBarState int state, DownloadProgressInfoBarData info) {
+            // Do nothing, so we don't impact other info bars.
+        }
+    }
+
     public DownloadTest(boolean useDownloadOfflineContentProvider) {
         mUseDownloadOfflineContentProvider = useDownloadOfflineContentProvider;
     }
@@ -131,6 +146,15 @@
         mDownloadTestRule.startMainActivityOnBlankPage();
     }
 
+    void waitForLastDownloadToFinish() {
+        CriteriaHelper.pollUiThread(() -> {
+            List<DownloadItem> downloads = mDownloadTestRule.getAllDownloads();
+            Criteria.checkThat(downloads.size(), Matchers.greaterThanOrEqualTo(1));
+            Criteria.checkThat(downloads.get(downloads.size() - 1).getDownloadInfo().state(),
+                    Matchers.is(DownloadState.COMPLETE));
+        });
+    }
+
     @Test
     @MediumTest
     @Feature({"Downloads"})
@@ -235,28 +259,39 @@
     @Test
     @MediumTest
     @Feature({"Downloads"})
-    @DisabledTest(message = "crbug.com/597230")
     public void testDuplicateHttpPostDownload_Cancel() {
+        // Remove download progress info bar.
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> DownloadManagerService.getDownloadManagerService()
+                                   .setInfoBarControllerForTesting(
+                                           new TestDownloadInfoBarController()));
+
         // Download a file.
         mDownloadTestRule.loadUrl(mTestServer.getURL(TEST_DOWNLOAD_DIRECTORY + "post.html"));
         waitForFocus();
         View currentView = mDownloadTestRule.getActivity().getActivityTab().getView();
-        int callCount = mDownloadTestRule.getChromeDownloadCallCount();
         TouchCommon.singleClickView(currentView);
-        Assert.assertTrue("Failed to finish downloading file for the first time.",
-                mDownloadTestRule.waitForChromeDownloadToFinish(callCount));
+        waitForLastDownloadToFinish();
+        int downloadCount = mDownloadTestRule.getAllDownloads().size();
 
         // Download a file with the same name.
         mDownloadTestRule.loadUrl(mTestServer.getURL(TEST_DOWNLOAD_DIRECTORY + "post.html"));
         waitForFocus();
         currentView = mDownloadTestRule.getActivity().getActivityTab().getView();
-        callCount = mDownloadTestRule.getChromeDownloadCallCount();
         TouchCommon.singleClickView(currentView);
         assertPollForInfoBarSize(1);
-        Assert.assertTrue("CREATE NEW button wasn't found",
+        Assert.assertTrue("CANCEL button wasn't found",
                 InfoBarUtil.clickSecondaryButton(mDownloadTestRule.getInfoBars().get(0)));
-        Assert.assertFalse("Download should not happen when clicking cancel button",
-                mDownloadTestRule.waitForChromeDownloadToFinish(callCount));
+        InfoBarUtil.waitUntilNoInfoBarsExist(mDownloadTestRule.getInfoBars());
+        // The download should be canceled.
+        List<DownloadItem> downloads = mDownloadTestRule.getAllDownloads();
+        Assert.assertEquals(downloads.size(), downloadCount + 1);
+        Set<Integer> states =
+                new HashSet<>(Arrays.asList(downloads.get(downloadCount).getDownloadInfo().state(),
+                        downloads.get(downloadCount - 1).getDownloadInfo().state()));
+        Assert.assertEquals(states,
+                new HashSet<>(Arrays.asList(DownloadState.COMPLETE, DownloadState.CANCELLED)));
     }
 
     @Test
@@ -291,56 +326,6 @@
                 mDownloadTestRule.hasDownload(FILENAME_TEXT_1, SUPERBO_CONTENTS));
     }
 
-    @Test
-    @MediumTest
-    @Feature({"Downloads"})
-    @DisabledTest(message = "crbug.com/597230")
-    public void testDuplicateHttpPostDownload_AllowMultipleInfoBars() throws Exception {
-        Assert.assertFalse(mDownloadTestRule.hasDownload(FILENAME_TEXT, SUPERBO_CONTENTS));
-        // Download a file.
-        mDownloadTestRule.loadUrl(mTestServer.getURL(TEST_DOWNLOAD_DIRECTORY + "post.html"));
-        waitForFocus();
-        View currentView = mDownloadTestRule.getActivity().getActivityTab().getView();
-        int callCount = mDownloadTestRule.getChromeDownloadCallCount();
-        TouchCommon.singleClickView(currentView);
-        Assert.assertTrue("Failed to finish downloading file for the first time.",
-                mDownloadTestRule.waitForChromeDownloadToFinish(callCount));
-
-        // Download the file for the second time.
-        mDownloadTestRule.loadUrl(mTestServer.getURL(TEST_DOWNLOAD_DIRECTORY + "post.html"));
-        waitForFocus();
-        currentView = mDownloadTestRule.getActivity().getActivityTab().getView();
-        TouchCommon.singleClickView(currentView);
-        assertPollForInfoBarSize(1);
-
-        // Download the file for the third time.
-        mDownloadTestRule.loadUrl(mTestServer.getURL(TEST_DOWNLOAD_DIRECTORY + "post.html"));
-        waitForFocus();
-        currentView = mDownloadTestRule.getActivity().getActivityTab().getView();
-        TouchCommon.singleClickView(currentView);
-        assertPollForInfoBarSize(2);
-
-        // Now create two new files by clicking on the infobars.
-        callCount = mDownloadTestRule.getChromeDownloadCallCount();
-        Assert.assertTrue("CREATE NEW button wasn't found",
-                InfoBarUtil.clickSecondaryButton(mDownloadTestRule.getInfoBars().get(0)));
-        Assert.assertTrue("Failed to finish downloading the second file.",
-                mDownloadTestRule.waitForChromeDownloadToFinish(callCount));
-        assertPollForInfoBarSize(1);
-        callCount = mDownloadTestRule.getChromeDownloadCallCount();
-        Assert.assertTrue("CREATE NEW button wasn't found",
-                InfoBarUtil.clickSecondaryButton(mDownloadTestRule.getInfoBars().get(0)));
-        Assert.assertTrue("Failed to finish downloading the third file.",
-                mDownloadTestRule.waitForChromeDownloadToFinish(callCount));
-
-        Assert.assertTrue("Missing first download",
-                mDownloadTestRule.hasDownload(FILENAME_TEXT, SUPERBO_CONTENTS));
-        Assert.assertTrue("Missing second download",
-                mDownloadTestRule.hasDownload(FILENAME_TEXT_1, SUPERBO_CONTENTS));
-        Assert.assertTrue("Missing third download",
-                mDownloadTestRule.hasDownload(FILENAME_TEXT_2, SUPERBO_CONTENTS));
-    }
-
     private void goToLastTab() {
         final TabModel model = mDownloadTestRule.getActivity().getCurrentTabModel();
         final int count = model.getCount();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
index 6c2e3c8..19dff4e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
@@ -51,6 +51,7 @@
     public static final long UPDATE_DELAY_MILLIS = 1000;
 
     private final CustomMainActivityStart mActivityStart;
+    private List<DownloadItem> mAllDownloads;
 
     public DownloadTestRule(CustomMainActivityStart action) {
         super(ChromeActivity.class);
@@ -186,10 +187,18 @@
         return eventReceived;
     }
 
+    public List<DownloadItem> getAllDownloads() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            DownloadManagerService.getDownloadManagerService().getAllDownloads(false);
+        });
+        return mAllDownloads;
+    }
+
     private class TestDownloadManagerServiceObserver
             implements DownloadManagerService.DownloadObserver {
         @Override
         public void onAllDownloadsRetrieved(final List<DownloadItem> list, boolean isOffTheRecord) {
+            mAllDownloads = list;
         }
 
         @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
index 4ff47c2..5966096 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
@@ -30,10 +30,15 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
+import android.view.View;
 
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 import androidx.test.espresso.action.ViewActions;
+import androidx.test.espresso.contrib.RecyclerViewActions;
+import androidx.test.espresso.matcher.BoundedMatcher;
 import androidx.test.filters.MediumTest;
 
+import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.junit.Rule;
 import org.junit.Test;
@@ -45,12 +50,12 @@
 import org.chromium.base.DiscardableReferencePool;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.task.PostTask;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.download.DownloadLaterPromptStatus;
 import org.chromium.chrome.browser.download.R;
+import org.chromium.chrome.browser.download.home.list.holder.ListItemViewHolder;
 import org.chromium.chrome.browser.download.home.rename.RenameUtils;
 import org.chromium.chrome.browser.download.home.toolbar.DownloadHomeToolbar;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
@@ -77,6 +82,7 @@
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
 import org.chromium.ui.test.util.UiRestriction;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -106,6 +112,27 @@
 
     private DiscardableReferencePool mDiscardableReferencePool;
 
+    /**
+     * Returns a Matcher to find a particular {@link ViewHolder} that contains certain text.
+     * @param text The text that the view holder has in its view hierarchy.
+     */
+    private static Matcher<ViewHolder> hasTextInViewHolder(String text) {
+        return new BoundedMatcher<ViewHolder, ListItemViewHolder>(ListItemViewHolder.class) {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("has text: " + text);
+            }
+
+            @Override
+            protected boolean matchesSafely(ListItemViewHolder listItemViewHolder) {
+                ArrayList<View> outViews = new ArrayList<>();
+                listItemViewHolder.itemView.findViewsWithText(
+                        outViews, text, View.FIND_VIEWS_WITH_TEXT);
+                return !outViews.isEmpty();
+            }
+        };
+    }
+
     @Override
     public void setUpTest() throws Exception {
         super.setUpTest();
@@ -182,7 +209,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "https://crbug.com/1039491")
     public void testLaunchingActivity() {
         TestThreadUtils.runOnUiThreadBlocking(() -> { setUpUi(); });
 
@@ -190,10 +216,15 @@
         onView(withText("Downloads")).check(matches(isDisplayed()));
 
         // Shows the list items.
-        onView(withText("page 1")).check(matches(isDisplayed()));
         onView(withText("page 2")).check(matches(isDisplayed()));
         onView(withText("page 3")).check(matches(isDisplayed()));
         onView(withText("page 4")).check(matches(isDisplayed()));
+
+        // The last item may be outside the view port, that recycler view won't create the view
+        // holder, so scroll to that view holder first.
+        onView(withId(R.id.download_home_recycler_view))
+                .perform(RecyclerViewActions.scrollToHolder(hasTextInViewHolder("page 1")));
+        onView(withText("page 1")).check(matches(isDisplayed()));
     }
 
     @Test
@@ -463,6 +494,10 @@
     }
 
     private void checkItemsDisplayed(boolean item0, boolean item1, boolean item2, boolean item3) {
+        // TODO(xingliu): Fix this properly. Some items may be outside the view port, that the
+        // recycler view won't create the view holder, and isDisplayed() will not check those items
+        // as well.
+        // See https://crbug.com/1039491.
         onView(withText("page 1")).check(item0 ? matches(isDisplayed()) : doesNotExist());
         onView(withText("page 2")).check(item1 ? matches(isDisplayed()) : doesNotExist());
         onView(withText("page 3")).check(item2 ? matches(isDisplayed()) : doesNotExist());
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index bb51b79..78ae618 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -4579,6 +4579,9 @@
   <message name="IDS_CROSTINI_TERMINAL_STATUS_INSTALL_IMAGE_LOADER" desc="Text shown in the crostini terminal when it is checking whether the virtual machine component is installed">
     Checking the virtual machine
   </message>
+  <message name="IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE" desc="Text shown in the crostini terminal when it is starting the VM controller">
+    Starting the virtual machine controller
+  </message>
   <message name="IDS_CROSTINI_TERMINAL_STATUS_CREATE_DISK_IMAGE" desc="Text shown in the crostini terminal when it is checking whether the VM image is installed">
     Checking the virtual machine image
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE.png.sha1
new file mode 100644
index 0000000..c2dd105
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE.png.sha1
@@ -0,0 +1 @@
+d5dfef986211c69d831ac4515dd9defdc772cfc3
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 765392e..84f6b77 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5646,18 +5646,34 @@
       </if>
 
       <!-- Star View menu -->
-      <message name="IDS_STAR_VIEW_MENU_ADD_BOOKMARK" desc="The item label of the menu triggered from the star icon in the location bar for adding a bookmark.">
-        Add Bookmark
-      </message>
-      <message name="IDS_STAR_VIEW_MENU_EDIT_BOOKMARK" desc="The item label of the menu triggered from the star icon in the location bar for editing a bookmark.">
-        Edit Bookmark
-      </message>
-      <message name="IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER" desc="The item label of the menu triggered from the star icon in the location bar for moving the current tab to read later.">
-        Add to Read later
-      </message>
-      <message name="IDS_STAR_VIEW_MENU_MARK_AS_READ" desc="The item label of the menu triggered from the star icon in the location bar for marking the current tab's read later entry as read.">
-        Mark as Read
-      </message>
+      <if expr="use_titlecase">
+        <message name="IDS_STAR_VIEW_MENU_ADD_BOOKMARK" desc="The item label of the menu triggered from the star icon in the location bar for adding a bookmark.">
+          Add Bookmark
+        </message>
+        <message name="IDS_STAR_VIEW_MENU_EDIT_BOOKMARK" desc="The item label of the menu triggered from the star icon in the location bar for editing a bookmark.">
+          Edit Bookmark
+        </message>
+        <message name="IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER" desc="The item label of the menu triggered from the star icon in the location bar for moving the current tab to read later.">
+          Read Later
+        </message>
+        <message name="IDS_STAR_VIEW_MENU_MARK_AS_READ" desc="The item label of the menu triggered from the star icon in the location bar for marking the current tab's read later entry as read.">
+          Mark as Read
+        </message>
+      </if>
+      <if expr="not use_titlecase">
+        <message name="IDS_STAR_VIEW_MENU_ADD_BOOKMARK" desc="The item label of the menu triggered from the star icon in the location bar for adding a bookmark.">
+          Add bookmark
+        </message>
+        <message name="IDS_STAR_VIEW_MENU_EDIT_BOOKMARK" desc="The item label of the menu triggered from the star icon in the location bar for editing a bookmark.">
+          Edit bookmark
+        </message>
+        <message name="IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER" desc="The item label of the menu triggered from the star icon in the location bar for moving the current tab to read later.">
+          Read later
+        </message>
+        <message name="IDS_STAR_VIEW_MENU_MARK_AS_READ" desc="The item label of the menu triggered from the star icon in the location bar for marking the current tab's read later entry as read.">
+          Mark as read
+        </message>
+      </if>
 
       <!--Tooltip strings-->
       <message name="IDS_TOOLTIP_BACK" desc="The tooltip for back button">
diff --git a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_ADD_BOOKMARK.png.sha1 b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_ADD_BOOKMARK.png.sha1
index 7d33700f..0e22c8a6 100644
--- a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_ADD_BOOKMARK.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_ADD_BOOKMARK.png.sha1
@@ -1 +1 @@
-cd19bd38c1c505428e1b952dff29ea2dcc23a749
\ No newline at end of file
+c0d46ea4cadbcbee88e04c014b8ea3baf7ef14f0
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_EDIT_BOOKMARK.png.sha1 b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_EDIT_BOOKMARK.png.sha1
index 5140577..2f475ca6 100644
--- a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_EDIT_BOOKMARK.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_EDIT_BOOKMARK.png.sha1
@@ -1 +1 @@
-ca95daa02a7ba6b6dcc742fa2359f58058ac8025
\ No newline at end of file
+391a21a7ca6b9cb583c18ef95d93ccd17a4ed74b
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MARK_AS_READ.png.sha1 b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MARK_AS_READ.png.sha1
index 5140577..2f475ca6 100644
--- a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MARK_AS_READ.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MARK_AS_READ.png.sha1
@@ -1 +1 @@
-ca95daa02a7ba6b6dcc742fa2359f58058ac8025
\ No newline at end of file
+391a21a7ca6b9cb583c18ef95d93ccd17a4ed74b
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER.png.sha1 b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER.png.sha1
index 7d33700f..0e22c8a6 100644
--- a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER.png.sha1
@@ -1 +1 @@
-cd19bd38c1c505428e1b952dff29ea2dcc23a749
\ No newline at end of file
+c0d46ea4cadbcbee88e04c014b8ea3baf7ef14f0
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ad74844..4f93a2c1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -282,8 +282,6 @@
     "command_updater_delegate.h",
     "command_updater_impl.cc",
     "command_updater_impl.h",
-    "complex_tasks/endpoint_fetcher/endpoint_fetcher.cc",
-    "complex_tasks/endpoint_fetcher/endpoint_fetcher.h",
     "complex_tasks/task_tab_helper.cc",
     "complex_tasks/task_tab_helper.h",
     "component_updater/autofill_states_component_installer.cc",
@@ -455,6 +453,8 @@
     "download/simple_download_manager_coordinator_factory.h",
     "download/trusted_sources_manager.cc",
     "download/trusted_sources_manager.h",
+    "endpoint_fetcher/endpoint_fetcher.cc",
+    "endpoint_fetcher/endpoint_fetcher.h",
     "engagement/important_sites_usage_counter.cc",
     "engagement/important_sites_usage_counter.h",
     "engagement/important_sites_util.cc",
@@ -2974,6 +2974,7 @@
       "//chrome/android:jni_headers",
       "//chrome/android/modules/extra_icu/provider:native",
       "//chrome/browser/android/webapk:proto",
+      "//chrome/browser/endpoint_fetcher:jni_headers",
       "//chrome/browser/flags:flags_android",
       "//chrome/browser/notifications/chime/android",
       "//chrome/browser/notifications/scheduler/public",
diff --git a/chrome/browser/android/vr/arcore_device/ar_renderer.cc b/chrome/browser/android/vr/arcore_device/ar_renderer.cc
index 660538b7..0052d93 100644
--- a/chrome/browser/android/vr/arcore_device/ar_renderer.cc
+++ b/chrome/browser/android/vr/arcore_device/ar_renderer.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/android/vr/arcore_device/ar_renderer.h"
 
 #include "base/stl_util.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 
 namespace device {
 
diff --git a/chrome/browser/android/vr/gvr_graphics_delegate.cc b/chrome/browser/android/vr/gvr_graphics_delegate.cc
index c2b8d99..e78aee9 100644
--- a/chrome/browser/android/vr/gvr_graphics_delegate.cc
+++ b/chrome/browser/android/vr/gvr_graphics_delegate.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/android/vr/gvr_util.h"
 #include "chrome/browser/vr/gl_texture_location.h"
 #include "chrome/browser/vr/vr_geometry_util.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "third_party/skia/include/core/SkImageEncoder.h"
 #include "third_party/skia/include/core/SkPixmap.h"
 #include "ui/gfx/geometry/angle_conversions.h"
diff --git a/chrome/browser/apps/app_shim/app_shim_manager_mac.cc b/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
index 56e14baa..82bd756e 100644
--- a/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
+++ b/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
@@ -357,8 +357,17 @@
 void AppShimManager::LoadAndLaunchApp(
     const web_app::AppId& app_id,
     const base::FilePath& profile_path,
-    std::vector<base::FilePath> launch_files,
+    const std::vector<base::FilePath>& launch_files,
     LoadAndLaunchAppCallback launch_callback) {
+  // Check to see if the app is already running for a profile compatible with
+  // |profile_path|. If so, early-out.
+  if (LoadAndLaunchApp_TryExistingProfileStates(
+          app_id, profile_path, launch_files, &launch_callback)) {
+    // If we used an existing profile, |launch_callback| should have been run.
+    DCHECK(!launch_callback);
+    return;
+  }
+
   // Retrieve the list of last-active profiles. If there are no last-active
   // profiles (which is rare -- e.g, when the last-active profiles were
   // removed), then use all profiles for which the app is installed.
@@ -379,10 +388,10 @@
   // Attempt load all of the profiles in |profile_paths_to_launch|, and once
   // they're loaded (or have failed to load), call
   // OnShimProcessConnectedAndProfilesToLaunchLoaded.
-  base::OnceClosure callback = base::BindOnce(
-      &AppShimManager::LoadAndLaunchApp_OnProfilesAndAppReady,
-      weak_factory_.GetWeakPtr(), app_id, std::move(launch_files),
-      profile_paths_to_launch, std::move(launch_callback));
+  base::OnceClosure callback =
+      base::BindOnce(&AppShimManager::LoadAndLaunchApp_OnProfilesAndAppReady,
+                     weak_factory_.GetWeakPtr(), app_id, launch_files,
+                     profile_paths_to_launch, std::move(launch_callback));
   {
     // This will update |callback| to be a chain of callbacks that load the
     // profiles in |profile_paths_to_load|, one by one, using
@@ -404,9 +413,53 @@
   std::move(callback).Run();
 }
 
+bool AppShimManager::LoadAndLaunchApp_TryExistingProfileStates(
+    const web_app::AppId& app_id,
+    const base::FilePath& profile_path,
+    const std::vector<base::FilePath>& launch_files,
+    LoadAndLaunchAppCallback* launch_callback) {
+  auto found_app = apps_.find(app_id);
+  if (found_app == apps_.end())
+    return false;
+  AppState* app_state = found_app->second.get();
+
+  // Search for an existing ProfileState for this app.
+  Profile* profile = nullptr;
+  ProfileState* profile_state = nullptr;
+  if (!profile_path.empty()) {
+    // If |profile_path| is populated, then only retrieve that specified
+    // profile's ProfileState.
+    profile = ProfileForPath(profile_path);
+    auto found_profile = app_state->profiles.find(profile);
+    if (found_profile == app_state->profiles.end())
+      return false;
+    profile_state = found_profile->second.get();
+  } else {
+    // If no profile was specified, select the first open profile encountered.
+    // TODO(https://crbug.com/829689): This should select the most-recently-used
+    // profile, not the first profile encountered.
+    auto it = app_state->profiles.begin();
+    if (it != app_state->profiles.end()) {
+      profile = it->first;
+      profile_state = it->second.get();
+    }
+  }
+  if (!profile_state)
+    return false;
+  DCHECK(profile);
+
+  // Launch the app, if appropriate.
+  LoadAndLaunchApp_LaunchIfAppropriate(profile, profile_state, app_id,
+                                       launch_files);
+
+  std::move(*launch_callback)
+      .Run(profile_state, chrome::mojom::AppShimLaunchResult::kSuccess);
+  return true;
+}
+
 void AppShimManager::LoadAndLaunchApp_OnProfilesAndAppReady(
     const web_app::AppId& app_id,
-    std::vector<base::FilePath> launch_files,
+    const std::vector<base::FilePath>& launch_files,
     const std::vector<base::FilePath>& profile_paths_to_launch,
     LoadAndLaunchAppCallback launch_callback) {
   // Launch all of the profiles in |profile_paths_to_launch|. Record the most
@@ -438,24 +491,9 @@
     if (delegate_->AppCanCreateHost(profile, app_id))
       profile_state = GetOrCreateProfileState(profile, app_id);
 
-    // If there exist any open window for this profile, then bring them to the
-    // front.
-    bool had_open_windows = false;
-    if (profile_state && !profile_state->browsers.empty()) {
-      for (auto* browser : profile_state->browsers) {
-        if (auto* window = browser->window()) {
-          window->Show();
-          had_open_windows = true;
-        }
-      }
-    }
-
-    // Launch the app (open a window for it) if there were no open windows for
-    // it already, or if we were asked to open files.
-    if (!had_open_windows || !launch_files.empty()) {
-      delegate_->LaunchApp(profile, app_id, launch_files);
-      launch_files.clear();
-    }
+    // Launch the app, if appropriate.
+    LoadAndLaunchApp_LaunchIfAppropriate(profile, profile_state, app_id,
+                                         launch_files);
 
     // If we successfully created a profile state, save it for |bootstrap| to
     // connect to once all launches are done.
@@ -464,6 +502,10 @@
     else
       launch_result = chrome::mojom::AppShimLaunchResult::kSuccessAndDisconnect;
 
+    // If files were specified, only open one new window.
+    if (!launch_files.empty())
+      break;
+
     // If this was the first profile in |profile_paths_to_launch|, then this
     // was the profile specified in the bootstrap, so stop here.
     if (iter == 0)
@@ -529,6 +571,33 @@
   host->OnBootstrapConnected(std::move(bootstrap));
 }
 
+void AppShimManager::LoadAndLaunchApp_LaunchIfAppropriate(
+    Profile* profile,
+    ProfileState* profile_state,
+    const web_app::AppId& app_id,
+    const std::vector<base::FilePath>& launch_files) {
+  // If |launch_files| is non-empty, then always do a launch to open the
+  // files.
+  bool do_launch = !launch_files.empty();
+
+  // Otherwise, only launch if there are no open windows.
+  if (!do_launch) {
+    bool had_windows = delegate_->ShowAppWindows(profile, app_id);
+    if (profile_state) {
+      for (auto* browser : profile_state->browsers) {
+        had_windows = true;
+        if (auto* window = browser->window())
+          window->Show();
+      }
+    }
+    if (!had_windows)
+      do_launch = true;
+  }
+
+  if (do_launch)
+    delegate_->LaunchApp(profile, app_id, launch_files);
+}
+
 // static
 AppShimManager* AppShimManager::Get() {
   // This will only return nullptr in certain unit tests that do not initialize
@@ -755,19 +824,13 @@
 }
 
 void AppShimManager::OnShimReopen(AppShimHost* host) {
-  // TODO(https://crbug.com/1080729): If the app has no windows open, this
-  // should trigger an app launch for PWAs as well.
-  if (host->UsesRemoteViews())
-    return;
-
-  // Legacy apps don't own their own windows, so when we focus the app,
-  // what we really want to do is focus the Chrome windows.
-  Profile* profile = ProfileForPath(host->GetProfilePath());
-  if (delegate_->ShowAppWindows(profile, host->GetAppId()))
-    return;
-
-  delegate_->LaunchApp(profile, host->GetAppId(),
-                       std::vector<base::FilePath>());
+  auto found_app = apps_.find(host->GetAppId());
+  DCHECK(found_app != apps_.end());
+  AppState* app_state = found_app->second.get();
+  LoadAndLaunchApp(
+      host->GetAppId(),
+      app_state->IsMultiProfile() ? base::FilePath() : host->GetProfilePath(),
+      std::vector<base::FilePath>(), base::DoNothing());
 }
 
 void AppShimManager::OnShimOpenedFiles(
@@ -776,49 +839,16 @@
   auto found_app = apps_.find(host->GetAppId());
   DCHECK(found_app != apps_.end());
   AppState* app_state = found_app->second.get();
-  Profile* profile = nullptr;
-  if (app_state->IsMultiProfile()) {
-    // TODO(https://crbug.com/829689): Open files using the most-recently-used
-    // profile. This just grabs one at random.
-    profile = app_state->profiles.begin()->first;
-  } else {
-    profile = ProfileForPath(host->GetProfilePath());
-  }
-  DCHECK(profile);
-  delegate_->LaunchApp(profile, host->GetAppId(), files);
+  LoadAndLaunchApp(
+      host->GetAppId(),
+      app_state->IsMultiProfile() ? base::FilePath() : host->GetProfilePath(),
+      files, base::DoNothing());
 }
 
 void AppShimManager::OnShimSelectedProfile(AppShimHost* host,
                                            const base::FilePath& profile_path) {
-  LoadProfileAndApp(
-      profile_path, host->GetAppId(),
-      base::BindOnce(&AppShimManager::OnShimSelectedProfileAndAppLoaded,
-                     weak_factory_.GetWeakPtr(), host->GetAppId()));
-}
-
-void AppShimManager::OnShimSelectedProfileAndAppLoaded(
-    const web_app::AppId& app_id,
-    Profile* profile) {
-  if (!delegate_->AppIsInstalled(profile, app_id))
-    return;
-
-  auto found_app = apps_.find(app_id);
-  if (found_app == apps_.end())
-    return;
-  AppState* app_state = found_app->second.get();
-  auto found_profile = app_state->profiles.find(profile);
-  if (found_profile != app_state->profiles.end()) {
-    // If this profile is currently open for the app, focus its windows.
-    ProfileState* profile_state = found_profile->second.get();
-    for (auto* browser : profile_state->browsers) {
-      if (auto* window = browser->window())
-        window->Show();
-    }
-  } else {
-    // Otherwise, launch the app for this profile (which will open a new
-    // window).
-    delegate_->LaunchApp(profile, app_id, std::vector<base::FilePath>());
-  }
+  LoadAndLaunchApp(host->GetAppId(), profile_path,
+                   std::vector<base::FilePath>(), base::DoNothing());
 }
 
 void AppShimManager::Observe(int type,
diff --git a/chrome/browser/apps/app_shim/app_shim_manager_mac.h b/chrome/browser/apps/app_shim/app_shim_manager_mac.h
index 0aac4a5..46aa7fa 100644
--- a/chrome/browser/apps/app_shim/app_shim_manager_mac.h
+++ b/chrome/browser/apps/app_shim/app_shim_manager_mac.h
@@ -245,7 +245,7 @@
   // The function LoadAndLaunchApp will:
   // - Find the appropriate profiles for which |app_id| should be launched.
   // - Load the profiles and ensure the app is enabled (using
-  //   LoadProfileAndApp).
+  //   LoadProfileAndApp), if needed.
   // - Launch the app, if appropriate.
   // The "if appropriate" above is defined as:
   // - If |launch_files| is non-empty, then will always launch the app
@@ -260,13 +260,23 @@
                               chrome::mojom::AppShimLaunchResult result)>;
   void LoadAndLaunchApp(const web_app::AppId& app_id,
                         const base::FilePath& profile_path,
-                        std::vector<base::FilePath> launch_files,
+                        const std::vector<base::FilePath>& launch_files,
                         LoadAndLaunchAppCallback launch_callback);
+  bool LoadAndLaunchApp_TryExistingProfileStates(
+      const web_app::AppId& app_id,
+      const base::FilePath& profile_path,
+      const std::vector<base::FilePath>& launch_files,
+      LoadAndLaunchAppCallback* launch_callback);
   void LoadAndLaunchApp_OnProfilesAndAppReady(
       const web_app::AppId& app_id,
-      std::vector<base::FilePath> launch_files,
+      const std::vector<base::FilePath>& launch_files,
       const std::vector<base::FilePath>& profile_paths_to_launch,
       LoadAndLaunchAppCallback launch_callback);
+  void LoadAndLaunchApp_LaunchIfAppropriate(
+      Profile* profile,
+      ProfileState* profile_state,
+      const web_app::AppId& app_id,
+      const std::vector<base::FilePath>& launch_files);
 
   // The final step of both paths for OnShimProcessConnected. This will connect
   // |bootstrap| to |profile_state|'s AppShimHost, if possible. The value of
@@ -276,10 +286,6 @@
       ProfileState* profile_state,
       chrome::mojom::AppShimLaunchResult result);
 
-  // Continuation of OnShimSelectedProfile, once the profile has loaded.
-  void OnShimSelectedProfileAndAppLoaded(const web_app::AppId& app_id,
-                                         Profile* profile);
-
   // Load the specified profile and extension, and run |callback| with
   // the result. The callback's arguments may be nullptr on failure.
   using LoadProfileAndAppCallback = base::OnceCallback<void(Profile*)>;
diff --git a/chrome/browser/apps/app_shim/app_shim_manager_mac_unittest.cc b/chrome/browser/apps/app_shim/app_shim_manager_mac_unittest.cc
index ad90487..b7d1ab3 100644
--- a/chrome/browser/apps/app_shim/app_shim_manager_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/app_shim_manager_mac_unittest.cc
@@ -933,6 +933,7 @@
 }
 
 TEST_F(AppShimManagerTest, MultiProfileSelectMenu) {
+  EXPECT_CALL(*delegate_, ShowAppWindows(_, _)).WillRepeatedly(Return(false));
   manager_->SetHostForCreate(std::move(host_aa_unique_));
   ShimLaunchedCallback launched_callback;
   delegate_->SetCaptureShimLaunchedCallback(&launched_callback);
@@ -963,6 +964,7 @@
 
   // Select profile A and B from the menu -- this should not request a launch,
   // because the profiles are already enabled.
+  EXPECT_CALL(*delegate_, ShowAppWindows(_, _)).WillRepeatedly(Return(true));
   EXPECT_CALL(*delegate_, LaunchApp(_, _, _)).Times(0);
   host_aa_->ProfileSelectedFromMenu(profile_path_a_);
   host_aa_->ProfileSelectedFromMenu(profile_path_b_);
diff --git a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
index 65f2495..5a00b18 100644
--- a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
+++ b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
@@ -24,8 +24,7 @@
                                                const AppId& app_id) {
   if (UseFallback(profile, app_id))
     return fallback_delegate_->ShowAppWindows(profile, app_id);
-  // This is only used by legacy apps.
-  NOTREACHED();
+  // Non-legacy app windows are handled in AppShimManager.
   return false;
 }
 
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 9b4140a..5771450 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -709,6 +709,10 @@
         nearby_share::mojom::NearbyShareSettings,
         chromeos::settings::OSSettingsUI, nearby_share::NearbyShareDialogUI>(
         map);
+    RegisterWebUIControllerInterfaceBinder<nearby_share::mojom::ContactManager,
+                                           chromeos::settings::OSSettingsUI,
+                                           nearby_share::NearbyShareDialogUI>(
+        map);
     RegisterWebUIControllerInterfaceBinder<
         nearby_share::mojom::DiscoveryManager,
         nearby_share::NearbyShareDialogUI>(map);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 789b541d..735af5af 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4650,9 +4650,9 @@
   // The test below matches what's done by ShouldServiceRequestIOThread in
   // local_ntp_source.cc.
   if (instant_service->IsInstantProcess(render_process_id)) {
-    uniquely_owned_factories->emplace(
+    factories->emplace(
         chrome::kChromeSearchScheme,
-        content::CreateWebUIURLLoader(
+        content::CreateWebUIURLLoaderFactory(
             frame_host, chrome::kChromeSearchScheme,
             /*allowed_webui_hosts=*/base::flat_set<std::string>()));
   }
@@ -4692,10 +4692,10 @@
     allowed_webui_hosts.emplace_back(chrome::kChromeUIAppIconHost);
   }
   if (!allowed_webui_hosts.empty()) {
-    uniquely_owned_factories->emplace(
-        content::kChromeUIScheme,
-        content::CreateWebUIURLLoader(frame_host, content::kChromeUIScheme,
-                                      std::move(allowed_webui_hosts)));
+    factories->emplace(content::kChromeUIScheme,
+                       content::CreateWebUIURLLoaderFactory(
+                           frame_host, content::kChromeUIScheme,
+                           std::move(allowed_webui_hosts)));
   }
 
   // Extension with a background page get file access that gets approval from
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 8170d0c..e17bbb92 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -943,14 +943,14 @@
     "concierge_helper_service.h",
     "crosapi/ash_chrome_service_impl.cc",
     "crosapi/ash_chrome_service_impl.h",
-    "crosapi/attestation_ash.cc",
-    "crosapi/attestation_ash.h",
     "crosapi/browser_loader.cc",
     "crosapi/browser_loader.h",
     "crosapi/browser_manager.cc",
     "crosapi/browser_manager.h",
     "crosapi/browser_util.cc",
     "crosapi/browser_util.h",
+    "crosapi/keystore_service_ash.cc",
+    "crosapi/keystore_service_ash.h",
     "crosapi/message_center_ash.cc",
     "crosapi/message_center_ash.h",
     "crosapi/screen_manager_ash.cc",
diff --git a/chrome/browser/chromeos/concierge_helper_service.cc b/chrome/browser/chromeos/concierge_helper_service.cc
index 8abed5d..38b98eb 100644
--- a/chrome/browser/chromeos/concierge_helper_service.cc
+++ b/chrome/browser/chromeos/concierge_helper_service.cc
@@ -18,6 +18,13 @@
 namespace chromeos {
 namespace {
 
+void OnStartConcierge(bool started) {
+  if (started)
+    VLOG(1) << "Concierge D-Bus service successfully started";
+  else
+    LOG(ERROR) << "Unable to start Concierge D-Bus service";
+}
+
 void OnSetVmCpuRestriction(
     base::Optional<vm_tools::concierge::SetVmCpuRestrictionResponse> response) {
   if (!response || !response->success()) {
@@ -26,6 +33,18 @@
   }
 }
 
+// Starts Concierge DBus service through debugd. If the service is already
+// running, this request will be ignored.
+void StartConcierge() {
+  auto* client = DBusThreadManager::Get()->GetDebugDaemonClient();
+  if (!client) {
+    LOG(WARNING) << "DebugDaemonClient is not available";
+    OnStartConcierge(false);
+    return;
+  }
+  client->StartConcierge(base::BindOnce(&OnStartConcierge));
+}
+
 // Adds a callback to be run when Concierge DBus service becomes available.
 // If the service is already available, runs the callback immediately.
 void WaitForConciergeToBeAvailable(
@@ -84,7 +103,9 @@
   return ConciergeHelperServiceFactory::GetForBrowserContext(context);
 }
 
-ConciergeHelperService::ConciergeHelperService() = default;
+ConciergeHelperService::ConciergeHelperService() {
+  StartConcierge();
+}
 
 void ConciergeHelperService::SetArcVmCpuRestriction(bool do_restrict) {
   MakeRestrictionRequest(vm_tools::concierge::CPU_CGROUP_ARCVM, do_restrict);
diff --git a/chrome/browser/chromeos/concierge_helper_service_unittest.cc b/chrome/browser/chromeos/concierge_helper_service_unittest.cc
index fac19b2e..9d5f2a9e 100644
--- a/chrome/browser/chromeos/concierge_helper_service_unittest.cc
+++ b/chrome/browser/chromeos/concierge_helper_service_unittest.cc
@@ -11,6 +11,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/debug_daemon/fake_debug_daemon_client.h"
 #include "chromeos/dbus/fake_concierge_client.h"
 #include "content/public/test/browser_task_environment.h"
 #include "dbus/bus.h"
@@ -19,6 +20,24 @@
 
 namespace chromeos {
 
+class TestDebugDaemonClient : public FakeDebugDaemonClient {
+ public:
+  TestDebugDaemonClient() = default;
+  ~TestDebugDaemonClient() override = default;
+
+  void StartConcierge(ConciergeCallback callback) override {
+    ++start_concierge_called_;
+    FakeDebugDaemonClient::StartConcierge(std::move(callback));
+  }
+
+  size_t start_concierge_called() const { return start_concierge_called_; }
+
+ private:
+  size_t start_concierge_called_{0};
+
+  DISALLOW_COPY_AND_ASSIGN(TestDebugDaemonClient);
+};
+
 class TestConciergeClient : public FakeConciergeClient {
  public:
   TestConciergeClient() = default;
@@ -77,6 +96,7 @@
   // testing::Test:
   void SetUp() override {
     auto setter = DBusThreadManager::GetSetterForTesting();
+    setter->SetDebugDaemonClient(std::make_unique<TestDebugDaemonClient>());
     setter->SetConciergeClient(std::make_unique<TestConciergeClient>());
     service_ = ConciergeHelperService::GetForBrowserContext(&profile_);
   }
@@ -91,6 +111,11 @@
         DBusThreadManager::Get()->GetConciergeClient());
   }
 
+  TestDebugDaemonClient* fake_debug_client() {
+    return static_cast<TestDebugDaemonClient*>(
+        DBusThreadManager::Get()->GetDebugDaemonClient());
+  }
+
   content::BrowserTaskEnvironment* task_environment() {
     return &task_environment_;
   }
@@ -103,6 +128,12 @@
   DISALLOW_COPY_AND_ASSIGN(ConciergeHelperServiceTest);
 };
 
+// Tests that ConciergeHelperService can be constructed and destructed. Also,
+// check that ConciergeHelperService starts Concierge on construction.
+TEST_F(ConciergeHelperServiceTest, TestConstructDestruct) {
+  EXPECT_EQ(1U, fake_debug_client()->start_concierge_called());
+}
+
 // Tests that ConciergeHelperService makes cpu restriction requests correctly.
 TEST_F(ConciergeHelperServiceTest, TestSetVmCpuRestriction) {
   vm_tools::concierge::SetVmCpuRestrictionResponse response;
diff --git a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
index 84bac48..75adec34 100644
--- a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
+++ b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
@@ -9,11 +9,11 @@
 #include <vector>
 
 #include "base/logging.h"
-#include "chrome/browser/chromeos/crosapi/attestation_ash.h"
+#include "chrome/browser/chromeos/crosapi/keystore_service_ash.h"
 #include "chrome/browser/chromeos/crosapi/message_center_ash.h"
 #include "chrome/browser/chromeos/crosapi/screen_manager_ash.h"
 #include "chrome/browser/chromeos/crosapi/select_file_ash.h"
-#include "chromeos/crosapi/mojom/attestation.mojom.h"
+#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
 #include "chromeos/crosapi/mojom/message_center.mojom.h"
 #include "chromeos/crosapi/mojom/screen_manager.mojom.h"
 #include "chromeos/crosapi/mojom/select_file.mojom.h"
@@ -32,10 +32,10 @@
 
 AshChromeServiceImpl::~AshChromeServiceImpl() = default;
 
-void AshChromeServiceImpl::BindAttestation(
-    mojo::PendingReceiver<crosapi::mojom::Attestation> receiver) {
-  attestation_ash_ =
-      std::make_unique<crosapi::AttestationAsh>(std::move(receiver));
+void AshChromeServiceImpl::BindKeystoreService(
+    mojo::PendingReceiver<crosapi::mojom::KeystoreService> receiver) {
+  keystore_service_ash_ =
+      std::make_unique<crosapi::KeystoreServiceAsh>(std::move(receiver));
 }
 
 void AshChromeServiceImpl::BindMessageCenter(
diff --git a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h
index cf59379..f9f04c8 100644
--- a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h
+++ b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h
@@ -13,7 +13,7 @@
 
 namespace crosapi {
 
-class AttestationAsh;
+class KeystoreServiceAsh;
 class MessageCenterAsh;
 class ScreenManagerAsh;
 class SelectFileAsh;
@@ -27,8 +27,8 @@
   ~AshChromeServiceImpl() override;
 
   // crosapi::mojom::AshChromeService:
-  void BindAttestation(
-      mojo::PendingReceiver<mojom::Attestation> receiver) override;
+  void BindKeystoreService(
+      mojo::PendingReceiver<mojom::KeystoreService> receiver) override;
   void BindMessageCenter(
       mojo::PendingReceiver<mojom::MessageCenter> receiver) override;
   void BindScreenManager(
@@ -41,7 +41,7 @@
  private:
   mojo::Receiver<mojom::AshChromeService> receiver_;
 
-  std::unique_ptr<AttestationAsh> attestation_ash_;
+  std::unique_ptr<KeystoreServiceAsh> keystore_service_ash_;
   std::unique_ptr<MessageCenterAsh> message_center_ash_;
   std::unique_ptr<ScreenManagerAsh> screen_manager_ash_;
   std::unique_ptr<SelectFileAsh> select_file_ash_;
diff --git a/chrome/browser/chromeos/crosapi/attestation_ash.h b/chrome/browser/chromeos/crosapi/attestation_ash.h
deleted file mode 100644
index b3ff385..0000000
--- a/chrome/browser/chromeos/crosapi/attestation_ash.h
+++ /dev/null
@@ -1,58 +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 CHROME_BROWSER_CHROMEOS_CROSAPI_ATTESTATION_ASH_H_
-#define CHROME_BROWSER_CHROMEOS_CROSAPI_ATTESTATION_ASH_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "chromeos/crosapi/mojom/attestation.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-
-namespace chromeos {
-namespace attestation {
-class TpmChallengeKey;
-struct TpmChallengeKeyResult;
-}  // namespace attestation
-}  // namespace chromeos
-
-namespace crosapi {
-
-// This class is the ash implementation of the Attestation crosapi. It allows
-// lacros to expose blessed extension APIs which issue key challenges. These in
-// turn are forwarded to ash, which signs the challenge with a private key.
-class AttestationAsh : public mojom::Attestation {
- public:
-  explicit AttestationAsh(mojo::PendingReceiver<mojom::Attestation> receiver);
-  AttestationAsh(const AttestationAsh&) = delete;
-  AttestationAsh& operator=(const AttestationAsh&) = delete;
-  ~AttestationAsh() override;
-
-  // mojom::Attestation:
-  void ChallengeKey(const std::string& challenge,
-                    mojom::ChallengeKeyType type,
-                    ChallengeKeyCallback callback) override;
-
- private:
-  // |challenge| is used as a opaque identifier to match against the unique_ptr
-  // in outstanding_challenges_. It should not be dereferenced.
-  void DidChallengeKey(
-      ChallengeKeyCallback callback,
-      void* challenge,
-      const chromeos::attestation::TpmChallengeKeyResult& result);
-
-  // Container to keep outstanding challenges alive.
-  std::vector<std::unique_ptr<chromeos::attestation::TpmChallengeKey>>
-      outstanding_challenges_;
-  mojo::Receiver<mojom::Attestation> receiver_;
-
-  base::WeakPtrFactory<AttestationAsh> weak_factory_{this};
-};
-
-}  // namespace crosapi
-
-#endif  // CHROME_BROWSER_CHROMEOS_CROSAPI_ATTESTATION_ASH_H_
diff --git a/chrome/browser/chromeos/crosapi/attestation_ash.cc b/chrome/browser/chromeos/crosapi/keystore_service_ash.cc
similarity index 63%
rename from chrome/browser/chromeos/crosapi/attestation_ash.cc
rename to chrome/browser/chromeos/crosapi/keystore_service_ash.cc
index b2a415c..5004548 100644
--- a/chrome/browser/chromeos/crosapi/attestation_ash.cc
+++ b/chrome/browser/chromeos/crosapi/keystore_service_ash.cc
@@ -2,36 +2,40 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/crosapi/attestation_ash.h"
+#include "chrome/browser/chromeos/crosapi/keystore_service_ash.h"
 
 #include <utility>
 
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/chromeos/attestation/tpm_challenge_key.h"
 #include "chrome/browser/profiles/profile_manager.h"
 
 namespace crosapi {
 
-AttestationAsh::AttestationAsh(
-    mojo::PendingReceiver<mojom::Attestation> receiver)
+KeystoreServiceAsh::KeystoreServiceAsh(
+    mojo::PendingReceiver<mojom::KeystoreService> receiver)
     : receiver_(this, std::move(receiver)) {}
 
-AttestationAsh::~AttestationAsh() = default;
+KeystoreServiceAsh::~KeystoreServiceAsh() = default;
 
-void AttestationAsh::ChallengeKey(const std::string& challenge,
-                                  mojom::ChallengeKeyType type,
-                                  ChallengeKeyCallback callback) {
+void KeystoreServiceAsh::ChallengeAttestationOnlyKeystore(
+    const std::string& challenge,
+    mojom::KeystoreType type,
+    bool migrate,
+    ChallengeAttestationOnlyKeystoreCallback callback) {
   if (!crosapi::mojom::IsKnownEnumValue(type)) {
-    mojom::ChallengeKeyResultPtr result_ptr = mojom::ChallengeKeyResult::New();
-    result_ptr->set_error_message("unsupported challenge key type");
+    mojom::ChallengeAttestationOnlyKeystoreResultPtr result_ptr =
+        mojom::ChallengeAttestationOnlyKeystoreResult::New();
+    result_ptr->set_error_message("unsupported keystore type");
     std::move(callback).Run(std::move(result_ptr));
     return;
   }
   chromeos::attestation::AttestationKeyType key_type;
   switch (type) {
-    case mojom::ChallengeKeyType::kUser:
+    case mojom::KeystoreType::kUser:
       key_type = chromeos::attestation::KEY_USER;
       break;
-    case mojom::ChallengeKeyType::kDevice:
+    case mojom::KeystoreType::kDevice:
       key_type = chromeos::attestation::KEY_DEVICE;
       break;
   }
@@ -42,20 +46,22 @@
   chromeos::attestation::TpmChallengeKey* challenge_key_ptr =
       challenge_key.get();
   outstanding_challenges_.push_back(std::move(challenge_key));
+  //  TODO(https://crbug.com/1127505): Plumb |migrate| param.
   challenge_key_ptr->BuildResponse(
       key_type, profile,
-      base::BindOnce(&AttestationAsh::DidChallengeKey,
+      base::BindOnce(&KeystoreServiceAsh::DidChallengeAttestationOnlyKeystore,
                      weak_factory_.GetWeakPtr(), std::move(callback),
                      challenge_key_ptr),
       challenge,
       /*register_key=*/false, /*key_name_for_spkac=*/"");
 }
 
-void AttestationAsh::DidChallengeKey(
-    ChallengeKeyCallback callback,
+void KeystoreServiceAsh::DidChallengeAttestationOnlyKeystore(
+    ChallengeAttestationOnlyKeystoreCallback callback,
     void* challenge_key_ptr,
     const chromeos::attestation::TpmChallengeKeyResult& result) {
-  mojom::ChallengeKeyResultPtr result_ptr = mojom::ChallengeKeyResult::New();
+  mojom::ChallengeAttestationOnlyKeystoreResultPtr result_ptr =
+      mojom::ChallengeAttestationOnlyKeystoreResult::New();
   if (result.IsSuccess()) {
     result_ptr->set_challenge_response(result.challenge_response);
   } else {
diff --git a/chrome/browser/chromeos/crosapi/keystore_service_ash.h b/chrome/browser/chromeos/crosapi/keystore_service_ash.h
new file mode 100644
index 0000000..2d341ea
--- /dev/null
+++ b/chrome/browser/chromeos/crosapi/keystore_service_ash.h
@@ -0,0 +1,62 @@
+// 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_CROSAPI_KEYSTORE_SERVICE_ASH_H_
+#define CHROME_BROWSER_CHROMEOS_CROSAPI_KEYSTORE_SERVICE_ASH_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace chromeos {
+namespace attestation {
+class TpmChallengeKey;
+struct TpmChallengeKeyResult;
+}  // namespace attestation
+}  // namespace chromeos
+
+namespace crosapi {
+
+// This class is the ash implementation of the KeystoreService crosapi. It
+// allows lacros to expose blessed extension APIs which can query or modify the
+// system keystores.
+class KeystoreServiceAsh : public mojom::KeystoreService {
+ public:
+  explicit KeystoreServiceAsh(
+      mojo::PendingReceiver<mojom::KeystoreService> receiver);
+  KeystoreServiceAsh(const KeystoreServiceAsh&) = delete;
+  KeystoreServiceAsh& operator=(const KeystoreServiceAsh&) = delete;
+  ~KeystoreServiceAsh() override;
+
+  // mojom::KeystoreService:
+  using KeystoreType = mojom::KeystoreType;
+  void ChallengeAttestationOnlyKeystore(
+      const std::string& challenge,
+      mojom::KeystoreType type,
+      bool migrate,
+      ChallengeAttestationOnlyKeystoreCallback callback) override;
+
+ private:
+  // |challenge| is used as a opaque identifier to match against the unique_ptr
+  // in outstanding_challenges_. It should not be dereferenced.
+  void DidChallengeAttestationOnlyKeystore(
+      ChallengeAttestationOnlyKeystoreCallback callback,
+      void* challenge,
+      const chromeos::attestation::TpmChallengeKeyResult& result);
+
+  // Container to keep outstanding challenges alive.
+  std::vector<std::unique_ptr<chromeos::attestation::TpmChallengeKey>>
+      outstanding_challenges_;
+  mojo::Receiver<mojom::KeystoreService> receiver_;
+
+  base::WeakPtrFactory<KeystoreServiceAsh> weak_factory_{this};
+};
+
+}  // namespace crosapi
+
+#endif  // CHROME_BROWSER_CHROMEOS_CROSAPI_KEYSTORE_SERVICE_ASH_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_disk.cc b/chrome/browser/chromeos/crostini/crostini_disk.cc
index 8ea4a8c6..225174f 100644
--- a/chrome/browser/chromeos/crostini/crostini_disk.cc
+++ b/chrome/browser/chromeos/crostini/crostini_disk.cc
@@ -73,15 +73,26 @@
         base::BindOnce(&OnAmountOfFreeDiskSpace, std::move(callback), profile,
                        std::move(vm_name)));
   } else {
+    VLOG(1) << "Starting concierge";
     // Since we only care about the disk's current size and whether it's a
     // sparse disk, we claim there's plenty of free space available to prevent
     // error conditions in |OnCrostiniSufficientlyRunning|.
     constexpr int64_t kFakeAvailableDiskBytes =
         kDiskHeadroomBytes + kRecommendedDiskSizeBytes;
 
-    OnCrostiniSufficientlyRunning(std::move(callback), profile,
-                                  std::move(vm_name), kFakeAvailableDiskBytes,
-                                  CrostiniResult::SUCCESS);
+    CrostiniManager::GetForProfile(profile)->StartConcierge(base::BindOnce(
+        [](OnceDiskInfoCallback callback, Profile* profile, std::string vm_name,
+           bool success) {
+          if (!success) {
+            LOG(ERROR) << "Failed to start concierge";
+            std::move(callback).Run(nullptr);
+            return;
+          }
+          OnCrostiniSufficientlyRunning(
+              std::move(callback), profile, std::move(vm_name),
+              kFakeAvailableDiskBytes, CrostiniResult::SUCCESS);
+        },
+        std::move(callback), profile, std::move(vm_name)));
   }
 }
 
diff --git a/chrome/browser/chromeos/crostini/crostini_installer.cc b/chrome/browser/chromeos/crostini/crostini_installer.cc
index 9faad08a..8de0de66 100644
--- a/chrome/browser/chromeos/crostini/crostini_installer.cc
+++ b/chrome/browser/chromeos/crostini/crostini_installer.cc
@@ -113,6 +113,8 @@
       return SetupResult::kSuccess;
     case InstallerError::kErrorLoadingTermina:
       return SetupResult::kErrorLoadingTermina;
+    case InstallerError::kErrorStartingConcierge:
+      return SetupResult::kErrorStartingConcierge;
     case InstallerError::kErrorCreatingDiskImage:
       return SetupResult::kErrorCreatingDiskImage;
     case InstallerError::kErrorStartingTermina:
@@ -147,6 +149,8 @@
       return SetupResult::kUserCancelledStart;
     case InstallerState::kInstallImageLoader:
       return SetupResult::kUserCancelledInstallImageLoader;
+    case InstallerState::kStartConcierge:
+      return SetupResult::kUserCancelledStartConcierge;
     case InstallerState::kCreateDiskImage:
       return SetupResult::kUserCancelledCreateDiskImage;
     case InstallerState::kStartTerminaVm:
@@ -322,6 +326,15 @@
     }
     return;
   }
+  UpdateInstallingState(InstallerState::kStartConcierge);
+}
+
+void CrostiniInstaller::OnConciergeStarted(bool success) {
+  DCHECK_EQ(installing_state_, InstallerState::kStartConcierge);
+  if (!success) {
+    HandleError(InstallerError::kErrorStartingConcierge);
+    return;
+  }
   UpdateInstallingState(InstallerState::kCreateDiskImage);
 }
 
@@ -486,8 +499,12 @@
       state_end_mark = 0.20;
       state_max_time = base::TimeDelta::FromSeconds(30);
       break;
-    case InstallerState::kCreateDiskImage:
+    case InstallerState::kStartConcierge:
       state_start_mark = 0.20;
+      state_end_mark = 0.21;
+      break;
+    case InstallerState::kCreateDiskImage:
+      state_start_mark = 0.21;
       state_end_mark = 0.22;
       break;
     case InstallerState::kStartTerminaVm:
diff --git a/chrome/browser/chromeos/crostini/crostini_installer.h b/chrome/browser/chromeos/crostini/crostini_installer.h
index ce38ef5f..ce26ed5 100644
--- a/chrome/browser/chromeos/crostini/crostini_installer.h
+++ b/chrome/browser/chromeos/crostini/crostini_installer.h
@@ -37,7 +37,7 @@
     // kUserCancelled = 1,
     kSuccess = 2,
     kErrorLoadingTermina = 3,
-    // kErrorStartingConcierge = 4,
+    kErrorStartingConcierge = 4,
     kErrorCreatingDiskImage = 5,
     kErrorStartingTermina = 6,
     kErrorStartingContainer = 7,
@@ -48,7 +48,7 @@
 
     kUserCancelledStart = 12,
     kUserCancelledInstallImageLoader = 13,
-    // kUserCancelledStartConcierge = 14,
+    kUserCancelledStartConcierge = 14,
     kUserCancelledCreateDiskImage = 15,
     kUserCancelledStartTerminaVm = 16,
     kUserCancelledCreateContainer = 17,
@@ -88,6 +88,7 @@
   // CrostiniManager::RestartObserver:
   void OnStageStarted(crostini::mojom::InstallerState stage) override;
   void OnComponentLoaded(crostini::CrostiniResult result) override;
+  void OnConciergeStarted(bool success) override;
   void OnDiskImageCreated(bool success,
                           vm_tools::concierge::DiskImageStatus status,
                           int64_t disk_size_available) override;
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 7a7fce0..903c98f 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -333,6 +333,23 @@
     }
     // Set the pref here, after we first successfully install something
     profile_->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled, true);
+    StartStage(mojom::InstallerState::kStartConcierge);
+    crostini_manager_->StartConcierge(base::BindOnce(
+        &CrostiniRestarter::ConciergeStarted, weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void ConciergeStarted(bool is_started) {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    for (auto& observer : observer_list_) {
+      observer.OnConciergeStarted(is_started);
+    }
+    if (ReturnEarlyIfAborted()) {
+      return;
+    }
+    if (!is_started) {
+      FinishRestart(CrostiniResult::CONCIERGE_START_FAILED);
+      return;
+    }
 
     // Allow concierge to choose an appropriate disk image size.
     int64_t disk_size_bytes = options_.disk_size_bytes.value_or(0);
@@ -1027,6 +1044,34 @@
   termina_installer_.Uninstall(std::move(callback));
 }
 
+void CrostiniManager::StartConcierge(BoolCallback callback) {
+  VLOG(1) << "Starting Concierge service";
+  chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->StartConcierge(
+      base::BindOnce(&CrostiniManager::OnStartConcierge,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void CrostiniManager::OnStartConcierge(BoolCallback callback, bool success) {
+  if (!success) {
+    LOG(ERROR) << "Failed to start Concierge service";
+    std::move(callback).Run(success);
+    return;
+  }
+  VLOG(1) << "Concierge service started";
+  VLOG(1) << "Waiting for Cicerone to announce availability.";
+
+  GetCiceroneClient()->WaitForServiceToBeAvailable(std::move(callback));
+}
+
+void CrostiniManager::OnStopConcierge(BoolCallback callback, bool success) {
+  if (!success) {
+    LOG(ERROR) << "Failed to stop Concierge service";
+  } else {
+    VLOG(1) << "Concierge service stopped";
+  }
+  std::move(callback).Run(success);
+}
+
 void CrostiniManager::CreateDiskImage(
     const base::FilePath& disk_path,
     vm_tools::concierge::StorageLocation storage_location,
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h
index 1ff400d..459f9c816 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.h
+++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -189,6 +189,7 @@
     virtual ~RestartObserver() {}
     virtual void OnStageStarted(mojom::InstallerState stage) {}
     virtual void OnComponentLoaded(CrostiniResult result) {}
+    virtual void OnConciergeStarted(bool success) {}
     virtual void OnDiskImageCreated(bool success,
                                     vm_tools::concierge::DiskImageStatus status,
                                     int64_t disk_size_bytes) {}
@@ -242,6 +243,10 @@
   // Unloads and removes termina.
   void UninstallTermina(BoolCallback callback);
 
+  // Starts the Concierge service. |callback| is called after the method call
+  // finishes.
+  void StartConcierge(BoolCallback callback);
+
   // Checks the arguments for creating a new Termina VM disk image. Creates a
   // disk image for a Termina VM via ConciergeClient::CreateDiskImage.
   // |callback| is called if the arguments are bad, or after the method call
@@ -698,6 +703,14 @@
       base::Optional<vm_tools::concierge::GetVmEnterpriseReportingInfoResponse>
           response);
 
+  // Callback for CrostiniClient::StartConcierge. Called after the
+  // DebugDaemon service method finishes.
+  void OnStartConcierge(BoolCallback callback, bool success);
+
+  // Callback for CrostiniClient::StopConcierge. Called after the
+  // DebugDaemon service method finishes.
+  void OnStopConcierge(BoolCallback callback, bool success);
+
   // Callback for CiceroneClient::StartLxd. May indicate that LXD is still being
   // started in which case we will wait for OnStartLxdProgress events.
   void OnStartLxd(
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index d694b35..c80358bec 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -645,6 +645,12 @@
     }
   }
 
+  void OnConciergeStarted(bool success) override {
+    if (abort_on_concierge_started_) {
+      Abort();
+    }
+  }
+
   void OnDiskImageCreated(bool success,
                           vm_tools::concierge::DiskImageStatus status,
                           int64_t disk_size_available) override {
@@ -747,6 +753,7 @@
   const CrostiniManager::RestartId uninitialized_id_ =
       CrostiniManager::kUninitializedRestartId;
   bool abort_on_component_loaded_ = false;
+  bool abort_on_concierge_started_ = false;
   bool abort_on_disk_image_created_ = false;
   bool abort_on_vm_started_ = false;
   bool abort_on_container_created_ = false;
@@ -857,6 +864,21 @@
   ExpectRestarterUmaCount(1);
 }
 
+TEST_F(CrostiniManagerRestartTest, AbortOnConciergeStarted) {
+  abort_on_concierge_started_ = true;
+  restart_id_ = crostini_manager()->RestartCrostini(
+      container_id(),
+      base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
+                     base::Unretained(this), run_loop()->QuitClosure()),
+      this);
+  run_loop()->Run();
+  EXPECT_FALSE(fake_concierge_client_->create_disk_image_called());
+  EXPECT_FALSE(fake_concierge_client_->start_termina_vm_called());
+  EXPECT_FALSE(fake_concierge_client_->get_container_ssh_keys_called());
+  ExpectCrostiniRestartResult(CrostiniResult::RESTART_ABORTED);
+  ExpectRestarterUmaCount(1);
+}
+
 TEST_F(CrostiniManagerRestartTest, AbortOnDiskImageCreated) {
   abort_on_disk_image_created_ = true;
   restart_id_ = crostini_manager()->RestartCrostini(
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.cc b/chrome/browser/chromeos/crostini/crostini_remover.cc
index ed92fb8..e3133ab 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.cc
+++ b/chrome/browser/chromeos/crostini/crostini_remover.cc
@@ -34,6 +34,15 @@
 
 void CrostiniRemover::RemoveCrostini() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  CrostiniManager::GetForProfile(profile_)->StartConcierge(
+      base::BindOnce(&CrostiniRemover::OnConciergeStarted, this));
+}
+
+void CrostiniRemover::OnConciergeStarted(bool is_successful) {
+  if (!is_successful) {
+    std::move(callback_).Run(CrostiniResult::UNKNOWN_ERROR);
+    return;
+  }
   CrostiniManager::GetForProfile(profile_)->StopVm(
       vm_name_, base::BindOnce(&CrostiniRemover::StopVmFinished, this));
 }
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.h b/chrome/browser/chromeos/crostini/crostini_remover.h
index 89c92c5..afc43327 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.h
+++ b/chrome/browser/chromeos/crostini/crostini_remover.h
@@ -22,6 +22,7 @@
 
   ~CrostiniRemover();
 
+  void OnConciergeStarted(bool is_successful);
   void StopVmFinished(crostini::CrostiniResult result);
   void DestroyDiskImageFinished(bool success);
   void UninstallTerminaFinished(bool success);
diff --git a/chrome/browser/chromeos/crostini/crostini_types.mojom b/chrome/browser/chromeos/crostini/crostini_types.mojom
index 56420cda..65efb5c 100644
--- a/chrome/browser/chromeos/crostini/crostini_types.mojom
+++ b/chrome/browser/chromeos/crostini/crostini_types.mojom
@@ -7,6 +7,7 @@
 enum InstallerState {
   kStart,               // Just started installation
   kInstallImageLoader,  // Loading the Termina VM component.
+  kStartConcierge,      // Starting the Concierge D-Bus client.
   kCreateDiskImage,     // Creating the image for the Termina VM.
   kStartTerminaVm,      // Starting the Termina VM.
   kCreateContainer,     // Creating the container inside the Termina VM.
@@ -20,6 +21,7 @@
 enum InstallerError {
   kNone,
   kErrorLoadingTermina,
+  kErrorStartingConcierge,
   kErrorCreatingDiskImage,
   kErrorStartingTermina,
   kErrorStartingContainer,
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index 31c9414..c39117e 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/grit/generated_resources.h"
 #include "extensions/browser/device_local_account_util.h"
+#include "extensions/common/api/incognito.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_constants.h"
@@ -132,7 +133,7 @@
     // Shared Modules configuration: Import resources from another extension.
     emk::kImport,
 
-    emk::kIncognito,
+    ::extensions::api::incognito::ManifestKeys::kIncognito,
 
     // Keylogging.
     // emk::kInputComponents,
diff --git a/chrome/browser/chromeos/login/screens/mock_update_screen.h b/chrome/browser/chromeos/login/screens/mock_update_screen.h
index 50e091e..d7a99ef 100644
--- a/chrome/browser/chromeos/login/screens/mock_update_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_update_screen.h
@@ -39,12 +39,10 @@
 
   MOCK_METHOD(void, SetUIState, (UpdateView::UIState value));
   MOCK_METHOD(void,
-              SetUpdateStatusMessagePercent,
-              (const base::string16& value));
-  MOCK_METHOD(void,
-              SetUpdateStatusMessageTimeLeft,
-              (const base::string16& value));
-  MOCK_METHOD(void, SetBetterUpdateProgress, (int value));
+              SetUpdateStatus,
+              (int percent,
+               const base::string16& percent_message,
+               const base::string16& timeleft_message));
   MOCK_METHOD(void, SetEstimatedTimeLeft, (int value));
   MOCK_METHOD(void, SetShowEstimatedTimeLeft, (bool value));
   MOCK_METHOD(void, SetUpdateCompleted, (bool value));
diff --git a/chrome/browser/chromeos/login/screens/update_screen.cc b/chrome/browser/chromeos/login/screens/update_screen.cc
index d18dd913..935c042 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.cc
+++ b/chrome/browser/chromeos/login/screens/update_screen.cc
@@ -443,20 +443,23 @@
                                           base::TimeDelta time_left) {
   if (!view_)
     return;
-  view_->SetUpdateStatusMessagePercent(l10n_util::GetStringFUTF16(
-      IDS_UPDATE_STATUS_SUBTITLE_PERCENT, base::FormatPercent(percent)));
+  base::string16 time_left_message;
   if (time_left.InMinutes() == 0) {
-    view_->SetUpdateStatusMessageTimeLeft(l10n_util::GetStringFUTF16(
+    time_left_message = l10n_util::GetStringFUTF16(
         IDS_UPDATE_STATUS_SUBTITLE_TIME_LEFT,
         l10n_util::GetPluralStringFUTF16(IDS_TIME_LONG_SECS,
-                                         time_left.InSeconds())));
+                                         time_left.InSeconds()));
   } else {
-    view_->SetUpdateStatusMessageTimeLeft(l10n_util::GetStringFUTF16(
+    time_left_message = l10n_util::GetStringFUTF16(
         IDS_UPDATE_STATUS_SUBTITLE_TIME_LEFT,
         l10n_util::GetPluralStringFUTF16(IDS_TIME_LONG_MINS,
-                                         time_left.InMinutes())));
+                                         time_left.InMinutes()));
   }
-  view_->SetBetterUpdateProgress(percent);
+  view_->SetUpdateStatus(
+      percent,
+      l10n_util::GetStringFUTF16(IDS_UPDATE_STATUS_SUBTITLE_PERCENT,
+                                 base::FormatPercent(percent)),
+      time_left_message);
 }
 
 void UpdateScreen::UpdateBatteryWarningVisibility() {
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index b904b59..0d8f2b5d1 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -152,6 +152,10 @@
 constexpr char kFakeBoardVersion[] = "rev1234";
 constexpr uint64_t kFakeChassisType = 9;
 constexpr char kFakeProductName[] = "ProductName";
+constexpr char kFakeVersionMilestone[] = "87";
+constexpr char kFakeVersionBuildNumber[] = "13544";
+constexpr char kFakeVersionPatchNumber[] = "59.0";
+constexpr char kFakeVersionReleaseChannel[] = "stable-channel";
 // CPU test values:
 constexpr uint32_t kFakeNumTotalThreads = 8;
 constexpr char kFakeModelName[] = "fake_cpu_model_name";
@@ -517,7 +521,10 @@
           kFakeFirstPowerDate, kFakeManufactureDate, kFakeSkuNumber,
           kFakeMarketingName, kFakeBiosVersion, kFakeBoardName,
           kFakeBoardVersion, cros_healthd::UInt64Value::New(kFakeChassisType),
-          kFakeProductName));
+          kFakeProductName,
+          cros_healthd::OsVersion::New(
+              kFakeVersionMilestone, kFakeVersionBuildNumber,
+              kFakeVersionPatchNumber, kFakeVersionReleaseChannel)));
 }
 
 std::vector<cros_healthd::CpuCStateInfoPtr> CreateCStateInfo() {
diff --git a/chrome/browser/chromeos/remote_apps/remote_apps_manager.h b/chrome/browser/chromeos/remote_apps/remote_apps_manager.h
index 0798d565..81f2a817 100644
--- a/chrome/browser/chromeos/remote_apps/remote_apps_manager.h
+++ b/chrome/browser/chromeos/remote_apps/remote_apps_manager.h
@@ -44,6 +44,7 @@
 // This service is only created for Managed Guest Sessions.
 // The IDs of the added apps and folders are GUIDs generated using
 // |base::GenerateGUID()|.
+// See crbug.com/1101208 for more details on Remote Apps.
 class RemoteAppsManager : public KeyedService,
                           public apps::RemoteApps::Delegate,
                           public app_list::AppListSyncableService::Observer,
diff --git a/chrome/browser/complex_tasks/OWNERS b/chrome/browser/complex_tasks/OWNERS
index cfeb8c6..2c9c952 100644
--- a/chrome/browser/complex_tasks/OWNERS
+++ b/chrome/browser/complex_tasks/OWNERS
@@ -1,3 +1,3 @@
 wychen@chromium.org
 yusufo@chromium.org
-bsep@chromium.org
\ No newline at end of file
+
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
index 0413e52..9025870 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -233,5 +233,7 @@
                      &Delegate::SetOpenNewWindowForPopups, delegate);
   d->RegisterHandler("registerExtensionsAPI", &Delegate::RegisterExtensionsAPI,
                      delegate);
+  d->RegisterHandlerWithCallback("getSurveyAPIKey", &Delegate::GetSurveyAPIKey,
+                                 delegate);
   return d;
 }
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
index 149dcde4..4b167b13 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
@@ -104,6 +104,7 @@
     virtual void SetOpenNewWindowForPopups(bool value) = 0;
     virtual void RegisterExtensionsAPI(const std::string& origin,
                                        const std::string& script) = 0;
+    virtual void GetSurveyAPIKey(const DispatchCallback& callback) = 0;
   };
 
   using DispatchCallback = Delegate::DispatchCallback;
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index e457463..0f73e43 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -73,6 +73,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/permissions/permissions_data.h"
+#include "google_apis/google_api_keys.h"
 #include "ipc/ipc_channel.h"
 #include "net/base/escape.h"
 #include "net/base/net_errors.h"
@@ -921,9 +922,14 @@
         target_tab->GetURL().scheme() == gurl.scheme()) {
       std::vector<std::string> allowed_webui_hosts;
       content::RenderFrameHost* frame_host = web_contents()->GetMainFrame();
-      url_loader_factory = content::CreateWebUIURLLoader(
-          frame_host, target_tab->GetURL().scheme(),
-          std::move(allowed_webui_hosts));
+
+      mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote =
+          content::CreateWebUIURLLoaderFactory(frame_host,
+                                               target_tab->GetURL().scheme(),
+                                               std::move(allowed_webui_hosts));
+      url_loader_factory = network::SharedURLLoaderFactory::Create(
+          std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
+              std::move(pending_remote)));
     } else {
       base::DictionaryValue response;
       response.SetBoolean("schemeSupported", false);
@@ -1530,6 +1536,12 @@
   extensions_api_[origin + "/"] = script;
 }
 
+void DevToolsUIBindings::GetSurveyAPIKey(const DispatchCallback& callback) {
+  base::DictionaryValue response;
+  response.SetString("apiKey", google_apis::GetDevtoolsSurveysAPIKey());
+  callback.Run(&response);
+}
+
 void DevToolsUIBindings::SetDelegate(Delegate* delegate) {
   delegate_.reset(delegate);
 }
diff --git a/chrome/browser/devtools/devtools_ui_bindings.h b/chrome/browser/devtools/devtools_ui_bindings.h
index d7d4361..c4318ae 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.h
+++ b/chrome/browser/devtools/devtools_ui_bindings.h
@@ -171,6 +171,7 @@
   void SetOpenNewWindowForPopups(bool value) override;
   void RegisterExtensionsAPI(const std::string& origin,
                              const std::string& script) override;
+  void GetSurveyAPIKey(const DispatchCallback& callback) override;
 
   void EnableRemoteDeviceCounter(bool enable);
 
diff --git a/chrome/browser/download/android/java/res/values-v17/ids.xml b/chrome/browser/download/android/java/res/values-v17/ids.xml
index a002f28..c7d87d1 100644
--- a/chrome/browser/download/android/java/res/values-v17/ids.xml
+++ b/chrome/browser/download/android/java/res/values-v17/ids.xml
@@ -6,4 +6,5 @@
 <resources>
     <item name="circular_progress_view_action" type="id" />
     <item name="circular_progress_view_progress" type="id" />
+    <item name="download_home_recycler_view" type="id" />
 </resources>
\ No newline at end of file
diff --git a/chrome/browser/endpoint_fetcher/BUILD.gn b/chrome/browser/endpoint_fetcher/BUILD.gn
new file mode 100644
index 0000000..4b28139d
--- /dev/null
+++ b/chrome/browser/endpoint_fetcher/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  deps = [
+    ":jni_headers",
+    "//base:base_java",
+    "//base:jni_java",
+    "//chrome/browser/profiles/android:java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
+  ]
+  sources = [
+    "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java",
+    "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java",
+  ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+generate_jni("jni_headers") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java",
+    "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java",
+  ]
+}
diff --git a/chrome/browser/endpoint_fetcher/OWNERS b/chrome/browser/endpoint_fetcher/OWNERS
new file mode 100644
index 0000000..1508f1f4
--- /dev/null
+++ b/chrome/browser/endpoint_fetcher/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/complex_tasks/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.cc b/chrome/browser/endpoint_fetcher/endpoint_fetcher.cc
similarity index 98%
rename from chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.cc
rename to chrome/browser/endpoint_fetcher/endpoint_fetcher.cc
index fd175f3..9711e2b 100644
--- a/chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.cc
+++ b/chrome/browser/endpoint_fetcher/endpoint_fetcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.h"
+#include "chrome/browser/endpoint_fetcher/endpoint_fetcher.h"
 
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
@@ -22,8 +22,8 @@
 #include "base/android/callback_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
-#include "chrome/android/chrome_jni_headers/EndpointFetcher_jni.h"
-#include "chrome/android/chrome_jni_headers/EndpointResponse_jni.h"
+#include "chrome/browser/endpoint_fetcher/jni_headers/EndpointFetcher_jni.h"
+#include "chrome/browser/endpoint_fetcher/jni_headers/EndpointResponse_jni.h"
 #include "chrome/browser/profiles/profile_android.h"
 
 #endif  // defined(OS_ANDROID)
diff --git a/chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.h b/chrome/browser/endpoint_fetcher/endpoint_fetcher.h
similarity index 95%
rename from chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.h
rename to chrome/browser/endpoint_fetcher/endpoint_fetcher.h
index ec5c30b..2f9c480d 100644
--- a/chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.h
+++ b/chrome/browser/endpoint_fetcher/endpoint_fetcher.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_COMPLEX_TASKS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
-#define CHROME_BROWSER_COMPLEX_TASKS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
+#ifndef CHROME_BROWSER_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
+#define CHROME_BROWSER_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
 
 #include <string>
 #include <vector>
@@ -137,4 +137,4 @@
   base::WeakPtrFactory<EndpointFetcher> weak_ptr_factory_{this};
 };
 
-#endif  // CHROME_BROWSER_COMPLEX_TASKS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
+#endif  // CHROME_BROWSER_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
diff --git a/chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher_unittest.cc b/chrome/browser/endpoint_fetcher/endpoint_fetcher_unittest.cc
similarity index 98%
rename from chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher_unittest.cc
rename to chrome/browser/endpoint_fetcher/endpoint_fetcher_unittest.cc
index 5132ce1c..416707f 100644
--- a/chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher_unittest.cc
+++ b/chrome/browser/endpoint_fetcher/endpoint_fetcher_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.h"
+#include "chrome/browser/endpoint_fetcher/endpoint_fetcher.h"
 
 #include <string>
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointFetcher.java b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointFetcher.java
rename to chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java
index 5d76141..c5f375c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointFetcher.java
+++ b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.complex_tasks.endpoint_fetcher;
+package org.chromium.chrome.browser.endpoint_fetcher;
 
 import androidx.annotation.MainThread;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointResponse.java b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java
similarity index 92%
rename from chrome/android/java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointResponse.java
rename to chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java
index a5daf0b..2751d00 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointResponse.java
+++ b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.complex_tasks.endpoint_fetcher;
+package org.chromium.chrome.browser.endpoint_fetcher;
 
 import org.chromium.base.annotations.CalledByNative;
 
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.cc
index 1b78345..180db45 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.cc
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.cc
@@ -43,22 +43,26 @@
   EXTENSION_FUNCTION_VALIDATE(params);
   // TODO(https://crbug.com/1113443): This implementation needs to check if the
   // extension is allowlisted via the AttestationExtensionAllowlist policy.
-  auto c = base::BindOnce(
-      &EnterprisePlatformKeysChallengeMachineKeyFunction::OnChallengedKeyLacros,
-      this);
-  chromeos::LacrosChromeServiceImpl::Get()->attestation_remote()->ChallengeKey(
-      StringFromVector(params->challenge),
-      crosapi::mojom::ChallengeKeyType::kDevice, std::move(c));
+  auto c = base::BindOnce(&EnterprisePlatformKeysChallengeMachineKeyFunction::
+                              OnChallengeAttestationOnlyKeystore,
+                          this);
+  chromeos::LacrosChromeServiceImpl::Get()
+      ->keystore_service_remote()
+      ->ChallengeAttestationOnlyKeystore(StringFromVector(params->challenge),
+                                         crosapi::mojom::KeystoreType::kDevice,
+                                         /*migrate=*/*params->register_key,
+                                         std::move(c));
   return RespondLater();
 }
 
-void EnterprisePlatformKeysChallengeMachineKeyFunction::OnChallengedKeyLacros(
-    crosapi::mojom::ChallengeKeyResultPtr result) {
+void EnterprisePlatformKeysChallengeMachineKeyFunction::
+    OnChallengeAttestationOnlyKeystore(ResultPtr result) {
+  using Result = crosapi::mojom::ChallengeAttestationOnlyKeystoreResult;
   switch (result->which()) {
-    case crosapi::mojom::ChallengeKeyResult::Tag::ERROR_MESSAGE:
+    case Result::Tag::ERROR_MESSAGE:
       Respond(Error(result->get_error_message()));
       return;
-    case crosapi::mojom::ChallengeKeyResult::Tag::CHALLENGE_RESPONSE:
+    case Result::Tag::CHALLENGE_RESPONSE:
       Respond(ArgumentList(api_epk::ChallengeMachineKey::Results::Create(
           VectorFromString(result->get_challenge_response()))));
       return;
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.h b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.h
index 0ffc65c..a52392f 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.h
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_PLATFORM_KEYS_ENTERPRISE_PLATFORM_KEYS_API_LACROS_H_
 #define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_PLATFORM_KEYS_ENTERPRISE_PLATFORM_KEYS_API_LACROS_H_
 
-#include "chromeos/crosapi/mojom/attestation.mojom.h"
+#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
 #include "extensions/browser/extension_function.h"
 
 namespace extensions {
@@ -56,7 +56,8 @@
   ~EnterprisePlatformKeysChallengeMachineKeyFunction() override = default;
   ResponseAction Run() override;
 
-  void OnChallengedKeyLacros(crosapi::mojom::ChallengeKeyResultPtr result);
+  using ResultPtr = crosapi::mojom::ChallengeAttestationOnlyKeystoreResultPtr;
+  void OnChallengeAttestationOnlyKeystore(ResultPtr result);
   DECLARE_EXTENSION_FUNCTION("enterprise.platformKeys.challengeMachineKey",
                              ENTERPRISE_PLATFORMKEYS_CHALLENGEMACHINEKEY)
 };
diff --git a/chrome/browser/extensions/api/terminal/crostini_startup_status.cc b/chrome/browser/extensions/api/terminal/crostini_startup_status.cc
index ccdbe0e..46b9d08 100644
--- a/chrome/browser/extensions/api/terminal/crostini_startup_status.cc
+++ b/chrome/browser/extensions/api/terminal/crostini_startup_status.cc
@@ -100,6 +100,9 @@
           {InstallerState::kInstallImageLoader,
            l10n_util::GetStringUTF8(
                IDS_CROSTINI_TERMINAL_STATUS_INSTALL_IMAGE_LOADER)},
+          {InstallerState::kStartConcierge,
+           l10n_util::GetStringUTF8(
+               IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE)},
           {InstallerState::kCreateDiskImage,
            l10n_util::GetStringUTF8(
                IDS_CROSTINI_TERMINAL_STATUS_CREATE_DISK_IMAGE)},
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm
index 8539450..939982e 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac.mm
@@ -166,7 +166,7 @@
     {true,  false, false, true,  kVK_ANSI_L,            IDC_SHOW_DOWNLOADS},
     {true,  true,  false, false, kVK_ANSI_C,            IDC_DEV_TOOLS_INSPECT},
     {true,  false, false, true,  kVK_ANSI_C,            IDC_DEV_TOOLS_INSPECT},
-    {true,  true,  false, false, kVK_ANSI_E,            IDC_TAB_SEARCH},
+    {true,  true,  false, false, kVK_ANSI_A,            IDC_TAB_SEARCH},
 
     {true,  false, false, true,  kVK_DownArrow,         IDC_FOCUS_NEXT_PANE},
     {true,  false, false, true,  kVK_UpArrow,           IDC_FOCUS_PREVIOUS_PANE},
diff --git a/chrome/browser/media/kaleidoscope/kaleidoscope_data_provider_impl.cc b/chrome/browser/media/kaleidoscope/kaleidoscope_data_provider_impl.cc
index fe3d71f..3415f7a 100644
--- a/chrome/browser/media/kaleidoscope/kaleidoscope_data_provider_impl.cc
+++ b/chrome/browser/media/kaleidoscope/kaleidoscope_data_provider_impl.cc
@@ -131,8 +131,15 @@
 
 void KaleidoscopeDataProviderImpl::GetShouldShowFirstRunExperience(
     GetShouldShowFirstRunExperienceCallback cb) {
-  std::move(cb).Run(kaleidoscope::KaleidoscopeService::Get(profile_)
-                        ->ShouldShowFirstRunExperience());
+  auto* service = kaleidoscope::KaleidoscopeService::Get(profile_);
+
+  // The Kaleidoscope Service would not be available for incognito profiles.
+  if (!service) {
+    std::move(cb).Run(false);
+    return;
+  }
+
+  std::move(cb).Run(service->ShouldShowFirstRunExperience());
 }
 
 void KaleidoscopeDataProviderImpl::SetFirstRunExperienceStep(
diff --git a/chrome/browser/nearby_sharing/contacts/BUILD.gn b/chrome/browser/nearby_sharing/contacts/BUILD.gn
index 7045e67..8f316db 100644
--- a/chrome/browser/nearby_sharing/contacts/BUILD.gn
+++ b/chrome/browser/nearby_sharing/contacts/BUILD.gn
@@ -22,6 +22,7 @@
     "//chrome/browser/nearby_sharing/logging",
     "//chrome/browser/nearby_sharing/proto",
     "//chrome/browser/nearby_sharing/scheduling",
+    "//chrome/browser/ui/webui/nearby_share/public/mojom",
     "//components/prefs",
   ]
 }
@@ -63,7 +64,9 @@
     "//chrome/browser/nearby_sharing/proto",
     "//chrome/browser/nearby_sharing/scheduling",
     "//chrome/browser/nearby_sharing/scheduling:test_support",
+    "//chrome/browser/ui/webui/nearby_share/public/mojom",
     "//components/sync_preferences:test_support",
+    "//content/test:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.cc b/chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.cc
index 9e6e099..a2257c2 100644
--- a/chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.cc
+++ b/chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.cc
@@ -40,3 +40,12 @@
 void FakeNearbyShareContactManager::OnStart() {}
 
 void FakeNearbyShareContactManager::OnStop() {}
+
+void FakeNearbyShareContactManager::Bind(
+    mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver) {}
+
+void FakeNearbyShareContactManager::AddDownloadContactsObserver(
+    ::mojo::PendingRemote<nearby_share::mojom::DownloadContactsObserver>
+        observer) {}
+
+void FakeNearbyShareContactManager::DownloadContacts() {}
diff --git a/chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.h b/chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.h
index 9d17088b..a21fc496 100644
--- a/chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.h
+++ b/chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.h
@@ -91,6 +91,14 @@
       const std::set<std::string>& allowed_contact_ids) override;
   void OnStart() override;
   void OnStop() override;
+  void Bind(mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver)
+      override;
+
+  // nearby_share::mojom::ContactsManager:
+  void AddDownloadContactsObserver(
+      ::mojo::PendingRemote<nearby_share::mojom::DownloadContactsObserver>
+          observer) override;
+  void DownloadContacts() override;
 
   std::vector<bool> download_contacts_calls_;
   std::vector<std::set<std::string>> set_allowed_contacts_calls_;
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.cc b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.cc
index 941e1b7..c2c0d28 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.cc
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.cc
@@ -32,6 +32,14 @@
   OnStop();
 }
 
+void NearbyShareContactManager::SetAllowedContacts(
+    const std::vector<std::string>& allowed_contacts) {
+  // This is mojo version of the call, but mojo doesn't support sets, so we
+  // have to convert the vector to set.
+  std::set<std::string> set(allowed_contacts.begin(), allowed_contacts.end());
+  SetAllowedContacts(set);
+}
+
 void NearbyShareContactManager::NotifyAllowlistChanged(
     bool were_contacts_added_to_allowlist,
     bool were_contacts_removed_from_allowlist) {
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h
index 461b804..f0c093c 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h
@@ -15,6 +15,9 @@
 #include "base/observer_list_types.h"
 #include "base/optional.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
+#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 // The Nearby Share contacts manager interfaces with the Nearby server in the
 // following ways:
@@ -29,7 +32,7 @@
 //
 // All contact data and update notifications are conveyed via observer methods;
 // the manager does not return data directly from function calls.
-class NearbyShareContactManager {
+class NearbyShareContactManager : public nearby_share::mojom::ContactManager {
  public:
   class Observer : public base::CheckedObserver {
    public:
@@ -44,7 +47,7 @@
   };
 
   NearbyShareContactManager();
-  virtual ~NearbyShareContactManager();
+  ~NearbyShareContactManager() override;
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
@@ -71,6 +74,15 @@
   virtual void SetAllowedContacts(
       const std::set<std::string>& allowed_contact_ids) = 0;
 
+  virtual void Bind(
+      mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver) = 0;
+
+  // nearby_share::mojom::ContactManager:
+  void SetAllowedContacts(
+      const std::vector<std::string>& allowed_contacts) override;
+  // This prevents the mojo version of DownloadContacts from being hidden.
+  using nearby_share::mojom::ContactManager::DownloadContacts;
+
  protected:
   virtual void OnStart() = 0;
   virtual void OnStop() = 0;
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
index 7a84f36..6dddd08 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
@@ -18,6 +18,8 @@
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler.h"
 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_factory.h"
+#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-shared.h"
+#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "components/prefs/pref_service.h"
 
 namespace {
@@ -57,6 +59,52 @@
   return contacts;
 }
 
+nearby_share::mojom::ContactIdentifierPtr ProtoToMojo(
+    const nearbyshare::proto::Contact_Identifier& identifier) {
+  nearby_share::mojom::ContactIdentifierPtr identifier_ptr =
+      nearby_share::mojom::ContactIdentifier::New();
+  switch (identifier.identifier_case()) {
+    case nearbyshare::proto::Contact_Identifier::IdentifierCase::kAccountName:
+      identifier_ptr->set_account_name(identifier.account_name());
+      break;
+    case nearbyshare::proto::Contact_Identifier::IdentifierCase::
+        kObfuscatedGaia:
+      identifier_ptr->set_obfuscated_gaia(identifier.obfuscated_gaia());
+      break;
+    case nearbyshare::proto::Contact_Identifier::IdentifierCase::kPhoneNumber:
+      identifier_ptr->set_phone_number(identifier.phone_number());
+      break;
+    case nearbyshare::proto::Contact_Identifier::IdentifierCase::
+        IDENTIFIER_NOT_SET:
+      NOTREACHED();
+      break;
+  }
+  return identifier_ptr;
+}
+
+nearby_share::mojom::ContactRecordPtr ProtoToMojo(
+    const nearbyshare::proto::ContactRecord& contact_record) {
+  nearby_share::mojom::ContactRecordPtr contact_record_ptr =
+      nearby_share::mojom::ContactRecord::New();
+  contact_record_ptr->id = contact_record.id();
+  contact_record_ptr->person_name = contact_record.person_name();
+  contact_record_ptr->image_url = GURL(contact_record.image_url());
+  for (const auto& identifier : contact_record.identifiers()) {
+    contact_record_ptr->identifiers.push_back(ProtoToMojo(identifier));
+  }
+  return contact_record_ptr;
+}
+
+std::vector<nearby_share::mojom::ContactRecordPtr> ProtoToMojo(
+    const std::vector<nearbyshare::proto::ContactRecord>& contacts) {
+  std::vector<nearby_share::mojom::ContactRecordPtr> mojo_contacts;
+  mojo_contacts.reserve(contacts.size());
+  for (const auto& contact_record : contacts) {
+    mojo_contacts.push_back(ProtoToMojo(contact_record));
+  }
+  return mojo_contacts;
+}
+
 }  // namespace
 
 // static
@@ -140,6 +188,21 @@
   contact_upload_scheduler_->Stop();
 }
 
+void NearbyShareContactManagerImpl::Bind(
+    mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver) {
+  receiver_set_.Add(this, std::move(receiver));
+}
+
+void NearbyShareContactManagerImpl::AddDownloadContactsObserver(
+    ::mojo::PendingRemote<nearby_share::mojom::DownloadContactsObserver>
+        observer) {
+  observers_set_.Add(std::move(observer));
+}
+
+void NearbyShareContactManagerImpl::DownloadContacts() {
+  DownloadContacts(/*only_download_if_changed=*/false);
+}
+
 std::set<std::string> NearbyShareContactManagerImpl::GetAllowedContacts()
     const {
   std::set<std::string> allowlist;
@@ -189,7 +252,9 @@
             GetAllowedContacts(), *contacts));
 
     // Notify observers that the contact list was downloaded.
-    NotifyContactsDownloaded(GetAllowedContacts(), *contacts);
+    std::set<std::string> allowed_contact_ids = GetAllowedContacts();
+    NotifyContactsDownloaded(allowed_contact_ids, *contacts);
+    NotifyMojoObserverContactsDownloaded(allowed_contact_ids, *contacts);
 
     // Request a contacts upload if needed, or process an existing upload
     // request now that we have the access to the full contacts list.
@@ -221,6 +286,11 @@
 }
 
 void NearbyShareContactManagerImpl::OnContactsDownloadFailure() {
+  // Notify mojo remotes.
+  for (auto& remote : observers_set_) {
+    remote->OnContactsDownloadFailed();
+  }
+
   contact_download_scheduler_->HandleResult(/*success=*/false);
 }
 
@@ -289,3 +359,21 @@
 
   return true;
 }
+
+void NearbyShareContactManagerImpl::NotifyMojoObserverContactsDownloaded(
+    const std::set<std::string>& allowed_contact_ids,
+    const std::vector<nearbyshare::proto::ContactRecord>& contacts) {
+  if (observers_set_.empty()) {
+    return;
+  }
+
+  // Mojo doesn't have sets, so we have to copy to an array.
+  std::vector<std::string> allowed_contact_ids_vector(
+      allowed_contact_ids.begin(), allowed_contact_ids.end());
+
+  // Notify mojo remotes.
+  for (auto& remote : observers_set_) {
+    remote->OnContactsDownloaded(allowed_contact_ids_vector,
+                                 ProtoToMojo(contacts));
+  }
+}
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.h b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.h
index c806bcdc..6c3ac1f 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.h
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.h
@@ -13,6 +13,10 @@
 #include "base/optional.h"
 #include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
 
 class NearbyShareClientFactory;
 class NearbyShareContactDownloader;
@@ -74,6 +78,18 @@
       const std::set<std::string>& allowed_contact_ids) override;
   void OnStart() override;
   void OnStop() override;
+  void Bind(mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver)
+      override;
+
+  void NotifyMojoObserverContactsDownloaded(
+      const std::set<std::string>& allowed_contact_ids,
+      const std::vector<nearbyshare::proto::ContactRecord>& contacts);
+
+  // nearby_share::mojom::ContactsManager:
+  void AddDownloadContactsObserver(
+      ::mojo::PendingRemote<nearby_share::mojom::DownloadContactsObserver>
+          observer) override;
+  void DownloadContacts() override;
 
   std::set<std::string> GetAllowedContacts() const;
   void OnContactsDownloadRequested();
@@ -101,6 +117,8 @@
   std::unique_ptr<NearbyShareScheduler> contact_download_scheduler_;
   std::unique_ptr<NearbyShareScheduler> contact_upload_scheduler_;
   std::unique_ptr<NearbyShareContactDownloader> contact_downloader_;
+  mojo::RemoteSet<nearby_share::mojom::DownloadContactsObserver> observers_set_;
+  mojo::ReceiverSet<nearby_share::mojom::ContactManager> receiver_set_;
   base::WeakPtrFactory<NearbyShareContactManagerImpl> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl_unittest.cc b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl_unittest.cc
index 8bbb5af..9c37191 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl_unittest.cc
@@ -22,7 +22,10 @@
 #include "chrome/browser/nearby_sharing/scheduling/fake_nearby_share_scheduler.h"
 #include "chrome/browser/nearby_sharing/scheduling/fake_nearby_share_scheduler_factory.h"
 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_factory.h"
+#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-test-utils.h"
+#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -65,8 +68,20 @@
   for (size_t i = 0; i < num_contacts; ++i) {
     nearbyshare::proto::ContactRecord contact;
     contact.set_id(GetTestContactId(i));
-    contact.add_identifiers()->set_account_name(GetTestContactEmail(i));
-    contact.add_identifiers()->set_phone_number(GetTestContactPhone(i));
+    contact.set_image_url("https://google.com");
+    contact.set_person_name("John Doe");
+    // only one of these fields should be set...
+    switch ((i % 3)) {
+      case 0:
+        contact.add_identifiers()->set_account_name(GetTestContactEmail(i));
+        break;
+      case 1:
+        contact.add_identifiers()->set_phone_number(GetTestContactPhone(i));
+        break;
+      case 2:
+        contact.add_identifiers()->set_obfuscated_gaia("4938tyah");
+        break;
+    }
     contact_list.push_back(contact);
   }
   return contact_list;
@@ -90,6 +105,28 @@
   return contacts;
 }
 
+class TestDownloadContactsObserver
+    : public nearby_share::mojom::DownloadContactsObserver {
+ public:
+  void OnContactsDownloaded(
+      const std::vector<std::string>& allowed_contacts,
+      std::vector<nearby_share::mojom::ContactRecordPtr> contacts) override {
+    allowed_contacts_ = allowed_contacts;
+    contacts_ = std::move(contacts);
+    on_contacts_downloaded_called_ = true;
+  }
+
+  void OnContactsDownloadFailed() override {
+    on_contacts_download_failed_called_ = true;
+  }
+
+  std::vector<std::string> allowed_contacts_;
+  std::vector<nearby_share::mojom::ContactRecordPtr> contacts_;
+  bool on_contacts_downloaded_called_ = false;
+  bool on_contacts_download_failed_called_ = false;
+  mojo::Receiver<nearby_share::mojom::DownloadContactsObserver> receiver_{this};
+};
+
 }  // namespace
 
 class NearbyShareContactManagerImplTest
@@ -119,12 +156,18 @@
 
     manager_ = NearbyShareContactManagerImpl::Factory::Create(
         &pref_service_, &http_client_factory_, &local_device_data_manager_);
+    manager_awaiter_ =
+        std::make_unique<nearby_share::mojom::ContactManagerAsyncWaiter>(
+            manager_.get());
     VerifySchedulerInitialization();
     manager_->AddObserver(this);
+    manager_->AddDownloadContactsObserver(
+        mojo_observer_.receiver_.BindNewPipeAndPassRemote());
     manager_->Start();
   }
 
   void TearDown() override {
+    manager_awaiter_.reset();
     manager_->RemoveObserver(this);
     manager_.reset();
     NearbyShareSchedulerFactory::SetFactoryForTesting(nullptr);
@@ -132,6 +175,10 @@
   }
 
   void DownloadContacts(bool only_download_if_changed) {
+    // Manually reset these before each download.
+    mojo_observer_.on_contacts_downloaded_called_ = false;
+    mojo_observer_.on_contacts_download_failed_called_ = false;
+
     // Verify that the download scheduler is sent request.
     size_t num_requests = download_scheduler()->num_immediate_requests();
     manager_->DownloadContacts(only_download_if_changed);
@@ -194,6 +241,15 @@
     EXPECT_EQ(num_handled_results + 1,
               download_scheduler()->handled_results().size());
     EXPECT_TRUE(download_scheduler()->handled_results().back());
+
+    // Verify the mojo observer was called if we had contacts.
+    mojo_observer_.receiver_.FlushForTesting();
+    EXPECT_EQ(contacts.has_value(),
+              mojo_observer_.on_contacts_downloaded_called_);
+    EXPECT_FALSE(mojo_observer_.on_contacts_download_failed_called_);
+    if (contacts) {
+      VerifyMojoContacts(contacts.value(), mojo_observer_.contacts_);
+    }
   }
 
   void FailDownload(bool expected_only_download_if_changed) {
@@ -205,6 +261,11 @@
     EXPECT_EQ(num_handled_results + 1,
               download_scheduler()->handled_results().size());
     EXPECT_FALSE(download_scheduler()->handled_results().back());
+
+    // Verify the mojo observer was called as well.
+    mojo_observer_.receiver_.FlushForTesting();
+    EXPECT_FALSE(mojo_observer_.on_contacts_downloaded_called_);
+    EXPECT_TRUE(mojo_observer_.on_contacts_download_failed_called_);
   }
 
   void TriggerUploadFromScheduler() {
@@ -397,6 +458,47 @@
     }
   }
 
+  void VerifyMojoContacts(
+      const std::vector<nearbyshare::proto::ContactRecord>& proto_list,
+      const std::vector<nearby_share::mojom::ContactRecordPtr>& mojo_list) {
+    ASSERT_EQ(proto_list.size(), mojo_list.size());
+    int i = 0;
+    for (auto& proto_contact : proto_list) {
+      auto& mojo_contact = mojo_list.at(i++);
+      EXPECT_EQ(proto_contact.id(), mojo_contact->id);
+      EXPECT_EQ(proto_contact.person_name(), mojo_contact->person_name);
+      EXPECT_EQ(GURL(proto_contact.image_url()), mojo_contact->image_url);
+      ASSERT_EQ((size_t)proto_contact.identifiers().size(),
+                mojo_contact->identifiers.size());
+      int j = 0;
+      for (auto& proto_identifier : proto_contact.identifiers()) {
+        auto& mojo_identifier = mojo_contact->identifiers.at(j++);
+        switch (proto_identifier.identifier_case()) {
+          case nearbyshare::proto::Contact_Identifier::IdentifierCase::
+              kAccountName:
+            EXPECT_EQ(proto_identifier.account_name(),
+                      mojo_identifier->get_account_name());
+            break;
+          case nearbyshare::proto::Contact_Identifier::IdentifierCase::
+              kObfuscatedGaia:
+            EXPECT_EQ(proto_identifier.obfuscated_gaia(),
+                      mojo_identifier->get_obfuscated_gaia());
+            break;
+          case nearbyshare::proto::Contact_Identifier::IdentifierCase::
+              kPhoneNumber:
+            EXPECT_EQ(proto_identifier.phone_number(),
+                      mojo_identifier->get_phone_number());
+            break;
+          case nearbyshare::proto::Contact_Identifier::IdentifierCase::
+              IDENTIFIER_NOT_SET:
+            NOTREACHED();
+            break;
+        }
+      }
+    }
+  }
+
+  TestDownloadContactsObserver mojo_observer_;
   std::vector<AllowlistChangedNotification> allowlist_changed_notifications_;
   std::vector<ContactsDownloadedNotification>
       contacts_downloaded_notifications_;
@@ -407,6 +509,9 @@
   FakeNearbyShareSchedulerFactory scheduler_factory_;
   FakeNearbyShareContactDownloader::Factory downloader_factory_;
   std::unique_ptr<NearbyShareContactManager> manager_;
+  std::unique_ptr<nearby_share::mojom::ContactManagerAsyncWaiter>
+      manager_awaiter_;
+  content::BrowserTaskEnvironment task_environment_;
 };
 
 TEST_F(NearbyShareContactManagerImplTest, SetAllowlist) {
diff --git a/chrome/browser/platform_util_win.cc b/chrome/browser/platform_util_win.cc
index 522a4ac..7142fbe 100644
--- a/chrome/browser/platform_util_win.cc
+++ b/chrome/browser/platform_util_win.cc
@@ -109,16 +109,19 @@
   if (escaped_url.length() > kMaxUrlLength)
     return;
 
-  // Specify the user's %TMP% directory as the CWD so that any new proc spawned
-  // does not inherit this proc's CWD. Without this, uninstalls may be broken by
-  // a long-lived child proc that holds a handle to the browser's version
-  // directory.
-  base::FilePath temp_dir;
-  base::PathService::Get(base::DIR_TEMP, &temp_dir);
-  if (reinterpret_cast<ULONG_PTR>(ShellExecuteA(NULL, "open",
-                                                escaped_url.c_str(), NULL,
-                                                temp_dir.AsUTF8Unsafe().c_str(),
-                                                SW_SHOWNORMAL)) <= 32) {
+  // Specify %windir%\system32 as the CWD so that any new proc spawned does not
+  // inherit this proc's CWD. Without this, uninstalls may be broken by a
+  // long-lived child proc that holds a handle to the browser's version
+  // directory (the browser's CWD). A process's CWD is in the standard list of
+  // directories to search when loading a DLL, and precedes the system directory
+  // when safe DLL search mode is disabled (not the default). Setting the CWD to
+  // the system directory is a nice way to mitigate a potential DLL search order
+  // hijack for processes that don't implement their own mitigation.
+  base::FilePath system_dir;
+  base::PathService::Get(base::DIR_SYSTEM, &system_dir);
+  if (reinterpret_cast<ULONG_PTR>(ShellExecuteA(
+          NULL, "open", escaped_url.c_str(), NULL,
+          system_dir.AsUTF8Unsafe().c_str(), SW_SHOWNORMAL)) <= 32) {
     // On failure, it may be good to display a message to the user.
     // https://crbug.com/727913
     return;
diff --git a/chrome/browser/resources/chromeos/crostini_installer/app.js b/chrome/browser/resources/chromeos/crostini_installer/app.js
index c92b178f..ff3c5108 100644
--- a/chrome/browser/resources/chromeos/crostini_installer/app.js
+++ b/chrome/browser/resources/chromeos/crostini_installer/app.js
@@ -412,6 +412,9 @@
       case InstallerState.kInstallImageLoader:
         messageId = 'loadTerminaMessage';
         break;
+      case InstallerState.kStartConcierge:
+        messageId = 'startConciergeMessage';
+        break;
       case InstallerState.kCreateDiskImage:
         messageId = 'createDiskImageMessage';
         break;
@@ -458,6 +461,9 @@
       case InstallerError.kErrorLoadingTermina:
         messageId = 'loadTerminaError';
         break;
+      case InstallerError.kErrorStartingConcierge:
+        messageId = 'startConciergeError';
+        break;
       case InstallerError.kErrorCreatingDiskImage:
         messageId = 'createDiskImageError';
         break;
diff --git a/chrome/browser/resources/chromeos/login/oobe_update.html b/chrome/browser/resources/chromeos/login/oobe_update.html
index 38f633cb..b9633aa 100644
--- a/chrome/browser/resources/chromeos/login/oobe_update.html
+++ b/chrome/browser/resources/chromeos/login/oobe_update.html
@@ -73,9 +73,11 @@
             icon1x="oobe-32:googleg" icon2x="oobe-64:googleg">
         </hd-iron-icon>
         <div slot="subtitle">
-          <span>[[updateStatusMessagePercent]]</span>
+          <span id="better-update-percent">[[updateStatusMessagePercent]]</span>
           <span> | </span>
-          <span>[[updateStatusMessageTimeLeft]]</span>
+          <span id="better-update-timeleft">
+            [[updateStatusMessageTimeLeft]]
+          </span>
         </div>
         <paper-progress slot="progress" id="update-progress"
             value="[[betterUpdateProgressValue]]">
diff --git a/chrome/browser/resources/chromeos/login/oobe_update.js b/chrome/browser/resources/chromeos/login/oobe_update.js
index a24178b..2a6b32a 100644
--- a/chrome/browser/resources/chromeos/login/oobe_update.js
+++ b/chrome/browser/resources/chromeos/login/oobe_update.js
@@ -14,6 +14,13 @@
 const USER_ACTION_REJECT_UPDATE_OVER_CELLUAR = 'update-reject-cellular';
 const USER_ACTION_CANCEL_UPDATE_SHORTCUT = 'cancel-update';
 
+const UNREACHABLE_PERCENT = 1000;
+// Thresholds which are used to determine when update status announcement should
+// take place. Last element is not reachable to simplify implementation.
+const PERCENT_THRESHOLDS = [
+  0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 98, 99, 100, UNREACHABLE_PERCENT
+];
+
 /**
  * Enum for the UI states corresponding to sub steps inside update screen.
  * These values must be kept in sync with UpdateView::UIState in C++ code.
@@ -43,9 +50,7 @@
     'setCancelUpdateShortcutEnabled',
     'showLowBatteryWarningMessage',
     'setUIState',
-    'setUpdateStatusMessagePercent',
-    'setUpdateStatusMessageTimeLeft',
-    'setBetterUpdateProgress',
+    'setUpdateStatus',
     'setAutoTransition',
   ],
 
@@ -184,6 +189,14 @@
       type: Boolean,
       value: true,
     },
+
+    /**
+     * Index of threshold that has been already achieved.
+     */
+    thresholdIndex: {
+      type: Number,
+      value: 0,
+    }
   },
 
   ready() {
@@ -289,22 +302,6 @@
   },
 
   /**
-   * Sets message above the progress bar.
-   * @param {string} message Message that should be set.
-   */
-  setUpdateStatusMessagePercent(message) {
-    this.updateStatusMessagePercent = message;
-  },
-
-  /**
-   * Sets message above the progress bar.
-   * @param {string} message Message that should be set.
-   */
-  setUpdateStatusMessageTimeLeft(message) {
-    this.updateStatusMessageTimeLeft = message;
-  },
-
-  /**
    * Sets which dialog should be shown.
    * @param {UpdateUIState} value Current UI state.
    */
@@ -314,10 +311,27 @@
 
   /**
    * Sets percent to be shown in progress bar.
-   * @param {number} value Current progress
+   * @param {number} percent Current progress
+   * @param {string} messagePercent Message describing current progress.
+   * @param {string} messageTimeLeft Message describing time left.
    */
-  setBetterUpdateProgress(value) {
-    this.betterUpdateProgressValue = value;
+  setUpdateStatus(percent, messagePercent, messageTimeLeft) {
+    // Sets aria-live polite on percent and timeleft container every time new
+    // threshold has been achieved otherwise do not initiate spoken feedback
+    // update by setting aria-live off.
+    if (percent >= PERCENT_THRESHOLDS[this.thresholdIndex]) {
+      while (percent >= PERCENT_THRESHOLDS[this.thresholdIndex]) {
+        this.thresholdIndex = this.thresholdIndex + 1;
+      }
+      this.$['better-update-percent'].setAttribute('aria-live', 'polite');
+      this.$['better-update-timeleft'].setAttribute('aria-live', 'polite');
+    } else {
+      this.$['better-update-timeleft'].setAttribute('aria-live', 'off');
+      this.$['better-update-percent'].setAttribute('aria-live', 'off');
+    }
+    this.betterUpdateProgressValue = percent;
+    this.updateStatusMessagePercent = messagePercent;
+    this.updateStatusMessageTimeLeft = messageTimeLeft;
   },
 
   /**
diff --git a/chrome/browser/resources/nearby_share/shared/BUILD.gn b/chrome/browser/resources/nearby_share/shared/BUILD.gn
index 3aced57d..3924737 100644
--- a/chrome/browser/resources/nearby_share/shared/BUILD.gn
+++ b/chrome/browser/resources/nearby_share/shared/BUILD.gn
@@ -50,6 +50,7 @@
   deps = [
     "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js_library_for_compile",
     "//ui/webui/resources/js:cr",
+    "//url/mojom:url_mojom_gurl_js_library_for_compile",
   ]
 }
 
@@ -141,6 +142,7 @@
   deps = [
     "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js_library_for_compile",
     "//ui/webui/resources/js:cr.m",
+    "//url/mojom:url_mojom_gurl_js_library_for_compile",
   ]
   extra_deps = [ ":modulize" ]
 }
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_share_settings.html b/chrome/browser/resources/nearby_share/shared/nearby_share_settings.html
index a7c3461..1ac68182 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_share_settings.html
+++ b/chrome/browser/resources/nearby_share/shared/nearby_share_settings.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
 
+<script src="chrome://resources/mojo/url/mojom/url.mojom-lite.js"></script>
 <script src="/mojo/nearby_share_settings.mojom-lite.js"></script>
 <script src="nearby_share_settings.js"></script>
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_share_settings.js b/chrome/browser/resources/nearby_share/shared/nearby_share_settings.js
index 5fd6e04..067c356 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_share_settings.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_share_settings.js
@@ -4,6 +4,7 @@
 
 // clang-format off
 // #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+// #import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js'
 // #import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
 // #import '/mojo/nearby_share_settings.mojom-lite.js';
 // clang-format on
diff --git a/chrome/browser/resources/new_tab_page/realbox_dropdown.html b/chrome/browser/resources/new_tab_page/realbox_dropdown.html
index f19be8e..e963cb22 100644
--- a/chrome/browser/resources/new_tab_page/realbox_dropdown.html
+++ b/chrome/browser/resources/new_tab_page/realbox_dropdown.html
@@ -23,7 +23,6 @@
   .header {
     align-items: center;
     display: flex;
-    justify-content: space-between;
     margin-top: 8px;
     outline: none;
     padding-bottom: 6px;
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_manager.js b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_manager.js
index 4f0f2c00..9f4c54f 100644
--- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_manager.js
+++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_manager.js
@@ -4,6 +4,7 @@
 
 // clang-format off
 // #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+// #import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js'
 // #import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
 // #import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
 // #import '/mojo/nearby_share_target_types.mojom-lite.js';
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index d9476a4..be71e3a 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -330,9 +330,14 @@
   return false;
 #else
   Profile* profile = Profile::FromBrowserContext(GetBrowserContext());
+  if (!profile)
+    return false;
+  AdvancedProtectionStatusManager* advanced_protection_status_manager =
+      AdvancedProtectionStatusManagerFactory::GetForProfile(profile);
+  if (!advanced_protection_status_manager)
+    return false;
   return base::FeatureList::IsEnabled(kPromptAppForDeepScanning) &&
-         AdvancedProtectionStatusManagerFactory::GetForProfile(profile)
-             ->IsUnderAdvancedProtection();
+         advanced_protection_status_manager->IsUnderAdvancedProtection();
 #endif
 }
 
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
index 50e3e1a..acfae3d5 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
@@ -7,39 +7,49 @@
 import android.content.Context;
 import android.net.Uri;
 
+import org.chromium.blink.mojom.TextFragmentSelectorProducer;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.share_sheet.ChromeOptionShareCallback;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.components.browser_ui.share.ShareParams;
-import org.chromium.ui.base.WindowAndroid;
+import org.chromium.services.service_manager.InterfaceProvider;
 
 /**
  * Handles the Link To Text action in the Sharing Hub.
  */
-public class LinkToTextCoordinator {
+public class LinkToTextCoordinator extends EmptyTabObserver {
     private static final String SHARE_TEXT_TEMPLATE = "\"%s\"\n%s";
     private static final String TEXT_FRAGMENT_PREFIX = ":~:text=";
+    private static final String INVALID_SELECTOR = "";
     private final Context mContext;
-    private final WindowAndroid mWindow;
     private final ChromeOptionShareCallback mChromeOptionShareCallback;
     private final String mVisibleUrl;
     private final String mSelectedText;
+    private final Tab mTab;
 
-    public LinkToTextCoordinator(Context context, WindowAndroid window,
+    private TextFragmentSelectorProducer mProducer;
+    private boolean mCancelRequest;
+
+    public LinkToTextCoordinator(Context context, Tab tab,
             ChromeOptionShareCallback chromeOptionShareCallback, String visibleUrl,
             String selectedText) {
         mContext = context;
-        mWindow = window;
         mChromeOptionShareCallback = chromeOptionShareCallback;
         mVisibleUrl = visibleUrl;
         mSelectedText = selectedText;
+        mTab = tab;
+        mTab.addObserver(this);
+        mCancelRequest = false;
 
-        // TODO(1102382): Replace following line with a request to create text fragment selector and
-        // pass |OnSelectorReady| as callback.
-        onSelectorReady("");
+        requestSelector();
     }
 
     public void onSelectorReady(String selector) {
+        if (mCancelRequest) return;
+
         String successMessage =
                 mContext.getResources().getString(R.string.link_to_text_success_message);
         String failureMessage =
@@ -48,9 +58,10 @@
         // TODO(1102382): Consider creating SharedParams on sharesheet side. In that case there will
         // be no need to keep the WindowAndroid in this class.
         String textToShare = getTextToShare(selector);
-        ShareParams params = new ShareParams.Builder(mWindow, /*title=*/"", /*url=*/"")
-                                     .setText(textToShare)
-                                     .build();
+        ShareParams params =
+                new ShareParams.Builder(mTab.getWindowAndroid(), /*title=*/"", /*url=*/"")
+                        .setText(textToShare)
+                        .build();
 
         ChromeShareExtras chromeShareExtras = new ChromeShareExtras.Builder().build();
         mChromeOptionShareCallback.showThirdPartyShareSheetWithMessage(
@@ -58,6 +69,23 @@
                 System.currentTimeMillis());
     }
 
+    public void requestSelector() {
+        if (mTab.getWebContents().getMainFrame() != mTab.getWebContents().getFocusedFrame()) {
+            onSelectorReady(INVALID_SELECTOR);
+            return;
+        }
+
+        InterfaceProvider interfaces = mTab.getWebContents().getMainFrame().getRemoteInterfaces();
+        mProducer = interfaces.getInterface(TextFragmentSelectorProducer.MANAGER);
+        mProducer.generateSelector(new TextFragmentSelectorProducer.GenerateSelectorResponse() {
+            @Override
+            public void call(String selector) {
+                onSelectorReady(selector);
+                cleanup();
+            }
+        });
+    }
+
     public String getTextToShare(String selector) {
         String url = mVisibleUrl;
         if (!selector.isEmpty()) {
@@ -67,4 +95,29 @@
         }
         return String.format(SHARE_TEXT_TEMPLATE, mSelectedText, url);
     }
+
+    // Discard results if tab is not on foreground anymore.
+    @Override
+    public void onHidden(Tab tab, @TabHidingType int type) {
+        cleanup();
+    }
+
+    // Discard results if tab content is changed by typing new URL in omnibox.
+    @Override
+    public void onUpdateUrl(Tab tab, String url) {
+        cleanup();
+    }
+
+    // Discard results if tab content crashes.
+    @Override
+    public void onCrash(Tab tab) {
+        cleanup();
+    }
+
+    private void cleanup() {
+        // TODO(gayane): Consider canceling request in renderer.
+        if (mProducer != null) mProducer.close();
+        mCancelRequest = true;
+        mTab.removeObserver(this);
+    }
 }
\ No newline at end of file
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
index 70d2875..823d950 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
@@ -97,7 +97,8 @@
         mOrderedFirstPartyOptions = new ArrayList<>();
         initializeFirstPartyOptionsInOrder();
         mChromeOptionShareCallback = chromeOptionShareCallback;
-        mUrl = getUrlToShare(shareParams, chromeShareExtras, mTabProvider.get().getUrl().getSpec());
+        mUrl = getUrlToShare(shareParams, chromeShareExtras,
+                mTabProvider.get().isInitialized() ? mTabProvider.get().getUrl().getSpec() : "");
     }
 
     /**
@@ -340,10 +341,9 @@
                 .setIcon(R.drawable.link, R.string.sharing_highlights)
                 .setFeatureNameForMetrics("SharingHubAndroid.LinkToTextSelected")
                 .setOnClickCallback((view) -> {
-                    LinkToTextCoordinator linkToTextCoordinator = new LinkToTextCoordinator(
-                            mActivity, mTabProvider.get().getWindowAndroid(),
-                            mChromeOptionShareCallback, mShareParams.getUrl(),
-                            mShareParams.getText());
+                    LinkToTextCoordinator linkToTextCoordinator =
+                            new LinkToTextCoordinator(mActivity, mTabProvider.get(),
+                                    mChromeOptionShareCallback, mUrl, mShareParams.getText());
                 })
                 .build();
     }
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
index f768a0cfd..2a5b5d1 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
@@ -11,6 +11,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
+import android.content.Context;
 
 import androidx.test.filters.SmallTest;
 
@@ -19,11 +20,14 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.share.share_sheet.ChromeOptionShareCallback;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
 
 /**
@@ -31,10 +35,26 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 public class LinkToTextCoordinatorTest {
+    // Mock class for |LinkToTextCoordinator| that disables |requestSelector| call.
+    private class MockLinkToTextCoordinator extends LinkToTextCoordinator {
+        public MockLinkToTextCoordinator(Context context, Tab tab,
+                ChromeOptionShareCallback chromeOptionShareCallback, String visibleUrl,
+                String selectedText) {
+            super(context, tab, chromeOptionShareCallback, visibleUrl, selectedText);
+        }
+
+        @Override
+        public void requestSelector() {}
+    };
+
     @Mock
     private ChromeOptionShareCallback mShareCallback;
     @Mock
     private WindowAndroid mWindow;
+    @Mock
+    private Tab mTab;
+    @Mock
+    private WebContents mWebContents;
 
     private Activity mAcivity;
     private static final String SELECTED_TEXT = "selection";
@@ -47,6 +67,8 @@
         doNothing()
                 .when(mShareCallback)
                 .showThirdPartyShareSheetWithMessage(anyString(), any(), any(), anyLong());
+        Mockito.when(mTab.getWebContents()).thenReturn(mWebContents);
+        Mockito.when(mTab.getWindowAndroid()).thenReturn(mWindow);
     }
 
     @Test
@@ -54,8 +76,8 @@
     public void getTextToShareTest() {
         String selector = "selector";
         String expectedTextToShare = "\"selection\"\nwww.example.com#:~:text=selector";
-        LinkToTextCoordinator coordinator = new LinkToTextCoordinator(
-                mAcivity, mWindow, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
         Assert.assertEquals(expectedTextToShare, coordinator.getTextToShare(selector));
     }
 
@@ -64,8 +86,8 @@
     public void getTextToShareTest_URLWithFragment() {
         String selector = "selector";
         String expectedTextToShare = "\"selection\"\nwww.example.com#:~:text=selector";
-        LinkToTextCoordinator coordinator = new LinkToTextCoordinator(
-                mAcivity, mWindow, mShareCallback, VISIBLE_URL + "#elementid", SELECTED_TEXT);
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL + "#elementid", SELECTED_TEXT);
         Assert.assertEquals(expectedTextToShare, coordinator.getTextToShare(selector));
     }
 
@@ -74,17 +96,29 @@
     public void getTextToShareTest_EmptySelector() {
         String selector = "";
         String expectedTextToShare = "\"selection\"\nwww.example.com";
-        LinkToTextCoordinator coordinator = new LinkToTextCoordinator(
-                mAcivity, mWindow, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
         Assert.assertEquals(expectedTextToShare, coordinator.getTextToShare(selector));
     }
 
     @Test
     @SmallTest
     public void onSelectorReadyTest() {
-        LinkToTextCoordinator coordinator = new LinkToTextCoordinator(
-                mAcivity, mWindow, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
         // OnSelectorReady should call back the share sheet.
+        coordinator.onSelectorReady("selector");
+        verify(mShareCallback)
+                .showThirdPartyShareSheetWithMessage(anyString(), any(), any(), anyLong());
+    }
+
+    @Test
+    @SmallTest
+    public void onSelectorReadyTest_EmptySelector() {
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
+        // OnSelectorReady should call back the share sheet.
+        coordinator.onSelectorReady("");
         verify(mShareCallback)
                 .showThirdPartyShareSheetWithMessage(anyString(), any(), any(), anyLong());
     }
diff --git a/chrome/browser/signin/chrome_signin_helper_unittest.cc b/chrome/browser/signin/chrome_signin_helper_unittest.cc
index a3e9d3a..3b743d4 100644
--- a/chrome/browser/signin/chrome_signin_helper_unittest.cc
+++ b/chrome/browser/signin/chrome_signin_helper_unittest.cc
@@ -46,14 +46,13 @@
   ~TestRequestInterceptor() override = default;
 
  private:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
     std::string response_headers =
         base::StringPrintf("HTTP/1.1 200 OK\n\n%s: %s\n", header_name_.c_str(),
                            header_value_.c_str());
-    return new net::URLRequestTestJob(request, network_delegate,
-                                      response_headers, "", true);
+    return std::make_unique<net::URLRequestTestJob>(request, response_headers,
+                                                    "", true);
   }
 
   const std::string header_name_;
diff --git a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
index 6b1b1e6..1d90b85 100644
--- a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
+++ b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
@@ -31,12 +31,12 @@
 // The total number of Chrome accelerators (available on Chrome OS).
 constexpr int kChromeAcceleratorsTotalNum = 97;
 // The hash of Chrome accelerators (available on Chrome OS).
-constexpr char kChromeAcceleratorsHash[] = "efe4c88a2b35238274820158dd46fc0a";
+constexpr char kChromeAcceleratorsHash[] = "ae0381db45f4d0fde37ff78948963369";
 #else
 // The total number of Chrome accelerators (available on Chrome OS).
 constexpr int kChromeAcceleratorsTotalNum = 96;
 // The hash of Chrome accelerators (available on Chrome OS).
-constexpr char kChromeAcceleratorsHash[] = "9a9ca7beaf63ecdbfdb94aad12ed2c68";
+constexpr char kChromeAcceleratorsHash[] = "a4be06225eea30653044ea033b8b702d";
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
 const char* BooleanToString(bool value) {
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 6d6a3b9..e1fe26002 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -499,10 +499,10 @@
     return;
 
   // Trying to open a background tab when in an app browser results in
-  // focusing a regular browser window an opening a tab in the background
+  // focusing a regular browser window and opening a tab in the background
   // of that window. Change the disposition to NEW_FOREGROUND_TAB so that
   // the new tab is focused.
-  if (source_browser && source_browser->deprecated_is_app() &&
+  if (source_browser && source_browser->is_type_app() &&
       params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
     params->disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
   }
diff --git a/chrome/browser/ui/browser_window_state.cc b/chrome/browser/ui/browser_window_state.cc
index 8059886..6a677ad 100644
--- a/chrome/browser/ui/browser_window_state.cc
+++ b/chrome/browser/ui/browser_window_state.cc
@@ -58,7 +58,7 @@
 
   base::DictionaryValue* Get() override {
     base::DictionaryValue* all_apps_dict = DictionaryPrefUpdate::Get();
-    base::DictionaryValue* this_app_dict_weak = NULL;
+    base::DictionaryValue* this_app_dict_weak = nullptr;
     if (!all_apps_dict->GetDictionary(window_name_, &this_app_dict_weak)) {
       auto this_app_dict = std::make_unique<base::DictionaryValue>();
       this_app_dict_weak = this_app_dict.get();
@@ -114,8 +114,8 @@
   const base::DictionaryValue* app_windows =
       prefs->GetDictionary(prefs::kAppWindowPlacement);
   if (!app_windows)
-    return NULL;
-  const base::DictionaryValue* to_return = NULL;
+    return nullptr;
+  const base::DictionaryValue* to_return = nullptr;
   app_windows->GetDictionary(window_name, &to_return);
   return to_return;
 }
@@ -123,7 +123,7 @@
 bool ShouldSaveWindowPlacement(const Browser* browser) {
   // Never track app popup windows that do not have a trusted source (i.e.
   // popup windows spawned by an app).  See similar code in
-  //   SessionService::ShouldTrackBrowser().
+  // SessionService::ShouldTrackBrowser().
   return !browser->deprecated_is_app() || browser->is_trusted_source();
 }
 
@@ -131,10 +131,7 @@
   // Applications other than web apps (such as devtools) save their window size.
   // Web apps, on the other hand, have the same behavior as popups, and save
   // their content bounds.
-  bool is_app_with_window_bounds =
-      browser->deprecated_is_app() &&
-      !web_app::AppBrowserController::IsForWebAppBrowser(browser);
-  return !browser->is_type_normal() && !is_app_with_window_bounds &&
+  return !browser->is_type_normal() && !browser->is_type_devtools() &&
          !browser->is_trusted_source();
 }
 
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index 8d9cd48d..c084818 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -472,8 +472,10 @@
           omnibox::kEntitySuggestionsReduceLatency,
           OmniboxFieldTrial::kEntitySuggestionsReduceLatencyDecoderWakeupParam,
           false)) {
-    BitmapFetcherServiceFactory::GetForBrowserContext(profile_)
-        ->WakeupDecoder();
+    if (auto* service =
+            BitmapFetcherServiceFactory::GetForBrowserContext(profile_)) {
+      service->WakeupDecoder();
+    }
   }
 }
 
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index 23d2949f4..ab47a7e 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -41,6 +41,7 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/ntp_tiles/constants.h"
 #include "components/omnibox/browser/omnibox_view.h"
+#include "components/omnibox/common/omnibox_features.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_entry.h"
@@ -1162,10 +1163,17 @@
 
   // Verify that the omnibox displays |slow_url|.
   OmniboxView* view = browser()->window()->GetLocationBar()->GetOmniboxView();
-  std::string omnibox_text = base::UTF16ToUTF8(view->GetText());
-  EXPECT_THAT(omnibox_text, ::testing::StartsWith(slow_url.host()));
-  EXPECT_THAT(omnibox_text, ::testing::EndsWith(slow_url.path()));
-  EXPECT_THAT(slow_url.spec(), ::testing::EndsWith(omnibox_text));
+  // Depending on field trial configuration, the omnibox text might contain the
+  // full URL or just a portion of it.
+  if (base::FeatureList::IsEnabled(
+          omnibox::kRevealSteadyStateUrlPathQueryAndRefOnHover)) {
+    EXPECT_EQ(base::ASCIIToUTF16(slow_url.spec()), view->GetText());
+  } else {
+    std::string omnibox_text = base::UTF16ToUTF8(view->GetText());
+    EXPECT_THAT(omnibox_text, ::testing::StartsWith(slow_url.host()));
+    EXPECT_THAT(omnibox_text, ::testing::EndsWith(slow_url.path()));
+    EXPECT_THAT(slow_url.spec(), ::testing::EndsWith(omnibox_text));
+  }
 }
 
 // Verifies that Chrome won't spawn a separate renderer process for
diff --git a/chrome/browser/ui/uma_browsing_activity_observer.cc b/chrome/browser/ui/uma_browsing_activity_observer.cc
index a3464b18..4e251cf 100644
--- a/chrome/browser/ui/uma_browsing_activity_observer.cc
+++ b/chrome/browser/ui/uma_browsing_activity_observer.cc
@@ -144,8 +144,8 @@
                                   browser->tab_strip_model()->count(), 1, 200,
                                   50);
     }
-
-    if (browser->deprecated_is_app())
+    if (browser->is_type_app() || browser->is_type_app_popup() ||
+        browser->is_type_devtools())
       app_window_count++;
     else if (browser->is_type_popup())
       popup_window_count++;
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc
index 93e3ea2d8..caa411e 100644
--- a/chrome/browser/ui/views/accelerator_table.cc
+++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -44,7 +44,7 @@
     {ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR,
      IDC_CLOSE_WINDOW},
     {ui::VKEY_F, ui::EF_PLATFORM_ACCELERATOR, IDC_FIND},
-    {ui::VKEY_E, ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR,
+    {ui::VKEY_A, ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR,
      IDC_TAB_SEARCH},
     {ui::VKEY_G, ui::EF_PLATFORM_ACCELERATOR, IDC_FIND_NEXT},
     {ui::VKEY_G, ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
index fa4e9f5f..ada7078c 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -72,6 +72,11 @@
   DCHECK(bb_model_);
   DCHECK(bb_model_->client()->CanBeEditedByUser(parent));
   SetCanResize(true);
+  SetModalType(ui::MODAL_TYPE_WINDOW);
+  SetShowCloseButton(false);
+  SetAcceptCallback(base::BindOnce(&BookmarkEditorView::ApplyEdits,
+                                   base::Unretained(this), nullptr));
+  SetTitle(details_.GetWindowTitleId());
   SetButtonLabel(ui::DIALOG_BUTTON_OK, l10n_util::GetStringUTF16(IDS_SAVE));
   if (show_tree_) {
     new_folder_button_ =
@@ -102,32 +107,6 @@
   return true;
 }
 
-ui::ModalType BookmarkEditorView::GetModalType() const {
-  return ui::MODAL_TYPE_WINDOW;
-}
-
-bool BookmarkEditorView::ShouldShowCloseButton() const {
-  return false;
-}
-
-base::string16 BookmarkEditorView::GetWindowTitle() const {
-  return l10n_util::GetStringUTF16(details_.GetWindowTitleId());
-}
-
-bool BookmarkEditorView::Accept() {
-  if (!IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)) {
-    if (details_.GetNodeType() != BookmarkNode::FOLDER) {
-      // The url is invalid, focus the url field.
-      url_tf_->SelectAll(true);
-      url_tf_->RequestFocus();
-    }
-    return false;
-  }
-  // Otherwise save changes and close the dialog box.
-  ApplyEdits();
-  return true;
-}
-
 gfx::Size BookmarkEditorView::CalculatePreferredSize() const {
   if (!show_tree_)
     return views::View::CalculatePreferredSize();
@@ -523,23 +502,18 @@
   return nullptr;
 }
 
-void BookmarkEditorView::ApplyEdits() {
+void BookmarkEditorView::ApplyEdits(EditorNode* parent) {
   DCHECK(bb_model_->loaded());
 
-  if (tree_view_)
-    tree_view_->CommitEdit();
+  if (!parent) {
+    if (tree_view_)
+      tree_view_->CommitEdit();
 
-  EditorNode* parent =
-      show_tree_ ? tree_model_->AsNode(tree_view_->GetSelectedNode()) : nullptr;
-  if (show_tree_ && !parent) {
-    NOTREACHED();
-    return;
+    if (show_tree_) {
+      parent = tree_model_->AsNode(tree_view_->GetSelectedNode());
+      DCHECK(parent);
+    }
   }
-  ApplyEdits(parent);
-}
-
-void BookmarkEditorView::ApplyEdits(EditorNode* parent) {
-  DCHECK(!show_tree_ || parent);
 
   // We're going to apply edits to the bookmark bar model, which will call us
   // back. Normally when a structural edit occurs we reset the tree model.
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
index 004734e..cd2c28b 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
@@ -80,10 +80,6 @@
 
   // views::DialogDelegateView:
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
-  ui::ModalType GetModalType() const override;
-  bool ShouldShowCloseButton() const override;
-  base::string16 GetWindowTitle() const override;
-  bool Accept() override;
 
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
@@ -175,12 +171,10 @@
   // Returns the node with the specified id, or NULL if one can't be found.
   EditorNode* FindNodeWithID(BookmarkEditorView::EditorNode* node, int64_t id);
 
-  // Invokes ApplyEdits with the selected node.
-  void ApplyEdits();
-
   // Applies the edits done by the user. |parent| gives the parent of the URL
-  // being edited.
-  void ApplyEdits(EditorNode* parent);
+  // being edited. If |parent| is null, the selected node from the treeview's
+  // parent is used.
+  void ApplyEdits(EditorNode* parent = nullptr);
 
   // Recursively adds newly created folders and sets the title of nodes to
   // match the user edited title.
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
index 9c41e7f..602173ab 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -205,9 +205,12 @@
 
 IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogViewTest, NotifyDelegate) {
   {
-    // User presses install.
+    // User presses install. Note that we have to wait for the 0ms delay for
+    // the install button to become enabled, hence the RunLoop later.
+    ExtensionInstallDialogView::SetInstallButtonDelayForTesting(0);
     ExtensionInstallPromptTestHelper helper;
     ExtensionInstallDialogView* delegate_view = CreateAndShowPrompt(&helper);
+    base::RunLoop().RunUntilIdle();
     delegate_view->AcceptDialog();
     EXPECT_EQ(ExtensionInstallPrompt::Result::ACCEPTED, helper.result());
   }
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_supervised_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_supervised_browsertest.cc
index 5867f1f9..d778020 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_supervised_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_supervised_browsertest.cc
@@ -150,8 +150,10 @@
   task_runner->FastForwardBy(duration);
 
   // Supervised user presses "Ask a parent".
+  ExtensionInstallDialogView::SetInstallButtonDelayForTesting(0);
   ExtensionInstallDialogView* delegate_view =
       CreateAndShowPrompt(&helper, install_prompt.GetPromptForTesting());
+  base::RunLoop().RunUntilIdle();
   delegate_view->AcceptDialog();
   EXPECT_EQ(ExtensionInstallPrompt::Result::ACCEPTED, helper.result());
 
diff --git a/chrome/browser/ui/views/frame/browser_window_property_manager_win.cc b/chrome/browser/ui/views/frame/browser_window_property_manager_win.cc
index 0d45e05..2f3923ce 100644
--- a/chrome/browser/ui/views/frame/browser_window_property_manager_win.cc
+++ b/chrome/browser/ui/views/frame/browser_window_property_manager_win.cc
@@ -51,13 +51,14 @@
   // Set the app user model id for this application to that of the application
   // name. See http://crbug.com/7028.
   base::string16 app_id =
-      browser->deprecated_is_app()
+      browser->is_type_app() || browser->is_type_app_popup() ||
+              browser->is_type_devtools()
           ? shell_integration::win::GetAppUserModelIdForApp(
                 base::UTF8ToWide(browser->app_name()), profile->GetPath())
           : shell_integration::win::GetAppUserModelIdForBrowser(
                 profile->GetPath());
   // Apps set their relaunch details based on app's details.
-  if (browser->deprecated_is_app()) {
+  if (browser->is_type_app() || browser->is_type_app_popup()) {
     ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
     const extensions::Extension* extension = registry->GetExtensionById(
         web_app::GetAppIdFromApplicationName(browser->app_name()),
@@ -77,9 +78,9 @@
   base::FilePath icon_path;
   base::string16 command_line_string;
   base::string16 pinned_name;
-  if (!browser->deprecated_is_app() && shortcut_manager &&
+  if ((browser->is_type_normal() || browser->is_type_popup()) &&
+      shortcut_manager &&
       profile->GetPrefs()->HasPrefPath(prefs::kProfileIconVersion)) {
-
     // Set relaunch details to use profile.
     base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
     shortcut_manager->GetShortcutProperties(profile->GetPath(), &command_line,
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 2576da6580..2889d7f9 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -650,33 +650,32 @@
 
   // If rich autocompletion is enabled, split |location_bounds| for the
   // |omnibox_view_| and |omnibox_additional_text_view_|.
-  if (OmniboxFieldTrial::RichAutocompletionShowAdditionalText()) {
-    if (OmniboxFieldTrial::RichAutocompletionTwoLineOmnibox()) {
-      // Split vertically.
-      auto omnibox_bounds = location_bounds;
-      omnibox_bounds.set_height(location_bounds.height() / 2);
-      omnibox_view_->SetBoundsRect(omnibox_bounds);
-      auto omnibox_additional_text_bounds = omnibox_bounds;
-      omnibox_additional_text_bounds.set_x(location_bounds.x() + 3);
-      omnibox_additional_text_bounds.set_y(omnibox_bounds.bottom());
-      omnibox_additional_text_view_->SetBoundsRect(
-          omnibox_additional_text_bounds);
+  if (OmniboxFieldTrial::RichAutocompletionShowAdditionalText() &&
+      OmniboxFieldTrial::RichAutocompletionTwoLineOmnibox()) {
+    // Split vertically.
+    auto omnibox_bounds = location_bounds;
+    omnibox_bounds.set_height(location_bounds.height() / 2);
+    omnibox_view_->SetBoundsRect(omnibox_bounds);
+    auto omnibox_additional_text_bounds = omnibox_bounds;
+    omnibox_additional_text_bounds.set_x(location_bounds.x() + 3);
+    omnibox_additional_text_bounds.set_y(omnibox_bounds.bottom());
+    omnibox_additional_text_view_->SetBoundsRect(
+        omnibox_additional_text_bounds);
 
-    } else if (!omnibox_view_->GetText().empty()) {
-      // Split horizontally.
-      auto omnibox_bounds = location_bounds;
-      omnibox_bounds.set_width(std::min(
-          omnibox_view_->GetUnelidedTextWidth() + 10, location_bounds.width()));
-      omnibox_view_->SetBoundsRect(omnibox_bounds);
-      auto omnibox_additional_text_bounds = location_bounds;
-      omnibox_additional_text_bounds.set_x(omnibox_bounds.x() +
-                                           omnibox_bounds.width());
-      omnibox_additional_text_bounds.set_width(
-          std::max(location_bounds.width() - omnibox_bounds.width(), 0));
-      omnibox_additional_text_view_->SetBoundsRect(
-          omnibox_additional_text_bounds);
-    }
-
+  } else if (OmniboxFieldTrial::RichAutocompletionShowAdditionalText() &&
+             !omnibox_view_->GetText().empty()) {
+    // Split horizontally.
+    auto omnibox_bounds = location_bounds;
+    omnibox_bounds.set_width(std::min(
+        omnibox_view_->GetUnelidedTextWidth() + 10, location_bounds.width()));
+    omnibox_view_->SetBoundsRect(omnibox_bounds);
+    auto omnibox_additional_text_bounds = location_bounds;
+    omnibox_additional_text_bounds.set_x(omnibox_bounds.x() +
+                                         omnibox_bounds.width());
+    omnibox_additional_text_bounds.set_width(
+        std::max(location_bounds.width() - omnibox_bounds.width(), 0));
+    omnibox_additional_text_view_->SetBoundsRect(
+        omnibox_additional_text_bounds);
   } else {
     omnibox_view_->SetBoundsRect(location_bounds);
   }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
index 0d106eb..6c3d221 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
@@ -536,7 +536,10 @@
   ui_test_utils::NavigateToURL(browser(),
                                GURL("http://example.com/#%E2%98%83"));
 
-  EXPECT_EQ(view->GetText(), base::UTF8ToUTF16("example.com/#\u2603"));
+  EXPECT_EQ(view->GetText(),
+            OmniboxFieldTrial::ShouldRevealPathQueryRefOnHover()
+                ? base::UTF8ToUTF16("http://example.com/#\u2603")
+                : base::UTF8ToUTF16("example.com/#\u2603"));
 }
 
 // Ensure that when the user navigates between suggestions, that the accessible
@@ -725,23 +728,40 @@
       static_cast<OmniboxViewViews*>(omnibox_view);
 
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL url = embedded_test_server()->GetURL("/title1.html");
+  // Use a hostname ("a.test") since IP addresses aren't eligible for eliding.
+  GURL url = embedded_test_server()->GetURL("a.test", "/title1.html");
   base::string16 url_text = base::ASCIIToUTF16(url.spec());
 
   ui_test_utils::NavigateToURL(browser(), url);
 
-  // By default, the elided URL should be shown.
-  EXPECT_EQ(url_text,
-            base::ASCIIToUTF16("http://") + omnibox_view_views->GetText());
+  // By default, the URL should be elided. Depending on field trial
+  // configuration, this will be implemented by pushing the scheme out of the
+  // display area or by eliding it from the actual text.
+  if (OmniboxFieldTrial::ShouldRevealPathQueryRefOnHover()) {
+    EXPECT_EQ(url_text, omnibox_view_views->GetText());
+    EXPECT_GT(
+        0, omnibox_view_views->GetRenderText()->GetUpdatedDisplayOffset().x());
+  } else {
+    EXPECT_EQ(url_text,
+              base::ASCIIToUTF16("http://") + omnibox_view_views->GetText());
+  }
 
   // After toggling the setting, the full URL should be shown.
   chrome::ToggleShowFullURLs(browser());
   EXPECT_EQ(url_text, omnibox_view_views->GetText());
+  EXPECT_EQ(0,
+            omnibox_view_views->GetRenderText()->GetUpdatedDisplayOffset().x());
 
   // Toggling the setting again should go back to the elided URL.
   chrome::ToggleShowFullURLs(browser());
-  EXPECT_EQ(url_text,
-            base::ASCIIToUTF16("http://") + omnibox_view_views->GetText());
+  if (OmniboxFieldTrial::ShouldRevealPathQueryRefOnHover()) {
+    EXPECT_EQ(url_text, omnibox_view_views->GetText());
+    EXPECT_GT(
+        0, omnibox_view_views->GetRenderText()->GetUpdatedDisplayOffset().x());
+  } else {
+    EXPECT_EQ(url_text,
+              base::ASCIIToUTF16("http://") + omnibox_view_views->GetText());
+  }
 }
 
 // The following set of tests require UIA accessibility support, which only
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index d798dd1..1736942 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -194,6 +194,8 @@
                              const GURL& url);
   ~InternalPageInfoBubbleView() override;
 
+  gfx::Size CalculatePreferredSize() const override;
+
   DISALLOW_COPY_AND_ASSIGN(InternalPageInfoBubbleView);
 };
 
@@ -405,11 +407,16 @@
 
   views::BubbleDialogDelegateView::CreateBubble(this);
 
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
+      ChromeLayoutProvider::Get()->GetDistanceMetric(
+          views::DISTANCE_RELATED_CONTROL_VERTICAL)));
+
   // Use a normal label's style for the title since there is no content.
   views::Label* title_label =
       static_cast<views::Label*>(GetBubbleFrameView()->title());
   title_label->SetFontList(views::Label::GetDefaultFontList());
-  title_label->SetMultiLine(false);
+  title_label->SetMultiLine(true);
   title_label->SetElideBehavior(gfx::NO_ELIDE);
 
   SizeToContents();
@@ -417,6 +424,20 @@
 
 InternalPageInfoBubbleView::~InternalPageInfoBubbleView() {}
 
+gfx::Size InternalPageInfoBubbleView::CalculatePreferredSize() const {
+  // Without a layout manager this will recurse infinitely
+  // (GetHeightForWidth() calls CalculatePreferredSize()).
+  // TODO(crbug.com/1128500): Fix infinite recursion or always
+  // install a layout manager.
+  if (!GetLayoutManager())
+    return gfx::Size();
+
+  const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
+                        DISTANCE_BUBBLE_PREFERRED_WIDTH) -
+                    margins().width();
+  return gfx::Size(width, GetHeightForWidth(width));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // PageInfoBubbleView
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc
index 5cf3ff67..3cb5f1e 100644
--- a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.cc
@@ -34,6 +34,7 @@
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
+#include "ui/views/controls/color_tracking_icon_view.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
@@ -94,15 +95,12 @@
 
   if (visible_requests_[0]->GetContentSettingsType() ==
       ContentSettingsType::PLUGINS) {
-    auto learn_more_button = views::CreateVectorImageButton(this);
+    auto* learn_more_button =
+        SetExtraView(views::CreateVectorImageButtonWithNativeTheme(
+            this, vector_icons::kHelpOutlineIcon));
     learn_more_button->SetFocusForPlatform();
     learn_more_button->SetTooltipText(
         l10n_util::GetStringUTF16(IDS_LEARN_MORE));
-    SkColor text_color = GetNativeTheme()->GetSystemColor(
-        ui::NativeTheme::kColorId_LabelEnabledColor);
-    views::SetImageFromVectorIcon(learn_more_button.get(),
-                                  vector_icons::kHelpOutlineIcon, text_color);
-    SetExtraView(std::move(learn_more_button));
   }
 }
 
@@ -168,14 +166,10 @@
                          DISTANCE_SUBSECTION_HORIZONTAL_INDENT)),
       provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL)));
 
-  auto* icon =
-      line_container->AddChildView(std::make_unique<views::ImageView>());
-  const gfx::VectorIcon& vector_id = request->GetIconId();
-  const SkColor icon_color = icon->GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_DefaultIconColor);
   constexpr int kPermissionIconSize = 18;
-  icon->SetImage(
-      gfx::CreateVectorIcon(vector_id, kPermissionIconSize, icon_color));
+  auto* icon = line_container->AddChildView(
+      std::make_unique<views::ColorTrackingIconView>(request->GetIconId(),
+                                                     kPermissionIconSize));
   icon->SetVerticalAlignment(views::ImageView::Alignment::kLeading);
 
   auto* label = line_container->AddChildView(
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 2f91f26..8d66d505 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -930,9 +930,7 @@
 }
 
 void ToolbarView::UpdateHomeButtonVisibility() {
-  const bool show_home_button =
-      show_home_button_.GetValue() || browser_->deprecated_is_app();
-  home_->SetVisible(show_home_button);
+  home_->SetVisible(show_home_button_.GetValue());
 }
 
 void ToolbarView::OnTouchUiChanged() {
diff --git a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
index 9a46512..c5c2c9b 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
@@ -49,6 +49,7 @@
       {"errorTitle", IDS_CROSTINI_INSTALLER_ERROR_TITLE},
 
       {"loadTerminaError", IDS_CROSTINI_INSTALLER_LOAD_TERMINA_ERROR},
+      {"startConciergeError", IDS_CROSTINI_INSTALLER_START_CONCIERGE_ERROR},
       {"createDiskImageError", IDS_CROSTINI_INSTALLER_CREATE_DISK_IMAGE_ERROR},
       {"startTerminaVmError", IDS_CROSTINI_INSTALLER_START_TERMINA_VM_ERROR},
       {"startContainerError", IDS_CROSTINI_INSTALLER_START_CONTAINER_ERROR},
@@ -60,6 +61,7 @@
       {"unknownError", IDS_CROSTINI_INSTALLER_UNKNOWN_ERROR},
 
       {"loadTerminaMessage", IDS_CROSTINI_INSTALLER_LOAD_TERMINA_MESSAGE},
+      {"startConciergeMessage", IDS_CROSTINI_INSTALLER_START_CONCIERGE_MESSAGE},
       {"createDiskImageMessage",
        IDS_CROSTINI_INSTALLER_CREATE_DISK_IMAGE_MESSAGE},
       {"startTerminaVmMessage",
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 fd9f76ef..aac46d69 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
@@ -71,18 +71,12 @@
   CallJS("login.UpdateScreen.setUIState", static_cast<int>(value));
 }
 
-void UpdateScreenHandler::SetUpdateStatusMessagePercent(
-    const base::string16& value) {
-  CallJS("login.UpdateScreen.setUpdateStatusMessagePercent", value);
-}
-
-void UpdateScreenHandler::SetUpdateStatusMessageTimeLeft(
-    const base::string16& value) {
-  CallJS("login.UpdateScreen.setUpdateStatusMessageTimeLeft", value);
-}
-
-void UpdateScreenHandler::SetBetterUpdateProgress(int value) {
-  CallJS("login.UpdateScreen.setBetterUpdateProgress", value);
+void UpdateScreenHandler::SetUpdateStatus(
+    int percent,
+    const base::string16& percent_message,
+    const base::string16& timeleft_message) {
+  CallJS("login.UpdateScreen.setUpdateStatus", percent, percent_message,
+         timeleft_message);
 }
 
 void UpdateScreenHandler::SetEstimatedTimeLeft(int value) {
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 2e28d37..c9fd3a78 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
@@ -47,9 +47,9 @@
   virtual void Unbind() = 0;
 
   virtual void SetUIState(UIState value) = 0;
-  virtual void SetUpdateStatusMessagePercent(const base::string16& value) = 0;
-  virtual void SetUpdateStatusMessageTimeLeft(const base::string16& value) = 0;
-  virtual void SetBetterUpdateProgress(int value) = 0;
+  virtual void SetUpdateStatus(int percent,
+                               const base::string16& percent_message,
+                               const base::string16& timeleft_message) = 0;
   // Set the estimated time left, in seconds.
   virtual void SetEstimatedTimeLeft(int value) = 0;
   virtual void SetShowEstimatedTimeLeft(bool value) = 0;
@@ -77,9 +77,9 @@
   void Unbind() override;
 
   void SetUIState(UpdateView::UIState value) override;
-  void SetUpdateStatusMessagePercent(const base::string16& value) override;
-  void SetUpdateStatusMessageTimeLeft(const base::string16& value) override;
-  void SetBetterUpdateProgress(int value) override;
+  void SetUpdateStatus(int percent,
+                       const base::string16& percent_message,
+                       const base::string16& timeleft_message) override;
   void SetEstimatedTimeLeft(int value) override;
   void SetShowEstimatedTimeLeft(bool value) override;
   void SetUpdateCompleted(bool value) override;
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
index 147c7a9..26b65a0 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h"
 #include "chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.h"
 #include "chrome/browser/nearby_sharing/nearby_share_settings.h"
 #include "chrome/browser/nearby_sharing/nearby_sharing_service.h"
@@ -93,6 +94,14 @@
   nearby_sharing_service->GetSettings()->Bind(std::move(receiver));
 }
 
+void NearbyShareDialogUI::BindInterface(
+    mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver) {
+  NearbySharingService* nearby_sharing_service =
+      NearbySharingServiceFactory::GetForBrowserContext(
+          Profile::FromWebUI(web_ui()));
+  nearby_sharing_service->GetContactManager()->Bind(std::move(receiver));
+}
+
 void NearbyShareDialogUI::HandleClose(const base::ListValue* args) {
   for (auto& observer : observers_) {
     observer.OnClose();
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
index 72a61cb..570d88d 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
@@ -43,6 +43,10 @@
   // ui::MojoWebUIController
   void BindInterface(
       mojo::PendingReceiver<mojom::NearbyShareSettings> receiver);
+  // Binds to the existing contacts manager instance owned by the nearby share
+  // keyed service.
+  void BindInterface(
+      mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver);
 
  private:
   void HandleClose(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/nearby_share/public/mojom/BUILD.gn b/chrome/browser/ui/webui/nearby_share/public/mojom/BUILD.gn
index 0c7c0ce..f57ea35 100644
--- a/chrome/browser/ui/webui/nearby_share/public/mojom/BUILD.gn
+++ b/chrome/browser/ui/webui/nearby_share/public/mojom/BUILD.gn
@@ -7,5 +7,8 @@
 mojom("mojom") {
   sources = [ "nearby_share_settings.mojom" ]
 
-  public_deps = [ "//mojo/public/mojom/base" ]
+  public_deps = [
+    "//mojo/public/mojom/base",
+    "//url/mojom:url_mojom_gurl",
+  ]
 }
diff --git a/chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom b/chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom
index 41c92c8..c0322ca 100644
--- a/chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom
+++ b/chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 module nearby_share.mojom;
 
+import "url/mojom/url.mojom";
+
 // TODO(crbug.com/1110098): Remove kUnknown.
 // Represents the data usage preference when sharing.
 enum DataUsage {
@@ -72,6 +74,9 @@
   // Set the visibility of this device with respect to the user's contacts.
   SetVisibility(Visibility visibility);
 
+  // TODO(crbug.com/1128110): This functionality has been moved to
+  //     ContactManger and will be removed in a follow up CL.
+  //
   // Get all contact ids that this device should be visible to when visibility
   // is |kSelectedContacts|.
   GetAllowedContacts() => (array<string> allowed_contacts);
@@ -79,3 +84,82 @@
   // is |kSelectedContacts|.
   SetAllowedContacts(array<string> allowed_contacts);
 };
+
+
+// Translated from the proto file (Contact.Identifier):
+// https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/nearby_sharing/proto/rpc_resources.proto
+// A single contact identifier.
+union ContactIdentifier {
+  string obfuscated_gaia;
+  string phone_number;
+  string account_name;
+};
+
+// Translated from the proto file (ContactRecord):
+// https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/nearby_sharing/proto/rpc_resources.proto
+// A contact record from People backend.
+struct ContactRecord {
+  // The stable id of this contact record.
+  string id;
+  // The contact record's name.
+  string person_name;
+  // The URL of an image displayed to the user when selecting a share
+  // target.
+  url.mojom.Url image_url;
+  array<ContactIdentifier> identifiers;
+};
+
+// Allows a client of |ContactManager| to observe contacts downloading results.
+// These events can be in response to direct requests or due to periodic
+// updates.
+//
+// This interface is implemented in a shared Polymer component that is used in
+// two WebUIs: chrome://nearby and chrome://os-settings.
+interface DownloadContactsObserver {
+  // Notifies the observer that contacts have been downloaded successfully and
+  // gives the most recent allowed contact ids at the same time.
+  //
+  // |allowed_contacts| is a set of contacts ids used when visibility is
+  //    |Visibility::kSelectedContacts|. This can be set explicitly with
+  //    SetAllowedContacts() on |ContactManager|. This array may be empty if the
+  //    user has not selected any contacts yet.
+  // |contacts| is a full set of a user's GAIA contacts from the the people
+  //    api. The list is pre-sorted by person_name server side. Any ids present
+  //    in allowed_contacts will have a corresponding contact record here. This
+  //    array may be empty if the user has no contacts (in which case
+  //    |allowed_contacts| will also be empty).
+  OnContactsDownloaded(array<string> allowed_contacts,
+                       array<ContactRecord> contacts);
+
+  // Notifies the observer that contacts have failed to download.
+  // NOTE: At the moment we don't provide a specific reason as to why this
+  // failed, it is just a signal for the UI to show a generic error/retry page.
+  OnContactsDownloadFailed();
+};
+
+// The contacts manager allows clients to observe contacts downloading status
+// and results. Contact downloads can happen periodically or on demand with
+// DownloadContacts(). The results are not returned directly on DownloadContacts
+// because multiple queued download requests may result in only a single
+// response and a client is likely interested in getting any contacts download
+// even if they did not explicitly request it (i.e. periodic update).
+//
+// Implemented in the browser process and owned by a keyed service.
+interface ContactManager {
+  // Adds an observer which can listen for the success or failure of contact
+  // downloading.
+  AddDownloadContactsObserver(pending_remote<DownloadContactsObserver>
+      observer);
+
+  // Request a download of contacts from the server. This will queue the call
+  // with the scheduler which may or may not execute right away depending on the
+  // online state of the device.
+  DownloadContacts();
+
+  // Set all contact ids that this device should be visible to when visibility
+  // is |Visibility::kSelectedContacts|. Once set, this will update the device's
+  // private certificates. The latest value of this will be sent on the next
+  // OnContactsDownloaded event on the observer interface, but it will not
+  // trigger an immediate OnContactsDownloaded() event.
+  SetAllowedContacts(array<string> allowed_contacts);
+};
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
index 927dc8c..4841abb 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -8,6 +8,7 @@
 
 #include "ash/public/cpp/network_config_service.h"
 #include "base/metrics/histogram_functions.h"
+#include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h"
 #include "chrome/browser/nearby_sharing/nearby_receive_manager.h"
 #include "chrome/browser/nearby_sharing/nearby_share_settings.h"
 #include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
@@ -179,6 +180,14 @@
                               std::move(receiver));
 }
 
+void OSSettingsUI::BindInterface(
+    mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver) {
+  NearbySharingService* service =
+      NearbySharingServiceFactory::GetForBrowserContext(
+          Profile::FromWebUI(web_ui()));
+  service->GetContactManager()->Bind(std::move(receiver));
+}
+
 WEB_UI_CONTROLLER_TYPE_IMPL(OSSettingsUI)
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
index d11f9d6..fe123ad 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
@@ -73,6 +73,11 @@
   void BindInterface(
       mojo::PendingReceiver<nearby_share::mojom::ReceiveManager> receiver);
 
+  // Binds to the existing contacts manager instance owned by the nearby share
+  // keyed service.
+  void BindInterface(
+      mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver);
+
  private:
   base::TimeTicks time_when_opened_;
 
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index fa8a808..0e6bddb 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -22,19 +22,6 @@
   flags = [ "USE_VR_ASSETS_COMPONENT=$use_vr_assets_component" ]
 }
 
-use_command_buffer = is_win
-
-config("vr_gl_mode") {
-  if (use_command_buffer) {
-    defines = [
-      "VR_USE_COMMAND_BUFFER",
-      "GL_GLEXT_PROTOTYPES",
-    ]
-  } else {
-    defines = [ "VR_USE_NATIVE_GL" ]
-  }
-}
-
 component("vr_ui") {
   sources = [
     "animation.cc",
@@ -190,6 +177,7 @@
     "//components/url_formatter",
     "//components/vector_icons",
     "//device/base",
+    "//device/vr:vr_base",
     "//device/vr/buildflags:buildflags",
     "//media",
     "//net",
@@ -329,7 +317,6 @@
     "content_input_delegate.h",
     "exit_vr_prompt_choice.h",
     "fov_rectangle.h",
-    "gl_bindings.h",
     "gl_texture_location.h",
     "input_event.cc",
     "input_event.h",
@@ -377,8 +364,6 @@
     "vr_base_export.h",
     "vr_geometry_util.cc",
     "vr_geometry_util.h",
-    "vr_gl_util.cc",
-    "vr_gl_util.h",
   ]
 
   sources += [
@@ -390,10 +375,10 @@
 
   public_deps = [
     ":vr_buildflags",
-    ":vr_gl_bindings",
     "//components/omnibox/browser",
     "//components/strings:components_strings_grit",
     "//content/public/common",
+    "//device/vr:vr_gl_bindings",
     "//device/vr/public/cpp",
     "//ui/base",
   ]
@@ -418,16 +403,6 @@
   }
 }
 
-source_set("vr_gl_bindings") {
-  sources = [ "gl_bindings.h" ]
-  public_configs = [ ":vr_gl_mode" ]
-  if (use_command_buffer) {
-    public_deps = [ "//gpu/command_buffer/client:gles2_c_lib" ]
-  } else {
-    public_deps = [ "//ui/gl" ]
-  }
-}
-
 test("vr_common_unittests") {
   sources = [
     "animation_unittest.cc",
@@ -592,8 +567,8 @@
   ]
 
   public_deps = [
-    ":vr_gl_bindings",
     "//base/test:test_support",
+    "//device/vr:vr_gl_bindings",
     "//skia",
     "//testing/gtest",
   ]
diff --git a/chrome/browser/vr/elements/controller.cc b/chrome/browser/vr/elements/controller.cc
index f742674..108066a 100644
--- a/chrome/browser/vr/elements/controller.cc
+++ b/chrome/browser/vr/elements/controller.cc
@@ -10,7 +10,7 @@
 #include "cc/animation/keyframed_animation_curve.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
 #include "chrome/browser/vr/ui_scene_constants.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "third_party/skia/include/core/SkColor.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/elements/environment/background.cc b/chrome/browser/vr/elements/environment/background.cc
index f8450ac..c90d87e 100644
--- a/chrome/browser/vr/elements/environment/background.cc
+++ b/chrome/browser/vr/elements/environment/background.cc
@@ -7,7 +7,7 @@
 #include "chrome/browser/vr/model/assets.h"
 #include "chrome/browser/vr/skia_surface_provider.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
diff --git a/chrome/browser/vr/elements/environment/background.h b/chrome/browser/vr/elements/environment/background.h
index 95b7c78..ff3a22f 100644
--- a/chrome/browser/vr/elements/environment/background.h
+++ b/chrome/browser/vr/elements/environment/background.h
@@ -6,8 +6,8 @@
 #define CHROME_BROWSER_VR_ELEMENTS_ENVIRONMENT_BACKGROUND_H_
 
 #include "chrome/browser/vr/elements/ui_element.h"
-#include "chrome/browser/vr/gl_bindings.h"
 #include "chrome/browser/vr/renderers/base_quad_renderer.h"
+#include "device/vr/gl_bindings.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
 class SkBitmap;
diff --git a/chrome/browser/vr/elements/environment/grid.cc b/chrome/browser/vr/elements/environment/grid.cc
index 839d099b..3e1ff8a 100644
--- a/chrome/browser/vr/elements/environment/grid.cc
+++ b/chrome/browser/vr/elements/environment/grid.cc
@@ -6,7 +6,7 @@
 
 #include "chrome/browser/vr/target_property.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 
 namespace vr {
 
diff --git a/chrome/browser/vr/elements/environment/stars.cc b/chrome/browser/vr/elements/environment/stars.cc
index 47fc9a7..658be12 100644
--- a/chrome/browser/vr/elements/environment/stars.cc
+++ b/chrome/browser/vr/elements/environment/stars.cc
@@ -10,7 +10,7 @@
 #include "base/stl_util.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
 #include "chrome/browser/vr/ui_scene_constants.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "ui/gfx/animation/tween.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/elements/laser.cc b/chrome/browser/vr/elements/laser.cc
index ada1ea4..2373ca5 100644
--- a/chrome/browser/vr/elements/laser.cc
+++ b/chrome/browser/vr/elements/laser.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/vr/model/model.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
 #include "chrome/browser/vr/ui_scene_constants.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 
 namespace vr {
 
diff --git a/chrome/browser/vr/elements/reticle.cc b/chrome/browser/vr/elements/reticle.cc
index 7c3f046..78d6dd0 100644
--- a/chrome/browser/vr/elements/reticle.cc
+++ b/chrome/browser/vr/elements/reticle.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/vr/ui_scene.h"
 #include "chrome/browser/vr/ui_scene_constants.h"
 #include "chrome/browser/vr/vector_icons/vector_icons.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 
 namespace vr {
 
diff --git a/chrome/browser/vr/elements/shadow.cc b/chrome/browser/vr/elements/shadow.cc
index 0178e193..ed4e220 100644
--- a/chrome/browser/vr/elements/shadow.cc
+++ b/chrome/browser/vr/elements/shadow.cc
@@ -6,7 +6,7 @@
 
 #include "base/numerics/ranges.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "ui/gfx/animation/tween.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/elements/textured_element.h b/chrome/browser/vr/elements/textured_element.h
index 0716eea8..565ac92 100644
--- a/chrome/browser/vr/elements/textured_element.h
+++ b/chrome/browser/vr/elements/textured_element.h
@@ -7,8 +7,8 @@
 
 #include "base/macros.h"
 #include "chrome/browser/vr/elements/ui_element.h"
-#include "chrome/browser/vr/gl_bindings.h"
 #include "chrome/browser/vr/vr_ui_export.h"
+#include "device/vr/gl_bindings.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "ui/gfx/geometry/size.h"
 
diff --git a/chrome/browser/vr/elements/ui_element.cc b/chrome/browser/vr/elements/ui_element.cc
index fa0697a8..b1df55bc 100644
--- a/chrome/browser/vr/elements/ui_element.cc
+++ b/chrome/browser/vr/elements/ui_element.cc
@@ -15,7 +15,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/vr/input_event.h"
 #include "chrome/browser/vr/model/camera_model.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "third_party/skia/include/core/SkRRect.h"
 #include "third_party/skia/include/core/SkRect.h"
 #include "ui/gfx/geometry/angle_conversions.h"
diff --git a/chrome/browser/vr/renderers/base_quad_renderer.cc b/chrome/browser/vr/renderers/base_quad_renderer.cc
index aff8ff2..d6046093 100644
--- a/chrome/browser/vr/renderers/base_quad_renderer.cc
+++ b/chrome/browser/vr/renderers/base_quad_renderer.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/vr/renderers/base_quad_renderer.h"
 
 #include "base/stl_util.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "ui/gfx/transform.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/renderers/base_renderer.cc b/chrome/browser/vr/renderers/base_renderer.cc
index 3c253813..b19133e 100644
--- a/chrome/browser/vr/renderers/base_renderer.cc
+++ b/chrome/browser/vr/renderers/base_renderer.cc
@@ -7,7 +7,7 @@
 #include <ostream>
 
 #include "base/check.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 
 namespace vr {
 
diff --git a/chrome/browser/vr/renderers/base_renderer.h b/chrome/browser/vr/renderers/base_renderer.h
index fdb5b87..6a5395c 100644
--- a/chrome/browser/vr/renderers/base_renderer.h
+++ b/chrome/browser/vr/renderers/base_renderer.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_VR_RENDERERS_BASE_RENDERER_H_
 
 #include "base/macros.h"
-#include "chrome/browser/vr/gl_bindings.h"
+#include "device/vr/gl_bindings.h"
 
 namespace vr {
 
diff --git a/chrome/browser/vr/renderers/external_textured_quad_renderer.cc b/chrome/browser/vr/renderers/external_textured_quad_renderer.cc
index 2dc45cb..93e09c2 100644
--- a/chrome/browser/vr/renderers/external_textured_quad_renderer.cc
+++ b/chrome/browser/vr/renderers/external_textured_quad_renderer.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/vr/renderers/external_textured_quad_renderer.h"
 
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "ui/gfx/transform.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/renderers/radial_gradient_quad_renderer.cc b/chrome/browser/vr/renderers/radial_gradient_quad_renderer.cc
index c731518e..8e4f4a4 100644
--- a/chrome/browser/vr/renderers/radial_gradient_quad_renderer.cc
+++ b/chrome/browser/vr/renderers/radial_gradient_quad_renderer.cc
@@ -6,7 +6,7 @@
 
 #include "chrome/browser/vr/elements/corner_radii.h"
 #include "chrome/browser/vr/renderers/textured_quad_renderer.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/transform.h"
diff --git a/chrome/browser/vr/renderers/texture_copy_renderer.cc b/chrome/browser/vr/renderers/texture_copy_renderer.cc
index 8e01a174..74d612d6 100644
--- a/chrome/browser/vr/renderers/texture_copy_renderer.cc
+++ b/chrome/browser/vr/renderers/texture_copy_renderer.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/vr/renderers/texture_copy_renderer.h"
 
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 
 namespace vr {
 
diff --git a/chrome/browser/vr/renderers/textured_quad_renderer.cc b/chrome/browser/vr/renderers/textured_quad_renderer.cc
index b9af5339..0ff0f8b 100644
--- a/chrome/browser/vr/renderers/textured_quad_renderer.cc
+++ b/chrome/browser/vr/renderers/textured_quad_renderer.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/vr/renderers/textured_quad_renderer.h"
 
 #include "base/stl_util.h"
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "ui/gfx/transform.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/renderers/transparent_quad_renderer.cc b/chrome/browser/vr/renderers/transparent_quad_renderer.cc
index 574b8a8a..22856d8 100644
--- a/chrome/browser/vr/renderers/transparent_quad_renderer.cc
+++ b/chrome/browser/vr/renderers/transparent_quad_renderer.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/vr/renderers/transparent_quad_renderer.h"
 
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 #include "ui/gfx/transform.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/skia_surface_provider.h b/chrome/browser/vr/skia_surface_provider.h
index 39d64ac..8b8b4f8 100644
--- a/chrome/browser/vr/skia_surface_provider.h
+++ b/chrome/browser/vr/skia_surface_provider.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_SKIA_SURFACE_PROVIDER_H_
 #define CHROME_BROWSER_VR_SKIA_SURFACE_PROVIDER_H_
 
-#include "chrome/browser/vr/gl_bindings.h"
+#include "device/vr/gl_bindings.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 
diff --git a/chrome/browser/vr/test/gl_test_environment.h b/chrome/browser/vr/test/gl_test_environment.h
index de4b160..ece381f 100644
--- a/chrome/browser/vr/test/gl_test_environment.h
+++ b/chrome/browser/vr/test/gl_test_environment.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
-#include "chrome/browser/vr/gl_bindings.h"
+#include "device/vr/gl_bindings.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace gl {
diff --git a/chrome/browser/vr/ui_renderer.cc b/chrome/browser/vr/ui_renderer.cc
index 8d7f837..8c4d2d4 100644
--- a/chrome/browser/vr/ui_renderer.cc
+++ b/chrome/browser/vr/ui_renderer.cc
@@ -6,10 +6,10 @@
 
 #include "base/trace_event/trace_event.h"
 #include "chrome/browser/vr/elements/ui_element.h"
-#include "chrome/browser/vr/gl_bindings.h"
 #include "chrome/browser/vr/render_info.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
 #include "chrome/browser/vr/ui_scene.h"
+#include "device/vr/gl_bindings.h"
 
 namespace vr {
 
diff --git a/chrome/browser/vr/vr_gl_util.h b/chrome/browser/vr/vr_gl_util.h
deleted file mode 100644
index 56e5583..0000000
--- a/chrome/browser/vr/vr_gl_util.h
+++ /dev/null
@@ -1,53 +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.
-
-#ifndef CHROME_BROWSER_VR_VR_GL_UTIL_H_
-#define CHROME_BROWSER_VR_VR_GL_UTIL_H_
-
-#include <array>
-#include <string>
-
-#include "chrome/browser/vr/gl_bindings.h"
-#include "chrome/browser/vr/vr_base_export.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-#define SHADER(Src) "#version 100\n" #Src
-#define OEIE_SHADER(Src) \
-  "#version 100\n#extension GL_OES_EGL_image_external : require\n" #Src
-#define VOID_OFFSET(x) reinterpret_cast<void*>(x)
-
-namespace gfx {
-class Transform;
-}  // namespace gfx
-
-namespace vr {
-
-VR_BASE_EXPORT std::array<float, 16> MatrixToGLArray(
-    const gfx::Transform& matrix);
-
-// Compile a shader. This is intended for browser-internal shaders only,
-// don't use this for user-supplied arbitrary shaders since data is handed
-// directly to the GL driver without further sanity checks.
-VR_BASE_EXPORT GLuint CompileShader(GLenum shader_type,
-                                    const std::string& shader_source,
-                                    std::string& error);
-
-// Compile and link a program.
-VR_BASE_EXPORT GLuint CreateAndLinkProgram(GLuint vertex_shader_handle,
-                                           GLuint fragment_shader_handle,
-                                           std::string& error);
-
-// Sets default texture parameters given a texture type.
-VR_BASE_EXPORT void SetTexParameters(GLenum texture_type);
-
-// Sets color uniforms given an SkColor.
-VR_BASE_EXPORT void SetColorUniform(GLuint handle, SkColor c);
-
-// Sets color uniforms (but not alpha) given an SkColor. The alpha is assumed to
-// be 1.0 in this case.
-VR_BASE_EXPORT void SetOpaqueColorUniform(GLuint handle, SkColor c);
-
-}  // namespace vr
-
-#endif  // CHROME_BROWSER_VR_VR_GL_UTIL_H_
diff --git a/chrome/browser/vr/webxr_vr_frame_loop_browser_test.cc b/chrome/browser/vr/webxr_vr_frame_loop_browser_test.cc
index 26b3e166..afc2452 100644
--- a/chrome/browser/vr/webxr_vr_frame_loop_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_frame_loop_browser_test.cc
@@ -60,6 +60,9 @@
 }  // namespace
 
 WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestNoStalledFrameLoop) {
+  // TODO(https://crbug.com/1126786): WaitForFrames seems to be timing out on
+  // the WMR runtime.
+  WEBXR_VR_DISABLE_TEST_ON(XrBrowserTestBase::RuntimeType::RUNTIME_WMR);
   MyXRMock my_mock;
 
   // Load the test page, and enter presentation.
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index aa873a5..8e17320c 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1600171148-66b09300985d04c4504d7b1ddf5998f31579a7e2.profdata
+chrome-linux-master-1600192792-36de61e8f764b4ecaec9d88047348ce6694afdaf.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 1cab3b61..5b295b3 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1600084447-f1bad6c3f3b8af9e95e3b8f79a7a1df8147866eb.profdata
+chrome-win64-master-1600148958-91c0cd454c0f8748eee83f19943ecba0f6b92ac0.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index bddbc4d..4b4d004 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3259,7 +3259,6 @@
     "../browser/chrome_content_browser_client_unittest.cc",
     "../browser/chrome_process_singleton_win_unittest.cc",
     "../browser/command_updater_impl_unittest.cc",
-    "../browser/complex_tasks/endpoint_fetcher/endpoint_fetcher_unittest.cc",
     "../browser/complex_tasks/task_tab_helper_unittest.cc",
     "../browser/component_updater/autofill_states_component_installer_unittest.cc",
     "../browser/component_updater/chrome_component_updater_configurator_unittest.cc",
@@ -3302,6 +3301,7 @@
     "../browser/download/download_target_determiner_unittest.cc",
     "../browser/download/download_ui_controller_unittest.cc",
     "../browser/download/offline_item_utils_unittest.cc",
+    "../browser/endpoint_fetcher/endpoint_fetcher_unittest.cc",
     "../browser/engagement/important_sites_usage_counter_unittest.cc",
     "../browser/engagement/important_sites_util_unittest.cc",
     "../browser/engagement/site_engagement_helper_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java
index 3de028f3..6888bd69 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java
@@ -26,9 +26,8 @@
             @Override
             public void evaluate() throws Throwable {
                 /**
-                 * Loads the native library on the activity UI thread (must not be called from the
-                 * UI thread).  After loading the library, this will initialize the browser process
-                 * if necessary.
+                 * Loads the native library on the activity UI thread.  After loading the library,
+                 * this will initialize the browser process if necessary.
                  */
                 NativeLibraryTestUtils.loadNativeLibraryAndInitBrowserProcess();
                 base.evaluate();
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 3ba36ed..1ebf04b 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -268,6 +268,7 @@
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_nameservers_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_password_input_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_property_list_mojo_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_exclusions_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_input_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_select_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_siminfo_test.m.js",
diff --git a/chrome/test/data/webui/chromeos/crostini_installer_app_test.js b/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
index 617eea1..7afe8f9 100644
--- a/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
+++ b/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
@@ -149,8 +149,7 @@
     expectEquals(fakeBrowserProxy.handler.getCallCount('install'), 1);
     expectTrue(getInstallButton().hidden);
 
-    fakeBrowserProxy.page.onProgressUpdate(
-        InstallerState.kCreateDiskImage, 0.5);
+    fakeBrowserProxy.page.onProgressUpdate(InstallerState.kStartConcierge, 0.5);
     await flushTasks();
     expectTrue(
         !!app.$$('#installing-message > div').textContent.trim(),
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
index cc746ff..1422fcd 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
@@ -43,6 +43,7 @@
   ['NetworkNameservers', 'network/network_nameservers_test.js', []],
   ['NetworkPasswordInput', 'network/network_password_input_test.js', []],
   ['NetworkPropertyListMojo', 'network/network_property_list_mojo_test.js', []],
+  ['NetworkProxyExclusions', 'network/network_proxy_exclusions_test.js', []],
   ['NetworkProxyInput', 'network/network_proxy_input_test.js', []],
   ['NetworkSiminfo', 'network/network_siminfo_test.js', []],
 ].forEach(test => registerTest('NetworkComponents', 'os-settings', ...test));
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
index e7c58cd3..a606c92b 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
@@ -25,6 +25,7 @@
  ['NetworkNameservers', 'network/network_nameservers_test.m.js'],
  ['NetworkPasswordInput', 'network/network_password_input_test.m.js'],
  ['NetworkPropertyListMojo', 'network/network_property_list_mojo_test.m.js'],
+ ['NetworkProxyExclusions', 'network/network_proxy_exclusions_test.m.js'],
  ['NetworkProxyInput', 'network/network_proxy_input_test.m.js'],
  ['NetworkSelect', 'network/network_select_test.m.js'],
  ['NetworkSiminfo', 'network/network_siminfo_test.m.js'],
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/network/BUILD.gn
index 1eb1e489..1e1678ad 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/network/BUILD.gn
@@ -21,6 +21,7 @@
     "network_nameservers_test.js",
     "network_password_input_test.js",
     "network_property_list_mojo_test.js",
+    "network_proxy_exclusions_test.js",
     "network_proxy_input_test.js",
     "network_select_test.js",
     "network_siminfo_test.js",
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_exclusions_test.js b/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_exclusions_test.js
new file mode 100644
index 0000000..037334c
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_exclusions_test.js
@@ -0,0 +1,41 @@
+// 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.
+
+// clang-format off
+// #import 'chrome://os-settings/strings.m.js';
+// #import 'chrome://resources/cr_components/chromeos/network/network_proxy_exclusions.m.js';
+//
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// clang-format on
+
+suite('NetworkProxyExclusionsTest', function() {
+  /** @type {!NetworkProxyExclusions|undefined} */
+  let proxyExclusions;
+
+  setup(function() {
+    proxyExclusions = document.createElement('network-proxy-exclusions');
+    document.body.appendChild(proxyExclusions);
+    Polymer.dom.flush();
+  });
+
+  test('Clear fires proxy-exclusions-change event', function(done) {
+    proxyExclusions.exclusions = [
+      'one', 'two', 'three'
+    ];
+    Polymer.dom.flush();
+
+    // Verify that clicking the clear button fires the proxy-exclusions-change
+    // event and that the item was removed from the exclusions list.
+    proxyExclusions.addEventListener('proxy-exclusions-change', function() {
+      assertDeepEquals(proxyExclusions.exclusions, ['two', 'three']);
+      done();
+    });
+
+    const clearBtn = proxyExclusions.$$('cr-icon-button');
+    assertTrue(!!clearBtn);
+
+    // Simulate pressing clear on the first exclusion list item.
+    clearBtn.click();
+  });
+});
diff --git a/chrome/updater/app/app_install.cc b/chrome/updater/app/app_install.cc
index 9ae05f1..e500006 100644
--- a/chrome/updater/app/app_install.cc
+++ b/chrome/updater/app/app_install.cc
@@ -11,6 +11,7 @@
 #include "base/check.h"
 #include "base/command_line.h"
 #include "base/i18n/icu_util.h"
+#include "base/logging.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -21,7 +22,9 @@
 #include "chrome/updater/control_service.h"
 #include "chrome/updater/persisted_data.h"
 #include "chrome/updater/prefs.h"
+#include "chrome/updater/registration_data.h"
 #include "chrome/updater/setup.h"
+#include "chrome/updater/update_service.h"
 #include "chrome/updater/updater_version.h"
 #include "components/prefs/pref_service.h"
 
@@ -78,13 +81,6 @@
 
 void AppInstall::Initialize() {
   base::i18n::InitializeICU();
-
-  // Creating |global_prefs_| requires acquiring a global lock, and this lock is
-  // typically owned by the RPC server. That means that if the server is
-  // running, the following code will block, and the install will not proceed
-  // until the server releases the lock.
-  global_prefs_ = CreateGlobalPrefs();
-  local_prefs_ = CreateLocalPrefs();
 }
 
 void AppInstall::FirstTaskRun() {
@@ -94,22 +90,21 @@
   splash_screen_ = splash_screen_maker_.Run();
   splash_screen_->Show();
 
-  base::ThreadPool::PostTaskAndReplyWithResult(
+  base::ThreadPool::PostTask(
       FROM_HERE,
       {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-      base::BindOnce([]() { return InstallCandidate(false); }),
       base::BindOnce(
-          [](SplashScreen* splash_screen, base::OnceCallback<void(int)> done,
-             int result) {
-            splash_screen->Dismiss(base::BindOnce(std::move(done), result));
-          },
-          splash_screen_.get(),
-          base::BindOnce(&AppInstall::InstallCandidateDone, this)));
+          &InstallCandidate, false, base::SequencedTaskRunnerHandle::Get(),
+          base::BindOnce(
+              [](SplashScreen* splash_screen,
+                 base::OnceCallback<void(int)> done, int result) {
+                splash_screen->Dismiss(base::BindOnce(std::move(done), result));
+              },
+              splash_screen_.get(),
+              base::BindOnce(&AppInstall::InstallCandidateDone, this))));
 }
 
-// Updates the prefs if installing the updater is successful, then continue
-// installing the application if --app-id is specified on the command line.
 void AppInstall::InstallCandidateDone(int result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -118,50 +113,42 @@
     return;
   }
 
-  // Invoke |HandleAppId| to continue the execution flow.
-  MakeActive(base::BindOnce(&AppInstall::HandleAppId, this));
+  // Invoke ControlService::Run to wake this version of the updater, do an
+  // update check, and possibly promote this version as a result.
+  // The instance of |CreateControlService| has sequence affinity. Bind it
+  // in the closure to ensure it is released in this sequence.
+  scoped_refptr<ControlService> control_service = CreateControlService();
+  control_service->Run(base::BindOnce(
+      [](scoped_refptr<ControlService> /*control_service*/,
+         scoped_refptr<AppInstall> app_install) {
+        app_install->RegisterUpdater();
+      },
+      control_service, base::WrapRefCounted(this)));
+}
+
+void AppInstall::RegisterUpdater() {
+  // TODO(crbug.com/1128060): We should update the updater's registration with
+  // the new version, brand code, etc. For now, fake it.
+  RegistrationResponse result;
+  result.status_code = 0;
+  RegisterUpdaterDone(result);
+}
+
+void AppInstall::RegisterUpdaterDone(const RegistrationResponse& response) {
+  VLOG(1) << "Updater registration complete, code = " << response.status_code;
+  HandleAppId();
 }
 
 void AppInstall::HandleAppId() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // This releases the prefs lock, and the RPC server can be started.
-  global_prefs_ = nullptr;
-  local_prefs_ = nullptr;
-
-  // If no app id is provided, then invoke ControlService::Run to wake
-  // this version of the updater, do an update check, and possibly promote
-  // this version as a result.
   const std::string app_id =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kAppIdSwitch);
   if (app_id.empty()) {
-    // The instance of |CreateControlService| has sequence affinity. Bind it
-    // in the closure to ensure it is released in this sequence.
-    scoped_refptr<ControlService> control_service = CreateControlService();
-    control_service->Run(base::BindOnce(
-        [](scoped_refptr<ControlService> /*control_service*/,
-           scoped_refptr<AppInstall> app_install) { app_install->Shutdown(0); },
-        control_service, base::WrapRefCounted(this)));
+    Shutdown(0);
     return;
   }
-
   app_install_controller_ = app_install_controller_maker_.Run();
   app_install_controller_->InstallApp(
       app_id, base::BindOnce(&AppInstall::Shutdown, this));
 }
 
-// TODO(crbug.com/1109231) - this is a temporary workaround.
-void AppInstall::MakeActive(base::OnceClosure done) {
-  local_prefs_->SetQualified(true);
-  local_prefs_->GetPrefService()->CommitPendingWrite(base::BindOnce(
-      [](base::OnceClosure done, PrefService* pref_service) {
-        DCHECK(pref_service);
-        auto persisted_data = base::MakeRefCounted<PersistedData>(pref_service);
-        persisted_data->SetProductVersion(
-            kUpdaterAppId, base::Version(UPDATER_VERSION_STRING));
-        pref_service->CommitPendingWrite(std::move(done));
-      },
-      std::move(done), global_prefs_->GetPrefService()));
-}
-
 }  // namespace updater
diff --git a/chrome/updater/app/app_install.h b/chrome/updater/app/app_install.h
index 4065362..cdcc11f 100644
--- a/chrome/updater/app/app_install.h
+++ b/chrome/updater/app/app_install.h
@@ -20,13 +20,12 @@
 
 namespace updater {
 
-class LocalPrefs;
-class GlobalPrefs;
+struct RegistrationResponse;
 
 // This class defines an interface for installing an application. The interface
 // is intended to be implemented for scenerios where UI and RPC calls to
 // |UpdateService| are involved, hence the word `controller` in the name of
-// the ]interface.
+// the interface.
 class AppInstallController
     : public base::RefCountedThreadSafe<AppInstallController> {
  public:
@@ -57,14 +56,14 @@
 
   void InstallCandidateDone(int result);
 
+  void RegisterUpdater();
+
+  void RegisterUpdaterDone(const RegistrationResponse& response);
+
   // Handles the --app-id command line argument, and triggers installing of the
   // corresponding app-id if the argument is present.
   void HandleAppId();
 
-  // Makes this version of the updater active, self-registers for updates, then
-  // runs the |done| closure.
-  void MakeActive(base::OnceClosure done);
-
   // Bound to the main sequence.
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -80,14 +79,6 @@
 
   scoped_refptr<AppInstallController> app_install_controller_;
 
-  // These prefs objects are used to make the updater active and register this
-  // version of the updater for self-updates.
-  //
-  // TODO(crbug.com/1109231) - this is a temporary workaround until a better
-  // fix is found.
-  std::unique_ptr<LocalPrefs> local_prefs_;
-  std::unique_ptr<GlobalPrefs> global_prefs_;
-
   scoped_refptr<base::TaskRunner> make_active_task_runner_;
 };
 
diff --git a/chrome/updater/app/app_register.cc b/chrome/updater/app/app_register.cc
index 86a89c19..b79a9ac 100644
--- a/chrome/updater/app/app_register.cc
+++ b/chrome/updater/app/app_register.cc
@@ -134,9 +134,11 @@
     return;
   }
 
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()}, base::BindOnce(&InstallCandidate, false),
-      base::BindOnce(&AppRegister::InstallCandidateDone, this));
+  base::ThreadPool::PostTask(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&InstallCandidate, false,
+                     base::SequencedTaskRunnerHandle::Get(),
+                     base::BindOnce(&AppRegister::InstallCandidateDone, this)));
 }
 
 void AppRegister::InstallCandidateDone(int result) {
diff --git a/chrome/updater/app/app_server.cc b/chrome/updater/app/app_server.cc
index 59ee0fe..2d7e354 100644
--- a/chrome/updater/app/app_server.cc
+++ b/chrome/updater/app/app_server.cc
@@ -10,6 +10,7 @@
 #include "base/version.h"
 #include "chrome/updater/configurator.h"
 #include "chrome/updater/constants.h"
+#include "chrome/updater/persisted_data.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/updater_version.h"
 #include "components/prefs/pref_service.h"
@@ -78,6 +79,12 @@
   if (!result)
     return false;
   global_prefs->SetActiveVersion(UPDATER_VERSION_STRING);
+  scoped_refptr<PersistedData> persisted_data =
+      base::MakeRefCounted<PersistedData>(global_prefs->GetPrefService());
+  if (!persisted_data->GetProductVersion(kUpdaterAppId).IsValid()) {
+    persisted_data->SetProductVersion(kUpdaterAppId,
+                                      base::Version(UPDATER_VERSION_STRING));
+  }
   global_prefs->SetSwapping(false);
   PrefsCommitPendingWrites(global_prefs->GetPrefService());
   return true;
diff --git a/chrome/updater/app/app_update.cc b/chrome/updater/app/app_update.cc
index 88bf05d..47d90c9 100644
--- a/chrome/updater/app/app_update.cc
+++ b/chrome/updater/app/app_update.cc
@@ -8,6 +8,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/version.h"
 #include "chrome/updater/app/app.h"
 #include "chrome/updater/configurator.h"
@@ -41,9 +42,11 @@
 }
 
 void AppUpdate::FirstTaskRun() {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()}, base::BindOnce(&InstallCandidate, false),
-      base::BindOnce(&AppUpdate::SetupDone, this));
+  base::ThreadPool::PostTask(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&InstallCandidate, false,
+                     base::SequencedTaskRunnerHandle::Get(),
+                     base::BindOnce(&AppUpdate::SetupDone, this)));
 }
 
 void AppUpdate::SetupDone(int result) {
diff --git a/chrome/updater/app/server/mac/service_delegate.mm b/chrome/updater/app/server/mac/service_delegate.mm
index 49647b3..35c89cc5 100644
--- a/chrome/updater/app/server/mac/service_delegate.mm
+++ b/chrome/updater/app/server/mac/service_delegate.mm
@@ -285,6 +285,7 @@
                  appServer:(scoped_refptr<updater::AppServerMac>)appServer {
   if (self = [super init]) {
     _service = service;
+    _appServer = appServer;
     _callbackRunner = base::SequencedTaskRunnerHandle::Get();
   }
   return self;
diff --git a/chrome/updater/installer.cc b/chrome/updater/installer.cc
index 21614d44..9026fe7 100644
--- a/chrome/updater/installer.cc
+++ b/chrome/updater/installer.cc
@@ -64,6 +64,7 @@
   const auto pv = persisted_data_->GetProductVersion(app_id_);
   if (pv.IsValid()) {
     pv_ = pv;
+    checker_path_ = persisted_data_->GetExistenceCheckerPath(app_id_);
     fingerprint_ = persisted_data_->GetFingerprint(app_id_);
   } else {
     pv_ = base::Version(kNullVersion);
diff --git a/chrome/updater/installer.h b/chrome/updater/installer.h
index e7f22ae..b3db933 100644
--- a/chrome/updater/installer.h
+++ b/chrome/updater/installer.h
@@ -102,6 +102,7 @@
 
   // These members are not updated when the installer succeeds.
   base::Version pv_;
+  base::FilePath checker_path_;
   std::string fingerprint_;
 };
 
diff --git a/chrome/updater/installer_mac.cc b/chrome/updater/installer_mac.cc
index 07f2989..0bb22d1 100644
--- a/chrome/updater/installer_mac.cc
+++ b/chrome/updater/installer_mac.cc
@@ -17,10 +17,8 @@
   DVLOG(1) << "Running application install from DMG";
   // InstallFromDMG() returns the exit code of the script. 0 is success and
   // anything else should be an error.
-  return InstallFromDMG(
-      app_installer, persisted_data_->GetExistenceCheckerPath(app_id_),
-      base::StrCat({persisted_data_->GetProductVersion(app_id_).GetString(),
-                    " ", arguments}));
+  return InstallFromDMG(app_installer, checker_path_,
+                        base::StrCat({pv_.GetString(), " ", arguments}));
 }
 
 }  // namespace updater
diff --git a/chrome/updater/setup.h b/chrome/updater/setup.h
index 951d0d5..247ee419 100644
--- a/chrome/updater/setup.h
+++ b/chrome/updater/setup.h
@@ -5,9 +5,19 @@
 #ifndef CHROME_UPDATER_SETUP_H_
 #define CHROME_UPDATER_SETUP_H_
 
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class TaskRunner;
+}  // namespace base
+
 namespace updater {
 
-int InstallCandidate(bool is_machine);
+// Installs the candidate, then posts |callback| to |runner|.
+void InstallCandidate(bool is_machine,
+                      scoped_refptr<base::TaskRunner> runner,
+                      base::OnceCallback<void(int)> callback);
 
 }  // namespace updater
 
diff --git a/chrome/updater/setup_mac.cc b/chrome/updater/setup_mac.cc
index 1751577..2fd2475 100644
--- a/chrome/updater/setup_mac.cc
+++ b/chrome/updater/setup_mac.cc
@@ -5,10 +5,18 @@
 #include "chrome/updater/mac/setup/setup.h"
 #include "chrome/updater/setup.h"
 
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/task_runner.h"
+
 namespace updater {
 
-int InstallCandidate(bool is_machine) {
-  return InstallCandidate();
+void InstallCandidate(bool is_machine,
+                      scoped_refptr<base::TaskRunner> runner,
+                      base::OnceCallback<void(int)> callback) {
+  runner->PostDelayedTask(
+      FROM_HERE, base::BindOnce(std::move(callback), InstallCandidate()),
+      base::TimeDelta::FromSeconds(3));
 }
 
 }  // namespace updater
diff --git a/chrome/updater/setup_win.cc b/chrome/updater/setup_win.cc
index 87e9b5c..3328b029 100644
--- a/chrome/updater/setup_win.cc
+++ b/chrome/updater/setup_win.cc
@@ -5,10 +5,17 @@
 #include "chrome/updater/setup.h"
 #include "chrome/updater/win/setup/setup.h"
 
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/task_runner.h"
+
 namespace updater {
 
-int InstallCandidate(bool is_machine) {
-  return Setup(is_machine);
+void InstallCandidate(bool is_machine,
+                      scoped_refptr<base::TaskRunner> runner,
+                      base::OnceCallback<void(int)> callback) {
+  runner->PostTask(FROM_HERE,
+                   base::BindOnce(std::move(callback), Setup(is_machine)));
 }
 
 }  // namespace updater
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index 16bbe5f..a14257e1 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -55,22 +55,6 @@
 TEST_F(IntegrationTest, InstallUninstall) {
   Install();
   ExpectInstalled();
-  Uninstall();
-}
-
-TEST_F(IntegrationTest, InstallAndPromote) {
-  Install();
-  ExpectInstalled();
-// TODO(crbug.com/1109231): resolve implementation inconsistencies for
-// different platforms. In this case, Windows promotes during Install, and
-// as a post-condition, the version of the updater is active before --wake
-// runs.
-#if defined(OS_WIN)
-  ExpectActiveVersion(UPDATER_VERSION_STRING);
-#else
-  ExpectActiveVersion("0");
-#endif
-  RunWake(0);  // Candidate qualifies and promotes to active.
   ExpectActiveVersion(UPDATER_VERSION_STRING);
   ExpectActive();
   Uninstall();
diff --git a/chrome/updater/test/integration_tests_mac.mm b/chrome/updater/test/integration_tests_mac.mm
index a0d3c5c..b2e34f36 100644
--- a/chrome/updater/test/integration_tests_mac.mm
+++ b/chrome/updater/test/integration_tests_mac.mm
@@ -128,7 +128,7 @@
   const base::FilePath path = GetExecutablePath();
   ASSERT_FALSE(path.empty());
   base::CommandLine command_line(path);
-  command_line.AppendSwitch(kUpdateSwitch);
+  command_line.AppendSwitch(kInstallSwitch);
   int exit_code = -1;
   ASSERT_TRUE(Run(command_line, &exit_code));
   EXPECT_EQ(0, exit_code);
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index 324cada..8d3c475e 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -111,10 +111,10 @@
 #if defined(OS_WIN)
   if (command_line->HasSwitch(kComServiceSwitch))
     return ServiceMain::RunComService(command_line);
+#endif  // OS_WIN
 
   if (command_line->HasSwitch(kInstallSwitch))
     return MakeAppInstall()->Run();
-#endif  // OS_WIN
 
   if (command_line->HasSwitch(kUninstallSwitch))
     return MakeAppUninstall()->Run();
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 9099ed3..5597ec9 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -907,9 +907,9 @@
           browser_context, std::move(extension_factory)));
 #endif
 
-  uniquely_owned_factories->emplace(
+  factories->emplace(
       kChromeResourceScheme,
-      content::CreateWebUIURLLoader(
+      content::CreateWebUIURLLoaderFactory(
           frame_host, kChromeResourceScheme,
           /*allowed_webui_hosts=*/base::flat_set<std::string>()));
 }
diff --git a/chromecast/graphics/rounded_window_corners_manager.cc b/chromecast/graphics/rounded_window_corners_manager.cc
index 30793f8..faca933 100644
--- a/chromecast/graphics/rounded_window_corners_manager.cc
+++ b/chromecast/graphics/rounded_window_corners_manager.cc
@@ -33,6 +33,17 @@
   return window->id() != CastWindowManager::CORNERS_OVERLAY ? window : nullptr;
 }
 
+bool HasNonAppParent(const aura::Window* window) {
+  const aura::Window* parent = window->parent();
+  while (parent && parent->IsVisible()) {
+    if (parent->id() != CastWindowManager::APP)
+      return true;
+    else
+      parent = parent->parent();
+  }
+  return false;
+}
+
 }  // namespace
 
 // Keeps track of the creation and destruction of webview container windows, and
@@ -101,9 +112,12 @@
       return;
 
     int window_id = topmost_visible_window->id();
+    // The window may be a child to a visible non-app window that does not draw
+    // its own corners, so this needs to be checked for.
     bool set_rounded_corners =
         (window_id != CastWindowManager::APP) ||
-        base::Contains(observed_container_windows_, topmost_visible_window);
+        base::Contains(observed_container_windows_, topmost_visible_window) ||
+        HasNonAppParent(topmost_visible_window);
 
     if (rounded_corners_ == set_rounded_corners)
       return;
diff --git a/chromecast/graphics/rounded_window_corners_manager_unittest.cc b/chromecast/graphics/rounded_window_corners_manager_unittest.cc
index 0d3ace34..ef0c9ce 100644
--- a/chromecast/graphics/rounded_window_corners_manager_unittest.cc
+++ b/chromecast/graphics/rounded_window_corners_manager_unittest.cc
@@ -344,4 +344,32 @@
   webview = nullptr;
 }
 
+TEST_F(RoundedWindowCornersManagerTest, UnmanagedAppWithChild) {
+  aura::Window* root_window = mock_cast_window_manager_->GetRootWindow();
+  root_window->Show();
+  std::unique_ptr<aura::Window> window_host =
+      std::make_unique<aura::Window>(nullptr);
+  window_host->Init(ui::LAYER_TEXTURED);
+  window_host->Show();
+  root_window->AddChild(window_host.get());
+  std::unique_ptr<aura::Window> unmanaged_app =
+      std::make_unique<aura::Window>(nullptr);
+  unmanaged_app->Init(ui::LAYER_TEXTURED);
+  unmanaged_app->set_id(CastWindowManager::UNMANAGED_APP);
+  window_host->AddChild(unmanaged_app.get());
+
+  EXPECT_CALL(*mock_cast_window_manager_, SetEnableRoundedCorners(true));
+  unmanaged_app->Show();
+
+  std::unique_ptr<aura::Window> child = std::make_unique<aura::Window>(nullptr);
+  child->Init(ui::LAYER_TEXTURED);
+  unmanaged_app->AddChild(child.get());
+  // Rounded corners should be retained if the unmanaged app parent is visible.
+  child->Show();
+  child = nullptr;
+
+  EXPECT_CALL(*mock_cast_window_manager_, SetEnableRoundedCorners(false));
+  unmanaged_app = nullptr;
+}
+
 }  // namespace chromecast
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 3d15f9d..4588056 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13466.0.0
\ No newline at end of file
+13470.0.0
\ No newline at end of file
diff --git a/chromeos/components/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.cc b/chromeos/components/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.cc
index c6e840ae..4f61827f 100644
--- a/chromeos/components/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.cc
+++ b/chromeos/components/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.cc
@@ -154,8 +154,15 @@
         NonRemovableBlockDeviceResult::NewBlockDeviceInfo(std::move(infos));
   }
   {
+    auto os_version = chromeos::cros_healthd::mojom::OsVersion::New();
+    os_version->release_milestone = "87";
+    os_version->build_number = "13544";
+    os_version->patch_number = "59.0";
+    os_version->release_channel = "stable-channel";
+
     auto system_info = chromeos::cros_healthd::mojom::SystemInfo::New();
     system_info->product_sku_number = "sku-18";
+    system_info->os_version = std::move(os_version);
 
     telemetry_info->system_result =
         chromeos::cros_healthd::mojom::SystemResult::NewSystemInfo(
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index b874314..81a70e0 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -449,7 +449,7 @@
 // Enables or disables showing the battery level in the System Tray and Settings
 // UI for supported Bluetooth Devices.
 const base::Feature kShowBluetoothDeviceBattery{
-    "ShowBluetoothDeviceBattery", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ShowBluetoothDeviceBattery", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Shows the Play Store icon in Demo Mode.
 const base::Feature kShowPlayInDemoMode{"ShowPlayInDemoMode",
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn
index 50335d33..a4d44065 100644
--- a/chromeos/crosapi/mojom/BUILD.gn
+++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -6,9 +6,9 @@
 
 mojom("mojom") {
   sources = [
-    "attestation.mojom",
     "bitmap.mojom",
     "crosapi.mojom",
+    "keystore_service.mojom",
     "message_center.mojom",
     "notification.mojom",
     "screen_manager.mojom",
diff --git a/chromeos/crosapi/mojom/attestation.mojom b/chromeos/crosapi/mojom/attestation.mojom
deleted file mode 100644
index eb8b1fd..0000000
--- a/chromeos/crosapi/mojom/attestation.mojom
+++ /dev/null
@@ -1,31 +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.
-
-module crosapi.mojom;
-
-[Extensible]
-enum ChallengeKeyType {
-  // The challenge is attested by keys belonging to the current user.
-  kUser = 0,
-
-  // The challenge is attested by keys belonging to the device.
-  kDevice = 1,
-};
-
-union ChallengeKeyResult {
-  // Implies failure.
-  string error_message;
-
-  // Implies success.
-  string challenge_response;
-};
-
-// This interface is implemented by ash-chrome. It provides lacros-chrome a
-// mechanism to use the system keystore to attest challenges.
-interface Attestation {
-  // Requests that the OS keystore attest a challenge.
-  ChallengeKey@0(string challenge, ChallengeKeyType type) =>
-      (ChallengeKeyResult result);
-};
-
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 038659a7..2c96ace1 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -7,7 +7,7 @@
 
 module crosapi.mojom;
 
-import "chromeos/crosapi/mojom/attestation.mojom";
+import "chromeos/crosapi/mojom/keystore_service.mojom";
 import "chromeos/crosapi/mojom/message_center.mojom";
 import "chromeos/crosapi/mojom/screen_manager.mojom";
 import "chromeos/crosapi/mojom/select_file.mojom";
@@ -16,8 +16,8 @@
 // AshChromeService defines the APIs that live in ash-chrome and are
 // accessed from lacros-chrome.
 interface AshChromeService {
-  // Binds the Attestation interface for challenging keys.
-  BindAttestation@2(pending_receiver<Attestation> receiver);
+  // Binds the KeystoreService interface for challenging keys.
+  BindKeystoreService@2(pending_receiver<KeystoreService> receiver);
 
   // Binds the MessageCenter interface for showing notification messages.
   BindMessageCenter@3(pending_receiver<MessageCenter> receiver);
diff --git a/chromeos/crosapi/mojom/keystore_service.mojom b/chromeos/crosapi/mojom/keystore_service.mojom
new file mode 100644
index 0000000..db00e1e
--- /dev/null
+++ b/chromeos/crosapi/mojom/keystore_service.mojom
@@ -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.
+
+module crosapi.mojom;
+
+// This interface mirrors the enterprise.platformKeys extension API.
+// TODO(https://crbug.com/1128022): Figure out the appropriate API surface for
+// long-term stabilization.
+
+// The system has a keystore and a certificate store. Keys are tuples
+// of (private key, public key). These are generated by the system and the
+// private key is never shared. Certificates provide proof of ownership of a
+// private key. There are many uses for keys and certificates -- this interface
+// currently focuses on the use cases for the enterprise_platform_keys extension
+// API.
+
+// Both keystores and certificate stores have two variants: device and user.
+// Device keys/certificates are available to all affiliated users on the device.
+// User keys/certificates are only available to the current user.
+[Stable, Extensible]
+enum KeystoreType {
+  kUser = 0,
+  kDevice = 1,
+};
+
+// Returned by ChallengeAttestationOnlyKeystore().
+[Stable]
+union ChallengeAttestationOnlyKeystoreResult {
+  // Implies failure.
+  string error_message;
+
+  // Implies success.
+  string challenge_response;
+};
+
+// This interface is implemented by ash-chrome. It provides lacros-chrome a
+// mechanism to modify and query the attestation-only and generate purpose
+// keystores.
+[Stable]
+interface KeystoreService {
+  // This API serves a challenge to a special "attestation-only" keystore. This
+  // keystore only contains 2 private keys (1 for the user, 1 for the device).
+  // The challenge must be generated via the Verified Access Web API. If
+  // |migrate| is true, then after the attestation, the key is migrated
+  // from the attestation-only keystore to the regular keystore. A new
+  // "attestation-only" key is generated on demand if a key does not exist
+  // because it was recently migrated. If a key is migrated, the expectation is
+  // that the caller will later call ImportCertificate() to associate a
+  // certificate with the migrated key.
+  ChallengeAttestationOnlyKeystore@0(
+      string challenge, KeystoreType type, bool migrate) =>
+          (ChallengeAttestationOnlyKeystoreResult result);
+};
+
diff --git a/chromeos/dbus/debug_daemon/debug_daemon_client.cc b/chromeos/dbus/debug_daemon/debug_daemon_client.cc
index 878c339..4ae7156 100644
--- a/chromeos/dbus/debug_daemon/debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon/debug_daemon_client.cc
@@ -531,6 +531,16 @@
                        std::move(error_callback)));
   }
 
+  void StartConcierge(ConciergeCallback callback) override {
+    dbus::MethodCall method_call(debugd::kDebugdInterface,
+                                 debugd::kStartVmConcierge);
+    dbus::MessageWriter writer(&method_call);
+    debugdaemon_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&DebugDaemonClientImpl::OnStartConcierge,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void StartPluginVmDispatcher(const std::string& owner_id,
                                const std::string& lang,
                                PluginVmDispatcherCallback callback) override {
@@ -860,6 +870,15 @@
       std::move(error_callback).Run();
   }
 
+  void OnStartConcierge(ConciergeCallback callback, dbus::Response* response) {
+    bool result = false;
+    if (response) {
+      dbus::MessageReader reader(response);
+      reader.PopBool(&result);
+    }
+    std::move(callback).Run(result);
+  }
+
   void OnStartPluginVmDispatcher(PluginVmDispatcherCallback callback,
                                  dbus::Response* response,
                                  dbus::ErrorResponse* error) {
diff --git a/chromeos/dbus/debug_daemon/debug_daemon_client.h b/chromeos/dbus/debug_daemon/debug_daemon_client.h
index d493c10..bed3e4f 100644
--- a/chromeos/dbus/debug_daemon/debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon/debug_daemon_client.h
@@ -235,6 +235,14 @@
                                  CupsRemovePrinterCallback callback,
                                  base::OnceClosure error_callback) = 0;
 
+  // A callback to handle the result of StartConcierge.
+  using ConciergeCallback = base::OnceCallback<void(bool success)>;
+  // Calls debugd::kStartVmConcierge, which starts the Concierge service.
+  // |callback| is called when the method finishes. If the |callback| is called
+  // with true, it is guaranteed that the service is ready to accept requests.
+  // It is not necessary for ConciergeClient to use WaitForServiceToBeAvailable.
+  virtual void StartConcierge(ConciergeCallback callback) = 0;
+
   // A callback to handle the result of
   // StartPluginVmDispatcher/StopPluginVmDispatcher.
   using PluginVmDispatcherCallback = base::OnceCallback<void(bool success)>;
diff --git a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
index 5bda3a2..a4d66a5e 100644
--- a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
@@ -257,6 +257,11 @@
       FROM_HERE, base::BindOnce(std::move(callback), has_printer));
 }
 
+void FakeDebugDaemonClient::StartConcierge(ConciergeCallback callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), true));
+}
+
 void FakeDebugDaemonClient::StartPluginVmDispatcher(
     const std::string& /* owner_id */,
     const std::string& /* lang */,
diff --git a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
index 069b356..798a642a 100644
--- a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
@@ -88,6 +88,7 @@
   void CupsRemovePrinter(const std::string& name,
                          CupsRemovePrinterCallback callback,
                          base::OnceClosure error_callback) override;
+  void StartConcierge(ConciergeCallback callback) override;
   void StartPluginVmDispatcher(const std::string& owner_id,
                                const std::string& lang,
                                PluginVmDispatcherCallback callback) override;
diff --git a/chromeos/dbus/power/fake_power_manager_client.cc b/chromeos/dbus/power/fake_power_manager_client.cc
index 1defbab..cf65a79 100644
--- a/chromeos/dbus/power/fake_power_manager_client.cc
+++ b/chromeos/dbus/power/fake_power_manager_client.cc
@@ -14,6 +14,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/posix/unix_domain_socket.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -383,10 +384,13 @@
 
 void FakePowerManagerClient::RefreshBluetoothBattery(
     const std::string& address) {
+  if (!base::Contains(peripheral_battery_refresh_levels_, address))
+    return;
+
   for (auto& observer : observers_) {
     observer.PeripheralBatteryStatusReceived(
         SysnameFromBluetoothAddress(address), "somename",
-        peripheral_battery_refresh_level_);
+        peripheral_battery_refresh_levels_[address]);
   }
 }
 
diff --git a/chromeos/dbus/power/fake_power_manager_client.h b/chromeos/dbus/power/fake_power_manager_client.h
index ec3ea71..4b0007e 100644
--- a/chromeos/dbus/power/fake_power_manager_client.h
+++ b/chromeos/dbus/power/fake_power_manager_client.h
@@ -79,8 +79,9 @@
   void set_user_activity_callback(base::RepeatingClosure callback) {
     user_activity_callback_ = std::move(callback);
   }
-  void set_peripheral_battery_refresh_level(int level) {
-    peripheral_battery_refresh_level_ = level;
+  void set_peripheral_battery_refresh_level(const std::string& address,
+                                            int level) {
+    peripheral_battery_refresh_levels_[address] = level;
   }
 
   // PowerManagerClient overrides:
@@ -313,7 +314,7 @@
   bool simulate_start_arc_timer_failure_ = false;
 
   // Used in RefreshBluetoothBattery.
-  int peripheral_battery_refresh_level_ = 0;
+  base::flat_map<std::string, int> peripheral_battery_refresh_levels_;
 
   // Note: This should remain the last member so it'll be destroyed and
   // invalidate its weak pointers before any other members are destroyed.
diff --git a/chromeos/lacros/lacros_chrome_service_impl.cc b/chromeos/lacros/lacros_chrome_service_impl.cc
index eff53e1..31cf3bb 100644
--- a/chromeos/lacros/lacros_chrome_service_impl.cc
+++ b/chromeos/lacros/lacros_chrome_service_impl.cc
@@ -117,10 +117,10 @@
     ash_chrome_service_->BindScreenManager(std::move(pending_receiver));
   }
 
-  void BindAttestationReceiver(
-      mojo::PendingReceiver<crosapi::mojom::Attestation> pending_receiver) {
+  void BindKeystoreServiceReceiver(
+      mojo::PendingReceiver<crosapi::mojom::KeystoreService> pending_receiver) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    ash_chrome_service_->BindAttestation(std::move(pending_receiver));
+    ash_chrome_service_->BindKeystoreService(std::move(pending_receiver));
   }
 
   base::WeakPtr<LacrosChromeServiceNeverBlockingState> GetWeakPtr() {
@@ -209,14 +209,14 @@
           &LacrosChromeServiceNeverBlockingState::BindSelectFileReceiver,
           weak_sequenced_state_, std::move(select_file_pending_receiver)));
 
-  mojo::PendingReceiver<crosapi::mojom::Attestation>
-      attestation_pending_receiver =
-          attestation_remote_.BindNewPipeAndPassReceiver();
+  mojo::PendingReceiver<crosapi::mojom::KeystoreService>
+      keystore_service_pending_receiver =
+          keystore_service_remote_.BindNewPipeAndPassReceiver();
   never_blocking_sequence_->PostTask(
       FROM_HERE,
       base::BindOnce(
-          &LacrosChromeServiceNeverBlockingState::BindAttestationReceiver,
-          weak_sequenced_state_, std::move(attestation_pending_receiver)));
+          &LacrosChromeServiceNeverBlockingState::BindKeystoreServiceReceiver,
+          weak_sequenced_state_, std::move(keystore_service_pending_receiver)));
 
   mojo::PendingReceiver<device::mojom::HidManager>
       hid_manager_pending_receiver =
diff --git a/chromeos/lacros/lacros_chrome_service_impl.h b/chromeos/lacros/lacros_chrome_service_impl.h
index 11ab1e972..6d28b38 100644
--- a/chromeos/lacros/lacros_chrome_service_impl.h
+++ b/chromeos/lacros/lacros_chrome_service_impl.h
@@ -12,8 +12,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
-#include "chromeos/crosapi/mojom/attestation.mojom.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
 #include "chromeos/crosapi/mojom/message_center.mojom.h"
 #include "chromeos/crosapi/mojom/screen_manager.mojom.h"
 #include "chromeos/crosapi/mojom/select_file.mojom.h"
@@ -80,10 +80,10 @@
   }
 
   // This must be called on the affine sequence. It exposes a remote that can
-  // be used to perform attestation on challenges.
-  mojo::Remote<crosapi::mojom::Attestation>& attestation_remote() {
+  // be used to query the system keystores.
+  mojo::Remote<crosapi::mojom::KeystoreService>& keystore_service_remote() {
     DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
-    return attestation_remote_;
+    return keystore_service_remote_;
   }
 
   // This must be called on the affine sequence. It exposes a remote that can
@@ -123,10 +123,10 @@
   mojo::Remote<crosapi::mojom::SelectFile> select_file_remote_;
   mojo::Remote<device::mojom::HidManager> hid_manager_remote_;
 
-  // This member allows lacros-chrome to use the Attestation interface. This
+  // This member allows lacros-chrome to use the KeystoreService interface. This
   // member is affine to the affine sequence. It is initialized in the
   // constructor and it is immediately available for use.
-  mojo::Remote<crosapi::mojom::Attestation> attestation_remote_;
+  mojo::Remote<crosapi::mojom::KeystoreService> keystore_service_remote_;
 
   // This member is instantiated on the affine sequence alongside the
   // constructor. All subsequent invocations of this member, including
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 0ef32e8..ae1b643 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-86-4240.11-1599471529-benchmark-86.0.4240.36-r1.orderfile.xz
+chromeos-chrome-orderfile-field-86-4240.27-1600081136-benchmark-86.0.4240.36-r1.orderfile.xz
diff --git a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
index 952b063..1bf2d1a 100644
--- a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
+++ b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
@@ -432,6 +432,20 @@
   ProbeError error;
 };
 
+// Structure containing information about the operating system version of the
+// device. This structure decomposes a full version string into its parts:
+// (87.13544.59.0).
+struct OsVersion {
+  // The OS version release milestone (87).
+  string release_milestone;
+  // The OS version build number (13544).
+  string build_number;
+  // The OS version patch number (59.0).
+  string patch_number;
+  // The OS release channel (stable-channel).
+  string release_channel;
+};
+
 // System Information.
 struct SystemInfo {
   // The date the device was first activated.
@@ -456,6 +470,8 @@
   UInt64Value? chassis_type;
   // The product name (model) of the system.
   string? product_name;
+  // The OS version of the system.
+  OsVersion os_version;
 };
 
 // Network probe result. Can either be populated with the NetworkHealthSnapshot
@@ -504,8 +520,8 @@
   // Information about the device's Bluetooth adapters and devices. Only present
   // when kBluetooth was included in the categories input to ProbeTelemetryInfo.
   BluetoothResult? bluetooth_result;
-  // Information about the system from various sources. Only present when
-  // kSystem was included in the categories input to ProbeTelemetryInfo.
+  // Information about the system. Only present when kSystem was included in the
+  // categories input to ProbeTelemetryInfo.
   SystemResult? system_result;
   // Information about the networking devices and associated networks of the
   // system. Only present when kNetwork was included in the categories input to
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index d831266..9b62fe38 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -67,7 +67,13 @@
     LOG(ERROR) << "SetVmCpuRestriction for ARCVM failed";
 }
 
-void SetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state) {
+void DoSetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state,
+                              bool concierge_started) {
+  if (!concierge_started) {
+    LOG(ERROR) << "Concierge D-Bus service is not available";
+    return;
+  }
+
   auto* client = chromeos::DBusThreadManager::Get()->GetConciergeClient();
   if (!client) {
     LOG(ERROR) << "ConciergeClient is not available";
@@ -91,6 +97,17 @@
                               base::BindOnce(&OnSetArcVmCpuRestriction));
 }
 
+void SetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state) {
+  auto* client = chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+  if (!client) {
+    LOG(WARNING) << "DebugDaemonClient is not available";
+    return;
+  }
+  // TODO(wvk): Call StartConcierge() only when the service is not running.
+  client->StartConcierge(
+      base::BindOnce(&DoSetArcVmCpuRestriction, cpu_restriction_state));
+}
+
 void SetArcContainerCpuRestriction(CpuRestrictionState cpu_restriction_state) {
   if (!chromeos::SessionManagerClient::Get()) {
     LOG(WARNING) << "SessionManagerClient is not available";
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index d112e43..d25be35 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -527,6 +527,14 @@
                        weak_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void UpgradeArc(UpgradeParams params,
+                  chromeos::VoidDBusMethodCallback callback) override {
+    VLOG(1) << "Starting Concierge service";
+    GetDebugDaemonClient()->StartConcierge(base::BindOnce(
+        &ArcVmClientAdapter::OnConciergeStarted, weak_factory_.GetWeakPtr(),
+        std::move(params), std::move(callback)));
+  }
+
   void StopArcInstance(bool on_shutdown, bool should_backup_log) override {
     if (on_shutdown) {
       // Do nothing when |on_shutdown| is true because either vm_concierge.conf
@@ -667,8 +675,14 @@
     should_notify_observers_ = true;
   }
 
-  void UpgradeArc(UpgradeParams params,
-                  chromeos::VoidDBusMethodCallback callback) override {
+  void OnConciergeStarted(UpgradeParams params,
+                          chromeos::VoidDBusMethodCallback callback,
+                          bool success) {
+    if (!success) {
+      LOG(ERROR) << "Failed to start Concierge service for arcvm";
+      std::move(callback).Run(false);
+      return;
+    }
     VLOG(2) << "Checking file system status";
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
diff --git a/components/arc/session/arc_vm_client_adapter_unittest.cc b/components/arc/session/arc_vm_client_adapter_unittest.cc
index 0cc55878..6a469e4 100644
--- a/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -97,6 +97,11 @@
   TestDebugDaemonClient() = default;
   ~TestDebugDaemonClient() override = default;
 
+  void StartConcierge(ConciergeCallback callback) override {
+    start_concierge_called_ = true;
+    std::move(callback).Run(start_concierge_result_);
+  }
+
   void BackupArcBugReport(const std::string& userhash,
                           chromeos::VoidDBusMethodCallback callback) override {
     backup_arc_bug_report_called_ = true;
@@ -110,7 +115,14 @@
     backup_arc_bug_report_result_ = result;
   }
 
+  bool start_concierge_called() const { return start_concierge_called_; }
+  void set_start_concierge_result(bool result) {
+    start_concierge_result_ = result;
+  }
+
  private:
+  bool start_concierge_called_ = false;
+  bool start_concierge_result_ = true;
   bool backup_arc_bug_report_called_ = false;
   bool backup_arc_bug_report_result_ = true;
 
@@ -301,6 +313,14 @@
   }
 
  protected:
+  bool GetStartConciergeCalled() {
+    return GetTestDebugDaemonClient()->start_concierge_called();
+  }
+
+  void SetStartConciergeResponse(bool response) {
+    GetTestDebugDaemonClient()->set_start_concierge_result(response);
+  }
+
   void SetValidUserInfo() { SetUserInfo(kUserIdHash, kSerialNumber); }
 
   void SetUserInfo(const std::string& hash, const std::string& serial) {
@@ -735,6 +755,7 @@
   InjectUpstartStartJobFailure(kArcVmServerProxyJobName);
 
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -760,6 +781,7 @@
 
   // Upgrade should still succeed.
   UpgradeArc(true);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 }
@@ -774,6 +796,7 @@
 
   EnableAdbOverUsbForTesting();
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -798,6 +821,7 @@
   InjectUpstartStartJobFailure(kArcCreateDataJobName);
 
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -823,6 +847,7 @@
   InjectUpstartStartJobFailure(kArcVmMountMyFilesJobName);
 
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -849,6 +874,30 @@
   InjectUpstartStartJobFailure(kArcVmMountRemovableMediaJobName);
 
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
+  EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
+  EXPECT_FALSE(arc_instance_stopped_called());
+
+  // Try to stop the VM. StopVm will fail in this case because
+  // no VM is running.
+  vm_tools::concierge::StopVmResponse response;
+  response.set_success(false);
+  GetTestConciergeClient()->set_stop_vm_response(response);
+  adapter()->StopArcInstance(/*on_shutdown=*/false,
+                             /*should_backup_log=*/false);
+  run_loop()->Run();
+  EXPECT_TRUE(GetTestConciergeClient()->stop_vm_called());
+  EXPECT_TRUE(arc_instance_stopped_called());
+}
+
+// Tests that UpgradeArc() handles StartConcierge() failures properly.
+TEST_F(ArcVmClientAdapterTest, UpgradeArc_StartConciergeFailure) {
+  SetValidUserInfo();
+  StartMiniArc();
+  // Inject failure to StartConcierge().
+  SetStartConciergeResponse(false);
+  UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -872,6 +921,7 @@
   StartMiniArc();
 
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -895,6 +945,7 @@
   StartMiniArc();
 
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -920,6 +971,7 @@
   GetTestConciergeClient()->set_start_vm_response(start_vm_response);
 
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -942,6 +994,7 @@
   GetTestConciergeClient()->set_start_vm_response(base::nullopt);
 
   UpgradeArc(false);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -962,6 +1015,7 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -986,6 +1040,7 @@
 
   UpgradeParams params(GetPopulatedUpgradeParams());
   UpgradeArcWithParams(true, std::move(params));
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 }
@@ -1009,6 +1064,7 @@
   params.preferred_languages = {"en_US"};
 
   UpgradeArcWithParams(true, std::move(params));
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 }
@@ -1028,6 +1084,7 @@
   params.demo_session_apps_path = base::FilePath(kDemoImage);
 
   UpgradeArcWithParams(true, std::move(params));
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1051,6 +1108,7 @@
   StartMiniArcWithParams(true, std::move(start_params));
   UpgradeParams params(GetPopulatedUpgradeParams());
   UpgradeArcWithParams(true, std::move(params));
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
   EXPECT_TRUE(
@@ -1083,6 +1141,7 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1097,6 +1156,7 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1111,6 +1171,7 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1133,6 +1194,7 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
+  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index b5e1414..eb24efd 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -7,6 +7,12 @@
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//tools/grit/grit_rule.gni")
 
+# Reset sources_assignment_filter for the BUILD.gn file to prevent
+# regression during the migration of Chromium away from the feature.
+# See docs/no_sources_assignment_filter.md for more information.
+# TODO(crbug.com/1018739): remove this when migration is done.
+set_sources_assignment_filter([])
+
 grit("autofill_address_rewriter_resources") {
   source = "autofill_address_rewriter_resources.grd"
   outputs = [
@@ -56,8 +62,6 @@
     "autofill_handler.h",
     "autofill_handler_proxy.cc",
     "autofill_handler_proxy.h",
-    "autofill_ie_toolbar_import_win.cc",
-    "autofill_ie_toolbar_import_win.h",
     "autofill_manager.cc",
     "autofill_manager.h",
     "autofill_manager_test_delegate.h",
@@ -125,8 +129,6 @@
     "form_data_importer.h",
     "form_parsing/address_field.cc",
     "form_parsing/address_field.h",
-    "form_parsing/autofill_parsing_utils.cc",
-    "form_parsing/autofill_parsing_utils.h",
     "form_parsing/autofill_scanner.cc",
     "form_parsing/autofill_scanner.h",
     "form_parsing/credit_card_field.cc",
@@ -185,8 +187,6 @@
     "metrics/form_event_logger_base.cc",
     "metrics/form_event_logger_base.h",
     "metrics/form_events.h",
-    "pattern_provider/pattern_provider.cc",
-    "pattern_provider/pattern_provider.h",
     "payments/account_info_getter.h",
     "payments/autofill_offer_manager.cc",
     "payments/autofill_offer_manager.h",
@@ -304,6 +304,13 @@
     "webdata/system_encryptor.h",
   ]
 
+  if (is_win) {
+    sources += [
+      "autofill_ie_toolbar_import_win.cc",
+      "autofill_ie_toolbar_import_win.h",
+    ]
+  }
+
   if (is_ios) {
     sources += [
       "keyboard_accessory_metrics_logger.h",
@@ -583,7 +590,6 @@
     "autofill_driver_factory_unittest.cc",
     "autofill_experiments_unittest.cc",
     "autofill_external_delegate_unittest.cc",
-    "autofill_ie_toolbar_import_win_unittest.cc",
     "autofill_manager_unittest.cc",
     "autofill_merge_unittest.cc",
     "autofill_metrics_unittest.cc",
@@ -627,7 +633,6 @@
     "logging/log_buffer_submitter_unittest.cc",
     "logging/log_manager_unittest.cc",
     "logging/log_router_unittest.cc",
-    "pattern_provider/pattern_provider_unittest.cc",
     "payments/credit_card_access_manager_unittest.cc",
     "payments/credit_card_cvc_authenticator_unittest.cc",
     "payments/credit_card_save_manager_unittest.cc",
@@ -659,6 +664,10 @@
     "webdata/web_data_service_unittest.cc",
   ]
 
+  if (is_win) {
+    sources += [ "autofill_ie_toolbar_import_win_unittest.cc" ]
+  }
+
   if (is_ios || is_android) {
     sources += [
       "autofill_assistant_unittest.cc",
diff --git a/components/autofill/core/browser/form_parsing/autofill_parsing_utils.cc b/components/autofill/core/browser/form_parsing/autofill_parsing_utils.cc
deleted file mode 100644
index efbf7cb2..0000000
--- a/components/autofill/core/browser/form_parsing/autofill_parsing_utils.cc
+++ /dev/null
@@ -1,43 +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/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
-
-namespace autofill {
-
-MatchingPattern::MatchingPattern() = default;
-MatchingPattern::MatchingPattern(const MatchingPattern& mp) = default;
-MatchingPattern& MatchingPattern::operator=(const MatchingPattern& mp) =
-    default;
-
-MatchingPattern::~MatchingPattern() = default;
-
-autofill::MatchingPattern GetCompanyPatternEn() {
-  autofill::MatchingPattern m_p;
-  m_p.pattern_identifier = "kCompanyPatternEn";
-  m_p.positive_pattern = "company|business|organization|organisation";
-  m_p.positive_score = 1.1f;
-  m_p.negative_pattern = "";
-  m_p.match_field_attributes = autofill::MATCH_NAME;
-  m_p.match_field_input_types = autofill::MATCH_TEXT;
-  m_p.language = "en";
-
-  return m_p;
-}
-
-autofill::MatchingPattern GetCompanyPatternDe() {
-  autofill::MatchingPattern m_p;
-
-  m_p.pattern_identifier = "kCompanyPatternDe";
-  m_p.positive_pattern = "|(?<!con)firma|firmenname";
-  m_p.positive_score = 1.1f;
-  m_p.negative_pattern = "";
-  m_p.match_field_attributes = autofill::MATCH_LABEL | autofill::MATCH_NAME;
-  m_p.match_field_input_types = autofill::MATCH_TEXT;
-  m_p.language = "de";
-
-  return m_p;
-}
-
-}  // namespace autofill
\ No newline at end of file
diff --git a/components/autofill/core/browser/form_parsing/autofill_parsing_utils.h b/components/autofill/core/browser/form_parsing/autofill_parsing_utils.h
deleted file mode 100644
index 8335c21..0000000
--- a/components/autofill/core/browser/form_parsing/autofill_parsing_utils.h
+++ /dev/null
@@ -1,67 +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_AUTOFILL_CORE_BROWSER_FORM_PARSING_AUTOFILL_PARSING_UTILS_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_AUTOFILL_PARSING_UTILS_H_
-
-#include <string>
-
-namespace autofill {
-
-// A bit-field used for matching specific parts of a field in question.
-// Attributes.
-enum MatchAttributes {
-  MATCH_LABEL = 1 << 0,
-  MATCH_NAME = 1 << 1,
-  MATCH_ATTRIBUTES_DEFAULT = MATCH_LABEL | MATCH_NAME,
-};
-
-// A bit-field used for matching specific parts of a field in question.
-// Input types.
-enum MatchFieldTypes {
-  MATCH_TEXT = 1 << 2,
-  MATCH_EMAIL = 1 << 3,
-  MATCH_TELEPHONE = 1 << 4,
-  MATCH_SELECT = 1 << 5,
-  MATCH_TEXT_AREA = 1 << 6,
-  MATCH_PASSWORD = 1 << 7,
-  MATCH_NUMBER = 1 << 8,
-  MATCH_SEARCH = 1 << 9,
-  MATCH_ALL_INPUTS = MATCH_TEXT | MATCH_EMAIL | MATCH_TELEPHONE | MATCH_SELECT |
-                     MATCH_TEXT_AREA | MATCH_PASSWORD | MATCH_NUMBER |
-                     MATCH_SEARCH,
-
-  // By default match label and name for input/text types.
-  MATCH_INPUTS_DEFAULT = MATCH_TEXT,
-};
-
-constexpr int MATCH_DEFAULT = MATCH_ATTRIBUTES_DEFAULT | MATCH_INPUTS_DEFAULT;
-
-// Structure for a better organization of data and regular expressions
-// for autofill regex_constants. In the future, to implement faster
-// changes without global updates also for having a quick possibility
-// to recognize incorrect matches.
-struct MatchingPattern {
-  MatchingPattern();
-  MatchingPattern(const MatchingPattern& mp);
-  MatchingPattern& operator=(const MatchingPattern& mp);
-  ~MatchingPattern();
-
-  std::string pattern_identifier;
-  std::string positive_pattern;
-  float positive_score = 1.1f;
-  std::string negative_pattern;
-  int match_field_attributes;
-  int match_field_input_types;
-  std::string language;
-};
-
-// Use these functions instead of storing "non standats type" constants that
-// bots might complaining over.
-MatchingPattern GetCompanyPatternEn();
-MatchingPattern GetCompanyPatternDe();
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_PARSING_UTILS_H_
diff --git a/components/autofill/core/browser/form_parsing/form_field.cc b/components/autofill/core/browser/form_parsing/form_field.cc
index 2b778b9b..3ecfdf4 100644
--- a/components/autofill/core/browser/form_parsing/form_field.cc
+++ b/components/autofill/core/browser/form_parsing/form_field.cc
@@ -159,10 +159,10 @@
   return ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT, match, logging);
 }
 
+// static
 bool FormField::ParseFieldSpecifics(AutofillScanner* scanner,
                                     const base::string16& pattern,
-                                    int match_field_attributes,
-                                    int match_field_input_types,
+                                    int match_type,
                                     AutofillField** match,
                                     const RegExLogging& logging) {
   if (scanner->IsEnd())
@@ -170,25 +170,10 @@
 
   const AutofillField* field = scanner->Cursor();
 
-  if (!MatchesFormControlType(field->form_control_type,
-                              match_field_input_types))
+  if (!MatchesFormControlType(field->form_control_type, match_type))
     return false;
 
-  return MatchAndAdvance(scanner, pattern, match_field_attributes,
-                         match_field_input_types, match, logging);
-}
-
-// static
-bool FormField::ParseFieldSpecifics(AutofillScanner* scanner,
-                                    const base::string16& pattern,
-                                    int match_type,
-                                    AutofillField** match,
-                                    const RegExLogging& logging) {
-  int match_field_attributes = match_type & 0b11;
-  int match_field_types = match_type & ~0b11;
-
-  return ParseFieldSpecifics(scanner, pattern, match_field_attributes,
-                             match_field_types, match, logging);
+  return MatchAndAdvance(scanner, pattern, match_type, match, logging);
 }
 
 // static
@@ -211,15 +196,14 @@
   candidates.AddFieldCandidate(type, score);
 }
 
+// static.
 bool FormField::MatchAndAdvance(AutofillScanner* scanner,
                                 const base::string16& pattern,
-                                int match_field_attributes,
-                                int match_field_input_types,
+                                int match_type,
                                 AutofillField** match,
                                 const RegExLogging& logging) {
   AutofillField* field = scanner->Cursor();
-  if (FormField::Match(field, pattern, match_field_attributes,
-                       match_field_input_types, logging)) {
+  if (FormField::Match(field, pattern, match_type, logging)) {
     if (match)
       *match = field;
     scanner->Advance();
@@ -229,35 +213,22 @@
   return false;
 }
 
-// static.
-bool FormField::MatchAndAdvance(AutofillScanner* scanner,
-                                const base::string16& pattern,
-                                int match_type,
-                                AutofillField** match,
-                                const RegExLogging& logging) {
-  int match_field_attributes = match_type & 0b11;
-  int match_field_types = match_type & ~0b11;
-
-  return MatchAndAdvance(scanner, pattern, match_field_attributes,
-                         match_field_types, match, logging);
-}
-
+// static
 bool FormField::Match(const AutofillField* field,
                       const base::string16& pattern,
-                      int match_field_attributes,
-                      int match_field_input_types,
+                      int match_type,
                       const RegExLogging& logging) {
   bool found_match = false;
   base::StringPiece match_type_string;
   base::StringPiece16 value;
   base::string16 match;
 
-  if ((match_field_attributes & MATCH_LABEL) &&
+  if ((match_type & FormField::MATCH_LABEL) &&
       MatchesPattern(field->label, pattern, &match)) {
     found_match = true;
     match_type_string = "Match in label";
     value = field->label;
-  } else if ((match_field_attributes & MATCH_NAME) &&
+  } else if ((match_type & FormField::MATCH_NAME) &&
              MatchesPattern(field->parseable_name(), pattern, &match)) {
     found_match = true;
     match_type_string = "Match in name";
@@ -281,18 +252,6 @@
 }
 
 // static
-bool FormField::Match(const AutofillField* field,
-                      const base::string16& pattern,
-                      int match_type,
-                      const RegExLogging& logging) {
-  int match_field_attributes = match_type & 0b11;
-  int match_field_types = match_type & ~0b11;
-
-  return Match(field, pattern, match_field_attributes, match_field_types,
-               logging);
-}
-
-// static
 void FormField::ParseFormFieldsPass(ParseFunction parse,
                                     const std::vector<AutofillField*>& fields,
                                     FieldCandidatesMap* field_candidates,
diff --git a/components/autofill/core/browser/form_parsing/form_field.h b/components/autofill/core/browser/form_parsing/form_field.h
index 5e024f6..ba404c2 100644
--- a/components/autofill/core/browser/form_parsing/form_field.h
+++ b/components/autofill/core/browser/form_parsing/form_field.h
@@ -12,7 +12,6 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
 #include "components/autofill/core/browser/form_parsing/field_candidates.h"
 
 namespace autofill {
@@ -46,6 +45,29 @@
       LogManager* log_manager = nullptr);
 
  protected:
+  // A bit-field used for matching specific parts of a field in question.
+  enum MatchType {
+    // Attributes.
+    MATCH_LABEL = 1 << 0,
+    MATCH_NAME = 1 << 1,
+
+    // Input types.
+    MATCH_TEXT = 1 << 2,
+    MATCH_EMAIL = 1 << 3,
+    MATCH_TELEPHONE = 1 << 4,
+    MATCH_SELECT = 1 << 5,
+    MATCH_TEXT_AREA = 1 << 6,
+    MATCH_PASSWORD = 1 << 7,
+    MATCH_NUMBER = 1 << 8,
+    MATCH_SEARCH = 1 << 9,
+    MATCH_ALL_INPUTS = MATCH_TEXT | MATCH_EMAIL | MATCH_TELEPHONE |
+                       MATCH_SELECT | MATCH_TEXT_AREA | MATCH_PASSWORD |
+                       MATCH_NUMBER | MATCH_SEARCH,
+
+    // By default match label and name for input/text types.
+    MATCH_DEFAULT = MATCH_LABEL | MATCH_NAME | MATCH_TEXT,
+  };
+
   // Initial values assigned to FieldCandidates by their corresponding parsers.
   static const float kBaseEmailParserScore;
   static const float kBasePhoneParserScore;
@@ -77,15 +99,6 @@
                                   AutofillField** match,
                                   const RegExLogging& logging = {});
 
-  // The same as ParseFieldSpecifics but with splitted match_types into
-  // MatchAttributes and MatchFieldTypes.
-  static bool ParseFieldSpecifics(AutofillScanner* scanner,
-                                  const base::string16& pattern,
-                                  int match_field_attributes,
-                                  int match_field_input_types,
-                                  AutofillField** match,
-                                  const RegExLogging& logging = {});
-
   // Attempts to parse a field with an empty label.  Returns true
   // on success and fills |match| with a pointer to the field.
   static bool ParseEmptyLabel(AutofillScanner* scanner, AutofillField** match);
@@ -126,30 +139,13 @@
                               AutofillField** match,
                               const RegExLogging& logging = {});
 
-  // The same as MatchAndAdvance but with splitted match_types into
-  // MatchAttributes and MatchFieldTypes.
-  static bool MatchAndAdvance(AutofillScanner* scanner,
-                              const base::string16& pattern,
-                              int match_field_attributes,
-                              int match_field_input_types,
-                              AutofillField** match,
-                              const RegExLogging& logging = {});
-
-  // Matches the regular expression |pattern| against the components of
-  // |field| as specified in the |match_type| bit field (see |MatchType|).
+  // Matches the regular expression |pattern| against the components of |field|
+  // as specified in the |match_type| bit field (see |MatchType|).
   static bool Match(const AutofillField* field,
                     const base::string16& pattern,
                     int match_type,
                     const RegExLogging& logging = {});
 
-  // The same as Match but with splitted match_types into MatchAttributes
-  // and MatchFieldTypes.
-  static bool Match(const AutofillField* field,
-                    const base::string16& pattern,
-                    int match_field_attributes,
-                    int match_field_input_types,
-                    const RegExLogging& logging = {});
-
   // Perform a "pass" over the |fields| where each pass uses the supplied
   // |parse| method to match content to a given field type.
   // |fields| is both an input and an output parameter.  Upon exit |fields|
diff --git a/components/autofill/core/browser/form_parsing/form_field_unittest.cc b/components/autofill/core/browser/form_parsing/form_field_unittest.cc
index bfadf83..09a0a05 100644
--- a/components/autofill/core/browser/form_parsing/form_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/form_field_unittest.cc
@@ -23,91 +23,109 @@
   AutofillField field;
 
   // Empty strings match.
-  EXPECT_TRUE(FormField::Match(&field, base::string16(), MATCH_LABEL));
+  EXPECT_TRUE(
+      FormField::Match(&field, base::string16(), FormField::MATCH_LABEL));
 
   // Empty pattern matches non-empty string.
   field.label = ASCIIToUTF16("a");
-  EXPECT_TRUE(FormField::Match(&field, base::string16(), MATCH_LABEL));
+  EXPECT_TRUE(
+      FormField::Match(&field, base::string16(), FormField::MATCH_LABEL));
 
   // Strictly empty pattern matches empty string.
   field.label = base::string16();
-  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^$"), MATCH_LABEL));
+  EXPECT_TRUE(
+      FormField::Match(&field, ASCIIToUTF16("^$"), FormField::MATCH_LABEL));
 
   // Strictly empty pattern does not match non-empty string.
   field.label = ASCIIToUTF16("a");
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^$"), MATCH_LABEL));
+  EXPECT_FALSE(
+      FormField::Match(&field, ASCIIToUTF16("^$"), FormField::MATCH_LABEL));
 
   // Non-empty pattern doesn't match empty string.
   field.label = base::string16();
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("a"), MATCH_LABEL));
+  EXPECT_FALSE(
+      FormField::Match(&field, ASCIIToUTF16("a"), FormField::MATCH_LABEL));
 
   // Beginning of line.
   field.label = ASCIIToUTF16("head_tail");
-  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^head"), MATCH_LABEL));
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^tail"), MATCH_LABEL));
+  EXPECT_TRUE(
+      FormField::Match(&field, ASCIIToUTF16("^head"), FormField::MATCH_LABEL));
+  EXPECT_FALSE(
+      FormField::Match(&field, ASCIIToUTF16("^tail"), FormField::MATCH_LABEL));
 
   // End of line.
   field.label = ASCIIToUTF16("head_tail");
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("head$"), MATCH_LABEL));
-  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("tail$"), MATCH_LABEL));
+  EXPECT_FALSE(
+      FormField::Match(&field, ASCIIToUTF16("head$"), FormField::MATCH_LABEL));
+  EXPECT_TRUE(
+      FormField::Match(&field, ASCIIToUTF16("tail$"), FormField::MATCH_LABEL));
 
   // Exact.
   field.label = ASCIIToUTF16("head_tail");
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^head$"), MATCH_LABEL));
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^tail$"), MATCH_LABEL));
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("^head_tail$"), MATCH_LABEL));
+  EXPECT_FALSE(
+      FormField::Match(&field, ASCIIToUTF16("^head$"), FormField::MATCH_LABEL));
+  EXPECT_FALSE(
+      FormField::Match(&field, ASCIIToUTF16("^tail$"), FormField::MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^head_tail$"),
+                               FormField::MATCH_LABEL));
 
   // Escaped dots.
   field.label = ASCIIToUTF16("m.i.");
   // Note: This pattern is misleading as the "." characters are wild cards.
-  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m.i."), MATCH_LABEL));
-  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."), MATCH_LABEL));
+  EXPECT_TRUE(
+      FormField::Match(&field, ASCIIToUTF16("m.i."), FormField::MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."),
+                               FormField::MATCH_LABEL));
   field.label = ASCIIToUTF16("mXiX");
-  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m.i."), MATCH_LABEL));
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."), MATCH_LABEL));
+  EXPECT_TRUE(
+      FormField::Match(&field, ASCIIToUTF16("m.i."), FormField::MATCH_LABEL));
+  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."),
+                                FormField::MATCH_LABEL));
 
   // Repetition.
   field.label = ASCIIToUTF16("headtail");
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("head.*tail"), MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"),
+                               FormField::MATCH_LABEL));
   field.label = ASCIIToUTF16("headXtail");
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("head.*tail"), MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"),
+                               FormField::MATCH_LABEL));
   field.label = ASCIIToUTF16("headXXXtail");
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("head.*tail"), MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"),
+                               FormField::MATCH_LABEL));
   field.label = ASCIIToUTF16("headtail");
-  EXPECT_FALSE(
-      FormField::Match(&field, ASCIIToUTF16("head.+tail"), MATCH_LABEL));
+  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("head.+tail"),
+                                FormField::MATCH_LABEL));
   field.label = ASCIIToUTF16("headXtail");
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("head.+tail"), MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.+tail"),
+                               FormField::MATCH_LABEL));
   field.label = ASCIIToUTF16("headXXXtail");
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("head.+tail"), MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.+tail"),
+                               FormField::MATCH_LABEL));
 
   // Alternation.
   field.label = ASCIIToUTF16("head_tail");
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("head|other"), MATCH_LABEL));
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("tail|other"), MATCH_LABEL));
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("bad|good"), MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head|other"),
+                               FormField::MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("tail|other"),
+                               FormField::MATCH_LABEL));
+  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("bad|good"),
+                                FormField::MATCH_LABEL));
 
   // Case sensitivity.
   field.label = ASCIIToUTF16("xxxHeAd_tAiLxxx");
-  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head_tail"), MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head_tail"),
+                               FormField::MATCH_LABEL));
 
   // Word boundaries.
   field.label = ASCIIToUTF16("contains word:");
-  EXPECT_TRUE(
-      FormField::Match(&field, ASCIIToUTF16("\\bword\\b"), MATCH_LABEL));
-  EXPECT_FALSE(
-      FormField::Match(&field, ASCIIToUTF16("\\bcon\\b"), MATCH_LABEL));
+  EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("\\bword\\b"),
+                               FormField::MATCH_LABEL));
+  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("\\bcon\\b"),
+                                FormField::MATCH_LABEL));
   // Make sure the circumflex in 'crepe' is not treated as a word boundary.
   field.label = base::UTF8ToUTF16("cr\xC3\xAApe");
-  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("\\bcr\\b"), MATCH_LABEL));
+  EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("\\bcr\\b"),
+                                FormField::MATCH_LABEL));
 }
 
 // Test that we ignore checkable elements.
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider.cc b/components/autofill/core/browser/pattern_provider/pattern_provider.cc
deleted file mode 100644
index 5dc2631..0000000
--- a/components/autofill/core/browser/pattern_provider/pattern_provider.cc
+++ /dev/null
@@ -1,32 +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/autofill/core/browser/pattern_provider/pattern_provider.h"
-
-#include <algorithm>
-#include <iostream>
-#include <string>
-
-#include "components/autofill/core/browser/autofill_type.h"
-
-namespace autofill {
-PatternProvider::PatternProvider(autofill::ServerFieldType type) {
-  autofill::MatchingPattern kCompanyPatternEn = autofill::GetCompanyPatternEn();
-  autofill::MatchingPattern kCompanyPatternDe = autofill::GetCompanyPatternDe();
-
-  patterns_[AutofillType(COMPANY_NAME).ToString()]["en"] = kCompanyPatternEn;
-  patterns_[AutofillType(COMPANY_NAME).ToString()]["de"] = kCompanyPatternDe;
-}
-
-PatternProvider::~PatternProvider() {
-  patterns_.clear();
-}
-
-autofill::MatchingPattern PatternProvider::GetSingleMatchPattern(
-    autofill::ServerFieldType type,
-    const std::string& page_language) {
-  base::StringPiece type_s = autofill::FieldTypeToStringPiece(type);
-  return patterns_[type_s.as_string()][page_language];
-}
-}  //  namespace autofill
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider.h b/components/autofill/core/browser/pattern_provider/pattern_provider.h
deleted file mode 100644
index 82ef8ac..0000000
--- a/components/autofill/core/browser/pattern_provider/pattern_provider.h
+++ /dev/null
@@ -1,51 +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_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_
-
-#include <string>
-#include "base/macros.h"
-#include "base/synchronization/lock.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
-#include "components/autofill/core/common/autofill_regex_constants.h"
-#include "third_party/re2/src/re2/re2.h"
-
-namespace autofill {
-
-class PatternProvider {
- public:
-  PatternProvider();
-  PatternProvider(ServerFieldType type, const std::string& page_language);
-  explicit PatternProvider(ServerFieldType type);
-  ~PatternProvider();
-
-  // Provides us with all patterns that can match our field type and page
-  // language.
-  const std::vector<MatchingPattern>& GetMatchPatterns(
-      ServerFieldType type,
-      const std::string& page_language);
-
-  const std::vector<MatchingPattern>& GetMatchPatterns(
-      const std::string& pattern_name,
-      const std::string& page_launguage);
-
-  // Provides us with all patterns that can match our field type.
-  const std::vector<MatchingPattern>& GetAllPatternsBaseOnType(
-      ServerFieldType type);
-
-  // Function that returns pattern that match our field type and page language.
-  MatchingPattern GetSingleMatchPattern(ServerFieldType type,
-                                        const std::string& page_language);
-
- private:
-  // Func to sort the incoming map by score.
-  void SortPatternsByScore(std::vector<MatchingPattern>& patterns);
-
-  // Local map to store patterns keyed by field type and page language.
-  std::map<std::string, std::map<std::string, MatchingPattern>> patterns_;
-};
-}  // namespace autofill
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_
\ No newline at end of file
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc b/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc
deleted file mode 100644
index 12421c2..0000000
--- a/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc
+++ /dev/null
@@ -1,38 +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/autofill/core/browser/pattern_provider/pattern_provider.h"
-
-#include <stddef.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/test/gtest_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace autofill {
-
-bool operator==(const MatchingPattern& mp1, const MatchingPattern& mp2) {
-  return (mp1.language == mp2.language &&
-          mp1.match_field_attributes == mp2.match_field_attributes &&
-          mp1.match_field_input_types == mp2.match_field_input_types &&
-          mp1.negative_pattern == mp2.negative_pattern &&
-          mp1.pattern_identifier == mp2.pattern_identifier &&
-          mp1.positive_pattern == mp2.positive_pattern &&
-          mp1.positive_score == mp2.positive_score);
-}
-
-TEST(AutofillPatternProvider, Single_Match) {
-  MatchingPattern kCompanyPatternEn = GetCompanyPatternEn();
-  MatchingPattern kCompanyPatternDe = GetCompanyPatternDe();
-  PatternProvider pattern_provider(COMPANY_NAME);
-  EXPECT_EQ(pattern_provider.GetSingleMatchPattern(COMPANY_NAME, "en"),
-            kCompanyPatternEn);
-  EXPECT_EQ(pattern_provider.GetSingleMatchPattern(COMPANY_NAME, "de"),
-            kCompanyPatternDe);
-}
-
-}  // namespace autofill
\ No newline at end of file
diff --git a/components/browser_ui/contacts_picker/android/BUILD.gn b/components/browser_ui/contacts_picker/android/BUILD.gn
index 6862760..ea3ccfed 100644
--- a/components/browser_ui/contacts_picker/android/BUILD.gn
+++ b/components/browser_ui/contacts_picker/android/BUILD.gn
@@ -71,6 +71,7 @@
     "java/res/layout/contacts_picker_dialog.xml",
     "java/res/layout/contacts_picker_toolbar.xml",
     "java/res/layout/top_view.xml",
+    "java/res/values/dimens.xml",
     "java/res/values/styles.xml",
   ]
   deps = [
diff --git a/components/browser_ui/contacts_picker/android/java/res/values/dimens.xml b/components/browser_ui/contacts_picker/android/java/res/values/dimens.xml
new file mode 100644
index 0000000..40610d8
--- /dev/null
+++ b/components/browser_ui/contacts_picker/android/java/res/values/dimens.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <dimen name="contact_picker_icon_size">36dp</dimen>
+</resources>
diff --git a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactViewHolder.java b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactViewHolder.java
index c1badc2..eea6b09 100644
--- a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactViewHolder.java
+++ b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactViewHolder.java
@@ -71,7 +71,7 @@
             mItemView.initialize(contact, bitmap);
         } else {
             Bitmap icon = mCategoryView.getIconCache().getBitmap(mContact.getId());
-            if (icon == null) {
+            if (icon == null && !contact.getId().equals(ContactDetails.SELF_CONTACT_ID)) {
                 mWorkerTask = new FetchIconWorkerTask(mContact.getId(), mContentResolver, this);
                 mWorkerTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
             }
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd
index 8914372..d656b112a 100644
--- a/components/browser_ui/strings/android/browser_ui_strings.grd
+++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -300,9 +300,6 @@
       <message name="IDS_MENU_ITEM_MOVE_TO_TOP" desc="Option in item menu. User can click the 'Move to top' option to move the item up to the top of its list. [CHAR-LIMIT=24]">
         Move to top
       </message>
-      <message name="IDS_OPEN_SETTINGS" desc="Generic label for a button to show settings screen. [CHAR-LIMIT=20]">
-        Open settings
-      </message>
 
       <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_MENU" desc="Content description for the settings menu button.">
         More options
diff --git a/components/browser_ui/util/android/BUILD.gn b/components/browser_ui/util/android/BUILD.gn
index 7e5260e..362b8df 100644
--- a/components/browser_ui/util/android/BUILD.gn
+++ b/components/browser_ui/util/android/BUILD.gn
@@ -6,6 +6,7 @@
 
 android_library("java") {
   sources = [
+    "java/src/org/chromium/components/browser_ui/util/AvatarGenerator.java",
     "java/src/org/chromium/components/browser_ui/util/BitmapCache.java",
     "java/src/org/chromium/components/browser_ui/util/BrowserControlsVisibilityDelegate.java",
     "java/src/org/chromium/components/browser_ui/util/ComposedBrowserControlsVisibilityDelegate.java",
diff --git a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/AvatarGenerator.java b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/AvatarGenerator.java
new file mode 100644
index 0000000..ff00358a
--- /dev/null
+++ b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/AvatarGenerator.java
@@ -0,0 +1,46 @@
+// 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.
+
+package org.chromium.components.browser_ui.util;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+
+/** Utilities for manipulating account avatars. */
+public class AvatarGenerator {
+    /**
+     * Rescales avatar image and crops it into a circle.
+     * @param resources the Resources used to set initial target density.
+     * @param avatar the uncropped avatar.
+     * @param imageSize the target image size in pixels.
+     * @return the scaled and cropped avatar.
+     */
+    public static Drawable makeRoundAvatar(Resources resources, Bitmap avatar, int imageSize) {
+        if (avatar == null) return null;
+
+        Bitmap output = Bitmap.createBitmap(imageSize, imageSize, Config.ARGB_8888);
+        Canvas canvas = new Canvas(output);
+        // Fill the canvas with transparent color.
+        canvas.drawColor(Color.TRANSPARENT);
+        // Draw a white circle.
+        float radius = (float) imageSize / 2;
+        Paint paint = new Paint();
+        paint.setAntiAlias(true);
+        paint.setColor(Color.WHITE);
+        canvas.drawCircle(radius, radius, radius, paint);
+        // Use SRC_IN so white circle acts as a mask while drawing the avatar.
+        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+        canvas.drawBitmap(avatar, null, new Rect(0, 0, imageSize, imageSize), paint);
+        return new BitmapDrawable(resources, output);
+    }
+}
diff --git a/components/cronet/android/test/url_request_intercepting_job_factory.cc b/components/cronet/android/test/url_request_intercepting_job_factory.cc
index c832bba..9bb40a22 100644
--- a/components/cronet/android/test/url_request_intercepting_job_factory.cc
+++ b/components/cronet/android/test/url_request_intercepting_job_factory.cc
@@ -21,15 +21,13 @@
 URLRequestInterceptingJobFactory::~URLRequestInterceptingJobFactory() = default;
 
 std::unique_ptr<net::URLRequestJob> URLRequestInterceptingJobFactory::CreateJob(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate) const {
+    net::URLRequest* request) const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   std::unique_ptr<net::URLRequestJob> job =
-      base::WrapUnique<net::URLRequestJob>(
-          interceptor_->MaybeInterceptRequest(request, network_delegate));
+      interceptor_->MaybeInterceptRequest(request);
   if (job)
     return job;
-  return job_factory_->CreateJob(request, network_delegate);
+  return job_factory_->CreateJob(request);
 }
 
 bool URLRequestInterceptingJobFactory::IsSafeRedirectTarget(
diff --git a/components/cronet/android/test/url_request_intercepting_job_factory.h b/components/cronet/android/test/url_request_intercepting_job_factory.h
index 07645c0..9a46ed96 100644
--- a/components/cronet/android/test/url_request_intercepting_job_factory.h
+++ b/components/cronet/android/test/url_request_intercepting_job_factory.h
@@ -39,8 +39,7 @@
 
   // URLRequestJobFactory implementation
   std::unique_ptr<net::URLRequestJob> CreateJob(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override;
+      net::URLRequest* request) const override;
   bool IsSafeRedirectTarget(const GURL& location) const override;
 
  private:
diff --git a/components/domain_reliability/uploader_unittest.cc b/components/domain_reliability/uploader_unittest.cc
index 65c34e4b..548448bb 100644
--- a/components/domain_reliability/uploader_unittest.cc
+++ b/components/domain_reliability/uploader_unittest.cc
@@ -39,16 +39,14 @@
 
 class UploadMockURLRequestJob : public net::URLRequestJob {
  public:
-  UploadMockURLRequestJob(net::URLRequest* request,
-                          net::NetworkDelegate* network_delegate,
-                          MockUploadResult result)
-      : net::URLRequestJob(request, network_delegate),
-        upload_stream_(nullptr),
-        result_(result) {
+  UploadMockURLRequestJob(net::URLRequest* request, MockUploadResult result)
+      : net::URLRequestJob(request), upload_stream_(nullptr), result_(result) {
     EXPECT_FALSE(request->allow_credentials());
     EXPECT_TRUE(request->load_flags() & net::LOAD_DO_NOT_SAVE_COOKIES);
   }
 
+  ~UploadMockURLRequestJob() override = default;
+
  protected:
   void Start() override {
     int rv = upload_stream_->Init(
@@ -65,8 +63,6 @@
   }
 
  private:
-  ~UploadMockURLRequestJob() override {}
-
   void OnStreamInitialized(int rv) {
     EXPECT_EQ(net::OK, rv);
 
@@ -109,9 +105,8 @@
 
   ~UploadInterceptor() override { EXPECT_TRUE(results_.empty()); }
 
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* delegate) const override {
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
     EXPECT_FALSE(results_.empty());
     MockUploadResult result = results_.front();
     results_.pop_front();
@@ -121,7 +116,7 @@
 
     ++request_count_;
 
-    return new UploadMockURLRequestJob(request, delegate, result);
+    return std::make_unique<UploadMockURLRequestJob>(request, result);
   }
 
   void ExpectRequestAndReturnError(int net_error) {
diff --git a/components/omnibox/browser/shortcuts_provider.cc b/components/omnibox/browser/shortcuts_provider.cc
index 3574e5a..9f5b5ff 100644
--- a/components/omnibox/browser/shortcuts_provider.cc
+++ b/components/omnibox/browser/shortcuts_provider.cc
@@ -123,20 +123,10 @@
   TRACE_EVENT0("omnibox", "ShortcutsProvider::Start");
   matches_.clear();
 
-  if (input.focus_type() != OmniboxFocusType::DEFAULT ||
-      (input.type() == metrics::OmniboxInputType::EMPTY) ||
-      input.text().empty() || !initialized_)
-    return;
-
-  base::TimeTicks start_time = base::TimeTicks::Now();
-  GetMatches(input);
-  if (input.text().length() < 6) {
-    base::TimeTicks end_time = base::TimeTicks::Now();
-    std::string name = "ShortcutsProvider.QueryIndexTime." +
-                       base::NumberToString(input.text().size());
-    base::HistogramBase* counter = base::Histogram::FactoryGet(
-        name, 1, 1000, 50, base::Histogram::kUmaTargetedHistogramFlag);
-    counter->Add(static_cast<int>((end_time - start_time).InMilliseconds()));
+  if (input.focus_type() == OmniboxFocusType::DEFAULT &&
+      input.type() != metrics::OmniboxInputType::EMPTY &&
+      !input.text().empty() && initialized_) {
+    GetMatches(input);
   }
 }
 
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
index 2b05abc8..3865d53 100644
--- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
+++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
@@ -6,9 +6,6 @@
 
 import android.annotation.SuppressLint;
 import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.provider.Settings;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
@@ -176,22 +173,18 @@
 
     /**
      * Displays the dialog explaining that Chrome has detected an overlay. Offers the user to close
-     * overlay window or revoke "Draw on top" permission in Android settings.
+     * the overlay window and try again.
      */
     private void showFilteredTouchEventDialog(Context context) {
-        // Settings.ACTION_MANAGE_OVERLAY_PERMISSION is only supported on M+ therefore we shouldn't
-        // display this dialog on L. The function won't be called on L anyway because touch
-        // filtering was introduced in M.
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
-
         // Don't show another dialog if one is already displayed.
         if (mOverlayDetectedDialogModel != null) return;
 
         ModalDialogProperties.Controller overlayDetectedDialogController =
                 new SimpleModalDialogController(mModalDialogManager, (Integer dismissalCause) -> {
-                    if (dismissalCause == DialogDismissalCause.POSITIVE_BUTTON_CLICKED) {
-                        context.startActivity(
-                                new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));
+                    if (dismissalCause == DialogDismissalCause.POSITIVE_BUTTON_CLICKED
+                            && mDialogModel != null) {
+                        mModalDialogManager.dismissDialog(
+                                mDialogModel, DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE);
                     }
                     mOverlayDetectedDialogModel = null;
                 });
@@ -204,7 +197,7 @@
                         .with(ModalDialogProperties.MESSAGE, context.getResources(),
                                 R.string.overlay_detected_dialog_message)
                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, context.getResources(),
-                                R.string.open_settings)
+                                R.string.cancel)
                         .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, context.getResources(),
                                 R.string.try_again)
                         .with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
diff --git a/components/permissions/android/permissions_android_strings.grd b/components/permissions/android/permissions_android_strings.grd
index 60b560d..b5d2cf4 100644
--- a/components/permissions/android/permissions_android_strings.grd
+++ b/components/permissions/android/permissions_android_strings.grd
@@ -198,10 +198,10 @@
 
       <!-- Overlay detected dialog -->
       <message name="IDS_OVERLAY_DETECTED_DIALOG_TITLE" desc="Title of the dialog that informs the user about detected overlay window that prevents interaction with permissions.">
-        Another app is displaying over <ph name="APP_NAME">%1$s<ex>Chrome</ex></ph>
+        This site can’t ask for your permission
       </message>
       <message name="IDS_OVERLAY_DETECTED_DIALOG_MESSAGE" desc="Dialog message that informs the user about detected overlay window that prevents interaction with permissions.">
-        To change the permission for this site, close the other app and try again.\n\nIf you can’t close the app, turn off the app’s permission to “Display over other apps” in Android settings.
+        Close any bubbles or overlays from other apps. Then, try again.
       </message>
     </messages>
   </release>
diff --git a/components/permissions/android/permissions_android_strings_grd/IDS_OVERLAY_DETECTED_DIALOG_MESSAGE.png.sha1 b/components/permissions/android/permissions_android_strings_grd/IDS_OVERLAY_DETECTED_DIALOG_MESSAGE.png.sha1
index e94e244..3136fef 100644
--- a/components/permissions/android/permissions_android_strings_grd/IDS_OVERLAY_DETECTED_DIALOG_MESSAGE.png.sha1
+++ b/components/permissions/android/permissions_android_strings_grd/IDS_OVERLAY_DETECTED_DIALOG_MESSAGE.png.sha1
@@ -1 +1 @@
-59eeb17905a49e3917ea3de287f529e8de3df872
\ No newline at end of file
+9d91339a62c7718bb8da43b6702173172723980d
\ No newline at end of file
diff --git a/components/permissions/android/permissions_android_strings_grd/IDS_OVERLAY_DETECTED_DIALOG_TITLE.png.sha1 b/components/permissions/android/permissions_android_strings_grd/IDS_OVERLAY_DETECTED_DIALOG_TITLE.png.sha1
index e94e244..3136fef 100644
--- a/components/permissions/android/permissions_android_strings_grd/IDS_OVERLAY_DETECTED_DIALOG_TITLE.png.sha1
+++ b/components/permissions/android/permissions_android_strings_grd/IDS_OVERLAY_DETECTED_DIALOG_TITLE.png.sha1
@@ -1 +1 @@
-59eeb17905a49e3917ea3de287f529e8de3df872
\ No newline at end of file
+9d91339a62c7718bb8da43b6702173172723980d
\ No newline at end of file
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc
index 2eb454a..33491d64 100644
--- a/components/signin/core/browser/about_signin_internals.cc
+++ b/components/signin/core/browser/about_signin_internals.cc
@@ -674,7 +674,6 @@
   // Time and status information of the possible sign in types.
   base::ListValue* detailed_info =
       AddSection(signin_info.get(), "Last Signin Details");
-  signin_status->Set("signin_info", std::move(signin_info));
   for (signin_internals_util::TimedSigninStatusField i =
            signin_internals_util::TIMED_FIELDS_BEGIN;
        i < signin_internals_util::TIMED_FIELDS_END; ++i) {
@@ -709,8 +708,8 @@
     AddSectionEntry(detailed_info, "Token Service Next Retry",
                     base::TimeToISO8601(next_retry_time), "");
   }
-
 #endif  // !defined(OS_CHROMEOS)
+  signin_status->Set("signin_info", std::move(signin_info));
 
   // Token information for all services.
   auto token_info = std::make_unique<base::ListValue>();
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc
index a8d32b5..6f6bf90 100644
--- a/components/signin/core/browser/account_reconcilor.cc
+++ b/components/signin/core/browser/account_reconcilor.cc
@@ -34,6 +34,7 @@
 #include "google_apis/gaia/google_service_auth_error.h"
 
 using signin::AccountReconcilorDelegate;
+using signin::ConsentLevel;
 using signin_metrics::AccountReconcilorState;
 
 namespace {
@@ -125,8 +126,8 @@
   return token_revoked;
 }
 
-// TODO(msalama): Move this code and |RevokeAllSecondaryTokens|
-// to |DiceAccountReconcilorDelegate|.
+// TODO(https://crbug.com/1122551): Move this code and
+// |RevokeAllSecondaryTokens| to |DiceAccountReconcilorDelegate|.
 signin::RevokeTokenAction RevokeTokensNotInCookies(
     signin::IdentityManager* identity_manager,
     const CoreAccountId& primary_account,
@@ -496,7 +497,8 @@
                                  base::Unretained(this)));
   }
 
-  const CoreAccountId& account_id = identity_manager_->GetPrimaryAccountId();
+  CoreAccountId account_id = identity_manager_->GetPrimaryAccountId(
+      delegate_->GetConsentLevelForPrimaryAccount());
   if (identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
           account_id) &&
       delegate_->ShouldAbortReconcileIfPrimaryHasError()) {
@@ -625,8 +627,14 @@
       << "Ignore " << accounts.size() - verified_gaia_accounts.size()
       << " unverified account(s).";
 
-  CoreAccountId primary_account = identity_manager_->GetPrimaryAccountId();
+  ConsentLevel consent_level = delegate_->GetConsentLevelForPrimaryAccount();
+  CoreAccountId primary_account =
+      identity_manager_->GetPrimaryAccountId(consent_level);
   if (delegate_->ShouldRevokeTokensNotInCookies()) {
+    // This code is only used with DiceAccountReconcilorDelegate and should
+    // thus use sync account.
+    // TODO(https://crbug.com/1122551): Move to |DiceAccountReconcilorDelegate|.
+    DCHECK_EQ(consent_level, ConsentLevel::kSync);
     signin::RevokeTokenAction revoke_token_action = RevokeTokensNotInCookies(
         identity_manager_, primary_account, verified_gaia_accounts);
     delegate_->OnRevokeTokensNotInCookiesCompleted(revoke_token_action);
@@ -636,6 +644,7 @@
   // completely remove them from Chrome.
   // Revoking the token for the primary account is not supported (it should be
   // signed out or put to auth error state instead).
+  // TODO(https://crbug.com/1122551): Move to |DiceAccountReconcilorDelegate|.
   AccountReconcilorDelegate::RevokeTokenOption revoke_option =
       delegate_->ShouldRevokeSecondaryTokensBeforeReconcile(
           verified_gaia_accounts);
@@ -669,8 +678,13 @@
   if (!delegate_->ShouldRevokeTokensOnCookieDeleted())
     return;
 
-  const CoreAccountId& primary_account =
-      identity_manager_->GetPrimaryAccountId();
+  // This code is only used with DiceAccountReconcilorDelegate and should thus
+  // use sync account.
+  // TODO(https://crbug.com/1122551): Move to |DiceAccountReconcilorDelegate|.
+  DCHECK_EQ(delegate_->GetConsentLevelForPrimaryAccount(), ConsentLevel::kSync);
+
+  CoreAccountId primary_account =
+      identity_manager_->GetPrimaryAccountId(ConsentLevel::kSync);
   // Revoke secondary tokens.
   RevokeAllSecondaryTokens(
       identity_manager_, AccountReconcilorDelegate::RevokeTokenOption::kRevoke,
diff --git a/components/signin/core/browser/account_reconcilor_delegate.cc b/components/signin/core/browser/account_reconcilor_delegate.cc
index 1c672b5..2136f33 100644
--- a/components/signin/core/browser/account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/account_reconcilor_delegate.cc
@@ -31,6 +31,11 @@
   return false;
 }
 
+ConsentLevel AccountReconcilorDelegate::GetConsentLevelForPrimaryAccount()
+    const {
+  return ConsentLevel::kSync;
+}
+
 CoreAccountId AccountReconcilorDelegate::GetFirstGaiaAccountForReconcile(
     const std::vector<CoreAccountId>& chrome_accounts,
     const std::vector<gaia::ListedAccount>& gaia_accounts,
diff --git a/components/signin/core/browser/account_reconcilor_delegate.h b/components/signin/core/browser/account_reconcilor_delegate.h
index d666b2e..7477dec 100644
--- a/components/signin/core/browser/account_reconcilor_delegate.h
+++ b/components/signin/core/browser/account_reconcilor_delegate.h
@@ -10,6 +10,7 @@
 
 #include "base/time/time.h"
 #include "components/signin/public/base/multilogin_parameters.h"
+#include "components/signin/public/identity_manager/consent_level.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/google_service_auth_error.h"
@@ -64,6 +65,10 @@
   // error state. Defaults to false.
   virtual bool ShouldAbortReconcileIfPrimaryHasError() const;
 
+  // Returns the consent level that should be used for obtaining the primary
+  // account. Defaults to ConsentLevel::kSync.
+  virtual ConsentLevel GetConsentLevelForPrimaryAccount() const;
+
   // Returns the first account to add in the Gaia cookie.
   // If this returns an empty string, the user must be logged out of all
   // accounts.
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index ad3a178..5474b4b 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -562,6 +562,10 @@
     EXPECT_EQ(identity_manager->GetAccountsWithRefreshTokens().size(),
               tokens.size());
 
+    signin::ConsentLevel consent_level =
+        GetMockReconcilor()->delegate_->GetConsentLevelForPrimaryAccount();
+    CoreAccountId primary_account_id =
+        identity_manager->GetPrimaryAccountId(consent_level);
     bool authenticated_account_found = false;
     for (const Token& token : tokens) {
       CoreAccountId account_id =
@@ -572,12 +576,12 @@
           identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
               account_id));
       if (token.is_authenticated) {
-        EXPECT_EQ(account_id, identity_manager->GetPrimaryAccountId());
+        EXPECT_EQ(account_id, primary_account_id);
         authenticated_account_found = true;
       }
     }
     if (!authenticated_account_found)
-      EXPECT_EQ(CoreAccountId(), identity_manager->GetPrimaryAccountId());
+      EXPECT_EQ(CoreAccountId(), primary_account_id);
   }
 
   void SetupTokens(const char* tokens_string) {
@@ -632,14 +636,14 @@
     std::vector<Cookie> cookies = ParseCookieString(cookies_);
     ConfigureCookieManagerService(cookies);
 
-    // Setup tokens. This triggers listing cookies so we need to setup cookies
-    // before that.
-    SetupTokens(tokens_);
-
     // Call list accounts now so that the next call completes synchronously.
     identity_test_env()->identity_manager()->GetAccountsInCookieJar();
     base::RunLoop().RunUntilIdle();
 
+    // Setup tokens. This triggers listing cookies so we need to setup cookies
+    // before that.
+    SetupTokens(tokens_);
+
     // Setup expectations.
     testing::InSequence mock_sequence;
     bool logout_action = false;
@@ -1121,14 +1125,14 @@
   std::vector<Cookie> cookies = ParseCookieString(cookies_after_reconcile_);
   ConfigureCookieManagerService(cookies);
 
-  // Setup tokens. This triggers listing cookies so we need to setup cookies
-  // before that.
-  SetupTokens(tokens_after_reconcile_);
-
   // Call list accounts now so that the next call completes synchronously.
   identity_test_env()->identity_manager()->GetAccountsInCookieJar();
   base::RunLoop().RunUntilIdle();
 
+  // Setup tokens. This triggers listing cookies so we need to setup cookies
+  // before that.
+  SetupTokens(tokens_after_reconcile_);
+
   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0);
   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0);
   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_))
@@ -1171,14 +1175,14 @@
   ConfigureCookieManagerService(cookies);
   std::vector<Cookie> cookies_after_reconcile = cookies;
 
-  // Setup tokens. This triggers listing cookies so we need to setup cookies
-  // before that.
-  SetupTokens(GetParam().tokens);
-
   // Call list accounts now so that the next call completes synchronously.
   identity_test_env()->identity_manager()->GetAccountsInCookieJar();
   base::RunLoop().RunUntilIdle();
 
+  // Setup tokens. This triggers listing cookies so we need to setup cookies
+  // before that.
+  SetupTokens(GetParam().tokens);
+
   // Setup expectations.
   testing::InSequence mock_sequence;
   if (GetParam().gaia_api_calls_multilogin[0] != '\0') {
@@ -1669,9 +1673,6 @@
   // Enable Mirror.
   SetAccountConsistency(signin::AccountConsistencyMethod::kMirror);
 
-  // Setup tokens.
-  SetupTokens(GetParam().tokens);
-
   // Setup cookies.
   std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies);
   ConfigureCookieManagerService(cookies);
@@ -1680,6 +1681,9 @@
   identity_test_env()->identity_manager()->GetAccountsInCookieJar();
   base::RunLoop().RunUntilIdle();
 
+  // Setup tokens.
+  SetupTokens(GetParam().tokens);
+
   // Setup expectations.
   testing::InSequence mock_sequence;
   bool logout_action = false;
@@ -1790,10 +1794,6 @@
 // clang-format on
 
 TEST_P(AccountReconcilorTestActiveDirectory, TableRowTestMultilogin) {
-  // Setup tokens.
-  std::vector<Token> tokens = ParseTokenString(GetParam().tokens);
-  SetupTokens(GetParam().tokens);
-
   // Setup cookies.
   std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies);
   ConfigureCookieManagerService(cookies);
@@ -1802,6 +1802,10 @@
   identity_test_env()->identity_manager()->GetAccountsInCookieJar();
   base::RunLoop().RunUntilIdle();
 
+  // Setup tokens.
+  std::vector<Token> tokens = ParseTokenString(GetParam().tokens);
+  SetupTokens(GetParam().tokens);
+
   testing::InSequence mock_sequence;
   MockAccountReconcilor* reconcilor = GetMockReconcilor(
       std::make_unique<signin::ActiveDirectoryAccountReconcilorDelegate>());
diff --git a/components/signin/core/browser/mirror_account_reconcilor_delegate.cc b/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
index db56be5..d855b2d 100644
--- a/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
@@ -5,7 +5,9 @@
 #include "components/signin/core/browser/mirror_account_reconcilor_delegate.h"
 
 #include "base/logging.h"
+#include "build/build_config.h"
 #include "components/signin/core/browser/account_reconcilor.h"
+#include "components/signin/public/base/account_consistency_method.h"
 
 namespace signin {
 
@@ -14,6 +16,8 @@
     : identity_manager_(identity_manager) {
   DCHECK(identity_manager_);
   identity_manager_->AddObserver(this);
+  reconcile_enabled_ =
+      identity_manager_->HasPrimaryAccount(GetConsentLevelForPrimaryAccount());
 }
 
 MirrorAccountReconcilorDelegate::~MirrorAccountReconcilorDelegate() {
@@ -21,7 +25,7 @@
 }
 
 bool MirrorAccountReconcilorDelegate::IsReconcileEnabled() const {
-  return identity_manager_->HasPrimaryAccount();
+  return reconcile_enabled_;
 }
 
 bool MirrorAccountReconcilorDelegate::IsAccountConsistencyEnforced() const {
@@ -37,6 +41,16 @@
   return true;
 }
 
+ConsentLevel MirrorAccountReconcilorDelegate::GetConsentLevelForPrimaryAccount()
+    const {
+#if defined(OS_ANDROID)
+  if (base::FeatureList::IsEnabled(kMobileIdentityConsistency)) {
+    return ConsentLevel::kNotRequired;
+  }
+#endif
+  return ConsentLevel::kSync;
+}
+
 CoreAccountId MirrorAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile(
     const std::vector<CoreAccountId>& chrome_accounts,
     const std::vector<gaia::ListedAccount>& gaia_accounts,
@@ -61,14 +75,38 @@
                                            gaia_accounts);
 }
 
+// TODO(https://crbug.com/1046746): Replace separate IdentityManager::Observer
+// method overrides below with a single OnPrimaryAccountChanged method and
+// inline UpdateReconcilorStatus.
 void MirrorAccountReconcilorDelegate::OnPrimaryAccountSet(
     const CoreAccountInfo& primary_account_info) {
-  reconcilor()->EnableReconcile();
+  UpdateReconcilorStatus();
 }
 
 void MirrorAccountReconcilorDelegate::OnPrimaryAccountCleared(
     const CoreAccountInfo& previous_primary_account_info) {
-  reconcilor()->DisableReconcile(true /* logout_all_gaia_accounts */);
+  UpdateReconcilorStatus();
+}
+
+void MirrorAccountReconcilorDelegate::OnUnconsentedPrimaryAccountChanged(
+    const CoreAccountInfo& unconsented_primary_account_info) {
+  UpdateReconcilorStatus();
+}
+
+void MirrorAccountReconcilorDelegate::UpdateReconcilorStatus() {
+  // Have to check whether the state has actually changed, as calling
+  // DisableReconcile logs out all accounts even if it was already disabled.
+  bool should_enable_reconcile =
+      identity_manager_->HasPrimaryAccount(GetConsentLevelForPrimaryAccount());
+  if (reconcile_enabled_ == should_enable_reconcile)
+    return;
+
+  reconcile_enabled_ = should_enable_reconcile;
+  if (should_enable_reconcile) {
+    reconcilor()->EnableReconcile();
+  } else {
+    reconcilor()->DisableReconcile(true /* logout_all_gaia_accounts */);
+  }
 }
 
 }  // namespace signin
diff --git a/components/signin/core/browser/mirror_account_reconcilor_delegate.h b/components/signin/core/browser/mirror_account_reconcilor_delegate.h
index 6916639..aa54f81 100644
--- a/components/signin/core/browser/mirror_account_reconcilor_delegate.h
+++ b/components/signin/core/browser/mirror_account_reconcilor_delegate.h
@@ -34,6 +34,7 @@
   bool IsAccountConsistencyEnforced() const override;
   gaia::GaiaSource GetGaiaApiSource() const override;
   bool ShouldAbortReconcileIfPrimaryHasError() const override;
+  ConsentLevel GetConsentLevelForPrimaryAccount() const override;
   CoreAccountId GetFirstGaiaAccountForReconcile(
       const std::vector<CoreAccountId>& chrome_accounts,
       const std::vector<gaia::ListedAccount>& gaia_accounts,
@@ -51,8 +52,13 @@
       const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const CoreAccountInfo& previous_primary_account_info) override;
+  void OnUnconsentedPrimaryAccountChanged(
+      const CoreAccountInfo& unconsented_primary_account_info) override;
+
+  void UpdateReconcilorStatus();
 
   IdentityManager* identity_manager_;
+  bool reconcile_enabled_;
 
   DISALLOW_COPY_AND_ASSIGN(MirrorAccountReconcilorDelegate);
 };
diff --git a/components/signin/internal/identity_manager/primary_account_manager.cc b/components/signin/internal/identity_manager/primary_account_manager.cc
index 88de9c42..b6dbf04d 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager.cc
@@ -12,6 +12,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/internal/identity_manager/account_tracker_service.h"
@@ -310,12 +311,20 @@
   VLOG(1) << "StartSignOut: " << static_cast<int>(signout_source_metric) << ", "
           << static_cast<int>(signout_delete_metric) << ", "
           << static_cast<int>(remove_option);
-  client_->PreSignOut(
-      base::BindOnce(&PrimaryAccountManager::OnSignoutDecisionReached,
-                     base::Unretained(this), signout_source_metric,
-                     signout_delete_metric, remove_option,
-                     assert_signout_allowed),
-      signout_source_metric);
+  if (IsAuthenticated()) {
+    client_->PreSignOut(
+        base::BindOnce(&PrimaryAccountManager::OnSignoutDecisionReached,
+                       base::Unretained(this), signout_source_metric,
+                       signout_delete_metric, remove_option,
+                       assert_signout_allowed),
+        signout_source_metric);
+  } else {
+    // Sign-out is always allowed if there's only unconsented primary account
+    // without sync consent, so skip calling PreSignOut.
+    OnSignoutDecisionReached(signout_source_metric, signout_delete_metric,
+                             remove_option, assert_signout_allowed,
+                             SigninClient::SignoutDecision::ALLOW_SIGNOUT);
+  }
 }
 
 void PrimaryAccountManager::OnSignoutDecisionReached(
@@ -331,7 +340,7 @@
   VLOG(1) << "OnSignoutDecisionReached: "
           << (signout_decision == SigninClient::SignoutDecision::ALLOW_SIGNOUT);
   signin_metrics::LogSignout(signout_source_metric, signout_delete_metric);
-  if (!IsAuthenticated()) {
+  if (primary_account_info().IsEmpty()) {
     return;
   }
 
diff --git a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
index d5a946b..f8c37b3d 100644
--- a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "build/build_config.h"
 #include "components/image_fetcher/core/fake_image_decoder.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -184,6 +185,8 @@
   EXPECT_TRUE(token_service_.GetAccounts().empty());
 }
 
+// kRemoveAuthenticatedAccountIfInError isn't supported on Android.
+#if !defined(OS_ANDROID)
 TEST_F(PrimaryAccountManagerTest, SignOutDiceNoRevoke) {
   account_consistency_ = signin::AccountConsistencyMethod::kDice;
   CreatePrimaryAccountManager();
@@ -239,6 +242,7 @@
   std::vector<CoreAccountId> expected_tokens = {other_account_id};
   EXPECT_EQ(expected_tokens, token_service_.GetAccounts());
 }
+#endif
 
 TEST_F(PrimaryAccountManagerTest, SignOutWhileProhibited) {
   CreatePrimaryAccountManager();
@@ -258,6 +262,23 @@
   EXPECT_FALSE(manager_->IsAuthenticated());
 }
 
+TEST_F(PrimaryAccountManagerTest, UnconsentedSignOutWhileProhibited) {
+  CreatePrimaryAccountManager();
+  EXPECT_FALSE(manager_->IsAuthenticated());
+  EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().email.empty());
+  EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty());
+
+  CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
+  CoreAccountInfo account_info = account_tracker()->GetAccountInfo(account_id);
+  manager_->SetUnconsentedPrimaryAccountInfo(account_info);
+  EXPECT_TRUE(manager_->HasUnconsentedPrimaryAccount());
+  EXPECT_FALSE(manager_->IsAuthenticated());
+  signin_client()->set_is_signout_allowed(false);
+  manager_->SignOut(signin_metrics::SIGNOUT_TEST,
+                    signin_metrics::SignoutDelete::IGNORE_METRIC);
+  EXPECT_FALSE(manager_->HasUnconsentedPrimaryAccount());
+}
+
 TEST_F(PrimaryAccountManagerTest, ProhibitedAtStartup) {
   CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
   user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id.ToString());
diff --git a/components/signin/public/identity_manager/primary_account_mutator_unittest.cc b/components/signin/public/identity_manager/primary_account_mutator_unittest.cc
index 780d722b..1fd559b 100644
--- a/components/signin/public/identity_manager/primary_account_mutator_unittest.cc
+++ b/components/signin/public/identity_manager/primary_account_mutator_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/run_loop.h"
 #include "base/scoped_observer.h"
 #include "base/test/task_environment.h"
+#include "build/build_config.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/consent_level.h"
@@ -480,6 +481,9 @@
       RemoveAccountExpectation::kRemoveAll);
 }
 
+// kRemoveAuthenticatedAccountIfInError isn't supported on Android.
+#if !defined(OS_ANDROID)
+
 // Test that ClearPrimaryAccount(...) with ClearAccountTokensAction::kDefault
 // and AccountConsistencyMethod::kDice keeps all accounts when the the primary
 // account does not have an authentication error (see *_AuthError test).
@@ -500,6 +504,7 @@
       signin::PrimaryAccountMutator::ClearAccountsAction::kDefault,
       RemoveAccountExpectation::kRemovePrimary, AuthExpectation::kAuthError);
 }
+#endif  // !defined(OS_ANDROID)
 #endif  // !defined(OS_CHROMEOS)
 
 #if defined(OS_CHROMEOS)
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index df884907..b4109fe 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -1466,7 +1466,9 @@
   if (!sessions_invalidations_enabled_) {
     types.Remove(SESSIONS);
   }
-  invalidations_service->SetInterestedDataTypes(types);
+  invalidations_service->SetInterestedDataTypes(
+      types, base::BindRepeating(&ProfileSyncService::TriggerRefresh,
+                                 sync_enabled_weak_factory_.GetWeakPtr()));
 }
 
 SyncCycleSnapshot ProfileSyncService::GetLastCycleSnapshotForDebugging() const {
diff --git a/components/sync/driver/profile_sync_service_unittest.cc b/components/sync/driver/profile_sync_service_unittest.cc
index 8eeb6bd..871bf8b 100644
--- a/components/sync/driver/profile_sync_service_unittest.cc
+++ b/components/sync/driver/profile_sync_service_unittest.cc
@@ -1625,7 +1625,7 @@
        ShouldSendDataTypesToSyncInvalidationsService) {
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
-  EXPECT_CALL(*sync_invalidations_service(), SetInterestedDataTypes(_));
+  EXPECT_CALL(*sync_invalidations_service(), SetInterestedDataTypes(_, _));
   InitializeForFirstSync();
 }
 
@@ -1641,10 +1641,10 @@
   InitializeForNthSync();
 
   EXPECT_CALL(*sync_invalidations_service(),
-              SetInterestedDataTypes(ContainsSessions()));
+              SetInterestedDataTypes(ContainsSessions(), _));
   service()->SetInvalidationsForSessionsEnabled(true);
   EXPECT_CALL(*sync_invalidations_service(),
-              SetInterestedDataTypes(Not(ContainsSessions())));
+              SetInterestedDataTypes(Not(ContainsSessions()), _));
   service()->SetInvalidationsForSessionsEnabled(false);
 }
 
diff --git a/components/sync/invalidations/BUILD.gn b/components/sync/invalidations/BUILD.gn
index 3cbc902..713dbd2 100644
--- a/components/sync/invalidations/BUILD.gn
+++ b/components/sync/invalidations/BUILD.gn
@@ -9,9 +9,9 @@
     "fcm_handler.cc",
     "fcm_handler.h",
     "fcm_registration_token_observer.h",
+    "interested_data_types_handler.h",
     "interested_data_types_manager.cc",
     "interested_data_types_manager.h",
-    "interested_data_types_observer.h",
     "invalidations_listener.h",
     "switches.cc",
     "switches.h",
diff --git a/components/sync/invalidations/interested_data_types_handler.h b/components/sync/invalidations/interested_data_types_handler.h
new file mode 100644
index 0000000..478b655f
--- /dev/null
+++ b/components/sync/invalidations/interested_data_types_handler.h
@@ -0,0 +1,23 @@
+// 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_SYNC_INVALIDATIONS_INTERESTED_DATA_TYPES_HANDLER_H_
+#define COMPONENTS_SYNC_INVALIDATIONS_INTERESTED_DATA_TYPES_HANDLER_H_
+
+#include "base/callback.h"
+
+namespace syncer {
+
+// An interface to handle changes on data types for which the device wants to
+// receive invalidations. Implementations are expected to call the provided
+// callback when the list of data types is sent to the Sync server.
+class InterestedDataTypesHandler {
+ public:
+  // Called on each change of interested data types.
+  virtual void OnInterestedDataTypesChanged(base::OnceClosure callback) = 0;
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_INVALIDATIONS_INTERESTED_DATA_TYPES_HANDLER_H_
diff --git a/components/sync/invalidations/interested_data_types_manager.cc b/components/sync/invalidations/interested_data_types_manager.cc
index 9346bd38..d83d8152 100644
--- a/components/sync/invalidations/interested_data_types_manager.cc
+++ b/components/sync/invalidations/interested_data_types_manager.cc
@@ -4,22 +4,24 @@
 
 #include "components/sync/invalidations/interested_data_types_manager.h"
 
-#include "components/sync/invalidations/interested_data_types_observer.h"
+#include <utility>
+
+#include "base/bind.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/invalidations/interested_data_types_handler.h"
 
 namespace syncer {
 
 InterestedDataTypesManager::InterestedDataTypesManager() = default;
 
-InterestedDataTypesManager::~InterestedDataTypesManager() = default;
-
-void InterestedDataTypesManager::AddInterestedDataTypesObserver(
-    InterestedDataTypesObserver* observer) {
-  observers_.AddObserver(observer);
+InterestedDataTypesManager::~InterestedDataTypesManager() {
+  DCHECK(!interested_data_types_handler_);
 }
 
-void InterestedDataTypesManager::RemoveInterestedDataTypesObserver(
-    InterestedDataTypesObserver* observer) {
-  observers_.RemoveObserver(observer);
+void InterestedDataTypesManager::SetInterestedDataTypesHandler(
+    InterestedDataTypesHandler* handler) {
+  DCHECK(!interested_data_types_handler_ || !handler);
+  interested_data_types_handler_ = handler;
 }
 
 const ModelTypeSet& InterestedDataTypesManager::GetInterestedDataTypes() const {
@@ -27,10 +29,13 @@
 }
 
 void InterestedDataTypesManager::SetInterestedDataTypes(
-    const ModelTypeSet& data_types) {
+    const ModelTypeSet& data_types,
+    SyncInvalidationsService::InterestedDataTypesAppliedCallback callback) {
+  ModelTypeSet new_data_types = Difference(data_types, data_types_);
   data_types_ = data_types;
-  for (InterestedDataTypesObserver& observer : observers_) {
-    observer.OnInterestedDataTypesChanged();
+  if (interested_data_types_handler_) {
+    interested_data_types_handler_->OnInterestedDataTypesChanged(
+        base::BindOnce(std::move(callback), new_data_types));
   }
 }
 
diff --git a/components/sync/invalidations/interested_data_types_manager.h b/components/sync/invalidations/interested_data_types_manager.h
index 35060bff..a0796f1 100644
--- a/components/sync/invalidations/interested_data_types_manager.h
+++ b/components/sync/invalidations/interested_data_types_manager.h
@@ -5,11 +5,11 @@
 #ifndef COMPONENTS_SYNC_INVALIDATIONS_INTERESTED_DATA_TYPES_MANAGER_H_
 #define COMPONENTS_SYNC_INVALIDATIONS_INTERESTED_DATA_TYPES_MANAGER_H_
 
-#include "base/observer_list.h"
 #include "components/sync/base/model_type.h"
+#include "components/sync/invalidations/sync_invalidations_service.h"
 
 namespace syncer {
-class InterestedDataTypesObserver;
+class InterestedDataTypesHandler;
 
 // Manages for which data types are invalidations sent to this device.
 class InterestedDataTypesManager {
@@ -20,20 +20,18 @@
   InterestedDataTypesManager& operator=(const InterestedDataTypesManager&) =
       delete;
 
-  // Add or remove a interested data types change observer. |observer| must not
-  // be nullptr.
-  void AddInterestedDataTypesObserver(InterestedDataTypesObserver* observer);
-  void RemoveInterestedDataTypesObserver(InterestedDataTypesObserver* observer);
+  // Set the interested data types change handler. |handler| can be nullptr to
+  // unregister any existing handler. There can be at most one handler.
+  void SetInterestedDataTypesHandler(InterestedDataTypesHandler* handler);
 
   // Get or set the interested data types.
   const ModelTypeSet& GetInterestedDataTypes() const;
-  void SetInterestedDataTypes(const ModelTypeSet& data_types);
+  void SetInterestedDataTypes(
+      const ModelTypeSet& data_types,
+      SyncInvalidationsService::InterestedDataTypesAppliedCallback callback);
 
  private:
-  base::ObserverList<InterestedDataTypesObserver,
-                     /*check_empty=*/true,
-                     /*allow_reentrancy=*/false>
-      observers_;
+  InterestedDataTypesHandler* interested_data_types_handler_ = nullptr;
 
   ModelTypeSet data_types_;
 };
diff --git a/components/sync/invalidations/interested_data_types_manager_unittest.cc b/components/sync/invalidations/interested_data_types_manager_unittest.cc
index f282b6d2..c70797d 100644
--- a/components/sync/invalidations/interested_data_types_manager_unittest.cc
+++ b/components/sync/invalidations/interested_data_types_manager_unittest.cc
@@ -4,16 +4,19 @@
 
 #include "components/sync/invalidations/interested_data_types_manager.h"
 
-#include "components/sync/invalidations/interested_data_types_observer.h"
+#include "base/bind_helpers.h"
+#include "components/sync/invalidations/interested_data_types_handler.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
+
 namespace syncer {
 namespace {
 
-class MockDataTypesObserver : public InterestedDataTypesObserver {
+class MockDataTypesHandler : public InterestedDataTypesHandler {
  public:
-  MOCK_METHOD0(OnInterestedDataTypesChanged, void());
+  MOCK_METHOD1(OnInterestedDataTypesChanged, void(base::OnceClosure callback));
 };
 
 class InterestedDataTypesManagerTest : public testing::Test {
@@ -22,20 +25,23 @@
 };
 
 TEST_F(InterestedDataTypesManagerTest, ShouldReturnGivenDataTypes) {
-  manager_.SetInterestedDataTypes(ModelTypeSet(BOOKMARKS, PREFERENCES));
+  manager_.SetInterestedDataTypes(ModelTypeSet(BOOKMARKS, PREFERENCES),
+                                  base::DoNothing());
   EXPECT_EQ(ModelTypeSet(BOOKMARKS, PREFERENCES),
             manager_.GetInterestedDataTypes());
-  manager_.SetInterestedDataTypes(ModelTypeSet(PREFERENCES, PASSWORDS));
+  manager_.SetInterestedDataTypes(ModelTypeSet(PREFERENCES, PASSWORDS),
+                                  base::DoNothing());
   EXPECT_EQ(ModelTypeSet(PREFERENCES, PASSWORDS),
             manager_.GetInterestedDataTypes());
 }
 
 TEST_F(InterestedDataTypesManagerTest, ShouldNotifyOnChange) {
-  testing::NiceMock<MockDataTypesObserver> observer;
-  manager_.AddInterestedDataTypesObserver(&observer);
-  EXPECT_CALL(observer, OnInterestedDataTypesChanged());
-  manager_.SetInterestedDataTypes(ModelTypeSet(PASSWORDS, AUTOFILL));
-  manager_.RemoveInterestedDataTypesObserver(&observer);
+  testing::NiceMock<MockDataTypesHandler> handler;
+  manager_.SetInterestedDataTypesHandler(&handler);
+  EXPECT_CALL(handler, OnInterestedDataTypesChanged(_));
+  manager_.SetInterestedDataTypes(ModelTypeSet(PASSWORDS, AUTOFILL),
+                                  base::DoNothing());
+  manager_.SetInterestedDataTypesHandler(nullptr);
 }
 
 }  // namespace
diff --git a/components/sync/invalidations/interested_data_types_observer.h b/components/sync/invalidations/interested_data_types_observer.h
deleted file mode 100644
index 90c6389..0000000
--- a/components/sync/invalidations/interested_data_types_observer.h
+++ /dev/null
@@ -1,22 +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_SYNC_INVALIDATIONS_INTERESTED_DATA_TYPES_OBSERVER_H_
-#define COMPONENTS_SYNC_INVALIDATIONS_INTERESTED_DATA_TYPES_OBSERVER_H_
-
-#include "base/observer_list.h"
-
-namespace syncer {
-
-// An interface to observe changes on data types for which the device wants to
-// receive invalidations.
-class InterestedDataTypesObserver : public base::CheckedObserver {
- public:
-  // Called on each change of interested data types.
-  virtual void OnInterestedDataTypesChanged() = 0;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_INVALIDATIONS_INTERESTED_DATA_TYPES_OBSERVER_H_
diff --git a/components/sync/invalidations/mock_sync_invalidations_service.h b/components/sync/invalidations/mock_sync_invalidations_service.h
index c1a8187..742f581f 100644
--- a/components/sync/invalidations/mock_sync_invalidations_service.h
+++ b/components/sync/invalidations/mock_sync_invalidations_service.h
@@ -28,13 +28,13 @@
               (FCMRegistrationTokenObserver * observer));
   MOCK_METHOD(const std::string&, GetFCMRegistrationToken, (), (const));
   MOCK_METHOD(void,
-              AddInterestedDataTypesObserver,
-              (InterestedDataTypesObserver * observer));
-  MOCK_METHOD(void,
-              RemoveInterestedDataTypesObserver,
-              (InterestedDataTypesObserver * observer));
+              SetInterestedDataTypesHandler,
+              (InterestedDataTypesHandler * handler));
   MOCK_METHOD(const ModelTypeSet&, GetInterestedDataTypes, (), (const));
-  MOCK_METHOD(void, SetInterestedDataTypes, (const ModelTypeSet& data_types));
+  MOCK_METHOD(void,
+              SetInterestedDataTypes,
+              (const ModelTypeSet& data_types,
+               InterestedDataTypesAppliedCallback callback));
 };
 
 }  // namespace syncer
diff --git a/components/sync/invalidations/sync_invalidations_service.h b/components/sync/invalidations/sync_invalidations_service.h
index 475f86f..b29d4fd 100644
--- a/components/sync/invalidations/sync_invalidations_service.h
+++ b/components/sync/invalidations/sync_invalidations_service.h
@@ -7,13 +7,14 @@
 
 #include <string>
 
+#include "base/callback.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/sync/base/model_type.h"
 
 namespace syncer {
 class FCMRegistrationTokenObserver;
 class InvalidationsListener;
-class InterestedDataTypesObserver;
+class InterestedDataTypesHandler;
 
 // Service which is used to register with FCM. It is used to obtain an FCM token
 // which is used to send invalidations from the server. The service also
@@ -22,6 +23,11 @@
 // should be added.
 class SyncInvalidationsService : public KeyedService {
  public:
+  // Data types which are newly marked as interesting will be passed to the
+  // callback.
+  using InterestedDataTypesAppliedCallback =
+      base::OnceCallback<void(const ModelTypeSet&)>;
+
   // Start or stop listening to invalidations.
   virtual void SetActive(bool active) = 0;
 
@@ -39,16 +45,16 @@
   // received yet.
   virtual const std::string& GetFCMRegistrationToken() const = 0;
 
-  // Add or remove an interested data types change observer. |observer| must not
-  // be nullptr.
-  virtual void AddInterestedDataTypesObserver(
-      InterestedDataTypesObserver* observer) = 0;
-  virtual void RemoveInterestedDataTypesObserver(
-      InterestedDataTypesObserver* observer) = 0;
+  // Set the interested data types change handler. |handler| can be nullptr to
+  // unregister any existing handler. There can be at most one handler.
+  virtual void SetInterestedDataTypesHandler(
+      InterestedDataTypesHandler* handler) = 0;
 
   // Get or set for which data types should the device receive invalidations.
   virtual const ModelTypeSet& GetInterestedDataTypes() const = 0;
-  virtual void SetInterestedDataTypes(const ModelTypeSet& data_types) = 0;
+  virtual void SetInterestedDataTypes(
+      const ModelTypeSet& data_types,
+      InterestedDataTypesAppliedCallback callback) = 0;
 };
 
 }  // namespace syncer
diff --git a/components/sync/invalidations/sync_invalidations_service_impl.cc b/components/sync/invalidations/sync_invalidations_service_impl.cc
index be5f1ee..d244cc0 100644
--- a/components/sync/invalidations/sync_invalidations_service_impl.cc
+++ b/components/sync/invalidations/sync_invalidations_service_impl.cc
@@ -4,6 +4,8 @@
 
 #include "components/sync/invalidations/sync_invalidations_service_impl.h"
 
+#include <utility>
+
 #include "components/sync/invalidations/fcm_handler.h"
 
 namespace syncer {
@@ -62,14 +64,9 @@
   return fcm_handler_->GetFCMRegistrationToken();
 }
 
-void SyncInvalidationsServiceImpl::AddInterestedDataTypesObserver(
-    InterestedDataTypesObserver* observer) {
-  data_types_manager_.AddInterestedDataTypesObserver(observer);
-}
-
-void SyncInvalidationsServiceImpl::RemoveInterestedDataTypesObserver(
-    InterestedDataTypesObserver* observer) {
-  data_types_manager_.RemoveInterestedDataTypesObserver(observer);
+void SyncInvalidationsServiceImpl::SetInterestedDataTypesHandler(
+    InterestedDataTypesHandler* handler) {
+  data_types_manager_.SetInterestedDataTypesHandler(handler);
 }
 
 const ModelTypeSet& SyncInvalidationsServiceImpl::GetInterestedDataTypes()
@@ -78,8 +75,9 @@
 }
 
 void SyncInvalidationsServiceImpl::SetInterestedDataTypes(
-    const ModelTypeSet& data_types) {
-  data_types_manager_.SetInterestedDataTypes(data_types);
+    const ModelTypeSet& data_types,
+    InterestedDataTypesAppliedCallback callback) {
+  data_types_manager_.SetInterestedDataTypes(data_types, std::move(callback));
 }
 
 void SyncInvalidationsServiceImpl::Shutdown() {
diff --git a/components/sync/invalidations/sync_invalidations_service_impl.h b/components/sync/invalidations/sync_invalidations_service_impl.h
index 666d945..d7d5024 100644
--- a/components/sync/invalidations/sync_invalidations_service_impl.h
+++ b/components/sync/invalidations/sync_invalidations_service_impl.h
@@ -38,12 +38,12 @@
   void AddTokenObserver(FCMRegistrationTokenObserver* observer) override;
   void RemoveTokenObserver(FCMRegistrationTokenObserver* observer) override;
   const std::string& GetFCMRegistrationToken() const override;
-  void AddInterestedDataTypesObserver(
-      InterestedDataTypesObserver* observer) override;
-  void RemoveInterestedDataTypesObserver(
-      InterestedDataTypesObserver* observer) override;
+  void SetInterestedDataTypesHandler(
+      InterestedDataTypesHandler* handler) override;
   const ModelTypeSet& GetInterestedDataTypes() const override;
-  void SetInterestedDataTypes(const ModelTypeSet& data_types) override;
+  void SetInterestedDataTypes(
+      const ModelTypeSet& data_types,
+      InterestedDataTypesAppliedCallback callback) override;
 
   // KeyedService overrides.
   void Shutdown() override;
diff --git a/components/sync_device_info/device_info_sync_bridge.cc b/components/sync_device_info/device_info_sync_bridge.cc
index 9a0ec9b..f6ca59e 100644
--- a/components/sync_device_info/device_info_sync_bridge.cc
+++ b/components/sync_device_info/device_info_sync_bridge.cc
@@ -225,7 +225,11 @@
   return local_device_info_provider_.get();
 }
 
-void DeviceInfoSyncBridge::RefreshLocalDeviceInfo() {
+void DeviceInfoSyncBridge::RefreshLocalDeviceInfo(base::OnceClosure callback) {
+  if (!callback.is_null()) {
+    device_info_synced_callback_list_.push_back(std::move(callback));
+  }
+
   SendLocalData();
 }
 
@@ -318,6 +322,15 @@
 
   batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
   CommitAndNotify(std::move(batch), has_changes);
+
+  DCHECK(!local_cache_guid_.empty());
+  if (!change_processor()->IsEntityUnsynced(local_cache_guid_)) {
+    for (base::OnceClosure& callback : device_info_synced_callback_list_) {
+      std::move(callback).Run();
+    }
+    device_info_synced_callback_list_.clear();
+  }
+
   return base::nullopt;
 }
 
diff --git a/components/sync_device_info/device_info_sync_bridge.h b/components/sync_device_info/device_info_sync_bridge.h
index 6db0ab0..a89b6c3 100644
--- a/components/sync_device_info/device_info_sync_bridge.h
+++ b/components/sync_device_info/device_info_sync_bridge.h
@@ -51,8 +51,9 @@
   // Refresh local copy of device info in memory, and informs sync of the
   // change. Used when the caller knows a property of local device info has
   // changed (e.g. SharingInfo), and must be sync-ed to other devices as soon as
-  // possible, without waiting for the periodic commits.
-  void RefreshLocalDeviceInfo();
+  // possible, without waiting for the periodic commits. |callback| will be
+  // called when device info is synced.
+  void RefreshLocalDeviceInfo(base::OnceClosure callback);
 
   // ModelTypeSyncBridge implementation.
   void OnSyncStarting(const DataTypeActivationRequest& request) override;
@@ -163,6 +164,8 @@
   // Used to update our local device info once every pulse interval.
   base::OneShotTimer pulse_timer_;
 
+  std::vector<base::OnceClosure> device_info_synced_callback_list_;
+
   const std::unique_ptr<DeviceInfoPrefs> device_info_prefs_;
 
   base::WeakPtrFactory<DeviceInfoSyncBridge> weak_ptr_factory_{this};
diff --git a/components/sync_device_info/device_info_sync_bridge_unittest.cc b/components/sync_device_info/device_info_sync_bridge_unittest.cc
index 6dbd4ec..8593f44 100644
--- a/components/sync_device_info/device_info_sync_bridge_unittest.cc
+++ b/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
@@ -502,7 +503,9 @@
 
   void ForcePulse() { bridge()->ForcePulseForTest(); }
 
-  void RefreshLocalDeviceInfo() { bridge()->RefreshLocalDeviceInfo(); }
+  void RefreshLocalDeviceInfo() {
+    bridge()->RefreshLocalDeviceInfo(base::OnceClosure());
+  }
 
   void CommitToStoreAndWait(std::unique_ptr<WriteBatch> batch) {
     base::RunLoop loop;
@@ -1307,6 +1310,24 @@
   InitializeAndMergeInitialData(SyncMode::kFull);
 }
 
+TEST_F(DeviceInfoSyncBridgeTest, ShouldNotifyWhenDeviceInfoIsSynced) {
+  InitializeAndMergeInitialData(SyncMode::kFull);
+
+  base::MockOnceClosure callback;
+  bridge()->RefreshLocalDeviceInfo(callback.Get());
+
+  std::string guid = local_device()->GetLocalDeviceInfo()->guid();
+  EXPECT_CALL(*processor(), IsEntityUnsynced(guid)).WillOnce(Return(true));
+  EXPECT_CALL(callback, Run()).Times(0);
+  bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(),
+                             EntityChangeList());
+
+  EXPECT_CALL(*processor(), IsEntityUnsynced(guid)).WillOnce(Return(false));
+  EXPECT_CALL(callback, Run());
+  bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(),
+                             EntityChangeList());
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/sync_device_info/device_info_sync_service.h b/components/sync_device_info/device_info_sync_service.h
index a2741bd..064e06e 100644
--- a/components/sync_device_info/device_info_sync_service.h
+++ b/components/sync_device_info/device_info_sync_service.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
 
@@ -37,8 +38,10 @@
   // Interface to refresh local copy of device info in memory, and informs sync
   // of the change. Used when the caller knows a property of local device info
   // has changed (e.g. SharingInfo), and must be sync-ed to other devices as
-  // soon as possible, without waiting for the periodic commits.
-  virtual void RefreshLocalDeviceInfo() = 0;
+  // soon as possible, without waiting for the periodic commits. |callback| will
+  // be called when device info is synced.
+  virtual void RefreshLocalDeviceInfo(
+      base::OnceClosure callback = base::OnceClosure()) = 0;
 };
 
 }  // namespace syncer
diff --git a/components/sync_device_info/device_info_sync_service_impl.cc b/components/sync_device_info/device_info_sync_service_impl.cc
index 87a5f883..9e1baf26 100644
--- a/components/sync_device_info/device_info_sync_service_impl.cc
+++ b/components/sync_device_info/device_info_sync_service_impl.cc
@@ -46,7 +46,7 @@
 
   if (sync_invalidations_service_) {
     sync_invalidations_service_->AddTokenObserver(this);
-    sync_invalidations_service_->AddInterestedDataTypesObserver(this);
+    sync_invalidations_service_->SetInterestedDataTypesHandler(this);
   }
 }
 
@@ -66,22 +66,24 @@
   return bridge_->change_processor()->GetControllerDelegate();
 }
 
-void DeviceInfoSyncServiceImpl::RefreshLocalDeviceInfo() {
-  bridge_->RefreshLocalDeviceInfo();
+void DeviceInfoSyncServiceImpl::RefreshLocalDeviceInfo(
+    base::OnceClosure callback) {
+  bridge_->RefreshLocalDeviceInfo(std::move(callback));
 }
 
 void DeviceInfoSyncServiceImpl::OnFCMRegistrationTokenChanged() {
   RefreshLocalDeviceInfo();
 }
 
-void DeviceInfoSyncServiceImpl::OnInterestedDataTypesChanged() {
-  RefreshLocalDeviceInfo();
+void DeviceInfoSyncServiceImpl::OnInterestedDataTypesChanged(
+    base::OnceClosure callback) {
+  RefreshLocalDeviceInfo(std::move(callback));
 }
 
 void DeviceInfoSyncServiceImpl::Shutdown() {
   if (sync_invalidations_service_) {
     sync_invalidations_service_->RemoveTokenObserver(this);
-    sync_invalidations_service_->RemoveInterestedDataTypesObserver(this);
+    sync_invalidations_service_->SetInterestedDataTypesHandler(nullptr);
   }
 }
 
diff --git a/components/sync_device_info/device_info_sync_service_impl.h b/components/sync_device_info/device_info_sync_service_impl.h
index 583fdd6..e538ef4f 100644
--- a/components/sync_device_info/device_info_sync_service_impl.h
+++ b/components/sync_device_info/device_info_sync_service_impl.h
@@ -9,7 +9,7 @@
 #include <string>
 
 #include "components/sync/invalidations/fcm_registration_token_observer.h"
-#include "components/sync/invalidations/interested_data_types_observer.h"
+#include "components/sync/invalidations/interested_data_types_handler.h"
 #include "components/sync/model/model_type_store.h"
 #include "components/sync_device_info/device_info_sync_service.h"
 
@@ -23,7 +23,7 @@
 
 class DeviceInfoSyncServiceImpl : public DeviceInfoSyncService,
                                   public FCMRegistrationTokenObserver,
-                                  public InterestedDataTypesObserver {
+                                  public InterestedDataTypesHandler {
  public:
   // |local_device_info_provider| must not be null.
   // |device_info_prefs| must not be null.
@@ -43,13 +43,14 @@
   LocalDeviceInfoProvider* GetLocalDeviceInfoProvider() override;
   DeviceInfoTracker* GetDeviceInfoTracker() override;
   base::WeakPtr<ModelTypeControllerDelegate> GetControllerDelegate() override;
-  void RefreshLocalDeviceInfo() override;
+  void RefreshLocalDeviceInfo(
+      base::OnceClosure callback = base::OnceClosure()) override;
 
   // FCMRegistrationTokenObserver implementation.
   void OnFCMRegistrationTokenChanged() override;
 
-  // InterestedDataTypesObserver implementation.
-  void OnInterestedDataTypesChanged() override;
+  // InterestedDataTypesHandler implementation.
+  void OnInterestedDataTypesChanged(base::OnceClosure callback) override;
 
   // KeyedService overrides.
   void Shutdown() override;
diff --git a/components/sync_device_info/fake_device_info_sync_service.cc b/components/sync_device_info/fake_device_info_sync_service.cc
index c0abede..5c1cbb1 100644
--- a/components/sync_device_info/fake_device_info_sync_service.cc
+++ b/components/sync_device_info/fake_device_info_sync_service.cc
@@ -25,8 +25,12 @@
   return fake_model_type_controller_delegate_.GetWeakPtr();
 }
 
-void FakeDeviceInfoSyncService::RefreshLocalDeviceInfo() {
+void FakeDeviceInfoSyncService::RefreshLocalDeviceInfo(
+    base::OnceClosure callback) {
   refresh_local_device_info_count_++;
+  if (!callback.is_null()) {
+    std::move(callback).Run();
+  }
 }
 
 int FakeDeviceInfoSyncService::RefreshLocalDeviceInfoCount() {
diff --git a/components/sync_device_info/fake_device_info_sync_service.h b/components/sync_device_info/fake_device_info_sync_service.h
index cc29872..884f31ada 100644
--- a/components/sync_device_info/fake_device_info_sync_service.h
+++ b/components/sync_device_info/fake_device_info_sync_service.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SYNC_DEVICE_INFO_FAKE_DEVICE_INFO_SYNC_SERVICE_H_
 #define COMPONENTS_SYNC_DEVICE_INFO_FAKE_DEVICE_INFO_SYNC_SERVICE_H_
 
+#include "base/callback.h"
 #include "components/sync/model/fake_model_type_controller_delegate.h"
 #include "components/sync_device_info/device_info_sync_service.h"
 #include "components/sync_device_info/fake_device_info_tracker.h"
@@ -21,9 +22,9 @@
   FakeLocalDeviceInfoProvider* GetLocalDeviceInfoProvider() override;
   FakeDeviceInfoTracker* GetDeviceInfoTracker() override;
   base::WeakPtr<ModelTypeControllerDelegate> GetControllerDelegate() override;
-  void RefreshLocalDeviceInfo() override;
+  void RefreshLocalDeviceInfo(base::OnceClosure callback) override;
 
-  // Returns number of times RefreshLocalDeviceInfo() has neen called.
+  // Returns number of times RefreshLocalDeviceInfo() has been called.
   int RefreshLocalDeviceInfoCount();
 
  private:
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 5209915..82805124 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2047,7 +2047,6 @@
     "webui/web_ui_impl.h",
     "webui/web_ui_message_handler.cc",
     "webui/web_ui_url_loader_factory.cc",
-    "webui/web_ui_url_loader_factory_internal.h",
     "worker_host/dedicated_worker_host.cc",
     "worker_host/dedicated_worker_host.h",
     "worker_host/dedicated_worker_host_factory_impl.cc",
@@ -2839,9 +2838,9 @@
 # See comment at the top of //content/BUILD.gn for how this works.
 group("for_content_tests") {
   visibility = [
-    "//content/web_test:web_test_browser",
-    "//content/test/*",
     "//content/public/test/android/*",
+    "//content/test/*",
+    "//content/web_test:web_test_browser",
   ]
   if (!is_component_build) {
     public_deps = [ ":browser" ]
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 36410794..bd01aa0 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -15,6 +15,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_piece_forward.h"
 #include "base/system/sys_info.h"
+#include "base/task/common/task_annotator.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
@@ -78,6 +79,7 @@
 #include "services/device/public/mojom/vibration_manager.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
 #include "third_party/blink/public/mojom/app_banner/app_banner.mojom.h"
 
@@ -152,6 +154,9 @@
     EnableFeatureAndSetParams(
         features::kBackForwardCache, "skip_same_site_if_unload_exists",
         skip_same_site_if_unload_exists_ ? "true" : "false");
+    EnableFeatureAndSetParams(
+        blink::features::kLogUnexpectedIPCPostedToBackForwardCachedDocuments,
+        "delay_before_tracking_ms", "0");
 #if defined(OS_ANDROID)
     EnableFeatureAndSetParams(features::kBackForwardCache,
                               "process_binding_strength", "NORMAL");
@@ -2332,6 +2337,46 @@
       blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock, FROM_HERE);
 }
 
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, LogIpcPostedToCachedFrame) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // 1) Navigate to a page.
+  GURL url(embedded_test_server()->GetURL("/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+  RenderFrameHostImpl* rfh_a = current_frame_host();
+
+  // 2) Navigate away. The first page should be in the cache.
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
+
+  // 3) Post IPC tasks to the page, testing both mojo remote and associated
+  // remote objects.
+
+  // TODO(hbolaria) - implement non-frame-associated tracking, which will be
+  // used by the code below.
+
+  // Post a non-associated interface. Will be routed to a frame-specific task
+  // queue with IPC set in SimpleWatcher.
+  base::RunLoop run_loop;
+  rfh_a->GetHighPriorityLocalFrame()->DispatchBeforeUnload(
+      false,
+      base::BindOnce([](base::RepeatingClosure quit_closure, bool proceed,
+                        base::TimeTicks start_time,
+                        base::TimeTicks end_time) { quit_closure.Run(); },
+                     run_loop.QuitClosure()));
+  run_loop.Run();
+
+  // 4) Check the histogram.
+  FetchHistogramsFromChildProcesses();
+  base::HistogramBase::Sample sample = base::HistogramBase::Sample(
+      base::TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName(
+          "blink.mojom.HighPriorityLocalFrame"));
+  histogram_tester_.ExpectUniqueSample(
+      "BackForwardCache.Experimental."
+      "UnexpectedIPCMessagePostedToCachedFrame.MethodHash",
+      sample, 1);
+}
+
 class MockAppBannerService : public blink::mojom::AppBannerService {
  public:
   MockAppBannerService() = default;
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index f6b8e53..af9c5ca7 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -1299,16 +1299,10 @@
                 // even when there is high priority work to do.
                 base::TaskPriority::USER_VISIBLE));
   } else if (rfh && params->url().SchemeIs(content::kChromeUIScheme)) {
-    mojo::PendingRemote<network::mojom::URLLoaderFactory>
-        url_loader_factory_remote;
-    mojo::MakeSelfOwnedReceiver(
-        CreateWebUIURLLoader(rfh, params->url().scheme(),
-                             base::flat_set<std::string>()),
-        url_loader_factory_remote.InitWithNewPipeAndPassReceiver());
     pending_url_loader_factory =
-        CreatePendingSharedURLLoaderFactoryFromURLLoaderFactory(
-            CreateWebUIURLLoader(rfh, params->url().scheme(),
-                                 base::flat_set<std::string>()));
+        std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
+            CreateWebUIURLLoaderFactory(rfh, params->url().scheme(),
+                                        base::flat_set<std::string>()));
   } else if (rfh && params->url().SchemeIsFileSystem()) {
     StoragePartitionImpl* storage_partition =
         static_cast<StoragePartitionImpl*>(
diff --git a/content/browser/download/save_file_manager.cc b/content/browser/download/save_file_manager.cc
index a4d1228..b140be6 100644
--- a/content/browser/download/save_file_manager.cc
+++ b/content/browser/download/save_file_manager.cc
@@ -252,7 +252,6 @@
     request->mode = network::mojom::RequestMode::kNavigate;
 
     network::mojom::URLLoaderFactory* factory = nullptr;
-    std::unique_ptr<network::mojom::URLLoaderFactory> url_loader_factory;
     mojo::Remote<network::mojom::URLLoaderFactory> factory_remote;
     auto* rfh = RenderFrameHostImpl::FromID(render_process_host_id,
                                             render_frame_routing_id);
@@ -279,9 +278,8 @@
           storage_partition->GetFileSystemContext(), partition_domain));
       factory = factory_remote.get();
     } else if (rfh && url.SchemeIs(content::kChromeUIScheme)) {
-      url_loader_factory = CreateWebUIURLLoader(rfh, url.scheme(),
-                                                base::flat_set<std::string>());
-      factory = url_loader_factory.get();
+      factory_remote.Bind(CreateWebUIURLLoaderFactory(rfh, url.scheme(), {}));
+      factory = factory_remote.get();
     } else {
       factory = storage_partition->GetURLLoaderFactoryForBrowserProcess().get();
     }
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 1e572be..c4ed2ec 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -48,7 +48,6 @@
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/browser/web_package/web_bundle_utils.h"
 #include "content/browser/webui/url_data_manager_backend.h"
-#include "content/browser/webui/web_ui_url_loader_factory_internal.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -59,6 +58,7 @@
 #include "content/public/browser/ssl_status.h"
 #include "content/public/browser/url_loader_request_interceptor.h"
 #include "content/public/browser/url_loader_throttles.h"
+#include "content/public/browser/web_ui_url_loader_factory.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -1154,8 +1154,11 @@
         &factory_receiver, nullptr /* header_client */,
         nullptr /* bypass_redirect_checks */, nullptr /* disable_secure_dns */,
         nullptr /* factory_override */);
-    CreateWebUIURLLoaderBinding(frame_tree_node, scheme,
-                                std::move(factory_receiver));
+
+    mojo::Remote<network::mojom::URLLoaderFactory> direct_factory_for_webui(
+        CreateWebUIURLLoaderFactory(frame_tree_node->current_frame_host(),
+                                    scheme, {}));
+    direct_factory_for_webui->Clone(std::move(factory_receiver));
   }
 
   mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index d398c2e..f49fbbaf 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -128,7 +128,6 @@
 #include "content/browser/webtransport/quic_transport_connector_impl.h"
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
-#include "content/browser/webui/web_ui_url_loader_factory_internal.h"
 #include "content/browser/worker_host/dedicated_worker_host_factory_impl.h"
 #include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/common/associated_interfaces.mojom.h"
@@ -163,6 +162,7 @@
 #include "content/public/browser/site_isolation_policy.h"
 #include "content/public/browser/sms_fetcher.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_ui_url_loader_factory.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_constants.h"
@@ -6102,8 +6102,10 @@
           base::nullopt /* navigation_id */, &factory_receiver,
           nullptr /* header_client */, nullptr /* bypass_redirect_checks */,
           nullptr /* disable_secure_dns */, nullptr /* factory_override */);
-      CreateWebUIURLLoaderBinding(frame_tree_node(), scheme,
-                                  std::move(factory_receiver));
+      mojo::Remote<network::mojom::URLLoaderFactory> direct_factory_for_webui(
+          CreateWebUIURLLoaderFactory(this, scheme, {}));
+      direct_factory_for_webui->Clone(std::move(factory_receiver));
+
       // If the renderer has webui bindings, then don't give it access to
       // network loader for security reasons.
       // http://crbug.com/829412: make an exception for a small whitelist
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 9b1c9de..29d9411 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -3717,17 +3717,26 @@
 // Check that same site navigation correctly resets document_used_web_otp_.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
                        SameSiteNavigationResetsDocumentUsedWebOTP) {
-  const GURL first_url(
-      embedded_test_server()->GetURL("/page_with_webotp.html"));
-  const GURL second_url(embedded_test_server()->GetURL("/empty.html"));
-
-  // Load a URL that maps to the same SiteInstance as the second URL, to make
-  // sure the second navigation will not be cross-process.
+  const GURL first_url(embedded_test_server()->GetURL("/title1.html"));
   ASSERT_TRUE(NavigateToURL(shell(), first_url));
 
+  std::string script = R"(
+    var element = document.createElement('div');
+    document.body.appendChild(element);
+    navigator.credentials.get({
+      otp: {transport:['sms']}
+    })
+    .then(content => element.value = content.code);
+  )";
+  EXPECT_TRUE(ExecuteScript(web_contents(), script));
+  EXPECT_TRUE(WaitForLoadStop(web_contents()));
+
   RenderFrameHostImpl* main_rfh = web_contents()->GetMainFrame();
   EXPECT_TRUE(main_rfh->DocumentUsedWebOTP());
 
+  // Loads a URL that maps to the same SiteInstance as the first URL, to make
+  // sure the navigation will not be cross-process.
+  const GURL second_url(embedded_test_server()->GetURL("/title2.html"));
   ASSERT_TRUE(NavigateToURL(shell(), second_url));
   EXPECT_FALSE(main_rfh->DocumentUsedWebOTP());
 }
diff --git a/content/browser/webui/web_ui_url_loader_factory.cc b/content/browser/webui/web_ui_url_loader_factory.cc
index edddae2..542bec7 100644
--- a/content/browser/webui/web_ui_url_loader_factory.cc
+++ b/content/browser/webui/web_ui_url_loader_factory.cc
@@ -18,6 +18,7 @@
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/loader/non_network_url_loader_factory_base.h"
 #include "content/browser/webui/network_error_url_loader.h"
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/browser/webui/url_data_source_impl.h"
@@ -25,6 +26,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -202,30 +204,31 @@
                                      std::move(data_available_callback));
 }
 
-// This class has two ownership models. When it's created by
-// CreateWebUIURLLoaderBinding it is owned by its receivers and will delete
-// itself when it has no more receivers. Otherwise it's strongly owned.
-class WebUIURLLoaderFactory : public network::mojom::URLLoaderFactory {
+class WebUIURLLoaderFactory : public NonNetworkURLLoaderFactoryBase {
  public:
+  // Returns mojo::PendingRemote to a newly constructed WebUIURLLoaderFactory.
+  // The factory is self-owned - it will delete itself once there are no more
+  // receivers (including the receiver associated with the returned
+  // mojo::PendingRemote and the receivers bound by the Clone method).
+  //
   // |allowed_hosts| is an optional set of allowed host names. If empty then
   // all hosts are allowed.
-  WebUIURLLoaderFactory(
+  static mojo::PendingRemote<network::mojom::URLLoaderFactory> Create(
       FrameTreeNode* ftn,
       const std::string& scheme,
-      base::flat_set<std::string> allowed_hosts,
-      mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver =
-          mojo::PendingReceiver<network::mojom::URLLoaderFactory>())
-      : frame_tree_node_id_(ftn->frame_tree_node_id()),
-        scheme_(scheme),
-        allowed_hosts_(std::move(allowed_hosts)) {
-    if (factory_receiver) {
-      loader_factory_receivers_.set_disconnect_handler(base::BindRepeating(
-          &WebUIURLLoaderFactory::OnDisconnect, base::Unretained(this)));
-      loader_factory_receivers_.Add(this, std::move(factory_receiver));
-    }
+      base::flat_set<std::string> allowed_hosts) {
+    mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
+
+    // The WebUIURLLoaderFactory will delete itself when there are no more
+    // receivers - see the NonNetworkURLLoaderFactoryBase::OnDisconnect method.
+    new WebUIURLLoaderFactory(ftn, scheme, std::move(allowed_hosts),
+                              pending_remote.InitWithNewPipeAndPassReceiver());
+
+    return pending_remote;
   }
 
-  ~WebUIURLLoaderFactory() override {}
+ private:
+  ~WebUIURLLoaderFactory() override = default;
 
   // network::mojom::URLLoaderFactory implementation:
   void CreateLoaderAndStart(
@@ -250,13 +253,7 @@
 
     if (request.url.scheme() != scheme_) {
       DVLOG(1) << "Bad scheme: " << request.url.scheme();
-      if (loader_factory_receivers_.empty()) {
-        // This factory is being used directly without going through a mojo pipe
-        // so just assert.
-        CHECK(false) << "Incorrect scheme";
-      } else {
-        loader_factory_receivers_.ReportBadMessage("Incorrect scheme");
-      }
+      mojo::ReportBadMessage("Incorrect scheme");
       mojo::Remote<network::mojom::URLLoaderClient>(std::move(client))
           ->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
       return;
@@ -271,11 +268,7 @@
       base::debug::SetCrashKeyString(crash_key, request.url.spec());
 
       DVLOG(1) << "Bad host: \"" << request.url.host() << '"';
-      if (loader_factory_receivers_.empty()) {
-        CHECK(false) << "Incorrect host";
-      } else {
-        loader_factory_receivers_.ReportBadMessage("Incorrect host");
-      }
+      mojo::ReportBadMessage("Incorrect host");
       mojo::Remote<network::mojom::URLLoaderClient>(std::move(client))
           ->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
       return;
@@ -305,44 +298,33 @@
                    browser_context);
   }
 
-  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
-      override {
-    loader_factory_receivers_.Add(this, std::move(receiver));
-  }
-
   const std::string& scheme() const { return scheme_; }
 
- private:
-  void OnDisconnect() {
-    if (loader_factory_receivers_.empty())
-      delete this;
-  }
+  WebUIURLLoaderFactory(
+      FrameTreeNode* ftn,
+      const std::string& scheme,
+      base::flat_set<std::string> allowed_hosts,
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver)
+      : NonNetworkURLLoaderFactoryBase(std::move(factory_receiver)),
+        frame_tree_node_id_(ftn->frame_tree_node_id()),
+        scheme_(scheme),
+        allowed_hosts_(std::move(allowed_hosts)) {}
 
   int const frame_tree_node_id_;
   const std::string scheme_;
   const base::flat_set<std::string> allowed_hosts_;  // if empty all allowed.
-  mojo::ReceiverSet<network::mojom::URLLoaderFactory> loader_factory_receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(WebUIURLLoaderFactory);
 };
 
 }  // namespace
 
-std::unique_ptr<network::mojom::URLLoaderFactory> CreateWebUIURLLoader(
-    RenderFrameHost* render_frame_host,
-    const std::string& scheme,
-    base::flat_set<std::string> allowed_hosts) {
-  return std::make_unique<WebUIURLLoaderFactory>(
-      FrameTreeNode::From(render_frame_host), scheme, std::move(allowed_hosts));
-}
-
-void CreateWebUIURLLoaderBinding(
-    FrameTreeNode* node,
-    const std::string& scheme,
-    mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver) {
-  // Deletes itself when the last receiver is destructed.
-  new WebUIURLLoaderFactory(node, scheme, base::flat_set<std::string>(),
-                            std::move(factory_receiver));
+mojo::PendingRemote<network::mojom::URLLoaderFactory>
+CreateWebUIURLLoaderFactory(RenderFrameHost* render_frame_host,
+                            const std::string& scheme,
+                            base::flat_set<std::string> allowed_hosts) {
+  return WebUIURLLoaderFactory::Create(FrameTreeNode::From(render_frame_host),
+                                       scheme, std::move(allowed_hosts));
 }
 
 }  // namespace content
diff --git a/content/browser/webui/web_ui_url_loader_factory_internal.h b/content/browser/webui/web_ui_url_loader_factory_internal.h
deleted file mode 100644
index 7fbdd80..0000000
--- a/content/browser/webui/web_ui_url_loader_factory_internal.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_WEBUI_WEB_UI_URL_LOADER_FACTORY_INTERNAL_H_
-#define CONTENT_BROWSER_WEBUI_WEB_UI_URL_LOADER_FACTORY_INTERNAL_H_
-
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
-
-namespace content {
-class FrameTreeNode;
-
-// Creates a URLLoaderFactory for serving WebUI requests to the given |node|.
-// The factory will only create loaders for requests with the same scheme as
-// |scheme|. This is needed because there is more than one scheme used for
-// WebUI, and not all have WebUI bindings.
-void CreateWebUIURLLoaderBinding(
-    FrameTreeNode* node,
-    const std::string& scheme,
-    mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver);
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_WEBUI_WEB_UI_URL_LOADER_FACTORY_INTERNAL_H_
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 5f0c4350..68b20c8 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -144,9 +144,6 @@
   // Enables the Blink feature when the base::Feature is enabled,
   // otherwise no change.
   kEnableOnly,
-  // Enables the Blink feature when the base::Feature is enabled
-  // via an override on the command-line, otherwise no change.
-  kEnableOnlyIfOverriddenFromCommandLine,
   // Disables the Blink feature when the base::Feature is *disabled*,
   // otherwise no change.
   kDisableOnly,
@@ -175,13 +172,6 @@
       if (feature_enabled)
         enabler(true);
       break;
-    case kEnableOnlyIfOverriddenFromCommandLine:
-      if (FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine(
-              chromium_feature.name, FeatureList::OVERRIDE_ENABLE_FEATURE)) {
-        DCHECK(feature_enabled);
-        enabler(true);
-      }
-      break;
     case kDisableOnly:
       if (!feature_enabled)
         enabler(false);
@@ -276,8 +266,7 @@
      blink::features::kAllowSyncXHRInPageDismissal, kEnableOnly},
     {wf::EnableAutoplayIgnoresWebAudio, media::kAutoplayIgnoreWebAudio,
      kDefault},
-    {wf::EnablePortals, blink::features::kPortals,
-     kEnableOnlyIfOverriddenFromCommandLine},
+    {wf::EnablePortals, blink::features::kPortals, kDefault},
     {wf::EnableImplicitRootScroller, blink::features::kImplicitRootScroller,
      kDefault},
     {wf::EnableCSSOMViewScrollCoordinates,
diff --git a/content/public/browser/web_ui_url_loader_factory.h b/content/public/browser/web_ui_url_loader_factory.h
index b26c467..419767f 100644
--- a/content/public/browser/web_ui_url_loader_factory.h
+++ b/content/public/browser/web_ui_url_loader_factory.h
@@ -5,22 +5,28 @@
 #ifndef CONTENT_PUBLIC_BROWSER_WEB_UI_URL_LOADER_FACTORY_H_
 #define CONTENT_PUBLIC_BROWSER_WEB_UI_URL_LOADER_FACTORY_H_
 
-#include <memory>
 #include <string>
 
 #include "base/containers/flat_set.h"
 #include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 
 namespace content {
 class RenderFrameHost;
 
-// Create a URLLoaderFactory for loading resources matching the specified
-// |scheme| and also from a "pseudo host" matching one in |allowed_hosts|.
-CONTENT_EXPORT std::unique_ptr<network::mojom::URLLoaderFactory>
-CreateWebUIURLLoader(RenderFrameHost* render_frame_host,
-                     const std::string& scheme,
-                     base::flat_set<std::string> allowed_hosts);
+// Create and bind a URLLoaderFactory for loading resources matching the
+// specified |scheme| and also from a "pseudo host" matching one in
+// |allowed_hosts|.
+//
+// The factory will only create loaders for requests with the same scheme as
+// |scheme|. This is needed because there is more than one scheme used for
+// WebUI, and not all have WebUI bindings.
+CONTENT_EXPORT
+mojo::PendingRemote<network::mojom::URLLoaderFactory>
+CreateWebUIURLLoaderFactory(RenderFrameHost* render_frame_host,
+                            const std::string& scheme,
+                            base::flat_set<std::string> allowed_hosts);
 
 }  // namespace content
 
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java
index c0af188d..640d01f 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/NativeLibraryTestUtils.java
@@ -4,8 +4,6 @@
 
 package org.chromium.content_public.browser.test;
 
-import org.junit.Assert;
-
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
@@ -18,14 +16,14 @@
  */
 public class NativeLibraryTestUtils {
     /**
-     * Loads the native library on the activity UI thread (must not be called from the UI thread).
+     * Loads the native library on the activity UI thread.
      */
     public static void loadNativeLibraryNoBrowserProcess() {
         handleNativeInitialization(false);
     }
 
     /**
-     * Loads the native library on the activity UI thread (must not be called from the UI thread).
+     * Loads the native library on the activity UI thread.
      * After loading the library, this will initialize the browser process.
      */
     public static void loadNativeLibraryAndInitBrowserProcess() {
@@ -33,8 +31,6 @@
     }
 
     private static void handleNativeInitialization(final boolean initBrowserProcess) {
-        Assert.assertFalse(ThreadUtils.runningOnUiThread());
-
         // LibraryLoader is not in general multithreaded; as other InstrumentationTestCase code
         // (specifically, ChromeBrowserProvider) uses it from the main thread we must do
         // likewise.
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index e24ea26..be444b3f 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -283,10 +283,6 @@
 
   ~RenderFrameImpl() override;
 
-  // Called by RenderWidget when meaningful layout has happened.
-  // See RenderFrameObserver::DidMeaningfulLayout declaration for details.
-  void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type);
-
   // Draw commands have been issued by blink::LayerTreeView.
   void DidCommitAndDrawCompositorFrame();
 
@@ -776,6 +772,7 @@
   void AssociateInputAndOutputForAec(
       const base::UnguessableToken& input_stream_id,
       const std::string& output_device_id) override;
+  void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) override;
 
   // Binds to the fullscreen service in the browser.
   void BindFullscreen(
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 5b8818db..98debfa 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -724,11 +724,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // WebWidgetClient
 
-void RenderWidget::DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) {
-  for (auto& observer : render_frames_)
-    observer.DidMeaningfulLayout(layout_type);
-}
-
 void RenderWidget::SetHandlingInputEvent(bool handling_input_event) {
   GetWebWidget()->SetHandlingInputEvent(handling_input_event);
 }
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 376a2920..beddad0 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -234,7 +234,6 @@
 
   // blink::WebWidgetClient
   void ScheduleAnimation() override;
-  void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) override;
   void CloseWidgetSoon() override;
   void ClosePopupWidgetSoon() override;
   void Show(blink::WebNavigationPolicy) override;
diff --git a/content/test/data/page_with_webotp.html b/content/test/data/page_with_webotp.html
deleted file mode 100644
index 415146b..0000000
--- a/content/test/data/page_with_webotp.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<input autocomplete="one-time-code">
-<script>
-  const ac = document.querySelector('input[autocomplete="one-time-code"]');
-  navigator.credentials.get({
-    otp: {transport:['sms']}
-  })
-  .then(content => ac.value = content.code);
-</script>
-
diff --git a/device/fido/BUILD.gn b/device/fido/BUILD.gn
index 0c071c7..b6dbc06 100644
--- a/device/fido/BUILD.gn
+++ b/device/fido/BUILD.gn
@@ -16,6 +16,7 @@
     "cable/cable_discovery_data.h",
     "cable/noise.cc",
     "cable/noise.h",
+    "cable/v2_constants.h",
     "cable/v2_handshake.cc",
     "cable/v2_handshake.h",
     "cbor_extract.cc",
@@ -263,6 +264,21 @@
   }
 }
 
+static_library("cablev2_registration") {
+  sources = [
+    "cable/v2_registration.cc",
+    "cable/v2_registration.h",
+  ]
+  deps = [
+    ":fido",
+    "//base",
+    "//components/cbor",
+    "//components/device_event_log",
+    "//components/gcm_driver",
+    "//components/gcm_driver/instance_id",
+  ]
+}
+
 if (is_chromeos) {
   proto_library("u2f_proto") {
     sources = [ "//third_party/cros_system_api/dbus/u2f/u2f_interface.proto" ]
diff --git a/device/fido/DEPS b/device/fido/DEPS
index 690e920..7df6a75c 100644
--- a/device/fido/DEPS
+++ b/device/fido/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+components/apdu",
   "+components/cbor",
+  "+components/gcm_driver",
   "+crypto",
   "+dbus",
   "+net/base",
diff --git a/device/fido/cable/v2_constants.h b/device/fido/cable/v2_constants.h
new file mode 100644
index 0000000..4318ec0
--- /dev/null
+++ b/device/fido/cable/v2_constants.h
@@ -0,0 +1,32 @@
+// 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 DEVICE_FIDO_CABLE_V2_CONSTANTS_H_
+#define DEVICE_FIDO_CABLE_V2_CONSTANTS_H_
+
+namespace device {
+namespace cablev2 {
+
+// kNonceSize is the number of bytes of nonce in the BLE advert.
+constexpr size_t kNonceSize = 8;
+// kClientNonceSize is the number of bytes of nonce sent by the client, via the
+// tunnel server, for a pairing-based handshake.
+constexpr size_t kClientNonceSize = 16;
+// kRoutingIdSize is the number of bytes of routing information in the BLE
+// advert.
+constexpr size_t kRoutingIdSize = 3;
+// kTunnelIdSize is the number of bytes of opaque tunnel ID, used to identify a
+// specific tunnel to the tunnel service.
+constexpr size_t kTunnelIdSize = 16;
+// kEIDKeySize is the size of the AES key used to encrypt BLE adverts.
+constexpr size_t kEIDKeySize = 32;
+// kPSKSize is the size of the Noise pre-shared key used in handshakes.
+constexpr size_t kPSKSize = 32;
+// kRootSecretSize is the size of the main key maintained by authenticators.
+constexpr size_t kRootSecretSize = 32;
+
+}  // namespace cablev2
+}  // namespace device
+
+#endif  // DEVICE_FIDO_CABLE_V2_CONSTANTS_H_
diff --git a/device/fido/cable/v2_registration.cc b/device/fido/cable/v2_registration.cc
new file mode 100644
index 0000000..1c26b62
--- /dev/null
+++ b/device/fido/cable/v2_registration.cc
@@ -0,0 +1,177 @@
+// 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 "device/fido/cable/v2_registration.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "components/cbor/reader.h"
+#include "components/cbor/values.h"
+#include "components/device_event_log/device_event_log.h"
+#include "components/gcm_driver/gcm_app_handler.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/gcm_driver/instance_id/instance_id_driver.h"
+#include "device/fido/fido_parsing_utils.h"
+
+namespace device {
+namespace cablev2 {
+namespace authenticator {
+
+namespace {
+
+static const char kFCMAppId[] = "chrome.android.features.cablev2_authenticator";
+static const char kFCMSenderId[] = "141743603694";
+
+class FCMHandler : public gcm::GCMAppHandler, public Registration {
+ public:
+  FCMHandler(instance_id::InstanceIDDriver* instance_id_driver,
+             base::RepeatingCallback<void(std::unique_ptr<Registration::Event>)>
+                 event_callback)
+      : event_callback_(std::move(event_callback)),
+        instance_id_driver_(instance_id_driver),
+        instance_id_(instance_id_driver->GetInstanceID(kFCMAppId)) {
+    gcm::GCMDriver* const gcm_driver = instance_id_->gcm_driver();
+    CHECK(gcm_driver->GetAppHandler(kFCMAppId) == nullptr);
+    instance_id_->gcm_driver()->AddAppHandler(kFCMAppId, this);
+
+    instance_id_->GetToken(
+        kFCMSenderId, instance_id::kGCMScope,
+        /*time_to_live=*/base::TimeDelta(), /*options=*/{},
+        /*flags=*/{},
+        base::BindOnce(&FCMHandler::GetTokenComplete, base::Unretained(this)));
+  }
+
+  ~FCMHandler() override {
+    instance_id_->gcm_driver()->RemoveAppHandler(kFCMAppId);
+    instance_id_driver_->RemoveInstanceID(kFCMAppId);
+  }
+
+  // Registration:
+
+  base::Optional<std::vector<uint8_t>> contact_id() const override {
+    if (!registration_token_) {
+      return base::nullopt;
+    }
+    return std::vector<uint8_t>(registration_token_->begin(),
+                                registration_token_->end());
+  }
+
+  // GCMAppHandler:
+
+  void OnMessage(const std::string& app_id,
+                 const gcm::IncomingMessage& message) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK_EQ(app_id, kFCMAppId);
+    DCHECK_EQ(message.sender_id, kFCMSenderId);
+
+    if (app_id != kFCMAppId || message.sender_id != kFCMSenderId) {
+      FIDO_LOG(ERROR) << "Discarding FCM message from " << message.sender_id;
+      return;
+    }
+
+    base::Optional<std::unique_ptr<Registration::Event>> event =
+        MessageToEvent(message.data);
+    if (!event) {
+      FIDO_LOG(ERROR) << "Failed to decode FCM message. Ignoring.";
+      return;
+    }
+
+    event_callback_.Run(std::move(*event));
+  }
+
+  void ShutdownHandler() override {}
+  void OnStoreReset() override {}
+  void OnMessagesDeleted(const std::string& app_id) override {}
+  void OnSendError(
+      const std::string& app_id,
+      const gcm::GCMClient::SendErrorDetails& send_error_details) override {}
+  void OnSendAcknowledged(const std::string& app_id,
+                          const std::string& message_id) override {}
+  void OnMessageDecryptionFailed(const std::string& app_id,
+                                 const std::string& message_id,
+                                 const std::string& error_message) override {}
+  bool CanHandle(const std::string& app_id) const override { return false; }
+
+ private:
+  void GetTokenComplete(const std::string& token,
+                        instance_id::InstanceID::Result result) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    if (result != instance_id::InstanceID::SUCCESS) {
+      FIDO_LOG(ERROR) << "Getting FCM token failed: "
+                      << static_cast<int>(result);
+      return;
+    }
+
+    FIDO_LOG(ERROR) << __func__ << " " << token;
+    registration_token_ = token;
+  }
+
+  static base::Optional<std::unique_ptr<Registration::Event>> MessageToEvent(
+      const gcm::MessageData& data) {
+    auto event = std::make_unique<Registration::Event>();
+    gcm::MessageData::const_iterator it = data.find("caBLE.tunnelID");
+    if (it == data.end() ||
+        !base::HexStringToSpan(it->second, event->tunnel_id)) {
+      return base::nullopt;
+    }
+
+    it = data.find("caBLE.routingID");
+    if (it == data.end() ||
+        !base::HexStringToSpan(it->second, event->routing_id)) {
+      return base::nullopt;
+    }
+
+    std::vector<uint8_t> payload_bytes;
+    it = data.find("caBLE.clientPayload");
+    if (it == data.end() ||
+        !base::HexStringToBytes(it->second, &payload_bytes)) {
+      return base::nullopt;
+    }
+
+    base::Optional<cbor::Value> payload = cbor::Reader::Read(payload_bytes);
+    if (!payload || !payload->is_map()) {
+      return base::nullopt;
+    }
+
+    const cbor::Value::MapValue& map = payload->GetMap();
+    cbor::Value::MapValue::const_iterator cbor_it = map.find(cbor::Value(1));
+    if (cbor_it == map.end() || !cbor_it->second.is_bytestring()) {
+      return base::nullopt;
+    }
+    event->pairing_id = cbor_it->second.GetBytestring();
+
+    if (!fido_parsing_utils::CopyCBORBytestring(&event->client_nonce, map, 2)) {
+      return base::nullopt;
+    }
+
+    return event;
+  }
+
+  base::RepeatingCallback<void(std::unique_ptr<Registration::Event>)>
+      event_callback_;
+  instance_id::InstanceIDDriver* const instance_id_driver_;
+  instance_id::InstanceID* const instance_id_;
+  base::Optional<std::string> registration_token_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+}  // namespace
+
+Registration::~Registration() = default;
+Registration::Event::Event() = default;
+Registration::Event::~Event() = default;
+
+std::unique_ptr<Registration> Register(
+    instance_id::InstanceIDDriver* instance_id_driver,
+    base::RepeatingCallback<void(std::unique_ptr<Registration::Event>)>
+        event_callback) {
+  return std::make_unique<FCMHandler>(instance_id_driver,
+                                      std::move(event_callback));
+}
+
+}  // namespace authenticator
+}  // namespace cablev2
+}  // namespace device
diff --git a/device/fido/cable/v2_registration.h b/device/fido/cable/v2_registration.h
new file mode 100644
index 0000000..90461edc
--- /dev/null
+++ b/device/fido/cable/v2_registration.h
@@ -0,0 +1,64 @@
+// 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 DEVICE_FIDO_CABLE_V2_REGISTRATION_H_
+#define DEVICE_FIDO_CABLE_V2_REGISTRATION_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/optional.h"
+#include "device/fido/cable/v2_constants.h"
+
+namespace instance_id {
+class InstanceIDDriver;
+}
+
+namespace device {
+namespace cablev2 {
+namespace authenticator {
+
+// Registration represents a subscription to events from the tunnel service.
+class Registration {
+ public:
+  // An Event contains the information sent by the tunnel service when a peer is
+  // trying to connect.
+  struct Event {
+    Event();
+    ~Event();
+    Event(const Event&) = delete;
+    Event& operator=(const Event&) = delete;
+
+    std::array<uint8_t, kTunnelIdSize> tunnel_id;
+    std::array<uint8_t, kRoutingIdSize> routing_id;
+    std::vector<uint8_t> pairing_id;
+    std::array<uint8_t, kClientNonceSize> client_nonce;
+  };
+
+  virtual ~Registration();
+
+  // contact_id returns an opaque token that may be placed in pairing data for
+  // desktops to later connect to. |nullopt| will be returned if the value is
+  // not yet ready.
+  virtual base::Optional<std::vector<uint8_t>> contact_id() const = 0;
+};
+
+// Register subscribes to the tunnel service and returns a |Registration|. This
+// should only be called once in an address space. Subsequent calls may CHECK.
+// The |event_callback| is called, on the same thread, whenever a paired device
+// requests a tunnel.
+std::unique_ptr<Registration> Register(
+    instance_id::InstanceIDDriver* instance_id_driver,
+    base::RepeatingCallback<void(std::unique_ptr<Registration::Event>)>
+        event_callback);
+
+}  // namespace authenticator
+}  // namespace cablev2
+}  // namespace device
+
+#endif  // DEVICE_FIDO_CABLE_V2_REGISTRATION_H_
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index 5a2f428..cc8916b 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -8,12 +8,33 @@
   import("//build/config/android/rules.gni")  # For generate_jni().
 }
 
+config("vr_gl_mode") {
+  if (use_command_buffer) {
+    defines = [
+      "VR_USE_COMMAND_BUFFER",
+      "GL_GLEXT_PROTOTYPES",
+    ]
+  } else {
+    defines = [ "VR_USE_NATIVE_GL" ]
+  }
+}
+
+source_set("vr_gl_bindings") {
+  sources = [ "gl_bindings.h" ]
+  public_configs = [ ":vr_gl_mode" ]
+  if (use_command_buffer) {
+    public_deps = [ "//gpu/command_buffer/client:gles2_c_lib" ]
+  } else {
+    public_deps = [ "//ui/gl" ]
+  }
+}
+
 if (enable_vr) {
   # TODO(https://crbug.com/1073113): Flesh out and cleanup this target.
   component("vr_base") {
     visibility = [
       # TODO(https://crbug.com/843374): Move arcore_device
-      "//chrome/browser/android/vr/*",
+      "//chrome/browser/*",
       "//content/services/isolated_xr_device/*",
       "//device/vr/*",
     ]
@@ -25,9 +46,13 @@
       "vr_device.h",
       "vr_device_base.cc",
       "vr_device_base.h",
+      "vr_gl_util.cc",
+      "vr_gl_util.h",
     ]
 
+    public_configs = [ ":vr_gl_mode" ]
     public_deps = [
+      ":vr_gl_bindings",
       "//device/vr/public/cpp",
       "//device/vr/public/mojom",
     ]
diff --git a/device/vr/DEPS b/device/vr/DEPS
index 7f06383..96654db3 100644
--- a/device/vr/DEPS
+++ b/device/vr/DEPS
@@ -9,6 +9,8 @@
   "+third_party/gvr-android-sdk/src",
   "+third_party/libovr/src",
   "+third_party/openvr/src/headers/openvr.h",
+  "+third_party/skia/include/core/SkColor.h",
   "+ui/display",
   "+ui/gfx",
+  "+ui/gl/gl_bindings.h",
 ]
diff --git a/device/vr/buildflags/buildflags.gni b/device/vr/buildflags/buildflags.gni
index d85ca83..21cce4e 100644
--- a/device/vr/buildflags/buildflags.gni
+++ b/device/vr/buildflags/buildflags.gni
@@ -14,6 +14,8 @@
 
   enable_windows_mr = is_win
 
+  use_command_buffer = is_win
+
   # To build with OpenXR support, the OpenXR Loader needs to be pulled to
   # third_party/openxr.
   enable_openxr = checkout_openxr && is_win
diff --git a/chrome/browser/vr/gl_bindings.h b/device/vr/gl_bindings.h
similarity index 80%
rename from chrome/browser/vr/gl_bindings.h
rename to device/vr/gl_bindings.h
index 3f44e6ad..b727fdd4 100644
--- a/chrome/browser/vr/gl_bindings.h
+++ b/device/vr/gl_bindings.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_VR_GL_BINDINGS_H_
-#define CHROME_BROWSER_VR_GL_BINDINGS_H_
+#ifndef DEVICE_VR_GL_BINDINGS_H_
+#define DEVICE_VR_GL_BINDINGS_H_
 
 #if defined(VR_USE_COMMAND_BUFFER)
 
@@ -23,4 +23,4 @@
 
 #endif  // defined(VR_USE_COMMAND_BUFFER)
 
-#endif  // CHROME_BROWSER_VR_GL_BINDINGS_H_
+#endif  // DEVICE_VR_GL_BINDINGS_H_
diff --git a/chrome/browser/vr/vr_gl_util.cc b/device/vr/vr_gl_util.cc
similarity index 98%
rename from chrome/browser/vr/vr_gl_util.cc
rename to device/vr/vr_gl_util.cc
index 4fa8799..c289b69 100644
--- a/chrome/browser/vr/vr_gl_util.cc
+++ b/device/vr/vr_gl_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/vr/vr_gl_util.h"
+#include "device/vr/vr_gl_util.h"
 
 #include "ui/gfx/transform.h"
 
diff --git a/device/vr/vr_gl_util.h b/device/vr/vr_gl_util.h
new file mode 100644
index 0000000..e84543df
--- /dev/null
+++ b/device/vr/vr_gl_util.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef DEVICE_VR_VR_GL_UTIL_H_
+#define DEVICE_VR_VR_GL_UTIL_H_
+
+#include <array>
+#include <string>
+
+#include "base/component_export.h"
+#include "device/vr/gl_bindings.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+#define SHADER(Src) "#version 100\n" #Src
+#define OEIE_SHADER(Src) \
+  "#version 100\n#extension GL_OES_EGL_image_external : require\n" #Src
+#define VOID_OFFSET(x) reinterpret_cast<void*>(x)
+
+namespace gfx {
+class Transform;
+}  // namespace gfx
+
+namespace vr {
+
+COMPONENT_EXPORT(DEVICE_VR_BASE)
+std::array<float, 16> MatrixToGLArray(const gfx::Transform& matrix);
+
+// Compile a shader. This is intended for browser-internal shaders only,
+// don't use this for user-supplied arbitrary shaders since data is handed
+// directly to the GL driver without further sanity checks.
+COMPONENT_EXPORT(DEVICE_VR_BASE)
+GLuint CompileShader(GLenum shader_type,
+                     const std::string& shader_source,
+                     std::string& error);
+
+// Compile and link a program.
+COMPONENT_EXPORT(DEVICE_VR_BASE)
+GLuint CreateAndLinkProgram(GLuint vertex_shader_handle,
+                            GLuint fragment_shader_handle,
+                            std::string& error);
+
+// Sets default texture parameters given a texture type.
+COMPONENT_EXPORT(DEVICE_VR_BASE) void SetTexParameters(GLenum texture_type);
+
+// Sets color uniforms given an SkColor.
+COMPONENT_EXPORT(DEVICE_VR_BASE) void SetColorUniform(GLuint handle, SkColor c);
+
+// Sets color uniforms (but not alpha) given an SkColor. The alpha is assumed to
+// be 1.0 in this case.
+COMPONENT_EXPORT(DEVICE_VR_BASE)
+void SetOpaqueColorUniform(GLuint handle, SkColor c);
+
+}  // namespace vr
+
+#endif  // DEVICE_VR_VR_GL_UTIL_H_
diff --git a/extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc b/extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc
index bc6ec53..2c131ec9 100644
--- a/extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc
+++ b/extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc
@@ -32,8 +32,8 @@
     return;
   }
 
-  auto factory = content::CreateWebUIURLLoader(rfh, url_.scheme(),
-                                               base::flat_set<std::string>());
+  mojo::Remote<network::mojom::URLLoaderFactory> factory(
+      content::CreateWebUIURLLoaderFactory(rfh, url_.scheme(), {}));
 
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("webui_content_scripts_download", R"(
diff --git a/extensions/common/api/incognito.json b/extensions/common/api/incognito.json
new file mode 100644
index 0000000..6a1dd29
--- /dev/null
+++ b/extensions/common/api/incognito.json
@@ -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.
+
+[
+  {
+    "namespace": "incognito",
+    "description": "Dummy namepsace for the incognito manifest key.",
+    "types": [
+      {
+        "id": "IncognitoMode",
+        "type": "string",
+        "enum": ["split", "spanning", "not_allowed"]
+      }
+    ],
+    "manifest_keys": {
+      "incognito": {
+        "description": "Configures whether and how the extension runs in incognito mode. Default mode is <code>spanning</code>.",
+        "$ref": "IncognitoMode",
+        "optional": true
+      }
+    }
+  }
+]
diff --git a/extensions/common/api/schema.gni b/extensions/common/api/schema.gni
index 9dd1b9c..658da015 100644
--- a/extensions/common/api/schema.gni
+++ b/extensions/common/api/schema.gni
@@ -31,6 +31,7 @@
   "management.json",
   "hid.idl",
   "idle.json",
+  "incognito.json",
   "metrics_private.json",
   "mime_handler_private.idl",
   "mime_handler_view_guest_internal.json",
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index e6058882..ea38ac40 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -74,7 +74,6 @@
 const char kId[] = "id";
 const char kImeOptionsPage[] = "options_page";
 const char kImport[] = "import";
-const char kIncognito[] = "incognito";
 const char kIncludeGlobs[] = "include_globs";
 const char kIndicator[] = "indicator";
 const char kInputComponents[] = "input_components";
@@ -475,8 +474,6 @@
     "Invalid value for 'import[*].id'.";
 const char kInvalidImportVersion[] =
     "Invalid value for 'import[*].minimum_version'.";
-const char kInvalidIncognitoBehavior[] =
-    "Invalid value for 'incognito'.";
 const char kInvalidInputComponents[] =
     "Invalid value for 'input_components'";
 const char kInvalidInputComponentDescription[] =
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index 3406644..5ad6f7ad 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -75,7 +75,6 @@
 extern const char kId[];
 extern const char kImeOptionsPage[];
 extern const char kImport[];
-extern const char kIncognito[];
 extern const char kIncludeGlobs[];
 extern const char kIndicator[];
 extern const char kInputComponents[];
@@ -370,7 +369,6 @@
 extern const char kInvalidImportAndExport[];
 extern const char kInvalidImportId[];
 extern const char kInvalidImportVersion[];
-extern const char kInvalidIncognitoBehavior[];
 extern const char kInvalidInputComponents[];
 extern const char kInvalidInputComponentDescription[];
 extern const char kInvalidInputComponentLayoutName[];
diff --git a/extensions/common/manifest_handlers/incognito_info.cc b/extensions/common/manifest_handlers/incognito_info.cc
index 6eac595..33fa964 100644
--- a/extensions/common/manifest_handlers/incognito_info.cc
+++ b/extensions/common/manifest_handlers/incognito_info.cc
@@ -6,71 +6,64 @@
 
 #include <memory>
 
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/manifest_constants.h"
 
 namespace extensions {
 
-namespace keys = manifest_keys;
+using IncognitoManifestKeys = api::incognito::ManifestKeys;
 
-IncognitoInfo::IncognitoInfo(Mode mode) : mode(mode) {}
-
-IncognitoInfo::~IncognitoInfo() {
+IncognitoInfo::IncognitoInfo(api::incognito::IncognitoMode mode) : mode(mode) {
+  DCHECK_NE(api::incognito::INCOGNITO_MODE_NONE, mode);
 }
 
+IncognitoInfo::~IncognitoInfo() = default;
+
 // static
 bool IncognitoInfo::IsSplitMode(const Extension* extension) {
   IncognitoInfo* info = static_cast<IncognitoInfo*>(
-      extension->GetManifestData(keys::kIncognito));
-  return info ? info->mode == Mode::SPLIT : false;
+      extension->GetManifestData(IncognitoManifestKeys::kIncognito));
+  return info ? info->mode == api::incognito::INCOGNITO_MODE_SPLIT : false;
 }
 
 // static
 bool IncognitoInfo::IsIncognitoAllowed(const Extension* extension) {
-  IncognitoInfo* info =
-      static_cast<IncognitoInfo*>(extension->GetManifestData(keys::kIncognito));
-  return info ? info->mode != Mode::NOT_ALLOWED : true;
+  IncognitoInfo* info = static_cast<IncognitoInfo*>(
+      extension->GetManifestData(IncognitoManifestKeys::kIncognito));
+  return info ? info->mode != api::incognito::INCOGNITO_MODE_NOT_ALLOWED : true;
 }
 
-IncognitoHandler::IncognitoHandler() {
-}
-
-IncognitoHandler::~IncognitoHandler() {
-}
+IncognitoHandler::IncognitoHandler() = default;
+IncognitoHandler::~IncognitoHandler() = default;
 
 bool IncognitoHandler::Parse(Extension* extension, base::string16* error) {
-  // Extensions and Chrome apps default to spanning mode.
-  // Hosted and legacy packaged apps default to split mode.
-  IncognitoInfo::Mode mode =
+  // Extensions and Chrome apps default to spanning mode. Hosted and legacy
+  // packaged apps default to split mode.
+  api::incognito::IncognitoMode default_mode =
       extension->is_hosted_app() || extension->is_legacy_packaged_app()
-          ? IncognitoInfo::Mode::SPLIT
-          : IncognitoInfo::Mode::SPANNING;
-  if (!extension->manifest()->HasKey(keys::kIncognito)) {
-    extension->SetManifestData(keys::kIncognito,
-                               std::make_unique<IncognitoInfo>(mode));
+          ? api::incognito::INCOGNITO_MODE_SPLIT
+          : api::incognito::INCOGNITO_MODE_SPANNING;
+
+  // This check is necessary since the "incognito" manifest key may not be
+  // available to the extension.
+  if (!extension->manifest()->HasKey(IncognitoManifestKeys::kIncognito)) {
+    extension->SetManifestData(IncognitoManifestKeys::kIncognito,
+                               std::make_unique<IncognitoInfo>(default_mode));
     return true;
   }
 
-  std::string incognito_string;
-  if (!extension->manifest()->GetString(keys::kIncognito, &incognito_string)) {
-    *error = base::ASCIIToUTF16(manifest_errors::kInvalidIncognitoBehavior);
+  IncognitoManifestKeys manifest_keys;
+  if (!IncognitoManifestKeys::ParseFromDictionary(
+          *extension->manifest()->value(), &manifest_keys, error)) {
     return false;
   }
 
-  if (incognito_string == manifest_values::kIncognitoSplit) {
-    mode = IncognitoInfo::Mode::SPLIT;
-  } else if (incognito_string == manifest_values::kIncognitoSpanning) {
-    mode = IncognitoInfo::Mode::SPANNING;
-  } else if (incognito_string == manifest_values::kIncognitoNotAllowed) {
-    mode = IncognitoInfo::Mode::NOT_ALLOWED;
-  } else {
-    *error = base::ASCIIToUTF16(manifest_errors::kInvalidIncognitoBehavior);
-    return false;
-  }
+  api::incognito::IncognitoMode mode = manifest_keys.incognito;
 
-  extension->SetManifestData(keys::kIncognito,
+  // This will be the case if the manifest key was omitted.
+  if (mode == api::incognito::INCOGNITO_MODE_NONE)
+    mode = default_mode;
+
+  extension->SetManifestData(IncognitoManifestKeys::kIncognito,
                              std::make_unique<IncognitoInfo>(mode));
   return true;
 }
@@ -80,7 +73,7 @@
 }
 
 base::span<const char* const> IncognitoHandler::Keys() const {
-  static constexpr const char* kKeys[] = {keys::kIncognito};
+  static constexpr const char* kKeys[] = {IncognitoManifestKeys::kIncognito};
   return kKeys;
 }
 
diff --git a/extensions/common/manifest_handlers/incognito_info.h b/extensions/common/manifest_handlers/incognito_info.h
index 234936e..19dcd033 100644
--- a/extensions/common/manifest_handlers/incognito_info.h
+++ b/extensions/common/manifest_handlers/incognito_info.h
@@ -7,23 +7,19 @@
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "extensions/common/api/incognito.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_handler.h"
 
 namespace extensions {
 
 struct IncognitoInfo : public Extension::ManifestData {
-  enum Mode { SPLIT, SPANNING, NOT_ALLOWED };
-
-  explicit IncognitoInfo(Mode mode);
-
+  explicit IncognitoInfo(api::incognito::IncognitoMode mode);
   ~IncognitoInfo() override;
 
-  // If true, a separate process will be used for the extension in incognito
-  // mode.
-  Mode mode;
+  api::incognito::IncognitoMode mode;
 
-  // Return the incognito mode information for the given |extension|.
+  // Return whether the |extension| should run in split incognito mode.
   static bool IsSplitMode(const Extension* extension);
 
   // Return whether this extension can be run in incognito mode as specified
diff --git a/google_apis/google_api_keys.cc b/google_apis/google_api_keys.cc
index 327f894..917edc5 100644
--- a/google_apis/google_api_keys.cc
+++ b/google_apis/google_api_keys.cc
@@ -95,6 +95,12 @@
 #define GOOGLE_API_KEY_SODA DUMMY_API_TOKEN
 #endif
 
+// API key for the DevTools frontend to use for surveys. Note there is no
+// public API to replace this functionality.
+#if !defined(GOOGLE_API_KEY_DEVTOOLS_SURVEYS)
+#define GOOGLE_API_KEY_DEVTOOLS_SURVEYS DUMMY_API_TOKEN
+#endif
+
 // These are used as shortcuts for developers and users providing
 // OAuth credentials via preprocessor defines or environment
 // variables.  If set, they will be used to replace any of the client
@@ -147,6 +153,11 @@
         GOOGLE_API_KEY_SODA, STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_SODA),
         nullptr, std::string(), environment.get(), command_line, gaia_config);
 
+    api_key_devtools_surveys_ = CalculateKeyValue(
+        GOOGLE_API_KEY_DEVTOOLS_SURVEYS,
+        STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_DEVTOOLS_SURVEYS), nullptr,
+        std::string(), environment.get(), command_line, gaia_config);
+
     metrics_key_ = CalculateKeyValue(
         GOOGLE_METRICS_SIGNING_KEY,
         STRINGIZE_NO_EXPANSION(GOOGLE_METRICS_SIGNING_KEY), nullptr,
@@ -213,6 +224,9 @@
   std::string api_key_remoting() const { return api_key_remoting_; }
   std::string api_key_sharing() const { return api_key_sharing_; }
   std::string api_key_soda() const { return api_key_soda_; }
+  std::string api_key_devtools_surveys() const {
+    return api_key_devtools_surveys_;
+  }
 
   std::string metrics_key() const { return metrics_key_; }
 
@@ -324,6 +338,7 @@
   std::string api_key_remoting_;
   std::string api_key_sharing_;
   std::string api_key_soda_;
+  std::string api_key_devtools_surveys_;
   std::string metrics_key_;
   std::string client_ids_[CLIENT_NUM_ITEMS];
   std::string client_secrets_[CLIENT_NUM_ITEMS];
@@ -356,6 +371,10 @@
   return g_api_key_cache.Get().api_key_soda();
 }
 
+std::string GetDevtoolsSurveysAPIKey() {
+  return g_api_key_cache.Get().api_key_devtools_surveys();
+}
+
 #if defined(OS_IOS)
 void SetAPIKey(const std::string& api_key) {
   g_api_key_cache.Get().set_api_key(api_key);
diff --git a/google_apis/google_api_keys.h b/google_apis/google_api_keys.h
index 6d999ab..5297c066 100644
--- a/google_apis/google_api_keys.h
+++ b/google_apis/google_api_keys.h
@@ -83,6 +83,10 @@
 // Retrieves the Speech On-Device API (SODA) API Key.
 std::string GetSodaAPIKey();
 
+// Retrieves the DevTools Survey API Key. Note there is no public API to replace
+// this functionality.
+std::string GetDevtoolsSurveysAPIKey();
+
 #if defined(OS_IOS)
 // Sets the API key. This should be called as early as possible before this
 // API key is even accessed.
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 3f6c524..6c762e5 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -9397,6 +9397,35 @@
       }
     }
     builders {
+      name: "chromeos-amd64-generic-lacros-dbg"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.chromiumos\",\"recipe\":\"chromium\"}"
+      execution_timeout_secs: 10800
+      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: "chromeos-amd64-generic-lacros-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index 6bb8463..5ef2df63 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -336,6 +336,11 @@
     short_name: "lcr"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-lacros-dbg"
+    category: "chromium.chromiumos|lacros|x64"
+    short_name: "dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/chromeos-arm-generic-rel"
     category: "chromium.chromiumos|simple|release"
     short_name: "arm"
@@ -1611,6 +1616,11 @@
     short_name: "lcr"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-lacros-dbg"
+    category: "chromium.chromiumos|lacros|x64"
+    short_name: "dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/chromeos-arm-generic-rel"
     category: "chromium.chromiumos|simple|release"
     short_name: "arm"
@@ -4926,6 +4936,11 @@
     short_name: "lcr"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-lacros-dbg"
+    category: "lacros|x64"
+    short_name: "dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/chromeos-arm-generic-rel"
     category: "simple|release"
     short_name: "arm"
@@ -12138,12 +12153,30 @@
   id: "luci.chromium.try"
   name: "luci.chromium.try"
   builders {
+    name: "buildbucket/luci.chromium.try/android-10-arm64-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android-asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android-bfcache-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android-binary-size"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-cronet-arm-dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.try/android-cronet-marshmallow-arm64-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-deterministic-dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android-deterministic-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-lollipop-arm-rel"
   }
   builders {
@@ -12156,67 +12189,31 @@
     name: "buildbucket/luci.chromium.try/android-marshmallow-x86-rel-non-cq"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-official"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android-opus-arm-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-oreo-arm64-cts-networkservice-dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-oreo-arm64-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-pie-arm64-coverage-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-pie-arm64-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-pie-arm64-rel"
   }
   builders {
     name: "buildbucket/luci.chromium.try/android-pie-x86-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-10-arm64-rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android-webview-pie-arm64-fyi-rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_archive_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_arm64_dbg_recipe"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_angle_deqp_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_angle_vk32_deqp_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_angle_vk64_deqp_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_angle_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_angle_vk32_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_angle_vk64_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_cfi_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_compile_dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_compile_x64_dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_compile_x86_dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_mojo"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_n5x_swarming_dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android_optional_gpu_tests_rel"
+    name: "buildbucket/luci.chromium.try/android-weblayer-pie-x86-fyi-rel"
   }
   builders {
     name: "buildbucket/luci.chromium.try/android-webview-marshmallow-arm64-dbg"
@@ -12231,6 +12228,69 @@
     name: "buildbucket/luci.chromium.try/android-webview-pie-arm64-dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-webview-pie-arm64-fyi-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_angle_deqp_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_angle_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_angle_vk32_deqp_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_angle_vk32_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_angle_vk64_deqp_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_angle_vk64_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_archive_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_arm64_dbg_recipe"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_blink_rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_cfi_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_clang_dbg_recipe"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_compile_dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_compile_x64_dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_compile_x86_dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_cronet"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_mojo"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_n5x_swarming_dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_optional_gpu_tests_rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/android_unswarmed_pixel_aosp"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/cast_shell_android"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/cast_shell_audio_linux"
   }
   builders {
@@ -12264,12 +12324,57 @@
     name: "buildbucket/luci.chromium.try/closure_compilation"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/dawn-linux-x64-deps-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/dawn-mac-x64-deps-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/dawn-try-win10-x64-asan-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/dawn-try-win10-x86-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/dawn-win10-x64-deps-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/dawn-win10-x86-deps-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/fuchsia-angle-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-arm64-cast"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/fuchsia-compile-x64-dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-fyi-arm64-dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-fyi-arm64-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-fyi-x64-dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-fyi-x64-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-official"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/fuchsia-x64-cast"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/fuchsia_arm64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/fuchsia_x64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-l-nexus-5-32"
   }
   builders {
@@ -12312,6 +12417,12 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-vk-64"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-chromeos-amd64-generic"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-chromeos-kevin"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-intel-dqp"
   }
   builders {
@@ -12321,6 +12432,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-intel-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-intel-sk-dawn-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-intel-skv"
   }
   builders {
@@ -12342,51 +12456,6 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-nvidia-tsn"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-amd-dbg-32"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-amd-dqp-32"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-amd-rel-32"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-nvidia-dqp-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-nvidia-rel-32"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-nvidia-rel-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-intel-dqp-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-intel-exp-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-intel-rel-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dbg-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dqp-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-exp-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-rel-32"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-rel-64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-skgl-64"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-mac-amd-dqp"
   }
   builders {
@@ -12429,6 +12498,60 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-mac-nvidia-retina-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-intel-dqp-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-intel-exp-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-intel-rel-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dbg-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dqp-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dx12vk-dbg-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dx12vk-rel-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-exp-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-rel-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-rel-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-sk-dawn-rel-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-skgl-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-amd-dbg-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-amd-dqp-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-amd-rel-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-nvidia-dqp-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-nvidia-rel-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win7-nvidia-rel-64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-try-android-m-nexus-5x-64"
   }
   builders {
@@ -12444,30 +12567,165 @@
     name: "buildbucket/luci.chromium.try/gpu-try-mac-intel-dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-try-win10-nvidia-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-device"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-simulator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-simulator-code-coverage"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-simulator-cr-recipe"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-simulator-cronet"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-simulator-eg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-simulator-full-configs"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-simulator-multi-window"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios-simulator-noncq"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios13-beta-simulator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios13-sdk-simulator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios14-beta-simulator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios14-sdk-simulator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/layout_test_leak_detection"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/leak_detection_linux"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-angle-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-annotator-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-autofill-assistant"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-bfcache-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-blink-heap-concurrent-marking-tsan-rel"
   }
   builders {
     name: "buildbucket/luci.chromium.try/linux-blink-heap-verification-try"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-blink-optional-highdpi-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-blink-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-chromeos-compile-dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-chromeos-dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-chromeos-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-clang-tidy-dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-clang-tidy-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-dawn-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-dcheck-off-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-gcc-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-inverse-fieldtrials-fyi-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-lacros-fyi-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-lacros-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-layout-tests-edit-ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-layout-tests-fragment-item"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-libfuzzer-asan-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-official"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-ozone-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-perfetto-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-swangle-chromium-try-x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-swangle-try-tot-angle-x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-swangle-try-tot-angle-x86"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-swangle-try-tot-swiftshader-x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-swangle-try-tot-swiftshader-x86"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-swangle-try-x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-swangle-try-x86"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-trusty-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-viz-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-webkit-msan-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/linux-wpt-fyi-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux_android_dbg_ng"
   }
   builders {
@@ -12510,15 +12768,6 @@
     name: "buildbucket/luci.chromium.try/linux_chromium_compile_rel_ng"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-clang-tidy-dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/linux-clang-tidy-rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/linux-rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux_chromium_dbg_ng"
   }
   builders {
@@ -12531,18 +12780,9 @@
     name: "buildbucket/luci.chromium.try/linux_chromium_ubsan_rel_ng"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-lacros-fyi-rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux_layout_tests_composite_after_paint"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-layout-tests-edit-ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/linux-layout-tests-fragment-item"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux_layout_tests_layout_ng_disabled"
   }
   builders {
@@ -12558,39 +12798,48 @@
     name: "buildbucket/luci.chromium.try/linux_upload_clang"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-viz-rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/linux-wpt-fyi-rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/network_service_linux"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/ios-device"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/ios-simulator"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/ios-simulator-cronet"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/ios-simulator-eg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/ios13-sdk-simulator"
+    name: "buildbucket/luci.chromium.try/linux_vr"
   }
   builders {
     name: "buildbucket/luci.chromium.try/mac-angle-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/mac-rel"
+    name: "buildbucket/luci.chromium.try/mac-arm64-rel"
   }
   builders {
     name: "buildbucket/luci.chromium.try/mac-coverage-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/mac-dawn-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac-official"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac-osxbeta-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac-swangle-chromium-try-x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac10.12-blink-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac10.13-blink-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac10.14-blink-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac10.15-blink-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/mac11.0-blink-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/mac_chromium_10.10"
   }
   builders {
@@ -12606,6 +12855,9 @@
     name: "buildbucket/luci.chromium.try/mac_chromium_10.15_rel_ng"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/mac_chromium_11.0_rel_ng"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/mac_chromium_archive_rel_ng"
   }
   builders {
@@ -12627,33 +12879,15 @@
     name: "buildbucket/luci.chromium.try/mac_upload_clang"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/network_service_linux"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/tricium-metrics-analysis"
   }
   builders {
     name: "buildbucket/luci.chromium.try/try-nougat-phone-tester"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-oreo-arm64-dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android-pie-arm64-dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/win7-rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/win10_chromium_x64_dbg_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/win10_chromium_x64_rel_ng"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/win10_chromium_x64_rel_ng_exp"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/win10_chromium_x64_1909_fyi_rel_ng"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/win-angle-deqp-rel-32"
   }
   builders {
@@ -12666,6 +12900,69 @@
     name: "buildbucket/luci.chromium.try/win-angle-rel-64"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/win-annotator-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-celab-try-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-dawn-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-libfuzzer-asan-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-official"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-swangle-chromium-try-x86"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-swangle-try-tot-angle-x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-swangle-try-tot-angle-x86"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-swangle-try-tot-swiftshader-x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-swangle-try-tot-swiftshader-x86"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-swangle-try-x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win-swangle-try-x86"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win10-blink-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win10_chromium_x64_1909_fyi_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win10_chromium_x64_dbg_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win10_chromium_x64_rel_ng"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win10_chromium_x64_rel_ng_exp"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win32-official"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win7-blink-rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/win7-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/win_archive"
   }
   builders {
@@ -12692,9 +12989,6 @@
   builders {
     name: "buildbucket/luci.chromium.try/win_x64_archive"
   }
-  builders {
-    name: "buildbucket/luci.chromium.try/win-celab-try-rel"
-  }
   builder_view_only: true
 }
 consoles {
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index 1d6b86b..d67cd575 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -4667,6 +4667,16 @@
   }
 }
 job {
+  id: "chromeos-amd64-generic-lacros-dbg"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "chromeos-amd64-generic-lacros-dbg"
+  }
+}
+job {
   id: "chromeos-amd64-generic-lacros-rel"
   realm: "ci"
   acl_sets: "ci"
@@ -8605,6 +8615,16 @@
   }
 }
 job {
+  id: "ci-chromeos-amd64-generic-lacros-dbg"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "chromeos-amd64-generic-lacros-dbg"
+  }
+}
+job {
   id: "ci-chromeos-amd64-generic-lacros-rel"
   realm: "ci"
   acl_sets: "ci"
@@ -12396,6 +12416,7 @@
   triggers: "chromeos-amd64-generic-asan-rel"
   triggers: "chromeos-amd64-generic-cfi-thin-lto-rel"
   triggers: "ci-chromeos-amd64-generic-dbg"
+  triggers: "chromeos-amd64-generic-lacros-dbg"
   triggers: "chromeos-amd64-generic-lacros-rel"
   triggers: "ci-chromeos-amd64-generic-rel"
   triggers: "chromeos-arm-generic-dbg"
diff --git a/infra/config/lib/try.star b/infra/config/lib/try.star
index c6a2c483..69f09a8 100644
--- a/infra/config/lib/try.star
+++ b/infra/config/lib/try.star
@@ -36,6 +36,7 @@
     cq_group = None,
     list_view = args.COMPUTE,
     main_list_view = None,
+    subproject_list_view = None,
 )
 
 def declare_bucket(milestone_vars, *, branch_selector = branches.MAIN_ONLY):
@@ -205,6 +206,7 @@
         cq_group = args.DEFAULT,
         list_view = args.DEFAULT,
         main_list_view = args.DEFAULT,
+        subproject_list_view = args.DEFAULT,
         tryjob = None,
         **kwargs):
     """Define a try builder.
@@ -227,7 +229,11 @@
       main_console_view - A string identifying the ID of the main list
         view to add an entry to. Supports a module-level default that
         defaults to None. Note that `add_to_list_view` has no effect on
-        creating an entry to the main list view.
+        adding an entry to the main list view.
+      subproject_list_view - A string identifying the ID of the
+        subproject list view to add an entry to. Suppoers a module-level
+        default that defaults to None. Not that `add_to_list_view` has
+        no effect on adding an entry to the subproject list view.
       tryjob - A struct containing the details of the tryjob verifier for the
         builder, obtained by calling the `tryjob` function.
     """
@@ -290,6 +296,13 @@
             list_view = main_list_view,
         )
 
+    subproject_list_view = defaults.get_value("subproject_list_view", subproject_list_view)
+    if subproject_list_view:
+        luci.list_view_entry(
+            builder = builder,
+            list_view = subproject_list_view,
+        )
+
 def blink_builder(*, name, goma_backend = None, **kwargs):
     return try_builder(
         name = name,
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 270a095..6f3ff3a 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -1196,6 +1196,19 @@
 )
 
 ci.chromiumos_builder(
+    name = "chromeos-amd64-generic-lacros-dbg",
+    branch_selector = branches.STANDARD_RELEASES,
+    console_view_entry = ci.console_view_entry(
+        category = "lacros|x64",
+        short_name = "dbg",
+    ),
+    cq_mirrors_console_view = settings.cq_mirrors_console_name,
+    main_console_view = settings.main_console_name,
+    # TODO(crbug.com/1121667) Enable tree closing when it's stable.
+    tree_closing = False,
+)
+
+ci.chromiumos_builder(
     name = "chromeos-amd64-generic-rel",
     branch_selector = branches.ALL_RELEASES,
     console_view_entry = ci.console_view_entry(
diff --git a/infra/config/subprojects/chromium/consoles/luci.chromium.try.star b/infra/config/subprojects/chromium/consoles/luci.chromium.try.star
deleted file mode 100644
index 10dc2c8..0000000
--- a/infra/config/subprojects/chromium/consoles/luci.chromium.try.star
+++ /dev/null
@@ -1,198 +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.
-
-luci.list_view(
-    name = "luci.chromium.try",
-    entries = [
-        "try/android-cronet-arm-dbg",
-        "try/android-cronet-marshmallow-arm64-rel",
-        "try/android-lollipop-arm-rel",
-        "try/android-marshmallow-arm64-rel",
-        "try/android-marshmallow-x86-rel",
-        "try/android-marshmallow-x86-rel-non-cq",
-        # TODO(crbug.com/1111436) Added it back once all Pixel 1s are flashed
-        # back to NJH47F
-        # "try/android-nougat-arm64-rel",
-        "try/android-oreo-arm64-cts-networkservice-dbg",
-        "try/android-pie-arm64-coverage-rel",
-        "try/android-pie-arm64-rel",
-        "try/android-pie-x86-rel",
-        "try/android-10-arm64-rel",
-        "try/android-webview-pie-arm64-fyi-rel",
-        "try/android_archive_rel_ng",
-        "try/android_arm64_dbg_recipe",
-        "try/android_angle_deqp_rel_ng",
-        "try/android_angle_vk32_deqp_rel_ng",
-        "try/android_angle_vk64_deqp_rel_ng",
-        "try/android_angle_rel_ng",
-        "try/android_angle_vk32_rel_ng",
-        "try/android_angle_vk64_rel_ng",
-        "try/android_cfi_rel_ng",
-        "try/android_compile_dbg",
-        "try/android_compile_x64_dbg",
-        "try/android_compile_x86_dbg",
-        "try/android_mojo",
-        "try/android_n5x_swarming_dbg",
-        "try/android_optional_gpu_tests_rel",
-        "try/android-webview-marshmallow-arm64-dbg",
-        "try/android-webview-nougat-arm64-dbg",
-        "try/android-webview-oreo-arm64-dbg",
-        "try/android-webview-pie-arm64-dbg",
-        "try/cast_shell_audio_linux",
-        "try/cast_shell_linux",
-        "try/chromeos-amd64-generic-cfi-thin-lto-rel",
-        "try/chromeos-amd64-generic-dbg",
-        "try/chromeos-amd64-generic-rel",
-        "try/chromeos-arm-generic-dbg",
-        "try/chromeos-arm-generic-rel",
-        "try/chromeos-kevin-compile-rel",
-        "try/chromeos-kevin-rel",
-        "try/chromium_presubmit",
-        "try/closure_compilation",
-        "try/fuchsia-angle-rel",
-        "try/fuchsia-compile-x64-dbg",
-        "try/gpu-fyi-try-android-l-nexus-5-32",
-        "try/gpu-fyi-try-android-l-nexus-6-32",
-        "try/gpu-fyi-try-android-m-nexus-5x-64",
-        "try/gpu-fyi-try-android-m-nexus-5x-deqp-64",
-        "try/gpu-fyi-try-android-m-nexus-5x-skgl-64",
-        "try/gpu-fyi-try-android-m-nexus-6p-64",
-        "try/gpu-fyi-try-android-m-nexus-9-64",
-        "try/gpu-fyi-try-android-n-nvidia-shield-tv-64",
-        "try/gpu-fyi-try-android-p-pixel-2-32",
-        "try/gpu-fyi-try-android-p-pixel-2-skv-32",
-        "try/gpu-fyi-try-android-q-pixel-2-deqp-vk-32",
-        "try/gpu-fyi-try-android-q-pixel-2-deqp-vk-64",
-        "try/gpu-fyi-try-android-q-pixel-2-vk-32",
-        "try/gpu-fyi-try-android-q-pixel-2-vk-64",
-        "try/gpu-fyi-try-linux-intel-dqp",
-        "try/gpu-fyi-try-linux-intel-exp",
-        "try/gpu-fyi-try-linux-intel-rel",
-        "try/gpu-fyi-try-linux-intel-skv",
-        "try/gpu-fyi-try-linux-nvidia-dbg",
-        "try/gpu-fyi-try-linux-nvidia-dqp",
-        "try/gpu-fyi-try-linux-nvidia-exp",
-        "try/gpu-fyi-try-linux-nvidia-rel",
-        "try/gpu-fyi-try-linux-nvidia-skv",
-        "try/gpu-fyi-try-linux-nvidia-tsn",
-        "try/gpu-fyi-try-win7-amd-dbg-32",
-        "try/gpu-fyi-try-win7-amd-dqp-32",
-        "try/gpu-fyi-try-win7-amd-rel-32",
-        "try/gpu-fyi-try-win7-nvidia-dqp-64",
-        "try/gpu-fyi-try-win7-nvidia-rel-32",
-        "try/gpu-fyi-try-win7-nvidia-rel-64",
-        "try/gpu-fyi-try-win10-intel-dqp-64",
-        "try/gpu-fyi-try-win10-intel-exp-64",
-        "try/gpu-fyi-try-win10-intel-rel-64",
-        "try/gpu-fyi-try-win10-nvidia-dbg-64",
-        "try/gpu-fyi-try-win10-nvidia-dqp-64",
-        "try/gpu-fyi-try-win10-nvidia-exp-64",
-        "try/gpu-fyi-try-win10-nvidia-rel-32",
-        "try/gpu-fyi-try-win10-nvidia-rel-64",
-        "try/gpu-fyi-try-win10-nvidia-skgl-64",
-        "try/gpu-fyi-try-mac-amd-dqp",
-        "try/gpu-fyi-try-mac-amd-pro-rel",
-        "try/gpu-fyi-try-mac-amd-retina-dbg",
-        "try/gpu-fyi-try-mac-amd-retina-exp",
-        "try/gpu-fyi-try-mac-amd-retina-rel",
-        "try/gpu-fyi-try-mac-arm64-apple-dtk-rel",
-        "try/gpu-fyi-try-mac-asan",
-        "try/gpu-fyi-try-mac-intel-dbg",
-        "try/gpu-fyi-try-mac-intel-dqp",
-        "try/gpu-fyi-try-mac-intel-exp",
-        "try/gpu-fyi-try-mac-intel-rel",
-        "try/gpu-fyi-try-mac-nvidia-retina-dbg",
-        "try/gpu-fyi-try-mac-nvidia-retina-exp",
-        "try/gpu-fyi-try-mac-nvidia-retina-rel",
-        "try/gpu-try-android-m-nexus-5x-64",
-        "try/gpu-try-linux-nvidia-dbg",
-        "try/gpu-try-linux-nvidia-rel",
-        "try/gpu-try-mac-amd-retina-dbg",
-        "try/gpu-try-mac-intel-dbg",
-        "try/linux-angle-rel",
-        "try/linux-autofill-assistant",
-        "try/linux-blink-heap-concurrent-marking-tsan-rel",
-        "try/linux-blink-heap-verification-try",
-        "try/linux-chromeos-rel",
-        "try/linux-inverse-fieldtrials-fyi-rel",
-        "try/linux-libfuzzer-asan-rel",
-        "try/linux-ozone-rel",
-        "try/linux_android_dbg_ng",
-        "try/linux_angle_deqp_rel_ng",
-        "try/linux_angle_ozone_rel_ng",
-        "try/linux_chromium_analysis",
-        "try/linux_chromium_archive_rel_ng",
-        "try/linux_chromium_asan_rel_ng",
-        "try/linux_chromium_cfi_rel_ng",
-        "try/linux_chromium_chromeos_asan_rel_ng",
-        "try/linux_chromium_chromeos_msan_rel_ng",
-        "try/linux_chromium_clobber_deterministic",
-        "try/linux_chromium_clobber_rel_ng",
-        "try/linux_chromium_compile_dbg_32_ng",
-        "try/linux_chromium_compile_dbg_ng",
-        "try/linux_chromium_compile_rel_ng",
-        "try/linux-clang-tidy-dbg",
-        "try/linux-clang-tidy-rel",
-        "try/linux-rel",
-        "try/linux_chromium_dbg_ng",
-        "try/linux_chromium_msan_rel_ng",
-        "try/linux_chromium_tsan_rel_ng",
-        "try/linux_chromium_ubsan_rel_ng",
-        "try/linux-lacros-fyi-rel",
-        "try/linux_layout_tests_composite_after_paint",
-        "try/linux-layout-tests-edit-ng",
-        "try/linux-layout-tests-fragment-item",
-        "try/linux_layout_tests_layout_ng_disabled",
-        "try/linux_mojo",
-        "try/linux_mojo_chromeos",
-        "try/linux_optional_gpu_tests_rel",
-        "try/linux_upload_clang",
-        "try/linux-viz-rel",
-        "try/linux-wpt-fyi-rel",
-        "try/network_service_linux",
-        "try/ios-device",
-        "try/ios-simulator",
-        "try/ios-simulator-cronet",
-        "try/ios-simulator-eg",
-        "try/ios13-sdk-simulator",
-        "try/mac-angle-rel",
-        "try/mac-rel",
-        "try/mac-coverage-rel",
-        "try/mac_chromium_10.10",
-        "try/mac_chromium_10.12_rel_ng",
-        "try/mac_chromium_10.13_rel_ng",
-        "try/mac_chromium_10.14_rel_ng",
-        "try/mac_chromium_10.15_rel_ng",
-        "try/mac_chromium_archive_rel_ng",
-        "try/mac_chromium_asan_rel_ng",
-        "try/mac_chromium_compile_dbg_ng",
-        "try/mac_chromium_compile_rel_ng",
-        "try/mac_chromium_dbg_ng",
-        "try/mac_optional_gpu_tests_rel",
-        "try/mac_upload_clang",
-        "try/tricium-metrics-analysis",
-        "try/try-nougat-phone-tester",
-        "try/android-oreo-arm64-dbg",
-        "try/android-pie-arm64-dbg",
-        "try/win7-rel",
-        "try/win10_chromium_x64_dbg_ng",
-        "try/win10_chromium_x64_rel_ng",
-        "try/win10_chromium_x64_rel_ng_exp",
-        "try/win10_chromium_x64_1909_fyi_rel_ng",
-        "try/win-angle-deqp-rel-32",
-        "try/win-angle-deqp-rel-64",
-        "try/win-angle-rel-32",
-        "try/win-angle-rel-64",
-        "try/win_archive",
-        "try/win_chromium_compile_dbg_ng",
-        "try/win_chromium_compile_rel_ng",
-        "try/win_chromium_dbg_ng",
-        "try/win_chromium_x64_rel_ng",
-        "try/win_mojo",
-        "try/win_optional_gpu_tests_rel",
-        "try/win_upload_clang",
-        "try/win_x64_archive",
-        "try/win-celab-try-rel",
-    ],
-)
diff --git a/infra/config/subprojects/chromium/gpu.try.star b/infra/config/subprojects/chromium/gpu.try.star
index a8bf6e1..6862949 100644
--- a/infra/config/subprojects/chromium/gpu.try.star
+++ b/infra/config/subprojects/chromium/gpu.try.star
@@ -9,6 +9,7 @@
 try_.set_defaults(
     settings,
     execution_timeout = 6 * time.hour,
+    subproject_list_view = "luci.chromium.try",
     service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
 )
 
diff --git a/infra/config/subprojects/chromium/subproject.star b/infra/config/subprojects/chromium/subproject.star
index 93619a4..faacd790 100644
--- a/infra/config/subprojects/chromium/subproject.star
+++ b/infra/config/subprojects/chromium/subproject.star
@@ -36,7 +36,6 @@
 # )
 
 branches.exec("./consoles/android.packager.star")
-branches.exec("./consoles/luci.chromium.try.star")
 branches.exec("./consoles/metadata.exporter.star")
 branches.exec("./consoles/sheriff.ios.star")
 
diff --git a/infra/config/subprojects/chromium/swangle.try.star b/infra/config/subprojects/chromium/swangle.try.star
index 3ca5f86a..a19806f 100644
--- a/infra/config/subprojects/chromium/swangle.try.star
+++ b/infra/config/subprojects/chromium/swangle.try.star
@@ -8,6 +8,7 @@
 try_.set_defaults(
     settings,
     execution_timeout = 2 * time.hour,
+    subproject_list_view = "luci.chromium.try",
     service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
 )
 
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index b8eb2fd..dd6b463d 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -12,11 +12,17 @@
 try_.set_defaults(
     settings,
     add_to_list_view = True,
+    subproject_list_view = "luci.chromium.try",
 )
 
 # Automatically maintained consoles
 
 try_.list_view(
+    name = "luci.chromium.try",
+    branch_selector = branches.ALL_RELEASES,
+)
+
+try_.list_view(
     name = "tryserver.blink",
     branch_selector = branches.STANDARD_RELEASES,
 )
diff --git a/ios/chrome/app/application_delegate/metric_kit_subscriber.mm b/ios/chrome/app/application_delegate/metric_kit_subscriber.mm
index a82ee6c..b06e1c9 100644
--- a/ios/chrome/app/application_delegate/metric_kit_subscriber.mm
+++ b/ios/chrome/app/application_delegate/metric_kit_subscriber.mm
@@ -57,7 +57,7 @@
 }
 #endif
 
-void ReportDuration(const char* histogram_name, NSMeasurement* measurement)
+void ReportLongDuration(const char* histogram_name, NSMeasurement* measurement)
     API_AVAILABLE(ios(13.0)) {
   if (!measurement) {
     return;
@@ -65,7 +65,10 @@
   double value =
       [measurement measurementByConvertingToUnit:NSUnitDuration.seconds]
           .doubleValue;
-  base::UmaHistogramTimes(histogram_name, base::TimeDelta::FromSecondsD(value));
+  base::UmaHistogramCustomTimes(
+      histogram_name, base::TimeDelta::FromSecondsD(value),
+      base::TimeDelta::FromSeconds(1),
+      base::TimeDelta::FromSeconds(86400 /* secs per day */), 50);
 }
 
 void ReportMemory(const char* histogram_name, NSMeasurement* measurement)
@@ -277,10 +280,10 @@
     return;
   }
 
-  ReportDuration("IOS.MetricKit.ForegroundTimePerDay",
-                 payload.applicationTimeMetrics.cumulativeForegroundTime);
-  ReportDuration("IOS.MetricKit.BackgroundTimePerDay",
-                 payload.applicationTimeMetrics.cumulativeBackgroundTime);
+  ReportLongDuration("IOS.MetricKit.ForegroundTimePerDay",
+                     payload.applicationTimeMetrics.cumulativeForegroundTime);
+  ReportLongDuration("IOS.MetricKit.BackgroundTimePerDay",
+                     payload.applicationTimeMetrics.cumulativeBackgroundTime);
   ReportMemory("IOS.MetricKit.AverageSuspendedMemory",
                payload.memoryMetrics.averageSuspendedMemory.averageMeasurement);
   ReportMemory("IOS.MetricKit.PeakMemoryUsage",
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index a27d8c9..a7611c7 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -927,6 +927,9 @@
       <message name="IDS_IOS_ICON_SEARCH" desc="Accessibility label for the search icon [iOS only].">
         Search
       </message>
+      <message name="IDS_IOS_INFO_BUTTON_ACCESSIBILITY_HINT" desc="Action hint for the info button of the TableViewInfoButtonCell. This is spoken by VoiceOver. [iOS only]">
+        Double tap for more information
+      </message>
       <message name="IDS_IOS_INFOBAR_BANNER_DISMISS_HINT" desc="Accessibility hint that indicates the user that can dismiss the Infobar Banner. [Length:Unlimited]">
         Dismiss
       </message>
@@ -2300,7 +2303,7 @@
         Double tap to toggle setting
       </message>
       <message name="IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT" desc="Action hint for any managed switch in settings. This is spoken by VoiceOver. [iOS only]">
-        This setting is managed
+        This setting is managed, double tap for more information
       </message>
       <message name="IDS_IOS_TOOLBAR_CLOSE_MENU" desc="The accessibility label for the toolbar close settings menu button [iOS only].">
         Close Menu
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INFO_BUTTON_ACCESSIBILITY_HINT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INFO_BUTTON_ACCESSIBILITY_HINT.png.sha1
new file mode 100644
index 0000000..860d1de
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INFO_BUTTON_ACCESSIBILITY_HINT.png.sha1
@@ -0,0 +1 @@
+6eee137d38ea101af02a996212e20b0a45cb6a5f
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT.png.sha1
new file mode 100644
index 0000000..a7f19dc
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT.png.sha1
@@ -0,0 +1 @@
+db847cb408ebf5437c718a7e8db2bdc8f5074afa
\ No newline at end of file
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index 9f5a8649..c3d7346e 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -49,13 +49,13 @@
     "//ios/chrome/browser/drag_and_drop",
     "//ios/chrome/browser/open_in:features",
     "//ios/chrome/browser/policy:feature_flags",
+    "//ios/chrome/browser/screen_time:feature_flags",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/content_suggestions:feature_flags",
     "//ios/chrome/browser/ui/download:features",
     "//ios/chrome/browser/ui/fullscreen:feature_flags",
     "//ios/chrome/browser/ui/infobars:feature_flags",
     "//ios/chrome/browser/ui/page_info:features",
-    "//ios/chrome/browser/ui/screen_time:feature_flags",
     "//ios/chrome/browser/ui/settings/autofill:feature_flags",
     "//ios/chrome/browser/ui/tab_grid:features",
     "//ios/chrome/browser/ui/table_view:feature_flags",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index cf63965e..c3101d0 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -62,13 +62,13 @@
 #include "ios/chrome/browser/flags/ios_chrome_flag_descriptions.h"
 #import "ios/chrome/browser/open_in/features.h"
 #include "ios/chrome/browser/policy/policy_features.h"
+#include "ios/chrome/browser/screen_time/features.h"
 #include "ios/chrome/browser/system_flags.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/download/features.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h"
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/page_info/features.h"
-#include "ios/chrome/browser/ui/screen_time/features.h"
 #include "ios/chrome/browser/ui/settings/autofill/features.h"
 #import "ios/chrome/browser/ui/tab_grid/features.h"
 #import "ios/chrome/browser/ui/table_view/feature_flags.h"
diff --git a/ios/chrome/browser/screen_time/BUILD.gn b/ios/chrome/browser/screen_time/BUILD.gn
new file mode 100644
index 0000000..814f18b
--- /dev/null
+++ b/ios/chrome/browser/screen_time/BUILD.gn
@@ -0,0 +1,12 @@
+# 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.
+
+source_set("feature_flags") {
+  sources = [
+    "features.h",
+    "features.mm",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+  deps = [ "//base" ]
+}
diff --git a/ios/chrome/browser/screen_time/OWNERS b/ios/chrome/browser/screen_time/OWNERS
new file mode 100644
index 0000000..5df7217
--- /dev/null
+++ b/ios/chrome/browser/screen_time/OWNERS
@@ -0,0 +1,4 @@
+edchin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/screen_time/features.h b/ios/chrome/browser/screen_time/features.h
similarity index 70%
rename from ios/chrome/browser/ui/screen_time/features.h
rename to ios/chrome/browser/screen_time/features.h
index 8b38665..56b5fea 100644
--- a/ios/chrome/browser/ui/screen_time/features.h
+++ b/ios/chrome/browser/screen_time/features.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_SCREEN_TIME_FEATURES_H_
-#define IOS_CHROME_BROWSER_UI_SCREEN_TIME_FEATURES_H_
+#ifndef IOS_CHROME_BROWSER_SCREEN_TIME_FEATURES_H_
+#define IOS_CHROME_BROWSER_SCREEN_TIME_FEATURES_H_
 
 #include "base/feature_list.h"
 
@@ -13,4 +13,4 @@
 // Returns true if ScreenTime integration is enabled.
 bool IsScreenTimeIntegrationEnabled();
 
-#endif  // IOS_CHROME_BROWSER_UI_SCREEN_TIME_FEATURES_H_
+#endif  // IOS_CHROME_BROWSER_SCREEN_TIME_FEATURES_H_
diff --git a/ios/chrome/browser/ui/screen_time/features.mm b/ios/chrome/browser/screen_time/features.mm
similarity index 91%
rename from ios/chrome/browser/ui/screen_time/features.mm
rename to ios/chrome/browser/screen_time/features.mm
index b6ebeff..425d21e 100644
--- a/ios/chrome/browser/ui/screen_time/features.mm
+++ b/ios/chrome/browser/screen_time/features.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/screen_time/features.h"
+#import "ios/chrome/browser/screen_time/features.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
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 4a4db90..eba4653 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -2255,9 +2255,6 @@
       UIAction* editAction = [actionFactory actionToEditWithBlock:^{
         [self editNode:node];
       }];
-      if (!canEditNode) {
-        editAction.attributes = UIMenuElementAttributesDisabled;
-      }
       [menuElements addObject:editAction];
 
       [menuElements
@@ -2267,13 +2264,20 @@
                  indexPath:indexPath];
           }]];
 
-      [menuElements addObject:[actionFactory actionToDeleteWithBlock:^{
-                      std::set<const BookmarkNode*> nodes;
-                      nodes.insert(node);
-                      [self handleSelectNodesForDeletion:nodes];
-                      base::RecordAction(base::UserMetricsAction(
-                          "MobileBookmarkManagerEntryDeleted"));
-                    }]];
+      UIAction* deleteAction = [actionFactory actionToDeleteWithBlock:^{
+        std::set<const BookmarkNode*> nodes;
+        nodes.insert(node);
+        [self handleSelectNodesForDeletion:nodes];
+        base::RecordAction(
+            base::UserMetricsAction("MobileBookmarkManagerEntryDeleted"));
+      }];
+      [menuElements addObject:deleteAction];
+
+      // Disable Edit and Delete if the node cannot be edited.
+      if (!canEditNode) {
+        editAction.attributes = UIMenuElementAttributesDisabled;
+        deleteAction.attributes = UIMenuElementAttributesDisabled;
+      }
 
       return [UIMenu menuWithTitle:@"" children:menuElements];
     };
diff --git a/ios/chrome/browser/ui/browser_container/BUILD.gn b/ios/chrome/browser/ui/browser_container/BUILD.gn
index 5807c8a..99a808e7 100644
--- a/ios/chrome/browser/ui/browser_container/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_container/BUILD.gn
@@ -18,10 +18,10 @@
     "//ios/chrome/browser/main",
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/web_content_area",
+    "//ios/chrome/browser/screen_time:feature_flags",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/overlays",
     "//ios/chrome/browser/ui/screen_time",
-    "//ios/chrome/browser/ui/screen_time:feature_flags",
     "//ios/chrome/browser/web_state_list",
     "//ios/web/public",
     "//url",
diff --git a/ios/chrome/browser/ui/browser_container/browser_container_coordinator.mm b/ios/chrome/browser/ui/browser_container/browser_container_coordinator.mm
index 54f17f5..a62f3e3 100644
--- a/ios/chrome/browser/ui/browser_container/browser_container_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_container/browser_container_coordinator.mm
@@ -9,10 +9,10 @@
 #include "base/check.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#import "ios/chrome/browser/screen_time/features.h"
 #import "ios/chrome/browser/ui/browser_container/browser_container_mediator.h"
 #import "ios/chrome/browser/ui/browser_container/browser_container_view_controller.h"
 #import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
-#import "ios/chrome/browser/ui/screen_time/features.h"
 #include "url/gurl.h"
 
 #if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm
index 475ad44d..4c46cac2 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm
@@ -264,8 +264,8 @@
                                                      view:self];
 
   // Layout guides should both be setup
-  DCHECK(imageLayoutGuide.isConstrained);
-  DCHECK(textLayoutGuide.isConstrained);
+  DCHECK(imageLayoutGuide);
+  DCHECK(textLayoutGuide);
 
   // The text stack view is attached to both ends of the layout gude. This is
   // because it needs to switch directions if the device is in LTR mode and the
diff --git a/ios/chrome/browser/ui/screen_time/BUILD.gn b/ios/chrome/browser/ui/screen_time/BUILD.gn
index 295ab119..1546c631 100644
--- a/ios/chrome/browser/ui/screen_time/BUILD.gn
+++ b/ios/chrome/browser/ui/screen_time/BUILD.gn
@@ -23,7 +23,6 @@
     ]
     configs += [ "//build/config/compiler:enable_arc" ]
     deps = [
-      ":feature_flags",
       "//base",
       "//ios/chrome/browser/browser_state",
       "//ios/chrome/browser/main:public",
@@ -35,12 +34,3 @@
     ]
   }
 }
-
-source_set("feature_flags") {
-  sources = [
-    "features.h",
-    "features.mm",
-  ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-  deps = [ "//base" ]
-}
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
index 43fcc30..d82eb14 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_settings_egtest.mm
@@ -93,9 +93,11 @@
 // Helper to open the settings page for Autofill credit cards.
 - (void)openCreditCardsSettings {
   [ChromeEarlGreyUI openSettingsMenu];
-  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(
-                                          l10n_util::GetNSString(
-                                              IDS_AUTOFILL_PAYMENT_METHODS))]
+  [[[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(
+                                           l10n_util::GetNSString(
+                                               IDS_AUTOFILL_PAYMENT_METHODS))]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
+      onElementWithMatcher:chrome_test_util::SettingsCollectionView()]
       performAction:grey_tap()];
 }
 
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
index db12651..5fd9820 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
@@ -218,6 +218,8 @@
       l10n_util::GetNSString(IDS_AUTOFILL_ENABLE_CREDIT_CARDS_TOGGLE_LABEL);
   // The status could only be off when the pref is managed.
   cardManagedItem.statusText = l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  cardManagedItem.accessibilityHint =
+      l10n_util::GetNSString(IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
   cardManagedItem.accessibilityIdentifier = kAutofillCreditCardManagedViewId;
   return cardManagedItem;
 }
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
index a848ba5..9bc122e0 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
@@ -173,6 +173,8 @@
       l10n_util::GetNSString(IDS_AUTOFILL_ENABLE_PROFILES_TOGGLE_LABEL);
   // The status could only be off when the pref is managed.
   managedAddressItem.statusText = l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  managedAddressItem.accessibilityHint =
+      l10n_util::GetNSString(IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
   managedAddressItem.accessibilityIdentifier = @"addressItem_managed";
   return managedAddressItem;
 }
diff --git a/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm b/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
index 97fa171..8c55703 100644
--- a/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
@@ -160,6 +160,8 @@
       [_disablePopupsSetting value]
           ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
           : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  blockPopupsManagedItem.accessibilityHint =
+      l10n_util::GetNSString(IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
   blockPopupsManagedItem.accessibilityIdentifier =
       @"blockPopupsContentView_managed";
   return blockPopupsManagedItem;
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
index a206b52..dc4a38b 100644
--- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
+++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
@@ -713,6 +713,8 @@
   managedItem.detailText = GetNSString(detailStringID);
   managedItem.statusText = status ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
                                   : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  managedItem.accessibilityHint =
+      l10n_util::GetNSString(IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
   return managedItem;
 }
 
diff --git a/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
index 38b236b..cfeedd07 100644
--- a/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
@@ -147,6 +147,9 @@
         [self.dataSource translateEnabled]
             ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
             : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+    translateManagedItem.accessibilityHint = l10n_util::GetNSString(
+        IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
+
     [model addItem:translateManagedItem
         toSectionWithIdentifier:SectionIdentifierTranslate];
   } else {
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 11f1dd4..e877555 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
@@ -537,6 +537,8 @@
       [_passwordManagerEnabled value]
           ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
           : l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  managedSavePasswordItem.accessibilityHint =
+      l10n_util::GetNSString(IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
   managedSavePasswordItem.accessibilityIdentifier =
       @"savePasswordsItem_managed";
   return managedSavePasswordItem;
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 2fdd549..d128354 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -588,6 +588,8 @@
   managedDefaultSearchEngineItem.text =
       l10n_util::GetNSString(IDS_IOS_SEARCH_ENGINE_SETTING_TITLE);
   managedDefaultSearchEngineItem.iconImageName = kSettingsSearchEngineImageName;
+  managedDefaultSearchEngineItem.accessibilityHint =
+      l10n_util::GetNSString(IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
 
   const base::DictionaryValue* dict = _browserState->GetPrefs()->GetDictionary(
       DefaultSearchManager::kDefaultSearchProviderDataPrefName);
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.mm b/ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.mm
index 01fc778..6e0c00d 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.mm
@@ -313,8 +313,10 @@
 }
 
 - (NSString*)accessibilityHint {
-  return l10n_util::GetNSString(
-      IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
+  if (self.accessibilityHint.length) {
+    return self.accessibilityHint;
+  }
+  return l10n_util::GetNSString(IDS_IOS_INFO_BUTTON_ACCESSIBILITY_HINT);
 }
 
 - (NSString*)accessibilityLabel {
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.h
index 580ebaa7..7643f528 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.h
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.h
@@ -27,6 +27,9 @@
 // The status text string.
 @property(nonatomic, copy) NSString* statusText;
 
+// The accessibility hint text string.
+@property(nonatomic, copy) NSString* accessibilityHint;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_INFO_BUTTON_ITEM_H_
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.mm
index f69cdfd9..0be6c97 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.mm
@@ -33,6 +33,9 @@
     [cell updatePaddingForDetailText:NO];
   }
   cell.statusTextLabel.text = self.statusText;
+  if (self.accessibilityHint) {
+    cell.accessibilityHint = self.accessibilityHint;
+  }
   cell.selectionStyle = UITableViewCellSelectionStyleNone;
 
   // Update the icon image, if one is present.
diff --git a/ios/chrome/common/credential_provider/archivable_credential_store.mm b/ios/chrome/common/credential_provider/archivable_credential_store.mm
index 7f11ab38..5d9a84d0 100644
--- a/ios/chrome/common/credential_provider/archivable_credential_store.mm
+++ b/ios/chrome/common/credential_provider/archivable_credential_store.mm
@@ -129,10 +129,11 @@
   });
 }
 
-- (id<Credential>)credentialWithIdentifier:(NSString*)identifier {
+- (id<Credential>)credentialWithRecordIdentifier:(NSString*)recordIdentifier {
+  DCHECK(recordIdentifier.length);
   __block id<Credential> credential;
   dispatch_sync(self.workingQueue, ^{
-    credential = self.memoryStorage[identifier];
+    credential = self.memoryStorage[recordIdentifier];
   });
   return credential;
 }
diff --git a/ios/chrome/common/credential_provider/credential_store.h b/ios/chrome/common/credential_provider/credential_store.h
index e71618a..dd397be 100644
--- a/ios/chrome/common/credential_provider/credential_store.h
+++ b/ios/chrome/common/credential_provider/credential_store.h
@@ -36,8 +36,8 @@
 // to update the data on disk.
 - (void)removeCredentialWithRecordIdentifier:(NSString*)recordIdentifier;
 
-// Returns the credential with matching |identifier| or nil if none.
-- (id<Credential>)credentialWithIdentifier:(NSString*)identifier;
+// Returns the credential with matching |recordIdentifier| or nil if none.
+- (id<Credential>)credentialWithRecordIdentifier:(NSString*)recordIdentifier;
 
 @end
 
diff --git a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
index 82ae8556..4fd6f3e 100644
--- a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
@@ -173,7 +173,7 @@
     (ASPasswordCredentialIdentity*)credentialIdentity {
   NSString* identifier = credentialIdentity.recordIdentifier;
   id<Credential> credential =
-      [self.credentialStore credentialWithIdentifier:identifier];
+      [self.credentialStore credentialWithRecordIdentifier:identifier];
   if (credential) {
     NSString* password =
         PasswordWithKeychainIdentifier(credential.keychainIdentifier);
diff --git a/ios/net/protocol_handler_util_unittest.mm b/ios/net/protocol_handler_util_unittest.mm
index a96b10a6..1416007 100644
--- a/ios/net/protocol_handler_util_unittest.mm
+++ b/ios/net/protocol_handler_util_unittest.mm
@@ -46,8 +46,9 @@
 
 class HeadersURLRequestJob : public URLRequestJob {
  public:
-  HeadersURLRequestJob(URLRequest* request)
-      : URLRequestJob(request, nullptr) {}
+  explicit HeadersURLRequestJob(URLRequest* request) : URLRequestJob(request) {}
+
+  ~HeadersURLRequestJob() override {}
 
   void Start() override {
     // Fills response headers and returns immediately.
@@ -88,8 +89,6 @@
   }
 
  protected:
-  ~HeadersURLRequestJob() override {}
-
   std::string GetContentTypeValue() const {
     if (request()->url().path_piece() == "/badcontenttype")
       return "\xff";
@@ -99,10 +98,9 @@
 
 class NetURLRequestInterceptor : public URLRequestInterceptor {
  public:
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return new HeadersURLRequestJob(request);
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    return std::make_unique<HeadersURLRequestJob>(request);
   }
 };
 
diff --git a/ios/web/webui/url_data_manager_ios_backend.mm b/ios/web/webui/url_data_manager_ios_backend.mm
index 853d7f77..6228f77b2 100644
--- a/ios/web/webui/url_data_manager_ios_backend.mm
+++ b/ios/web/webui/url_data_manager_ios_backend.mm
@@ -92,7 +92,6 @@
  public:
   // |is_incognito| set when job is generated from an incognito profile.
   URLRequestChromeJob(net::URLRequest* request,
-                      net::NetworkDelegate* network_delegate,
                       BrowserState* browser_state,
                       bool is_incognito);
 
@@ -205,10 +204,9 @@
 };
 
 URLRequestChromeJob::URLRequestChromeJob(net::URLRequest* request,
-                                         net::NetworkDelegate* network_delegate,
                                          BrowserState* browser_state,
                                          bool is_incognito)
-    : net::URLRequestJob(request, network_delegate),
+    : net::URLRequestJob(request),
       data_offset_(0),
       pending_buf_size_(0),
       allow_caching_(true),
@@ -401,12 +399,11 @@
   ~ChromeProtocolHandler() override {}
 
   std::unique_ptr<net::URLRequestJob> CreateJob(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
+      net::URLRequest* request) const override {
     DCHECK(request);
 
-    return std::make_unique<URLRequestChromeJob>(request, network_delegate,
-                                                 browser_state_, is_incognito_);
+    return std::make_unique<URLRequestChromeJob>(request, browser_state_,
+                                                 is_incognito_);
   }
 
   bool IsSafeRedirectTarget(const GURL& location) const override {
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index cd2834f..f7d5114 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -165,15 +165,14 @@
     DCHECK(thread_checker_.CalledOnValidThread());
     DCHECK(task_runner_->BelongsToCurrentThread());
 
-    connector_.reset(new mojo::Connector(
-        std::move(handle), mojo::Connector::SINGLE_THREADED_SEND,
-        task_runner_));
+    connector_ = std::make_unique<mojo::Connector>(
+        std::move(handle), mojo::Connector::SINGLE_THREADED_SEND, task_runner_,
+        "IPC Channel");
     connector_->set_incoming_receiver(&dispatcher_);
     connector_->set_connection_error_handler(
         base::BindOnce(&ChannelAssociatedGroupController::OnPipeError,
                        base::Unretained(this)));
     connector_->set_enforce_errors_from_incoming_receiver(false);
-    connector_->SetWatcherHeapProfilerTag("IPC Channel");
     if (quota_checker_)
       connector_->SetMessageQuotaChecker(quota_checker_);
 
diff --git a/media/gpu/vaapi/vaapi_jpeg_encoder.cc b/media/gpu/vaapi/vaapi_jpeg_encoder.cc
index 643dc91c..664df42 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encoder.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_encoder.cc
@@ -343,45 +343,29 @@
   // Set picture parameters.
   VAEncPictureParameterBufferJPEG pic_param;
   FillPictureParameters(input_size, quality, output_buffer_id, &pic_param);
-  if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType,
-                                    &pic_param)) {
-    return false;
-  }
 
   if (!q_matrix_cached_) {
     q_matrix_cached_.reset(new VAQMatrixBufferJPEG());
     FillQMatrix(q_matrix_cached_.get());
   }
-  if (!vaapi_wrapper_->SubmitBuffer(VAQMatrixBufferType,
-                                    q_matrix_cached_.get())) {
-    return false;
-  }
 
   if (!huff_table_param_cached_) {
     huff_table_param_cached_.reset(new VAHuffmanTableBufferJPEGBaseline());
     FillHuffmanTableParameters(huff_table_param_cached_.get());
   }
-  if (!vaapi_wrapper_->SubmitBuffer(VAHuffmanTableBufferType,
-                                    huff_table_param_cached_.get())) {
-    return false;
-  }
 
   // Set slice parameters.
   if (!slice_param_cached_) {
     slice_param_cached_.reset(new VAEncSliceParameterBufferJPEG());
     FillSliceParameters(slice_param_cached_.get());
   }
-  if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType,
-                                    slice_param_cached_.get())) {
-    return false;
-  }
 
   size_t jpeg_header_size =
       exif_buffer_size > 0
           ? kJpegDefaultHeaderSize + kJFIFApp1HeaderSize + exif_buffer_size
           : kJpegDefaultHeaderSize + kJFIFApp0Size;
   std::vector<uint8_t> jpeg_header(jpeg_header_size);
-  size_t length_in_bits =
+  const size_t length_in_bits =
       FillJpegHeader(input_size, exif_buffer, exif_buffer_size, quality,
                      jpeg_header.data(), exif_offset);
 
@@ -390,14 +374,19 @@
   header_param.type = VAEncPackedHeaderRawData;
   header_param.bit_length = length_in_bits;
   header_param.has_emulation_bytes = 0;
-  if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
-                                    &header_param)) {
-    return false;
-  }
 
-  if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
-                                    (length_in_bits + 7) / 8,
-                                    jpeg_header.data())) {
+  if (!vaapi_wrapper_->SubmitBuffers(
+          {{VAEncPictureParameterBufferType, sizeof(pic_param), &pic_param},
+           {VAQMatrixBufferType, sizeof(*q_matrix_cached_),
+            q_matrix_cached_.get()},
+           {VAHuffmanTableBufferType, sizeof(*huff_table_param_cached_),
+            huff_table_param_cached_.get()},
+           {VAEncSliceParameterBufferType, sizeof(*slice_param_cached_),
+            slice_param_cached_.get()},
+           {VAEncPackedHeaderParameterBufferType, sizeof(header_param),
+            &header_param},
+           {VAEncPackedHeaderDataBufferType, (length_in_bits + 7) / 8,
+            jpeg_header.data()}})) {
     return false;
   }
 
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index 1fde3cc3..508513dec6 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -300,7 +300,7 @@
   bool SyncSurface(VASurfaceID va_surface_id);
 
   // Calls SubmitBuffer_Locked() to request libva to allocate a new VABufferID
-  // of |va_buffer_type| and |size|, and to copy the |data| into it. The
+  // of |va_buffer_type| and |size|, and to map-and-copy the |data| into it. The
   // allocated VABufferIDs stay alive until DestroyPendingBuffers_Locked(). Note
   // that this method does not submit the buffers for execution, they are simply
   // stored until ExecuteAndDestroyPendingBuffers()/Execute_Locked(). The
diff --git a/media/video/vpx_video_encoder.cc b/media/video/vpx_video_encoder.cc
index 1257f11..7d463f5 100644
--- a/media/video/vpx_video_encoder.cc
+++ b/media/video/vpx_video_encoder.cc
@@ -65,7 +65,7 @@
     config->rc_target_bitrate = opts.bitrate.value() / 1000;
   } else {
     config->rc_end_usage = VPX_VBR;
-    config->rc_target_bitrate = static_cast<double>(opts.width * opts.height) /
+    config->rc_target_bitrate = double{opts.width} * double{opts.height} /
                                 config->g_w / config->g_h *
                                 config->rc_target_bitrate;
   }
diff --git a/mojo/core/node_controller.cc b/mojo/core/node_controller.cc
index 298079f..029bd350 100644
--- a/mojo/core/node_controller.cc
+++ b/mojo/core/node_controller.cc
@@ -993,11 +993,8 @@
   // Feed the broker any pending invitees of our own.
   while (!pending_broker_clients.empty()) {
     const ports::NodeName& invitee_name = pending_broker_clients.front();
-    auto it = pending_invitations_.find(invitee_name);
-    // If for any reason we don't have a pending invitation for the invitee,
-    // there's nothing left to do: we've already swapped the relevant state into
-    // the stack.
-    if (it != pending_invitations_.end()) {
+    auto it = peers_.find(invitee_name);
+    if (it != peers_.end()) {
       broker->AddBrokerClient(invitee_name,
                               it->second->CloneRemoteProcessHandle());
     }
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index 16e478c..1616f1bf 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -84,7 +84,8 @@
   // The Connector takes ownership of |message_pipe|.
   Connector(ScopedMessagePipeHandle message_pipe,
             ConnectorConfig config,
-            scoped_refptr<base::SequencedTaskRunner> runner);
+            scoped_refptr<base::SequencedTaskRunner> runner,
+            const char* heap_profiler_tag = "unknown interface");
   ~Connector() override;
 
   // Sets outgoing serialization mode.
@@ -193,10 +194,6 @@
 
   base::SequencedTaskRunner* task_runner() const { return task_runner_.get(); }
 
-  // Sets the tag used by the heap profiler.
-  // |tag| must be a const string literal.
-  void SetWatcherHeapProfilerTag(const char* tag);
-
   // Sets the quota checker.
   void SetMessageQuotaChecker(
       scoped_refptr<internal::MessageQuotaChecker> checker);
diff --git a/mojo/public/cpp/bindings/lib/binding_state.cc b/mojo/public/cpp/bindings/lib/binding_state.cc
index ecfe41c..2fb462f0d 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.cc
+++ b/mojo/public/cpp/bindings/lib/binding_state.cc
@@ -120,8 +120,7 @@
                  ? MultiplexRouter::SINGLE_INTERFACE_WITH_SYNC_METHODS
                  : MultiplexRouter::SINGLE_INTERFACE);
   router_ = new MultiplexRouter(std::move(receiver_state->pipe), config, false,
-                                sequenced_runner);
-  router_->SetPrimaryInterfaceName(interface_name);
+                                sequenced_runner, interface_name);
   router_->SetConnectionGroup(std::move(receiver_state->connection_group));
 
   endpoint_client_.reset(new InterfaceEndpointClient(
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index 4840109..93ea250 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -144,13 +144,15 @@
 
 Connector::Connector(ScopedMessagePipeHandle message_pipe,
                      ConnectorConfig config,
-                     scoped_refptr<base::SequencedTaskRunner> runner)
+                     scoped_refptr<base::SequencedTaskRunner> runner,
+                     const char* heap_profiler_tag)
     : message_pipe_(std::move(message_pipe)),
       task_runner_(std::move(runner)),
       error_(false),
       force_immediate_dispatch_(!EnableTaskPerMessage()),
       outgoing_serialization_mode_(g_default_outgoing_serialization_mode),
       incoming_serialization_mode_(g_default_incoming_serialization_mode),
+      heap_profiler_tag_(heap_profiler_tag),
       nesting_observer_(RunLoopNestingObserver::GetForThread()) {
   if (config == MULTI_THREADED_SEND)
     lock_.emplace();
@@ -351,14 +353,6 @@
   sync_watcher_->AllowWokenUpBySyncWatchOnSameThread();
 }
 
-void Connector::SetWatcherHeapProfilerTag(const char* tag) {
-  if (tag) {
-    heap_profiler_tag_ = tag;
-    if (handle_watcher_)
-      handle_watcher_->set_heap_profiler_tag(tag);
-  }
-}
-
 void Connector::SetMessageQuotaChecker(
     scoped_refptr<internal::MessageQuotaChecker> checker) {
   DCHECK(checker && !quota_checker_);
@@ -414,9 +408,9 @@
   DCHECK(!handle_watcher_);
 
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  handle_watcher_.reset(new SimpleWatcher(
-      FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL, task_runner_));
-  handle_watcher_->set_heap_profiler_tag(heap_profiler_tag_);
+  handle_watcher_ = std::make_unique<SimpleWatcher>(
+      FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL, task_runner_,
+      heap_profiler_tag_);
   MojoResult rv = handle_watcher_->Watch(
       message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
       base::BindRepeating(&Connector::OnWatcherHandleReady,
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.cc b/mojo/public/cpp/bindings/lib/interface_ptr_state.cc
index bd36391..b7c4d7cf 100644
--- a/mojo/public/cpp/bindings/lib/interface_ptr_state.cc
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.cc
@@ -90,13 +90,14 @@
                  ? MultiplexRouter::SINGLE_INTERFACE_WITH_SYNC_METHODS
                  : MultiplexRouter::SINGLE_INTERFACE);
   DCHECK(runner_->RunsTasksInCurrentSequence());
-  router_ = new MultiplexRouter(std::move(handle_), config, true, runner_);
-  endpoint_client_.reset(new InterfaceEndpointClient(
+  router_ = new MultiplexRouter(std::move(handle_), config, true, runner_,
+                                interface_name);
+  endpoint_client_ = std::make_unique<InterfaceEndpointClient>(
       router_->CreateLocalEndpointHandle(kPrimaryInterfaceId), nullptr,
       std::move(payload_validator), false, std::move(runner_),
       // The version is only queried from the client so the value passed here
       // will not be used.
-      0u, interface_name));
+      0u, interface_name);
   return true;
 }
 
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
index d1e3711..d3cbe961 100644
--- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -260,7 +260,6 @@
             Interface::PassesAssociatedKinds_, Interface::HasSyncMethods_,
             std::make_unique<typename Interface::ResponseValidator_>(),
             Interface::Name_)) {
-      router()->SetPrimaryInterfaceName(Interface::Name_);
       proxy_ = std::make_unique<Proxy>(endpoint_client());
     }
   }
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index 08da6c26d..5f2e814 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -14,6 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
+#include "base/strings/string_util.h"
 #include "base/synchronization/waitable_event.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_controller.h"
@@ -315,14 +316,16 @@
     ScopedMessagePipeHandle message_pipe,
     Config config,
     bool set_interface_id_namespace_bit,
-    scoped_refptr<base::SequencedTaskRunner> runner)
+    scoped_refptr<base::SequencedTaskRunner> runner,
+    const char* primary_interface_name)
     : set_interface_id_namespace_bit_(set_interface_id_namespace_bit),
       task_runner_(runner),
       dispatcher_(this),
       connector_(std::move(message_pipe),
                  config == MULTI_INTERFACE ? Connector::MULTI_THREADED_SEND
                                            : Connector::SINGLE_THREADED_SEND,
-                 std::move(runner)),
+                 std::move(runner),
+                 primary_interface_name),
       control_message_handler_(this),
       control_message_proxy_(&connector_) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
@@ -352,6 +355,14 @@
       std::make_unique<MessageHeaderValidator>();
   header_validator_ = header_validator.get();
   dispatcher_.SetValidator(std::move(header_validator));
+
+  if (primary_interface_name) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    header_validator_->SetDescription(base::JoinString(
+        {primary_interface_name, "[primary] MessageHeaderValidator"}, " "));
+    control_message_handler_.SetDescription(base::JoinString(
+        {primary_interface_name, "[primary] PipeControlMessageHandler"}, " "));
+  }
 }
 
 MultiplexRouter::~MultiplexRouter() {
@@ -369,15 +380,6 @@
   dispatcher_.SetFilter(std::move(filter));
 }
 
-void MultiplexRouter::SetPrimaryInterfaceName(const char* name) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  header_validator_->SetDescription(std::string(name) +
-                                    " [primary] MessageHeaderValidator");
-  control_message_handler_.SetDescription(
-      std::string(name) + " [primary] PipeControlMessageHandler");
-  connector_.SetWatcherHeapProfilerTag(name);
-}
-
 void MultiplexRouter::SetConnectionGroup(ConnectionGroup::Ref ref) {
   connector_.SetConnectionGroup(std::move(ref));
 }
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index 16128e8..49c6785c 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -79,17 +79,13 @@
   MultiplexRouter(ScopedMessagePipeHandle message_pipe,
                   Config config,
                   bool set_interface_id_namespace_bit,
-                  scoped_refptr<base::SequencedTaskRunner> runner);
+                  scoped_refptr<base::SequencedTaskRunner> runner,
+                  const char* primary_interface_name = "unknown interface");
 
   // Sets a MessageReceiver which can filter a message after validation but
   // before dispatch.
   void SetIncomingMessageFilter(std::unique_ptr<MessageFilter> filter);
 
-  // Sets the primary interface name for this router. Only used when reporting
-  // message header or control message validation errors.
-  // |name| must be a string literal.
-  void SetPrimaryInterfaceName(const char* name);
-
   // Adds this object to a ConnectionGroup identified by |ref|. All receiving
   // pipe endpoints decoded from inbound messages on this MultiplexRouter will
   // be added to the same group.
diff --git a/mojo/public/cpp/system/simple_watcher.cc b/mojo/public/cpp/system/simple_watcher.cc
index 1e3b7fb..9fd9fe1 100644
--- a/mojo/public/cpp/system/simple_watcher.cc
+++ b/mojo/public/cpp/system/simple_watcher.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
 #include "base/synchronization/lock.h"
+#include "base/task/common/task_annotator.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/heap_profiler.h"
 #include "base/trace_event/trace_event.h"
@@ -30,9 +31,10 @@
       MojoHandleSignals signals,
       MojoTriggerCondition condition,
       int watch_id,
-      MojoResult* result) {
+      MojoResult* result,
+      const char* heap_profiler_tag) {
     scoped_refptr<Context> context =
-        new Context(watcher, task_runner, watch_id);
+        new Context(watcher, task_runner, watch_id, heap_profiler_tag);
 
     // If MojoAddTrigger succeeds, it effectively assumes ownership of a
     // reference to |context|. In that case, this reference is balanced in
@@ -67,10 +69,12 @@
 
   Context(base::WeakPtr<SimpleWatcher> weak_watcher,
           scoped_refptr<base::SequencedTaskRunner> task_runner,
-          int watch_id)
+          int watch_id,
+          const char* heap_profiler_tag)
       : weak_watcher_(weak_watcher),
         task_runner_(task_runner),
-        watch_id_(watch_id) {}
+        watch_id_(watch_id),
+        heap_profiler_tag_(heap_profiler_tag) {}
 
   ~Context() = default;
 
@@ -87,28 +91,37 @@
       // the default task runner for the IO thread.
       weak_watcher_->OnHandleReady(watch_id_, result, state);
     } else {
-      task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(&SimpleWatcher::OnHandleReady,
-                                    weak_watcher_, watch_id_, result, state));
+      {
+        // Annotate the posted task with |heap_profiler_tag_| as the IPC
+        // interface.
+        base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(
+            heap_profiler_tag_);
+        task_runner_->PostTask(
+            FROM_HERE, base::BindOnce(&SimpleWatcher::OnHandleReady,
+                                      weak_watcher_, watch_id_, result, state));
+      }
     }
   }
 
   const base::WeakPtr<SimpleWatcher> weak_watcher_;
   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
   const int watch_id_;
+  const char* heap_profiler_tag_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(Context);
 };
 
 SimpleWatcher::SimpleWatcher(const base::Location& from_here,
                              ArmingPolicy arming_policy,
-                             scoped_refptr<base::SequencedTaskRunner> runner)
+                             scoped_refptr<base::SequencedTaskRunner> runner,
+                             const char* heap_profiler_tag)
     : arming_policy_(arming_policy),
       task_runner_(std::move(runner)),
       is_default_task_runner_(base::ThreadTaskRunnerHandle::IsSet() &&
                               task_runner_ ==
                                   base::ThreadTaskRunnerHandle::Get()),
-      heap_profiler_tag_(from_here.file_name()) {
+      heap_profiler_tag_(heap_profiler_tag ? heap_profiler_tag
+                                           : from_here.file_name()) {
   MojoResult rv = CreateTrap(&Context::CallNotify, &trap_handle_);
   DCHECK_EQ(MOJO_RESULT_OK, rv);
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
@@ -139,7 +152,7 @@
   MojoResult result = MOJO_RESULT_UNKNOWN;
   context_ = Context::Create(weak_factory_.GetWeakPtr(), task_runner_,
                              trap_handle_.get(), handle_, signals, condition,
-                             watch_id_, &result);
+                             watch_id_, &result, heap_profiler_tag_);
   if (!context_) {
     handle_.set_value(kInvalidHandleValue);
     callback_.Reset();
@@ -216,10 +229,15 @@
     return;
 
   DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv);
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SimpleWatcher::OnHandleReady, weak_factory_.GetWeakPtr(),
-                     watch_id_, ready_result, ready_state));
+  {
+    // Annotate the posted task with |heap_profiler_tag_| as the IPC interface.
+    base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(
+        heap_profiler_tag_);
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&SimpleWatcher::OnHandleReady,
+                                          weak_factory_.GetWeakPtr(), watch_id_,
+                                          ready_result, ready_state));
+  }
 }
 
 void SimpleWatcher::OnHandleReady(int watch_id,
@@ -263,5 +281,4 @@
       ArmOrNotify();
   }
 }
-
 }  // namespace mojo
diff --git a/mojo/public/cpp/system/simple_watcher.h b/mojo/public/cpp/system/simple_watcher.h
index 0cfafe5..24123bc 100644
--- a/mojo/public/cpp/system/simple_watcher.h
+++ b/mojo/public/cpp/system/simple_watcher.h
@@ -89,7 +89,8 @@
   SimpleWatcher(const base::Location& from_here,
                 ArmingPolicy arming_policy,
                 scoped_refptr<base::SequencedTaskRunner> runner =
-                    base::SequencedTaskRunnerHandle::Get());
+                    base::SequencedTaskRunnerHandle::Get(),
+                const char* heap_profiler_tag = nullptr);
   ~SimpleWatcher();
 
   // Indicates if the SimpleWatcher is currently watching a handle.
@@ -179,12 +180,6 @@
   Handle handle() const { return handle_; }
   ReadyCallbackWithState ready_callback() const { return callback_; }
 
-  // Sets the tag used by the heap profiler.
-  // |tag| must be a const string literal.
-  void set_heap_profiler_tag(const char* heap_profiler_tag) {
-    heap_profiler_tag_ = heap_profiler_tag;
-  }
-
  private:
   class Context;
 
diff --git a/net/base/file_stream_context.cc b/net/base/file_stream_context.cc
index f2009c8..eccb65e 100644
--- a/net/base/file_stream_context.cc
+++ b/net/base/file_stream_context.cc
@@ -113,6 +113,11 @@
                                Int64CompletionOnceCallback callback) {
   DCHECK(!async_in_progress_);
 
+  if (offset < 0) {
+    std::move(callback).Run(net::ERR_INVALID_ARGUMENT);
+    return;
+  }
+
   bool posted = base::PostTaskAndReplyWithResult(
       task_runner_.get(), FROM_HERE,
       base::BindOnce(&Context::SeekFileImpl, base::Unretained(this), offset),
diff --git a/net/cert_net/cert_net_fetcher_url_request_unittest.cc b/net/cert_net/cert_net_fetcher_url_request_unittest.cc
index 9568a1a1..08e92be 100644
--- a/net/cert_net/cert_net_fetcher_url_request_unittest.cc
+++ b/net/cert_net/cert_net_fetcher_url_request_unittest.cc
@@ -260,9 +260,8 @@
 
  private:
   // URLRequestInterceptor implementation:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
     EXPECT_TRUE(request->disable_secure_dns());
     *invoked_interceptor_ = true;
     return nullptr;
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index c634da52..3816b918 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -420,9 +420,8 @@
 
 // Callback that allows the test to substitute its own implementation
 // of URLRequestJob to handle the request.
-typedef base::RepeatingCallback<URLRequestJob*(
+typedef base::RepeatingCallback<std::unique_ptr<URLRequestJob>(
     URLRequest* request,
-    NetworkDelegate* network_delegate,
     SocketDataProvider* data_provider)>
     DohJobMakerCallback;
 
@@ -432,10 +431,9 @@
  public:
   URLRequestMockDohJob(
       URLRequest* request,
-      NetworkDelegate* network_delegate,
       SocketDataProvider* data_provider,
       ResponseModifierCallback response_modifier = ResponseModifierCallback())
-      : URLRequestJob(request, network_delegate),
+      : URLRequestJob(request),
         content_length_(0),
         leftover_data_len_(0),
         data_provider_(data_provider),
@@ -783,8 +781,7 @@
     }
   }
 
-  URLRequestJob* MaybeInterceptRequest(URLRequest* request,
-                                       NetworkDelegate* network_delegate) {
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(URLRequest* request) {
     // If the path indicates a redirect, skip checking the list of
     // configured servers, because it won't be there and we still want
     // to handle it.
@@ -847,10 +844,10 @@
     SocketDataProvider* provider = socket_factory_->mock_data().GetNext();
 
     if (doh_job_maker_)
-      return doh_job_maker_.Run(request, network_delegate, provider);
+      return doh_job_maker_.Run(request, provider);
 
-    return new URLRequestMockDohJob(request, network_delegate, provider,
-                                    response_modifier_);
+    return std::make_unique<URLRequestMockDohJob>(request, provider,
+                                                  response_modifier_);
   }
 
   class DohJobInterceptor : public URLRequestInterceptor {
@@ -859,10 +856,9 @@
     ~DohJobInterceptor() override {}
 
     // URLRequestInterceptor implementation:
-    URLRequestJob* MaybeInterceptRequest(
-        URLRequest* request,
-        NetworkDelegate* network_delegate) const override {
-      return test_->MaybeInterceptRequest(request, network_delegate);
+    std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+        URLRequest* request) const override {
+      return test_->MaybeInterceptRequest(request);
     }
 
    private:
@@ -1591,13 +1587,12 @@
   EXPECT_TRUE(helper0.RunUntilDone(transaction_factory_.get()));
 }
 
-URLRequestJob* DohJobMakerCallbackFailLookup(URLRequest* request,
-                                             NetworkDelegate* network_delegate,
-                                             SocketDataProvider* data) {
+std::unique_ptr<URLRequestJob> DohJobMakerCallbackFailLookup(
+    URLRequest* request,
+    SocketDataProvider* data) {
   URLRequestMockDohJob::MatchQueryData(request, data);
-  return new URLRequestFailedJob(request, network_delegate,
-                                 URLRequestFailedJob::START,
-                                 ERR_NAME_NOT_RESOLVED);
+  return std::make_unique<URLRequestFailedJob>(
+      request, URLRequestFailedJob::START, ERR_NAME_NOT_RESOLVED);
 }
 
 TEST_F(DnsTransactionTest, HttpsPostLookupFailDohServerLookup) {
@@ -1614,12 +1609,12 @@
   EXPECT_TRUE(helper0.RunUntilDone(transaction_factory_.get()));
 }
 
-URLRequestJob* DohJobMakerCallbackFailStart(URLRequest* request,
-                                            NetworkDelegate* network_delegate,
-                                            SocketDataProvider* data) {
+std::unique_ptr<URLRequestJob> DohJobMakerCallbackFailStart(
+    URLRequest* request,
+    SocketDataProvider* data) {
   URLRequestMockDohJob::MatchQueryData(request, data);
-  return new URLRequestFailedJob(request, network_delegate,
-                                 URLRequestFailedJob::START, ERR_FAILED);
+  return std::make_unique<URLRequestFailedJob>(
+      request, URLRequestFailedJob::START, ERR_FAILED);
 }
 
 TEST_F(DnsTransactionTest, HttpsPostLookupFailStart) {
@@ -1635,12 +1630,12 @@
   EXPECT_TRUE(helper0.RunUntilDone(transaction_factory_.get()));
 }
 
-URLRequestJob* DohJobMakerCallbackFailSync(URLRequest* request,
-                                           NetworkDelegate* network_delegate,
-                                           SocketDataProvider* data) {
+std::unique_ptr<URLRequestJob> DohJobMakerCallbackFailSync(
+    URLRequest* request,
+    SocketDataProvider* data) {
   URLRequestMockDohJob::MatchQueryData(request, data);
-  return new URLRequestFailedJob(request, network_delegate,
-                                 URLRequestFailedJob::READ_SYNC, ERR_FAILED);
+  return std::make_unique<URLRequestFailedJob>(
+      request, URLRequestFailedJob::READ_SYNC, ERR_FAILED);
 }
 
 TEST_F(DnsTransactionTest, HttpsPostLookupFailSync) {
@@ -1656,12 +1651,12 @@
   EXPECT_TRUE(helper0.RunUntilDone(transaction_factory_.get()));
 }
 
-URLRequestJob* DohJobMakerCallbackFailAsync(URLRequest* request,
-                                            NetworkDelegate* network_delegate,
-                                            SocketDataProvider* data) {
+std::unique_ptr<URLRequestJob> DohJobMakerCallbackFailAsync(
+    URLRequest* request,
+    SocketDataProvider* data) {
   URLRequestMockDohJob::MatchQueryData(request, data);
-  return new URLRequestFailedJob(request, network_delegate,
-                                 URLRequestFailedJob::READ_ASYNC, ERR_FAILED);
+  return std::make_unique<URLRequestFailedJob>(
+      request, URLRequestFailedJob::READ_ASYNC, ERR_FAILED);
 }
 
 TEST_F(DnsTransactionTest, HttpsPostLookupFailAsync) {
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index f0871c2..5cf4c1c 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -1644,12 +1644,14 @@
   ZeroRttState state;
 
   ssl_early_data_reason_t early_data_reason = crypto_stream_->EarlyDataReason();
-  if (early_data_reason == ssl_early_data_disabled) {
-    state = ZeroRttState::kNotAttempted;
-  } else if (early_data_reason == ssl_early_data_accepted) {
+  if (early_data_reason == ssl_early_data_accepted) {
     state = ZeroRttState::kAttemptedAndSucceeded;
-  } else {
+  } else if (early_data_reason == ssl_early_data_peer_declined ||
+             early_data_reason == ssl_early_data_session_not_resumed ||
+             early_data_reason == ssl_early_data_hello_retry_request) {
     state = ZeroRttState::kAttemptedAndRejected;
+  } else {
+    state = ZeroRttState::kNotAttempted;
   }
   UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ZeroRttState", state);
   UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ZeroRttReason", early_data_reason,
diff --git a/net/test/url_request/ssl_certificate_error_job.cc b/net/test/url_request/ssl_certificate_error_job.cc
index 5044c74..ebe76f3 100644
--- a/net/test/url_request/ssl_certificate_error_job.cc
+++ b/net/test/url_request/ssl_certificate_error_job.cc
@@ -27,10 +27,9 @@
   ~MockJobInterceptor() override = default;
 
   // URLRequestJobFactory::ProtocolHandler implementation:
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return new SSLCertificateErrorJob(request, network_delegate);
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    return std::make_unique<SSLCertificateErrorJob>(request);
   }
 
  private:
@@ -39,10 +38,10 @@
 
 }  // namespace
 
-SSLCertificateErrorJob::SSLCertificateErrorJob(
-    URLRequest* request,
-    NetworkDelegate* network_delegate)
-    : URLRequestJob(request, network_delegate) {}
+SSLCertificateErrorJob::SSLCertificateErrorJob(URLRequest* request)
+    : URLRequestJob(request) {}
+
+SSLCertificateErrorJob::~SSLCertificateErrorJob() = default;
 
 void SSLCertificateErrorJob::Start() {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -61,8 +60,6 @@
   return GURL(base::StringPrintf("https://%s", kMockHostname));
 }
 
-SSLCertificateErrorJob::~SSLCertificateErrorJob() = default;
-
 void SSLCertificateErrorJob::NotifyError() {
   SSLInfo info;
   info.cert_status = CERT_STATUS_DATE_INVALID;
diff --git a/net/test/url_request/ssl_certificate_error_job.h b/net/test/url_request/ssl_certificate_error_job.h
index 7a2c86f7..c3af368 100644
--- a/net/test/url_request/ssl_certificate_error_job.h
+++ b/net/test/url_request/ssl_certificate_error_job.h
@@ -12,14 +12,14 @@
 
 namespace net {
 
-class NetworkDelegate;
 class URLRequest;
 
 // SSLCertificateErrorJob simulates a ERR_CERT_DATE_INVALID error.
 class SSLCertificateErrorJob : public URLRequestJob {
  public:
-  SSLCertificateErrorJob(URLRequest* request,
-                         NetworkDelegate* network_delegate);
+  explicit SSLCertificateErrorJob(URLRequest* request);
+
+  ~SSLCertificateErrorJob() override;
 
   // URLRequestJob implementation:
   void Start() override;
@@ -30,8 +30,6 @@
   static GURL GetMockUrl();
 
  private:
-  ~SSLCertificateErrorJob() override;
-
   void NotifyError();
 
   base::WeakPtrFactory<SSLCertificateErrorJob> weak_factory_{this};
diff --git a/net/test/url_request/url_request_failed_job.cc b/net/test/url_request/url_request_failed_job.cc
index 78f74f4..09bc153 100644
--- a/net/test/url_request/url_request_failed_job.cc
+++ b/net/test/url_request/url_request_failed_job.cc
@@ -41,9 +41,8 @@
   ~MockJobInterceptor() override = default;
 
   // URLRequestJobFactory::ProtocolHandler implementation:
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
     int net_error = OK;
     URLRequestFailedJob::FailurePhase phase =
         URLRequestFailedJob::FailurePhase::MAX_FAILURE_PHASE;
@@ -57,7 +56,7 @@
         }
       }
     }
-    return new URLRequestFailedJob(request, network_delegate, phase, net_error);
+    return std::make_unique<URLRequestFailedJob>(request, phase, net_error);
   }
 
  private:
@@ -78,10 +77,9 @@
 }  // namespace
 
 URLRequestFailedJob::URLRequestFailedJob(URLRequest* request,
-                                         NetworkDelegate* network_delegate,
                                          FailurePhase phase,
                                          int net_error)
-    : URLRequestJob(request, network_delegate),
+    : URLRequestJob(request),
       phase_(phase),
       net_error_(net_error),
       total_received_bytes_(0) {
@@ -90,11 +88,10 @@
   CHECK_LT(net_error, OK);
 }
 
-URLRequestFailedJob::URLRequestFailedJob(URLRequest* request,
-                                         NetworkDelegate* network_delegate,
-                                         int net_error)
-    : URLRequestFailedJob(request, network_delegate, START, net_error) {
-}
+URLRequestFailedJob::URLRequestFailedJob(URLRequest* request, int net_error)
+    : URLRequestFailedJob(request, START, net_error) {}
+
+URLRequestFailedJob::~URLRequestFailedJob() = default;
 
 void URLRequestFailedJob::Start() {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -179,8 +176,6 @@
   return GetMockUrl("https", hostname, START, net_error);
 }
 
-URLRequestFailedJob::~URLRequestFailedJob() = default;
-
 void URLRequestFailedJob::StartAsync() {
   if (phase_ == START) {
     if (net_error_ != ERR_IO_PENDING) {
diff --git a/net/test/url_request/url_request_failed_job.h b/net/test/url_request/url_request_failed_job.h
index 8ce4763f..3b14f9c1 100644
--- a/net/test/url_request/url_request_failed_job.h
+++ b/net/test/url_request/url_request_failed_job.h
@@ -29,15 +29,15 @@
   };
 
   URLRequestFailedJob(URLRequest* request,
-                      NetworkDelegate* network_delegate,
                       FailurePhase phase,
                       int net_error);
 
   // Same as above, except that the job fails at FailurePhase.START.
   URLRequestFailedJob(URLRequest* request,
-                      NetworkDelegate* network_delegate,
                       int net_error);
 
+  ~URLRequestFailedJob() override;
+
   // URLRequestJob implementation:
   void Start() override;
   int ReadRawData(IOBuffer* buf, int buf_size) override;
@@ -72,7 +72,6 @@
                                          const std::string& hostname);
 
  protected:
-  ~URLRequestFailedJob() override;
   void StartAsync();
 
  private:
diff --git a/net/test/url_request/url_request_hanging_read_job.cc b/net/test/url_request/url_request_hanging_read_job.cc
index df9bf44..4089df8d 100644
--- a/net/test/url_request/url_request_hanging_read_job.cc
+++ b/net/test/url_request/url_request_hanging_read_job.cc
@@ -31,10 +31,9 @@
   ~MockJobInterceptor() override = default;
 
   // URLRequestInterceptor implementation
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return new URLRequestHangingReadJob(request, network_delegate);
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    return std::make_unique<URLRequestHangingReadJob>(request);
   }
 
  private:
@@ -43,12 +42,12 @@
 
 }  // namespace
 
-URLRequestHangingReadJob::URLRequestHangingReadJob(
-    URLRequest* request,
-    NetworkDelegate* network_delegate)
-    : URLRequestJob(request, network_delegate),
-      content_length_(10)  // non-zero content-length
-{}
+URLRequestHangingReadJob::URLRequestHangingReadJob(URLRequest* request)
+    : URLRequestJob(request),
+      // non-zero content-length
+      content_length_(10) {}
+
+URLRequestHangingReadJob::~URLRequestHangingReadJob() = default;
 
 void URLRequestHangingReadJob::Start() {
   // Start reading asynchronously so that all error reporting and data
@@ -58,8 +57,6 @@
                                 weak_factory_.GetWeakPtr()));
 }
 
-URLRequestHangingReadJob::~URLRequestHangingReadJob() = default;
-
 int URLRequestHangingReadJob::ReadRawData(IOBuffer* buf, int buf_size) {
   // Make read hang. It never completes.
   return ERR_IO_PENDING;
diff --git a/net/test/url_request/url_request_hanging_read_job.h b/net/test/url_request/url_request_hanging_read_job.h
index bd4a4a7..d91580d 100644
--- a/net/test/url_request/url_request_hanging_read_job.h
+++ b/net/test/url_request/url_request_hanging_read_job.h
@@ -16,8 +16,8 @@
 // A URLRequestJob that hangs when try to read response body.
 class URLRequestHangingReadJob : public URLRequestJob {
  public:
-  URLRequestHangingReadJob(URLRequest* request,
-                           NetworkDelegate* network_delegate);
+  explicit URLRequestHangingReadJob(URLRequest* request);
+  ~URLRequestHangingReadJob() override;
 
   void Start() override;
   int ReadRawData(IOBuffer* buf, int buf_size) override;
@@ -31,7 +31,6 @@
 
  private:
   void GetResponseInfoConst(HttpResponseInfo* info) const;
-  ~URLRequestHangingReadJob() override;
 
   void StartAsync();
 
diff --git a/net/test/url_request/url_request_mock_data_job.cc b/net/test/url_request/url_request_mock_data_job.cc
index 59e69f9..964067a 100644
--- a/net/test/url_request/url_request_mock_data_job.cc
+++ b/net/test/url_request/url_request_mock_data_job.cc
@@ -79,13 +79,12 @@
   ~MockJobInterceptor() override = default;
 
   // URLRequestInterceptor implementation
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return new URLRequestMockDataJob(request, network_delegate,
-                                     GetDataFromRequest(*request),
-                                     GetRepeatCountFromRequest(*request),
-                                     GetRequestClientCertificate(*request));
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    return std::make_unique<URLRequestMockDataJob>(
+        request, GetDataFromRequest(*request),
+        GetRepeatCountFromRequest(*request),
+        GetRequestClientCertificate(*request));
   }
 
  private:
@@ -95,11 +94,10 @@
 }  // namespace
 
 URLRequestMockDataJob::URLRequestMockDataJob(URLRequest* request,
-                                             NetworkDelegate* network_delegate,
                                              const std::string& data,
                                              int data_repeat_count,
                                              bool request_client_certificate)
-    : URLRequestJob(request, network_delegate),
+    : URLRequestJob(request),
       data_offset_(0),
       request_client_certificate_(request_client_certificate) {
   DCHECK_GT(data_repeat_count, 0);
@@ -108,6 +106,8 @@
   }
 }
 
+URLRequestMockDataJob::~URLRequestMockDataJob() = default;
+
 void URLRequestMockDataJob::OverrideResponseHeaders(
     const std::string& headers) {
   headers_ = headers;
@@ -121,8 +121,6 @@
                                 weak_factory_.GetWeakPtr()));
 }
 
-URLRequestMockDataJob::~URLRequestMockDataJob() = default;
-
 int URLRequestMockDataJob::ReadRawData(IOBuffer* buf, int buf_size) {
   int bytes_read =
       std::min(static_cast<size_t>(buf_size), data_.length() - data_offset_);
diff --git a/net/test/url_request/url_request_mock_data_job.h b/net/test/url_request/url_request_mock_data_job.h
index 1e18369..6011a58 100644
--- a/net/test/url_request/url_request_mock_data_job.h
+++ b/net/test/url_request/url_request_mock_data_job.h
@@ -23,10 +23,10 @@
 class URLRequestMockDataJob : public URLRequestJob {
  public:
   URLRequestMockDataJob(URLRequest* request,
-                        NetworkDelegate* network_delegate,
                         const std::string& data,
                         int data_repeat_count,
                         bool request_client_certificate);
+  ~URLRequestMockDataJob() override;
 
   void Start() override;
   int ReadRawData(IOBuffer* buf, int buf_size) override;
@@ -61,9 +61,6 @@
   // Overrides response headers in the mocked response.
   void OverrideResponseHeaders(const std::string& headers);
 
- protected:
-  ~URLRequestMockDataJob() override;
-
  private:
   void GetResponseInfoConst(HttpResponseInfo* info) const;
 
diff --git a/net/test/url_request/url_request_mock_http_job.cc b/net/test/url_request/url_request_mock_http_job.cc
index 1f96c0b..8533009 100644
--- a/net/test/url_request/url_request_mock_http_job.cc
+++ b/net/test/url_request/url_request_mock_http_job.cc
@@ -41,11 +41,10 @@
   ~MockJobInterceptor() override = default;
 
   // URLRequestJobFactory::ProtocolHandler implementation
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return new URLRequestMockHTTPJob(
-        request, network_delegate,
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    return std::make_unique<URLRequestMockHTTPJob>(
+        request,
         map_all_requests_to_base_path_ ? base_path_ : GetOnDiskPath(request));
   }
 
@@ -126,11 +125,9 @@
 }
 
 URLRequestMockHTTPJob::URLRequestMockHTTPJob(URLRequest* request,
-                                             NetworkDelegate* network_delegate,
                                              const base::FilePath& file_path)
     : URLRequestTestJobBackedByFile(
           request,
-          network_delegate,
           file_path,
           base::ThreadPool::CreateTaskRunner({base::MayBlock()})) {}
 
diff --git a/net/test/url_request/url_request_mock_http_job.h b/net/test/url_request/url_request_mock_http_job.h
index 905cfc71..2c053792 100644
--- a/net/test/url_request/url_request_mock_http_job.h
+++ b/net/test/url_request/url_request_mock_http_job.h
@@ -30,8 +30,8 @@
  public:
   // Note that all file I/O is done using ThreadPool.
   URLRequestMockHTTPJob(URLRequest* request,
-                        NetworkDelegate* network_delegate,
                         const base::FilePath& file_path);
+  ~URLRequestMockHTTPJob() override;
 
   // URLRequestJob overrides.
   void Start() override;
@@ -67,9 +67,6 @@
   static std::unique_ptr<URLRequestInterceptor> CreateInterceptorForSingleFile(
       const base::FilePath& file);
 
- protected:
-  ~URLRequestMockHTTPJob() override;
-
  private:
   void GetResponseInfoConst(HttpResponseInfo* info) const;
   void SetHeadersAndStart(const std::string& raw_headers);
diff --git a/net/test/url_request/url_request_test_job_backed_by_file.cc b/net/test/url_request/url_request_test_job_backed_by_file.cc
index 72e0655..b128704 100644
--- a/net/test/url_request/url_request_test_job_backed_by_file.cc
+++ b/net/test/url_request/url_request_test_job_backed_by_file.cc
@@ -54,10 +54,9 @@
 
 URLRequestTestJobBackedByFile::URLRequestTestJobBackedByFile(
     URLRequest* request,
-    NetworkDelegate* network_delegate,
     const base::FilePath& file_path,
     const scoped_refptr<base::TaskRunner>& file_task_runner)
-    : URLRequestJob(request, network_delegate),
+    : URLRequestJob(request),
       file_path_(file_path),
       stream_(new FileStream(file_task_runner)),
       file_task_runner_(file_task_runner),
diff --git a/net/test/url_request/url_request_test_job_backed_by_file.h b/net/test/url_request/url_request_test_job_backed_by_file.h
index cea7d39..e95c8a6f 100644
--- a/net/test/url_request/url_request_test_job_backed_by_file.h
+++ b/net/test/url_request/url_request_test_job_backed_by_file.h
@@ -38,7 +38,6 @@
  public:
   URLRequestTestJobBackedByFile(
       URLRequest* request,
-      NetworkDelegate* network_delegate,
       const base::FilePath& file_path,
       const scoped_refptr<base::TaskRunner>& file_task_runner);
 
diff --git a/net/test/url_request/url_request_test_job_backed_by_file_unittest.cc b/net/test/url_request/url_request_test_job_backed_by_file_unittest.cc
index 37495dd2..e4f4e87 100644
--- a/net/test/url_request/url_request_test_job_backed_by_file_unittest.cc
+++ b/net/test/url_request/url_request_test_job_backed_by_file_unittest.cc
@@ -35,7 +35,6 @@
   // OnReadComplete.
   TestURLRequestTestJobBackedByFile(
       URLRequest* request,
-      NetworkDelegate* network_delegate,
       const base::FilePath& file_path,
       const scoped_refptr<base::TaskRunner>& file_task_runner,
       int* open_result,
@@ -43,7 +42,6 @@
       bool* done_reading,
       std::string* observed_content)
       : URLRequestTestJobBackedByFile(request,
-                                      network_delegate,
                                       file_path,
                                       file_task_runner),
         open_result_(open_result),
@@ -232,9 +230,8 @@
       kUrl, DEFAULT_PRIORITY, &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
   TestScopedURLInterceptor interceptor(
       kUrl, std::make_unique<TestURLRequestTestJobBackedByFile>(
-                request.get(), context_.network_delegate(), path,
-                base::ThreadTaskRunnerHandle::Get(), open_result, seek_position,
-                done_reading, observed_content));
+                request.get(), path, base::ThreadTaskRunnerHandle::Get(),
+                open_result, seek_position, done_reading, observed_content));
   if (!range.empty()) {
     request->SetExtraRequestHeaderByName(HttpRequestHeaders::kRange, range,
                                          true /*overwrite*/);
diff --git a/net/url_request/ftp_protocol_handler.cc b/net/url_request/ftp_protocol_handler.cc
index a052bfa5..03783e50 100644
--- a/net/url_request/ftp_protocol_handler.cc
+++ b/net/url_request/ftp_protocol_handler.cc
@@ -35,19 +35,16 @@
 FtpProtocolHandler::~FtpProtocolHandler() = default;
 
 std::unique_ptr<URLRequestJob> FtpProtocolHandler::CreateJob(
-    URLRequest* request,
-    NetworkDelegate* network_delegate) const {
+    URLRequest* request) const {
   DCHECK_EQ("ftp", request->url().scheme());
 
   if (!IsPortAllowedForScheme(request->url().EffectiveIntPort(),
                               request->url().scheme_piece())) {
-    return std::make_unique<URLRequestErrorJob>(request, network_delegate,
-                                                ERR_UNSAFE_PORT);
+    return std::make_unique<URLRequestErrorJob>(request, ERR_UNSAFE_PORT);
   }
 
-  return std::make_unique<URLRequestFtpJob>(request, network_delegate,
-                                            ftp_transaction_factory_.get(),
-                                            ftp_auth_cache_);
+  return std::make_unique<URLRequestFtpJob>(
+      request, ftp_transaction_factory_.get(), ftp_auth_cache_);
 }
 
 FtpProtocolHandler::FtpProtocolHandler(
diff --git a/net/url_request/ftp_protocol_handler.h b/net/url_request/ftp_protocol_handler.h
index d1e1906..114a051 100644
--- a/net/url_request/ftp_protocol_handler.h
+++ b/net/url_request/ftp_protocol_handler.h
@@ -17,7 +17,6 @@
 class FtpAuthCache;
 class FtpTransactionFactory;
 class HostResolver;
-class NetworkDelegate;
 class URLRequestJob;
 
 // Implements a ProtocolHandler for FTP.
@@ -37,9 +36,7 @@
       std::unique_ptr<FtpTransactionFactory> ftp_transaction_factory,
       FtpAuthCache* auth_cache);
 
-  std::unique_ptr<URLRequestJob> CreateJob(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override;
+  std::unique_ptr<URLRequestJob> CreateJob(URLRequest* request) const override;
 
  private:
   friend class FtpTestURLRequestContext;
diff --git a/net/url_request/report_sender_unittest.cc b/net/url_request/report_sender_unittest.cc
index e897d36d..c921c6cc 100644
--- a/net/url_request/report_sender_unittest.cc
+++ b/net/url_request/report_sender_unittest.cc
@@ -81,8 +81,7 @@
 // URLRequestJob that returns an HTTP 500 response.
 class MockServerErrorJob : public URLRequestJob {
  public:
-  MockServerErrorJob(URLRequest* request, NetworkDelegate* network_delegate)
-      : URLRequestJob(request, network_delegate) {}
+  explicit MockServerErrorJob(URLRequest* request) : URLRequestJob(request) {}
   ~MockServerErrorJob() override = default;
 
  protected:
@@ -103,10 +102,9 @@
   MockServerErrorJobInterceptor() = default;
   ~MockServerErrorJobInterceptor() override = default;
 
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return new MockServerErrorJob(request, network_delegate);
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    return std::make_unique<MockServerErrorJob>(request);
   }
 
  private:
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 87cc2d7..9138aae0 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -170,8 +170,8 @@
 
   Cancel();
 
-  if (network_delegate_) {
-    network_delegate_->NotifyURLRequestDestroyed(this);
+  if (network_delegate()) {
+    network_delegate()->NotifyURLRequestDestroyed(this);
     if (job_.get())
       job_->NotifyURLRequestDestroyed();
   }
@@ -507,9 +507,9 @@
   load_timing_info_.request_start_time = response_info_.request_time;
   load_timing_info_.request_start = base::TimeTicks::Now();
 
-  if (network_delegate_) {
+  if (network_delegate()) {
     OnCallToDelegate(NetLogEventType::NETWORK_DELEGATE_BEFORE_URL_REQUEST);
-    int error = network_delegate_->NotifyBeforeURLRequest(
+    int error = network_delegate()->NotifyBeforeURLRequest(
         this,
         base::BindOnce(&URLRequest::BeforeRequestComplete,
                        base::Unretained(this)),
@@ -521,7 +521,7 @@
     return;
   }
 
-  StartJob(context_->job_factory()->CreateJob(this, network_delegate_));
+  StartJob(context_->job_factory()->CreateJob(this));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -530,11 +530,8 @@
                        RequestPriority priority,
                        Delegate* delegate,
                        const URLRequestContext* context,
-                       NetworkDelegate* network_delegate,
                        NetworkTrafficAnnotationTag traffic_annotation)
     : context_(context),
-      network_delegate_(network_delegate ? network_delegate
-                                         : context->network_delegate()),
       net_log_(NetLogWithSource::Make(context->net_log(),
                                       NetLogSourceType::URL_REQUEST)),
       url_chain_(1, url),
@@ -588,18 +585,17 @@
   if (error != OK) {
     net_log_.AddEventWithStringParams(NetLogEventType::CANCELLED, "source",
                                       "delegate");
-    StartJob(
-        std::make_unique<URLRequestErrorJob>(this, network_delegate_, error));
+    StartJob(std::make_unique<URLRequestErrorJob>(this, error));
   } else if (!delegate_redirect_url_.is_empty()) {
     GURL new_url;
     new_url.Swap(&delegate_redirect_url_);
 
     StartJob(std::make_unique<URLRequestRedirectJob>(
-        this, network_delegate_, new_url,
+        this, new_url,
         // Use status code 307 to preserve the method, so POST requests work.
         URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "Delegate"));
   } else {
-    StartJob(context_->job_factory()->CreateJob(this, network_delegate_));
+    StartJob(context_->job_factory()->CreateJob(this));
   }
 }
 
@@ -639,8 +635,8 @@
   if (referrer_url !=
       URLRequestJob::ComputeReferrerForPolicy(
           referrer_policy_, referrer_url, url(), &same_origin_for_metrics)) {
-    if (!network_delegate_ ||
-        !network_delegate_->CancelURLRequestWithPolicyViolatingReferrerHeader(
+    if (!network_delegate() ||
+        !network_delegate()->CancelURLRequestWithPolicyViolatingReferrerHeader(
             *this, url(), referrer_url)) {
       referrer_.clear();
     } else {
@@ -649,8 +645,8 @@
       referrer_.clear();
       net_log_.AddEventWithStringParams(NetLogEventType::CANCELLED, "source",
                                         "delegate");
-      RestartWithJob(std::make_unique<URLRequestErrorJob>(
-          this, network_delegate_, ERR_BLOCKED_BY_CLIENT));
+      RestartWithJob(
+          std::make_unique<URLRequestErrorJob>(this, ERR_BLOCKED_BY_CLIENT));
       return;
     }
   }
@@ -801,8 +797,8 @@
   // In some cases (e.g. an event was canceled), we might have sent the
   // completion event and receive a NotifyResponseStarted() later.
   if (!has_notified_completion_ && net_error == OK) {
-    if (network_delegate_)
-      network_delegate_->NotifyResponseStarted(this, net_error);
+    if (network_delegate())
+      network_delegate()->NotifyResponseStarted(this, net_error);
   }
 
   // Notify in case the entire URL Request has been finished.
@@ -907,8 +903,8 @@
         redirect_info.new_url.possibly_invalid_spec());
   }
 
-  if (network_delegate_)
-    network_delegate_->NotifyBeforeRedirect(this, redirect_info.new_url);
+  if (network_delegate())
+    network_delegate()->NotifyBeforeRedirect(this, redirect_info.new_url);
 
   if (!final_upload_progress_.position() && upload_data_stream_)
     final_upload_progress_ = upload_data_stream_->GetUploadProgress();
@@ -938,6 +934,10 @@
   return context_;
 }
 
+NetworkDelegate* URLRequest::network_delegate() const {
+  return context_->network_delegate();
+}
+
 int64_t URLRequest::GetExpectedContentSize() const {
   int64_t expected_content_size = -1;
   if (job_.get())
@@ -996,9 +996,9 @@
 bool URLRequest::CanGetCookies() const {
   DCHECK_EQ(PrivacyMode::PRIVACY_MODE_DISABLED, privacy_mode_);
   bool can_get_cookies = g_default_can_use_cookies;
-  if (network_delegate_) {
+  if (network_delegate()) {
     can_get_cookies =
-        network_delegate_->CanGetCookies(*this, /*allowed_from_caller=*/true);
+        network_delegate()->CanGetCookies(*this, /*allowed_from_caller=*/true);
   }
 
   if (!can_get_cookies)
@@ -1010,10 +1010,10 @@
                               CookieOptions* options) const {
   DCHECK(!(load_flags_ & LOAD_DO_NOT_SAVE_COOKIES));
   bool can_set_cookies = g_default_can_use_cookies;
-  if (network_delegate_) {
+  if (network_delegate()) {
     can_set_cookies =
-        network_delegate_->CanSetCookie(*this, cookie, options,
-                                        /*allowed_from_caller=*/true);
+        network_delegate()->CanSetCookie(*this, cookie, options,
+                                         /*allowed_from_caller=*/true);
   }
   if (!can_set_cookies)
     net_log_.AddEvent(NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE);
@@ -1036,8 +1036,8 @@
   // TODO(mmenke): Looks like |g_default_can_use_cookies| is not too useful,
   // with the network service - remove it.
   bool enable_privacy_mode = !g_default_can_use_cookies;
-  if (network_delegate_) {
-    enable_privacy_mode = network_delegate_->ForcePrivacyMode(
+  if (network_delegate()) {
+    enable_privacy_mode = network_delegate()->ForcePrivacyMode(
         url(), site_for_cookies_, isolation_info_.top_frame_origin());
   }
   return enable_privacy_mode ? PRIVACY_MODE_ENABLED : PRIVACY_MODE_DISABLED;
@@ -1098,8 +1098,8 @@
   is_pending_ = false;
   is_redirecting_ = false;
   has_notified_completion_ = true;
-  if (network_delegate_)
-    network_delegate_->NotifyCompleted(this, job_.get() != nullptr, status_);
+  if (network_delegate())
+    network_delegate()->NotifyCompleted(this, job_.get() != nullptr, status_);
 }
 
 void URLRequest::OnCallToDelegate(NetLogEventType type) {
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 2df95bb9..adeb342 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -84,7 +84,6 @@
   // factories to be queried.  If no factory handles the request, then the
   // default job will be used.
   typedef URLRequestJob*(ProtocolFactory)(URLRequest* request,
-                                          NetworkDelegate* network_delegate,
                                           const std::string& scheme);
 
   // Max number of http redirects to follow. The Fetch spec says: "If
@@ -621,6 +620,9 @@
   // Used to specify the context (cookie store, cache) for this request.
   const URLRequestContext* context() const;
 
+  // Returns context()->network_delegate().
+  NetworkDelegate* network_delegate() const;
+
   const NetLogWithSource& net_log() const { return net_log_; }
 
   // Returns the expected content size if available
@@ -746,14 +748,10 @@
   friend class TestNetworkDelegate;
 
   // URLRequests are always created by calling URLRequestContext::CreateRequest.
-  //
-  // If no network delegate is passed in, will use the ones from the
-  // URLRequestContext.
   URLRequest(const GURL& url,
              RequestPriority priority,
              Delegate* delegate,
              const URLRequestContext* context,
-             NetworkDelegate* network_delegate,
              NetworkTrafficAnnotationTag traffic_annotation);
 
   // Resumes or blocks a request paused by the NetworkDelegate::OnBeforeRequest
@@ -797,9 +795,9 @@
                                  bool fatal);
   void NotifyReadCompleted(int bytes_read);
 
-  // These functions delegate to |network_delegate_| if it is not NULL.
-  // If |network_delegate_| is NULL, cookies can be used unless
-  // SetDefaultCookiePolicyToBlock() has been called.
+  // These functions delegate to the NetworkDelegate if it is not nullptr.
+  // Otherwise, cookies can be used unless SetDefaultCookiePolicyToBlock() has
+  // been called.
   bool CanGetCookies() const;
   bool CanSetCookie(const net::CanonicalCookie& cookie,
                     CookieOptions* options) const;
@@ -824,8 +822,6 @@
   // cookie store, socket pool, etc.)
   const URLRequestContext* context_;
 
-  NetworkDelegate* network_delegate_;
-
   // Tracks the time spent in various load states throughout this request.
   NetLogWithSource net_log_;
 
@@ -852,7 +848,7 @@
   // caller.
   bool allow_credentials_;
   // Privacy mode for current hop. Based on |allow_credentials_|, |load_flags_|,
-  // and information provided by |network_delegate_|. Saving cookies can
+  // and information provided by the NetworkDelegate. Saving cookies can
   // currently be blocked independently of this field by setting the deprecated
   // LOAD_DO_NOT_SAVE_COOKIES field in |load_flags_|.
   PrivacyMode privacy_mode_;
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index 56a3fe5..66bfcfb 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -105,8 +105,8 @@
     RequestPriority priority,
     URLRequest::Delegate* delegate,
     NetworkTrafficAnnotationTag traffic_annotation) const {
-  return base::WrapUnique(new URLRequest(
-      url, priority, delegate, this, network_delegate_, traffic_annotation));
+  return base::WrapUnique(
+      new URLRequest(url, priority, delegate, this, traffic_annotation));
 }
 
 void URLRequestContext::set_cookie_store(CookieStore* cookie_store) {
diff --git a/net/url_request/url_request_error_job.cc b/net/url_request/url_request_error_job.cc
index e47d6cddc..609e95d 100644
--- a/net/url_request/url_request_error_job.cc
+++ b/net/url_request/url_request_error_job.cc
@@ -13,10 +13,8 @@
 
 namespace net {
 
-URLRequestErrorJob::URLRequestErrorJob(URLRequest* request,
-                                       NetworkDelegate* network_delegate,
-                                       int error)
-    : URLRequestJob(request, network_delegate), error_(error) {}
+URLRequestErrorJob::URLRequestErrorJob(URLRequest* request, int error)
+    : URLRequestJob(request), error_(error) {}
 
 URLRequestErrorJob::~URLRequestErrorJob() = default;
 
diff --git a/net/url_request/url_request_error_job.h b/net/url_request/url_request_error_job.h
index c180442..205b150 100644
--- a/net/url_request/url_request_error_job.h
+++ b/net/url_request/url_request_error_job.h
@@ -17,7 +17,6 @@
 class NET_EXPORT URLRequestErrorJob : public URLRequestJob {
  public:
   URLRequestErrorJob(URLRequest* request,
-                     NetworkDelegate* network_delegate,
                      int error);
   ~URLRequestErrorJob() override;
 
diff --git a/net/url_request/url_request_filter.cc b/net/url_request/url_request_filter.cc
index c777e5e..77203dd 100644
--- a/net/url_request/url_request_filter.cc
+++ b/net/url_request/url_request_filter.cc
@@ -8,6 +8,7 @@
 #include "base/stl_util.h"
 #include "base/task/current_thread.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
 #include "net/url_request/url_request_job_factory.h"
 
 namespace net {
@@ -108,14 +109,14 @@
   hit_count_ = 0;
 }
 
-URLRequestJob* URLRequestFilter::MaybeInterceptRequest(
-    URLRequest* request,
-    NetworkDelegate* network_delegate) const {
+std::unique_ptr<URLRequestJob> URLRequestFilter::MaybeInterceptRequest(
+    URLRequest* request) const {
   DCHECK(base::CurrentIOThread::Get());
-  URLRequestJob* job = nullptr;
   if (!request->url().is_valid())
     return nullptr;
 
+  std::unique_ptr<URLRequestJob> job;
+
   // Check the hostname map first.
   const std::string hostname = request->url().host();
   const std::string scheme = request->url().scheme();
@@ -123,7 +124,7 @@
   {
     auto it = hostname_interceptor_map_.find(make_pair(scheme, hostname));
     if (it != hostname_interceptor_map_.end())
-      job = it->second->MaybeInterceptRequest(request, network_delegate);
+      job = it->second->MaybeInterceptRequest(request);
   }
 
   if (!job) {
@@ -131,7 +132,7 @@
     const std::string& url = request->url().spec();
     auto it = url_interceptor_map_.find(url);
     if (it != url_interceptor_map_.end())
-      job = it->second->MaybeInterceptRequest(request, network_delegate);
+      job = it->second->MaybeInterceptRequest(request);
   }
   if (job) {
     DVLOG(1) << "URLRequestFilter hit for " << request->url().spec();
diff --git a/net/url_request/url_request_filter.h b/net/url_request/url_request_filter.h
index 29b08f2..003620a 100644
--- a/net/url_request/url_request_filter.h
+++ b/net/url_request/url_request_filter.h
@@ -65,9 +65,8 @@
   int hit_count() const { return hit_count_; }
 
   // URLRequestInterceptor implementation:
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override;
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override;
 
  private:
   // scheme,hostname -> URLRequestInterceptor
diff --git a/net/url_request/url_request_filter_unittest.cc b/net/url_request/url_request_filter_unittest.cc
index 7af73646..d0e3502 100644
--- a/net/url_request/url_request_filter_unittest.cc
+++ b/net/url_request/url_request_filter_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/test/task_environment.h"
 #include "net/base/request_priority.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -28,11 +29,10 @@
   ~TestURLRequestInterceptor() override = default;
 
   // URLRequestInterceptor implementation:
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    job_ = new URLRequestTestJob(request, network_delegate);
-    return job_;
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    job_ = new URLRequestTestJob(request);
+    return base::WrapUnique<URLRequestJob>(job_);
   }
 
   // Is |job| the URLRequestJob generated during interception?
@@ -72,21 +72,19 @@
   EXPECT_TRUE(filter->AddUrlInterceptor(
       kUrl1, std::unique_ptr<URLRequestInterceptor>(interceptor)));
   {
-    std::unique_ptr<URLRequestJob> found(
-        filter->MaybeInterceptRequest(request1.get(), nullptr));
+    std::unique_ptr<URLRequestJob> found =
+        filter->MaybeInterceptRequest(request1.get());
     EXPECT_TRUE(interceptor->WasLastJobCreated(found.get()));
   }
   EXPECT_EQ(filter->hit_count(), 1);
 
   // Check we don't match other URLs.
-  EXPECT_TRUE(filter->MaybeInterceptRequest(request2.get(), nullptr) ==
-              nullptr);
+  EXPECT_FALSE(filter->MaybeInterceptRequest(request2.get()));
   EXPECT_EQ(1, filter->hit_count());
 
   // Check we can remove URL matching.
   filter->RemoveUrlHandler(kUrl1);
-  EXPECT_TRUE(filter->MaybeInterceptRequest(request1.get(), nullptr) ==
-              nullptr);
+  EXPECT_FALSE(filter->MaybeInterceptRequest(request1.get()));
   EXPECT_EQ(1, filter->hit_count());
 
   // Check hostname matching.
@@ -97,21 +95,19 @@
       kUrl1.scheme(), kUrl1.host(),
       std::unique_ptr<URLRequestInterceptor>(interceptor));
   {
-    std::unique_ptr<URLRequestJob> found(
-        filter->MaybeInterceptRequest(request1.get(), nullptr));
+    std::unique_ptr<URLRequestJob> found =
+        filter->MaybeInterceptRequest(request1.get());
     EXPECT_TRUE(interceptor->WasLastJobCreated(found.get()));
   }
   EXPECT_EQ(1, filter->hit_count());
 
   // Check we don't match other hostnames.
-  EXPECT_TRUE(filter->MaybeInterceptRequest(request2.get(), nullptr) ==
-              nullptr);
+  EXPECT_FALSE(filter->MaybeInterceptRequest(request2.get()));
   EXPECT_EQ(1, filter->hit_count());
 
   // Check we can remove hostname matching.
   filter->RemoveHostnameHandler(kUrl1.scheme(), kUrl1.host());
-  EXPECT_TRUE(filter->MaybeInterceptRequest(request1.get(), nullptr) ==
-              nullptr);
+  EXPECT_FALSE(filter->MaybeInterceptRequest(request1.get()));
   EXPECT_EQ(1, filter->hit_count());
 
   filter->ClearHandlers();
diff --git a/net/url_request/url_request_ftp_job.cc b/net/url_request/url_request_ftp_job.cc
index 8a23199..8ce8e65 100644
--- a/net/url_request/url_request_ftp_job.cc
+++ b/net/url_request/url_request_ftp_job.cc
@@ -43,10 +43,9 @@
 
 URLRequestFtpJob::URLRequestFtpJob(
     URLRequest* request,
-    NetworkDelegate* network_delegate,
     FtpTransactionFactory* ftp_transaction_factory,
     FtpAuthCache* ftp_auth_cache)
-    : URLRequestJob(request, network_delegate),
+    : URLRequestJob(request),
       proxy_resolution_service_(
           request_->context()->proxy_resolution_service()),
       read_in_progress_(false),
diff --git a/net/url_request/url_request_ftp_job.h b/net/url_request/url_request_ftp_job.h
index b796f69..6a0b17d 100644
--- a/net/url_request/url_request_ftp_job.h
+++ b/net/url_request/url_request_ftp_job.h
@@ -32,7 +32,6 @@
 
 class FtpAuthCache;
 class FtpTransactionFactory;
-class NetworkDelegate;
 class ProxyResolutionRequest;
 
 // A URLRequestJob subclass that is built on top of FtpTransaction. It
@@ -40,7 +39,6 @@
 class NET_EXPORT_PRIVATE URLRequestFtpJob : public URLRequestJob {
  public:
   URLRequestFtpJob(URLRequest* request,
-                   NetworkDelegate* network_delegate,
                    FtpTransactionFactory* ftp_transaction_factory,
                    FtpAuthCache* ftp_auth_cache);
   ~URLRequestFtpJob() override;
diff --git a/net/url_request/url_request_ftp_job_unittest.cc b/net/url_request/url_request_ftp_job_unittest.cc
index 7ce0eba8..0c1b174 100644
--- a/net/url_request/url_request_ftp_job_unittest.cc
+++ b/net/url_request/url_request_ftp_job_unittest.cc
@@ -120,11 +120,8 @@
     delete factory;
   }
 
-  std::unique_ptr<URLRequestJob> CreateJob(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return std::make_unique<URLRequestFtpJob>(request, network_delegate,
-                                              factory, auth_cache);
+  std::unique_ptr<URLRequestJob> CreateJob(URLRequest* request) const override {
+    return std::make_unique<URLRequestFtpJob>(request, factory, auth_cache);
   }
 
   bool IsSafeRedirectTarget(const GURL& location) const override {
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 4b58106..f5e754f 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -216,9 +216,7 @@
 
 namespace net {
 
-std::unique_ptr<URLRequestJob> URLRequestHttpJob::Create(
-    URLRequest* request,
-    NetworkDelegate* network_delegate) {
+std::unique_ptr<URLRequestJob> URLRequestHttpJob::Create(URLRequest* request) {
   const GURL& url = request->url();
 
   // URLRequestContext must have been initialized.
@@ -236,7 +234,7 @@
       replacements.SetSchemeStr(
           url.SchemeIs(url::kHttpScheme) ? url::kHttpsScheme : url::kWssScheme);
       return std::make_unique<URLRequestRedirectJob>(
-          request, network_delegate, url.ReplaceComponents(replacements),
+          request, url.ReplaceComponents(replacements),
           // Use status code 307 to preserve the method, so POST requests work.
           URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "HSTS");
     }
@@ -246,22 +244,20 @@
     // ERR_CLEARTEXT_NOT_PERMITTED if not.
     if (request->context()->check_cleartext_permitted() &&
         !android::IsCleartextPermitted(url.host())) {
-      return std::make_unique<URLRequestErrorJob>(request, network_delegate,
+      return std::make_unique<URLRequestErrorJob>(request,
                                                   ERR_CLEARTEXT_NOT_PERMITTED);
     }
 #endif
   }
 
-  return base::WrapUnique<URLRequestJob>(
-      new URLRequestHttpJob(request, network_delegate,
-                            request->context()->http_user_agent_settings()));
+  return base::WrapUnique<URLRequestJob>(new URLRequestHttpJob(
+      request, request->context()->http_user_agent_settings()));
 }
 
 URLRequestHttpJob::URLRequestHttpJob(
     URLRequest* request,
-    NetworkDelegate* network_delegate,
     const HttpUserAgentSettings* http_user_agent_settings)
-    : URLRequestJob(request, network_delegate),
+    : URLRequestJob(request),
       num_cookie_lines_left_(0),
       priority_(DEFAULT_PRIORITY),
       response_info_(nullptr),
@@ -407,14 +403,15 @@
 }
 
 void URLRequestHttpJob::StartTransaction() {
-  if (network_delegate()) {
+  NetworkDelegate* network_delegate = request()->network_delegate();
+  if (network_delegate) {
     OnCallToDelegate(
         NetLogEventType::NETWORK_DELEGATE_BEFORE_START_TRANSACTION);
     // The NetworkDelegate must watch for OnRequestDestroyed and not modify
     // |extra_headers| after it's called.
     // TODO(mattm): change the API to remove the out-params and take the
     // results as params of the callback.
-    int rv = network_delegate()->NotifyBeforeStartTransaction(
+    int rv = network_delegate->NotifyBeforeStartTransaction(
         request_,
         base::BindOnce(&URLRequestHttpJob::NotifyBeforeStartTransactionCallback,
                        weak_factory_.GetWeakPtr()),
@@ -908,7 +905,8 @@
   if (result == OK) {
     scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
 
-    if (network_delegate()) {
+    NetworkDelegate* network_delegate = request()->network_delegate();
+    if (network_delegate) {
       // Note that |this| may not be deleted until
       // |URLRequestHttpJob::OnHeadersReceivedCallback()| or
       // |NetworkDelegate::URLRequestDestroyed()| has been called.
@@ -921,7 +919,7 @@
       // any of the arguments after it's called.
       // TODO(mattm): change the API to remove the out-params and take the
       // results as params of the callback.
-      int error = network_delegate()->NotifyHeadersReceived(
+      int error = network_delegate->NotifyHeadersReceived(
           request_,
           base::BindOnce(&URLRequestHttpJob::OnHeadersReceivedCallback,
                          weak_factory_.GetWeakPtr()),
diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h
index 183b6da..274314b 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -47,16 +47,13 @@
   // Returns a job that returns a redirect in the case of HSTS, and returns a
   // job that fails for unencrypted requests if current settings dont allow
   // them. Never returns nullptr.
-  static std::unique_ptr<URLRequestJob> Create(
-      URLRequest* request,
-      NetworkDelegate* network_delegate);
+  static std::unique_ptr<URLRequestJob> Create(URLRequest* request);
 
   void SetRequestHeadersCallback(RequestHeadersCallback callback) override;
   void SetResponseHeadersCallback(ResponseHeadersCallback callback) override;
 
  protected:
   URLRequestHttpJob(URLRequest* request,
-                    NetworkDelegate* network_delegate,
                     const HttpUserAgentSettings* http_user_agent_settings);
 
   ~URLRequestHttpJob() override;
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index ae108a2..723a911 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -97,7 +97,6 @@
  public:
   explicit TestURLRequestHttpJob(URLRequest* request)
       : URLRequestHttpJob(request,
-                          request->context()->network_delegate(),
                           request->context()->http_user_agent_settings()),
         use_null_source_stream_(false) {}
 
diff --git a/net/url_request/url_request_interceptor.h b/net/url_request/url_request_interceptor.h
index 9ed1670..0ce071f 100644
--- a/net/url_request/url_request_interceptor.h
+++ b/net/url_request/url_request_interceptor.h
@@ -5,6 +5,8 @@
 #ifndef NET_URL_REQUEST_URL_REQUEST_INTERCEPTOR_H_
 #define NET_URL_REQUEST_URL_REQUEST_INTERCEPTOR_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "net/base/net_export.h"
 
@@ -12,7 +14,6 @@
 
 class URLRequest;
 class URLRequestJob;
-class NetworkDelegate;
 
 // In tests, URLRequestFilter lets URLRequestInterceptors create URLRequestJobs
 // to handle URLRequests before they're handed off to the ProtocolHandler for
@@ -27,9 +28,9 @@
 
   // Returns a URLRequestJob to handle |request|, if the interceptor wants to
   // take over the handling the request instead of the default ProtocolHandler.
-  // Otherwise, returns NULL.
-  virtual URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request, NetworkDelegate* network_delegate) const = 0;
+  // Otherwise, returns nullptr.
+  virtual std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(URLRequestInterceptor);
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 88d9a007..1f669a4 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -79,15 +79,13 @@
   DISALLOW_COPY_AND_ASSIGN(URLRequestJobSourceStream);
 };
 
-URLRequestJob::URLRequestJob(URLRequest* request,
-                             NetworkDelegate* network_delegate)
+URLRequestJob::URLRequestJob(URLRequest* request)
     : request_(request),
       done_(false),
       prefilter_bytes_read_(0),
       postfilter_bytes_read_(0),
       has_handled_response_(false),
-      expected_content_size_(-1),
-      network_delegate_(network_delegate) {}
+      expected_content_size_(-1) {}
 
 URLRequestJob::~URLRequestJob() {
 }
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index b3f9216..fe4dc8e 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -40,7 +40,6 @@
 class HttpResponseInfo;
 class IOBuffer;
 struct LoadTimingInfo;
-class NetworkDelegate;
 class ProxyServer;
 class SSLCertRequestInfo;
 class SSLInfo;
@@ -51,8 +50,7 @@
 
 class NET_EXPORT URLRequestJob {
  public:
-  explicit URLRequestJob(URLRequest* request,
-                         NetworkDelegate* network_delegate);
+  explicit URLRequestJob(URLRequest* request);
   virtual ~URLRequestJob();
 
   // Returns the request that owns this job.
@@ -325,9 +323,6 @@
   // or nullptr on error.
   virtual std::unique_ptr<SourceStream> SetUpSourceStream();
 
-  // Provides derived classes with access to the request's network delegate.
-  NetworkDelegate* network_delegate() { return network_delegate_; }
-
   // Set the proxy server that was used, if any.
   void SetProxyServer(const ProxyServer& proxy_server);
 
@@ -433,9 +428,6 @@
   // checks are performed, so this field must not be modified.
   base::Optional<RedirectInfo> deferred_redirect_info_;
 
-  // The network delegate to use with this request, if any.
-  NetworkDelegate* network_delegate_;
-
   // Non-null if ReadRawData() returned ERR_IO_PENDING, and the read has not
   // completed.
   CompletionOnceCallback read_raw_callback_;
diff --git a/net/url_request/url_request_job_factory.cc b/net/url_request/url_request_job_factory.cc
index d850556..dceb26f 100644
--- a/net/url_request/url_request_job_factory.cc
+++ b/net/url_request/url_request_job_factory.cc
@@ -30,10 +30,8 @@
   HttpProtocolHandler& operator=(const HttpProtocolHandler&) = delete;
   ~HttpProtocolHandler() override = default;
 
-  std::unique_ptr<URLRequestJob> CreateJob(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return URLRequestHttpJob::Create(request, network_delegate);
+  std::unique_ptr<URLRequestJob> CreateJob(URLRequest* request) const override {
+    return URLRequestHttpJob::Create(request);
   }
 };
 
@@ -81,30 +79,27 @@
 }
 
 std::unique_ptr<URLRequestJob> URLRequestJobFactory::CreateJob(
-    URLRequest* request,
-    NetworkDelegate* network_delegate) const {
+    URLRequest* request) const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // If we are given an invalid URL, then don't even try to inspect the scheme.
   if (!request->url().is_valid())
-    return std::make_unique<URLRequestErrorJob>(request, network_delegate,
-                                                ERR_INVALID_URL);
+    return std::make_unique<URLRequestErrorJob>(request, ERR_INVALID_URL);
 
   if (g_interceptor_for_testing) {
     std::unique_ptr<URLRequestJob> job(
-        g_interceptor_for_testing->MaybeInterceptRequest(request,
-                                                         network_delegate));
+        g_interceptor_for_testing->MaybeInterceptRequest(request));
     if (job)
       return job;
   }
 
   auto it = protocol_handler_map_.find(request->url().scheme());
   if (it == protocol_handler_map_.end()) {
-    return std::make_unique<URLRequestErrorJob>(request, network_delegate,
+    return std::make_unique<URLRequestErrorJob>(request,
                                                 ERR_UNKNOWN_URL_SCHEME);
   }
 
-  return it->second->CreateJob(request, network_delegate);
+  return it->second->CreateJob(request);
 }
 
 bool URLRequestJobFactory::IsSafeRedirectTarget(const GURL& location) const {
diff --git a/net/url_request/url_request_job_factory.h b/net/url_request/url_request_job_factory.h
index 58a55b1c..9d31c6c 100644
--- a/net/url_request/url_request_job_factory.h
+++ b/net/url_request/url_request_job_factory.h
@@ -18,7 +18,6 @@
 
 namespace net {
 
-class NetworkDelegate;
 class URLRequest;
 class URLRequestInterceptor;
 class URLRequestJob;
@@ -34,8 +33,7 @@
     // Creates a URLRequestJob for the particular protocol. Never returns
     // nullptr.
     virtual std::unique_ptr<URLRequestJob> CreateJob(
-        URLRequest* request,
-        NetworkDelegate* network_delegate) const = 0;
+        URLRequest* request) const = 0;
 
     // Indicates if it should be safe to redirect to |location|. Should handle
     // protocols handled by MaybeCreateJob().
@@ -54,9 +52,7 @@
   // with net::Error code if unable to handle request->url().
   //
   // Virtual for tests.
-  virtual std::unique_ptr<URLRequestJob> CreateJob(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const;
+  virtual std::unique_ptr<URLRequestJob> CreateJob(URLRequest* request) const;
 
   // Returns true if it's safe to redirect to |location|.
   //
diff --git a/net/url_request/url_request_job_factory_unittest.cc b/net/url_request/url_request_job_factory_unittest.cc
index 5ce221b..604d4ae 100644
--- a/net/url_request/url_request_job_factory_unittest.cc
+++ b/net/url_request/url_request_job_factory_unittest.cc
@@ -29,8 +29,7 @@
 
 class MockURLRequestJob : public URLRequestJob {
  public:
-  MockURLRequestJob(URLRequest* request, NetworkDelegate* network_delegate)
-      : URLRequestJob(request, network_delegate) {}
+  explicit MockURLRequestJob(URLRequest* request) : URLRequestJob(request) {}
 
   ~MockURLRequestJob() override = default;
 
@@ -50,10 +49,8 @@
 
 class DummyProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
  public:
-  std::unique_ptr<URLRequestJob> CreateJob(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return std::make_unique<MockURLRequestJob>(request, network_delegate);
+  std::unique_ptr<URLRequestJob> CreateJob(URLRequest* request) const override {
+    return std::make_unique<MockURLRequestJob>(request);
   }
 };
 
diff --git a/net/url_request/url_request_redirect_job.cc b/net/url_request/url_request_redirect_job.cc
index 58fefd34..d43060b 100644
--- a/net/url_request/url_request_redirect_job.cc
+++ b/net/url_request/url_request_redirect_job.cc
@@ -29,11 +29,10 @@
 namespace net {
 
 URLRequestRedirectJob::URLRequestRedirectJob(URLRequest* request,
-                                             NetworkDelegate* network_delegate,
                                              const GURL& redirect_destination,
                                              ResponseCode response_code,
                                              const std::string& redirect_reason)
-    : URLRequestJob(request, network_delegate),
+    : URLRequestJob(request),
       redirect_destination_(redirect_destination),
       response_code_(response_code),
       redirect_reason_(redirect_reason) {
diff --git a/net/url_request/url_request_redirect_job.h b/net/url_request/url_request_redirect_job.h
index 244de39..c4aa2df8 100644
--- a/net/url_request/url_request_redirect_job.h
+++ b/net/url_request/url_request_redirect_job.h
@@ -37,7 +37,6 @@
   // Constructs a job that redirects to the specified URL.  |redirect_reason| is
   // logged for debugging purposes, and must not be an empty string.
   URLRequestRedirectJob(URLRequest* request,
-                        NetworkDelegate* network_delegate,
                         const GURL& redirect_destination,
                         ResponseCode response_code,
                         const std::string& redirect_reason);
diff --git a/net/url_request/url_request_test_job.cc b/net/url_request/url_request_test_job.cc
index d1f6d4a..a3a4ed8b 100644
--- a/net/url_request/url_request_test_job.cc
+++ b/net/url_request/url_request_test_job.cc
@@ -126,14 +126,8 @@
   return std::string(kHeaders, base::size(kHeaders));
 }
 
-URLRequestTestJob::URLRequestTestJob(URLRequest* request,
-                                     NetworkDelegate* network_delegate)
-    : URLRequestTestJob(request, network_delegate, false) {}
-
-URLRequestTestJob::URLRequestTestJob(URLRequest* request,
-                                     NetworkDelegate* network_delegate,
-                                     bool auto_advance)
-    : URLRequestJob(request, network_delegate),
+URLRequestTestJob::URLRequestTestJob(URLRequest* request, bool auto_advance)
+    : URLRequestJob(request),
       auto_advance_(auto_advance),
       stage_(WAITING),
       priority_(DEFAULT_PRIORITY),
@@ -144,11 +138,10 @@
       async_reads_(false) {}
 
 URLRequestTestJob::URLRequestTestJob(URLRequest* request,
-                                     NetworkDelegate* network_delegate,
                                      const std::string& response_headers,
                                      const std::string& response_data,
                                      bool auto_advance)
-    : URLRequestJob(request, network_delegate),
+    : URLRequestJob(request),
       auto_advance_(auto_advance),
       stage_(WAITING),
       priority_(DEFAULT_PRIORITY),
diff --git a/net/url_request/url_request_test_job.h b/net/url_request/url_request_test_job.h
index 22f30ca..9c5906c 100644
--- a/net/url_request/url_request_test_job.h
+++ b/net/url_request/url_request_test_job.h
@@ -40,20 +40,13 @@
 class NET_EXPORT_PRIVATE URLRequestTestJob : public URLRequestJob {
  public:
   // Constructs a job to return one of the canned responses depending on the
-  // request url, with auto advance disabled.
-  URLRequestTestJob(URLRequest* request, NetworkDelegate* network_delegate);
-
-  // Constructs a job to return one of the canned responses depending on the
-  // request url, optionally with auto advance enabled.
-  URLRequestTestJob(URLRequest* request,
-                    NetworkDelegate* network_delegate,
-                    bool auto_advance);
+  // request url.
+  explicit URLRequestTestJob(URLRequest* request, bool auto_advance = false);
 
   // Constructs a job to return the given response regardless of the request
   // url. The headers should include the HTTP status line and use CRLF/LF as the
   // line separator.
   URLRequestTestJob(URLRequest* request,
-                    NetworkDelegate* network_delegate,
                     const std::string& response_headers,
                     const std::string& response_data,
                     bool auto_advance);
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 5692205..0fef1f0 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -656,10 +656,9 @@
 
   ~TestRequestInterceptor() override { CHECK(safe_to_delete_); }
 
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    return intercept_job_.release();
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    return std::move(intercept_job_);
   }
 
   bool job_used() const { return intercept_job_.get() == nullptr; }
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 6a9ed04..2f92038 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -336,10 +336,8 @@
  public:
   // The latest priority of the job is always written to |request_priority_|.
   PriorityMonitoringURLRequestJob(URLRequest* request,
-                                  NetworkDelegate* network_delegate,
                                   RequestPriority* request_priority)
-      : URLRequestTestJob(request, network_delegate),
-        request_priority_(request_priority) {
+      : URLRequestTestJob(request), request_priority_(request_priority) {
     *request_priority_ = DEFAULT_PRIORITY;
   }
 
@@ -979,11 +977,11 @@
   ~URLRequestInterceptorWithLoadTimingInfo() override = default;
 
   // URLRequestInterceptor implementation:
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    URLRequestTestJob* job = new URLRequestTestJob(
-        request, network_delegate, ok_headers(), ok_data(), true);
+  std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
+      URLRequest* request) const override {
+    std::unique_ptr<URLRequestTestJob> job =
+        std::make_unique<URLRequestTestJob>(request, ok_headers(), ok_data(),
+                                            true);
     job->set_load_timing_info(main_request_load_timing_info_);
     return job;
   }
@@ -1408,8 +1406,9 @@
   EXPECT_EQ(DEFAULT_PRIORITY, req->priority());
 
   RequestPriority job_priority;
-  std::unique_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob(
-      req.get(), &default_network_delegate_, &job_priority));
+  std::unique_ptr<URLRequestJob> job =
+      std::make_unique<PriorityMonitoringURLRequestJob>(req.get(),
+                                                        &job_priority);
   TestScopedURLInterceptor interceptor(req->url(), std::move(job));
   EXPECT_EQ(DEFAULT_PRIORITY, job_priority);
 
@@ -1428,8 +1427,9 @@
       TRAFFIC_ANNOTATION_FOR_TESTS));
 
   RequestPriority job_priority;
-  std::unique_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob(
-      req.get(), &default_network_delegate_, &job_priority));
+  std::unique_ptr<URLRequestJob> job =
+      std::make_unique<PriorityMonitoringURLRequestJob>(req.get(),
+                                                        &job_priority);
   TestScopedURLInterceptor interceptor(req->url(), std::move(job));
 
   req->SetPriority(LOW);
@@ -1451,8 +1451,9 @@
   EXPECT_EQ(MAXIMUM_PRIORITY, req->priority());
 
   RequestPriority job_priority;
-  std::unique_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob(
-      req.get(), &default_network_delegate_, &job_priority));
+  std::unique_ptr<URLRequestJob> job =
+      std::make_unique<PriorityMonitoringURLRequestJob>(req.get(),
+                                                        &job_priority);
   TestScopedURLInterceptor interceptor(req->url(), std::move(job));
 
   req->SetLoadFlags(LOAD_IGNORE_LIMITS);
@@ -2884,8 +2885,7 @@
     // URLRequestJobFactory::ProtocolHandler implementation:
 
     std::unique_ptr<URLRequestJob> CreateJob(
-        URLRequest* request,
-        NetworkDelegate* network_delegate) const override {
+        URLRequest* request) const override {
       NOTREACHED();
       return nullptr;
     }
@@ -6159,9 +6159,10 @@
   std::unique_ptr<URLRequest> r(default_context().CreateRequest(
       original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
 
-  std::unique_ptr<URLRequestRedirectJob> job(new URLRequestRedirectJob(
-      r.get(), &default_network_delegate_, redirect_url,
-      URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason"));
+  std::unique_ptr<URLRequestRedirectJob> job =
+      std::make_unique<URLRequestRedirectJob>(
+          r.get(), redirect_url, URLRequestRedirectJob::REDIRECT_302_FOUND,
+          "Very Good Reason");
   TestScopedURLInterceptor interceptor(r->url(), std::move(job));
 
   r->Start();
@@ -7978,10 +7979,10 @@
                     base::NumberToString(base::size(kData) - 1));
   req->SetExtraRequestHeaders(headers);
 
-  std::unique_ptr<URLRequestRedirectJob> job(new URLRequestRedirectJob(
-      req.get(), &default_network_delegate_,
-      http_test_server()->GetURL("/echo"),
-      URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason"));
+  std::unique_ptr<URLRequestRedirectJob> job =
+      std::make_unique<URLRequestRedirectJob>(
+          req.get(), http_test_server()->GetURL("/echo"),
+          URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason");
   TestScopedURLInterceptor interceptor(req->url(), std::move(job));
 
   req->Start();
@@ -8005,11 +8006,11 @@
                     base::NumberToString(base::size(kData) - 1));
   req->SetExtraRequestHeaders(headers);
 
-  std::unique_ptr<URLRequestRedirectJob> job(new URLRequestRedirectJob(
-      req.get(), &default_network_delegate_,
-      http_test_server()->GetURL("/echo"),
-      URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT,
-      "Very Good Reason"));
+  std::unique_ptr<URLRequestRedirectJob> job =
+      std::make_unique<URLRequestRedirectJob>(
+          req.get(), http_test_server()->GetURL("/echo"),
+          URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT,
+          "Very Good Reason");
   TestScopedURLInterceptor interceptor(req->url(), std::move(job));
 
   req->Start();
@@ -8208,9 +8209,10 @@
       initial_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
   EXPECT_EQ(DEFAULT_PRIORITY, req->priority());
 
-  std::unique_ptr<URLRequestRedirectJob> redirect_job(new URLRequestRedirectJob(
-      req.get(), &default_network_delegate_, redirect_url,
-      URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason"));
+  std::unique_ptr<URLRequestRedirectJob> redirect_job =
+      std::make_unique<URLRequestRedirectJob>(
+          req.get(), redirect_url, URLRequestRedirectJob::REDIRECT_302_FOUND,
+          "Very Good Reason");
   auto interceptor = std::make_unique<TestScopedURLInterceptor>(
       initial_url, std::move(redirect_job));
 
@@ -8221,8 +8223,9 @@
   interceptor.reset();
 
   RequestPriority job_priority;
-  std::unique_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob(
-      req.get(), &default_network_delegate_, &job_priority));
+  std::unique_ptr<URLRequestJob> job =
+      std::make_unique<PriorityMonitoringURLRequestJob>(req.get(),
+                                                        &job_priority);
   interceptor =
       std::make_unique<TestScopedURLInterceptor>(redirect_url, std::move(job));
 
@@ -9837,9 +9840,8 @@
 
  private:
   // URLRequestInterceptor implementation:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
     EXPECT_TRUE(request->disable_secure_dns());
     return nullptr;
   }
@@ -11616,10 +11618,11 @@
       GURL("http://not-a-real-domain/"), DEFAULT_PRIORITY, &d,
       TRAFFIC_ANNOTATION_FOR_TESTS));
 
-  std::unique_ptr<URLRequestRedirectJob> job(new URLRequestRedirectJob(
-      req.get(), &default_network_delegate_,
-      GURL("http://this-should-never-be-navigated-to/"),
-      URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "Jumbo shrimp"));
+  std::unique_ptr<URLRequestRedirectJob> job =
+      std::make_unique<URLRequestRedirectJob>(
+          req.get(), GURL("http://this-should-never-be-navigated-to/"),
+          URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT,
+          "Jumbo shrimp");
   TestScopedURLInterceptor interceptor(req->url(), std::move(job));
 
   req->Start();
diff --git a/remoting/host/token_validator_factory_impl_unittest.cc b/remoting/host/token_validator_factory_impl_unittest.cc
index 1d17b6f..298f3f0 100644
--- a/remoting/host/token_validator_factory_impl_unittest.cc
+++ b/remoting/host/token_validator_factory_impl_unittest.cc
@@ -48,11 +48,10 @@
 
   ~TestURLRequestInterceptor() override = default;
 
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    return new net::URLRequestTestJob(request, network_delegate, headers_,
-                                      response_, true);
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
+    return std::make_unique<net::URLRequestTestJob>(request, headers_,
+                                                    response_, true);
   }
 
  private:
@@ -70,11 +69,10 @@
 
   ~TestFailingURLRequestInterceptor() override = default;
 
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    return new net::URLRequestFailedJob(request, network_delegate,
-                                        failure_phase_, net_error_);
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
+    return std::make_unique<net::URLRequestFailedJob>(request, failure_phase_,
+                                                      net_error_);
   }
 
  private:
diff --git a/services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader_unittest.cc b/services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader_unittest.cc
index 264950e9..1102ad8 100644
--- a/services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader_unittest.cc
+++ b/services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader_unittest.cc
@@ -27,6 +27,7 @@
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_interceptor.h"
+#include "net/url_request/url_request_job.h"
 #include "services/cert_verifier/cert_net_url_loader/cert_net_fetcher_test.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -226,9 +227,8 @@
 
  private:
   // URLRequestInterceptor implementation:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
     EXPECT_TRUE(request->disable_secure_dns());
     *invoked_interceptor_ = true;
     return nullptr;
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 9660215..26c1027 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -160,15 +160,16 @@
 class URLRequestMultipleWritesJob : public net::URLRequestJob {
  public:
   URLRequestMultipleWritesJob(net::URLRequest* request,
-                              net::NetworkDelegate* network_delegate,
                               std::list<std::string> packets,
                               net::Error net_error,
                               bool async_reads)
-      : URLRequestJob(request, network_delegate),
+      : URLRequestJob(request),
         packets_(std::move(packets)),
         net_error_(net_error),
         async_reads_(async_reads) {}
 
+  ~URLRequestMultipleWritesJob() override = default;
+
   // net::URLRequestJob implementation:
   void Start() override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -199,8 +200,6 @@
   }
 
  private:
-  ~URLRequestMultipleWritesJob() override {}
-
   void StartAsync() { NotifyHeadersComplete(); }
 
   std::list<std::string> packets_;
@@ -225,12 +224,10 @@
   static GURL GetURL() { return GURL("http://foo"); }
 
   // URLRequestInterceptor implementation:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    return new URLRequestMultipleWritesJob(request, network_delegate,
-                                           std::move(packets_), net_error_,
-                                           async_reads_);
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
+    return std::make_unique<URLRequestMultipleWritesJob>(
+        request, std::move(packets_), net_error_, async_reads_);
   }
 
  private:
@@ -247,10 +244,10 @@
   // If |fill_entire_buffer| is true, each read fills the entire read buffer at
   // once. Otherwise, one byte is read at a time.
   URLRequestEternalSyncReadsJob(net::URLRequest* request,
-                                net::NetworkDelegate* network_delegate,
                                 bool fill_entire_buffer)
-      : URLRequestJob(request, network_delegate),
-        fill_entire_buffer_(fill_entire_buffer) {}
+      : URLRequestJob(request), fill_entire_buffer_(fill_entire_buffer) {}
+
+  ~URLRequestEternalSyncReadsJob() override = default;
 
   // net::URLRequestJob implementation:
   void Start() override {
@@ -271,8 +268,6 @@
   }
 
  private:
-  ~URLRequestEternalSyncReadsJob() override {}
-
   void StartAsync() { NotifyHeadersComplete(); }
 
   const bool fill_entire_buffer_;
@@ -293,16 +288,15 @@
   static GURL GetFillBufferURL() { return GURL("http://eternal/fill-buffer"); }
 
   // URLRequestInterceptor implementation:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
     if (request->url() == GetSingleByteURL()) {
-      return new URLRequestEternalSyncReadsJob(request, network_delegate,
-                                               false /* fill_entire_buffer */);
+      return std::make_unique<URLRequestEternalSyncReadsJob>(
+          request, false /* fill_entire_buffer */);
     }
     if (request->url() == GetFillBufferURL()) {
-      return new URLRequestEternalSyncReadsJob(request, network_delegate,
-                                               true /* fill_entire_buffer */);
+      return std::make_unique<URLRequestEternalSyncReadsJob>(
+          request, true /* fill_entire_buffer */);
     }
     return nullptr;
   }
@@ -319,13 +313,14 @@
   // once. Otherwise, one byte is read at a time.
   URLRequestSimulatedCacheJob(
       net::URLRequest* request,
-      net::NetworkDelegate* network_delegate,
       scoped_refptr<net::IOBuffer>* simulated_cache_dest,
       bool use_text_plain)
-      : URLRequestJob(request, network_delegate),
+      : URLRequestJob(request),
         simulated_cache_dest_(simulated_cache_dest),
         use_text_plain_(use_text_plain) {}
 
+  ~URLRequestSimulatedCacheJob() override = default;
+
   // net::URLRequestJob implementation:
   void Start() override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -356,7 +351,6 @@
   }
 
  private:
-  ~URLRequestSimulatedCacheJob() override {}
   void StartAsync() { NotifyHeadersComplete(); }
 
   scoped_refptr<net::IOBuffer>* simulated_cache_dest_;
@@ -374,11 +368,10 @@
       : simulated_cache_dest_(simulated_cache_dest),
         use_text_plain_(use_text_plain) {}
 
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    return new URLRequestSimulatedCacheJob(
-        request, network_delegate, simulated_cache_dest_, use_text_plain_);
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
+    return std::make_unique<URLRequestSimulatedCacheJob>(
+        request, simulated_cache_dest_, use_text_plain_);
   }
 
  private:
@@ -392,16 +385,16 @@
  public:
   // |transport_info| is subsequently passed to the OnConnected() callback.
   URLRequestFakeTransportInfoJob(net::URLRequest* request,
-                                 net::NetworkDelegate* network_delegate,
                                  const net::TransportInfo& transport_info)
-      : URLRequestJob(request, network_delegate),
-        transport_info_(transport_info) {}
+      : URLRequestJob(request), transport_info_(transport_info) {}
 
   URLRequestFakeTransportInfoJob(const URLRequestFakeTransportInfoJob&) =
       delete;
   URLRequestFakeTransportInfoJob& operator=(
       const URLRequestFakeTransportInfoJob&) = delete;
 
+  ~URLRequestFakeTransportInfoJob() override = default;
+
   // net::URLRequestJob implementation:
   void Start() override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -412,8 +405,6 @@
   int ReadRawData(net::IOBuffer* buf, int buf_size) override { return 0; }
 
  private:
-  ~URLRequestFakeTransportInfoJob() override = default;
-
   void StartAsync() {
     const int result = NotifyConnected(transport_info_);
     if (result != net::OK) {
@@ -446,14 +437,10 @@
       delete;
 
   // URLRequestInterceptor implementation:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    // NOTE: We cannot use make_unique() because
-    // URLRequestFakeTransportInfoJob's destructor is private, which prevents
-    // use of unique_ptr.
-    return new URLRequestFakeTransportInfoJob(request, network_delegate,
-                                              transport_info_);
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
+    return std::make_unique<URLRequestFakeTransportInfoJob>(request,
+                                                            transport_info_);
   }
 
  private:
@@ -2877,16 +2864,16 @@
 class MockHTTPSURLRequestJob : public net::URLRequestTestJob {
  public:
   MockHTTPSURLRequestJob(net::URLRequest* request,
-                         net::NetworkDelegate* network_delegate,
                          const std::string& response_headers,
                          const std::string& response_data,
                          bool auto_advance)
       : net::URLRequestTestJob(request,
-                               network_delegate,
                                response_headers,
                                response_data,
                                auto_advance) {}
 
+  ~MockHTTPSURLRequestJob() override = default;
+
   // net::URLRequestTestJob:
   void GetResponseInfo(net::HttpResponseInfo* info) override {
     // Get the original response info, but override the SSL info.
@@ -2897,8 +2884,6 @@
   }
 
  private:
-  ~MockHTTPSURLRequestJob() override {}
-
   DISALLOW_COPY_AND_ASSIGN(MockHTTPSURLRequestJob);
 };
 
@@ -2908,11 +2893,10 @@
   ~MockHTTPSJobURLRequestInterceptor() override {}
 
   // net::URLRequestInterceptor:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    return new MockHTTPSURLRequestJob(request, network_delegate, std::string(),
-                                      "dummy response", true);
+  std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest(
+      net::URLRequest* request) const override {
+    return std::make_unique<MockHTTPSURLRequestJob>(request, std::string(),
+                                                    "dummy response", true);
   }
 };
 
diff --git a/storage/browser/BUILD.gn b/storage/browser/BUILD.gn
index 48be0d9..c65fcf46 100644
--- a/storage/browser/BUILD.gn
+++ b/storage/browser/BUILD.gn
@@ -275,6 +275,8 @@
     "file_system/copy_or_move_operation_delegate_unittest.cc",
     "file_system/dragged_file_util_unittest.cc",
     "file_system/external_mount_points_unittest.cc",
+    "file_system/file_stream_reader_test.cc",
+    "file_system/file_stream_reader_test.h",
     "file_system/file_stream_test_utils.cc",
     "file_system/file_stream_test_utils.h",
     "file_system/file_system_context_unittest.cc",
diff --git a/storage/browser/file_system/file_stream_reader_test.cc b/storage/browser/file_system/file_stream_reader_test.cc
new file mode 100644
index 0000000..1ce59c6
--- /dev/null
+++ b/storage/browser/file_system/file_stream_reader_test.cc
@@ -0,0 +1,12 @@
+// 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 "storage/browser/file_system/file_stream_reader_test.h"
+
+namespace storage {
+
+const base::StringPiece FileStreamReaderTest::kTestFileName;
+const base::StringPiece FileStreamReaderTest::kTestData;
+
+}  // namespace storage
diff --git a/storage/browser/file_system/file_stream_reader_test.h b/storage/browser/file_system/file_stream_reader_test.h
new file mode 100644
index 0000000..b1c6a19e
--- /dev/null
+++ b/storage/browser/file_system/file_stream_reader_test.h
@@ -0,0 +1,276 @@
+// 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 STORAGE_BROWSER_FILE_SYSTEM_FILE_STREAM_READER_TEST_H_
+#define STORAGE_BROWSER_FILE_SYSTEM_FILE_STREAM_READER_TEST_H_
+
+#include "base/bind_helpers.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "storage/browser/file_system/file_stream_reader.h"
+#include "storage/browser/file_system/file_stream_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace storage {
+
+// An interface for derived FileStreamReader to implement.
+// This allows multiple FileStreamReader implementations can share the
+// same test framework.  Tests should implement CreateFileReader, WriteFile, and
+// TouchFile to manipulate files for their particular implementation.
+class FileStreamReaderTest : public testing::Test {
+ public:
+  static constexpr base::StringPiece kTestFileName = "test.dat";
+  static constexpr base::StringPiece kTestData = "0123456789";
+
+  virtual std::unique_ptr<FileStreamReader> CreateFileReader(
+      const std::string& file_name,
+      int64_t initial_offset,
+      const base::Time& expected_modification_time) = 0;
+  virtual void WriteFile(const std::string& file_name,
+                         const char* buf,
+                         size_t buf_size,
+                         base::Time* modification_time) = 0;
+  // Adjust a file's last modified time by |delta|.
+  virtual void TouchFile(const std::string& file_name,
+                         base::TimeDelta delta) = 0;
+  virtual void EnsureFileTaskFinished() {}
+
+  base::Time test_file_modification_time() const {
+    return test_file_modification_time_;
+  }
+
+  void WriteTestFile() {
+    WriteFile(kTestFileName.data(), kTestData.data(), kTestData.size(),
+              &test_file_modification_time_);
+  }
+
+  static void NeverCalled(int unused) { ADD_FAILURE(); }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
+  base::Time test_file_modification_time_;
+};
+
+template <class SubClass>
+class FileStreamReaderTypedTest : public SubClass {
+ public:
+  void SetUp() override {
+    SubClass::SetUp();
+    this->WriteTestFile();
+  }
+};
+
+TYPED_TEST_SUITE_P(FileStreamReaderTypedTest);
+
+TYPED_TEST_P(FileStreamReaderTypedTest, NonExistent) {
+  const char kFileName[] = "nonexistent";
+  std::unique_ptr<FileStreamReader> reader(
+      this->CreateFileReader(kFileName, 0, base::Time()));
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, 10, &result);
+  ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result);
+  ASSERT_EQ(0U, data.size());
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, Empty) {
+  const char kFileName[] = "empty";
+  this->WriteFile(kFileName, nullptr, 0, nullptr);
+
+  std::unique_ptr<FileStreamReader> reader(
+      this->CreateFileReader(kFileName, 0, base::Time()));
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, 10, &result);
+  ASSERT_EQ(net::OK, result);
+  ASSERT_EQ(0U, data.size());
+
+  net::TestInt64CompletionCallback callback;
+  int64_t length_result = reader->GetLength(callback.callback());
+  if (length_result == net::ERR_IO_PENDING)
+    length_result = callback.WaitForResult();
+  ASSERT_EQ(0, result);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, GetLengthNormal) {
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      this->kTestFileName.data(), 0, this->test_file_modification_time()));
+  net::TestInt64CompletionCallback callback;
+  int64_t result = reader->GetLength(callback.callback());
+  if (result == net::ERR_IO_PENDING)
+    result = callback.WaitForResult();
+  ASSERT_EQ(static_cast<int64_t>(this->kTestData.size()), result);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, GetLengthAfterModified) {
+  this->TouchFile(this->kTestFileName.data(), base::TimeDelta::FromSeconds(10));
+
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      this->kTestFileName.data(), 0, this->test_file_modification_time()));
+  net::TestInt64CompletionCallback callback1;
+  int64_t result = reader->GetLength(callback1.callback());
+  if (result == net::ERR_IO_PENDING)
+    result = callback1.WaitForResult();
+  ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
+
+  // With nullptr expected modification time this should work.
+  reader = this->CreateFileReader(this->kTestFileName.data(), 0, base::Time());
+  net::TestInt64CompletionCallback callback2;
+  result = reader->GetLength(callback2.callback());
+  if (result == net::ERR_IO_PENDING)
+    result = callback2.WaitForResult();
+  ASSERT_EQ(static_cast<int64_t>(this->kTestData.size()), result);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, GetLengthWithOffset) {
+  std::unique_ptr<FileStreamReader> reader(
+      this->CreateFileReader(this->kTestFileName.data(), 3, base::Time()));
+  net::TestInt64CompletionCallback callback;
+  int64_t result = reader->GetLength(callback.callback());
+  if (result == net::ERR_IO_PENDING)
+    result = callback.WaitForResult();
+  // Initial offset does not affect the result of GetLength.
+  ASSERT_EQ(static_cast<int64_t>(this->kTestData.size()), result);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, ReadNormal) {
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      this->kTestFileName.data(), 0, this->test_file_modification_time()));
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
+  ASSERT_EQ(net::OK, result);
+  ASSERT_EQ(this->kTestData, data);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, ReadAfterModified) {
+  // Touch file so that the file's modification time becomes different
+  // from what we expect. Note that the resolution on some filesystems
+  // is 1s so we can't test with deltas less than that.
+  this->TouchFile(this->kTestFileName.data(), base::TimeDelta::FromSeconds(-1));
+
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      this->kTestFileName.data(), 0, this->test_file_modification_time()));
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
+  ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
+  ASSERT_EQ(0U, data.size());
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, ReadAfterModifiedLessThanThreshold) {
+  // Due to precision loss converting int64_t->double->int64_t (e.g. through
+  // Blink) the expected/actual time may vary by microseconds. With
+  // modification time delta < 10us this should work.
+  this->TouchFile(this->kTestFileName.data(),
+                  base::TimeDelta::FromMicroseconds(1));
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      this->kTestFileName.data(), 0, this->test_file_modification_time()));
+  int result = 0;
+  std::string data;
+
+  ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
+  ASSERT_EQ(net::OK, result);
+  ASSERT_EQ(this->kTestData, data);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, ReadAfterModifiedWithMatchingTimes) {
+  this->TouchFile(this->kTestFileName.data(), base::TimeDelta());
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      this->kTestFileName.data(), 0, this->test_file_modification_time()));
+  int result = 0;
+  std::string data;
+
+  ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
+  ASSERT_EQ(net::OK, result);
+  ASSERT_EQ(this->kTestData, data);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, ReadAfterModifiedWithoutExpectedTime) {
+  this->TouchFile(this->kTestFileName.data(), base::TimeDelta::FromSeconds(-1));
+  std::unique_ptr<FileStreamReader> reader(
+      this->CreateFileReader(this->kTestFileName.data(), 0, base::Time()));
+  int result = 0;
+  std::string data;
+
+  ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
+  ASSERT_EQ(net::OK, result);
+  ASSERT_EQ(this->kTestData, data);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, ReadWithOffset) {
+  std::unique_ptr<FileStreamReader> reader(
+      this->CreateFileReader(this->kTestFileName.data(), 3, base::Time()));
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
+  ASSERT_EQ(net::OK, result);
+
+  ASSERT_EQ(this->kTestData.substr(3), data);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, ReadWithNegativeOffset) {
+  std::unique_ptr<FileStreamReader> reader(
+      this->CreateFileReader(this->kTestFileName.data(), -1, base::Time()));
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, 1, &result);
+  ASSERT_EQ(net::ERR_INVALID_ARGUMENT, result);
+  ASSERT_EQ(data.size(), 0u);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, ReadWithOffsetLargerThanFile) {
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      this->kTestFileName.data(), this->kTestData.size() + 1, base::Time()));
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, 1, &result);
+  ASSERT_EQ(data.size(), 0u);
+  ASSERT_EQ(net::OK, result);
+}
+
+TYPED_TEST_P(FileStreamReaderTypedTest, DeleteWithUnfinishedRead) {
+  std::unique_ptr<FileStreamReader> reader(
+      this->CreateFileReader(this->kTestFileName.data(), 0, base::Time()));
+
+  net::TestCompletionCallback callback;
+  scoped_refptr<net::IOBufferWithSize> buf =
+      base::MakeRefCounted<net::IOBufferWithSize>(this->kTestData.size());
+  int rv = reader->Read(buf.get(), buf->size(),
+                        base::BindOnce(&FileStreamReaderTest::NeverCalled));
+  if (rv < 0)
+    ASSERT_EQ(rv, net::ERR_IO_PENDING);
+
+  // Delete immediately.
+  // Should not crash; nor should NeverCalled be callback.
+  reader = nullptr;
+  this->EnsureFileTaskFinished();
+}
+
+REGISTER_TYPED_TEST_SUITE_P(FileStreamReaderTypedTest,
+                            NonExistent,
+                            Empty,
+                            GetLengthNormal,
+                            GetLengthAfterModified,
+                            GetLengthWithOffset,
+                            ReadNormal,
+                            ReadAfterModified,
+                            ReadAfterModifiedLessThanThreshold,
+                            ReadAfterModifiedWithMatchingTimes,
+                            ReadAfterModifiedWithoutExpectedTime,
+                            ReadWithOffset,
+                            ReadWithNegativeOffset,
+                            ReadWithOffsetLargerThanFile,
+                            DeleteWithUnfinishedRead);
+
+}  // namespace storage
+
+#endif  // STORAGE_BROWSER_FILE_SYSTEM_FILE_STREAM_READER_TEST_H_
diff --git a/storage/browser/file_system/file_system_file_stream_reader_unittest.cc b/storage/browser/file_system/file_system_file_stream_reader_unittest.cc
index dc5f599..9dbc666 100644
--- a/storage/browser/file_system/file_system_file_stream_reader_unittest.cc
+++ b/storage/browser/file_system/file_system_file_stream_reader_unittest.cc
@@ -21,6 +21,8 @@
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "storage/browser/file_system/external_mount_points.h"
+
+#include "storage/browser/file_system/file_stream_reader_test.h"
 #include "storage/browser/file_system/file_stream_test_utils.h"
 #include "storage/browser/file_system/file_system_context.h"
 #include "storage/browser/file_system/file_system_file_util.h"
@@ -33,58 +35,44 @@
 namespace storage {
 
 namespace {
-
 const char kURLOrigin[] = "http://remote/";
-const char kTestFileName[] = "test.dat";
-const char kTestData[] = "0123456789";
-const int kTestDataSize = base::size(kTestData) - 1;
-
-void NeverCalled(int unused) {
-  ADD_FAILURE();
-}
-
 }  // namespace
 
-class FileSystemFileStreamReaderTest : public testing::Test {
+class FileSystemFileStreamReaderTest : public FileStreamReaderTest {
  public:
   FileSystemFileStreamReaderTest() = default;
 
   void SetUp() override {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(dir_.CreateUniqueTempDir());
 
     file_system_context_ =
-        CreateFileSystemContextForTesting(nullptr, temp_dir_.GetPath());
+        CreateFileSystemContextForTesting(nullptr, dir_.GetPath());
 
-    file_system_context_->OpenFileSystem(url::Origin::Create(GURL(kURLOrigin)),
-                                         kFileSystemTypeTemporary,
-                                         OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
-                                         base::BindOnce(&OnOpenFileSystem));
+    file_system_context_->OpenFileSystem(
+        url::Origin::Create(GURL(kURLOrigin)), kFileSystemTypeTemporary,
+        OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+        base::BindOnce([](const GURL& root_url, const std::string& name,
+                          base::File::Error result) {
+          ASSERT_EQ(base::File::FILE_OK, result);
+        }));
     base::RunLoop().RunUntilIdle();
-
-    WriteFile(kTestFileName, kTestData, kTestDataSize,
-              &test_file_modification_time_);
   }
 
   void TearDown() override { base::RunLoop().RunUntilIdle(); }
 
- protected:
-  FileSystemFileStreamReader* CreateFileReader(
+  std::unique_ptr<FileStreamReader> CreateFileReader(
       const std::string& file_name,
       int64_t initial_offset,
-      const base::Time& expected_modification_time) {
-    return new FileSystemFileStreamReader(
+      const base::Time& expected_modification_time) override {
+    return FileStreamReader::CreateForFileSystemFile(
         file_system_context_.get(), GetFileSystemURL(file_name), initial_offset,
         expected_modification_time);
   }
 
-  base::Time test_file_modification_time() const {
-    return test_file_modification_time_;
-  }
-
   void WriteFile(const std::string& file_name,
                  const char* buf,
-                 int buf_size,
-                 base::Time* modification_time) {
+                 size_t buf_size,
+                 base::Time* modification_time) override {
     FileSystemURL url = GetFileSystemURL(file_name);
 
     ASSERT_EQ(base::File::FILE_OK,
@@ -99,11 +87,17 @@
       *modification_time = file_info.last_modified;
   }
 
- private:
-  static void OnOpenFileSystem(const GURL& root_url,
-                               const std::string& name,
-                               base::File::Error result) {
-    ASSERT_EQ(base::File::FILE_OK, result);
+  void TouchFile(const std::string& file_name, base::TimeDelta delta) override {
+    FileSystemURL url = GetFileSystemURL(file_name);
+
+    base::File::Info file_info;
+    ASSERT_EQ(base::File::FILE_OK,
+              AsyncFileTestHelper::GetMetadata(file_system_context_.get(), url,
+                                               &file_info));
+    ASSERT_EQ(base::File::FILE_OK,
+              AsyncFileTestHelper::TouchFile(file_system_context_.get(), url,
+                                             file_info.last_accessed,
+                                             file_info.last_modified + delta));
   }
 
   FileSystemURL GetFileSystemURL(const std::string& file_name) {
@@ -112,140 +106,13 @@
         base::FilePath().AppendASCII(file_name));
   }
 
-  base::test::SingleThreadTaskEnvironment task_environment_{
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
-  base::ScopedTempDir temp_dir_;
+ private:
+  base::ScopedTempDir dir_;
   scoped_refptr<FileSystemContext> file_system_context_;
-  base::Time test_file_modification_time_;
 };
 
-TEST_F(FileSystemFileStreamReaderTest, NonExistent) {
-  const char kFileName[] = "nonexistent";
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kFileName, 0, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, 10, &result);
-  ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result);
-  ASSERT_EQ(0U, data.size());
-}
-
-TEST_F(FileSystemFileStreamReaderTest, Empty) {
-  const char kFileName[] = "empty";
-  WriteFile(kFileName, nullptr, 0, nullptr);
-
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kFileName, 0, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, 10, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(0U, data.size());
-
-  net::TestInt64CompletionCallback callback;
-  int64_t length_result = reader->GetLength(callback.callback());
-  if (length_result == net::ERR_IO_PENDING)
-    length_result = callback.WaitForResult();
-  ASSERT_EQ(0, length_result);
-}
-
-TEST_F(FileSystemFileStreamReaderTest, GetLengthNormal) {
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kTestFileName, 0, test_file_modification_time()));
-  net::TestInt64CompletionCallback callback;
-  int64_t result = reader->GetLength(callback.callback());
-  if (result == net::ERR_IO_PENDING)
-    result = callback.WaitForResult();
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(FileSystemFileStreamReaderTest, GetLengthAfterModified) {
-  // Pass a fake expected modifictaion time so that the expectation fails.
-  base::Time fake_expected_modification_time =
-      test_file_modification_time() - base::TimeDelta::FromSeconds(10);
-
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kTestFileName, 0, fake_expected_modification_time));
-  net::TestInt64CompletionCallback callback1;
-  int64_t result = reader->GetLength(callback1.callback());
-  if (result == net::ERR_IO_PENDING)
-    result = callback1.WaitForResult();
-  ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
-
-  // With nullptr expected modification time this should work.
-  reader.reset(CreateFileReader(kTestFileName, 0, base::Time()));
-  net::TestInt64CompletionCallback callback2;
-  result = reader->GetLength(callback2.callback());
-  if (result == net::ERR_IO_PENDING)
-    result = callback2.WaitForResult();
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(FileSystemFileStreamReaderTest, GetLengthWithOffset) {
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kTestFileName, 3, base::Time()));
-  net::TestInt64CompletionCallback callback;
-  int64_t result = reader->GetLength(callback.callback());
-  if (result == net::ERR_IO_PENDING)
-    result = callback.WaitForResult();
-  // Initial offset does not affect the result of GetLength.
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(FileSystemFileStreamReaderTest, ReadNormal) {
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kTestFileName, 0, test_file_modification_time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(kTestData, data);
-}
-
-TEST_F(FileSystemFileStreamReaderTest, ReadAfterModified) {
-  // Pass a fake expected modifictaion time so that the expectation fails.
-  base::Time fake_expected_modification_time =
-      test_file_modification_time() - base::TimeDelta::FromSeconds(10);
-
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kTestFileName, 0, fake_expected_modification_time));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
-  ASSERT_EQ(0U, data.size());
-
-  // With nullptr expected modification time this should work.
-  data.clear();
-  reader.reset(CreateFileReader(kTestFileName, 0, base::Time()));
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(kTestData, data);
-}
-
-TEST_F(FileSystemFileStreamReaderTest, ReadWithOffset) {
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kTestFileName, 3, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(&kTestData[3], data);
-}
-
-TEST_F(FileSystemFileStreamReaderTest, DeleteWithUnfinishedRead) {
-  std::unique_ptr<FileSystemFileStreamReader> reader(
-      CreateFileReader(kTestFileName, 0, base::Time()));
-
-  net::TestCompletionCallback callback;
-  scoped_refptr<net::IOBufferWithSize> buf =
-      base::MakeRefCounted<net::IOBufferWithSize>(kTestDataSize);
-  int rv = reader->Read(buf.get(), buf->size(), base::BindOnce(&NeverCalled));
-  ASSERT_TRUE(rv == net::ERR_IO_PENDING || rv >= 0);
-
-  // Delete immediately.
-  // Should not crash; nor should NeverCalled be callback.
-  reader.reset();
-}
+INSTANTIATE_TYPED_TEST_SUITE_P(FileSystem,
+                               FileStreamReaderTypedTest,
+                               FileSystemFileStreamReaderTest);
 
 }  // namespace storage
diff --git a/storage/browser/file_system/local_file_stream_reader_unittest.cc b/storage/browser/file_system/local_file_stream_reader_unittest.cc
index 7b7887d..d1f6bf8 100644
--- a/storage/browser/file_system/local_file_stream_reader_unittest.cc
+++ b/storage/browser/file_system/local_file_stream_reader_unittest.cc
@@ -26,38 +26,19 @@
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
+#include "storage/browser/file_system/file_stream_reader_test.h"
 #include "storage/browser/file_system/file_stream_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace storage {
 
-namespace {
-
-const char kTestData[] = "0123456789";
-const int kTestDataSize = base::size(kTestData) - 1;
-
-void NeverCalled(int) {
-  ADD_FAILURE();
-}
-
-void QuitLoop() {
-  base::RunLoop::QuitCurrentWhenIdleDeprecated();
-}
-
-}  // namespace
-
-class LocalFileStreamReaderTest : public testing::Test {
+class LocalFileStreamReaderTest : public FileStreamReaderTest {
  public:
   LocalFileStreamReaderTest() : file_thread_("TestFileThread") {}
 
   void SetUp() override {
-    ASSERT_TRUE(file_thread_.Start());
     ASSERT_TRUE(dir_.CreateUniqueTempDir());
-
-    base::WriteFile(test_path(), kTestData);
-    base::File::Info info;
-    ASSERT_TRUE(base::GetFileInfo(test_path(), &info));
-    test_file_modification_time_ = info.last_modified;
+    ASSERT_TRUE(file_thread_.Start());
   }
 
   void TearDown() override {
@@ -67,195 +48,57 @@
     base::RunLoop().RunUntilIdle();
   }
 
- protected:
-  LocalFileStreamReader* CreateFileReader(
-      const base::FilePath& path,
+  std::unique_ptr<FileStreamReader> CreateFileReader(
+      const std::string& file_name,
       int64_t initial_offset,
-      const base::Time& expected_modification_time) {
-    return new LocalFileStreamReader(file_task_runner(), path, initial_offset,
-                                     expected_modification_time);
+      const base::Time& expected_modification_time) override {
+    return FileStreamReader::CreateForLocalFile(
+        file_task_runner(), test_dir().AppendASCII(file_name), initial_offset,
+        expected_modification_time);
   }
 
-  void TouchTestFile(base::TimeDelta delta) {
-    base::Time new_modified_time = test_file_modification_time() + delta;
-    ASSERT_TRUE(base::TouchFile(test_path(), test_file_modification_time(),
-                                new_modified_time));
+  void WriteFile(const std::string& file_name,
+                 const char* buf,
+                 size_t buf_size,
+                 base::Time* modification_time) override {
+    base::FilePath path = test_dir().AppendASCII(file_name);
+    base::WriteFile(path, buf, buf_size);
+
+    base::File::Info file_info;
+    ASSERT_TRUE(base::GetFileInfo(path, &file_info));
+    if (modification_time)
+      *modification_time = file_info.last_modified;
   }
 
+  void TouchFile(const std::string& file_name, base::TimeDelta delta) override {
+    base::FilePath path = test_dir().AppendASCII(file_name);
+    base::File::Info file_info;
+    ASSERT_TRUE(base::GetFileInfo(path, &file_info));
+    ASSERT_TRUE(base::TouchFile(path, file_info.last_accessed,
+                                file_info.last_modified + delta));
+  }
+
+  void EnsureFileTaskFinished() override {
+    base::RunLoop run_loop;
+    file_task_runner()->PostTaskAndReply(FROM_HERE, base::DoNothing(),
+
+                                         run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  base::FilePath test_dir() const { return dir_.GetPath(); }
+
   base::SingleThreadTaskRunner* file_task_runner() const {
     return file_thread_.task_runner().get();
   }
 
-  base::FilePath test_dir() const { return dir_.GetPath(); }
-  base::FilePath test_path() const {
-    return dir_.GetPath().AppendASCII("test");
-  }
-  base::Time test_file_modification_time() const {
-    return test_file_modification_time_;
-  }
-
-  void EnsureFileTaskFinished() {
-    file_task_runner()->PostTaskAndReply(FROM_HERE, base::DoNothing(),
-                                         base::BindOnce(&QuitLoop));
-    base::RunLoop().Run();
-  }
-
  private:
-  base::test::SingleThreadTaskEnvironment task_environment_{
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
-  base::Thread file_thread_;
   base::ScopedTempDir dir_;
-  base::Time test_file_modification_time_;
+  base::Thread file_thread_;
 };
 
-TEST_F(LocalFileStreamReaderTest, NonExistent) {
-  base::FilePath nonexistent_path = test_dir().AppendASCII("nonexistent");
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(nonexistent_path, 0, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, 10, &result);
-  ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result);
-  ASSERT_EQ(0U, data.size());
-}
-
-TEST_F(LocalFileStreamReaderTest, Empty) {
-  base::FilePath empty_path = test_dir().AppendASCII("empty");
-  base::File file(empty_path, base::File::FLAG_CREATE | base::File::FLAG_READ);
-  ASSERT_TRUE(file.IsValid());
-  file.Close();
-
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(empty_path, 0, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, 10, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(0U, data.size());
-
-  net::TestInt64CompletionCallback callback;
-  int64_t length_result = reader->GetLength(callback.callback());
-  if (length_result == net::ERR_IO_PENDING)
-    length_result = callback.WaitForResult();
-  ASSERT_EQ(0, result);
-}
-
-TEST_F(LocalFileStreamReaderTest, GetLengthNormal) {
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  net::TestInt64CompletionCallback callback;
-  int64_t result = reader->GetLength(callback.callback());
-  if (result == net::ERR_IO_PENDING)
-    result = callback.WaitForResult();
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(LocalFileStreamReaderTest, GetLengthAfterModified) {
-  // Touch file so that the file's modification time becomes different
-  // from what we expect.
-  TouchTestFile(base::TimeDelta::FromSeconds(-1));
-
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  net::TestInt64CompletionCallback callback1;
-  int64_t result = reader->GetLength(callback1.callback());
-  if (result == net::ERR_IO_PENDING)
-    result = callback1.WaitForResult();
-  ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
-
-  // With nullptr expected modification time this should work.
-  reader.reset(CreateFileReader(test_path(), 0, base::Time()));
-  net::TestInt64CompletionCallback callback2;
-  result = reader->GetLength(callback2.callback());
-  if (result == net::ERR_IO_PENDING)
-    result = callback2.WaitForResult();
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(LocalFileStreamReaderTest, GetLengthWithOffset) {
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(test_path(), 3, base::Time()));
-  net::TestInt64CompletionCallback callback;
-  int64_t result = reader->GetLength(callback.callback());
-  if (result == net::ERR_IO_PENDING)
-    result = callback.WaitForResult();
-  // Initial offset does not affect the result of GetLength.
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(LocalFileStreamReaderTest, ReadNormal) {
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(kTestData, data);
-}
-
-TEST_F(LocalFileStreamReaderTest, ReadAfterModified) {
-  // Touch file so that the file's modification time becomes different
-  // from what we expect. Note that the resolution on some filesystems
-  // is 1s so we can't test with deltas less than that.
-  TouchTestFile(base::TimeDelta::FromSeconds(-1));
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
-  EXPECT_EQ(0U, data.size());
-
-  // Due to precision loss converting int64_t->double->int64_t (e.g. through
-  // Blink) the expected/actual time may vary by microseconds. With
-  // modification time delta < 10us this should work.
-  TouchTestFile(base::TimeDelta::FromMicroseconds(1));
-  data.clear();
-  reader.reset(CreateFileReader(test_path(), 0, test_file_modification_time()));
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  EXPECT_EQ(net::OK, result);
-  EXPECT_EQ(kTestData, data);
-
-  // With matching modification times time this should work.
-  TouchTestFile(base::TimeDelta());
-  data.clear();
-  reader.reset(CreateFileReader(test_path(), 0, test_file_modification_time()));
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  EXPECT_EQ(net::OK, result);
-  EXPECT_EQ(kTestData, data);
-
-  // And with nullptr expected modification time this should work.
-  data.clear();
-  reader.reset(CreateFileReader(test_path(), 0, base::Time()));
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  EXPECT_EQ(net::OK, result);
-  EXPECT_EQ(kTestData, data);
-}
-
-TEST_F(LocalFileStreamReaderTest, ReadWithOffset) {
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(test_path(), 3, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(&kTestData[3], data);
-}
-
-TEST_F(LocalFileStreamReaderTest, DeleteWithUnfinishedRead) {
-  std::unique_ptr<LocalFileStreamReader> reader(
-      CreateFileReader(test_path(), 0, base::Time()));
-
-  net::TestCompletionCallback callback;
-  scoped_refptr<net::IOBufferWithSize> buf =
-      base::MakeRefCounted<net::IOBufferWithSize>(kTestDataSize);
-  int rv = reader->Read(buf.get(), buf->size(), base::BindOnce(&NeverCalled));
-  ASSERT_TRUE(rv == net::ERR_IO_PENDING || rv >= 0);
-
-  // Delete immediately.
-  // Should not crash; nor should NeverCalled be callback.
-  reader.reset();
-  EnsureFileTaskFinished();
-}
+INSTANTIATE_TYPED_TEST_SUITE_P(Local,
+                               FileStreamReaderTypedTest,
+                               LocalFileStreamReaderTest);
 
 }  // namespace storage
diff --git a/storage/browser/file_system/memory_file_stream_reader_unittest.cc b/storage/browser/file_system/memory_file_stream_reader_unittest.cc
index f41efcc..685f49c 100644
--- a/storage/browser/file_system/memory_file_stream_reader_unittest.cc
+++ b/storage/browser/file_system/memory_file_stream_reader_unittest.cc
@@ -21,232 +21,69 @@
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "storage/browser/file_system/file_stream_reader.h"
+#include "storage/browser/file_system/file_stream_reader_test.h"
 #include "storage/browser/file_system/file_stream_test_utils.h"
 #include "storage/browser/file_system/obfuscated_file_util_memory_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace storage {
 
-namespace {
-
-const char kTestData[] = "0123456789";
-const int kTestDataSize = base::size(kTestData) - 1;
-
-}  // namespace
-
-class MemoryFileStreamReaderTest : public testing::Test {
+class MemoryFileStreamReaderTest : public FileStreamReaderTest {
  public:
-  MemoryFileStreamReaderTest() {}
+  MemoryFileStreamReaderTest() = default;
 
   void SetUp() override {
-    ASSERT_TRUE(file_system_directory_.CreateUniqueTempDir());
-    file_util_ = std::make_unique<ObfuscatedFileUtilMemoryDelegate>(
-        file_system_directory_.GetPath());
-
-    file_util_->CreateFileForTesting(
-        test_path(), base::span<const char>(kTestData, kTestDataSize));
-    base::File::Info info;
-    ASSERT_EQ(base::File::FILE_OK, file_util_->GetFileInfo(test_path(), &info));
-    test_file_modification_time_ = info.last_modified;
+    ASSERT_TRUE(dir_.CreateUniqueTempDir());
+    file_util_ = std::make_unique<ObfuscatedFileUtilMemoryDelegate>(test_dir());
   }
 
   void TearDown() override {
     // In memory operations should not have any residue in file system
     // directory.
-    EXPECT_TRUE(base::IsDirectoryEmpty(file_system_directory_.GetPath()));
+    EXPECT_TRUE(base::IsDirectoryEmpty(test_dir()));
   }
 
-  ObfuscatedFileUtilMemoryDelegate* file_util() { return file_util_.get(); }
-
- protected:
   std::unique_ptr<FileStreamReader> CreateFileReader(
-      const base::FilePath& path,
+      const std::string& file_name,
       int64_t initial_offset,
-      const base::Time& expected_modification_time) {
+      const base::Time& expected_modification_time) override {
     return FileStreamReader::CreateForMemoryFile(
-        base::ThreadTaskRunnerHandle::Get(), file_util_->GetWeakPtr(), path,
-        initial_offset, expected_modification_time);
+        base::ThreadTaskRunnerHandle::Get(), file_util_->GetWeakPtr(),
+        test_dir().AppendASCII(file_name), initial_offset,
+        expected_modification_time);
   }
 
-  void TouchTestFile(base::TimeDelta delta) {
-    base::Time new_modified_time = test_file_modification_time() + delta;
+  void WriteFile(const std::string& file_name,
+                 const char* buf,
+                 size_t buf_size,
+                 base::Time* modification_time) override {
+    base::FilePath path = test_dir().AppendASCII(file_name);
+    file_util_->CreateFileForTesting(path,
+                                     base::span<const char>(buf, buf_size));
+    base::File::Info file_info;
+    ASSERT_EQ(base::File::FILE_OK, file_util_->GetFileInfo(path, &file_info));
+    if (modification_time)
+      *modification_time = file_info.last_modified;
+  }
+
+  void TouchFile(const std::string& file_name, base::TimeDelta delta) override {
+    base::FilePath path = test_dir().AppendASCII(file_name);
+    base::File::Info file_info;
+    ASSERT_EQ(base::File::FILE_OK, file_util_->GetFileInfo(path, &file_info));
     ASSERT_EQ(base::File::FILE_OK,
-              file_util()->Touch(test_path(), test_file_modification_time(),
-                                 new_modified_time));
+              file_util_->Touch(path, file_info.last_accessed,
+                                file_info.last_modified + delta));
   }
 
-  base::FilePath test_dir() const { return file_system_directory_.GetPath(); }
-  base::FilePath test_path() const {
-    return file_system_directory_.GetPath().AppendASCII("test");
-  }
-  base::Time test_file_modification_time() const {
-    return test_file_modification_time_;
-  }
+  base::FilePath test_dir() const { return dir_.GetPath(); }
 
  private:
-  base::test::TaskEnvironment task_environment_;
-  base::ScopedTempDir file_system_directory_;
+  base::ScopedTempDir dir_;
   std::unique_ptr<ObfuscatedFileUtilMemoryDelegate> file_util_;
-  base::Time test_file_modification_time_;
 };
 
-TEST_F(MemoryFileStreamReaderTest, NonExistent) {
-  base::FilePath nonexistent_path = test_dir().AppendASCII("nonexistent");
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(nonexistent_path, 0, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, 10, &result);
-  ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result);
-  ASSERT_EQ(0U, data.size());
-}
-
-TEST_F(MemoryFileStreamReaderTest, Empty) {
-  base::FilePath empty_path = test_dir().AppendASCII("empty");
-  bool created;
-  EXPECT_EQ(base::File::FILE_OK,
-            file_util()->EnsureFileExists(empty_path, &created));
-
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(empty_path, 0, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, 10, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(0U, data.size());
-
-  int64_t length_result = GetLengthFromReader(reader.get());
-  ASSERT_EQ(0, length_result);
-}
-
-TEST_F(MemoryFileStreamReaderTest, GetLengthNormal) {
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int64_t result = GetLengthFromReader(reader.get());
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(MemoryFileStreamReaderTest, GetLengthAfterModified) {
-  // Touch file so that the file's modification time becomes different
-  // from what we expect.
-  TouchTestFile(base::TimeDelta::FromSeconds(-1));
-
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int64_t result = GetLengthFromReader(reader.get());
-  ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
-}
-
-TEST_F(MemoryFileStreamReaderTest, GetLengthAfterModifiedWithNoExpectedTime) {
-  // Touch file so that the file's modification time becomes different
-  // from what we expect.
-  TouchTestFile(base::TimeDelta::FromSeconds(-1));
-
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 0, base::Time()));
-  int64_t result = GetLengthFromReader(reader.get());
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(MemoryFileStreamReaderTest, GetLengthWithOffset) {
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 3, base::Time()));
-  int64_t result = GetLengthFromReader(reader.get());
-  // Initial offset does not affect the result of GetLength.
-  ASSERT_EQ(kTestDataSize, result);
-}
-
-TEST_F(MemoryFileStreamReaderTest, ReadNormal) {
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(kTestData, data);
-}
-
-TEST_F(MemoryFileStreamReaderTest, ReadAfterModified) {
-  // Touch file so that the file's modification time becomes different
-  // from what we expect. Note that the resolution on some filesystems
-  // is 1s so we can't test with deltas less than that.
-  TouchTestFile(base::TimeDelta::FromSeconds(-1));
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
-  EXPECT_EQ(0U, data.size());
-}
-
-TEST_F(MemoryFileStreamReaderTest, ReadAfterModifiedLessThanThreshold) {
-  // Due to precision loss converting int64_t->double->int64_t (e.g. through
-  // Blink) the expected/actual time may vary by microseconds. With
-  // modification time delta < 10us this should work.
-  TouchTestFile(base::TimeDelta::FromMicroseconds(1));
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int result = 0;
-  std::string data;
-
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  EXPECT_EQ(net::OK, result);
-  EXPECT_EQ(kTestData, data);
-}
-
-TEST_F(MemoryFileStreamReaderTest, ReadAfterModifiedWithMatchingTimes) {
-  TouchTestFile(base::TimeDelta());
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 0, test_file_modification_time()));
-  int result = 0;
-  std::string data;
-
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  EXPECT_EQ(net::OK, result);
-  EXPECT_EQ(kTestData, data);
-}
-
-TEST_F(MemoryFileStreamReaderTest, ReadAfterModifiedWithoutExpectedTime) {
-  TouchTestFile(base::TimeDelta());
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 0, base::Time()));
-  int result = 0;
-  std::string data;
-
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  EXPECT_EQ(net::OK, result);
-  EXPECT_EQ(kTestData, data);
-}
-
-TEST_F(MemoryFileStreamReaderTest, ReadWithOffset) {
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), 3, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(&kTestData[3], data);
-}
-
-TEST_F(MemoryFileStreamReaderTest, ReadWithNegativeOffset) {
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), -1, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, 1, &result);
-  ASSERT_EQ(net::ERR_INVALID_ARGUMENT, result);
-  ASSERT_EQ(data.size(), 0u);
-}
-
-TEST_F(MemoryFileStreamReaderTest, ReadWithOffsetLargerThanFile) {
-  std::unique_ptr<FileStreamReader> reader(
-      CreateFileReader(test_path(), kTestDataSize + 1, base::Time()));
-  int result = 0;
-  std::string data;
-  ReadFromReader(reader.get(), &data, 1, &result);
-  ASSERT_EQ(net::OK, result);
-  ASSERT_EQ(data.size(), 0u);
-}
+INSTANTIATE_TYPED_TEST_SUITE_P(Memory,
+                               FileStreamReaderTypedTest,
+                               MemoryFileStreamReaderTest);
 
 }  // namespace storage
diff --git a/storage/browser/test/async_file_test_helper.cc b/storage/browser/test/async_file_test_helper.cc
index bd8d4375..254ce18 100644
--- a/storage/browser/test/async_file_test_helper.cc
+++ b/storage/browser/test/async_file_test_helper.cc
@@ -269,4 +269,18 @@
   return status;
 }
 
+base::File::Error AsyncFileTestHelper::TouchFile(
+    FileSystemContext* context,
+    const FileSystemURL& url,
+    const base::Time& last_access_time,
+    const base::Time& last_modified_time) {
+  base::File::Error result = base::File::FILE_ERROR_FAILED;
+  base::RunLoop run_loop;
+  context->operation_runner()->TouchFile(
+      url, last_access_time, last_modified_time,
+      AssignAndQuitCallback(&run_loop, &result));
+  run_loop.Run();
+  return result;
+}
+
 }  // namespace storage
diff --git a/storage/browser/test/async_file_test_helper.h b/storage/browser/test/async_file_test_helper.h
index a3a4030..caee1e0f 100644
--- a/storage/browser/test/async_file_test_helper.h
+++ b/storage/browser/test/async_file_test_helper.h
@@ -107,6 +107,14 @@
       FileSystemType type,
       int64_t* usage,
       int64_t* quota);
+
+  // Modifies timestamps of a file or directory at |url| with
+  // |last_access_time| and |last_modified_time|. The function DOES NOT
+  // create a file unlike 'touch' command on Linux.
+  static base::File::Error TouchFile(FileSystemContext* context,
+                                     const FileSystemURL& url,
+                                     const base::Time& last_access_time,
+                                     const base::Time& last_modified_time);
 };
 
 }  // namespace storage
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 8cbb71af..7a7ec271 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -50,6 +50,11 @@
       "chromiumos_preflight"
     ]
   },
+  "chromeos-amd64-generic-lacros-dbg": {
+    "additional_compile_targets": [
+      "chrome"
+    ]
+  },
   "chromeos-amd64-generic-rel": {
     "additional_compile_targets": [
       "chromiumos_preflight"
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json
index f8ca91239..8a412a9 100644
--- a/testing/buildbot/chromium.ci.json
+++ b/testing/buildbot/chromium.ci.json
@@ -202505,6 +202505,11 @@
       "chromiumos_preflight"
     ]
   },
+  "chromeos-amd64-generic-lacros-dbg": {
+    "additional_compile_targets": [
+      "chrome"
+    ]
+  },
   "chromeos-amd64-generic-lacros-rel": {
     "additional_compile_targets": [
       "chrome"
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index f0e5edaf..601d907c 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -986,6 +986,11 @@
           'chromiumos_preflight',
         ],
       },
+      'chromeos-amd64-generic-lacros-dbg': {
+        'additional_compile_targets': [
+          'chrome',
+        ],
+      },
       'chromeos-amd64-generic-rel': {
         'additional_compile_targets': [
           'chromiumos_preflight',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 2ad7acb..874334b 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4720,7 +4720,6 @@
                     },
                     "enable_features": [
                         "OmniboxDisplayTitleForCurrentUrl",
-                        "OmniboxDocumentProvider",
                         "OmniboxMaxURLMatches",
                         "OmniboxMaxZeroSuggestMatches",
                         "OmniboxReactiveZeroSuggestionsOnNTPOmnibox",
@@ -4776,6 +4775,24 @@
             ]
         }
     ],
+    "OmniboxDocumentProviderNonDogfoodExperiments": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled ",
+                    "enable_features": [
+                        "OmniboxDocumentProvider"
+                    ]
+                }
+            ]
+        }
+    ],
     "OmniboxEnableClipboardProviderImageSuggestions": [
         {
             "platforms": [
@@ -6100,28 +6117,6 @@
             ]
         }
     ],
-    "SafeBrowsingDelayedWarnings": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "mouse": "true"
-                    },
-                    "enable_features": [
-                        "SafeBrowsingDelayedWarnings"
-                    ]
-                }
-            ]
-        }
-    ],
     "SafeBrowsingEnhancedProtectionAndroid": [
         {
             "platforms": [
@@ -6504,6 +6499,27 @@
             ]
         }
     ],
+    "SimplifiedUrlDisplay": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "AllDisplayExperimentsEnabled",
+                    "enable_features": [
+                        "OmniboxUIExperimentElideToRegistrableDomain",
+                        "OmniboxUIExperimentHideSteadyStateUrlPathQueryAndRefOnInteraction",
+                        "OmniboxUIExperimentRevealSteadyStateUrlPathQueryAndRefOnHover",
+                        "SafeBrowsingDelayedWarnings"
+                    ]
+                }
+            ]
+        }
+    ],
     "SmartDimModelV3": [
         {
             "platforms": [
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 2375a0b..9e20a56 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -602,6 +602,7 @@
     "//third_party/accessibility_test_framework:accessibility_test_framework_java",
   ]
   resource_overlay = true
+  enable_bytecode_checks = false
 }
 
 # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index 8be1857..4a6dc806 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -304,7 +304,7 @@
                 |  # Omit this file since we use our own copy, included above.
                 |  # We can remove this once we migrate to AndroidX master for all libraries.
                 |  jar_excluded_patterns = [
-                |    "androidx/fragment/app/DialogFragment.java",
+                |    "androidx/fragment/app/DialogFragment*",
                 |  ]
                 |
                 |  ignore_proguard_configs = true
@@ -438,6 +438,7 @@
                 |
                 |""".stripMargin())
                 break
+            case 'androidx_test_espresso_espresso_contrib':
             case 'androidx_test_espresso_espresso_web':
             case 'androidx_window_window':
                 sb.append('  enable_bytecode_checks = false\n')
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 7f541274..a8578c8 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -771,5 +771,8 @@
 const base::Feature kPreferCompositingToLCDText = {
     "PreferCompositingToLCDText", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kLogUnexpectedIPCPostedToBackForwardCachedDocuments{
+    "LogUnexpectedIPCPostedToBackForwardCachedDocuments",
+    base::FEATURE_ENABLED_BY_DEFAULT};
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/perf_tests/layout/culled-inline-hittest.html b/third_party/blink/perf_tests/layout/culled-inline-hittest.html
new file mode 100644
index 0000000..99fdefd
--- /dev/null
+++ b/third_party/blink/perf_tests/layout/culled-inline-hittest.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>Hit-testing culled inline</title>
+<script src="../resources/runner.js"></script>
+<body>
+<div id="container" style="width: 800px"></div>
+<script>
+let span_count = 0;
+
+function createTree(container, children_list) {
+  let children = children_list.shift();
+  for (let i = 0; i < children; ++i) {
+    const span = document.createElement('span');
+    span.appendChild(document.createTextNode('1234567'));
+    container.appendChild(span);
+    container.appendChild(document.createTextNode(' '));
+    ++span_count;
+  }
+
+  if (children_list.length) {
+    const span = document.createElement('span');
+    createTree(span, children_list);
+    container.appendChild(span);
+  }
+}
+
+function setup() {
+  container.textContent = '';
+  createTree(container, [1, 1, 1, 1, 3000]);
+}
+
+function test() {
+  let bounds = container.getBoundingClientRect();
+  for (let y = 0; y < 200; y += 10) {
+    for (let x = bounds.x + 5; x < bounds.x + 800; x += 100)
+      document.elementFromPoint(x, y);
+    document.elementFromPoint(bounds.right - 2, y);
+  }
+}
+
+function run() {
+  PerfTestRunner.measureTime({
+    description: `Measures performance of hit-testing ${span_count} culled inline.`,
+    run: test
+  });
+}
+
+setup();
+run();
+</script>
+</body>
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 494f737e..1462502 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -315,6 +315,8 @@
 
 BLINK_COMMON_EXPORT extern const base::Feature kPreferCompositingToLCDText;
 
+BLINK_COMMON_EXPORT extern const base::Feature
+    kLogUnexpectedIPCPostedToBackForwardCachedDocuments;
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index af4cd62..c151c428 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -77,6 +77,7 @@
     "insecure_input/insecure_input_service.mojom",
     "keyboard_lock/keyboard_lock.mojom",
     "leak_detector/leak_detector.mojom",
+    "link_to_text/link_to_text.mojom",
     "loader/code_cache.mojom",
     "loader/content_security_notifier.mojom",
     "loader/fetch_client_settings_object.mojom",
diff --git a/third_party/blink/public/mojom/link_to_text/OWNERS b/third_party/blink/public/mojom/link_to_text/OWNERS
new file mode 100644
index 0000000..bf01ee9
--- /dev/null
+++ b/third_party/blink/public/mojom/link_to_text/OWNERS
@@ -0,0 +1,4 @@
+# COMPONENT: UI>Browser>Sharing
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/third_party/blink/public/mojom/link_to_text/link_to_text.mojom b/third_party/blink/public/mojom/link_to_text/link_to_text.mojom
new file mode 100644
index 0000000..342f4a1
--- /dev/null
+++ b/third_party/blink/public/mojom/link_to_text/link_to_text.mojom
@@ -0,0 +1,14 @@
+// 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.
+
+[JavaPackage="org.chromium.blink.mojom"]
+module blink.mojom;
+
+// TextFragmentSelectorProducer is used for requesting renderer to generate
+// text fragment selector for the latest text selection. Implemented in renderer.
+interface TextFragmentSelectorProducer {
+  // Generates text fragment selector according to
+  // https://github.com/WICG/scroll-to-text-fragment#proposed-solution.
+  GenerateSelector() => (string selector);
+};
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 1a19af4..c1713343 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -77,6 +77,7 @@
 #include "third_party/blink/public/web/web_frame_owner_properties.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
 #include "third_party/blink/public/web/web_history_item.h"
+#include "third_party/blink/public/web/web_meaningful_layout.h"
 #include "third_party/blink/public/web/web_media_inspector.h"
 #include "third_party/blink/public/web/web_navigation_params.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
@@ -691,6 +692,11 @@
   virtual void AssociateInputAndOutputForAec(
       const base::UnguessableToken& input_stream_id,
       const std::string& output_device_id) {}
+
+  // Called immediately following the first compositor-driven (frame-generating)
+  // layout that happened after an interesting document lifecycle change (see
+  // WebMeaningfulLayout for details.)
+  virtual void DidMeaningfulLayout(WebMeaningfulLayout) {}
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/web/web_widget_client.h b/third_party/blink/public/web/web_widget_client.h
index 2b6352a..981323c 100644
--- a/third_party/blink/public/web/web_widget_client.h
+++ b/third_party/blink/public/web/web_widget_client.h
@@ -53,7 +53,6 @@
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_text_input_type.h"
 #include "third_party/blink/public/platform/web_touch_action.h"
-#include "third_party/blink/public/web/web_meaningful_layout.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
 
 class SkBitmap;
@@ -97,11 +96,6 @@
   // will unconditionally ensure that the compositor is actually run.
   virtual void ScheduleAnimationForWebTests() {}
 
-  // Called immediately following the first compositor-driven (frame-generating)
-  // layout that happened after an interesting document lifecyle change (see
-  // WebMeaningfulLayout for details.)
-  virtual void DidMeaningfulLayout(WebMeaningfulLayout) {}
-
   // Called when some JS code has instructed the window associated to the main
   // frame to close, which will result in a request to the browser to close the
   // RenderWidget associated to it
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index addb2d7..333ac6f3 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -277,6 +277,7 @@
 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h"
+#include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h"
 #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
 #include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
 #include "third_party/blink/renderer/core/page/validation_message_client.h"
@@ -3119,6 +3120,9 @@
 
   markers_->PrepareForDestruction();
 
+  if (GetFrame()->GetTextFragmentSelectorGenerator())
+    GetFrame()->GetTextFragmentSelectorGenerator()->ClearSelection();
+
   GetPage()->DocumentDetached(this);
 
   probe::DocumentDetached(this);
diff --git a/third_party/blink/renderer/core/exported/web_meaningful_layouts_test.cc b/third_party/blink/renderer/core/exported/web_meaningful_layouts_test.cc
index 1550e5cb..3afcb96 100644
--- a/third_party/blink/renderer/core/exported/web_meaningful_layouts_test.cc
+++ b/third_party/blink/renderer/core/exported/web_meaningful_layouts_test.cc
@@ -28,7 +28,7 @@
 
   Compositor().BeginFrame();
 
-  EXPECT_EQ(1, WebWidgetClient().VisuallyNonEmptyLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().VisuallyNonEmptyLayoutCount());
 }
 
 TEST_F(WebMeaningfulLayoutsTest, VisuallyNonEmptyTextCharactersEventually) {
@@ -44,7 +44,7 @@
   // Pump a frame mid-load.
   Compositor().BeginFrame();
 
-  EXPECT_EQ(0, WebWidgetClient().VisuallyNonEmptyLayoutCount());
+  EXPECT_EQ(0, WebFrameClient().VisuallyNonEmptyLayoutCount());
 
   // Write more than 200 characters.
   main_resource.Write("!");
@@ -55,7 +55,7 @@
   // not as the character count goes over 200.
   Compositor().BeginFrame();
 
-  EXPECT_EQ(1, WebWidgetClient().VisuallyNonEmptyLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().VisuallyNonEmptyLayoutCount());
 }
 
 // TODO(dglazkov): Write pixel-count and canvas-based VisuallyNonEmpty tests
@@ -83,7 +83,7 @@
   Compositor().BeginFrame();
 
   // ... which correctly signals the VisuallyNonEmpty.
-  EXPECT_EQ(1, WebWidgetClient().VisuallyNonEmptyLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().VisuallyNonEmptyLayoutCount());
 }
 
 TEST_F(WebMeaningfulLayoutsTest, FinishedParsing) {
@@ -95,7 +95,7 @@
 
   Compositor().BeginFrame();
 
-  EXPECT_EQ(1, WebWidgetClient().FinishedParsingLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().FinishedParsingLayoutCount());
 }
 
 TEST_F(WebMeaningfulLayoutsTest, FinishedLoading) {
@@ -107,7 +107,7 @@
 
   Compositor().BeginFrame();
 
-  EXPECT_EQ(1, WebWidgetClient().FinishedLoadingLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().FinishedLoadingLayoutCount());
 }
 
 TEST_F(WebMeaningfulLayoutsTest, FinishedParsingThenLoading) {
@@ -121,8 +121,8 @@
 
   Compositor().BeginFrame();
 
-  EXPECT_EQ(1, WebWidgetClient().FinishedParsingLayoutCount());
-  EXPECT_EQ(0, WebWidgetClient().FinishedLoadingLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().FinishedParsingLayoutCount());
+  EXPECT_EQ(0, WebFrameClient().FinishedLoadingLayoutCount());
 
   image_resource.Complete("image data");
 
@@ -131,8 +131,8 @@
 
   Compositor().BeginFrame();
 
-  EXPECT_EQ(1, WebWidgetClient().FinishedParsingLayoutCount());
-  EXPECT_EQ(1, WebWidgetClient().FinishedLoadingLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().FinishedParsingLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().FinishedLoadingLayoutCount());
 }
 
 TEST_F(WebMeaningfulLayoutsTest, WithIFrames) {
@@ -145,9 +145,9 @@
 
   Compositor().BeginFrame();
 
-  EXPECT_EQ(1, WebWidgetClient().VisuallyNonEmptyLayoutCount());
-  EXPECT_EQ(1, WebWidgetClient().FinishedParsingLayoutCount());
-  EXPECT_EQ(0, WebWidgetClient().FinishedLoadingLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().VisuallyNonEmptyLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().FinishedParsingLayoutCount());
+  EXPECT_EQ(0, WebFrameClient().FinishedLoadingLayoutCount());
 
   iframe_resource.Complete("iframe data");
 
@@ -156,9 +156,9 @@
 
   Compositor().BeginFrame();
 
-  EXPECT_EQ(1, WebWidgetClient().VisuallyNonEmptyLayoutCount());
-  EXPECT_EQ(1, WebWidgetClient().FinishedParsingLayoutCount());
-  EXPECT_EQ(1, WebWidgetClient().FinishedLoadingLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().VisuallyNonEmptyLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().FinishedParsingLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().FinishedLoadingLayoutCount());
 }
 
 // NoOverflowInIncrementVisuallyNonEmptyPixelCount tests fail if the number of
@@ -175,7 +175,7 @@
   main_resource.Write("<DOCTYPE html><body><img src=\"test.svg\">");
   // Run pending tasks to initiate the request to test.svg.
   test::RunPendingTasks();
-  EXPECT_EQ(0, WebWidgetClient().VisuallyNonEmptyLayoutCount());
+  EXPECT_EQ(0, WebFrameClient().VisuallyNonEmptyLayoutCount());
 
   // We serve the SVG file and check VisuallyNonEmptyLayoutCount() before
   // main_resource.Finish() because finishing the main resource causes
@@ -187,7 +187,7 @@
       "width=\"65536\"></svg>");
   svg_resource.Finish();
   Compositor().BeginFrame();
-  EXPECT_EQ(1, WebWidgetClient().VisuallyNonEmptyLayoutCount());
+  EXPECT_EQ(1, WebFrameClient().VisuallyNonEmptyLayoutCount());
 
   main_resource.Finish();
 }
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index c440d6f..0ba9a9e 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -714,6 +714,21 @@
   return associated_interface_provider_.get();
 }
 
+void TestWebFrameClient::DidMeaningfulLayout(
+    WebMeaningfulLayout meaningful_layout) {
+  switch (meaningful_layout) {
+    case WebMeaningfulLayout::kVisuallyNonEmpty:
+      visually_non_empty_layout_count_++;
+      break;
+    case WebMeaningfulLayout::kFinishedParsing:
+      finished_parsing_layout_count_++;
+      break;
+    case WebMeaningfulLayout::kFinishedLoading:
+      finished_loading_layout_count_++;
+      break;
+  }
+}
+
 TestWebRemoteFrameClient::TestWebRemoteFrameClient()
     : associated_interface_provider_(new AssociatedInterfaceProvider(nullptr)) {
 }
@@ -765,21 +780,6 @@
   return layer_tree_host()->have_scroll_event_handlers();
 }
 
-void TestWebWidgetClient::DidMeaningfulLayout(
-    WebMeaningfulLayout meaningful_layout) {
-  switch (meaningful_layout) {
-    case WebMeaningfulLayout::kVisuallyNonEmpty:
-      visually_non_empty_layout_count_++;
-      break;
-    case WebMeaningfulLayout::kFinishedParsing:
-      finished_parsing_layout_count_++;
-      break;
-    case WebMeaningfulLayout::kFinishedLoading:
-      finished_loading_layout_count_++;
-      break;
-  }
-}
-
 viz::FrameSinkId TestWebWidgetClient::GetFrameSinkId() {
   return viz::FrameSinkId();
 }
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.h b/third_party/blink/renderer/core/frame/frame_test_helpers.h
index 381e4faa..396e417 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.h
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.h
@@ -203,16 +203,6 @@
   void ClearAnimationScheduled() { animation_scheduled_ = false; }
 
   bool HaveScrollEventHandlers() const;
-
-  int VisuallyNonEmptyLayoutCount() const {
-    return visually_non_empty_layout_count_;
-  }
-  int FinishedParsingLayoutCount() const {
-    return finished_parsing_layout_count_;
-  }
-  int FinishedLoadingLayoutCount() const {
-    return finished_loading_layout_count_;
-  }
   const Vector<std::unique_ptr<blink::WebCoalescedInputEvent>>&
   GetInjectedScrollEvents() const {
     return injected_scroll_events_;
@@ -238,7 +228,6 @@
 
   // WebWidgetClient overrides;
   void ScheduleAnimation() override { animation_scheduled_ = true; }
-  void DidMeaningfulLayout(WebMeaningfulLayout) override;
   viz::FrameSinkId GetFrameSinkId() override;
   void RequestNewLayerTreeFrameSink(
       LayerTreeFrameSinkCallback callback) override;
@@ -268,9 +257,6 @@
       injected_scroll_events_;
   std::unique_ptr<TestWidgetInputHandlerHost> widget_input_handler_host_;
   bool animation_scheduled_ = false;
-  int visually_non_empty_layout_count_ = 0;
-  int finished_parsing_layout_count_ = 0;
-  int finished_loading_layout_count_ = 0;
   mojo::AssociatedReceiver<mojom::blink::WidgetHost> receiver_{this};
 };
 
@@ -448,6 +434,17 @@
   WebPlugin* CreatePlugin(const WebPluginParams& params) override;
   AssociatedInterfaceProvider* GetRemoteNavigationAssociatedInterfaces()
       override;
+  void DidMeaningfulLayout(WebMeaningfulLayout) override;
+
+  int VisuallyNonEmptyLayoutCount() const {
+    return visually_non_empty_layout_count_;
+  }
+  int FinishedParsingLayoutCount() const {
+    return finished_parsing_layout_count_;
+  }
+  int FinishedLoadingLayoutCount() const {
+    return finished_loading_layout_count_;
+  }
 
  private:
   void CommitNavigation(std::unique_ptr<WebNavigationInfo>);
@@ -467,6 +464,9 @@
   std::unique_ptr<WebWidgetClient> owned_widget_client_;
   WebEffectiveConnectionType effective_connection_type_;
   Vector<String> console_messages_;
+  int visually_non_empty_layout_count_ = 0;
+  int finished_parsing_layout_count_ = 0;
+  int finished_loading_layout_count_ = 0;
 
   base::WeakPtrFactory<TestWebFrameClient> weak_factory_{this};
 };
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 29079e5c..e2002abc 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -152,6 +152,7 @@
 #include "third_party/blink/renderer/core/page/plugin_script_forbidden_scope.h"
 #include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
+#include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h"
 #include "third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/object_painter.h"
@@ -357,6 +358,13 @@
       WTF::BindRepeating(&LocalFrame::BindToHighPriorityReceiver,
                          WrapWeakPersistent(this)),
       GetTaskRunner(blink::TaskType::kInternalHighPriorityLocalFrame));
+
+  if (IsMainFrame()) {
+    GetInterfaceRegistry()->AddInterface(
+        WTF::BindRepeating(&LocalFrame::BindTextFragmentSelectorProducer,
+                           WrapWeakPersistent(this)));
+  }
+
   SetOpenerDoNotNotify(opener);
   loader_.Init();
 }
@@ -455,6 +463,7 @@
   visitor->Trace(receiver_);
   visitor->Trace(main_frame_receiver_);
   visitor->Trace(high_priority_frame_receiver_);
+  visitor->Trace(text_fragment_selector_generator_);
   Frame::Trace(visitor);
   Supplementable<LocalFrame>::Trace(visitor);
 }
@@ -592,7 +601,8 @@
   // up calling back to LocalFrameClient via WindowProxy.
   GetScriptController().ClearForClose();
 
-  DCHECK(!view_->IsAttached());
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  CHECK(!view_->IsAttached());
   SetView(nullptr);
 
   GetEventHandlerRegistry().DidRemoveAllEventHandlers(*DomWindow());
@@ -1386,6 +1396,10 @@
   }
   DCHECK(ad_tracker_ ? RuntimeEnabledFeatures::AdTaggingEnabled()
                      : !RuntimeEnabledFeatures::AdTaggingEnabled());
+  if (IsMainFrame()) {
+    text_fragment_selector_generator_ =
+        MakeGarbageCollected<TextFragmentSelectorGenerator>();
+  }
 
   Initialize();
 
@@ -3166,6 +3180,16 @@
       GetTaskRunner(blink::TaskType::kInternalHighPriorityLocalFrame));
 }
 
+void LocalFrame::BindTextFragmentSelectorProducer(
+    mojo::PendingReceiver<mojom::blink::TextFragmentSelectorProducer>
+        receiver) {
+  if (IsDetached() || !text_fragment_selector_generator_)
+    return;
+
+  text_fragment_selector_generator_->BindTextFragmentSelectorProducer(
+      std::move(receiver));
+}
+
 SpellChecker& LocalFrame::GetSpellChecker() const {
   DCHECK(DomWindow());
   return DomWindow()->GetSpellChecker();
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 785b893..213f430 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -45,6 +45,7 @@
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/frame/reporting_observer.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/optimization_guide/optimization_guide.mojom-blink.h"
 #include "third_party/blink/public/mojom/reporting/reporting.mojom-blink.h"
@@ -130,6 +131,7 @@
 class ScriptController;
 class SmoothScrollSequencer;
 class SpellChecker;
+class TextFragmentSelectorGenerator;
 class TextSuggestionController;
 class VirtualKeyboardOverlayChangedObserver;
 class WebContentSettingsClient;
@@ -684,6 +686,10 @@
     return LocalFrameToken(GetFrameToken());
   }
 
+  TextFragmentSelectorGenerator* GetTextFragmentSelectorGenerator() const {
+    return text_fragment_selector_generator_;
+  }
+
  private:
   friend class FrameNavigationDisabler;
   FRIEND_TEST_ALL_PREFIXES(LocalFrameTest, CharacterIndexAtPointWithPinchZoom);
@@ -756,6 +762,10 @@
   void BindToHighPriorityReceiver(
       mojo::PendingReceiver<mojom::blink::HighPriorityLocalFrame> receiver);
 
+  void BindTextFragmentSelectorProducer(
+      mojo::PendingReceiver<mojom::blink::TextFragmentSelectorProducer>
+          receiver);
+
   std::unique_ptr<FrameScheduler> frame_scheduler_;
 
   // Holds all PauseSubresourceLoadingHandles allowing either |this| to delete
@@ -897,6 +907,8 @@
   Member<RawSystemClipboard> raw_system_clipboard_;
 
   mojom::blink::BlinkOptimizationGuideHintsPtr optimization_guide_hints_;
+
+  Member<TextFragmentSelectorGenerator> text_fragment_selector_generator_;
 };
 
 inline FrameLoader& LocalFrame::Loader() const {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index d7014ea..1aa3ea9 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -1176,8 +1176,13 @@
                   WrapPersistent(this)));
   }
 
-  if (client_)
-    client_->DidMeaningfulLayout(layout_type);
+  ForEachWebLocalFrameControlledByWidget(
+      local_root_,
+      WTF::BindRepeating(
+          [](WebMeaningfulLayout layout_type, WebLocalFrame* local_frame) {
+            local_frame->Client()->DidMeaningfulLayout(layout_type);
+          },
+          layout_type));
 }
 
 void WebFrameWidgetBase::PresentationCallbackForMeaningfulLayout(
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index eaf1f69..d0a3c31 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -19,6 +19,7 @@
 #include "third_party/blink/public/platform/web_battery_savings.h"
 #include "third_party/blink/public/platform/web_drag_data.h"
 #include "third_party/blink/public/web/web_frame_widget.h"
+#include "third_party/blink/public/web/web_meaningful_layout.h"
 #include "third_party/blink/renderer/core/clipboard/data_object.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/graphics/apply_viewport_changes.h"
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index f7a92bc..f804bd6 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -5856,6 +5856,8 @@
       resolved_logical_height = BlockSizeFromAspectRatio(
           border_padding, *StyleRef().LogicalAspectRatio(),
           StyleRef().BoxSizing(), LogicalWidth());
+      resolved_logical_height = std::max(
+          LayoutUnit(), resolved_logical_height - borders_plus_padding);
     } else {
       resolved_logical_height = AdjustContentBoxLogicalHeightForBoxSizing(
           ValueForLength(logical_height_length, container_logical_height));
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
index 909325f..46d8d3c 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
@@ -168,7 +168,10 @@
   // Don't report shift of anonymous objects. Will report the children because
   // we want report real DOM nodes.
   if (object.IsAnonymous())
-    return true;
+    return false;
+
+  if (object.StyleRef().Visibility() != EVisibility::kVisible)
+    return false;
 
   // Ignore layout objects that move (in the coordinate space of the paint
   // invalidation container) on scroll.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index de108c6..b3d6e10 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -269,6 +269,32 @@
   return !IsListMarker();
 }
 
+bool NGInlineCursorPosition::IsPartOfCulledInlineBox(
+    const LayoutInline& layout_inline) const {
+  DCHECK(!layout_inline.ShouldCreateBoxFragment());
+  DCHECK(*this);
+  const LayoutObject* const layout_object = GetLayoutObject();
+  // We use |IsInline()| to exclude floating and out-of-flow objects.
+  if (!layout_object || !layout_object->IsInline() ||
+      layout_object->IsAtomicInlineLevel())
+    return false;
+  DCHECK(!layout_object->IsFloatingOrOutOfFlowPositioned());
+  DCHECK(!BoxFragment() || !BoxFragment()->IsFormattingContextRoot());
+  for (const LayoutObject* parent = layout_object->Parent(); parent;
+       parent = parent->Parent()) {
+    // Children of culled inline should be included.
+    if (parent == &layout_inline)
+      return true;
+    // Grand children should be included only if children are also culled.
+    if (const auto* parent_layout_inline = ToLayoutInlineOrNull(parent)) {
+      if (!parent_layout_inline->ShouldCreateBoxFragment())
+        continue;
+    }
+    return false;
+  }
+  return false;
+}
+
 bool NGInlineCursor::IsLastLineInInlineBlock() const {
   DCHECK(Current().IsLineBox());
   if (!GetLayoutBlockFlow()->IsAtomicInlineLevel())
@@ -1665,14 +1691,23 @@
   return nullptr;
 }
 
+void NGInlineCursor::CulledInlineTraversal::SetUseFragmentTree(
+    const LayoutInline& layout_inline) {
+  layout_inline_ = &layout_inline;
+  use_fragment_tree_ = true;
+}
+
 const LayoutObject* NGInlineCursor::CulledInlineTraversal::MoveToFirstFor(
     const LayoutInline& layout_inline) {
   layout_inline_ = &layout_inline;
+  use_fragment_tree_ = false;
   current_object_ = Find(layout_inline.FirstChild());
   return current_object_;
 }
 
 const LayoutObject* NGInlineCursor::CulledInlineTraversal::MoveToNext() {
+  if (!current_object_)
+    return nullptr;
   current_object_ =
       Find(current_object_->NextInPreOrderAfterChildren(layout_inline_));
   return current_object_;
@@ -1680,6 +1715,19 @@
 
 void NGInlineCursor::MoveToFirstForCulledInline(
     const LayoutInline& layout_inline) {
+  // When |this| is a descendant cursor, |this| may be limited to a very small
+  // subset of the |LayoutObject| descendants, and that traversing
+  // |LayoutObject| descendants is much more expensive. Prefer checking every
+  // fragment in that case.
+  if (IsDescendantsCursor()) {
+    culled_inline_.SetUseFragmentTree(layout_inline);
+    DCHECK(!CanMoveAcrossFragmentainer());
+    MoveToFirst();
+    while (Current() && !Current().IsPartOfCulledInlineBox(layout_inline))
+      MoveToNext();
+    return;
+  }
+
   if (const LayoutObject* layout_object =
           culled_inline_.MoveToFirstFor(layout_inline)) {
     MoveTo(*layout_object);
@@ -1691,6 +1739,16 @@
 
 void NGInlineCursor::MoveToNextForCulledInline() {
   DCHECK(culled_inline_);
+  if (culled_inline_.UseFragmentTree()) {
+    const LayoutInline* layout_inline = culled_inline_.GetLayoutInline();
+    DCHECK(layout_inline);
+    DCHECK(!CanMoveAcrossFragmentainer());
+    do {
+      MoveToNext();
+    } while (Current() && !Current().IsPartOfCulledInlineBox(*layout_inline));
+    return;
+  }
+
   MoveToNextForSameLayoutObjectExceptCulledInline();
   // If we're at the end of fragments for the current |LayoutObject| that
   // contributes to the current culled inline, find the next |LayoutObject|.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
index e7c3bd2..625461eb 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
@@ -195,6 +195,9 @@
     item_ = nullptr;
   }
 
+  // True if current position is part of culled inline box |layout_inline|.
+  bool IsPartOfCulledInlineBox(const LayoutInline& layout_inline) const;
+
   const NGPaintFragment* paint_fragment_ = nullptr;
   const NGFragmentItem* item_ = nullptr;
   ItemsSpan::iterator item_iter_;
@@ -527,8 +530,13 @@
    public:
     CulledInlineTraversal() = default;
 
-    explicit operator bool() const { return current_object_; }
-    void Reset() { current_object_ = nullptr; }
+    const LayoutInline* GetLayoutInline() const { return layout_inline_; }
+
+    explicit operator bool() const { return layout_inline_; }
+    void Reset() { layout_inline_ = nullptr; }
+
+    bool UseFragmentTree() const { return use_fragment_tree_; }
+    void SetUseFragmentTree(const LayoutInline& layout_inline);
 
     // Returns first/next |LayoutObject| that contribute to |layout_inline|.
     const LayoutObject* MoveToFirstFor(const LayoutInline& layout_inline);
@@ -539,6 +547,7 @@
 
     const LayoutObject* current_object_ = nullptr;
     const LayoutInline* layout_inline_ = nullptr;
+    bool use_fragment_tree_ = false;
   };
 
   void MoveToFirstForCulledInline(const LayoutInline& layout_inline);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
index 9ab17dd2..a1a7872 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
@@ -53,9 +53,6 @@
   // The range of text content for this item.
   NGTextOffset text_offset;
 
-  // Indicates the limits of the trailing space run.
-  base::Optional<unsigned> non_hangable_run_end;
-
   // Inline size of this item.
   LayoutUnit inline_size;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 3c71854..568bec8 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -198,7 +198,7 @@
       handled_leading_floats_index_(handled_leading_floats_index),
       base_direction_(node_.BaseDirection()) {
   available_width_ = ComputeAvailableWidth();
-  break_iterator_.SetBreakSpace(BreakSpaceType::kAfterSpaceRun);
+  break_iterator_.SetBreakSpace(BreakSpaceType::kBeforeSpaceRun);
 
   if (!break_token)
     return;
@@ -622,17 +622,6 @@
       return;
     }
 
-    // Hanging trailing spaces may resolve the overflow.
-    if (item_result->has_only_trailing_spaces) {
-      if (item_result->item->Style()->WhiteSpace() == EWhiteSpace::kPreWrap &&
-          IsBreakableSpace(Text()[item_result->EndOffset() - 1])) {
-        unsigned end_index = item_result - line_info->Results().begin();
-        Rewind(end_index, line_info);
-      }
-      state_ = LineBreakState::kTrailing;
-      return;
-    }
-
     // If this is all trailable spaces, this item is trailable, and next item
     // maybe too. Don't go to |HandleOverflow()| yet.
     if (IsAllBreakableSpaces(Text(), item_result->StartOffset(),
@@ -771,8 +760,6 @@
     item_result->inline_size = inline_size;
     item_result->text_offset.end = result.break_offset;
     item_result->text_offset.AssertNotEmpty();
-    item_result->non_hangable_run_end = result.non_hangable_run_end;
-    item_result->has_only_trailing_spaces = result.has_trailing_spaces;
     item_result->shape_result = std::move(shape_result);
     break;
   }
@@ -897,31 +884,20 @@
     if (end_offset >= item.EndOffset())
       break;
 
-    unsigned non_hangable_run_end = end_offset;
-    if (item.Style()->WhiteSpace() != EWhiteSpace::kBreakSpaces) {
-      while (non_hangable_run_end > item.StartOffset() &&
-             IsBreakableSpace(text[non_hangable_run_end - 1])) {
-        --non_hangable_run_end;
-      }
-    }
-
     // Inserting a hyphenation character is not supported yet.
-    // TODO (jfernandez): Maybe we need to use 'end_offset' here ?
-    if (text[non_hangable_run_end - 1] == kSoftHyphenCharacter)
+    if (text[end_offset - 1] == kSoftHyphenCharacter)
       return false;
 
     float start_position = shape_result.CachedPositionForOffset(
         start_offset - shape_result.StartIndex());
     float end_position = shape_result.CachedPositionForOffset(
-        non_hangable_run_end - shape_result.StartIndex());
+        end_offset - shape_result.StartIndex());
     float word_width = IsLtr(shape_result.Direction())
                            ? end_position - start_position
                            : start_position - end_position;
     min_width = std::max(word_width, min_width);
 
-    last_end_offset = non_hangable_run_end;
-    // TODO (jfernandez): I think that having the non_hangable_run_end
-    // would make this loop unnecessary/redudant.
+    last_end_offset = end_offset;
     start_offset = end_offset;
     while (start_offset < item.EndOffset() &&
            IsBreakableSpace(text[start_offset])) {
@@ -938,8 +914,7 @@
     return false;
 
   // Create an NGInlineItemResult that has the max of widths of all words.
-  item_result->text_offset.end =
-      std::max(last_end_offset, item_result->text_offset.start + 1);
+  item_result->text_offset.end = last_end_offset;
   item_result->text_offset.AssertNotEmpty();
   item_result->inline_size = LayoutUnit::FromFloatCeil(min_width);
   item_result->can_break_after = true;
@@ -1051,9 +1026,6 @@
          (item.Type() == NGInlineItem::kControl &&
           Text()[item.StartOffset()] == kTabulationCharacter));
   DCHECK(&shape_result);
-  bool is_control_tab = item.Type() == NGInlineItem::kControl &&
-                        Text()[item.StartOffset()] == kTabulationCharacter;
-  DCHECK(item.Type() == NGInlineItem::kText || is_control_tab);
   DCHECK_GE(offset_, item.StartOffset());
   DCHECK_LT(offset_, item.EndOffset());
   const String& text = Text();
@@ -1067,8 +1039,6 @@
 
   if (style.CollapseWhiteSpace()) {
     if (text[offset_] != kSpaceCharacter) {
-      if (offset_ > 0 && IsBreakableSpace(text[offset_ - 1]))
-        trailing_whitespace_ = WhitespaceState::kCollapsible;
       state_ = LineBreakState::kDone;
       return;
     }
@@ -1090,29 +1060,18 @@
     while (end < item.EndOffset() && IsBreakableSpace(text[end]))
       end++;
     if (end == offset_) {
-      if (IsBreakableSpace(text[end - 1]))
-        trailing_whitespace_ = WhitespaceState::kPreserved;
       state_ = LineBreakState::kDone;
       return;
     }
 
-    // TODO (jfernandez): Could we just modify the last ItemResult
-    // instead of creating a new one ?
-    // Probably we can (koji). We would need to review usage of these
-    // item results, and change them to use "non_hangable_run_end"
-    // instead.
     NGInlineItemResult* item_result = AddItem(item, end, line_info);
-    item_result->non_hangable_run_end = offset_;
     item_result->has_only_trailing_spaces = true;
     item_result->shape_result = ShapeResultView::Create(&shape_result);
     if (item_result->StartOffset() == item.StartOffset() &&
-        item_result->EndOffset() == item.EndOffset()) {
-      item_result->inline_size = item_result->shape_result
-                                     ? item_result->shape_result->SnappedWidth()
-                                     : LayoutUnit();
-    } else {
+        item_result->EndOffset() == item.EndOffset())
+      item_result->inline_size = item_result->shape_result->SnappedWidth();
+    else
       UpdateShapeResult(*line_info, item_result);
-    }
     position_ += item_result->inline_size;
     item_result->can_break_after =
         end < text.length() && !IsBreakableSpace(text[end]);
@@ -1703,8 +1662,7 @@
   DCHECK(!item_result->can_break_after);
   const NGInlineItemResults& item_results = line_info->Results();
   if (UNLIKELY(!was_auto_wrap && auto_wrap_ && item_results.size() >= 2)) {
-    if (IsPreviousItemOfType(NGInlineItem::kText))
-      ComputeCanBreakAfter(std::prev(item_result), auto_wrap_, break_iterator_);
+    ComputeCanBreakAfter(std::prev(item_result), auto_wrap_, break_iterator_);
   }
 }
 
@@ -1733,15 +1691,29 @@
   if (item_results.size() >= 2) {
     NGInlineItemResult* last = std::prev(item_result);
     if (was_auto_wrap || last->can_break_after) {
-      item_result->can_break_after =
-          last->can_break_after ||
-          IsBreakableSpace(Text()[item_result->EndOffset()]);
+      item_result->can_break_after = last->can_break_after;
       last->can_break_after = false;
       return;
     }
-    if (auto_wrap_ && !IsBreakableSpace(Text()[item_result->EndOffset() - 1]))
-      ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_);
   }
+  if (was_auto_wrap)
+    return;
+
+  DCHECK(!item_result->can_break_after);
+  if (!auto_wrap_)
+    return;
+
+  // When auto-wrap follows no-wrap, the boundary is determined by the break
+  // iterator. However, when space characters follow the boundary, the break
+  // iterator cannot compute this because it considers break opportunities are
+  // before a run of spaces.
+  const String& text = Text();
+  if (item_result->EndOffset() < text.length() &&
+      IsBreakableSpace(text[item_result->EndOffset()])) {
+    item_result->can_break_after = true;
+    return;
+  }
+  ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_);
 }
 
 // Handles when the last item overflows.
@@ -2177,10 +2149,6 @@
   spacing_.SetSpacing(style.GetFont());
 }
 
-bool NGLineBreaker::IsPreviousItemOfType(NGInlineItem::NGInlineItemType type) {
-  return item_index_ > 0 ? Items().at(item_index_ - 1).Type() == type : false;
-}
-
 void NGLineBreaker::MoveToNextOf(const NGInlineItem& item) {
   offset_ = item.EndOffset();
   item_index_++;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
index 47dc71d3..a9e40ebc 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
@@ -194,7 +194,6 @@
                                            NGLineInfo*) const;
   void SetCurrentStyle(const ComputedStyle&);
 
-  bool IsPreviousItemOfType(NGInlineItem::NGInlineItemType);
   void MoveToNextOf(const NGInlineItem&);
   void MoveToNextOf(const NGInlineItemResult&);
 
diff --git a/third_party/blink/renderer/core/page/DEPS b/third_party/blink/renderer/core/page/DEPS
index 3799876..1ddb8e26 100644
--- a/third_party/blink/renderer/core/page/DEPS
+++ b/third_party/blink/renderer/core/page/DEPS
@@ -7,4 +7,7 @@
   "chrome_client_impl_test\.cc": [
     "+base/run_loop.h",
   ],
+  "text_fragment_selector_generator_test\.cc": [
+    "+base/run_loop.h",
+  ],
 }
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 34bae4f..dc69a0c 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -524,11 +524,14 @@
 
 void ContextMenuController::UpdateTextFragmentSelectorGenerator(
     LocalFrame* selected_frame) {
+  if (!selected_frame->GetTextFragmentSelectorGenerator())
+    return;
+
   VisibleSelectionInFlatTree selection =
       selected_frame->Selection().ComputeVisibleSelectionInFlatTree();
   EphemeralRangeInFlatTree selection_range(selection.Start(), selection.End());
-  page_->GetTextFragmentSelectorGenerator().UpdateSelection(selected_frame,
-                                                            selection_range);
+  selected_frame->GetTextFragmentSelectorGenerator()->UpdateSelection(
+      selected_frame, selection_range);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index 4acfdcfa..97cb430 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -75,7 +75,6 @@
 #include "third_party/blink/renderer/core/page/scoped_page_pauser.h"
 #include "third_party/blink/renderer/core/page/scrolling/overscroll_controller.h"
 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
-#include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h"
 #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
 #include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
 #include "third_party/blink/renderer/core/page/validation_message_client_impl.h"
@@ -226,9 +225,7 @@
       next_related_page_(this),
       prev_related_page_(this),
       autoplay_flags_(0),
-      web_text_autosizer_page_info_({0, 0, 1.f}),
-      text_fragment_selector_generator_(
-          MakeGarbageCollected<TextFragmentSelectorGenerator>()) {
+      web_text_autosizer_page_info_({0, 0, 1.f}) {
   DCHECK(!AllPages().Contains(this));
   AllPages().insert(this);
 
@@ -343,7 +340,6 @@
 
 void Page::DocumentDetached(Document* document) {
   pointer_lock_controller_->DocumentDetached(document);
-  text_fragment_selector_generator_->DocumentDetached(document);
   context_menu_controller_->DocumentDetached(document);
   if (validation_message_client_)
     validation_message_client_->DocumentDetached(*document);
@@ -921,7 +917,6 @@
   visitor->Trace(plugins_changed_observers_);
   visitor->Trace(next_related_page_);
   visitor->Trace(prev_related_page_);
-  visitor->Trace(text_fragment_selector_generator_);
   Supplementable<Page>::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index b6a1caa..4e4e496 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -76,7 +76,6 @@
 class PluginData;
 class PluginsChangedObserver;
 class PointerLockController;
-class TextFragmentSelectorGenerator;
 class ScopedPagePauser;
 class ScrollingCoordinator;
 class ScrollbarTheme;
@@ -377,10 +376,6 @@
 
   static void PrepareForLeakDetection();
 
-  TextFragmentSelectorGenerator& GetTextFragmentSelectorGenerator() const {
-    return *text_fragment_selector_generator_;
-  }
-
  private:
   friend class ScopedPagePauser;
 
@@ -502,8 +497,6 @@
 
   WebScopedVirtualTimePauser history_navigation_virtual_time_pauser_;
 
-  const Member<TextFragmentSelectorGenerator> text_fragment_selector_generator_;
-
   DISALLOW_COPY_AND_ASSIGN(Page);
 };
 
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc
index 42c93d9..787fb1b 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
 
@@ -81,4 +82,28 @@
 
 TextFragmentSelector::TextFragmentSelector(SelectorType type) : type_(type) {}
 
+String TextFragmentSelector::ToString() const {
+  StringBuilder selector;
+  if (!prefix_.IsEmpty()) {
+    selector.Append(EncodeWithURLEscapeSequences(prefix_));
+    selector.Append("-,");
+  }
+
+  if (!start_.IsEmpty()) {
+    selector.Append(EncodeWithURLEscapeSequences(start_));
+  }
+
+  if (!end_.IsEmpty()) {
+    selector.Append(",");
+    selector.Append(EncodeWithURLEscapeSequences(end_));
+  }
+
+  if (!suffix_.IsEmpty()) {
+    selector.Append(",-");
+    selector.Append(EncodeWithURLEscapeSequences(suffix_));
+  }
+
+  return selector.ToString();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h
index 06013500..3161748 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h
@@ -34,10 +34,11 @@
   ~TextFragmentSelector() = default;
 
   SelectorType Type() const { return type_; }
-  String Start() const { return start_; }
-  String End() const { return end_; }
-  String Prefix() const { return prefix_; }
-  String Suffix() const { return suffix_; }
+  const String& Start() const { return start_; }
+  const String& End() const { return end_; }
+  const String& Prefix() const { return prefix_; }
+  const String& Suffix() const { return suffix_; }
+  String ToString() const;
 
  private:
   const SelectorType type_;
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
index 5164e55..bcecdb3 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h"
 
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/platform/interface_registry.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/finder/find_buffer.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
@@ -18,6 +20,11 @@
 void TextFragmentSelectorGenerator::UpdateSelection(
     LocalFrame* selection_frame,
     const EphemeralRangeInFlatTree& selection_range) {
+  DCHECK(selection_frame);
+
+  // Scroll-to-text doesn't support iframes.
+  DCHECK(selection_frame->IsMainFrame());
+
   selection_frame_ = selection_frame;
   selection_range_ = MakeGarbageCollected<Range>(
       selection_range.GetDocument(),
@@ -25,7 +32,21 @@
       ToPositionInDOMTree(selection_range.EndPosition()));
 }
 
-void TextFragmentSelectorGenerator::GenerateSelector() {
+void TextFragmentSelectorGenerator::BindTextFragmentSelectorProducer(
+    mojo::PendingReceiver<mojom::blink::TextFragmentSelectorProducer>
+        producer) {
+  selector_producer_.reset();
+  selector_producer_.Bind(
+      std::move(producer),
+      selection_frame_->GetTaskRunner(blink::TaskType::kInternalDefault));
+}
+
+void TextFragmentSelectorGenerator::GenerateSelector(
+    GenerateSelectorCallback callback) {
+  DCHECK(selection_range_);
+  DCHECK(callback);
+
+  pending_generate_selector_callback_ = std::move(callback);
   EphemeralRangeInFlatTree ephemeral_range(selection_range_);
 
   const TextFragmentSelector kInvalidSelector(
@@ -38,8 +59,10 @@
       FindBuffer::GetFirstBlockLevelAncestorInclusive(
           *ephemeral_range.EndPosition().AnchorNode());
 
-  if (!start_first_block_ancestor.isSameNode(&end_first_block_ancestor))
+  if (!start_first_block_ancestor.isSameNode(&end_first_block_ancestor)) {
     NotifySelectorReady(kInvalidSelector);
+    return;
+  }
 
   // TODO(gayane): If same node, need to check if start and end are interrupted
   // by a block. Example: <div>start of the selection <div> sub block </div>end
@@ -50,8 +73,10 @@
   String selected_text = PlainText(ephemeral_range);
 
   if (selected_text.length() < kNoContextMinChars ||
-      selected_text.length() > kExactTextMaxChars)
+      selected_text.length() > kExactTextMaxChars) {
     NotifySelectorReady(kInvalidSelector);
+    return;
+  }
 
   selector_ = std::make_unique<TextFragmentSelector>(
       TextFragmentSelector::SelectorType::kExact, selected_text, "", "", "");
@@ -72,19 +97,14 @@
   }
 }
 
-void TextFragmentSelectorGenerator::SetCallbackForTesting(
-    base::OnceCallback<void(const TextFragmentSelector&)> callback) {
-  callback_for_tests_ = std::move(callback);
-}
-
 void TextFragmentSelectorGenerator::NotifySelectorReady(
     const TextFragmentSelector& selector) {
-  if (!callback_for_tests_.is_null())
-    std::move(callback_for_tests_).Run(selector);
+  DCHECK(pending_generate_selector_callback_);
+  std::move(pending_generate_selector_callback_).Run(selector.ToString());
 }
 
-void TextFragmentSelectorGenerator::DocumentDetached(Document* document) {
-  if (selection_range_ && selection_range_->OwnerDocument() == *document) {
+void TextFragmentSelectorGenerator::ClearSelection() {
+  if (selection_range_) {
     selection_range_->Dispose();
     selection_range_ = nullptr;
     selection_frame_ = nullptr;
@@ -94,6 +114,7 @@
 void TextFragmentSelectorGenerator::Trace(Visitor* visitor) const {
   visitor->Trace(selection_frame_);
   visitor->Trace(selection_range_);
+  visitor->Trace(selector_producer_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
index 18220769..a0de513 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
@@ -5,9 +5,11 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_TEXT_FRAGMENT_SELECTOR_GENERATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_TEXT_FRAGMENT_SELECTOR_GENERATOR_H_
 
+#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-blink.h"
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h"
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
 
 namespace blink {
 
@@ -21,30 +23,33 @@
 // triggered when users request "link to text" for the selected text.
 class CORE_EXPORT TextFragmentSelectorGenerator final
     : public GarbageCollected<TextFragmentSelectorGenerator>,
-      public TextFragmentFinder::Client {
+      public TextFragmentFinder::Client,
+      public blink::mojom::blink::TextFragmentSelectorProducer {
  public:
   explicit TextFragmentSelectorGenerator() = default;
 
+  void BindTextFragmentSelectorProducer(
+      mojo::PendingReceiver<mojom::blink::TextFragmentSelectorProducer>
+          producer);
+
   // Sets the frame and range of the current selection.
   void UpdateSelection(LocalFrame* selection_frame,
                        const EphemeralRangeInFlatTree& selection_range);
 
+  // blink::mojom::blink::TextFragmentSelectorProducer interface
   // Generates selector for current selection.
-  void GenerateSelector();
+  void GenerateSelector(GenerateSelectorCallback callback) override;
 
   // TextFragmentFinder::Client interface
   void DidFindMatch(const EphemeralRangeInFlatTree& match,
                     const TextFragmentAnchorMetrics::Match match_metrics,
                     bool is_unique) override;
 
-  // Sets the callback used for notifying test results of |GenerateSelector|.
-  void SetCallbackForTesting(
-      base::OnceCallback<void(const TextFragmentSelector&)> callback);
-
   // Notifies the results of |GenerateSelector|.
   void NotifySelectorReady(const TextFragmentSelector& selector);
 
-  void DocumentDetached(Document* document);
+  // Releases members if necessary.
+  void ClearSelection();
 
   void Trace(Visitor*) const;
 
@@ -53,7 +58,12 @@
   Member<Range> selection_range_;
   std::unique_ptr<TextFragmentSelector> selector_;
 
-  base::OnceCallback<void(const TextFragmentSelector&)> callback_for_tests_;
+  // Used for communication between |TextFragmentSelectorGenerator| in renderer
+  // and |TextFragmentSelectorClientImpl| in browser.
+  HeapMojoReceiver<blink::mojom::blink::TextFragmentSelectorProducer,
+                   TextFragmentSelectorGenerator>
+      selector_producer_{this, nullptr};
+  GenerateSelectorCallback pending_generate_selector_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(TextFragmentSelectorGenerator);
 };
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator_test.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator_test.cc
index ddb466f7..4a3e33c 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator_test.cc
@@ -6,8 +6,14 @@
 
 #include <gtest/gtest.h>
 
+#include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-blink.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 
@@ -19,6 +25,33 @@
     SimTest::SetUp();
     WebView().MainFrameWidget()->Resize(WebSize(800, 600));
   }
+
+  void GenerateAndVerifySelector(Position selected_start,
+                                 Position selected_end,
+                                 String expected_selector) {
+    GetDocument()
+        .GetFrame()
+        ->GetTextFragmentSelectorGenerator()
+        ->UpdateSelection(GetDocument().GetFrame(),
+                          ToEphemeralRangeInFlatTree(
+                              EphemeralRange(selected_start, selected_end)));
+
+    bool callback_called = false;
+    auto lambda = [](bool& callback_called, const String& expected_selector,
+                     const String& selector) {
+      EXPECT_EQ(selector, expected_selector);
+      callback_called = true;
+    };
+    auto callback =
+        WTF::Bind(lambda, std::ref(callback_called), expected_selector);
+    GetDocument()
+        .GetFrame()
+        ->GetTextFragmentSelectorGenerator()
+        ->GenerateSelector(std::move(callback));
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_TRUE(callback_called);
+  }
 };
 
 TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector) {
@@ -33,22 +66,9 @@
   Node* first_paragraph = GetDocument().getElementById("first")->firstChild();
   const auto& selected_start = Position(first_paragraph, 0);
   const auto& selected_end = Position(first_paragraph, 28);
-  bool callback_called = false;
-  base::OnceCallback<void(const TextFragmentSelector&)> callback =
-      base::BindLambdaForTesting([&](const TextFragmentSelector& selector) {
-        EXPECT_EQ(selector.Type(), TextFragmentSelector::SelectorType::kExact);
-        EXPECT_EQ(selector.Start(), "First paragraph text that is");
-        callback_called = true;
-      });
 
-  TextFragmentSelectorGenerator generator;
-  generator.UpdateSelection(
-      GetDocument().GetFrame(),
-      ToEphemeralRangeInFlatTree(EphemeralRange(selected_start, selected_end)));
-  generator.SetCallbackForTesting(std::move(callback));
-  generator.GenerateSelector();
-
-  EXPECT_TRUE(callback_called);
+  GenerateAndVerifySelector(selected_start, selected_end,
+                            "First%20paragraph%20text%20that%20is");
 }
 
 TEST_F(TextFragmentSelectorGeneratorTest, ExactTextWithNestedTextNodes) {
@@ -64,22 +84,9 @@
   const auto& selected_start = Position(first_paragraph->firstChild(), 0);
   const auto& selected_end =
       Position(first_paragraph->firstChild()->nextSibling()->firstChild(), 6);
-  bool callback_called = false;
-  base::OnceCallback<void(const TextFragmentSelector&)> callback =
-      base::BindLambdaForTesting([&](const TextFragmentSelector& selector) {
-        EXPECT_EQ(selector.Type(), TextFragmentSelector::SelectorType::kExact);
-        EXPECT_EQ(selector.Start(), "First paragraph text that is longer");
-        callback_called = true;
-      });
 
-  TextFragmentSelectorGenerator generator;
-  generator.UpdateSelection(
-      GetDocument().GetFrame(),
-      ToEphemeralRangeInFlatTree(EphemeralRange(selected_start, selected_end)));
-  generator.SetCallbackForTesting(std::move(callback));
-  generator.GenerateSelector();
-
-  EXPECT_TRUE(callback_called);
+  GenerateAndVerifySelector(selected_start, selected_end,
+                            "First%20paragraph%20text%20that%20is%20longer");
 }
 
 TEST_F(TextFragmentSelectorGeneratorTest, ExactTextWithExtraSpace) {
@@ -94,22 +101,9 @@
   Node* second_paragraph = GetDocument().getElementById("second")->firstChild();
   const auto& selected_start = Position(second_paragraph, 0);
   const auto& selected_end = Position(second_paragraph, 23);
-  bool callback_called = false;
-  base::OnceCallback<void(const TextFragmentSelector&)> callback =
-      base::BindLambdaForTesting([&](const TextFragmentSelector& selector) {
-        EXPECT_EQ(selector.Type(), TextFragmentSelector::SelectorType::kExact);
-        EXPECT_EQ(selector.Start(), "Second paragraph text");
-        callback_called = true;
-      });
 
-  TextFragmentSelectorGenerator generator;
-  generator.UpdateSelection(
-      GetDocument().GetFrame(),
-      ToEphemeralRangeInFlatTree(EphemeralRange(selected_start, selected_end)));
-  generator.SetCallbackForTesting(std::move(callback));
-  generator.GenerateSelector();
-
-  EXPECT_TRUE(callback_called);
+  GenerateAndVerifySelector(selected_start, selected_end,
+                            "Second%20paragraph%20text");
 }
 
 // Multi-block selection is currently not implemented.
@@ -126,22 +120,8 @@
   Node* second_paragraph = GetDocument().getElementById("second")->firstChild();
   const auto& selected_start = Position(first_paragraph, 0);
   const auto& selected_end = Position(second_paragraph, 6);
-  bool callback_called = false;
-  base::OnceCallback<void(const TextFragmentSelector&)> callback =
-      base::BindLambdaForTesting([&](const TextFragmentSelector& selector) {
-        EXPECT_EQ(selector.Type(),
-                  TextFragmentSelector::SelectorType::kInvalid);
-        callback_called = true;
-      });
 
-  TextFragmentSelectorGenerator generator;
-  generator.UpdateSelection(
-      GetDocument().GetFrame(),
-      ToEphemeralRangeInFlatTree(EphemeralRange(selected_start, selected_end)));
-  generator.SetCallbackForTesting(std::move(callback));
-  generator.GenerateSelector();
-
-  EXPECT_TRUE(callback_called);
+  GenerateAndVerifySelector(selected_start, selected_end, "");
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/svg/graphics/filters/svg_fe_image.cc b/third_party/blink/renderer/core/svg/graphics/filters/svg_fe_image.cc
index 7e3b5a9..788fb3b 100644
--- a/third_party/blink/renderer/core/svg/graphics/filters/svg_fe_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/filters/svg_fe_image.cc
@@ -62,9 +62,9 @@
   FilterEffect::Trace(visitor);
 }
 
-static FloatRect GetLayoutObjectRepaintRect(const LayoutObject* layout_object) {
-  return layout_object->LocalToSVGParentTransform().MapRect(
-      layout_object->VisualRectInLocalSVGCoordinates());
+static FloatRect GetLayoutObjectRepaintRect(const LayoutObject& layout_object) {
+  return layout_object.LocalToSVGParentTransform().MapRect(
+      layout_object.VisualRectInLocalSVGCoordinates());
 }
 
 static AffineTransform MakeMapBetweenRects(const FloatRect& source,
@@ -97,7 +97,7 @@
   FloatRect dest_rect =
       GetFilter()->MapLocalRectToAbsoluteRect(FilterPrimitiveSubregion());
   if (const LayoutObject* layout_object = ReferencedLayoutObject()) {
-    FloatRect src_rect = GetLayoutObjectRepaintRect(layout_object);
+    FloatRect src_rect = GetLayoutObjectRepaintRect(*layout_object);
     if (element_->HasRelativeLengths()) {
       auto viewport_transform =
           ComputeViewportAdjustmentTransform(element_, dest_rect);
@@ -131,7 +131,7 @@
     image_size = image_->Size();
   } else if (const LayoutObject* layout_object = ReferencedLayoutObject()) {
     image_size =
-        EnclosingIntRect(GetLayoutObjectRepaintRect(layout_object)).Size();
+        EnclosingIntRect(GetLayoutObjectRepaintRect(*layout_object)).Size();
   }
   WriteIndent(ts, indent);
   ts << "[feImage";
@@ -155,16 +155,24 @@
     const LayoutObject& layout_object) {
   FloatRect dst_rect =
       GetFilter()->MapLocalRectToAbsoluteRect(FilterPrimitiveSubregion());
+  FloatRect src_rect = GetLayoutObjectRepaintRect(layout_object);
 
   AffineTransform transform;
   if (element_->HasRelativeLengths()) {
     auto viewport_transform =
         ComputeViewportAdjustmentTransform(element_, dst_rect);
-    if (viewport_transform)
+    if (viewport_transform) {
+      src_rect = viewport_transform->MapRect(src_rect);
       transform = *viewport_transform;
+    }
   } else {
+    src_rect = GetFilter()->MapLocalRectToAbsoluteRect(src_rect);
+    src_rect.Move(dst_rect.X(), dst_rect.Y());
     transform.Translate(dst_rect.X(), dst_rect.Y());
   }
+  // Intersect with the (transformed) source rect to remove "empty" bits of the
+  // image.
+  dst_rect.Intersect(src_rect);
 
   // Clip the filter primitive rect by the filter region and use that as the
   // cull rect for the paint record.
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
index c02a28f8..859395b 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
@@ -66,19 +66,6 @@
   CHECK_LE(offset, end);
 }
 
-unsigned FindNonHangableEnd(const String& text, unsigned candidate) {
-  DCHECK_LT(candidate, text.length());
-  DCHECK(IsBreakableSpace(text[candidate]));
-
-  // Looking for the non-hangable run end
-  unsigned non_hangable_end = candidate;
-  while (non_hangable_end > 0) {
-    if (!IsBreakableSpace(text[--non_hangable_end]))
-      return non_hangable_end + 1;
-  }
-  return non_hangable_end;
-}
-
 }  // namespace
 
 inline const String& ShapingLineBreaker::GetText() const {
@@ -123,11 +110,8 @@
     bool backwards) const {
   const String& text = GetText();
   unsigned word_end = break_iterator_->NextBreakOpportunity(offset);
-  if (word_end != offset && IsBreakableSpace(text[word_end - 1]))
-    word_end = std::max(start, FindNonHangableEnd(text, word_end - 1));
   if (word_end == offset) {
-    DCHECK(IsBreakableSpace(text[offset]) ||
-           offset == break_iterator_->PreviousBreakOpportunity(offset, start));
+    DCHECK_EQ(offset, break_iterator_->PreviousBreakOpportunity(offset, start));
     return {word_end, false};
   }
   unsigned previous_break_opportunity =
@@ -135,7 +119,6 @@
   unsigned word_start = previous_break_opportunity;
   // Skip the leading spaces of this word because the break iterator breaks
   // before spaces.
-  // TODO (jfernandez): This is no longer true, so we should remove this code.
   while (word_start < text.length() &&
          LazyLineBreakIterator::IsBreakableSpace(text[word_start]))
     word_start++;
@@ -151,8 +134,8 @@
 ShapingLineBreaker::BreakOpportunity
 ShapingLineBreaker::PreviousBreakOpportunity(unsigned offset,
                                              unsigned start) const {
-  const String& text = GetText();
   if (UNLIKELY(!IsSoftHyphenEnabled())) {
+    const String& text = GetText();
     for (;; offset--) {
       offset = break_iterator_->PreviousBreakOpportunity(offset, start);
       if (offset <= start || offset >= text.length() ||
@@ -164,24 +147,15 @@
   if (UNLIKELY(hyphenation_))
     return Hyphenate(offset, start, true);
 
-  // If the break opportunity is preceded by trailing spaces, find the
-  // end of non-hangable character (i.e., start of the space run).
-  unsigned break_offset =
-      break_iterator_->PreviousBreakOpportunity(offset, start);
-  unsigned non_hangable_run_end =
-      IsBreakableSpace(text[break_offset - 1])
-          ? FindNonHangableEnd(text, break_offset - 1)
-          : break_offset;
-
-  return {break_offset, non_hangable_run_end, false};
+  return {break_iterator_->PreviousBreakOpportunity(offset, start), false};
 }
 
 ShapingLineBreaker::BreakOpportunity ShapingLineBreaker::NextBreakOpportunity(
     unsigned offset,
     unsigned start,
     unsigned len) const {
-  const String& text = GetText();
   if (UNLIKELY(!IsSoftHyphenEnabled())) {
+    const String& text = GetText();
     for (;; offset++) {
       offset = break_iterator_->NextBreakOpportunity(offset);
       if (offset >= text.length() || text[offset - 1] != kSoftHyphenCharacter)
@@ -192,16 +166,7 @@
   if (UNLIKELY(hyphenation_))
     return Hyphenate(offset, start, false);
 
-  // We should also find the beginning of the space run to find the
-  // end of non-hangable character (i.e., start of the space run),
-  // which may be useful to avoid reshaping.
-  unsigned break_offset = break_iterator_->NextBreakOpportunity(offset, len);
-  unsigned non_hangable_run_end =
-      IsBreakableSpace(text[break_offset - 1])
-          ? FindNonHangableEnd(text, break_offset - 1)
-          : break_offset;
-
-  return {break_offset, non_hangable_run_end, false};
+  return {break_iterator_->NextBreakOpportunity(offset, len), false};
 }
 
 // Shapes a line of text by finding a valid and appropriate break opportunity
@@ -245,10 +210,7 @@
   DCHECK_LT(start, range_end);
   result_out->is_overflow = false;
   result_out->is_hyphenated = false;
-  result_out->has_trailing_spaces = false;
   const String& text = GetText();
-  const bool is_break_after_any_space =
-      break_iterator_->BreakSpace() == BreakSpaceType::kAfterEverySpace;
 
   // The start position in the original shape results.
   float start_position = result_->CachedPositionForOffset(start - range_start);
@@ -280,58 +242,23 @@
   // comparing floats. See ShapeLineZeroAvailableWidth on Linux/Mac.
   candidate_break = std::max(candidate_break, start);
 
-  // If we are in the middle of a trailing space sequence, which are
-  // defined by the UAX#14 spec as Break After (A) class, we should
-  // look for breaking opportunityes after the end of the sequence.
-  // https://www.unicode.org/reports/tr14/#BA
-  // TODO(jfernandez): if break-spaces, do special handling.
-  BreakOpportunity break_opportunity =
-      !IsBreakableSpace(text[candidate_break]) || is_break_after_any_space
-          ? PreviousBreakOpportunity(candidate_break, start)
-          : NextBreakOpportunity(std::max(candidate_break, start + 1), start,
-                                 range_end);
-
-  // There are no break opportunity before candidate_break, overflow.
+  // If there are no break opportunity before candidate_break, overflow.
   // Find the next break opportunity after the candidate_break.
-  // TODO: (jfernandez): Maybe also non_hangable_run_end <= start ?
+  BreakOpportunity break_opportunity =
+      PreviousBreakOpportunity(candidate_break, start);
   result_out->is_overflow = break_opportunity.offset <= start;
   if (result_out->is_overflow) {
-    DCHECK(is_break_after_any_space ||
-           !IsBreakableSpace(text[candidate_break]));
     if (options & kNoResultIfOverflow)
       return nullptr;
-    // No need to scan past range_end for a break opportunity.
+    // No need to scan past range_end for a break oppertunity.
     break_opportunity = NextBreakOpportunity(
         std::max(candidate_break, start + 1), start, range_end);
-  }
-
-  // We don't care whether this result contains only spaces if we
-  // are breaking after any space. We shouldn't early return either
-  // in that case.
-  if (!is_break_after_any_space &&
-      break_opportunity.non_hangable_run_end <= start) {
-    // TODO (jfenandez): There may be cases where candidate_break is
-    // not a breakable space but we also want to early return for
-    // triggering the trailing spaces handling
-    if (IsBreakableSpace(text[candidate_break])) {
-      result_out->has_trailing_spaces = true;
-      result_out->break_offset = std::min(range_end, break_opportunity.offset);
-      result_out->non_hangable_run_end = break_opportunity.non_hangable_run_end;
-#if DCHECK_IS_ON()
-      DCHECK(IsAllSpaces(text, start, result_out->break_offset));
-#endif
-      return ShapeResultView::Create(result_.get(), start,
-                                     result_out->break_offset);
-    }
-  }
-
-  // |range_end| may not be a break opportunity, but this function cannot
-  // measure beyond it.
-  if (break_opportunity.offset >= range_end) {
-    result_out->break_offset = range_end;
-    result_out->non_hangable_run_end = break_opportunity.non_hangable_run_end;
-    if (result_out->is_overflow)
+    // |range_end| may not be a break opportunity, but this function cannot
+    // measure beyond it.
+    if (break_opportunity.offset >= range_end) {
+      result_out->break_offset = range_end;
       return ShapeToEnd(start, first_safe, range_start, range_end);
+    }
   }
   CheckBreakOffset(break_opportunity.offset, start, range_end);
 
@@ -343,7 +270,6 @@
     if (first_safe >= break_opportunity.offset) {
       // There is no safe-to-break, reshape the whole range.
       result_out->break_offset = break_opportunity.offset;
-      result_out->non_hangable_run_end = break_opportunity.non_hangable_run_end;
       CheckBreakOffset(result_out->break_offset, start, range_end);
       return ShapeResultView::Create(
           Shape(start, break_opportunity.offset).get());
@@ -359,21 +285,12 @@
   DCHECK_LE(first_safe, break_opportunity.offset);
 
   scoped_refptr<const ShapeResult> line_end_result;
+  unsigned last_safe = break_opportunity.offset;
   bool reshape_line_end = true;
   if (options & kDontReshapeEndIfAtSpace) {
-    if (IsBreakableSpace(text[break_opportunity.offset - 1]))
+    if (IsBreakableSpace(text[break_opportunity.offset]))
       reshape_line_end = false;
   }
-  // Avoid re-shape if at the end of the range.
-  // TODO (jfernandez): Is this even possible ? Shouldn't we just
-  // early return if offset >= range_end ?
-  if (break_opportunity.offset == range_end)
-    reshape_line_end = false;
-  if (!is_break_after_any_space) {
-    break_opportunity.offset =
-        std::max(start + 1, break_opportunity.non_hangable_run_end);
-  }
-  unsigned last_safe = break_opportunity.offset;
   if (reshape_line_end) {
     // If the previous valid break opportunity is not at a safe-to-break
     // boundary reshape between the safe-to-break offset and the valid break
@@ -381,10 +298,6 @@
     // preceding boundary is tried until the available space is sufficient.
     while (true) {
       DCHECK_LE(start, break_opportunity.offset);
-      if (!is_break_after_any_space) {
-        break_opportunity.offset =
-            std::max(start + 1, break_opportunity.non_hangable_run_end);
-      }
       last_safe =
           result_->CachedPreviousSafeToBreakOffset(break_opportunity.offset);
       // No need to reshape the line end because this opportunity is safe.
@@ -398,7 +311,7 @@
 
       // Moved the opportunity back enough to require reshaping the whole line.
       if (UNLIKELY(last_safe < first_safe)) {
-        DCHECK(last_safe == 0 || last_safe < start);
+        DCHECK_LT(last_safe, start);
         last_safe = start;
         line_start_result = nullptr;
       }
@@ -432,17 +345,12 @@
       // because none can fit. The one after candidate_break is better for
       // ligatures, but the one before is better for kernings.
       result_out->is_overflow = true;
-      // TODO (jfernandez): Would be possible to refactor this logic
-      // with the one performed prior tp the reshape
-      // (FindBreakingOpportuntty() + overflow handling)?
       break_opportunity = PreviousBreakOpportunity(candidate_break, start);
       if (break_opportunity.offset <= start) {
         break_opportunity = NextBreakOpportunity(
             std::max(candidate_break, start + 1), start, range_end);
         if (break_opportunity.offset >= range_end) {
           result_out->break_offset = range_end;
-          result_out->non_hangable_run_end =
-              break_opportunity.non_hangable_run_end;
           return ShapeToEnd(start, first_safe, range_start, range_end);
         }
       }
@@ -472,7 +380,6 @@
   DCHECK_EQ(break_opportunity.offset - start, line_result->NumCharacters());
 
   result_out->break_offset = break_opportunity.offset;
-  result_out->non_hangable_run_end = break_opportunity.non_hangable_run_end;
   result_out->is_hyphenated =
       break_opportunity.is_hyphenated ||
       text[break_opportunity.offset - 1] == kSoftHyphenCharacter;
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h
index 297bdff5..8958179e 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h
@@ -53,15 +53,9 @@
     STACK_ALLOCATED();
 
    public:
-    // Indicates the limits of the space run.
-    base::Optional<unsigned> non_hangable_run_end;
-
     // Indicates the resulting break offset.
     unsigned break_offset;
 
-    // Indicates that the shape result contains trailing spaces
-    bool has_trailing_spaces;
-
     // True if there were no break opportunities that can fit. When this is
     // false, the result width should be smaller than or equal to the available
     // space.
@@ -113,17 +107,7 @@
     STACK_ALLOCATED();
 
    public:
-    BreakOpportunity(unsigned new_offset, bool hyphenated)
-        : offset(new_offset),
-          non_hangable_run_end(new_offset),
-          is_hyphenated(hyphenated) {}
-    BreakOpportunity(unsigned new_offset, unsigned run_end, bool hyphenated)
-        : offset(new_offset),
-          non_hangable_run_end(run_end),
-          is_hyphenated(hyphenated) {}
-
     unsigned offset;
-    unsigned non_hangable_run_end;
     bool is_hyphenated;
   };
   BreakOpportunity PreviousBreakOpportunity(unsigned offset,
diff --git a/third_party/blink/renderer/platform/scheduler/DEPS b/third_party/blink/renderer/platform/scheduler/DEPS
index 444958a..2ca4707 100644
--- a/third_party/blink/renderer/platform/scheduler/DEPS
+++ b/third_party/blink/renderer/platform/scheduler/DEPS
@@ -37,6 +37,7 @@
   "+base/synchronization/cancellation_flag.h",
   "+base/synchronization/lock.h",
   "+base/task/common/scoped_defer_task_posting.h",
+  "+base/task/common/task_annotator.h",
   "+base/task/sequence_manager/lazy_now.h",
   "+base/task/sequence_manager/sequence_manager.h",
   "+base/task/sequence_manager/task_queue.h",
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 03da9bd8..d07149df6 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
@@ -10,6 +10,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/common/scoped_defer_task_posting.h"
+#include "base/task/common/task_annotator.h"
 #include "base/task/sequence_manager/lazy_now.h"
 #include "base/time/time.h"
 #include "base/trace_event/blame_context.h"
@@ -718,7 +719,7 @@
 }
 
 base::WeakPtr<FrameSchedulerImpl>
-FrameSchedulerImpl::GetInvalidingOnBFCacheRestoreWeakPtr() {
+FrameSchedulerImpl::GetInvalidatingOnBFCacheRestoreWeakPtr() {
   return invalidating_on_bfcache_restore_weak_factory_.GetWeakPtr();
 }
 
@@ -1228,18 +1229,18 @@
           // Only log IPC tasks. IPC tasks are only logged currently as IPC
           // hash can be mapped back to a function name, and IPC tasks may
           // potentially post sensitive information.
-          if (!task.ipc_hash) {
+          if (!task.ipc_hash && !task.ipc_interface_name) {
             return;
           }
           base::ScopedDeferTaskPosting::PostOrDefer(
               task_runner, FROM_HERE,
               base::BindOnce(
                   &FrameSchedulerImpl::OnIPCTaskPostedWhileInBackForwardCache,
-                  frame_scheduler, task.ipc_hash, task.posted_from),
+                  frame_scheduler, task.ipc_hash, task.ipc_interface_name),
               base::TimeDelta());
         },
         main_thread_scheduler_->DefaultTaskRunner(),
-        GetInvalidingOnBFCacheRestoreWeakPtr()));
+        GetInvalidatingOnBFCacheRestoreWeakPtr()));
   }
 }
 
@@ -1254,7 +1255,20 @@
 
 void FrameSchedulerImpl::OnIPCTaskPostedWhileInBackForwardCache(
     uint32_t ipc_hash,
-    const base::Location& task_from) {
+    const char* ipc_interface_name) {
+  // IPC tasks may have an IPC interface name in addition to, or instead of an
+  // IPC hash. IPC hash is known from the mojo Accept method. When IPC hash is
+  // 0, then the IPC hash must be calculated from the IPC interface name
+  // instead.
+  if (!ipc_hash) {
+    // base::HashMetricName produces a uint64; however, the MD5 hash calculation
+    // for an IPC interface name is always calculated as uint32; the IPC hash on
+    // a task is also a uint32. The calculation here is meant to mimic the
+    // calculation used in base::MD5Hash32Constexpr.
+    ipc_hash = base::TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName(
+        ipc_interface_name);
+  }
+
   DCHECK(parent_page_scheduler_->IsStoredInBackForwardCache());
   base::UmaHistogramSparse(
       "BackForwardCache.Experimental.UnexpectedIPCMessagePostedToCachedFrame."
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 93120c4..4fc0c0c 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
@@ -142,7 +142,7 @@
 
   base::WeakPtr<FrameScheduler> GetWeakPtr() override;
   base::WeakPtr<const FrameSchedulerImpl> GetWeakPtr() const;
-  base::WeakPtr<FrameSchedulerImpl> GetInvalidingOnBFCacheRestoreWeakPtr();
+  base::WeakPtr<FrameSchedulerImpl> GetInvalidatingOnBFCacheRestoreWeakPtr();
 
   void ReportActiveSchedulerTrackedFeatures() override;
 
@@ -184,7 +184,7 @@
   void SetOnIPCTaskPostedWhileInBackForwardCacheHandler();
   void DetachOnIPCTaskPostedWhileInBackForwardCacheHandler();
   void OnIPCTaskPostedWhileInBackForwardCache(uint32_t ipc_hash,
-                                              const base::Location& task_from);
+                                              const char* ipc_interface_name);
 
   // Returns the list of active features which currently tracked by the
   // scheduler for back-forward cache metrics.
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 4602a823..87cc128 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -1263,6 +1263,58 @@
       worker_throttled_count, worker_stopped_count);
 }
 
+TEST_F(FrameSchedulerImplTest, LogIpcsPostedToFramesInBackForwardCache) {
+  base::HistogramTester histogram_tester;
+
+  // Create the task queue implicitly.
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      frame_scheduler_->GetTaskRunner(TaskType::kInternalTest);
+
+  StorePageInBackForwardCache();
+
+  // Run the tasks so that they are recorded in the histogram
+  task_environment_.FastForwardBy(base::TimeDelta::FromHours(1));
+
+  // Post IPC tasks, accounting for delay for when tracking starts.
+  {
+    base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(1);
+    task_runner->PostTask(FROM_HERE, base::DoNothing());
+  }
+  {
+    base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash_2(2);
+    task_runner->PostTask(FROM_HERE, base::DoNothing());
+  }
+  task_environment_.RunUntilIdle();
+
+  // Once the page is restored from the cache, IPCs should no longer be
+  // recorded.
+  RestorePageFromBackForwardCache();
+
+  // Start posting tasks immediately - will not be recorded
+  {
+    base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash_3(3);
+    task_runner->PostTask(FROM_HERE, base::DoNothing());
+  }
+  {
+    base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash_4(4);
+    task_runner->PostTask(FROM_HERE, base::DoNothing());
+  }
+
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "BackForwardCache.Experimental."
+          "UnexpectedIPCMessagePostedToCachedFrame.MethodHash"),
+      testing::UnorderedElementsAre(base::Bucket(1, 1), base::Bucket(2, 1)));
+
+  // TimeUntilIPCReceived should have values in the 300000 bucket corresponding
+  // with the hour delay in task_environment_.FastForwardBy.
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "BackForwardCache.Experimental."
+          "UnexpectedIPCMessagePostedToCachedFrame.TimeUntilIPCReceived"),
+      testing::UnorderedElementsAre(base::Bucket(300000, 2)));
+}
+
 TEST_F(FrameSchedulerImplTest,
        LogIpcsFromMultipleThreadsPostedToFramesInBackForwardCache) {
   base::HistogramTester histogram_tester;
@@ -1282,7 +1334,7 @@
       base::BindOnce(
           [](scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
             base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(1);
-            task_runner->PostTask(FROM_HERE, base::BindOnce([]() {}));
+            task_runner->PostTask(FROM_HERE, base::DoNothing());
           },
           task_runner));
   task_environment_.RunUntilIdle();
@@ -1298,7 +1350,7 @@
              base::RepeatingClosure restore_from_cache_callback) {
             {
               base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(2);
-              task_runner->PostTask(FROM_HERE, base::BindOnce([]() {}));
+              task_runner->PostTask(FROM_HERE, base::DoNothing());
             }
             {
               // Once the page is restored from the cache, ensure that the IPC
@@ -1308,7 +1360,7 @@
             }
             {
               base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(4);
-              task_runner->PostTask(FROM_HERE, base::BindOnce([]() {}));
+              task_runner->PostTask(FROM_HERE, base::DoNothing());
             }
           },
           task_runner, restore_from_cache_callback));
@@ -1320,7 +1372,7 @@
       base::BindOnce(
           [](scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
             base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(5);
-            task_runner->PostTask(FROM_HERE, base::BindOnce([]() {}));
+            task_runner->PostTask(FROM_HERE, base::DoNothing());
           },
           task_runner));
   task_environment_.RunUntilIdle();
@@ -1330,16 +1382,16 @@
       base::BindOnce(
           [](scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
             base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(6);
-            task_runner->PostTask(FROM_HERE, base::BindOnce([]() {}));
+            task_runner->PostTask(FROM_HERE, base::DoNothing());
           },
           task_runner));
   task_environment_.RunUntilIdle();
 
-  EXPECT_THAT(histogram_tester.GetAllSamples(
-                  "BackForwardCache.Experimental."
-                  "UnexpectedIPCMessagePostedToCachedFrame.MethodHash"),
-              testing::UnorderedElementsAreArray(
-                  {base::Bucket(1, 1), base::Bucket(2, 1)}));
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "BackForwardCache.Experimental."
+          "UnexpectedIPCMessagePostedToCachedFrame.MethodHash"),
+      testing::UnorderedElementsAre(base::Bucket(1, 1), base::Bucket(2, 1)));
 }
 
 // TODO(farahcharab) Move priority testing to MainThreadTaskQueueTest after
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index a404be3..bf03a0cb 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -68,6 +68,9 @@
 constexpr base::TimeDelta kThrottledWakeUpDuration =
     base::TimeDelta::FromMilliseconds(3);
 
+constexpr base::TimeDelta kDefaultDelayForTrackingIPCsPostedToCachedFrames =
+    base::TimeDelta::FromSeconds(15);
+
 // Values coming from the field trial config are interpreted as follows:
 //   -1 is "not set". Scheduler should use a reasonable default.
 //   0 corresponds to base::nullopt.
@@ -145,6 +148,21 @@
       kDelayForBackgroundAndNetworkIdleTabFreezingMillis.Get());
 }
 
+base::TimeDelta GetTimeToDelayIPCTrackingWhileStoredInBackForwardCache() {
+  if (base::FeatureList::IsEnabled(
+          features::kLogUnexpectedIPCPostedToBackForwardCachedDocuments)) {
+    static const base::FeatureParam<int>
+        kDelayForLoggingUnexpectedIPCPostedToBckForwardCacheMillis{
+            &features::kLogUnexpectedIPCPostedToBackForwardCachedDocuments,
+            "delay_before_tracking_ms",
+            static_cast<int>(kDefaultDelayForTrackingIPCsPostedToCachedFrames
+                                 .InMilliseconds())};
+    return base::TimeDelta::FromMilliseconds(
+        kDelayForLoggingUnexpectedIPCPostedToBckForwardCacheMillis.Get());
+  }
+  return kDefaultDelayForTrackingIPCsPostedToCachedFrames;
+}
+
 }  // namespace
 
 constexpr base::TimeDelta PageSchedulerImpl::kDefaultThrottledWakeUpInterval;
@@ -340,7 +358,7 @@
         *main_thread_scheduler_->ControlTaskRunner(), FROM_HERE,
         base::BindRepeating(&PageSchedulerImpl::SetUpIPCTaskDetection,
                             GetWeakPtr()),
-        base::TimeDelta::FromSeconds(15));
+        GetTimeToDelayIPCTrackingWhileStoredInBackForwardCache());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.cc b/third_party/blink/renderer/platform/text/text_break_iterator.cc
index e34b073..d0b2939 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.cc
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.cc
@@ -334,12 +334,6 @@
           continue;
         }
         break;
-      case BreakSpaceType::kAfterSpaceRun:
-        if (is_space)
-          continue;
-        if (is_last_space)
-          return i;
-        break;
       case BreakSpaceType::kAfterEverySpace:
         if (is_last_space)
           return i;
@@ -404,10 +398,6 @@
       return NextBreakablePosition<CharacterType, lineBreakType,
                                    BreakSpaceType::kBeforeSpaceRun>(pos, str,
                                                                     len);
-    case BreakSpaceType::kAfterSpaceRun:
-      return NextBreakablePosition<CharacterType, lineBreakType,
-                                   BreakSpaceType::kAfterSpaceRun>(pos, str,
-                                                                   len);
     case BreakSpaceType::kAfterEverySpace:
       return NextBreakablePosition<CharacterType, lineBreakType,
                                    BreakSpaceType::kAfterEverySpace>(pos, str,
@@ -526,8 +516,6 @@
       return ostream << "kAfterEverySpace";
     case BreakSpaceType::kBeforeSpaceRun:
       return ostream << "kBeforeSpaceRun";
-    case BreakSpaceType::kAfterSpaceRun:
-      return ostream << "kAfterSpaceRun";
   }
   NOTREACHED();
   return ostream << "BreakSpaceType::" << static_cast<int>(break_space);
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.h b/third_party/blink/renderer/platform/text/text_break_iterator.h
index 00b32c6..6192e29 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.h
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.h
@@ -111,11 +111,6 @@
   // LayoutNG line breaker uses this type.
   kBeforeSpaceRun,
 
-  // Break after a run of white space characters.
-  // This mode enables the LazyLineBreakIterator to completely rely on
-  // ICU for determining the breaking opportunities.
-  kAfterSpaceRun,
-
   // white-spaces:break-spaces allows breaking after any preserved white-space,
   // even when these are leading spaces so that we can avoid breaking
   // the word in case of overflow.
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 5588c60..cac8fca 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -120,7 +120,6 @@
 ### external/wpt/css/css-sizing/
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-004.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-009.tentative.html [ Failure ]
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-010.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-013.tentative.html [ Pass ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-024.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-028.tentative.html [ Failure ]
@@ -210,9 +209,6 @@
 crbug.com/591099 external/wpt/css/css-text/white-space/control-chars-00C.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-002.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-012.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-013.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-014.html [ Failure ]
 
 ### external/wpt/css/css-text/word-break/
 crbug.com/591099 external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Failure ]
@@ -347,9 +343,6 @@
 crbug.com/1012289 external/wpt/css/css-lists/list-style-type-string-006.html [ Failure ]
 crbug.com/1012294 external/wpt/css/css-lists/list-style-type-string-007.html [ Pass ]
 
-### external/wpt/css/css-content/
-crbug.com/591099 external/wpt/css/css-content/quotes-033.html [ Failure ]
-
 ### external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml [ Failure ]
 crbug.com/886592 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/align3/flex-abspos-staticpos-margin-002.html [ Failure ]
@@ -389,7 +382,6 @@
 
 ### virtual/text-antialias/
 crbug.com/591099 virtual/text-antialias/justify-ideograph-simple.html [ Failure ]
-crbug.com/591099 virtual/text-antialias/apply-start-width-after-skipped-text.html [ Failure ]
 
 ### http/tests/
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-node-vertical-rl.js [ Failure ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 7b82157..ebf372567 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -428,6 +428,13 @@
 external/wpt/css/css-text/letter-spacing/letter-spacing-105.html [ Skip ]
 external/wpt/css/css-text/letter-spacing/letter-spacing-106.html [ Skip ]
 
+# No browsers implemented the spec behavior, not web compatible, no consensus.
+# https://github.com/w3c/csswg-drafts/issues/1518
+external/wpt/css/css-text/letter-spacing/letter-spacing-bidi-003.xht [ Skip ]
+external/wpt/css/css-text/letter-spacing/letter-spacing-bidi-004.xht [ Skip ]
+external/wpt/css/css-text/letter-spacing/letter-spacing-bidi-005.xht [ Skip ]
+external/wpt/css/css-text/letter-spacing/letter-spacing-nesting-003.xht [ Skip ]
+
 # CSS Text features that are not planned to implement.
 external/wpt/css/css-text/parsing/word-boundary-detection-computed.html [ Skip ]
 external/wpt/css/css-text/parsing/word-boundary-detection-invalid.html [ Skip ]
@@ -459,15 +466,21 @@
 external/wpt/css/css-text/line-breaking/line-breaking-replaced-002.html [ Skip ]
 external/wpt/css/css-text/line-breaking/line-breaking-replaced-003.html [ Skip ]
 
+# `tab-size` at non-block container (inline) not implemented.
+external/wpt/css/css-text/tab-size/tab-size-inline-001.html [ Skip ]
+external/wpt/css/css-text/tab-size/tab-size-inline-002.html [ Skip ]
+external/wpt/css/css-text/tab-size/tab-size-integer-004.html [ Skip ]
+
 # Undefined behavior without any implementations.
 external/wpt/css/css-text/white-space/seg-break-transformation-019.html [ Skip ]
 
-# No browsers implemented the spec behavior, not web compatible, no consensus.
-# https://github.com/w3c/csswg-drafts/issues/1518
-external/wpt/css/css-text/letter-spacing/letter-spacing-bidi-003.xht [ Skip ]
-external/wpt/css/css-text/letter-spacing/letter-spacing-bidi-004.xht [ Skip ]
-external/wpt/css/css-text/letter-spacing/letter-spacing-bidi-005.xht [ Skip ]
-external/wpt/css/css-text/letter-spacing/letter-spacing-nesting-003.xht [ Skip ]
+# UAX#9 L1 is not implemented yet.
+crbug.com/316409 external/wpt/css/css-text/white-space/eol-spaces-bidi-002.html [ Skip ]
+
+# Removing trailing ogham spaces not implemented.
+external/wpt/css/css-text/white-space/trailing-ogham-001.html [ Skip ]
+external/wpt/css/css-text/white-space/trailing-ogham-002.html [ Skip ]
+external/wpt/css/css-text/white-space/trailing-ogham-003.html [ Skip ]
 
 external/wpt/css/CSS2/linebox/inline-formatting-context-010b.xht [ Skip ]
 external/wpt/css/CSS2/normal-flow/inline-block-replaced-height-008.xht [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 03d0d45..0ba6520 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -881,7 +881,6 @@
 crbug.com/591099 fast/multicol/vertical-lr/unsplittable-inline-block.html [ Failure ]
 crbug.com/591099 [ Win ] virtual/text-antialias/ellipsis-with-self-painting-layer.html [ Failure ]
 crbug.com/591099 [ Mac ] fast/multicol/file-upload-as-multicol.html [ Failure ]
-crbug.com/1098801 virtual/text-antialias/whitespace/whitespace-in-pre.html [ Failure ]
 
 # LayoutNG failures that needs to be triaged
 crbug.com/591099 virtual/text-antialias/selection/selection-rect-line-height-too-small.html [ Failure ]
@@ -2536,35 +2535,7 @@
 crbug.com/626703 [ Mac ] external/wpt/fetch/connection-pool/network-partition-key.html [ Timeout ]
 crbug.com/626703 [ Mac11.0 ] external/wpt/fetch/connection-pool/network-partition-key.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/fetch/connection-pool/network-partition-key.html [ Timeout ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-text/white-space/eol-spaces-bidi-002.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-text/white-space/eol-spaces-bidi-002.html [ Failure ]
-crbug.com/626703 [ Mac11.0 ] external/wpt/css/css-text/white-space/eol-spaces-bidi-002.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-text/white-space/eol-spaces-bidi-002.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-text/white-space/trailing-ogham-002.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-text/white-space/trailing-ogham-002.html [ Failure ]
-crbug.com/626703 [ Mac11.0 ] external/wpt/css/css-text/white-space/trailing-ogham-002.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-text/white-space/trailing-ogham-002.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-text/white-space/trailing-ogham-001.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-text/white-space/trailing-ogham-001.html [ Failure ]
-crbug.com/626703 [ Mac11.0 ] external/wpt/css/css-text/white-space/trailing-ogham-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-text/white-space/trailing-ogham-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-text/white-space/trailing-ogham-003.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-text/white-space/trailing-ogham-003.html [ Failure ]
-crbug.com/626703 [ Mac11.0 ] external/wpt/css/css-text/white-space/trailing-ogham-003.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-text/white-space/trailing-ogham-003.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-text/tab-size/tab-size-integer-004.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-text/tab-size/tab-size-integer-004.html [ Failure ]
-crbug.com/626703 [ Mac11.0 ] external/wpt/css/css-text/tab-size/tab-size-integer-004.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-text/tab-size/tab-size-integer-004.html [ Failure ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-top-left.html [ Timeout ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-text/tab-size/tab-size-inline-002.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-text/tab-size/tab-size-inline-002.html [ Failure ]
-crbug.com/626703 [ Mac11.0 ] external/wpt/css/css-text/tab-size/tab-size-inline-002.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-text/tab-size/tab-size-inline-002.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-text/tab-size/tab-size-inline-001.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-text/tab-size/tab-size-inline-001.html [ Failure ]
-crbug.com/626703 [ Mac11.0 ] external/wpt/css/css-text/tab-size/tab-size-inline-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-text/tab-size/tab-size-inline-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-043.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-048.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-023.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-wrap-trailing-spaces-013-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-wrap-trailing-spaces-013-ref.html
deleted file mode 100644
index af1f6bf2..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-wrap-trailing-spaces-013-ref.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<meta http-equiv="content-language" content="en, ja" />
-<title>CSS test Reference</title>
-<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
-<style>
-    div { white-space: pre;  }
-    span { background: blue; }
-</style>
-
-<p>This test passes if the line is after the white space, which hangs (blue).
-
-<div>ああ<span>&#x0020;<br>X</span></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-wrap-after-nowrap-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-wrap-after-nowrap-001-ref.html
index 71e1c82..80b2260c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-wrap-after-nowrap-001-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-wrap-after-nowrap-001-ref.html
@@ -24,5 +24,7 @@
   <section class="ideo">
     <div>国<br>国</div>
     <div>国<br>国</div>
+    <div>国<br>国</div>
+    <div>国<br>国</div>
   </section>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-012.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-012.html
deleted file mode 100644
index 7f7b707..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-012.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>CSS Text test: hanging trailing spaces with white-space:pre-wrap</title>
-<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com">
-<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
-<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
-<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-normal">
-<link rel="match" href="reference/white-space-pre-wrap-trailing-spaces-004-ref.html">
-<meta name="assert" content="Previous breaking opportunities are not considered if the overflow is caused by hanging trailing spaces.">
-<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
-<style>
-div {
-  font: 10px/1 Ahem;
-}
-.ref {
-  position: absolute;
-  color: red;
-  z-index: -1;
-}
-.ref span { color: green; }
-span { background: green; }
-.test {
-  color: green;
-
-  width: 4ch;
-  white-space: pre-wrap;
-}
-</style>
-
-<p>This test passes if there is a green square and no red.
-<div class="ref">XX<span>X</span>XX<br>XXXXX</span><br>XXXXX<br>XXXXX<br>XXXXX</div>
-<div class="test">XX X<span> X</span><span>XX  </span>XXXXX<br>XXXXX<br>XXXXX</div>
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-013.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-013.html
deleted file mode 100644
index 40f14987..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-013.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<meta http-equiv="content-language" content="en, ja" />
-<title>CSS Text test: hanging trailing spaces with white-space:pre-wrap</title>
-<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com">
-<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
-<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
-<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-normal">
-<link rel="match" href="reference/white-space-pre-wrap-trailing-spaces-013-ref.html">
-<meta name="assert" content="Previous breaking opportunities are not considered if the overflow is caused by hanging trailing spaces.">
-<style>
-div {
-  width: 2em;
-  white-space: pre-wrap;
-}
-span { background: blue; } /* If the space is removed instead of hanging, there will be no blue box*/
-</style>
-
-<p>This test passes if the line is after the white space, which hangs (blue).
-
-<div>ああ<span>&#x0020;X<span></div>
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-014.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-014.html
deleted file mode 100644
index 757e31e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-014.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>CSS Text test: hanging trailing spaces with white-space:pre-wrap</title>
-<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com">
-<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
-<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
-<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-normal">
-<link rel="match" href="reference/white-space-pre-wrap-trailing-spaces-004-ref.html">
-<meta name="assert" content="Previous breaking opportunities are not considered if the overflow is caused by hanging trailing spaces.">
-<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
-<style>
-div {
-  font: 10px/1 Ahem;
-}
-.ref {
-  position: absolute;
-  color: red;
-  z-index: -1;
-}
-span { background: green; }
-.ref span { color: green; }
-.test {
-  color: green;
-
-  width: 4ch;
-  white-space: pre-wrap;
-}
-</style>
-
-<p>This test passes if there is a green square and no red.
-<div class="ref">XX<span>X</span>X<span>X</span><br>XXXXX</span><br>XXXXX<br>XXXXX<br>XXXXX</div>
-<div class="test">XX<span> X</span> X<span>XX  </span>XXXXX<br>XXXXX<br>XXXXX</div>
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-015.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-015.html
deleted file mode 100644
index ea74160..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-015.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>CSS Text test: hanging trailing spaces with white-space:pre-wrap</title>
-<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com">
-<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
-<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
-<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-normal">
-<link rel="match" href="reference/white-space-pre-wrap-trailing-spaces-004-ref.html">
-<meta name="assert" content="Previous breaking opportunities are not considered if the overflow is caused by hanging trailing spaces.">
-<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
-<style>
-div {
-  font: 10px/1 Ahem;
-}
-.ref {
-  position: absolute;
-  color: red;
-  z-index: -1;
-}
-span { background: green; }
-.ref span { color: green; }
-.test {
-  color: green;
-
-  width: 5ch;
-  white-space: pre-wrap;
-}
-</style>
-
-<p>This test passes if there is a green square and no red.
-<div class="ref">XX<span>XXX</span><br>XXX<span>X</span>X<br>XXXXX</span><br>XXXXX<br>XXXXX<br></div>
-<div class="test">XX XX<span>X X</span><br>XXXXX<br>XXXXX<br>XXXXX</div>
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-wrap-after-nowrap-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-wrap-after-nowrap-001.html
index 5947c28..a484f1c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-wrap-after-nowrap-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-wrap-after-nowrap-001.html
@@ -35,5 +35,7 @@
   <section class="ideo">
     <div><span class="nowrap">国</span>国</div>
     <div><span class="nowrap">国</span><span class="normal">国</span></div>
+    <div><span class="nowrap">国<span class="normal">国</span></span></div>
+    <div class="nowrap">国<span class="normal">国</span></div>
   </section>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/layout-instability/move-distance-clamped.html b/third_party/blink/web_tests/external/wpt/layout-instability/move-distance-clamped.html
new file mode 100644
index 0000000..6c5f959
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/layout-instability/move-distance-clamped.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>Layout Instability: distance fraction not more than 1.0</title>
+<link rel="help" href="https://wicg.github.io/layout-instability/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-adapter.js"></script>
+<script src="resources/util.js"></script>
+<style>
+body { margin: 0; }
+#shifter {
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  left: -2000vw;
+  top: -2000vh;
+}
+</style>
+<div id="shifter"></div>
+<script>
+
+promise_test(async () => {
+  const watcher = new ScoreWatcher;
+
+  // Wait for the initial render to complete.
+  await waitForAnimationFrames(2);
+
+  // Modify the position of the div.
+  document.querySelector("#shifter").style = "left: 0; top: 0";
+
+  const docElement = document.documentElement;
+  const viewWidth = docElement.clientWidth;
+  const viewHeight = docElement.clientHeight;
+
+  // An element the size of the viewport has shifted by a huge distance, but
+  // the move distance is effectively the viewport width or height (whichever
+  // is larger) as the distance fraction is limited to a maximum of 1.0.
+  const expectedScore = computeExpectedScore(
+      viewWidth * viewHeight,
+      Math.max(viewWidth, viewHeight));
+
+  // Observer fires after the frame is painted.
+  cls_expect(watcher, {score: 0});
+  await watcher.promise;
+  cls_expect(watcher, {score: expectedScore});
+}, "Distance fraction not more than 1.0.");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/layout-instability/visibility-hidden.html b/third_party/blink/web_tests/external/wpt/layout-instability/visibility-hidden.html
new file mode 100644
index 0000000..ec1c331
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/layout-instability/visibility-hidden.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Layout Instability: visibility:hidden</title>
+<link rel="help" href="https://wicg.github.io/layout-instability/" />
+<div id="target" style="position: absolute; top: 0; width: 400px; height: 400px; visibility: hidden"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/util.js"></script>
+<script>
+
+promise_test(async () => {
+  const watcher = new ScoreWatcher;
+
+  // Wait for the initial render to complete.
+  await waitForAnimationFrames(2);
+
+  // Shift target, for which no shift should be reported because it's hidden.
+  document.querySelector("#target").style.top = '200px';
+
+  await waitForAnimationFrames(2);
+  // No shift should be reported.
+  assert_equals(watcher.score, 0);
+}, 'visibility:hidden');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/chromium.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
index 0c2fc40..cef01c4 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
@@ -41,20 +41,38 @@
         # Browser log for the current test under execution.
         self.test_log = []
 
-    def _append_test_message(self, test, subtest, status, expected, message):
+    def _append_test_message(self, test, subtest, wpt_actual_status, message):
         """
-        Appends the message data for a test.
+        Appends the message data for a test or subtest.
         :param str test: the name of the test
-        :param str subtest: the name of the subtest with the message
-        :param str status: the subtest status
-        :param str expected: the expected subtest statuses
+        :param str subtest: the name of the subtest with the message. Will be
+                            None if this is called for a test.
+        :param str wpt_actual_status: the test status as reported by WPT
         :param str message: the string to append to the message for this test
 
         Example:
-        [subtest foo] [FAIL expected PASS] message
+          [test_or_subtest_name]
+            expected: FAIL
+            message: some test message eg assert failure
         """
-        self.messages[test].append("[%s] [%s expected %s] %s" %
-                                   (subtest, status, expected, message))
+        # NOTE: throughout this function we output a key called "expected" but
+        # fill it in with the actual status. This is by design. The goal of this
+        # output is to look exactly like WPT's expectation metadata so that it
+        # can be easily diff-ed.
+        if subtest:
+            result = "  [%s]\n    expected: %s\n" % (subtest, wpt_actual_status)
+            if message:
+                result += "    message: %s\n" % message
+            self.messages[test].append(result)
+        else:
+            # No subtest, so this is the top-level test. The result must be
+            # prepended to the list of messages, so that the message for the
+            # test comes before any subtest messages.
+            test_name_last_part = test.split("/")[-1]
+            result = "[%s]\n  expected: %s\n" % (test_name_last_part, wpt_actual_status)
+            if message:
+                result += "  message: %s\n" % message
+            self.messages[test].insert(0, result)
 
     def _append_artifact(self, cur_dict, artifact_name, artifact_value):
         """
@@ -175,15 +193,18 @@
 
     def test_status(self, data):
         test_name = data["test"]
-        actual_status = self._map_status_name(data["status"])
+        wpt_actual_status = data["status"]
+        actual_status = self._map_status_name(wpt_actual_status)
         expected_statuses = self._get_expected_status_from_data(actual_status, data)
 
         is_unexpected = actual_status not in expected_statuses
         if is_unexpected and test_name not in self.tests_with_subtest_fails:
             self.tests_with_subtest_fails.add(test_name)
-        self._append_test_message(test_name, data.get("subtest", ""),
-                                  actual_status, expected_statuses,
-                                  data.get("message", ""))
+        # We should always get a subtest in the data dict, but it's technically
+        # possible that it's missing. Be resilient here.
+        subtest_name = data.get("subtest", "UNKNOWN SUBTEST")
+        self._append_test_message(test_name, subtest_name,
+                                  wpt_actual_status, data.get("message", ""))
 
     def test_end(self, data):
         test_name = data["test"]
@@ -203,8 +224,7 @@
             if actual_status == "PASS":
                 actual_status = "FAIL"
 
-        self._append_test_message(test_name, "",
-                                  actual_status, expected_statuses,
+        self._append_test_message(test_name, None, wpt_actual_status,
                                   data.get("message", ""))
         self._store_test_result(test_name,
                                 actual_status,
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
index 59f39c1..8f8e732 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
@@ -158,15 +158,15 @@
 
     t1_artifacts = output_json["tests"]["t1"]["artifacts"]
     assert t1_artifacts["log"] == [
-        "[t1_a] [FAIL expected PASS] t1_a_message",
-        "[t1_b] [PASS expected PASS] t1_b_message",
-        "[] [FAIL expected PASS] ",
+        "[t1]\n  expected: PASS\n",
+        "  [t1_a]\n    expected: FAIL\n    message: t1_a_message\n",
+        "  [t1_b]\n    expected: PASS\n    message: t1_b_message\n",
     ]
     assert t1_artifacts["wpt_subtest_failure"] == ["true"]
     t2_artifacts = output_json["tests"]["t2"]["artifacts"]
     assert t2_artifacts["log"] == [
-        "[t2_a] [PASS expected PASS] ",
-        "[] [TIMEOUT expected PASS] t2_message",
+        "[t2]\n  expected: TIMEOUT\n  message: t2_message\n",
+        "  [t2_a]\n    expected: PASS\n",
     ]
     assert "wpt_subtest_failure" not in t2_artifacts.keys()
 
@@ -195,7 +195,7 @@
 
     # The test status is reported as a pass here because the harness was able to
     # run the test to completion.
-    logger.test_end("t1", status="PASS", expected="PASS")
+    logger.test_end("t1", status="PASS", expected="PASS", message="top_message")
     logger.suite_end()
 
     # check nothing got output to stdout/stderr
@@ -211,10 +211,10 @@
     test_obj = output_json["tests"]["t1"]
     t1_artifacts = test_obj["artifacts"]
     assert t1_artifacts["log"] == [
-        "[t1_a] [FAIL expected PASS] t1_a_message",
-        "[t1_b] [PASS expected PASS] t1_b_message",
-        "[t1_c] [TIMEOUT expected PASS] t1_c_message",
-        "[] [FAIL expected PASS] ",
+        "[t1]\n  expected: PASS\n  message: top_message\n",
+        "  [t1_a]\n    expected: FAIL\n    message: t1_a_message\n",
+        "  [t1_b]\n    expected: PASS\n    message: t1_b_message\n",
+        "  [t1_c]\n    expected: TIMEOUT\n    message: t1_c_message\n",
     ]
     assert t1_artifacts["wpt_subtest_failure"] == ["true"]
     # The status of the test in the output is a failure because subtests failed,
@@ -252,7 +252,7 @@
 
     # The test status is reported as a pass here because the harness was able to
     # run the test to completion.
-    logger.test_end("t1", status="PASS", expected="PASS")
+    logger.test_end("t1", status="OK", expected="OK")
     logger.suite_end()
 
     # check nothing got output to stdout/stderr
@@ -267,11 +267,12 @@
 
     test_obj = output_json["tests"]["t1"]
     t1_log = test_obj["artifacts"]["log"]
+    print("Lpz t1log=%s" % t1_log)
     assert t1_log == [
-        "[t1_a] [FAIL expected FAIL] t1_a_message",
-        "[t1_b] [PASS expected PASS] t1_b_message",
-        "[t1_c] [TIMEOUT expected TIMEOUT] t1_c_message",
-        "[] [PASS expected PASS] ",
+        "[t1]\n  expected: OK\n",
+        "  [t1_a]\n    expected: FAIL\n    message: t1_a_message\n",
+        "  [t1_b]\n    expected: PASS\n    message: t1_b_message\n",
+        "  [t1_c]\n    expected: TIMEOUT\n    message: t1_c_message\n",
     ]
     # The status of the test in the output is a pass because the subtest
     # failures were all expected.
@@ -316,8 +317,8 @@
     test_obj = output_json["tests"]["t1"]
     t1_artifacts = test_obj["artifacts"]
     assert t1_artifacts["log"] == [
-        "[t1_a] [PASS expected FAIL] t1_a_message",
-        "[] [FAIL expected PASS] ",
+        "[t1]\n  expected: PASS\n",
+        "  [t1_a]\n    expected: PASS\n    message: t1_a_message\n",
     ]
     assert t1_artifacts["wpt_subtest_failure"] == ["true"]
     # Since the subtest status is unexpected, we fail the test. But we report
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/text-antialias/international/bidi-innertext-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/text-antialias/international/bidi-innertext-expected.png
deleted file mode 100644
index b790573..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/text-antialias/international/bidi-innertext-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
index caab5e5..605b04e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/whitespace/tab-character-basics-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/whitespace/tab-character-basics-expected.png
index b08ce32..51ad2db 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/whitespace/tab-character-basics-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/whitespace/tab-character-basics-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
index 1a775964..e9fdf44 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png b/third_party/blink/web_tests/platform/win/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
index 6d3a945..a46017d 100644
--- a/third_party/blink/web_tests/platform/win/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
index eee1e41..9ead6e8c 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/apply-start-width-after-skipped-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/serial/serialPort_loopback-manual.https.html b/third_party/blink/web_tests/wpt_internal/serial/serialPort_loopback-manual.https.html
index 0c56007d..9e7801d 100644
--- a/third_party/blink/web_tests/wpt_internal/serial/serialPort_loopback-manual.https.html
+++ b/third_party/blink/web_tests/wpt_internal/serial/serialPort_loopback-manual.https.html
@@ -159,9 +159,9 @@
           }
         })();
 
-        await port.setSignals({brk: true});
+        await port.setSignals({break: true});
         await readPromise;
-        await port.setSignals({brk: false});
+        await port.setSignals({break: false});
 
         const writer = port.writable.getWriter();
         // |data| is small enough to be completely transmitted.
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index d6ab6830..4ae02e7 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -132,6 +132,7 @@
       'chromeos-amd64-generic-asan-rel': 'chromeos_amd64-generic_asan',
       'chromeos-amd64-generic-cfi-thin-lto-rel': 'chromeos_amd64-generic_cfi_thin_lto',
       'chromeos-amd64-generic-dbg': 'chromeos_amd64-generic_dbg',
+      'chromeos-amd64-generic-lacros-dbg': 'chromeos_amd64-generic_lacros_dbg',
       'chromeos-amd64-generic-rel': 'chromeos_amd64-generic_use_fake_dbus_clients',
       'chromeos-arm-generic-dbg': 'chromeos_arm-generic_dbg',
       'chromeos-arm-generic-rel': 'chromeos_arm-generic',
@@ -1511,11 +1512,15 @@
     ],
 
     'chromeos_amd64-generic_lacros': [
-      'amd64-generic', 'amd64-lacros', 'goma', 'official',
+      'chromeos_amd64-generic', 'amd64-lacros', 'official',
     ],
 
     'chromeos_amd64-generic_lacros_rel': [
-      'amd64-generic', 'amd64-lacros', 'goma',
+      'chromeos_amd64-generic', 'amd64-lacros',
+    ],
+
+    'chromeos_amd64-generic_lacros_dbg': [
+      'chromeos_amd64-generic', 'amd64-lacros', 'debug',
     ],
 
     'chromeos_amd64-generic_dbg': [
@@ -2444,7 +2449,7 @@
     },
 
     'amd64-lacros': {
-      'gn_args': 'use_ozone=true ozone_platform_wayland=true ozone_platform_x11=false target_os="linux" is_chromeos_device=true use_evdev_gestures=false use_vaapi=false use_gtk=false use_glib=false enable_linux_installer=false rtc_use_pipewire=false use_gio=false use_v8_context_snapshot=false use_custom_libcxx=false use_pulseaudio=false use_pangocairo=false chromeos_is_browser_only=true use_system_libsync=false cros_host_sysroot="//build/linux/debian_sid_amd64-sysroot" cros_v8_snapshot_sysroot="//build/linux/debian_sid_amd64-sysroot" use_custom_libcxx_for_host=true'
+      'gn_args': 'use_ozone=true ozone_platform_wayland=true ozone_platform_x11=false target_os="linux" use_evdev_gestures=false use_vaapi=false use_gtk=false use_glib=false enable_linux_installer=false rtc_use_pipewire=false use_gio=false use_v8_context_snapshot=false use_custom_libcxx=false use_pulseaudio=false use_pangocairo=false chromeos_is_browser_only=true use_system_libsync=false cros_host_sysroot="//build/linux/debian_sid_amd64-sysroot" cros_v8_snapshot_sysroot="//build/linux/debian_sid_amd64-sysroot" use_custom_libcxx_for_host=true'
     },
 
     # We build Android with codecs on most bots to ensure maximum test
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 144970f..64777d0 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -29808,7 +29808,7 @@
 
 <histogram
     name="Compositing.Renderer.LayerSkippedForDrawPropertiesDueToBackface"
-    units="boolean" expires_after="M87">
+    units="boolean" expires_after="M90">
   <owner>chrishtr@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -29851,7 +29851,7 @@
 </histogram>
 
 <histogram name="Compositing.Renderer.LayerUpdateSkippedDueToBackface"
-    units="boolean" expires_after="M87">
+    units="boolean" expires_after="M90">
   <owner>chrishtr@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -36669,7 +36669,7 @@
 </histogram>
 
 <histogram name="Cryptohome.DeletedUserProfiles" units="profiles"
-    expires_after="M87">
+    expires_after="M90">
   <owner>slangley@chromium.org</owner>
   <owner>wiefangsun@chromium.org</owner>
   <summary>
@@ -36854,7 +36854,7 @@
 </histogram>
 
 <histogram name="Cryptohome.FreeDiskSpaceTotalFreedInMb" units="MiB"
-    expires_after="M87">
+    expires_after="M90">
   <owner>vsavu@chromium.org</owner>
   <owner>weifangsun@chromium.org</owner>
   <summary>
@@ -37053,7 +37053,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeBetweenFreeDiskSpace" units="s"
-    expires_after="M87">
+    expires_after="M90">
   <owner>vsavu@chromium.org</owner>
   <owner>slangley@chromium.org</owner>
   <owner>weifangsun@chromium.org</owner>
@@ -74837,7 +74837,7 @@
   </summary>
 </histogram>
 
-<histogram name="IOS.MetricKit.ForegroundTimePerDay" units="ms"
+<histogram name="IOS.MetricKit.ForegroundTimePerDay" units="s"
     expires_after="2021-01-06">
   <owner>eugenebut@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
@@ -166028,7 +166028,7 @@
 </histogram>
 
 <histogram name="Sharing.SharingHubAndroid.ThirdPartyAppUsage"
-    enum="SharingHubBottomRowIndex" expires_after="M87">
+    enum="SharingHubBottomRowIndex" expires_after="M89">
   <owner>kmilka@chromium.org</owner>
   <owner>src/components/send_tab_to_self/OWNERS</owner>
   <summary>
@@ -166038,7 +166038,7 @@
 </histogram>
 
 <histogram name="Sharing.SharingHubAndroid.TimeToShare" units="ms"
-    expires_after="M87">
+    expires_after="M89">
   <owner>kmilka@chromium.org</owner>
   <owner>src/components/send_tab_to_self/OWNERS</owner>
   <summary>
@@ -166048,7 +166048,7 @@
 </histogram>
 
 <histogram name="Sharing.SharingHubAndroid.TimeToShowShareSheet" units="ms"
-    expires_after="M87">
+    expires_after="M89">
   <owner>kmilka@chromium.org</owner>
   <owner>src/components/send_tab_to_self/OWNERS</owner>
   <summary>
@@ -166184,6 +166184,10 @@
 
 <histogram name="ShortcutsProvider.QueryIndexTime" units="ms"
     expires_after="2020-09-19">
+  <obsolete>
+    Removed 09/11/2020 as it has been unused for a while with no expectation to
+    be used in the future.
+  </obsolete>
   <owner>manukh@chromium.org</owner>
   <owner>chrome-omnibox-team@google.com</owner>
   <summary>
@@ -218113,7 +218117,12 @@
   <affected-histogram
       name="Graphics.Smoothness.PercentDroppedFrames.ScrollingThread"/>
   <affected-histogram
-      name="Graphics.Smoothness.PercentDroppedFrames.SlowerThread"/>
+      name="Graphics.Smoothness.PercentDroppedFrames.SlowerThread">
+    <obsolete>
+      Removed on 9/2020. No longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
+  </affected-histogram>
   <affected-histogram name="SingleThreadedCompositorLatency"/>
   <affected-histogram name="SingleThreadedCompositorLatency.MissedFrame"/>
   <affected-histogram
@@ -218138,7 +218147,12 @@
   <affected-histogram
       name="Graphics.Smoothness.PercentDroppedFrames.ScrollingThread"/>
   <affected-histogram
-      name="Graphics.Smoothness.PercentDroppedFrames.SlowerThread"/>
+      name="Graphics.Smoothness.PercentDroppedFrames.SlowerThread">
+    <obsolete>
+      Removed on 9/2020. No longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
+  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="SmoothnessThreadTypes" separator=".">
@@ -218148,7 +218162,12 @@
   <suffix name="ScrollingThread"
       label="The throughput of the thread responsible for handling the thread"/>
   <suffix name="SlowerThread"
-      label="The worse throughput of the main and the compositor thread"/>
+      label="The worse throughput of the main and the compositor thread">
+    <obsolete>
+      Removed on 9/2020. No longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
+  </suffix>
   <affected-histogram name="Graphics.Smoothness.PercentDroppedFrames"/>
 </histogram_suffixes>
 
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 1d2c174..9ae08af 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -4795,6 +4795,10 @@
     </summary>
   </metric>
   <metric name="SlowerThread.CompositorAnimation">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during a
       compositor animation.
@@ -4809,6 +4813,10 @@
     </aggregation>
   </metric>
   <metric name="SlowerThread.MainThreadAnimation">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during a main
       thread animation.
@@ -4837,6 +4845,10 @@
     </aggregation>
   </metric>
   <metric name="SlowerThread.RAF">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during rAF
       callback driven animation.
@@ -4851,6 +4863,10 @@
     </aggregation>
   </metric>
   <metric name="SlowerThread.ScrollbarScroll">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during
       scrollbar driven interactions.
@@ -4865,6 +4881,10 @@
     </aggregation>
   </metric>
   <metric name="SlowerThread.TouchScroll">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during
       touchscroll driven interactions.
@@ -4896,6 +4916,10 @@
     </aggregation>
   </metric>
   <metric name="SlowerThread.Video">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during video
       play.
@@ -4910,6 +4934,10 @@
     </aggregation>
   </metric>
   <metric name="SlowerThread.WheelScroll">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during
       mousewheel scroll driven interactions.
@@ -5020,48 +5048,80 @@
     </summary>
   </metric>
   <metric name="SlowerThread.CompositorAnimation">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during a
       compositor animation.
     </summary>
   </metric>
   <metric name="SlowerThread.MainThreadAnimation">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during a main
       thread animation.
     </summary>
   </metric>
   <metric name="SlowerThread.PinchZoom">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during
       pinch-zoom interactions.
     </summary>
   </metric>
   <metric name="SlowerThread.RAF">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during rAF
       callback driven animation.
     </summary>
   </metric>
   <metric name="SlowerThread.TouchScroll">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during
       touchscroll driven interactions.
     </summary>
   </metric>
   <metric name="SlowerThread.Universal">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread for all frame
       production.
     </summary>
   </metric>
   <metric name="SlowerThread.Video">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during video
       play.
     </summary>
   </metric>
   <metric name="SlowerThread.WheelScroll">
+    <obsolete>
+      Removed in 9/2020, since no longer needed after 'Universal' metric was
+      deprecated.
+    </obsolete>
     <summary>
       The worse throughput of the main and the compositor thread during
       mousewheel scroll driven interactions.
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 02e1816..df52d62 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -33,6 +33,8 @@
 crbug.com/882881 [ android-nexus-5x ] blink_perf.bindings/worker-structured-clone-json-from-worker.html [ Skip ]
 crbug.com/865400 [ android-pixel-2 ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
 crbug.com/865400 [ android-pixel-2 ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
+crbug.com/1128019 [ android-pixel-2 ] blink_perf.bindings/worker-structured-clone-different-payloads.html [ Skip ]
+crbug.com/1128019 [ android-nexus-5x ] blink_perf.bindings/worker-structured-clone-different-payloads.html [ Skip ]
 
 # Benchmark: blink_perf.css
 crbug.com/891878 [ android-nexus-5x android-webview ] blink_perf.css/CustomPropertiesVarAlias.html [ Skip ]
@@ -54,6 +56,14 @@
 crbug.com/966921 [ android ] blink_perf.layout/line-layout-fit-content.html [ Skip ]
 crbug.com/966921 [ android ] blink_perf.layout/line-layout-fit-content-break-word.html [ Skip ]
 
+# Benchmark: blink_perf.owp_storage
+crbug.com/1128019 [ android-pixel-2 ] blink_perf.owp_storage/idb-load-docs.html [ Skip ]
+crbug.com/1128019 [ android-pixel-2 ] blink_perf.owp_storage/idb-put-all.html [ Skip ]
+crbug.com/1128019 [ android-pixel-2 ] blink_perf.owp_storage/idb-put.html [ Skip ]
+crbug.com/1128019 [ android-nexus-5x ] blink_perf.owp_storage/idb-load-docs.html [ Skip ]
+crbug.com/1128019 [ android-nexus-5x ] blink_perf.owp_storage/idb-put-all.html [ Skip ]
+crbug.com/1128019 [ android-nexus-5x ] blink_perf.owp_storage/idb-put.html [ Skip ]
+
 # Benchmark: blink_perf.paint
 crbug.com/574483 [ android-low-end ] blink_perf.paint/* [ Skip ]
 crbug.com/799540 [ android-nexus-5 ] blink_perf.paint/* [ Skip ]
@@ -98,6 +108,7 @@
 # Benchmark: jetstream
 crbug.com/830600 [ android-nexus-5x android-webview ] jetstream/JetStream [ Skip ]
 crbug.com/1009843 [ android ] jetstream/JetStream [ Skip ]
+crbug.com/1128019 [ android-pixel-2 ] jetstream2/JetStream2 [ Skip ]
 
 # Benchmark: loading.desktop
 crbug.com/723783 [ win ] loading.desktop/Orange_cold [ Skip ]
@@ -304,6 +315,8 @@
 crbug.com/1098225 [ android-go android-webview ] system_health.common_mobile/browse:news:globo:2019 [ Skip ]
 crbug.com/1112337 [ android ] system_health.common_mobile/browse:news:cricbuzz:2019 [ Skip ]
 crbug.com/1113921 [ android-go ] system_health.common_mobile/load:tools:dropbox:2019 [ Skip ]
+crbug.com/1128019 [ android-pixel-2 ] system_health.common_mobile/load:tools:dropbox:2019 [ Skip ]
+crbug.com/1128019 [ android-nexus-5x ] system_health.common_mobile/load:tools:dropbox:2019 [ Skip ]
 
 # Benchmark: system_health.memory_desktop
 crbug.com/984599 [ linux ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
@@ -365,6 +378,7 @@
 crbug.com/1097719 [ android-nexus-5x android-webview ] system_health.memory_mobile/browse:social:instagram:2019 [ Skip ]
 crbug.com/1112337 [ android ] system_health.memory_mobile/browse:news:cricbuzz:2019 [ Skip ]
 crbug.com/1113921 [ android-go ] system_health.memory_mobile/load:tools:dropbox:2019 [ Skip ]
+crbug.com/1128019 [ android-pixel-2 ] system_health.memory_mobile/load:tools:dropbox:2019 [ Skip ]
 
 # Benchmark: tab_switching.typical_25
 crbug.com/747026 [ mac ] tab_switching.typical_25/multitab:misc:typical24 [ Skip ]
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index 26aa1dc..643b8d2 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -91,6 +91,8 @@
     const std::vector<AXEventIntent>& event_intents)
     : event(event), event_from(event_from), event_intents(event_intents) {}
 
+AXEventGenerator::EventParams::EventParams(const EventParams& other) = default;
+
 AXEventGenerator::EventParams::~EventParams() = default;
 
 AXEventGenerator::TargetedEvent::TargetedEvent(AXNode* node,
@@ -130,11 +132,17 @@
   if (map_iter_ == map_.end())
     return *this;
 
+  DCHECK(set_iter_ != map_iter_->second.end())
+      << "The set of events should not be empty";
   set_iter_++;
-  while (map_iter_ != map_.end() && set_iter_ == map_iter_->second.end()) {
+
+  if (set_iter_ == map_iter_->second.end()) {
     map_iter_++;
-    if (map_iter_ != map_.end())
+    if (map_iter_ != map_.end()) {
       set_iter_ = map_iter_->second.begin();
+      DCHECK(set_iter_ != map_iter_->second.end())
+          << "The set of events should not be empty";
+    }
   }
 
   return *this;
@@ -809,10 +817,11 @@
 void AXEventGenerator::PostprocessEvents() {
   std::map<AXNode*, IgnoredChangedStatesBitset> ancestor_ignored_changed_map;
   std::set<AXNode*> removed_subtree_created_nodes;
-  auto iter = tree_events_.begin();
-  while (iter != tree_events_.end()) {
-    AXNode* node = iter->first;
-    std::set<EventParams>& node_events = iter->second;
+
+  // First pass through |tree_events_|, remove events that we do not need.
+  for (auto& iter : tree_events_) {
+    AXNode* node = iter.first;
+    std::set<EventParams>& node_events = iter.second;
 
     // A newly created live region or alert should not *also* fire a
     // live region changed event.
@@ -864,10 +873,14 @@
         parent = parent->GetUnignoredParent();
       }
     }
+  }
 
-    // If this was the only event, remove the node entirely from the
-    // tree events.
-    if (node_events.size() == 0)
+  // Second pass through |tree_events_|, remove nodes that do not have any
+  // events left.
+  auto iter = tree_events_.begin();
+  while (iter != tree_events_.end()) {
+    std::set<EventParams>& node_events = iter->second;
+    if (node_events.empty())
       iter = tree_events_.erase(iter);
     else
       ++iter;
diff --git a/ui/accessibility/ax_event_generator.h b/ui/accessibility/ax_event_generator.h
index 85f3209c..bfaf0ee 100644
--- a/ui/accessibility/ax_event_generator.h
+++ b/ui/accessibility/ax_event_generator.h
@@ -106,10 +106,11 @@
   // IGNORED_CHANGED event.
   enum class IgnoredChangedState : uint8_t { kShow, kHide, kCount = 2 };
 
-  struct EventParams {
+  struct AX_EXPORT EventParams {
     EventParams(Event event,
                 ax::mojom::EventFrom event_from,
                 const std::vector<AXEventIntent>& event_intents);
+    EventParams(const EventParams& other);
     ~EventParams();
     Event event;
     ax::mojom::EventFrom event_from;
diff --git a/ui/base/ime/win/input_method_win_tsf.cc b/ui/base/ime/win/input_method_win_tsf.cc
index aca6422..bf0422c 100644
--- a/ui/base/ime/win/input_method_win_tsf.cc
+++ b/ui/base/ime/win/input_method_win_tsf.cc
@@ -142,7 +142,7 @@
 void InputMethodWinTSF::OnWillChangeFocusedClient(
     TextInputClient* focused_before,
     TextInputClient* focused) {
-  if (IsWindowFocused(focused_before)) {
+  if (ui::TSFBridge::GetInstance() && IsWindowFocused(focused_before)) {
     ConfirmCompositionText();
     ui::TSFBridge::GetInstance()->RemoveFocusedClient(focused_before);
   }
diff --git a/ui/gfx/x/x11.h b/ui/gfx/x/x11.h
index 6b36d48..d2dab403 100644
--- a/ui/gfx/x/x11.h
+++ b/ui/gfx/x/x11.h
@@ -139,7 +139,7 @@
 
 enum XEventQueueOwner { XlibOwnsEventQueue = 0, XCBOwnsEventQueue };
 
-using XErrorEvent = struct {
+using XErrorEvent = struct _XErrorEvent {
   int type;
   Display* display;
   XID resourceid;
@@ -149,7 +149,7 @@
   unsigned char minor_code;
 };
 
-using XRectangle = struct {
+using XRectangle = struct _XRectangle {
   short x, y;
   unsigned short width, height;
 };
@@ -161,7 +161,7 @@
   XPointer private_data;
 };
 
-using Visual = struct {
+using Visual = struct _Visual {
   XExtData* ext_data;
   VisualID visualid;
   int c_class;
@@ -170,7 +170,7 @@
   int map_entries;
 };
 
-using XVisualInfo = struct {
+using XVisualInfo = struct _XVisualInfo {
   Visual* visual;
   VisualID visualid;
   int screen;
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index c0c37674..977cefd 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -194,6 +194,10 @@
       return gfx::kGoogleBlue800;
 
     // Tooltip
+    case NativeTheme::kColorId_TooltipIcon:
+      return SkColorSetA(gfx::kGoogleGrey200, 0xBD);
+    case NativeTheme::kColorId_TooltipIconHovered:
+      return SK_ColorWHITE;
     case NativeTheme::kColorId_TooltipText:
       return SkColorSetA(gfx::kGoogleGrey200, 0xDE);
 
@@ -505,9 +509,9 @@
       return SkColorSetA(bg, 0xCC);
     }
     case NativeTheme::kColorId_TooltipIcon:
-      return SkColorSetARGB(0xBD, 0x44, 0x44, 0x44);
+      return SkColorSetA(gfx::kGoogleGrey800, 0xBD);
     case NativeTheme::kColorId_TooltipIconHovered:
-      return SkColorSetARGB(0xBD, 0, 0, 0);
+      return SkColorSetA(SK_ColorBLACK, 0xBD);
     case NativeTheme::kColorId_TooltipText:
       return SkColorSetA(kPrimaryTextColor, 0xDE);
 
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index 91c5532..d6fa761f 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -623,6 +623,7 @@
     case kColorId_TableHeaderText:
     case kColorId_TableGroupingIndicatorColor:
     case kColorId_TableHeaderSeparator:
+    case kColorId_TooltipIcon:
     case kColorId_TooltipText:
     case kColorId_ThrobberSpinningColor:
     case kColorId_ThrobberLightColor:
@@ -678,6 +679,7 @@
     case kColorId_FocusedMenuItemBackgroundColor:
     case kColorId_LabelTextSelectionBackgroundFocused:
     case kColorId_TextfieldSelectionBackgroundFocused:
+    case kColorId_TooltipIconHovered:
     case kColorId_TreeSelectionBackgroundFocused:
     case kColorId_TreeSelectionBackgroundUnfocused:
     case kColorId_TableSelectionBackgroundFocused:
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
index 5e5df13..3d2fcb72 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
@@ -87,12 +87,11 @@
       return;
 
     widgets_.push_back(widget);
-    observer_.Add(widget);
+    widget_observer_.Add(widget);
 
     aura::Window* window = widget->GetNativeWindow();
-    if (!window)
-      return;
-    window->AddObserver(this);
+    if (window)
+      window_observer_.Add(window);
   }
 
   gfx::NativeViewAccessible GetNativeViewAccessible() override {
@@ -104,7 +103,12 @@
   // WidgetObserver:
 
   void OnWidgetDestroying(Widget* widget) override {
-    observer_.Remove(widget);
+    widget_observer_.Remove(widget);
+
+    aura::Window* window = widget->GetNativeWindow();
+    if (window && window_observer_.IsObserving(window))
+      window_observer_.Remove(window);
+
     auto iter = std::find(widgets_.begin(), widgets_.end(), widget);
     if (iter != widgets_.end())
       widgets_.erase(iter);
@@ -163,7 +167,8 @@
   ui::AXNodeData data_;
   ui::AXUniqueId unique_id_;
   std::vector<Widget*> widgets_;
-  ScopedObserver<views::Widget, views::WidgetObserver> observer_{this};
+  ScopedObserver<views::Widget, views::WidgetObserver> widget_observer_{this};
+  ScopedObserver<aura::Window, aura::WindowObserver> window_observer_{this};
 };
 
 }  // namespace
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 57bad33..61be169 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -381,6 +381,7 @@
 }
 
 void DialogDelegate::AcceptDialog() {
+  DCHECK(IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
   if (already_started_close_ || !Accept())
     return;
 
@@ -390,6 +391,9 @@
 }
 
 void DialogDelegate::CancelDialog() {
+  // Note: don't DCHECK(IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL)) here;
+  // CancelDialog() is *always* reachable via Esc closing the dialog, even if
+  // the cancel button is disabled or there is no cancel button at all.
   if (already_started_close_ || !Cancel())
     return;
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/BUILD.gn b/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
index 3e1dec3..1952014 100644
--- a/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
@@ -273,7 +273,7 @@
     ":network_password_input.m",
     ":network_property_list_mojo.m",
     #  ":network_proxy.m",
-    #  ":network_proxy_exclusions.m",
+    ":network_proxy_exclusions.m",
     ":network_proxy_input.m",
     ":network_select.m",
     ":network_shared_css.m",
@@ -519,7 +519,11 @@
 js_library("network_proxy_exclusions.m") {
   sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":network_shared_css.m",
+    "//third_party/polymer/v3_0/components-chromium/iron-flex-layout:iron-flex-layout-classes",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
   extra_deps = [ ":network_proxy_exclusions_module" ]
 }
@@ -732,6 +736,8 @@
   js_file = "network_proxy_exclusions.js"
   html_file = "network_proxy_exclusions.html"
   html_type = "dom-module"
+  auto_imports = cr_components_chromeos_auto_imports
+  namespace_rewrites = cr_components_chromeos_namespace_rewrites
 }
 
 polymer_modulizer("network_proxy_input") {
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html b/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html
index c9f00cb..bef05a5 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html
@@ -1,9 +1,9 @@
 <link rel="import" href="../../../html/polymer.html">
 
-<link rel="import" href="../../../cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="../../../cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="../../../html/i18n_behavior.html">
 <link rel="import" href="network_shared_css.html">
 
 <dom-module id="network-proxy-exclusions">
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js b/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js
index e4d7b6109..d5296ed 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js
@@ -7,8 +7,6 @@
  * Includes UI for adding, changing, and removing entries.
  */
 
-(function() {
-
 Polymer({
   is: 'network-proxy-exclusions',
 
@@ -45,4 +43,3 @@
     this.fire('proxy-exclusions-change');
   }
 });
-})();
diff --git a/ui/webui/resources/cr_components/cr_components_resources_v3.grdp b/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
index 5455dc0..7017c3d 100644
--- a/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
+++ b/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
@@ -108,6 +108,10 @@
            file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.m.js"
            use_base_dir="false"
            type="BINDATA" />
+    <include name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_EXCLUSIONS_M_JS"
+           file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.m.js"
+           use_base_dir="false"
+           type="BINDATA" />
     <include name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_INPUT_M_JS"
            file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.m.js"
            use_base_dir="false"
diff --git a/url/BUILD.gn b/url/BUILD.gn
index 06c29f1..b14a1dc 100644
--- a/url/BUILD.gn
+++ b/url/BUILD.gn
@@ -222,15 +222,6 @@
       "//url/mojom:test_url_mojom_gurl",
     ]
   }
-  if (is_android) {
-    sources += [ "android/gurl_android_unittest.cc" ]
-    deps += [
-      ":gurl_android",
-      ":gurl_java",
-      ":gurl_javatests",
-      ":native_j_unittests_jni_headers",
-    ]
-  }
 }
 
 test("url_perftests") {
@@ -258,17 +249,42 @@
 }
 
 if (is_android) {
-  android_library("gurl_javatests") {
+  source_set("gurl_android_test_helper") {
     testonly = true
-    sources = [
-      "android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java",
+    sources = [ "android/gurl_java_test_helper.cc" ]
+    deps = [
+      ":gurl_android",
+      ":gurl_j_test_jni_headers",
+      ":url",
+      "//base/test:test_support",
+      "//testing/gtest",
     ]
+  }
+
+  android_library("gurl_android_test_helper_java") {
+    testonly = true
+    sources =
+        [ "android/javatests/src/org/chromium/url/GURLJavaTestHelper.java" ]
     deps = [
       ":gurl_java",
-      ":gurl_jni_headers",
       "//base:base_java",
       "//base:base_java_test_support",
       "//base:jni_java",
+    ]
+  }
+
+  android_library("gurl_javatests") {
+    testonly = true
+    sources = [ "android/javatests/src/org/chromium/url/GURLJavaTest.java" ]
+    deps = [
+      ":gurl_android_test_helper_java",
+      ":gurl_java",
+      "//base:base_java",
+      "//base:base_java_test_support",
+      "//base:jni_java",
+      "//content/public/test/android:content_java_test_support",
+      "//third_party/android_deps:androidx_core_core_java",
+      "//third_party/android_deps:androidx_test_runner_java",
       "//third_party/android_support_test_runner:rules_java",
       "//third_party/android_support_test_runner:runner_java",
       "//third_party/junit",
@@ -279,10 +295,9 @@
 
   # See https://bugs.chromium.org/p/chromium/issues/detail?id=908819 for why we
   # can't put 'java' in the name here.
-  generate_jni("native_j_unittests_jni_headers") {
+  generate_jni("gurl_j_test_jni_headers") {
     testonly = true
-    sources = [
-      "android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java",
-    ]
+    sources =
+        [ "android/javatests/src/org/chromium/url/GURLJavaTestHelper.java" ]
   }
 }
diff --git a/url/android/gurl_android_unittest.cc b/url/android/gurl_java_test_helper.cc
similarity index 76%
rename from url/android/gurl_android_unittest.cc
rename to url/android/gurl_java_test_helper.cc
index fe2b708..ba6409b 100644
--- a/url/android/gurl_android_unittest.cc
+++ b/url/android/gurl_java_test_helper.cc
@@ -6,29 +6,21 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
+#include "base/test/icu_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/android/gurl_android.h"
 #include "url/gurl.h"
-#include "url/native_j_unittests_jni_headers/GURLJavaTest_jni.h"
+#include "url/gurl_j_test_jni_headers/GURLJavaTestHelper_jni.h"
 
 using base::android::AttachCurrentThread;
 
 namespace url {
 
-class GURLAndroidTest : public ::testing::Test {
- public:
-  GURLAndroidTest()
-      : j_test_(Java_GURLJavaTest_Constructor(AttachCurrentThread())) {}
+static void JNI_GURLJavaTestHelper_InitializeICU(JNIEnv* env) {
+  base::test::InitializeICUForTesting();
+}
 
-  const base::android::ScopedJavaGlobalRef<jobject>& j_test() {
-    return j_test_;
-  }
-
- private:
-  base::android::ScopedJavaGlobalRef<jobject> j_test_;
-};
-
-TEST_F(GURLAndroidTest, TestGURLEquivalence) {
+static void JNI_GURLJavaTestHelper_TestGURLEquivalence(JNIEnv* env) {
   const char* cases[] = {
       // Common Standard URLs.
       "https://www.google.com",
@@ -62,17 +54,14 @@
       // Invalid URLs.
       "foobar",
   };
-  JNIEnv* env = AttachCurrentThread();
   for (const char* uri : cases) {
     GURL gurl(uri);
     base::android::ScopedJavaLocalRef<jobject> j_gurl =
-        Java_GURLJavaTest_createGURL(
-            env, j_test(), base::android::ConvertUTF8ToJavaString(env, uri));
+        Java_GURLJavaTestHelper_createGURL(
+            env, base::android::ConvertUTF8ToJavaString(env, uri));
     std::unique_ptr<GURL> gurl2 = GURLAndroid::ToNativeGURL(env, j_gurl);
     EXPECT_EQ(gurl, *gurl2);
   }
 }
 
-JAVA_TESTS(GURLAndroidTest, j_test())
-
 }  // namespace url
diff --git a/url/android/javatests/DEPS b/url/android/javatests/DEPS
new file mode 100644
index 0000000..aa93591
--- /dev/null
+++ b/url/android/javatests/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+content/public/test/android",
+]
diff --git a/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java b/url/android/javatests/src/org/chromium/url/GURLJavaTest.java
similarity index 91%
rename from url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java
rename to url/android/javatests/src/org/chromium/url/GURLJavaTest.java
index 0517ddf..d29b1ac 100644
--- a/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java
+++ b/url/android/javatests/src/org/chromium/url/GURLJavaTest.java
@@ -7,12 +7,18 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doThrow;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.CalledByNativeJavaTest;
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.util.Batch;
+import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 
 import java.net.URISyntaxException;
 
@@ -21,18 +27,18 @@
  * the logic is tested there. This test is primarily to make sure everything is plumbed through
  * correctly.
  */
+@RunWith(BaseJUnit4ClassRunner.class)
+@Batch(Batch.UNIT_TESTS)
 public class GURLJavaTest {
     @Mock
     GURL.Natives mGURLMocks;
 
-    @CalledByNative
-    private GURLJavaTest() {
+    @Before
+    public void setUp() {
         MockitoAnnotations.initMocks(this);
-    }
 
-    @CalledByNative
-    public GURL createGURL(String uri) {
-        return new GURL(uri);
+        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
+        GURLJavaTestHelper.nativeInitializeICU();
     }
 
     private void deepAssertEquals(GURL expected, GURL actual) {
@@ -51,8 +57,15 @@
         return Integer.toString(serialization.length()) + GURL.SERIALIZER_DELIMITER + serialization;
     }
 
+    @SmallTest
+    @Test
+    public void testGURLEquivalence() {
+        GURLJavaTestHelper.nativeTestGURLEquivalence();
+    }
+
     // Equivalent of GURLTest.Components
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     @SuppressWarnings(value = "AuthLeak")
     public void testComponents() {
         GURL empty = new GURL("");
@@ -85,7 +98,8 @@
     }
 
     // Equivalent of GURLTest.Empty
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     public void testEmpty() {
         GURLJni.TEST_HOOKS.setInstanceForTesting(mGURLMocks);
         doThrow(new RuntimeException("Should not need to parse empty URL"))
@@ -107,7 +121,8 @@
     }
 
     // Test that GURL and URI return the correct Origin.
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     @SuppressWarnings(value = "AuthLeak")
     public void testOrigin() throws URISyntaxException {
         final String kExpectedOrigin1 = "http://google.com:21/";
@@ -122,12 +137,13 @@
         Assert.assertEquals(kExpectedOrigin1, origin.getSpec());
     }
 
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     public void testWideInput() throws URISyntaxException {
         final String kExpectedSpec = "http://xn--1xa.com/";
 
         GURL url = new GURL("http://\u03C0.com");
-        Assert.assertEquals("http://xn--1xa.com/", url.getSpec());
+        Assert.assertEquals(kExpectedSpec, url.getSpec());
         Assert.assertEquals("http", url.getScheme());
         Assert.assertEquals("", url.getUsername());
         Assert.assertEquals("", url.getPassword());
@@ -138,7 +154,8 @@
         Assert.assertEquals("", url.getRef());
     }
 
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     @SuppressWarnings(value = "AuthLeak")
     public void testSerialization() {
         GURL cases[] = {
@@ -194,7 +211,8 @@
      * Tests that we re-parse the URL from the spec, which must always be the last token in the
      * serialization, if the serialization version differs.
      */
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     public void testSerializationWithVersionSkew() {
         GURL url = new GURL("https://www.google.com");
         String serialization = (GURL.SERIALIZER_VERSION + 1)
@@ -208,7 +226,8 @@
     /**
      * Tests that fields that aren't visible to java code are correctly serialized.
      */
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     public void testSerializationOfPrivateFields() {
         String serialization = GURL.SERIALIZER_VERSION
                 + ",true,"
@@ -226,7 +245,8 @@
     /**
      * Tests serialized GURL truncated by storage.
      */
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     public void testTruncatedDeserialization() {
         String serialization = "123,1,true,1,2,3,4,5,6,7,8,9,10";
         serialization = serialization.replace(',', GURL.SERIALIZER_DELIMITER);
@@ -237,7 +257,8 @@
     /**
      * Tests serialized GURL truncated by storage.
      */
-    @CalledByNativeJavaTest
+    @SmallTest
+    @Test
     public void testCorruptedSerializations() {
         String serialization = new GURL("https://www.google.ca").serialize();
         // Replace the scheme length (5) with an extra delimiter.
diff --git a/url/android/javatests/src/org/chromium/url/GURLJavaTestHelper.java b/url/android/javatests/src/org/chromium/url/GURLJavaTestHelper.java
new file mode 100644
index 0000000..3bd91a3
--- /dev/null
+++ b/url/android/javatests/src/org/chromium/url/GURLJavaTestHelper.java
@@ -0,0 +1,22 @@
+// 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.
+
+package org.chromium.url;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Helpers for GURLJavaTest that need to call into native code.
+ */
+@JNINamespace("url")
+public class GURLJavaTestHelper {
+    @CalledByNative
+    public static GURL createGURL(String uri) {
+        return new GURL(uri);
+    }
+
+    public static native void nativeInitializeICU();
+    public static native void nativeTestGURLEquivalence();
+}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
index a276fd9d..fb5334a1 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
@@ -11,10 +11,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.weblayer.Browser;
 import org.chromium.weblayer.Tab;
 import org.chromium.weblayer.TabListCallback;
+import org.chromium.weblayer.WebLayer;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
 import java.util.ArrayList;
@@ -137,7 +139,7 @@
 
     @Test
     @SmallTest
-    public void testDispose() {
+    public void testDestroyTab() {
         initialize("new_browser.html");
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -146,6 +148,7 @@
             browser.registerTabListCallback(callback);
             browser.destroyTab(mActivity.getBrowser().getActiveTab());
             Assert.assertTrue(callback.getObservedValues().contains(TabListCallbackImpl.ACTIVE));
+            Assert.assertTrue(callback.getObservedValues().contains(TabListCallbackImpl.REMOVED));
             Assert.assertEquals(1, browser.getTabs().size());
         });
     }
@@ -166,4 +169,28 @@
         EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView());
         closeTabCallback.waitForCloseTab();
     }
+
+    @Test
+    @SmallTest
+    public void testOnTabRemoved() throws Exception {
+        mActivity = mActivityTestRule.launchShellWithUrl("about:blank");
+        CallbackHelper callbackHelper = new CallbackHelper();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Browser browser = mActivity.getBrowser();
+            browser.registerTabListCallback(new TabListCallback() {
+                @Override
+                public void onTabRemoved(Tab tab) {
+                    if (WebLayer.getSupportedMajorVersion(mActivity) >= 87) {
+                        // |tab| should not be destroyed at this point. getGuid() is a good proxy
+                        // for verifying the tab hasn't been destroyed. Prior to 87 the tab was
+                        // destroyed at this point.
+                        tab.getGuid();
+                    }
+                    callbackHelper.notifyCalled();
+                }
+            });
+            mActivity.getBrowser().destroyTab(mActivity.getBrowser().createTab());
+        });
+        callbackHelper.waitForFirst();
+    }
 }
diff --git a/weblayer/browser/browser_impl.cc b/weblayer/browser/browser_impl.cc
index 40f1a92..4bf9dca0 100644
--- a/weblayer/browser/browser_impl.cc
+++ b/weblayer/browser/browser_impl.cc
@@ -145,12 +145,6 @@
   AddTab(reinterpret_cast<TabImpl*>(native_tab));
 }
 
-void BrowserImpl::RemoveTab(JNIEnv* env,
-                            long native_tab) {
-  // The Java side owns the Tab.
-  RemoveTab(reinterpret_cast<TabImpl*>(native_tab)).release();
-}
-
 ScopedJavaLocalRef<jobjectArray> BrowserImpl::GetTabs(JNIEnv* env) {
   ScopedJavaLocalRef<jclass> clazz =
       base::android::GetClass(env, "org/chromium/weblayer_private/TabImpl");
@@ -272,8 +266,10 @@
 }
 
 #if defined(OS_ANDROID)
-void BrowserImpl::DestroyTabFromJava(Tab* tab) {
-  RemoveTab(tab);
+void BrowserImpl::RemoveTabBeforeDestroyingFromJava(Tab* tab) {
+  // The java side owns the Tab, and is going to delete it shortly. See
+  // JNI_TabImpl_DeleteTab.
+  RemoveTab(tab).release();
 }
 #endif
 
@@ -290,6 +286,7 @@
 
 void BrowserImpl::DestroyTab(Tab* tab) {
 #if defined(OS_ANDROID)
+  // Route destruction through the java side.
   Java_BrowserImpl_destroyTabImpl(AttachCurrentThread(), java_impl_,
                                   static_cast<TabImpl*>(tab)->GetJavaTab());
 #else
diff --git a/weblayer/browser/browser_impl.h b/weblayer/browser/browser_impl.h
index 79f92f6..00b4b67 100644
--- a/weblayer/browser/browser_impl.h
+++ b/weblayer/browser/browser_impl.h
@@ -62,8 +62,6 @@
 
   void AddTab(JNIEnv* env,
               long native_tab);
-  void RemoveTab(JNIEnv* env,
-                 long native_tab);
   base::android::ScopedJavaLocalRef<jobjectArray> GetTabs(JNIEnv* env);
   void SetActiveTab(JNIEnv* env,
                     long native_tab);
@@ -105,9 +103,9 @@
 #if defined(OS_ANDROID)
   // On Android the Java Tab class owns the C++ Tab. DestroyTab() calls to the
   // Java Tab class to initiate deletion. This function is called from the Java
-  // side, and must not call DestroyTab(), otherwise we get stuck in infinite
-  // recursion.
-  void DestroyTabFromJava(Tab* tab);
+  // side to remove the tab from the browser and shortly followed by deleting
+  // the tab.
+  void RemoveTabBeforeDestroyingFromJava(Tab* tab);
 #endif
 
   // Browser:
diff --git a/weblayer/browser/content_view_render_view.cc b/weblayer/browser/content_view_render_view.cc
index 67b039c1..1c97f433 100644
--- a/weblayer/browser/content_view_render_view.cc
+++ b/weblayer/browser/content_view_render_view.cc
@@ -144,6 +144,13 @@
   return compositor_->GetResourceManager().GetJavaObject();
 }
 
+void ContentViewRenderView::UpdateBackgroundColor(JNIEnv* env) {
+  if (!compositor_)
+    return;
+  compositor_->SetBackgroundColor(
+      Java_ContentViewRenderView_getBackgroundColor(env, java_obj_));
+}
+
 void ContentViewRenderView::UpdateLayerTreeHost() {
   // TODO(wkorman): Rename Layout to UpdateLayerTreeHost in all Android
   // Compositor related classes.
@@ -179,6 +186,7 @@
       cc::ElementId(root_container_layer_->id()));
   root_container_layer_->SetIsDrawable(false);
   compositor_->SetRootLayer(root_container_layer_);
+  UpdateBackgroundColor(base::android::AttachCurrentThread());
 }
 
 }  // namespace weblayer
diff --git a/weblayer/browser/content_view_render_view.h b/weblayer/browser/content_view_render_view.h
index ddf177c..0cf93c0 100644
--- a/weblayer/browser/content_view_render_view.h
+++ b/weblayer/browser/content_view_render_view.h
@@ -62,6 +62,7 @@
   void SetNeedsRedraw(JNIEnv* env);
   void EvictCachedSurface(JNIEnv* env);
   base::android::ScopedJavaLocalRef<jobject> GetResourceManager(JNIEnv* env);
+  void UpdateBackgroundColor(JNIEnv* env);
 
   // CompositorClient implementation
   void UpdateLayerTreeHost() override;
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index 5968860..779d974 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -30,6 +30,7 @@
   deps = [
     ":weblayer_strings_grd",
     "//components/blocked_content/android:java_resources",
+    "//components/browser_ui/contacts_picker/android:java_resources",
     "//components/browser_ui/http_auth/android:java_resources",
     "//components/browser_ui/media/android:java_resources",
     "//components/browser_ui/settings/android:java_resources",
@@ -95,6 +96,7 @@
     "org/chromium/weblayer_private/BrowserImpl.java",
     "org/chromium/weblayer_private/BrowserViewController.java",
     "org/chromium/weblayer_private/ChildProcessServiceImpl.java",
+    "org/chromium/weblayer_private/ContactsPickerAdapter.java",
     "org/chromium/weblayer_private/ContentViewRenderView.java",
     "org/chromium/weblayer_private/CookieManagerImpl.java",
     "org/chromium/weblayer_private/CrashReporterControllerImpl.java",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
index 9ccb13e..46d08a3 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
@@ -12,6 +12,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.components.browser_ui.styles.R;
 import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory;
 import org.chromium.weblayer_private.interfaces.BrowserFragmentArgs;
@@ -144,6 +146,11 @@
         mBrowser.onFragmentPause();
     }
 
+    @Nullable
+    public BrowserImpl getBrowser() {
+        return mBrowser;
+    }
+
     public IBrowserFragment asIBrowserFragment() {
         return new IBrowserFragment.Stub() {
             @Override
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
index 924388c49..527337a 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
@@ -84,6 +84,17 @@
     };
 
     /**
+     * @param windowAndroid a window that was created by a {@link BrowserFragmentImpl}. It's not
+     *         valid to call this method with other {@link WindowAndroid} instances. Typically this
+     *         should be the {@link WindowAndroid} of a {@link WebContents}.
+     * @return the associated BrowserImpl instance.
+     */
+    public static BrowserImpl fromWindowAndroid(WindowAndroid windowAndroid) {
+        assert windowAndroid instanceof FragmentWindowAndroid;
+        return ((FragmentWindowAndroid) windowAndroid).getBrowser();
+    }
+
+    /**
      * Allows observing of visible security state of the active tab.
      */
     public static interface VisibleSecurityStateObserver {
@@ -572,7 +583,6 @@
         long createBrowser(long profile, BrowserImpl caller);
         void deleteBrowser(long browser);
         void addTab(long nativeBrowserImpl, long nativeTab);
-        void removeTab(long nativeBrowserImpl, long nativeTab);
         TabImpl[] getTabs(long nativeBrowserImpl);
         void setActiveTab(long nativeBrowserImpl, long nativeTab);
         TabImpl getActiveTab(long nativeBrowserImpl);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ContactsPickerAdapter.java b/weblayer/browser/java/org/chromium/weblayer_private/ContactsPickerAdapter.java
new file mode 100644
index 0000000..173ef85
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ContactsPickerAdapter.java
@@ -0,0 +1,120 @@
+// 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.
+
+package org.chromium.weblayer_private;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.components.browser_ui.contacts_picker.ContactDetails;
+import org.chromium.components.browser_ui.contacts_picker.PickerAdapter;
+import org.chromium.components.browser_ui.util.AvatarGenerator;
+import org.chromium.ui.base.WindowAndroid;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * A {@link PickerAdapter} for WebLayer.
+ * This class synthesizes the self contact based on data provided by the client.
+ */
+public class ContactsPickerAdapter extends PickerAdapter {
+    private final WindowAndroid mWindowAndroid;
+
+    // The avatar returned by the client, or null if it hasn't been returned.
+    private Bitmap mAvatar;
+
+    // True after the self contact has been synthesized and prepended to the contact list.
+    private boolean mSelfContactSynthesized;
+
+    ContactsPickerAdapter(WindowAndroid windowAndroid) {
+        mWindowAndroid = windowAndroid;
+    }
+
+    // PickerAdapter:
+
+    @Override
+    protected String findOwnerEmail() {
+        GoogleAccountsCallbackProxy accountsCallback = getGoogleAccountsCallback();
+        if (accountsCallback == null) return null;
+
+        String email;
+        try {
+            email = accountsCallback.getGaiaId();
+        } catch (RemoteException e) {
+            throw new AndroidRuntimeException(e);
+        }
+        return TextUtils.isEmpty(email) ? null : email;
+    }
+
+    @Override
+    protected void addOwnerInfoToContacts(ArrayList<ContactDetails> contacts) {
+        // This method should only be called if there was a valid email returned in
+        // findOwnerEmail().
+        GoogleAccountsCallbackProxy accountsCallback = getGoogleAccountsCallback();
+        assert accountsCallback != null;
+
+        String name;
+        // Weak ref so that the outstanding callback doesn't hold a ref to |this|.
+        final WeakReference<ContactsPickerAdapter> weakThis =
+                new WeakReference<ContactsPickerAdapter>(this);
+        try {
+            name = accountsCallback.getFullName();
+            accountsCallback.getAvatar(getIconRawPixelSize(), (Bitmap returnedAvatar) -> {
+                if (weakThis.get() == null) return;
+
+                weakThis.get().updateOwnerInfoWithIcon(returnedAvatar);
+            });
+        } catch (RemoteException e) {
+            throw new AndroidRuntimeException(e);
+        }
+
+        if (TextUtils.isEmpty(name)) {
+            name = getOwnerEmail();
+        }
+
+        ContactDetails contact = new ContactDetails(ContactDetails.SELF_CONTACT_ID, name,
+                Collections.singletonList(getOwnerEmail()), /*phoneNumbers=*/null,
+                /*addresses=*/null);
+        contact.setIsSelf(true);
+        contact.setSelfIcon(createAvatarDrawable());
+        contacts.add(0, contact);
+
+        mSelfContactSynthesized = true;
+    }
+
+    @Nullable
+    private GoogleAccountsCallbackProxy getGoogleAccountsCallback() {
+        TabImpl tab = BrowserImpl.fromWindowAndroid(mWindowAndroid).getActiveTab();
+        if (tab == null) return null;
+        return tab.getGoogleAccountsCallbackProxy();
+    }
+
+    private void updateOwnerInfoWithIcon(Bitmap icon) {
+        mAvatar = icon;
+        if (mSelfContactSynthesized && mAvatar != null) {
+            getAllContacts().get(0).setSelfIcon(createAvatarDrawable());
+            update();
+        }
+    }
+
+    private Drawable createAvatarDrawable() {
+        if (mAvatar == null) return null;
+
+        Resources res = mWindowAndroid.getContext().get().getResources();
+        return AvatarGenerator.makeRoundAvatar(res, mAvatar, getIconRawPixelSize());
+    }
+
+    private int getIconRawPixelSize() {
+        Resources res = mWindowAndroid.getContext().get().getResources();
+        return res.getDimensionPixelSize(R.dimen.contact_picker_icon_size);
+    }
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java b/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
index 816da01..ebbe031 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
@@ -5,6 +5,7 @@
 package org.chromium.weblayer_private;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -633,7 +634,6 @@
         mSurfaceParent = new SurfaceParent(context);
         addView(mSurfaceParent,
                 new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        setBackgroundColor(Color.WHITE);
 
         mInsetObserverView = InsetObserverView.create(context);
         addView(mInsetObserverView);
@@ -670,6 +670,7 @@
             }
         };
         mWindowAndroid.getDisplay().addObserver(mDisplayAndroidObserver);
+        updateBackgroundColor();
     }
 
     public void requestMode(@Mode int mode, ValueCallback<Boolean> callback) {
@@ -742,6 +743,12 @@
         }
     }
 
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateBackgroundColor();
+    }
+
     /**
      * Sets the background color of the surface / texture view.  This method is necessary because
      * the background color of ContentViewRenderView itself is covered by the background of
@@ -758,6 +765,7 @@
         if (mCurrent != null) {
             mCurrent.setBackgroundColor(color);
         }
+        ContentViewRenderViewJni.get().updateBackgroundColor(mNativeContentViewRenderView);
     }
 
     public InsetObserverView getInsetObserverView() {
@@ -838,6 +846,18 @@
         return mNativeContentViewRenderView;
     }
 
+    private void updateBackgroundColor() {
+        int uiMode = getContext().getResources().getConfiguration().uiMode;
+        boolean darkThemeEnabled =
+                (uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+        setBackgroundColor(darkThemeEnabled ? Color.BLACK : Color.WHITE);
+    }
+
+    @CalledByNative
+    private int getBackgroundColor() {
+        return mBackgroundColor;
+    }
+
     private boolean shouldAvoidSurfaceResizeForSoftKeyboard() {
         // TextureView is more common with embedding use cases that should lead to resize.
         boolean usingSurfaceView = mCurrent != null && mCurrent.getMode() == MODE_SURFACE_VIEW;
@@ -877,5 +897,6 @@
         void setNeedsRedraw(long nativeContentViewRenderView);
         void evictCachedSurface(long nativeContentViewRenderView);
         ResourceManager getResourceManager(long nativeContentViewRenderView);
+        void updateBackgroundColor(long nativeContentViewRenderView);
     }
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/FragmentWindowAndroid.java b/weblayer/browser/java/org/chromium/weblayer_private/FragmentWindowAndroid.java
index 9d6f242..46f3fbe 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/FragmentWindowAndroid.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/FragmentWindowAndroid.java
@@ -67,6 +67,10 @@
         mModalDialogManager = modalDialogManager;
     }
 
+    public BrowserImpl getBrowser() {
+        return mFragment.getBrowser();
+    }
+
     @Override
     @TargetApi(Build.VERSION_CODES.O)
     public void setWideColorEnabled(boolean enabled) {
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java b/weblayer/browser/java/org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java
index 25b12a730..7053932 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java
@@ -4,7 +4,9 @@
 
 package org.chromium.weblayer_private;
 
+import android.graphics.Bitmap;
 import android.os.RemoteException;
+import android.webkit.ValueCallback;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
@@ -12,6 +14,7 @@
 import org.chromium.components.signin.GAIAServiceType;
 import org.chromium.weblayer_private.interfaces.GoogleAccountServiceType;
 import org.chromium.weblayer_private.interfaces.IGoogleAccountsCallbackClient;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
 
 /**
  * Owns the C++ GoogleAccountsCallbackProxy which is responsible for forwarding all calls to this
@@ -48,10 +51,24 @@
     }
 
     @CalledByNative
-    private String getGaiaId() throws RemoteException {
+    String getGaiaId() throws RemoteException {
         return mClient.getGaiaId();
     }
 
+    String getFullName() throws RemoteException {
+        if (WebLayerFactoryImpl.getClientMajorVersion() >= 87) {
+            return mClient.getFullName();
+        }
+
+        return null;
+    }
+
+    void getAvatar(int targetSize, ValueCallback<Bitmap> avatarLoaded) throws RemoteException {
+        if (WebLayerFactoryImpl.getClientMajorVersion() >= 87) {
+            mClient.getAvatar(targetSize, ObjectWrapper.wrap(avatarLoaded));
+        }
+    }
+
     @GoogleAccountServiceType
     private static int implTypeToJavaType(@GAIAServiceType int type) {
         switch (type) {
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
index a662fbb..370e588 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
@@ -550,6 +550,10 @@
         }
     }
 
+    public GoogleAccountsCallbackProxy getGoogleAccountsCallbackProxy() {
+        return mGoogleAccountsCallbackProxy;
+    }
+
     @Override
     public IFaviconFetcher createFaviconFetcher(IFaviconFetcherClient client) {
         StrictModeWorkaround.apply();
@@ -854,6 +858,8 @@
         // Ensure that this method isn't called twice.
         assert mInterceptNavigationDelegate != null;
 
+        TabImplJni.get().removeTabFromBrowserBeforeDestroying(mNativeTab);
+
         if (WebLayerFactoryImpl.getClientMajorVersion() >= 84) {
             // Notify the client that this instance is being destroyed to prevent it from calling
             // back into this object if the embedder mistakenly tries to do so.
@@ -1061,13 +1067,14 @@
     @NativeMethods
     interface Natives {
         TabImpl fromWebContents(WebContents webContents);
-        long createTab(long profile, TabImpl caller);
+        long createTab(long tab, TabImpl caller);
+        void removeTabFromBrowserBeforeDestroying(long nativeTabImpl);
+        void deleteTab(long tab);
         void setJavaImpl(long nativeTabImpl, TabImpl impl);
         void onAutofillProviderChanged(long nativeTabImpl, AutofillProvider autofillProvider);
         void setBrowserControlsContainerViews(long nativeTabImpl,
                 long nativeTopBrowserControlsContainerView,
                 long nativeBottomBrowserControlsContainerView);
-        void deleteTab(long tab);
         WebContents getWebContents(long nativeTabImpl);
         void executeScript(long nativeTabImpl, String script, boolean useSeparateIsolate,
                 Callback<String> callback);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index 88f7249..2bdd8b8 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -43,9 +43,7 @@
 import org.chromium.base.compat.ApiHelperForO;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.components.browser_ui.contacts_picker.ContactDetails;
 import org.chromium.components.browser_ui.contacts_picker.ContactsPickerDialog;
-import org.chromium.components.browser_ui.contacts_picker.PickerAdapter;
 import org.chromium.components.browser_ui.photo_picker.DecoderServiceHost;
 import org.chromium.components.browser_ui.photo_picker.ImageDecoder;
 import org.chromium.components.browser_ui.photo_picker.PhotoPickerDialog;
@@ -276,18 +274,9 @@
                         boolean includeTel, boolean includeAddresses, boolean includeIcons,
                         String formattedOrigin) -> {
                     ContactsPickerDialog dialog = new ContactsPickerDialog(windowAndroid,
-                            // TODO(estade): implement owner email.
-                            new PickerAdapter() {
-                                @Override
-                                protected String findOwnerEmail() {
-                                    return null;
-                                }
-                                @Override
-                                protected void addOwnerInfoToContacts(
-                                        ArrayList<ContactDetails> contacts) {}
-                            },
-                            listener, allowMultiple, includeNames, includeEmails, includeTel,
-                            includeAddresses, includeIcons, formattedOrigin);
+                            new ContactsPickerAdapter(windowAndroid), listener, allowMultiple,
+                            includeNames, includeEmails, includeTel, includeAddresses, includeIcons,
+                            formattedOrigin);
                     dialog.show();
                     return dialog;
                 });
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IGoogleAccountsCallbackClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IGoogleAccountsCallbackClient.aidl
index 50589c6..dd6fb0b 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IGoogleAccountsCallbackClient.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IGoogleAccountsCallbackClient.aidl
@@ -4,7 +4,14 @@
 
 package org.chromium.weblayer_private.interfaces;
 
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
 interface IGoogleAccountsCallbackClient {
   void onGoogleAccountsRequest(int serviceType, in String email, in String continueUrl, boolean isSameTab) = 0;
   String getGaiaId() = 1;
+
+  // Since 87
+  String getFullName() = 2;
+  // avatarLoadedWrapper is a ValueCallback<Bitmap> that updates the profile icon when run.
+  void getAvatar(int desiredSize, in IObjectWrapper avatarLoadedWrapper) = 3;
 }
diff --git a/weblayer/browser/tab_impl.cc b/weblayer/browser/tab_impl.cc
index 4023f47..c84ec5a 100644
--- a/weblayer/browser/tab_impl.cc
+++ b/weblayer/browser/tab_impl.cc
@@ -580,9 +580,10 @@
 static void JNI_TabImpl_DeleteTab(JNIEnv* env, jlong tab) {
   TabImpl* tab_impl = reinterpret_cast<TabImpl*>(tab);
   DCHECK(tab_impl);
-  DCHECK(tab_impl->browser());
-  // Don't call Browser::DestroyTab() as it calls back to the java side.
-  tab_impl->browser()->DestroyTabFromJava(tab_impl);
+  // RemoveTabBeforeDestroyingFromJava() should have been called before this,
+  // which sets browser to null.
+  DCHECK(!tab_impl->browser());
+  delete tab_impl;
 }
 
 ScopedJavaLocalRef<jobject> TabImpl::GetWebContents(JNIEnv* env) {
@@ -808,6 +809,11 @@
       ->ManualTranslateWhenReady();
 }
 
+void TabImpl::RemoveTabFromBrowserBeforeDestroying(JNIEnv* env) {
+  DCHECK(browser_);
+  browser_->RemoveTabBeforeDestroyingFromJava(this);
+}
+
 void TabImpl::SetTranslateTargetLanguage(
     JNIEnv* env,
     const base::android::JavaParamRef<jstring>& translate_target_lang) {
diff --git a/weblayer/browser/tab_impl.h b/weblayer/browser/tab_impl.h
index acb659c..ebfe953 100644
--- a/weblayer/browser/tab_impl.h
+++ b/weblayer/browser/tab_impl.h
@@ -198,6 +198,7 @@
       const base::android::JavaParamRef<jstring>& js_object_name);
   jboolean CanTranslate(JNIEnv* env);
   void ShowTranslateUi(JNIEnv* env);
+  void RemoveTabFromBrowserBeforeDestroying(JNIEnv* env);
   void SetTranslateTargetLanguage(
       JNIEnv* env,
       const base::android::JavaParamRef<jstring>& translate_target_lang);
diff --git a/weblayer/browser/translate_browsertest.cc b/weblayer/browser/translate_browsertest.cc
index 4bde4c2..c2eaf27 100644
--- a/weblayer/browser/translate_browsertest.cc
+++ b/weblayer/browser/translate_browsertest.cc
@@ -99,18 +99,6 @@
       GetTranslateClient(shell)->translate_driver(), wait_event);
 }
 
-void WaitUntilLanguageDetermined(Shell* shell) {
-  CreateTranslateWaiter(
-      shell, translate::TranslateWaiter::WaitEvent::kLanguageDetermined)
-      ->Wait();
-}
-
-void WaitUntilPageTranslated(Shell* shell) {
-  CreateTranslateWaiter(shell,
-                        translate::TranslateWaiter::WaitEvent::kPageTranslated)
-      ->Wait();
-}
-
 }  // namespace
 
 #if defined(OS_ANDROID)
@@ -175,6 +163,8 @@
   }
 
   void TearDownOnMainThread() override {
+    language_determination_waiter_.reset();
+    page_translation_waiter_.reset();
     mock_network_change_notifier_.reset();
   }
 
@@ -192,6 +182,19 @@
   }
   void SetTranslateScript(const std::string& script) { script_ = script; }
 
+  void ResetLanguageDeterminationWaiter() {
+    language_determination_waiter_ = CreateTranslateWaiter(
+        shell(), translate::TranslateWaiter::WaitEvent::kLanguageDetermined);
+  }
+
+  void ResetPageTranslationWaiter() {
+    page_translation_waiter_ = CreateTranslateWaiter(
+        shell(), translate::TranslateWaiter::WaitEvent::kPageTranslated);
+  }
+
+  std::unique_ptr<translate::TranslateWaiter> language_determination_waiter_;
+  std::unique_ptr<translate::TranslateWaiter> page_translation_waiter_;
+
  private:
   std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
       const net::test_server::HttpRequest& request) {
@@ -225,20 +228,23 @@
 IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, PageLanguageDetection) {
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   // Go to a page in English.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/english_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("en", translate_client->GetLanguageState().original_language());
 
   // Now navigate to a page in French.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 }
 
@@ -248,23 +254,26 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   // Navigate to a page in French.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   // Translate the page through TranslateManager.
+  ResetPageTranslationWaiter();
   translate::TranslateManager* manager =
       translate_client->GetTranslateManager();
   manager->TranslatePage(
       translate_client->GetLanguageState().original_language(), "en", true);
 
-  WaitUntilPageTranslated(shell());
+  page_translation_waiter_->Wait();
 
   EXPECT_FALSE(translate_client->GetLanguageState().translation_error());
   EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
@@ -285,23 +294,26 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   // Navigate to a page in French.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   // Translate the page through TranslateManager.
+  ResetPageTranslationWaiter();
   translate::TranslateManager* manager =
       translate_client->GetTranslateManager();
   manager->TranslatePage(
       translate_client->GetLanguageState().original_language(), "en", true);
 
-  WaitUntilPageTranslated(shell());
+  page_translation_waiter_->Wait();
 
   EXPECT_FALSE(translate_client->GetLanguageState().translation_error());
   EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
@@ -322,17 +334,19 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   // Translate the page through TranslateManager.
+  ResetPageTranslationWaiter();
   translate::TranslateManager* manager =
       translate_client->GetTranslateManager();
   manager->TranslatePage(
       translate_client->GetLanguageState().original_language(), "en", true);
 
-  WaitUntilPageTranslated(shell());
+  page_translation_waiter_->Wait();
 
   EXPECT_TRUE(translate_client->GetLanguageState().translation_error());
   EXPECT_EQ(translate::TranslateErrors::TRANSLATION_ERROR,
@@ -346,23 +360,26 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   // Navigate to a page in French.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   // Translate the page through TranslateManager.
+  ResetPageTranslationWaiter();
   translate::TranslateManager* manager =
       translate_client->GetTranslateManager();
   manager->TranslatePage(
       translate_client->GetLanguageState().original_language(), "en", true);
 
-  WaitUntilPageTranslated(shell());
+  page_translation_waiter_->Wait();
 
   EXPECT_TRUE(translate_client->GetLanguageState().translation_error());
   EXPECT_EQ(translate::TranslateErrors::INITIALIZATION_ERROR,
@@ -375,23 +392,26 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   // Navigate to a page in French.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   // Translate the page through TranslateManager.
+  ResetPageTranslationWaiter();
   translate::TranslateManager* manager =
       translate_client->GetTranslateManager();
   manager->TranslatePage(
       translate_client->GetLanguageState().original_language(), "en", true);
 
-  WaitUntilPageTranslated(shell());
+  page_translation_waiter_->Wait();
 
   EXPECT_TRUE(translate_client->GetLanguageState().translation_error());
   EXPECT_EQ(translate::TranslateErrors::TRANSLATION_TIMEOUT,
@@ -413,21 +433,24 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   // Before browsing, set autotranslate from French to Chinese.
   translate_client->GetTranslatePrefs()->WhitelistLanguagePair("fr", "zh-CN");
 
   // Navigate to a page in French.
+  ResetLanguageDeterminationWaiter();
+  ResetPageTranslationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   // Autotranslation should kick in.
-  WaitUntilPageTranslated(shell());
+  page_translation_waiter_->Wait();
 
   EXPECT_FALSE(translate_client->GetLanguageState().translation_error());
   EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
@@ -445,8 +468,9 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   TestInfoBarManagerObserver infobar_observer;
@@ -457,9 +481,10 @@
 
   EXPECT_EQ(0u, infobar_service->infobar_count());
   // Navigate to a page in French.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   // The translate infobar should be added.
@@ -500,8 +525,9 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   TestInfoBarManagerObserver infobar_observer;
@@ -511,21 +537,23 @@
   infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
 
   // Navigate to a page in French and wait for the infobar to be added.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   run_loop.Run();
 
   // Select the target language via the Java infobar and ensure that translation
   // occurs.
+  ResetPageTranslationWaiter();
   auto* infobar =
       static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0));
   TranslateTestBridge::SelectButton(
       infobar, infobars::InfoBarAndroid::ActionType::ACTION_TRANSLATE);
 
-  WaitUntilPageTranslated(shell());
+  page_translation_waiter_->Wait();
 
   EXPECT_FALSE(translate_client->GetLanguageState().translation_error());
   EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
@@ -570,8 +598,9 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   TestInfoBarManagerObserver infobar_observer;
@@ -581,10 +610,11 @@
   infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
 
   // Navigate to a page in French and wait for the infobar to be added.
+  ResetLanguageDeterminationWaiter();
   EXPECT_EQ(0u, infobar_service->infobar_count());
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   run_loop.Run();
@@ -600,9 +630,10 @@
 
   // However, the infobar should not be shown on a new navigation to a page in
   // French.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page2.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   // NOTE: There is no notification to wait for for the event of the infobar not
@@ -615,9 +646,10 @@
   base::RunLoop run_loop2;
   infobar_observer.set_on_infobar_added_callback(run_loop2.QuitClosure());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/german_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("de", translate_client->GetLanguageState().original_language());
 
   run_loop2.Run();
@@ -641,8 +673,9 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   TestInfoBarManagerObserver infobar_observer;
@@ -652,10 +685,11 @@
   infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
 
   // Navigate to a page in French and wait for the infobar to be added.
+  ResetLanguageDeterminationWaiter();
   EXPECT_EQ(0u, infobar_service->infobar_count());
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   run_loop.Run();
@@ -670,9 +704,10 @@
 
   // However, the infobar should not be shown on a new navigation to this site,
   // independent of the detected language.
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/french_page2.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
   // NOTE: There is no notification to wait for for the event of the infobar not
@@ -680,9 +715,10 @@
   // were to be shown, this check would fail.
   EXPECT_EQ(0u, infobar_service->infobar_count());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
       GURL(embedded_test_server()->GetURL("/german_page.html")), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("de", translate_client->GetLanguageState().original_language());
   EXPECT_EQ(0u, infobar_service->infobar_count());
 
@@ -709,8 +745,9 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  WaitUntilLanguageDetermined(shell());
+  language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   TestInfoBarManagerObserver infobar_observer;
@@ -722,10 +759,11 @@
     base::RunLoop run_loop;
     infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
 
+    ResetLanguageDeterminationWaiter();
     EXPECT_EQ(0u, infobar_service->infobar_count());
     NavigateAndWaitForCompletion(
         GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
-    WaitUntilLanguageDetermined(shell());
+    language_determination_waiter_->Wait();
     EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
     run_loop.Run();
@@ -746,9 +784,10 @@
     base::RunLoop run_loop;
     infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
 
+    ResetLanguageDeterminationWaiter();
     NavigateAndWaitForCompletion(
         GURL(embedded_test_server()->GetURL("/french_page2.html")), shell());
-    WaitUntilLanguageDetermined(shell());
+    language_determination_waiter_->Wait();
     EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
 
     run_loop.Run();
@@ -760,9 +799,10 @@
     base::RunLoop run_loop;
     infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
 
+    ResetLanguageDeterminationWaiter();
     NavigateAndWaitForCompletion(
         GURL(embedded_test_server()->GetURL("/german_page.html")), shell());
-    WaitUntilLanguageDetermined(shell());
+    language_determination_waiter_->Wait();
     EXPECT_EQ("de", translate_client->GetLanguageState().original_language());
 
     run_loop.Run();
diff --git a/weblayer/public/java/org/chromium/weblayer/Browser.java b/weblayer/public/java/org/chromium/weblayer/Browser.java
index 5265dd89..12bdd2fee 100644
--- a/weblayer/public/java/org/chromium/weblayer/Browser.java
+++ b/weblayer/public/java/org/chromium/weblayer/Browser.java
@@ -85,6 +85,9 @@
 
     // Called prior to notifying IBrowser of destroy().
     void prepareForDestroy() {
+        // See comment in Tab$TabClientImpl.onTabDestroyed for details on this.
+        if (WebLayer.getSupportedMajorVersionInternal() >= 87) return;
+
         for (Tab tab : getTabs()) {
             Tab.unregisterTab(tab);
         }
diff --git a/weblayer/public/java/org/chromium/weblayer/GoogleAccountsCallback.java b/weblayer/public/java/org/chromium/weblayer/GoogleAccountsCallback.java
index 92db5a9..f12d9a9 100644
--- a/weblayer/public/java/org/chromium/weblayer/GoogleAccountsCallback.java
+++ b/weblayer/public/java/org/chromium/weblayer/GoogleAccountsCallback.java
@@ -4,6 +4,9 @@
 
 package org.chromium.weblayer;
 
+import android.graphics.Bitmap;
+import android.webkit.ValueCallback;
+
 import androidx.annotation.NonNull;
 
 /**
@@ -24,4 +27,24 @@
      * be provided on a best effort basis if the ID is not available immediately.
      */
     public abstract @NonNull String getGaiaId();
+
+    /**
+     * Returns the full name of the signed-in user, or empty if the user is signed out. This can
+     * be provided on a best effort basis if the name is not available immediately.
+     * @since 87
+     */
+    public @NonNull String getFullName() {
+        return new String();
+    }
+
+    /**
+     * Called to retrieve the signed-in user's avatar.
+     * @param desiredSize the size the avatar will be displayed at, in raw pixels. If a different
+     *         size avatar is returned, WebLayer will scale the returned image.
+     * @param avatarLoadedCallback to be called with the avatar when it is available (synchronously
+     *         or asynchronously). Until such time that it's called, WebLayer will fall back to a
+     *         monogram based on {@link getFullName()}, e.g. encircled "JD" for "Jill Doe".
+     * @since 87
+     */
+    public void getAvatar(int desiredSize, @NonNull ValueCallback<Bitmap> avatarLoadedCallback) {}
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/Tab.java b/weblayer/public/java/org/chromium/weblayer/Tab.java
index 516cdfe..18586e09 100644
--- a/weblayer/public/java/org/chromium/weblayer/Tab.java
+++ b/weblayer/public/java/org/chromium/weblayer/Tab.java
@@ -740,6 +740,13 @@
 
         @Override
         public void onTabDestroyed() {
+            // Prior to 87 this was called *before* onTabRemoved(). Post 87 this is called after
+            // onTabRemoved(). onTabRemoved() needs the Tab to be registered, so unregisterTab()
+            // should only be called in >= 87 (in < 87 it is called from
+            // Browser.prepareForDestroy()).
+            if (WebLayer.getSupportedMajorVersionInternal() >= 87) {
+                unregisterTab(Tab.this);
+            }
             // Ensure that the app will fail fast if the embedder mistakenly tries to call back
             // into the implementation via this Tab.
             mImpl = null;
@@ -874,13 +881,30 @@
         @Override
         public void onGoogleAccountsRequest(
                 int serviceType, String email, String continueUrl, boolean isSameTab) {
+            StrictModeWorkaround.apply();
             mCallback.onGoogleAccountsRequest(new GoogleAccountsParams(
                     serviceType, email, Uri.parse(continueUrl), isSameTab));
         }
 
         @Override
         public String getGaiaId() {
+            StrictModeWorkaround.apply();
             return mCallback.getGaiaId();
         }
+
+        @Override
+        public String getFullName() {
+            StrictModeWorkaround.apply();
+            return mCallback.getFullName();
+        }
+
+        @Override
+        public void getAvatar(int desiredSize, IObjectWrapper avatarLoadedWrapper) {
+            StrictModeWorkaround.apply();
+            ValueCallback<Bitmap> avatarLoadedCallback =
+                    (ValueCallback<Bitmap>) ObjectWrapper.unwrap(
+                            avatarLoadedWrapper, ValueCallback.class);
+            mCallback.getAvatar(desiredSize, avatarLoadedCallback);
+        }
     }
 }
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index fdec15a..2b8b65c 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -20,6 +20,7 @@
 
 android_resources("weblayer_shell_resources") {
   sources = [
+    "shell_apk/res/drawable-mdpi/avatar_sunglasses.png",
     "shell_apk/res/layout/alt_shell_browser_controls.xml",
     "shell_apk/res/layout/bottom_controls.xml",
     "shell_apk/res/layout/main.xml",
diff --git a/weblayer/shell/android/shell_apk/res/drawable-mdpi/avatar_sunglasses.png b/weblayer/shell/android/shell_apk/res/drawable-mdpi/avatar_sunglasses.png
new file mode 100644
index 0000000..6382c7a3
--- /dev/null
+++ b/weblayer/shell/android/shell_apk/res/drawable-mdpi/avatar_sunglasses.png
Binary files differ
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index 861ef94..b7a1fc7 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -12,8 +12,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.text.TextUtils;
 import android.view.ContextMenu;
 import android.view.KeyEvent;
@@ -23,6 +26,7 @@
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
+import android.webkit.ValueCallback;
 import android.widget.EditText;
 import android.widget.ImageButton;
 import android.widget.ImageView;
@@ -49,6 +53,8 @@
 import org.chromium.weblayer.FaviconFetcher;
 import org.chromium.weblayer.FindInPageCallback;
 import org.chromium.weblayer.FullscreenCallback;
+import org.chromium.weblayer.GoogleAccountsCallback;
+import org.chromium.weblayer.GoogleAccountsParams;
 import org.chromium.weblayer.NavigationCallback;
 import org.chromium.weblayer.NavigationController;
 import org.chromium.weblayer.NewTabCallback;
@@ -553,6 +559,30 @@
                 return true;
             }
         });
+        tab.setGoogleAccountsCallback(new GoogleAccountsCallback() {
+            @Override
+            public void onGoogleAccountsRequest(GoogleAccountsParams params) {}
+
+            @Override
+            public String getGaiaId() {
+                return "example.user@gmail.com";
+            }
+
+            @Override
+            public String getFullName() {
+                return "Jill Doe";
+            }
+
+            @Override
+            public void getAvatar(int desiredSize, ValueCallback<Bitmap> avatarLoadedCallback) {
+                // Simulate a delayed load.
+                final Handler handler = new Handler(Looper.getMainLooper());
+                handler.postDelayed(() -> {
+                    avatarLoadedCallback.onReceiveValue(BitmapFactory.decodeResource(
+                            getApplicationContext().getResources(), R.drawable.avatar_sunglasses));
+                }, 3000);
+            }
+        });
         mTabToFaviconFetcher.put(tab, tab.createFaviconFetcher(new FaviconCallback() {
             @Override
             public void onFaviconChanged(Bitmap favicon) {