diff --git a/DEPS b/DEPS
index 1076430..adcac60 100644
--- a/DEPS
+++ b/DEPS
@@ -167,11 +167,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '54548626a977a72804242af934d01f94d8c02225',
+  'skia_revision': 'e27a503a0a21af167e6a19d2e5c7b7e1039964bf',
   # 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': '50e5ba2dd3e763aa78740945c04555d859a6e502',
+  'v8_revision': '0aff71c3ecaa48d359801e7f8864793f04aeee04',
   # 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.
@@ -179,7 +179,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '8e7d9d6c2326a36ff57335ceeb41eb86cb639a7f',
+  'angle_revision': 'ef4c25e71c32487feade5e9f8e79cb569545b846',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -218,7 +218,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '5a1a79c0e8de8e886cc347ad22801982e8298a06',
+  'freetype_revision': '1e9229f0fcb46fd4cd8e0fdc48fb4a44ddb7a8a1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -230,7 +230,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': '70a8316d8b67cc343c7b19aa9399fe17b53deba1',
+  'catapult_revision': 'e74b5c944eda8877ae8d1058b427f61694b57f21',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -294,15 +294,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spirv_cross_revision': 'ffca8735ff42a9e7a3d1dbb984cf3bf2ca724ebc',
+  'spirv_cross_revision': 'e5d3a6655e13870a6f38f40bd88cc662b14e3cdf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'shaderc_revision': '614d79248ef2dea617085475f8d64148b37d7296',
+  'shaderc_revision': '621605ce2644d55ab74cb27a8afef97dc40f1b0f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'c932f3309322b833cf13556a408fab75fa51c77d',
+  'dawn_revision': 'e09869ed526818cb01b4bdbe728ffe638ab5f861',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1470,7 +1470,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c4e67ff117d6c640e6dd17989afe2fb7da7eecb',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e1092c0bc8b8a2865da7ce6284ec19d189f71e02',
+    Var('webrtc_git') + '/src.git' + '@' + '955f8fd0477ea2d4137eff032184e02de8508043',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1532,7 +1532,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9608b4a1db16fa2e05497bc934fbf8d36c1b3d54',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b91063d99c88b47f7c603daa423f2a49910445bd',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
index 04f4f66..a157f13 100644
--- a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
+++ b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
@@ -669,17 +669,17 @@
     target_client_->OnComplete(status);
 
   if (proxied_loader_binding_ && wait_for_loader_error) {
+    // Since the original client is gone no need to continue loading the
+    // request.
+    proxied_client_binding_.Close();
+    target_loader_.reset();
+
     // Don't delete |this| yet, in case the |proxied_loader_binding_|'s
     // error_handler is called with a reason to indicate an error which we want
     // to send to the client bridge. Also reset |target_client_| so we don't
     // get its error_handler called and then delete |this|.
     target_client_.reset();
 
-    // Since the original client is gone no need to continue loading the
-    // request.
-    proxied_client_binding_.Close();
-    target_loader_.reset();
-
     // In case there are pending checks as to whether this request should be
     // intercepted, we don't want that causing |target_client_| to be used
     // later.
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index cd9825a..e334965 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -2074,20 +2074,20 @@
       gfx::Point(peeking_top.x(), (peeking_top.y() + fullscreen_y) / 2));
   GetAppListTestHelper()->WaitUntilIdle();
 
-  EXPECT_LE(std::abs(GetAppListView()->GetAppListTransitionProgress(
-                         AppListView::kProgressFlagNone) -
-                     1.5f),
-            0.01f);
-
   gfx::Rect search_box_bounds =
       GetAppListView()->search_box_view()->GetBoundsInScreen();
   search_box_bounds.Inset(GetAppListView()->search_box_view()->GetInsets());
 
+  float progress = GetAppListView()->GetAppListTransitionProgress(
+      AppListView::kProgressFlagNone);
+  EXPECT_LE(std::abs(progress - 1.5f), 0.01f);
+
   EXPECT_EQ((peeking_top.y() + fullscreen_y) / 2 +
-                (config.search_box_peeking_top_padding() +
-                 fullscreen_search_box_padding) /
-                    2,
+                gfx::Tween::IntValueBetween(
+                    progress - 1, config.search_box_peeking_top_padding(),
+                    fullscreen_search_box_padding),
             search_box_bounds.y());
+
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
   EXPECT_TRUE(apps_grid_view()->GetVisible());
@@ -2118,18 +2118,17 @@
       gfx::Point(peeking_top.x(), (peeking_top.y() + closed_y) / 2));
   GetAppListTestHelper()->WaitUntilIdle();
 
-  EXPECT_LE(std::abs(GetAppListView()->GetAppListTransitionProgress(
-                         AppListView::kProgressFlagNone) -
-                     0.5f),
-            0.01f);
-
   search_box_bounds = GetAppListView()->search_box_view()->GetBoundsInScreen();
   search_box_bounds.Inset(GetAppListView()->search_box_view()->GetInsets());
 
+  progress = GetAppListView()->GetAppListTransitionProgress(
+      AppListView::kProgressFlagNone);
+  EXPECT_LE(std::abs(progress - 0.5f), 0.01f);
+
   EXPECT_EQ((peeking_top.y() + closed_y) / 2 +
-                (config.search_box_peeking_top_padding() +
-                 config.search_box_closed_top_padding()) /
-                    2,
+                gfx::Tween::IntValueBetween(
+                    progress, config.search_box_peeking_top_padding(),
+                    config.search_box_closed_top_padding()),
             search_box_bounds.y());
   EXPECT_EQ(ExpectedAppsGridTop(config, 900, search_box_bounds),
             apps_grid_view()->GetBoundsInScreen().y());
@@ -2186,19 +2185,18 @@
       gfx::Point(half_top.x(), (half_top.y() + fullscreen_y) / 2));
   GetAppListTestHelper()->WaitUntilIdle();
 
-  EXPECT_LE(std::abs(GetAppListView()->GetAppListTransitionProgress(
-                         AppListView::kProgressFlagSearchResults) -
-                     1.5f),
-            0.01f);
-
   gfx::Rect search_box_bounds =
       GetAppListView()->search_box_view()->GetBoundsInScreen();
   search_box_bounds.Inset(GetAppListView()->search_box_view()->GetInsets());
 
+  float progress = GetAppListView()->GetAppListTransitionProgress(
+      AppListView::kProgressFlagSearchResults);
+  EXPECT_LE(std::abs(progress - 1.5f), 0.01f);
+
   EXPECT_EQ((half_top.y() + fullscreen_y) / 2 +
-                (config.search_box_fullscreen_top_padding() +
-                 fullscreen_search_box_padding) /
-                    2,
+                gfx::Tween::IntValueBetween(
+                    progress - 1, config.search_box_fullscreen_top_padding(),
+                    fullscreen_search_box_padding),
             search_box_bounds.y());
   EXPECT_EQ(search_box_bounds.y(),
             search_result_page()->GetBoundsInScreen().y());
@@ -2243,18 +2241,18 @@
   // should be half distance between closed and peeking padding.
   generator->MoveTouch(gfx::Point(half_top.x(), (half_top.y() + closed_y) / 2));
   GetAppListTestHelper()->WaitUntilIdle();
-  EXPECT_LE(std::abs(GetAppListView()->GetAppListTransitionProgress(
-                         AppListView::kProgressFlagSearchResults) -
-                     0.5f),
-            0.01f);
+
+  progress = GetAppListView()->GetAppListTransitionProgress(
+      AppListView::kProgressFlagSearchResults);
+  EXPECT_LE(std::abs(progress - 0.5f), 0.01f);
 
   search_box_bounds = GetAppListView()->search_box_view()->GetBoundsInScreen();
   search_box_bounds.Inset(GetAppListView()->search_box_view()->GetInsets());
 
   EXPECT_EQ((half_top.y() + closed_y) / 2 +
-                (config.search_box_fullscreen_top_padding() +
-                 config.search_box_closed_top_padding()) /
-                    2,
+                gfx::Tween::IntValueBetween(
+                    progress, config.search_box_fullscreen_top_padding(),
+                    config.search_box_closed_top_padding()),
             search_box_bounds.y());
   EXPECT_EQ(search_box_bounds.y(),
             search_result_page()->GetBoundsInScreen().y());
@@ -2332,10 +2330,13 @@
       GetAppListView()->search_box_view()->GetBoundsInScreen();
   search_box_bounds.Inset(GetAppListView()->search_box_view()->GetInsets());
 
+  float progress = GetAppListView()->GetAppListTransitionProgress(
+      AppListView::kProgressFlagSearchResults);
+  EXPECT_LE(std::abs(progress - 1.5f), 0.01f);
   EXPECT_EQ((half_top.y() + fullscreen_y) / 2 +
-                (config.search_box_fullscreen_top_padding() +
-                 fullscreen_search_box_padding) /
-                    2,
+                gfx::Tween::IntValueBetween(
+                    progress - 1, config.search_box_fullscreen_top_padding(),
+                    fullscreen_search_box_padding),
             search_box_bounds.y());
   EXPECT_EQ(search_box_bounds.y(),
             search_result_page()->GetBoundsInScreen().y());
@@ -2392,10 +2393,13 @@
   search_box_bounds = GetAppListView()->search_box_view()->GetBoundsInScreen();
   search_box_bounds.Inset(GetAppListView()->search_box_view()->GetInsets());
 
+  progress = GetAppListView()->GetAppListTransitionProgress(
+      AppListView::kProgressFlagSearchResults);
+  EXPECT_LE(std::abs(progress - 1.5f), 0.01f);
   EXPECT_EQ((half_top.y() + fullscreen_y) / 2 +
-                (config.search_box_fullscreen_top_padding() +
-                 fullscreen_search_box_padding) /
-                    2,
+                gfx::Tween::IntValueBetween(
+                    progress - 1, config.search_box_fullscreen_top_padding(),
+                    fullscreen_search_box_padding),
             search_box_bounds.y());
   EXPECT_EQ(search_box_bounds.y(),
             search_result_page()->GetBoundsInScreen().y());
diff --git a/ash/system/audio/unified_volume_view.cc b/ash/system/audio/unified_volume_view.cc
index f0877d1..e75d670 100644
--- a/ash/system/audio/unified_volume_view.cc
+++ b/ash/system/audio/unified_volume_view.cc
@@ -22,9 +22,9 @@
 #include "ui/views/animation/ink_drop_highlight.h"
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/animation/ink_drop_mask.h"
+#include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/box_layout.h"
-#include "ui/views/view_class_properties.h"
 
 using chromeos::CrasAudioHandler;
 
@@ -88,10 +88,7 @@
     SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO));
     TrayPopupUtils::ConfigureTrayPopupButton(this);
 
-    auto path = std::make_unique<SkPath>();
-    path->addRoundRect(gfx::RectToSkRect(gfx::Rect(CalculatePreferredSize())),
-                       kTrayItemSize / 2, kTrayItemSize / 2);
-    SetProperty(views::kHighlightPathKey, path.release());
+    views::InstallPillHighlightPathGenerator(this);
   }
 
   ~MoreButton() override = default;
@@ -126,11 +123,6 @@
         UnifiedSystemTrayView::GetBackgroundColor());
   }
 
-  std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override {
-    return std::make_unique<views::RoundRectInkDropMask>(size(), gfx::Insets(),
-                                                         kTrayItemSize / 2);
-  }
-
   const char* GetClassName() const override { return "MoreButton"; }
 
  private:
diff --git a/ash/system/unified/rounded_label_button.cc b/ash/system/unified/rounded_label_button.cc
index bec4b62c..46b7d7b 100644
--- a/ash/system/unified/rounded_label_button.cc
+++ b/ash/system/unified/rounded_label_button.cc
@@ -14,7 +14,7 @@
 #include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/animation/ink_drop_ripple.h"
 #include "ui/views/border.h"
-#include "ui/views/view_class_properties.h"
+#include "ui/views/controls/highlight_path_generator.h"
 
 namespace ash {
 
@@ -30,11 +30,7 @@
   label()->SetFontList(views::Label::GetDefaultFontList().Derive(
       1, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
   TrayPopupUtils::ConfigureTrayPopupButton(this);
-
-  auto path = std::make_unique<SkPath>();
-  path->addRoundRect(gfx::RectToSkRect(gfx::Rect(CalculatePreferredSize())),
-                     kTrayItemSize / 2, kTrayItemSize / 2);
-  SetProperty(views::kHighlightPathKey, path.release());
+  views::InstallPillHighlightPathGenerator(this);
 }
 
 RoundedLabelButton::~RoundedLabelButton() = default;
diff --git a/ash/system/unified/top_shortcuts_view.cc b/ash/system/unified/top_shortcuts_view.cc
index 7ef376b..87e7eb1 100644
--- a/ash/system/unified/top_shortcuts_view.cc
+++ b/ash/system/unified/top_shortcuts_view.cc
@@ -23,6 +23,7 @@
 #include "ash/system/unified/user_chooser_view.h"
 #include "base/numerics/ranges.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/view_class_properties.h"
@@ -50,12 +51,7 @@
   SetInstallFocusRingOnFocus(true);
   SetFocusForPlatform();
 
-  int focus_ring_radius =
-      kTrayItemSize + kUnifiedCircularButtonFocusPadding.width();
-  auto path = std::make_unique<SkPath>();
-  path->addOval(gfx::RectToSkRect(
-      gfx::Rect(gfx::Size(focus_ring_radius, focus_ring_radius))));
-  SetProperty(views::kHighlightPathKey, path.release());
+  views::InstallCircleHighlightPathGenerator(this);
 }
 
 }  // namespace
diff --git a/ash/wallpaper/wallpaper_view.cc b/ash/wallpaper/wallpaper_view.cc
index d9e7fa53..b90568e 100644
--- a/ash/wallpaper/wallpaper_view.cc
+++ b/ash/wallpaper/wallpaper_view.cc
@@ -10,6 +10,7 @@
 #include "ash/shell.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "cc/paint/render_surface_filters.h"
 #include "ui/aura/window.h"
 #include "ui/display/display.h"
@@ -133,8 +134,14 @@
   // Create the blur and brightness filter to apply to the downsampled image.
   cc::PaintFlags filter_flags;
   cc::FilterOperations operations;
-  operations.Append(
-      cc::FilterOperation::CreateBrightnessFilter(repaint_opacity_));
+  // In tablet mode, the wallpaper already has a color filter applied in
+  // |OnPaint| so we don't need to darken here.
+  // TODO(crbug.com/944152): Merge this with the color filter in
+  // WallpaperBaseView.
+  if (!Shell::Get()->tablet_mode_controller()->InTabletMode()) {
+    operations.Append(
+        cc::FilterOperation::CreateBrightnessFilter(repaint_opacity_));
+  }
   operations.Append(cc::FilterOperation::CreateBlurFilter(
       blur, SkBlurImageFilter::kClamp_TileMode));
   sk_sp<cc::PaintFilter> filter = cc::RenderSurfaceFilters::BuildImageFilter(
diff --git a/base/BUILD.gn b/base/BUILD.gn
index ac9ad8f..3c92548 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2702,6 +2702,7 @@
     "task/common/operations_controller_unittest.cc",
     "task/common/task_annotator_unittest.cc",
     "task/lazy_task_runner_unittest.cc",
+    "task/post_job_unittest.cc",
     "task/post_task_unittest.cc",
     "task/promise/abstract_promise_unittest.cc",
     "task/promise/dependent_list_unittest.cc",
diff --git a/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java b/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
index 1af91c6..06b62375 100644
--- a/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
+++ b/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
@@ -4,6 +4,7 @@
 
 package org.chromium.base.task;
 
+import android.os.Handler;
 import android.view.Choreographer;
 
 import org.chromium.base.ThreadUtils;
@@ -20,22 +21,26 @@
     @Override
     public TaskRunner createTaskRunner(TaskTraits taskTraits) {
         if (taskTraits.mIsChoreographerFrame) return createChoreographerTaskRunner();
+        if (taskTraits.mUseCurrentThread) return postCurrentThreadTask(taskTraits);
         return new TaskRunnerImpl(taskTraits);
     }
 
     @Override
     public SequencedTaskRunner createSequencedTaskRunner(TaskTraits taskTraits) {
         if (taskTraits.mIsChoreographerFrame) return createChoreographerTaskRunner();
+        if (taskTraits.mUseCurrentThread) return postCurrentThreadTask(taskTraits);
         return new SequencedTaskRunnerImpl(taskTraits);
     }
 
     /**
-     * This maps to a single thread within the native thread pool. Due to that contract we
-     * can't run tasks posted on it until native has started.
+     * If CurrentThread is not specified, or we are being called from within a threadpool task
+     * this maps to a single thread within the native thread pool. If so we can't run tasks
+     * posted on it until native has started.
      */
     @Override
     public SingleThreadTaskRunner createSingleThreadTaskRunner(TaskTraits taskTraits) {
         if (taskTraits.mIsChoreographerFrame) return createChoreographerTaskRunner();
+        if (taskTraits.mUseCurrentThread) return postCurrentThreadTask(taskTraits);
         // Tasks posted via this API will not execute until after native has started.
         return new SingleThreadTaskRunnerImpl(null, taskTraits);
     }
@@ -64,6 +69,16 @@
         return false;
     }
 
+    private SingleThreadTaskRunner postCurrentThreadTask(TaskTraits taskTraits) {
+        // Until native has loaded we only support CurrentThread on the UI thread, migration from
+        // threadpool or other java threads adds a lot of complexity for likely zero usage.
+        assert PostTask.getNativeSchedulerReady() || ThreadUtils.runningOnUiThread();
+        // A handler is only needed before the native scheduler is ready.
+        Handler preNativeHandler =
+                PostTask.getNativeSchedulerReady() ? null : ThreadUtils.getUiThreadHandler();
+        return new SingleThreadTaskRunnerImpl(preNativeHandler, taskTraits);
+    }
+
     private synchronized ChoreographerTaskRunner createChoreographerTaskRunner() {
         // TODO(alexclarke): Migrate to the new Android UI thread trait when available.
         return ThreadUtils.runOnUiThreadBlockingNoException(
diff --git a/base/android/java/src/org/chromium/base/task/PostTask.java b/base/android/java/src/org/chromium/base/task/PostTask.java
index 648eb05..f9a71e7 100644
--- a/base/android/java/src/org/chromium/base/task/PostTask.java
+++ b/base/android/java/src/org/chromium/base/task/PostTask.java
@@ -28,6 +28,7 @@
     private static final Executor sPrenativeThreadPoolExecutor = new ChromeThreadPoolExecutor();
     private static Executor sPrenativeThreadPoolExecutorOverride;
     private static final TaskExecutor sTaskExecutors[] = getInitialTaskExecutors();
+    private static boolean sNativeSchedulerReady;
 
     private static TaskExecutor[] getInitialTaskExecutors() {
         TaskExecutor taskExecutors[] = new TaskExecutor[TaskTraits.MAX_EXTENSION_ID + 1];
@@ -88,7 +89,8 @@
             } else {
                 PostTaskJni.get().postDelayedTask(taskTraits.mPrioritySetExplicitly,
                         taskTraits.mPriority, taskTraits.mMayBlock, taskTraits.mUseThreadPool,
-                        taskTraits.mExtensionId, taskTraits.mExtensionData, task, delay);
+                        taskTraits.mUseCurrentThread, taskTraits.mExtensionId,
+                        taskTraits.mExtensionData, task, delay);
             }
         }
     }
@@ -210,8 +212,9 @@
      */
     static Executor getPrenativeThreadPoolExecutor() {
         synchronized (sLock) {
-            if (sPrenativeThreadPoolExecutorOverride != null)
+            if (sPrenativeThreadPoolExecutorOverride != null) {
                 return sPrenativeThreadPoolExecutorOverride;
+            }
             return sPrenativeThreadPoolExecutor;
         }
     }
@@ -237,11 +240,21 @@
         return sTaskExecutors[traits.mExtensionId];
     }
 
+    /**
+     * @return True if the native scheduler is ready.
+     */
+    static boolean getNativeSchedulerReady() {
+        synchronized (sLock) {
+            return sNativeSchedulerReady;
+        }
+    }
+
     @CalledByNative
     private static void onNativeSchedulerReady() {
         synchronized (sLock) {
             Set<TaskRunner> preNativeTaskRunners = sPreNativeTaskRunners;
             sPreNativeTaskRunners = null;
+            sNativeSchedulerReady = true;
             for (TaskRunner taskRunner : preNativeTaskRunners) {
                 taskRunner.initNativeTaskRunner();
             }
@@ -254,13 +267,14 @@
         synchronized (sLock) {
             sPreNativeTaskRunners =
                     Collections.newSetFromMap(new WeakHashMap<TaskRunner, Boolean>());
+            sNativeSchedulerReady = false;
         }
     }
 
     @NativeMethods
     interface Natives {
         void postDelayedTask(boolean prioritySetExplicitly, int priority, boolean mayBlock,
-                boolean useThreadPool, byte extensionId, byte[] extensionData, Runnable task,
-                long delay);
+                boolean useThreadPool, boolean useCurrentThread, byte extensionId,
+                byte[] extensionData, Runnable task, long delay);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
index 5a5e1f3..f77354f 100644
--- a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
+++ b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
@@ -74,8 +74,9 @@
 
     @GuardedBy("mLock")
     protected void destroyInternal() {
-        if (mNativeTaskRunnerAndroid != 0)
+        if (mNativeTaskRunnerAndroid != 0) {
             TaskRunnerImplJni.get().destroy(mNativeTaskRunnerAndroid);
+        }
         mNativeTaskRunnerAndroid = 0;
     }
 
@@ -163,7 +164,8 @@
         if (mNativeTaskRunnerAndroid == 0) {
             mNativeTaskRunnerAndroid = TaskRunnerImplJni.get().init(mTaskRunnerType,
                     mTaskTraits.mPrioritySetExplicitly, mTaskTraits.mPriority,
-                    mTaskTraits.mMayBlock, mTaskTraits.mUseThreadPool, mTaskTraits.mExtensionId,
+                    mTaskTraits.mMayBlock, mTaskTraits.mUseThreadPool,
+                    mTaskTraits.mUseCurrentThread, mTaskTraits.mExtensionId,
                     mTaskTraits.mExtensionData);
         }
     }
@@ -191,7 +193,8 @@
     interface Natives {
         // NB due to Proguard obfuscation it's easiest to pass the traits via arguments.
         long init(@TaskRunnerType int taskRunnerType, boolean prioritySetExplicitly, int priority,
-                boolean mayBlock, boolean useThreadPool, byte extensionId, byte[] extensionData);
+                boolean mayBlock, boolean useThreadPool, boolean useCurrentThread, byte extensionId,
+                byte[] extensionData);
 
         void destroy(long nativeTaskRunnerAndroid);
         void postDelayedTask(long nativeTaskRunnerAndroid, Runnable task, long delay);
diff --git a/base/android/java/src/org/chromium/base/task/TaskTraits.java b/base/android/java/src/org/chromium/base/task/TaskTraits.java
index ebc98d0..af1203c 100644
--- a/base/android/java/src/org/chromium/base/task/TaskTraits.java
+++ b/base/android/java/src/org/chromium/base/task/TaskTraits.java
@@ -66,9 +66,8 @@
     // For tasks that should run on the thread pool instead of the main thread.
     // Note that currently also tasks which lack this trait will execute on the
     // thread pool unless a trait for a named thread is given.
-    // TODO(skyostil@): Make it required to state the thread affinity for all
-    // tasks.
-    public static final TaskTraits THREAD_POOL = new TaskTraits().threadPool();
+    public static final TaskTraits THREAD_POOL =
+            new TaskTraits().threadPool().taskPriority(TaskPriority.USER_BLOCKING);
     public static final TaskTraits THREAD_POOL_USER_BLOCKING =
             THREAD_POOL.taskPriority(TaskPriority.USER_BLOCKING);
     public static final TaskTraits THREAD_POOL_USER_VISIBLE =
@@ -76,12 +75,23 @@
     public static final TaskTraits THREAD_POOL_BEST_EFFORT =
             THREAD_POOL.taskPriority(TaskPriority.BEST_EFFORT);
 
+    // For tasks that should run on the current thread.
+    public static final TaskTraits CURRENT_THREAD =
+            new TaskTraits().currentThread().taskPriority(TaskPriority.USER_BLOCKING);
+    public static final TaskTraits CURRENT_THREAD_USER_BLOCKING =
+            CURRENT_THREAD.taskPriority(TaskPriority.USER_BLOCKING);
+    public static final TaskTraits CURRENT_THREAD_USER_VISIBLE =
+            CURRENT_THREAD.taskPriority(TaskPriority.USER_VISIBLE);
+    public static final TaskTraits CURRENT_THREAD_BEST_EFFORT =
+            CURRENT_THREAD.taskPriority(TaskPriority.BEST_EFFORT);
+
     // For convenience of the JNI code, we use primitive types only.
     // Note shutdown behavior is not supported on android.
     boolean mPrioritySetExplicitly;
     int mPriority;
     boolean mMayBlock;
     boolean mUseThreadPool;
+    boolean mUseCurrentThread;
     byte mExtensionId;
     byte mExtensionData[];
     boolean mIsChoreographerFrame;
@@ -97,6 +107,7 @@
         mMayBlock = other.mMayBlock;
         mUseThreadPool = other.mUseThreadPool;
         mExtensionId = other.mExtensionId;
+        mUseCurrentThread = other.mUseCurrentThread;
         mExtensionData = other.mExtensionData;
     }
 
@@ -126,6 +137,12 @@
         return taskTraits;
     }
 
+    public TaskTraits currentThread() {
+        TaskTraits taskTraits = new TaskTraits(this);
+        taskTraits.mUseCurrentThread = true;
+        return taskTraits;
+    }
+
     /**
      * @return true if this task is using some TaskTraits extension.
      */
@@ -166,8 +183,12 @@
         } else if (object instanceof TaskTraits) {
             TaskTraits other = (TaskTraits) object;
             return mPrioritySetExplicitly == other.mPrioritySetExplicitly
-                    && mPriority == other.mPriority && mExtensionId == other.mExtensionId
-                    && Arrays.equals(mExtensionData, other.mExtensionData);
+                    && mPriority == other.mPriority && mMayBlock == other.mMayBlock
+                    && mUseThreadPool == other.mUseThreadPool
+                    && mUseCurrentThread == other.mUseCurrentThread
+                    && mExtensionId == other.mExtensionId
+                    && Arrays.equals(mExtensionData, other.mExtensionData)
+                    && mIsChoreographerFrame == other.mIsChoreographerFrame;
         } else {
             return false;
         }
@@ -180,6 +201,7 @@
         hash = 37 * hash + mPriority;
         hash = 37 * hash + (mMayBlock ? 0 : 1);
         hash = 37 * hash + (mUseThreadPool ? 0 : 1);
+        hash = 37 * hash + (mUseCurrentThread ? 0 : 1);
         hash = 37 * hash + (int) mExtensionId;
         hash = 37 * hash + Arrays.hashCode(mExtensionData);
         hash = 37 * hash + (mIsChoreographerFrame ? 0 : 1);
diff --git a/base/android/task_scheduler/post_task_android.cc b/base/android/task_scheduler/post_task_android.cc
index 78f1cf7..743dd01 100644
--- a/base/android/task_scheduler/post_task_android.cc
+++ b/base/android/task_scheduler/post_task_android.cc
@@ -49,11 +49,12 @@
     jint priority,
     jboolean may_block,
     jboolean use_thread_pool,
+    jboolean current_thread,
     jbyte extension_id,
     const base::android::JavaParamRef<jbyteArray>& extension_data) {
   return TaskTraits(priority_set_explicitly,
                     static_cast<TaskPriority>(priority), may_block,
-                    use_thread_pool,
+                    use_thread_pool, current_thread,
                     TaskTraitsExtensionStorage(
                         extension_id, GetExtensionData(env, extension_data)));
 }
@@ -64,19 +65,21 @@
     jint priority,
     jboolean may_block,
     jboolean use_thread_pool,
+    jboolean current_thread,
     jbyte extension_id,
     const base::android::JavaParamRef<jbyteArray>& extension_data,
     const base::android::JavaParamRef<jobject>& task,
     jlong delay) {
   // This could be run on any java thread, so we can't cache |env| in the
   // BindOnce because JNIEnv is thread specific.
-  PostDelayedTask(FROM_HERE,
-                  PostTaskAndroid::CreateTaskTraits(
-                      env, priority_set_explicitly, priority, may_block,
-                      use_thread_pool, extension_id, extension_data),
-                  BindOnce(&PostTaskAndroid::RunJavaTask,
-                           base::android::ScopedJavaGlobalRef<jobject>(task)),
-                  TimeDelta::FromMilliseconds(delay));
+  PostDelayedTask(
+      FROM_HERE,
+      PostTaskAndroid::CreateTaskTraits(
+          env, priority_set_explicitly, priority, may_block, use_thread_pool,
+          current_thread, extension_id, extension_data),
+      BindOnce(&PostTaskAndroid::RunJavaTask,
+               base::android::ScopedJavaGlobalRef<jobject>(task)),
+      TimeDelta::FromMilliseconds(delay));
 }
 
 // static
diff --git a/base/android/task_scheduler/post_task_android.h b/base/android/task_scheduler/post_task_android.h
index c15c822..80fdb3b 100644
--- a/base/android/task_scheduler/post_task_android.h
+++ b/base/android/task_scheduler/post_task_android.h
@@ -28,6 +28,7 @@
       jint priority,
       jboolean may_block,
       jboolean use_thread_pool,
+      jboolean current_thread,
       jbyte extension_id,
       const base::android::JavaParamRef<jbyteArray>& extension_data);
 
diff --git a/base/android/task_scheduler/task_runner_android.cc b/base/android/task_scheduler/task_runner_android.cc
index 18730e1..7531028 100644
--- a/base/android/task_scheduler/task_runner_android.cc
+++ b/base/android/task_scheduler/task_runner_android.cc
@@ -20,11 +20,12 @@
     jint priority,
     jboolean may_block,
     jboolean thread_pool,
+    jboolean current_thread,
     jbyte extension_id,
     const base::android::JavaParamRef<jbyteArray>& extension_data) {
   TaskTraits task_traits = PostTaskAndroid::CreateTaskTraits(
       env, priority_set_explicitly, priority, may_block, thread_pool,
-      extension_id, extension_data);
+      current_thread, extension_id, extension_data);
   scoped_refptr<TaskRunner> task_runner;
   switch (static_cast<TaskRunnerType>(task_runner_type)) {
     case TaskRunnerType::BASE:
diff --git a/base/task/post_job.cc b/base/task/post_job.cc
index 1e0b4d9..3c83068 100644
--- a/base/task/post_job.cc
+++ b/base/task/post_job.cc
@@ -7,6 +7,8 @@
 #include "base/task/scoped_set_task_priority_for_current_thread.h"
 #include "base/task/thread_pool/job_task_source.h"
 #include "base/task/thread_pool/pooled_task_runner_delegate.h"
+#include "base/task/thread_pool/thread_pool_impl.h"
+#include "base/task/thread_pool/thread_pool_instance.h"
 
 namespace base {
 namespace experimental {
@@ -100,6 +102,8 @@
 #endif  // DCHECK_IS_ON()
 }
 
+JobHandle::JobHandle() = default;
+
 JobHandle::JobHandle(scoped_refptr<internal::JobTaskSource> task_source)
     : task_source_(std::move(task_source)) {}
 
@@ -157,5 +161,32 @@
   task_source_ = nullptr;
 }
 
+JobHandle PostJob(const Location& from_here,
+                  const TaskTraits& traits,
+                  RepeatingCallback<void(JobDelegate*)> worker_task,
+                  RepeatingCallback<size_t()> max_concurrency_callback) {
+  DCHECK(ThreadPoolInstance::Get())
+      << "Ref. Prerequisite section of post_task.h.\n\n"
+         "Hint: if this is in a unit test, you're likely merely missing a "
+         "base::test::TaskEnvironment member in your fixture.\n";
+  DCHECK(traits.use_thread_pool())
+      << "The base::ThreadPool() trait is mandatory with PostJob().";
+  DCHECK_EQ(traits.extension_id(),
+            TaskTraitsExtensionStorage::kInvalidExtensionId)
+      << "Extension traits cannot be used with PostJob().";
+  TaskTraits adjusted_traits = traits;
+  adjusted_traits.InheritPriority(internal::GetTaskPriorityForCurrentThread());
+  auto task_source = base::MakeRefCounted<internal::JobTaskSource>(
+      from_here, adjusted_traits, std::move(worker_task),
+      std::move(max_concurrency_callback),
+      static_cast<internal::ThreadPoolImpl*>(ThreadPoolInstance::Get()));
+  const bool queued =
+      static_cast<internal::ThreadPoolImpl*>(ThreadPoolInstance::Get())
+          ->EnqueueJobTaskSource(task_source);
+  if (queued)
+    return internal::JobTaskSource::CreateJobHandle(std::move(task_source));
+  return JobHandle();
+}
+
 }  // namespace experimental
 }  // namespace base
\ No newline at end of file
diff --git a/base/task/post_job.h b/base/task/post_job.h
index b9a3283..cf8ca1a 100644
--- a/base/task/post_job.h
+++ b/base/task/post_job.h
@@ -6,6 +6,8 @@
 #define BASE_TASK_POST_JOB_H_
 
 #include "base/base_export.h"
+#include "base/callback.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -45,7 +47,7 @@
   void YieldIfNeeded();
 
   // Notifies the scheduler that max concurrency was increased, and the number
-  // of worker should be adjusted.
+  // of worker should be adjusted accordingly. See PostJob() for more details.
   void NotifyConcurrencyIncrease();
 
  private:
@@ -74,6 +76,7 @@
 // the posted Job.
 class BASE_EXPORT JobHandle {
  public:
+  JobHandle();
   // A job must either be joined, canceled or detached before the JobHandle is
   // destroyed.
   ~JobHandle();
@@ -85,12 +88,7 @@
   void UpdatePriority(TaskPriority new_priority);
 
   // Notifies the scheduler that max concurrency was increased, and the number
-  // of worker should be adjusted accordingly. This *must* be invoked shortly
-  // after |max_concurrency_callback| starts returning a value larger than
-  // previously returned values. This usually happens when new work items are
-  // added and the API user wants additional |worker_task| to run concurrently.
-  // TODO(839091): Update comment to be more specific about
-  // |max_concurrency_callback| once PostJob() landed.
+  // of workers should be adjusted accordingly. See PostJob() for more details.
   void NotifyConcurrencyIncrease();
 
   // Contributes to the job on this thread. Doesn't return until all tasks have
@@ -111,7 +109,6 @@
   void Detach();
 
  private:
-  // TODO(839091): Update friendship once PostJob is used.
   friend class internal::JobTaskSource;
 
   explicit JobHandle(scoped_refptr<internal::JobTaskSource> task_source);
@@ -121,6 +118,49 @@
   DISALLOW_COPY_AND_ASSIGN(JobHandle);
 };
 
+// Posts a repeating |worker_task| with specific |traits| to run in parallel.
+// Returns a JobHandle associated with the Job, which can be joined, canceled or
+// detached.
+// To avoid scheduling overhead, |worker_task| should do as much work as
+// possible in a loop when invoked, and JobDelegate::ShouldYield() should be
+// periodically invoked to conditionally exit and let the scheduler prioritize
+// work.
+//
+// A canonical implementation of |worker_task| looks like:
+//   void WorkerTask(JobDelegate* job_delegate) {
+//     while (!job_delegate->ShouldYield()) {
+//       auto work_item = worker_queue.TakeWorkItem(); // Smallest unit of work.
+//       if (!work_item)
+//         return:
+//       ProcessWork(work_item);
+//     }
+//   }
+//
+// |max_concurrency_callback| controls the maximum number of threads calling
+// |worker_task| concurrently. |worker_task| is only invoked if the number of
+// threads previously running |worker_task| was less than the value returned by
+// |max_concurrency_callback|. In general, |max_concurrency_callback| should
+// return the latest number of incomplete work items (smallest unit of work)
+// left to processed. JobHandle/JobDelegate::NotifyConcurrencyIncrease() *must*
+// be invoked shortly after |max_concurrency_callback| starts returning a value
+// larger than previously returned values. This usually happens when new work
+// items are added and the API user wants additional threads to invoke
+// |worker_task| concurrently. The callbacks may be called concurrently on any
+// thread until the job is complete. If the job handle is detached, the
+// callbacks may still be called, so they must not access global state that
+// could be destroyed.
+//
+// |traits| requirements:
+// - base::ThreadPool() must be specified.
+// - Extension traits (e.g. BrowserThread) cannot be specified.
+// - base::ThreadPolicy must be specified if the priority of the task runner
+//   will ever be increased from BEST_EFFORT.
+JobHandle BASE_EXPORT
+PostJob(const Location& from_here,
+        const TaskTraits& traits,
+        RepeatingCallback<void(JobDelegate*)> worker_task,
+        RepeatingCallback<size_t()> max_concurrency_callback);
+
 }  // namespace experimental
 }  // namespace base
 
diff --git a/base/task/post_job_unittest.cc b/base/task/post_job_unittest.cc
new file mode 100644
index 0000000..3fc3d6d
--- /dev/null
+++ b/base/task/post_job_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task/post_job.h"
+
+#include <atomic>
+
+#include "base/task/test_task_traits_extension.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/gtest_util.h"
+#include "base/test/task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(PostJobTest, PostJobSimple) {
+  test::TaskEnvironment task_environment;
+  std::atomic_size_t num_tasks_to_run(4);
+  auto handle = experimental::PostJob(
+      FROM_HERE, ThreadPool(),
+      BindLambdaForTesting(
+          [&](experimental::JobDelegate* delegate) { --num_tasks_to_run; }),
+      BindLambdaForTesting([&]() -> size_t { return num_tasks_to_run; }));
+  handle.Join();
+  DCHECK_EQ(num_tasks_to_run, 0U);
+}
+
+TEST(PostJobTest, PostJobExtension) {
+  testing::FLAGS_gtest_death_test_style = "threadsafe";
+  EXPECT_DCHECK_DEATH({
+    auto handle = experimental::PostJob(
+        FROM_HERE, TestExtensionBoolTrait(),
+        BindRepeating([](experimental::JobDelegate* delegate) {}),
+        BindRepeating([]() -> size_t { return 0; }));
+  });
+}
+
+}  // namespace base
\ No newline at end of file
diff --git a/base/task/post_task.cc b/base/task/post_task.cc
index 1e7e65c..244e5f6 100644
--- a/base/task/post_task.cc
+++ b/base/task/post_task.cc
@@ -41,6 +41,13 @@
 }
 
 TaskExecutor* GetTaskExecutorForTraits(const TaskTraits& traits) {
+  if (traits.use_current_thread()) {
+    TaskExecutor* executor = GetTaskExecutorForCurrentThread();
+    DCHECK(executor) << "Couldn't find a TaskExecutor for this thread. Note "
+                        "you can't use base::CurrentThread in a one-off "
+                        "base::ThreadPool task.";
+    return executor;
+  }
   TaskExecutor* executor = GetRegisteredTaskExecutorForTraits(traits);
   DCHECK(executor || ThreadPoolInstance::Get())
       << "Ref. Prerequisite section of post_task.h.\n\n"
diff --git a/base/task/post_task_unittest.cc b/base/task/post_task_unittest.cc
index f81f9d1..798eba8 100644
--- a/base/task/post_task_unittest.cc
+++ b/base/task/post_task_unittest.cc
@@ -225,6 +225,115 @@
   run_loop.Run();
 }
 
+TEST_F(PostTaskTestWithExecutor, ThreadPoolTaskRunnerCurrentThreadTrait) {
+  auto task_runner = CreateTaskRunner({ThreadPool()});
+  RunLoop run_loop;
+
+  EXPECT_TRUE(task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
+                                      // CurrentThread is meaningless in this
+                                      // context.
+                                      EXPECT_DCHECK_DEATH(
+                                          PostTask(FROM_HERE, {CurrentThread()},
+                                                   DoNothing()));
+                                      run_loop.Quit();
+                                    })));
+
+  run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+       ThreadPoolSequencedTaskRunnerCurrentThreadTrait) {
+  auto sequenced_task_runner = CreateSequencedTaskRunner({ThreadPool()});
+  RunLoop run_loop;
+
+  auto current_thread_task = BindLambdaForTesting([&]() {
+    EXPECT_TRUE(sequenced_task_runner->RunsTasksInCurrentSequence());
+    run_loop.Quit();
+  });
+
+  EXPECT_TRUE(sequenced_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        EXPECT_TRUE(
+            PostTask(FROM_HERE, {CurrentThread()}, current_thread_task));
+      })));
+
+  run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+       ThreadPoolSingleThreadTaskRunnerCurrentThreadTrait) {
+  auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()});
+  RunLoop run_loop;
+
+  auto current_thread_task = BindLambdaForTesting([&]() {
+    EXPECT_TRUE(single_thread_task_runner->RunsTasksInCurrentSequence());
+    run_loop.Quit();
+  });
+
+  EXPECT_TRUE(single_thread_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        EXPECT_TRUE(
+            PostTask(FROM_HERE, {CurrentThread()}, current_thread_task));
+      })));
+
+  run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor, ThreadPoolCurrentThreadChangePriority) {
+  auto single_thread_task_runner =
+      CreateSingleThreadTaskRunner({ThreadPool(), TaskPriority::USER_BLOCKING});
+  RunLoop run_loop;
+
+  auto current_thread_task = BindLambdaForTesting([&]() {
+    EXPECT_TRUE(single_thread_task_runner->RunsTasksInCurrentSequence());
+    run_loop.Quit();
+  });
+
+  EXPECT_TRUE(single_thread_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        // We should be able to request a priority change, although it may be
+        // ignored.
+        EXPECT_TRUE(PostTask(FROM_HERE,
+                             {CurrentThread(), TaskPriority::USER_VISIBLE},
+                             current_thread_task));
+      })));
+
+  run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+       ThreadPoolCurrentThreadCantChangeShutdownBehavior) {
+  auto single_thread_task_runner = CreateSingleThreadTaskRunner(
+      {ThreadPool(), TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+  RunLoop run_loop;
+
+  EXPECT_TRUE(single_thread_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        EXPECT_DCHECK_DEATH(PostTask(
+            FROM_HERE, {CurrentThread(), TaskShutdownBehavior::BLOCK_SHUTDOWN},
+            DoNothing()));
+        run_loop.Quit();
+      })));
+
+  run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+       ThreadPoolCurrentThreadCantSetSyncPrimitivesInNonSyncTaskRunner) {
+  auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()});
+  RunLoop run_loop;
+
+  EXPECT_TRUE(single_thread_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        EXPECT_DCHECK_DEATH(
+            PostTask(FROM_HERE, {CurrentThread(), WithBaseSyncPrimitives()},
+                     DoNothing()));
+        run_loop.Quit();
+      })));
+
+  run_loop.Run();
+}
+
 TEST_F(PostTaskTestWithExecutor, RegisterExecutorTwice) {
   testing::FLAGS_gtest_death_test_style = "threadsafe";
   EXPECT_DCHECK_DEATH(
diff --git a/base/task/sequence_manager/sequence_manager.h b/base/task/sequence_manager/sequence_manager.h
index 8ec2cf3..6e1cb0b 100644
--- a/base/task/sequence_manager/sequence_manager.h
+++ b/base/task/sequence_manager/sequence_manager.h
@@ -20,6 +20,7 @@
 namespace base {
 
 class MessagePump;
+class TaskObserver;
 
 namespace sequence_manager {
 
@@ -248,6 +249,14 @@
   virtual std::unique_ptr<NativeWorkHandle> OnNativeWorkPending(
       TaskQueue::QueuePriority priority) = 0;
 
+  // Adds an observer which reports task execution. Can only be called on the
+  // same thread that |this| is running on.
+  virtual void AddTaskObserver(TaskObserver* task_observer) = 0;
+
+  // Removes an observer which reports task execution. Can only be called on the
+  // same thread that |this| is running on.
+  virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0;
+
  protected:
   virtual std::unique_ptr<internal::TaskQueueImpl> CreateTaskQueueImpl(
       const TaskQueue::Spec& spec) = 0;
diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h
index d490475..1ee7944 100644
--- a/base/task/sequence_manager/sequence_manager_impl.h
+++ b/base/task/sequence_manager/sequence_manager_impl.h
@@ -42,8 +42,6 @@
 
 namespace base {
 
-class TaskObserver;
-
 namespace trace_event {
 class ConvertableToTraceFormat;
 }  // namespace trace_event
@@ -128,6 +126,8 @@
   std::string DescribeAllPendingTasks() const override;
   std::unique_ptr<NativeWorkHandle> OnNativeWorkPending(
       TaskQueue::QueuePriority priority) override;
+  void AddTaskObserver(TaskObserver* task_observer) override;
+  void RemoveTaskObserver(TaskObserver* task_observer) override;
 
   // SequencedTaskSource implementation:
   Task* SelectNextTask() override;
@@ -136,8 +136,6 @@
   bool HasPendingHighResolutionTasks() override;
   bool OnSystemIdle() override;
 
-  void AddTaskObserver(TaskObserver* task_observer);
-  void RemoveTaskObserver(TaskObserver* task_observer);
   void AddDestructionObserver(
       MessageLoopCurrent::DestructionObserver* destruction_observer);
   void RemoveDestructionObserver(
diff --git a/base/task/single_thread_task_executor_unittest.cc b/base/task/single_thread_task_executor_unittest.cc
index 8919298b..b64294f 100644
--- a/base/task/single_thread_task_executor_unittest.cc
+++ b/base/task/single_thread_task_executor_unittest.cc
@@ -5,6 +5,7 @@
 #include "base/task/single_thread_task_executor.h"
 
 #include "base/run_loop.h"
+#include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -42,4 +43,16 @@
   run_loop.Run();
 }
 
+TEST(SingleThreadTaskExecutorTest, CurrentThread) {
+  SingleThreadTaskExecutor single_thread_task_executor;
+
+  EXPECT_EQ(single_thread_task_executor.task_runner(),
+            base::CreateSingleThreadTaskRunner({base::CurrentThread()}));
+
+  // There's only one task queue so priority is ignored.
+  EXPECT_EQ(single_thread_task_executor.task_runner(),
+            base::CreateSingleThreadTaskRunner(
+                {base::CurrentThread(), base::TaskPriority::BEST_EFFORT}));
+}
+
 }  // namespace base
diff --git a/base/task/task_executor.cc b/base/task/task_executor.cc
index 0bb8651..355fa3f 100644
--- a/base/task/task_executor.cc
+++ b/base/task/task_executor.cc
@@ -74,4 +74,4 @@
   return nullptr;
 }
 
-}  // namespace base
\ No newline at end of file
+}  // namespace base
diff --git a/base/task/task_traits.h b/base/task/task_traits.h
index c1e52ed..03abf17 100644
--- a/base/task/task_traits.h
+++ b/base/task/task_traits.h
@@ -185,6 +185,14 @@
 // between tasks, see base::PostTask::CreateSequencedTaskRunner.
 struct ThreadPool {};
 
+// Tasks and task runners with this thread will run tasks on the virtual thread
+// (sequence) they are posted/created from. Other traits may be specified
+// alongside this one to refine properties for the associated tasks
+// (e.g. base::TaskPriority or content::BrowserTaskType) as long as those traits
+// are compatible with the current thread (e.g. cannot specify base::MayBlock()
+// on a non-blocking thread or alter base::TaskShutdownBehavior).
+struct CurrentThread {};
+
 // Describes metadata for a single task or a group of tasks.
 class BASE_EXPORT TaskTraits {
  public:
@@ -196,6 +204,7 @@
     ValidTrait(MayBlock);
     ValidTrait(WithBaseSyncPrimitives);
     ValidTrait(ThreadPool);
+    ValidTrait(CurrentThread);
   };
 
   // Invoking this constructor without arguments produces TaskTraits that are
@@ -255,15 +264,22 @@
         may_block_(trait_helpers::HasTrait<MayBlock, ArgTypes...>()),
         with_base_sync_primitives_(
             trait_helpers::HasTrait<WithBaseSyncPrimitives, ArgTypes...>()),
-        use_thread_pool_(trait_helpers::HasTrait<ThreadPool, ArgTypes...>()) {
+        use_thread_pool_(trait_helpers::HasTrait<ThreadPool, ArgTypes...>()),
+        use_current_thread_(
+            trait_helpers::HasTrait<CurrentThread, ArgTypes...>()) {
     constexpr bool has_thread_pool =
         trait_helpers::HasTrait<ThreadPool, ArgTypes...>();
     constexpr bool has_extension =
         !trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value;
+    constexpr bool has_current_thread =
+        trait_helpers::HasTrait<CurrentThread, ArgTypes...>();
     static_assert(
-        has_thread_pool ^ has_extension,
+        !has_current_thread || !has_thread_pool,
+        "base::CurrentThread is mutually exclusive with base::ThreadPool");
+    static_assert(
+        has_thread_pool ^ has_extension || has_current_thread,
         "Traits must explicitly specify a destination (e.g. ThreadPool or a "
-        "named thread like BrowserThread)");
+        "named thread like BrowserThread, or CurrentThread)");
   }
 
   constexpr TaskTraits(const TaskTraits& other) = default;
@@ -271,14 +287,15 @@
 
   // TODO(eseckler): Default the comparison operator once C++20 arrives.
   bool operator==(const TaskTraits& other) const {
-    static_assert(sizeof(TaskTraits) == 15,
+    static_assert(sizeof(TaskTraits) == 16,
                   "Update comparison operator when TaskTraits change");
     return extension_ == other.extension_ && priority_ == other.priority_ &&
            shutdown_behavior_ == other.shutdown_behavior_ &&
            thread_policy_ == other.thread_policy_ &&
            may_block_ == other.may_block_ &&
            with_base_sync_primitives_ == other.with_base_sync_primitives_ &&
-           use_thread_pool_ == other.use_thread_pool_;
+           use_thread_pool_ == other.use_thread_pool_ &&
+           use_current_thread_ == other.use_current_thread_;
   }
 
   // Sets the priority of tasks with these traits to |priority|.
@@ -335,6 +352,10 @@
   // Returns true if tasks with these traits execute on the thread pool.
   constexpr bool use_thread_pool() const { return use_thread_pool_; }
 
+  // Returns true if tasks with these traits execute on the virtual thread
+  // (sequence) they are posted/created from.
+  constexpr bool use_current_thread() const { return use_current_thread_; }
+
   uint8_t extension_id() const { return extension_.extension_id; }
 
   // Access the extension data by parsing it into the provided extension type.
@@ -353,6 +374,7 @@
              TaskPriority priority,
              bool may_block,
              bool use_thread_pool,
+             bool use_current_thread,
              TaskTraitsExtensionStorage extension)
       : extension_(extension),
         priority_(static_cast<uint8_t>(priority) |
@@ -362,8 +384,9 @@
         thread_policy_(static_cast<uint8_t>(ThreadPolicy::PREFER_BACKGROUND)),
         may_block_(may_block),
         with_base_sync_primitives_(false),
-        use_thread_pool_(use_thread_pool) {
-    static_assert(sizeof(TaskTraits) == 15, "Keep this constructor up to date");
+        use_thread_pool_(use_thread_pool),
+        use_current_thread_(use_current_thread) {
+    static_assert(sizeof(TaskTraits) == 16, "Keep this constructor up to date");
   }
 
   // This bit is set in |priority_|, |shutdown_behavior_| and |thread_policy_|
@@ -378,6 +401,7 @@
   bool may_block_;
   bool with_base_sync_primitives_;
   bool use_thread_pool_;
+  bool use_current_thread_;
 };
 
 // Returns string literals for the enums defined in this file. These methods
diff --git a/base/task/task_traits_unittest.nc b/base/task/task_traits_unittest.nc
index 8755ca0..73fc7be55 100644
--- a/base/task/task_traits_unittest.nc
+++ b/base/task/task_traits_unittest.nc
@@ -26,6 +26,8 @@
                                TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
 #elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE)  // [r"no matching constructor for initialization of 'const base::TaskTraits'"]
 constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, true};
+#elif defined(NCTEST_TASK_TRAITS_CURRENT_THREAD_AND_THREADPOOL)  // [r"base::CurrentThread is mutually exclusive with base::ThreadPool"]
+constexpr TaskTraits traits = {ThreadPool(), CurrentThread()};
 #endif
 
 }  // namespace base
diff --git a/base/task/thread_pool/job_task_source.h b/base/task/thread_pool/job_task_source.h
index cf0c76d..5783867 100644
--- a/base/task/thread_pool/job_task_source.h
+++ b/base/task/thread_pool/job_task_source.h
@@ -38,8 +38,9 @@
                 RepeatingCallback<size_t()> max_concurrency_callback,
                 PooledTaskRunnerDelegate* delegate);
 
-  experimental::JobHandle GetHandleForTesting() {
-    return experimental::JobHandle(this);
+  static experimental::JobHandle CreateJobHandle(
+      scoped_refptr<internal::JobTaskSource> task_source) {
+    return experimental::JobHandle(std::move(task_source));
   }
 
   // Notifies this task source that max concurrency was increased, and the
diff --git a/base/task/thread_pool/task_tracker.cc b/base/task/thread_pool/task_tracker.cc
index c6f664b..a74f140 100644
--- a/base/task/thread_pool/task_tracker.cc
+++ b/base/task/thread_pool/task_tracker.cc
@@ -120,9 +120,11 @@
   // |sequenced_task_runner| and |single_thread_task_runner| must outlive this
   // EphemeralTaskExecutor.
   EphemeralTaskExecutor(SequencedTaskRunner* sequenced_task_runner,
-                        SingleThreadTaskRunner* single_thread_task_runner)
+                        SingleThreadTaskRunner* single_thread_task_runner,
+                        const TaskTraits* sequence_traits)
       : sequenced_task_runner_(sequenced_task_runner),
-        single_thread_task_runner_(single_thread_task_runner) {
+        single_thread_task_runner_(single_thread_task_runner),
+        sequence_traits_(sequence_traits) {
     SetTaskExecutorForCurrentThread(this);
   }
 
@@ -135,23 +137,27 @@
                        const TaskTraits& traits,
                        OnceClosure task,
                        TimeDelta delay) override {
+    CheckTraitsCompatibleWithSequenceTraits(traits);
     return sequenced_task_runner_->PostDelayedTask(from_here, std::move(task),
                                                    delay);
   }
 
   scoped_refptr<TaskRunner> CreateTaskRunner(
       const TaskTraits& traits) override {
+    CheckTraitsCompatibleWithSequenceTraits(traits);
     return sequenced_task_runner_;
   }
 
   scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunner(
       const TaskTraits& traits) override {
+    CheckTraitsCompatibleWithSequenceTraits(traits);
     return sequenced_task_runner_;
   }
 
   scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
       const TaskTraits& traits,
       SingleThreadTaskRunnerThreadMode thread_mode) override {
+    CheckTraitsCompatibleWithSequenceTraits(traits);
     return single_thread_task_runner_;
   }
 
@@ -159,13 +165,30 @@
   scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner(
       const TaskTraits& traits,
       SingleThreadTaskRunnerThreadMode thread_mode) override {
+    CheckTraitsCompatibleWithSequenceTraits(traits);
     return single_thread_task_runner_;
   }
 #endif  // defined(OS_WIN)
 
  private:
-  SequencedTaskRunner* sequenced_task_runner_;
-  SingleThreadTaskRunner* single_thread_task_runner_;
+  // Currently ignores |traits.priority()|.
+  void CheckTraitsCompatibleWithSequenceTraits(const TaskTraits& traits) {
+    if (traits.shutdown_behavior_set_explicitly()) {
+      DCHECK_EQ(traits.shutdown_behavior(),
+                sequence_traits_->shutdown_behavior());
+    }
+
+    DCHECK(!traits.may_block() ||
+           traits.may_block() == sequence_traits_->may_block());
+
+    DCHECK(!traits.with_base_sync_primitives() ||
+           traits.with_base_sync_primitives() ==
+               sequence_traits_->with_base_sync_primitives());
+  }
+
+  SequencedTaskRunner* const sequenced_task_runner_;
+  SingleThreadTaskRunner* const single_thread_task_runner_;
+  const TaskTraits* const sequence_traits_;
 };
 
 }  // namespace
@@ -547,7 +570,7 @@
             static_cast<SequencedTaskRunner*>(task_source->task_runner()));
         ephemiral_task_executor.emplace(
             static_cast<SequencedTaskRunner*>(task_source->task_runner()),
-            nullptr);
+            nullptr, &traits);
         break;
       case TaskSourceExecutionMode::kSingleThread:
         DCHECK(task_source->task_runner());
@@ -555,7 +578,8 @@
             static_cast<SingleThreadTaskRunner*>(task_source->task_runner()));
         ephemiral_task_executor.emplace(
             static_cast<SequencedTaskRunner*>(task_source->task_runner()),
-            static_cast<SingleThreadTaskRunner*>(task_source->task_runner()));
+            static_cast<SingleThreadTaskRunner*>(task_source->task_runner()),
+            &traits);
         break;
     }
 
diff --git a/base/task/thread_pool/thread_group_unittest.cc b/base/task/thread_pool/thread_group_unittest.cc
index d09567b..3ed430a 100644
--- a/base/task/thread_pool/thread_group_unittest.cc
+++ b/base/task/thread_pool/thread_group_unittest.cc
@@ -681,8 +681,9 @@
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
       FROM_HERE, {ThreadPool()}, &mock_pooled_task_runner_delegate_);
 
-  experimental::JobHandle job_handle = task_source->GetHandleForTesting();
   mock_pooled_task_runner_delegate_.EnqueueJobTaskSource(task_source);
+  experimental::JobHandle job_handle =
+      internal::JobTaskSource::CreateJobHandle(task_source);
 
   // Wait for at least 1 task to start running.
   {
@@ -796,8 +797,9 @@
   scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
       FROM_HERE, {ThreadPool()}, &mock_pooled_task_runner_delegate_);
 
-  experimental::JobHandle job_handle = task_source->GetHandleForTesting();
   mock_pooled_task_runner_delegate_.EnqueueJobTaskSource(task_source);
+  experimental::JobHandle job_handle =
+      internal::JobTaskSource::CreateJobHandle(task_source);
   job_handle.Join();
   // All worker tasks should complete before Join() returns.
   EXPECT_EQ(0U, job_task->GetMaxConcurrency());
diff --git a/base/test/task_environment.cc b/base/test/task_environment.cc
index 99b1ebe..3f9d6af 100644
--- a/base/test/task_environment.cc
+++ b/base/test/task_environment.cc
@@ -19,6 +19,7 @@
 #include "base/task/post_task.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
 #include "base/task/sequence_manager/time_domain.h"
+#include "base/task/simple_task_executor.h"
 #include "base/task/thread_pool/thread_pool_impl.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/bind_test_util.h"
@@ -395,6 +396,7 @@
             .SetTimeDomain(mock_time_domain_.get()));
     task_runner_ = task_queue_->task_runner();
     sequence_manager_->SetDefaultTaskRunner(task_runner_);
+    simple_task_executor_ = std::make_unique<SimpleTaskExecutor>(task_runner_);
     CHECK(base::ThreadTaskRunnerHandle::IsSet())
         << "ThreadTaskRunnerHandle should've been set now.";
     CompleteInitialization();
diff --git a/base/test/task_environment.h b/base/test/task_environment.h
index c222141..ac45058 100644
--- a/base/test/task_environment.h
+++ b/base/test/task_environment.h
@@ -23,6 +23,7 @@
 
 class Clock;
 class FileDescriptorWatcher;
+class SimpleTaskExecutor;
 class TickClock;
 
 namespace subtle {
@@ -348,6 +349,9 @@
 
   std::unique_ptr<bool> owns_instance_ = std::make_unique<bool>(true);
 
+  // To support base::CurrentThread().
+  std::unique_ptr<SimpleTaskExecutor> simple_task_executor_;
+
   // Used to verify thread-affinity of operations that must occur on the main
   // thread. This is the case for anything that modifies or drives the
   // |sequence_manager_|.
diff --git a/base/test/task_environment_unittest.cc b/base/test/task_environment_unittest.cc
index b82f49d..e6a929e 100644
--- a/base/test/task_environment_unittest.cc
+++ b/base/test/task_environment_unittest.cc
@@ -1188,5 +1188,16 @@
   EXPECT_EQ(TimeTicks::Now(), start_time + kDelay);
 }
 
+TEST_F(TaskEnvironmentTest, CurrentThread) {
+  SingleThreadTaskEnvironment task_environment;
+  RunLoop run_loop;
+
+  PostTask(FROM_HERE, {CurrentThread()}, BindLambdaForTesting([&]() {
+             PostTask(FROM_HERE, {CurrentThread()}, run_loop.QuitClosure());
+           }));
+
+  run_loop.Run();
+}
+
 }  // namespace test
 }  // namespace base
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index ab98632..5f17228 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/task/post_task.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
 #include "base/task/task_executor.h"
 #include "base/test/bind_test_util.h"
@@ -530,8 +531,7 @@
   Thread a("GetTaskExecutorForCurrentThread");
   ASSERT_TRUE(a.Start());
 
-  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
-                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  base::WaitableEvent event;
 
   a.task_runner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
@@ -543,6 +543,28 @@
   a.Stop();
 }
 
+TEST_F(ThreadTest, CurrentThread) {
+  Thread a("CurrentThread");
+  ASSERT_TRUE(a.Start());
+
+  base::WaitableEvent event;
+
+  a.task_runner()->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        EXPECT_EQ(a.task_runner(),
+                  base::CreateSingleThreadTaskRunner({base::CurrentThread()}));
+
+        // There's only a single task runner so base::TaskPriority is ignored.
+        EXPECT_EQ(a.task_runner(), base::CreateSingleThreadTaskRunner(
+                                       {base::CurrentThread(),
+                                        base::TaskPriority::BEST_EFFORT}));
+        event.Signal();
+      }));
+
+  event.Wait();
+  a.Stop();
+}
+
 namespace {
 
 class SequenceManagerThreadDelegate : public Thread::Delegate {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index d388999..75c7271 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8900101731286284048
\ No newline at end of file
+8900075814043677168
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index d544d1e..3cb6c2e 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8900103511840373520
\ No newline at end of file
+8900078822768978496
\ No newline at end of file
diff --git a/build_overrides/shaderc.gni b/build_overrides/shaderc.gni
index a67f427..6cce450 100644
--- a/build_overrides/shaderc.gni
+++ b/build_overrides/shaderc.gni
@@ -5,3 +5,6 @@
 shaderc_glslang_dir = "//third_party/glslang/src"
 shaderc_spirv_tools_dir = "//third_party/SPIRV-Tools/src"
 shaderc_spirv_cross_dir = "//third_party/dawn/third_party"
+shaderc_spirv_headers_dir = "//third_party/spirv-headers/src"
+
+shaderc_enable_spvc_parser = false
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 305a667..fe3b9bb 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -110,9 +110,10 @@
     "layers/recording_source.h",
     "layers/render_surface_impl.cc",
     "layers/render_surface_impl.h",
+    "layers/scrollbar_layer_base.cc",
+    "layers/scrollbar_layer_base.h",
     "layers/scrollbar_layer_impl_base.cc",
     "layers/scrollbar_layer_impl_base.h",
-    "layers/scrollbar_layer_interface.h",
     "layers/solid_color_layer.cc",
     "layers/solid_color_layer.h",
     "layers/solid_color_layer_impl.cc",
diff --git a/cc/input/scrollbar_animation_controller_unittest.cc b/cc/input/scrollbar_animation_controller_unittest.cc
index b00998f..d88bbdb 100644
--- a/cc/input/scrollbar_animation_controller_unittest.cc
+++ b/cc/input/scrollbar_animation_controller_unittest.cc
@@ -76,15 +76,12 @@
     const int kTrackStart = 0;
     const int kTrackLength = 100;
     const bool kIsLeftSideVerticalScrollbar = false;
-    const bool kIsOverlayScrollbar = true;
 
     scroll_layer_ = AddLayer<LayerImpl>();
     h_scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>(
-        HORIZONTAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar,
-        kIsOverlayScrollbar);
+        HORIZONTAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar);
     v_scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>(
-        VERTICAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar,
-        kIsOverlayScrollbar);
+        VERTICAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar);
     SetElementIdsForTesting();
 
     clip_layer_ = root_layer();
@@ -1380,13 +1377,12 @@
   void SetUp() override {
     const int kTrackStart = 0;
     const bool kIsLeftSideVerticalScrollbar = false;
-    const bool kIsOverlayScrollbar = true;  // Allow opacity animations.
 
     LayerImpl* root = root_layer();
     scroll_layer_ = AddLayer<LayerImpl>();
     scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>(
         orientation(), kThumbThickness, kTrackStart,
-        kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar);
+        kIsLeftSideVerticalScrollbar);
     SetElementIdsForTesting();
 
     scroll_layer_->SetBounds(gfx::Size(200, 200));
diff --git a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
index 6b8668b..b851a58 100644
--- a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
+++ b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
@@ -70,11 +70,9 @@
     const int kTrackStart = 0;
     const int kTrackLength = 100;
     const bool kIsLeftSideVerticalScrollbar = false;
-    const bool kIsOverlayScrollbar = true;
 
     scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>(
-        HORIZONTAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar,
-        kIsOverlayScrollbar);
+        HORIZONTAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar);
 
     scrollbar_layer_->SetBounds(gfx::Size(kThumbThickness, kTrackLength));
     scrollbar_layer_->SetScrollElementId(scroll_layer->element_id());
diff --git a/cc/layers/painted_overlay_scrollbar_layer.cc b/cc/layers/painted_overlay_scrollbar_layer.cc
index 5a9e160..d46146e 100644
--- a/cc/layers/painted_overlay_scrollbar_layer.cc
+++ b/cc/layers/painted_overlay_scrollbar_layer.cc
@@ -44,31 +44,20 @@
   DCHECK(scrollbar_->HasThumb());
   DCHECK(scrollbar_->IsOverlay());
   DCHECK(scrollbar_->UsesNinePatchThumbResource());
-  SetIsScrollbar(true);
 }
 
 PaintedOverlayScrollbarLayer::~PaintedOverlayScrollbarLayer() = default;
 
-void PaintedOverlayScrollbarLayer::SetScrollElementId(ElementId element_id) {
-  if (element_id == scroll_element_id_)
-    return;
-
-  scroll_element_id_ = element_id;
-  SetNeedsCommit();
-}
-
 bool PaintedOverlayScrollbarLayer::OpacityCanAnimateOnImplThread() const {
   return scrollbar_->IsOverlay();
 }
 
 void PaintedOverlayScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
-  Layer::PushPropertiesTo(layer);
+  ScrollbarLayerBase::PushPropertiesTo(layer);
 
   PaintedOverlayScrollbarLayerImpl* scrollbar_layer =
       static_cast<PaintedOverlayScrollbarLayerImpl*>(layer);
 
-  scrollbar_layer->SetScrollElementId(scroll_element_id_);
-
   scrollbar_layer->SetThumbThickness(thumb_thickness_);
   scrollbar_layer->SetThumbLength(thumb_length_);
   if (scrollbar_->Orientation() == HORIZONTAL) {
@@ -105,7 +94,7 @@
     track_resource_.reset();
   }
 
-  Layer::SetLayerTreeHost(host);
+  ScrollbarLayerBase::SetLayerTreeHost(host);
 }
 
 gfx::Rect PaintedOverlayScrollbarLayer::OriginThumbRectForPainting() const {
diff --git a/cc/layers/painted_overlay_scrollbar_layer.h b/cc/layers/painted_overlay_scrollbar_layer.h
index 06b4926..4838fb8 100644
--- a/cc/layers/painted_overlay_scrollbar_layer.h
+++ b/cc/layers/painted_overlay_scrollbar_layer.h
@@ -8,15 +8,14 @@
 #include "cc/cc_export.h"
 #include "cc/input/scrollbar.h"
 #include "cc/layers/layer.h"
-#include "cc/layers/scrollbar_layer_interface.h"
+#include "cc/layers/scrollbar_layer_base.h"
 #include "cc/resources/scoped_ui_resource.h"
 
 namespace cc {
 
 // For composited overlay scrollbars with nine-patch thumb. For overlay
 // scrollbars whose thumb is not nine-patch, use PaintedScrollbarLayer.
-class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerInterface,
-                                               public Layer {
+class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerBase {
  public:
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
 
@@ -27,11 +26,6 @@
       std::unique_ptr<Scrollbar> scrollbar);
 
   bool OpacityCanAnimateOnImplThread() const override;
-
-  // ScrollbarLayerInterface
-  void SetScrollElementId(ElementId element_id) override;
-
-  // Layer interface
   bool Update() override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer) override;
@@ -56,7 +50,6 @@
   bool PaintTickmarks();
 
   std::unique_ptr<Scrollbar> scrollbar_;
-  ElementId scroll_element_id_;
 
   int thumb_thickness_;
   int thumb_length_;
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index 3f23827..eca1517 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -34,31 +34,20 @@
       thumb_length_(scrollbar_->ThumbLength()),
       is_overlay_(scrollbar_->IsOverlay()),
       has_thumb_(scrollbar_->HasThumb()),
-      thumb_opacity_(scrollbar_->ThumbOpacity()) {
-  SetIsScrollbar(true);
-}
+      thumb_opacity_(scrollbar_->ThumbOpacity()) {}
 
 PaintedScrollbarLayer::~PaintedScrollbarLayer() = default;
 
-void PaintedScrollbarLayer::SetScrollElementId(ElementId element_id) {
-  if (element_id == scroll_element_id_)
-    return;
-
-  scroll_element_id_ = element_id;
-  SetNeedsCommit();
-}
-
 bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
   return scrollbar_->IsOverlay();
 }
 
 void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
-  Layer::PushPropertiesTo(layer);
+  ScrollbarLayerBase::PushPropertiesTo(layer);
 
   PaintedScrollbarLayerImpl* scrollbar_layer =
       static_cast<PaintedScrollbarLayerImpl*>(layer);
 
-  scrollbar_layer->SetScrollElementId(scroll_element_id_);
   scrollbar_layer->set_internal_contents_scale_and_bounds(
       internal_contents_scale_, internal_content_bounds_);
 
@@ -97,7 +86,7 @@
     thumb_resource_ = nullptr;
   }
 
-  Layer::SetLayerTreeHost(host);
+  ScrollbarLayerBase::SetLayerTreeHost(host);
 }
 
 gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
@@ -165,7 +154,7 @@
 bool PaintedScrollbarLayer::Update() {
   {
     auto ignore_set_needs_commit = IgnoreSetNeedsCommit();
-    Layer::Update();
+    ScrollbarLayerBase::Update();
     UpdateInternalContentScale();
   }
 
diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h
index c9ea59b..444c042 100644
--- a/cc/layers/painted_scrollbar_layer.h
+++ b/cc/layers/painted_scrollbar_layer.h
@@ -8,13 +8,12 @@
 #include "cc/cc_export.h"
 #include "cc/input/scrollbar.h"
 #include "cc/layers/layer.h"
-#include "cc/layers/scrollbar_layer_interface.h"
+#include "cc/layers/scrollbar_layer_base.h"
 #include "cc/resources/scoped_ui_resource.h"
 
 namespace cc {
 
-class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface,
-                                        public Layer {
+class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerBase {
  public:
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
 
@@ -25,11 +24,6 @@
   PaintedScrollbarLayer& operator=(const PaintedScrollbarLayer&) = delete;
 
   bool OpacityCanAnimateOnImplThread() const override;
-
-  // ScrollbarLayerInterface
-  void SetScrollElementId(ElementId element_id) override;
-
-  // Layer interface
   bool Update() override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer) override;
diff --git a/cc/layers/scrollbar_layer_base.cc b/cc/layers/scrollbar_layer_base.cc
new file mode 100644
index 0000000..14c743b
--- /dev/null
+++ b/cc/layers/scrollbar_layer_base.cc
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/scrollbar_layer_base.h"
+
+#include "cc/layers/scrollbar_layer_impl_base.h"
+
+namespace cc {
+
+ScrollbarLayerBase::ScrollbarLayerBase() {
+  SetIsScrollbar(true);
+}
+
+ScrollbarLayerBase::~ScrollbarLayerBase() = default;
+
+void ScrollbarLayerBase::SetScrollElementId(ElementId element_id) {
+  if (element_id == scroll_element_id_)
+    return;
+
+  scroll_element_id_ = element_id;
+  SetNeedsCommit();
+}
+
+void ScrollbarLayerBase::PushPropertiesTo(LayerImpl* layer) {
+  Layer::PushPropertiesTo(layer);
+  static_cast<ScrollbarLayerImplBase*>(layer)->SetScrollElementId(
+      scroll_element_id_);
+}
+
+}  // namespace cc
diff --git a/cc/layers/scrollbar_layer_base.h b/cc/layers/scrollbar_layer_base.h
new file mode 100644
index 0000000..00c5dfa
--- /dev/null
+++ b/cc/layers/scrollbar_layer_base.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_LAYERS_SCROLLBAR_LAYER_BASE_H_
+#define CC_LAYERS_SCROLLBAR_LAYER_BASE_H_
+
+#include "cc/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+class CC_EXPORT ScrollbarLayerBase : public Layer {
+ public:
+  void SetScrollElementId(ElementId element_id);
+
+  void PushPropertiesTo(LayerImpl* layer) override;
+
+ protected:
+  ScrollbarLayerBase();
+  ~ScrollbarLayerBase() override;
+
+ private:
+  ElementId scroll_element_id_;
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_SCROLLBAR_LAYER_BASE_H_
diff --git a/cc/layers/scrollbar_layer_interface.h b/cc/layers/scrollbar_layer_interface.h
deleted file mode 100644
index 661028e4..0000000
--- a/cc/layers/scrollbar_layer_interface.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2013 The Chromium 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 CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_
-#define CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_
-
-#include "cc/cc_export.h"
-#include "cc/input/scrollbar.h"
-
-namespace cc {
-
-class CC_EXPORT ScrollbarLayerInterface {
- public:
-  ScrollbarLayerInterface(const ScrollbarLayerInterface&) = delete;
-  ScrollbarLayerInterface& operator=(const ScrollbarLayerInterface&) = delete;
-
-  virtual void SetScrollElementId(ElementId element_id) = 0;
-
- protected:
-  ScrollbarLayerInterface() {}
-  virtual ~ScrollbarLayerInterface() {}
-};
-
-}  // namespace cc
-
-#endif  // CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index b9a03eb..98a5011c 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -13,7 +13,7 @@
 #include "cc/layers/painted_overlay_scrollbar_layer.h"
 #include "cc/layers/painted_scrollbar_layer.h"
 #include "cc/layers/painted_scrollbar_layer_impl.h"
-#include "cc/layers/scrollbar_layer_interface.h"
+#include "cc/layers/scrollbar_layer_base.h"
 #include "cc/layers/solid_color_scrollbar_layer.h"
 #include "cc/layers/solid_color_scrollbar_layer_impl.h"
 #include "cc/resources/ui_resource_manager.h"
@@ -144,19 +144,16 @@
       int track_start) {
     scoped_refptr<Layer> layer_tree_root = Layer::Create();
     scoped_refptr<Layer> child1 = Layer::Create();
-    scoped_refptr<Layer> child2;
+    scoped_refptr<ScrollbarLayerBase> child2;
     if (use_solid_color_scrollbar) {
       const bool kIsLeftSideVerticalScrollbar = false;
       child2 = SolidColorScrollbarLayer::Create(scrollbar->Orientation(),
                                                 thumb_thickness, track_start,
                                                 kIsLeftSideVerticalScrollbar);
-      static_cast<SolidColorScrollbarLayer*>(child2.get())
-          ->SetScrollElementId(child1->element_id());
     } else {
       child2 = PaintedScrollbarLayer::Create(std::move(scrollbar));
-      static_cast<PaintedScrollbarLayer*>(child2.get())
-          ->SetScrollElementId(child1->element_id());
     }
+    child2->SetScrollElementId(child1->element_id());
     layer_tree_root->AddChild(child1);
     layer_tree_root->InsertChild(child2, reverse_order ? 0 : 1);
     scrollbar_layer_id_ = reverse_order ? child1->id() : child2->id();
@@ -822,12 +819,11 @@
   const int kTrackStart = 0;
   const int kThumbThickness = 10;
   const bool kIsLeftSideVerticalScrollbar = false;
-  const bool kIsOverlayScrollbar = false;
 
   SolidColorScrollbarLayerImpl* scrollbar_layer =
-      impl.AddLayer<SolidColorScrollbarLayerImpl>(
-          HORIZONTAL, kThumbThickness, kTrackStart,
-          kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar);
+      impl.AddLayer<SolidColorScrollbarLayerImpl>(HORIZONTAL, kThumbThickness,
+                                                  kTrackStart,
+                                                  kIsLeftSideVerticalScrollbar);
 
   scrollbar_layer->SetScrollElementId(scroll_layer->element_id());
   scroll_layer->SetScrollable(gfx::Size(980, 980));
@@ -863,11 +859,10 @@
   const int kTrackStart = 0;
   const int kThumbThickness = 10;
   const bool kIsLeftSideVerticalScrollbar = false;
-  const bool kIsOverlayScrollbar = false;
   SolidColorScrollbarLayerImpl* scrollbar_layer =
-      impl.AddLayer<SolidColorScrollbarLayerImpl>(
-          HORIZONTAL, kThumbThickness, kTrackStart,
-          kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar);
+      impl.AddLayer<SolidColorScrollbarLayerImpl>(HORIZONTAL, kThumbThickness,
+                                                  kTrackStart,
+                                                  kIsLeftSideVerticalScrollbar);
   scrollbar_layer->SetScrollElementId(scroll_layer->element_id());
   EXPECT_TRUE(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
   impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
@@ -951,24 +946,13 @@
     const int kThumbThickness = 3;
     const int kTrackStart = 0;
     const bool kIsLeftSideVerticalScrollbar = false;
-    const bool kIsOverlayScrollbar = false;
 
-    horizontal_scrollbar_layer_ =
-        SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(),
-                                             1,
-                                             HORIZONTAL,
-                                             kThumbThickness,
-                                             kTrackStart,
-                                             kIsLeftSideVerticalScrollbar,
-                                             kIsOverlayScrollbar);
-    vertical_scrollbar_layer_ =
-        SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(),
-                                             2,
-                                             VERTICAL,
-                                             kThumbThickness,
-                                             kTrackStart,
-                                             kIsLeftSideVerticalScrollbar,
-                                             kIsOverlayScrollbar);
+    horizontal_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
+        host_impl_->active_tree(), 1, HORIZONTAL, kThumbThickness, kTrackStart,
+        kIsLeftSideVerticalScrollbar);
+    vertical_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
+        host_impl_->active_tree(), 2, VERTICAL, kThumbThickness, kTrackStart,
+        kIsLeftSideVerticalScrollbar);
   }
 
  protected:
@@ -1054,7 +1038,7 @@
     std::unique_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false));
     scoped_refptr<Layer> layer_tree_root = Layer::Create();
     scoped_refptr<Layer> content_layer = Layer::Create();
-    scoped_refptr<Layer> scrollbar_layer;
+    scoped_refptr<ScrollbarLayerBase> scrollbar_layer;
     if (use_solid_color_scrollbar) {
       const int kThumbThickness = 3;
       const int kTrackStart = 0;
@@ -1062,13 +1046,10 @@
       scrollbar_layer = SolidColorScrollbarLayer::Create(
           scrollbar->Orientation(), kThumbThickness, kTrackStart,
           kIsLeftSideVerticalScrollbar);
-      static_cast<SolidColorScrollbarLayer*>(scrollbar_layer.get())
-          ->SetScrollElementId(layer_tree_root->element_id());
     } else {
       scrollbar_layer = PaintedScrollbarLayer::Create(std::move(scrollbar));
-      static_cast<PaintedScrollbarLayer*>(scrollbar_layer.get())
-          ->SetScrollElementId(layer_tree_root->element_id());
     }
+    scrollbar_layer->SetScrollElementId(layer_tree_root->element_id());
     layer_tree_root->AddChild(content_layer);
     layer_tree_root->AddChild(scrollbar_layer);
 
diff --git a/cc/layers/solid_color_scrollbar_layer.cc b/cc/layers/solid_color_scrollbar_layer.cc
index edf26390..92e321f 100644
--- a/cc/layers/solid_color_scrollbar_layer.cc
+++ b/cc/layers/solid_color_scrollbar_layer.cc
@@ -13,10 +13,9 @@
 
 std::unique_ptr<LayerImpl> SolidColorScrollbarLayer::CreateLayerImpl(
     LayerTreeImpl* tree_impl) {
-  const bool kIsOverlayScrollbar = true;
-  return SolidColorScrollbarLayerImpl::Create(
-      tree_impl, id(), orientation_, thumb_thickness_, track_start_,
-      is_left_side_vertical_scrollbar_, kIsOverlayScrollbar);
+  return SolidColorScrollbarLayerImpl::Create(tree_impl, id(), orientation_,
+                                              thumb_thickness_, track_start_,
+                                              is_left_side_vertical_scrollbar_);
 }
 
 scoped_refptr<SolidColorScrollbarLayer> SolidColorScrollbarLayer::Create(
@@ -39,7 +38,6 @@
       track_start_(track_start),
       is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar) {
   Layer::SetOpacity(0.f);
-  SetIsScrollbar(true);
 }
 
 SolidColorScrollbarLayer::~SolidColorScrollbarLayer() = default;
@@ -50,16 +48,6 @@
   Layer::SetOpacity(opacity);
 }
 
-void SolidColorScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
-  Layer::PushPropertiesTo(layer);
-  SolidColorScrollbarLayerImpl* scrollbar_layer =
-      static_cast<SolidColorScrollbarLayerImpl*>(layer);
-
-  DCHECK(!scrollbar_layer->HitTestable());
-
-  scrollbar_layer->SetScrollElementId(scroll_element_id_);
-}
-
 void SolidColorScrollbarLayer::SetNeedsDisplayRect(const gfx::Rect& rect) {
   // Never needs repaint.
 }
@@ -68,14 +56,6 @@
   return true;
 }
 
-void SolidColorScrollbarLayer::SetScrollElementId(ElementId element_id) {
-  if (element_id == scroll_element_id_)
-    return;
-
-  scroll_element_id_ = element_id;
-  SetNeedsCommit();
-}
-
 bool SolidColorScrollbarLayer::HitTestable() const {
   // Android scrollbars can't be interacted with by user input. They should
   // avoid hit testing so we don't enter any scrollbar scrolling code paths.
diff --git a/cc/layers/solid_color_scrollbar_layer.h b/cc/layers/solid_color_scrollbar_layer.h
index c759ae5..8aed2f5 100644
--- a/cc/layers/solid_color_scrollbar_layer.h
+++ b/cc/layers/solid_color_scrollbar_layer.h
@@ -7,12 +7,11 @@
 
 #include "cc/cc_export.h"
 #include "cc/layers/layer.h"
-#include "cc/layers/scrollbar_layer_interface.h"
+#include "cc/layers/scrollbar_layer_base.h"
 
 namespace cc {
 
-class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface,
-                                           public Layer {
+class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerBase {
  public:
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
 
@@ -27,17 +26,10 @@
 
   // Layer overrides.
   bool OpacityCanAnimateOnImplThread() const override;
-
   void SetOpacity(float opacity) override;
-  void PushPropertiesTo(LayerImpl* layer) override;
-
   void SetNeedsDisplayRect(const gfx::Rect& rect) override;
-
   bool HitTestable() const override;
 
-  // ScrollbarLayerInterface
-  void SetScrollElementId(ElementId element_id) override;
-
  protected:
   SolidColorScrollbarLayer(ScrollbarOrientation orientation,
                            int thumb_thickness,
@@ -45,7 +37,6 @@
                            bool is_left_side_vertical_scrollbar);
   ~SolidColorScrollbarLayer() override;
 
-  ElementId scroll_element_id_;
   ScrollbarOrientation orientation_;
   int thumb_thickness_;
   int track_start_;
diff --git a/cc/layers/solid_color_scrollbar_layer_impl.cc b/cc/layers/solid_color_scrollbar_layer_impl.cc
index b96e4bc3..e37741c 100644
--- a/cc/layers/solid_color_scrollbar_layer_impl.cc
+++ b/cc/layers/solid_color_scrollbar_layer_impl.cc
@@ -18,24 +18,19 @@
                                      ScrollbarOrientation orientation,
                                      int thumb_thickness,
                                      int track_start,
-                                     bool is_left_side_vertical_scrollbar,
-                                     bool is_overlay) {
+                                     bool is_left_side_vertical_scrollbar) {
   return base::WrapUnique(new SolidColorScrollbarLayerImpl(
       tree_impl, id, orientation, thumb_thickness, track_start,
-      is_left_side_vertical_scrollbar, is_overlay));
+      is_left_side_vertical_scrollbar));
 }
 
 SolidColorScrollbarLayerImpl::~SolidColorScrollbarLayerImpl() = default;
 
 std::unique_ptr<LayerImpl> SolidColorScrollbarLayerImpl::CreateLayerImpl(
     LayerTreeImpl* tree_impl) {
-  return SolidColorScrollbarLayerImpl::Create(tree_impl,
-                                              id(),
-                                              orientation(),
-                                              thumb_thickness_,
-                                              track_start_,
-                                              is_left_side_vertical_scrollbar(),
-                                              is_overlay_scrollbar());
+  return SolidColorScrollbarLayerImpl::Create(
+      tree_impl, id(), orientation(), thumb_thickness_, track_start_,
+      is_left_side_vertical_scrollbar());
 }
 
 SolidColorScrollbarLayerImpl::SolidColorScrollbarLayerImpl(
@@ -44,17 +39,15 @@
     ScrollbarOrientation orientation,
     int thumb_thickness,
     int track_start,
-    bool is_left_side_vertical_scrollbar,
-    bool is_overlay)
+    bool is_left_side_vertical_scrollbar)
     : ScrollbarLayerImplBase(tree_impl,
                              id,
                              orientation,
                              is_left_side_vertical_scrollbar,
-                             is_overlay),
+                             /*is_overlay*/ true),
       thumb_thickness_(thumb_thickness),
       track_start_(track_start),
-      color_(tree_impl->settings().solid_color_scrollbar_color) {
-}
+      color_(tree_impl->settings().solid_color_scrollbar_color) {}
 
 void SolidColorScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) {
   ScrollbarLayerImplBase::PushPropertiesTo(layer);
diff --git a/cc/layers/solid_color_scrollbar_layer_impl.h b/cc/layers/solid_color_scrollbar_layer_impl.h
index 710b4d6..7a3a72d 100644
--- a/cc/layers/solid_color_scrollbar_layer_impl.h
+++ b/cc/layers/solid_color_scrollbar_layer_impl.h
@@ -18,8 +18,7 @@
       ScrollbarOrientation orientation,
       int thumb_thickness,
       int track_start,
-      bool is_left_side_vertical_scrollbar,
-      bool is_overlay);
+      bool is_left_side_vertical_scrollbar);
   ~SolidColorScrollbarLayerImpl() override;
 
   // LayerImpl overrides.
@@ -37,8 +36,7 @@
                                ScrollbarOrientation orientation,
                                int thumb_thickness,
                                int track_start,
-                               bool is_left_side_vertical_scrollbar,
-                               bool is_overlay);
+                               bool is_left_side_vertical_scrollbar);
 
   // ScrollbarLayerImplBase implementation.
   int ThumbLength() const override;
diff --git a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
index 5f1b460..8544ce1 100644
--- a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
@@ -22,12 +22,11 @@
   int thumb_thickness = layer_size.width();
   int track_start = 0;
   bool is_left_side_vertical_scrollbar = false;
-  bool is_overlay = false;
 
   SolidColorScrollbarLayerImpl* scrollbar_layer_impl =
       impl.AddLayer<SolidColorScrollbarLayerImpl>(
           orientation, thumb_thickness, track_start,
-          is_left_side_vertical_scrollbar, is_overlay);
+          is_left_side_vertical_scrollbar);
   scrollbar_layer_impl->SetBounds(layer_size);
   scrollbar_layer_impl->SetDrawsContent(true);
   scrollbar_layer_impl->SetCurrentPos(25.f);
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index c81c31a..986eca85 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -3498,7 +3498,7 @@
     host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 4.f);
 
     auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-        host_impl_->active_tree(), VERTICAL, 10, 0, false, true);
+        host_impl_->active_tree(), VERTICAL, 10, 0, false);
     SetupScrollbarLayer(OuterViewportScrollLayer(), scrollbar);
 
     host_impl_->active_tree()->DidBecomeActive();
@@ -3712,7 +3712,7 @@
     LayerImpl* scroll =
         host_impl_->pending_tree()->OuterViewportScrollLayerForTesting();
     auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-        host_impl_->pending_tree(), VERTICAL, 10, 0, false, true);
+        host_impl_->pending_tree(), VERTICAL, 10, 0, false);
     SetupScrollbarLayer(scroll, scrollbar);
     scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(90, 0));
 
@@ -3807,7 +3807,7 @@
 
     // scrollbar_1 on root scroll.
     scrollbar_1_ = AddLayer<SolidColorScrollbarLayerImpl>(
-        host_impl_->active_tree(), VERTICAL, 15, 0, true, true);
+        host_impl_->active_tree(), VERTICAL, 15, 0, true);
     SetupScrollbarLayer(root_scroll, scrollbar_1_);
     scrollbar_1_->SetBounds(scrollbar_size_1);
     TouchActionRegion touch_action_region;
@@ -3820,7 +3820,7 @@
     GetTransformNode(child)->post_translation = gfx::Vector2dF(50, 50);
 
     scrollbar_2_ = AddLayer<SolidColorScrollbarLayerImpl>(
-        host_impl_->active_tree(), VERTICAL, 15, 0, true, true);
+        host_impl_->active_tree(), VERTICAL, 15, 0, true);
     SetupScrollbarLayer(child, scrollbar_2_);
     scrollbar_2_->SetBounds(scrollbar_size_2);
 
@@ -3998,7 +3998,7 @@
   LayerImpl* scroll =
       host_impl_->pending_tree()->OuterViewportScrollLayerForTesting();
   auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->pending_tree(), VERTICAL, 10, 0, false, true);
+      host_impl_->pending_tree(), VERTICAL, 10, 0, false);
   SetupScrollbarLayer(scroll, scrollbar);
   scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(90, 0));
 
@@ -4089,19 +4089,19 @@
   auto* container = InnerViewportScrollLayer();
   auto* root_scroll = OuterViewportScrollLayer();
   auto* vert_1_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), VERTICAL, 5, 5, true, true);
+      host_impl_->active_tree(), VERTICAL, 5, 5, true);
   CopyProperties(container, vert_1_scrollbar);
 
   auto* horiz_1_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), HORIZONTAL, 5, 5, true, true);
+      host_impl_->active_tree(), HORIZONTAL, 5, 5, true);
   CopyProperties(container, horiz_1_scrollbar);
 
   auto* vert_2_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), VERTICAL, 5, 5, true, true);
+      host_impl_->active_tree(), VERTICAL, 5, 5, true);
   CopyProperties(container, vert_2_scrollbar);
 
   auto* horiz_2_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), HORIZONTAL, 5, 5, true, true);
+      host_impl_->active_tree(), HORIZONTAL, 5, 5, true);
   CopyProperties(container, horiz_2_scrollbar);
 
   UpdateDrawProperties(host_impl_->active_tree());
@@ -4177,7 +4177,7 @@
   auto* root_scroll = OuterViewportScrollLayer();
 
   auto* vert_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), VERTICAL, 5, 0, false, true);
+      host_impl_->active_tree(), VERTICAL, 5, 0, false);
   SetupScrollbarLayer(root_scroll, vert_scrollbar);
   vert_scrollbar->SetBounds(gfx::Size(10, 200));
   vert_scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(300, 0));
@@ -4238,7 +4238,7 @@
   LayerImpl* root_scroll = OuterViewportScrollLayer();
   // The scrollbar is on the left side.
   auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), VERTICAL, 15, 0, true, true);
+      host_impl_->active_tree(), VERTICAL, 15, 0, true);
   SetupScrollbarLayer(root_scroll, scrollbar);
   scrollbar->SetBounds(scrollbar_size);
   TouchActionRegion touch_action_region;
@@ -5170,7 +5170,7 @@
   // Create a horizontal scrollbar.
   gfx::Size scrollbar_size(gfx::Size(50, 15));
   auto* scrollbar_layer = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), HORIZONTAL, 3, 20, false, true);
+      host_impl_->active_tree(), HORIZONTAL, 3, 20, false);
   SetupScrollbarLayer(OuterViewportScrollLayer(), scrollbar_layer);
   scrollbar_layer->SetBounds(scrollbar_size);
   TouchActionRegion touch_action_region;
@@ -12207,7 +12207,7 @@
 
   // scrollbar_1 on root scroll.
   auto* scrollbar_1 = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), VERTICAL, 15, 0, true, true);
+      host_impl_->active_tree(), VERTICAL, 15, 0, true);
   SetupScrollbarLayer(root_scroll, scrollbar_1);
   scrollbar_1->SetBounds(scrollbar_size_1);
   TouchActionRegion touch_action_region;
@@ -12292,7 +12292,7 @@
 
   // scrollbar_2 on child.
   auto* scrollbar_2 = AddLayer<SolidColorScrollbarLayerImpl>(
-      host_impl_->active_tree(), VERTICAL, 15, 0, true, true);
+      host_impl_->active_tree(), VERTICAL, 15, 0, true);
   LayerImpl* child =
       AddScrollableLayer(root_scroll, gfx::Size(100, 100), child_layer_size);
   child->SetOffsetToTransformParent(gfx::Vector2dF(50, 50));
@@ -13366,7 +13366,7 @@
       AddScrollableLayer(root, viewport_size, scroll_content_size);
 
   auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
-      layer_tree_impl, VERTICAL, 10, 0, false, true);
+      layer_tree_impl, VERTICAL, 10, 0, false);
   SetupScrollbarLayer(content, scrollbar);
   scrollbar->SetBounds(scrollbar_size);
   scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java
index 02646e5..c4573d4 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java
@@ -53,7 +53,7 @@
 
     // TODO(crbug.com/806868): Create a more specific test site for overlay testing.
     private static final String TEST_PAGE =
-            "/components/test/data/autofill_assistant/autofill_assistant_target_website.html";
+            "/components/test/data/autofill_assistant/html/autofill_assistant_target_website.html";
 
     @Before
     public void setUp() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index c63ea9e..ddc54f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -10,7 +10,6 @@
 import android.graphics.RectF;
 
 import org.chromium.base.ActivityState;
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -23,7 +22,6 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
 import org.chromium.chrome.browser.compositor.scene_layer.ContextualSearchSceneLayer;
 import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer;
-import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
 import org.chromium.chrome.browser.contextualsearch.ResolvedSearchTerm.CardTag;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -36,8 +34,6 @@
  * Controls the Contextual Search Panel.
  */
 public class ContextualSearchPanel extends OverlayPanel {
-    /** The number of times to allow scrolling.  After this limit we'll close. */
-    private static final int SCROLL_COUNT_LIMIT = 3;
 
     /** Restricts the maximized panel height to the given fraction of a tab. */
     private static final float MAXIMIZED_HEIGHT_FRACTION = 0.95f;
@@ -45,9 +41,6 @@
     /** Used for logging state changes. */
     private final ContextualSearchPanelMetrics mPanelMetrics;
 
-    /** The height of the bar shadow, in pixels. */
-    private final float mBarShadowHeightPx;
-
     /** The distance of the divider from the end of the bar, in dp. */
     private final float mEndButtonWidthDp;
 
@@ -75,9 +68,6 @@
      */
     private ScrimParams mScrimParams;
 
-    /** Number of times the panel has been scrolled already. */
-    private int mScrollCount;
-
     // ============================================================================================
     // Constructor
     // ============================================================================================
@@ -93,10 +83,6 @@
         mSceneLayer = createNewContextualSearchSceneLayer();
         mPanelMetrics = new ContextualSearchPanelMetrics();
 
-        mBarShadowHeightPx =
-                ApiCompatibilityUtils
-                        .getDrawable(mContext.getResources(), R.drawable.modern_toolbar_shadow)
-                        .getIntrinsicHeight();
         mEndButtonWidthDp = mContext.getResources().getDimensionPixelSize(
                                     R.dimen.contextual_search_padded_button_width)
                 * mPxToDp;
@@ -236,7 +222,6 @@
         setProgressBarCompletion(0);
         setProgressBarVisible(false);
         getImageControl().hideCustomImage(false);
-        mScrollCount = 0;
 
         super.onClosed(reason);
 
@@ -465,28 +450,6 @@
     }
 
     /**
-     * Makes the panel not visible by either hiding it or closing it completely.
-     * Decides which method is most appropriate, and then makes it not visible based on that
-     * decision.
-     * @param reason The reason we want the panel to not be visible.
-     */
-    public void makePanelNotVisible(@StateChangeReason int reason) {
-        if (++mScrollCount >= SCROLL_COUNT_LIMIT) {
-            closePanel(StateChangeReason.BASE_PAGE_SCROLL, true);
-        } else if (isHideDuringScrollEnabled()) {
-            hidePanel(reason);
-        }
-    }
-
-    /** @return whether hiding during scrolling is enabled for the Longpress-Resolve feature. */
-    private boolean isHideDuringScrollEnabled() {
-        return ContextualSearchFieldTrial.LONGPRESS_RESOLVE_HIDE_ON_SCROLL.equals(
-                ChromeFeatureList.getFieldTrialParamByFeature(
-                        ChromeFeatureList.CONTEXTUAL_SEARCH_LONGPRESS_RESOLVE,
-                        ContextualSearchFieldTrial.LONGPRESS_RESOLVE_PARAM_NAME));
-    }
-
-    /**
      * Called after the panel has navigated to prefetched Search Results.
      * If the user has the panel open then they will see the prefetched result starting to load.
      * Currently this just logs the time between the start of the search until the results start to
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
index 5e4b951..8cef9a45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
@@ -282,6 +282,7 @@
      * Specifies that this resolve must return a non-expanding result.
      */
     void setRestrictedResolve() {
+        // TODO(donnd): Improve by sending full context plus a boolean.
         mSurroundingText = mInitialSelectedWord;
         mSelectionStartOffset = 0;
         mSelectionEndOffset = mSurroundingText.length();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
index 33dd7e8..44061ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
@@ -26,8 +26,6 @@
     // Public settings synchronized with src/components/contextual_search/core/browser/public.cc
     //==========================================================================================
     public static final String LONGPRESS_RESOLVE_PARAM_NAME = "longpress_resolve_variation";
-    public static final String LONGPRESS_RESOLVE_HIDE_ON_SCROLL = "1";
-    public static final String LONGPRESS_RESOLVE_PRIVACY_AGGRESSIVE = "2";
     public static final String LONGPRESS_RESOLVE_PRESERVE_TAP = "3";
 
     //==========================================================================================
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 439b00a..f60adde 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -23,7 +23,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayContentDelegate;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.PanelState;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
@@ -1351,12 +1350,7 @@
     public void handleScrollStart() {
         if (mIsAccessibilityModeEnabled) return;
 
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXTUAL_SEARCH_LONGPRESS_RESOLVE)
-                || mSelectionController.getSelectionType() == SelectionType.TAP) {
-            hideContextualSearch(StateChangeReason.BASE_PAGE_SCROLL);
-        } else if (mSelectionController.getSelectionType() == SelectionType.RESOLVING_LONG_PRESS) {
-            mSearchPanel.makePanelNotVisible(StateChangeReason.BASE_PAGE_SCROLL);
-        }
+        hideContextualSearch(StateChangeReason.BASE_PAGE_SCROLL);
     }
 
     @Override
@@ -1681,11 +1675,8 @@
 
                 String selection = mSelectionController.getSelectedText();
                 assert !TextUtils.isEmpty(selection);
-                boolean isRestrictedResolve =
-                        mPolicy.isPrivacyAggressiveResolveEnabled() && mPolicy.isPromoAvailable()
-                        || mSelectionController.isAdjustedSelection();
                 mNetworkCommunicator.startSearchTermResolutionRequest(
-                        selection, isRestrictedResolve);
+                        selection, mSelectionController.isAdjustedSelection());
                 // If the we were unable to start the resolve, we've hidden the UI and set the
                 // context to null.
                 if (mContext == null || mSearchPanel == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
index 6cbf30a..046a177 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
@@ -147,11 +147,6 @@
             return false;
         }
 
-        if (isPrivacyAggressiveResolveEnabled()
-                && mSelectionController.getSelectionType() == SelectionType.RESOLVING_LONG_PRESS) {
-            return true;
-        }
-
         return isPromoAvailable() ? isBasePageHTTP(mNetworkCommunicator.getBasePageUrl()) : true;
     }
 
@@ -255,29 +250,10 @@
      *         is no existing request.
      */
     boolean shouldCreateVerbatimRequest() {
-        if (isPrivacyAggressiveResolveEnabled()) return false;
-
         @SelectionType
         int selectionType = mSelectionController.getSelectionType();
         return (mSelectionController.getSelectedText() != null
-                && (selectionType == SelectionType.LONG_PRESS
-                        || (selectionType == SelectionType.TAP
-                                && !shouldPreviousGestureResolve())));
-    }
-
-    /**
-     * Returns whether doing a privacy aggressive resolve is enabled (as opposed to privacy
-     * conservative).  When this is enabled, the selection is sent to the server immediately instead
-     * of waiting for the panel to be opened.  This allows the server to resolve the selection which
-     * will recognize entities, etc. and display those attributes in the Bar.
-     * @return Whether the privacy-aggressive behavior of immediately sending the selection to the
-     *         server is enabled.
-     */
-    boolean isPrivacyAggressiveResolveEnabled() {
-        return ContextualSearchFieldTrial.LONGPRESS_RESOLVE_PRIVACY_AGGRESSIVE.equals(
-                ChromeFeatureList.getFieldTrialParamByFeature(
-                        ChromeFeatureList.CONTEXTUAL_SEARCH_LONGPRESS_RESOLVE,
-                        ContextualSearchFieldTrial.LONGPRESS_RESOLVE_PARAM_NAME));
+                && (selectionType == SelectionType.LONG_PRESS || !shouldPreviousGestureResolve()));
     }
 
     /** @return whether Tap is disabled due to the longpress experiment. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
index e7d75af..07013a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -72,7 +72,6 @@
     private boolean mWasTapGestureDetected;
     // Reflects whether the last tap was valid and whether we still have a tap-based selection.
     private ContextualSearchTapState mLastTapState;
-    private boolean mShouldHandleSelectionModification;
     // Whether the selection was automatically expanded due to an adjustment (e.g. Resolve).
     private boolean mDidExpandSelection;
 
@@ -100,9 +99,9 @@
     private boolean mClearingSelection;
 
     /**
-     * Whether the current selection has been adjusted or not.  If it has been adjusted we must
-     * request a resolve for this exact term rather than anything that overlaps as we get with
-     * normal expanding resolves.
+     * Whether the current selection has been adjusted or not.  If the user has adjusted the
+     * selection we must request a resolve for this exact term rather than anything that overlaps,
+     * and not expand the selection (since it was explicitly set by the user).
      */
     private boolean mIsAdjustedSelection;
 
@@ -173,14 +172,6 @@
      * @param reason The reason for ending the Contextual Search.
      */
     void onSearchEnded(@OverlayPanel.StateChangeReason int reason) {
-        // If the user explicitly closes the panel after establishing a selection with long press,
-        // it should not reappear until a new selection is made. This prevents the panel from
-        // reappearing when a long press selection is modified after the user has taken action to
-        // get rid of the panel. See crbug.com/489461.
-        if (shouldPreventHandlingCurrentSelectionModification(reason)) {
-            preventHandlingCurrentSelectionModification();
-        }
-
         // Long press selections should remain visible after ending a Contextual Search.
         if (mSelectionType == SelectionType.TAP) clearSelection();
     }
@@ -334,7 +325,7 @@
                 resetAllStates();
                 break;
             case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED:
-                shouldHandleSelection = mShouldHandleSelectionModification;
+                shouldHandleSelection = true;
                 mIsAdjustedSelection = true;
                 break;
             default:
@@ -356,7 +347,6 @@
      * @param type The type of selection made by the user.
      */
     private void handleSelection(String selection, @SelectionType int type) {
-        mShouldHandleSelectionModification = true;
         boolean isValidSelection = validateSelectionSuppression(selection);
         mHandler.handleSelection(selection, isValidSelection, type, mX, mY);
     }
@@ -520,37 +510,6 @@
     }
 
     // ============================================================================================
-    // Selection Modification
-    // ============================================================================================
-
-    /**
-     * This method checks whether the selection modification should be handled. This method
-     * is needed to allow modifying selections that are occluded by the Panel.
-     * See crbug.com/489461.
-     *
-     * @param reason The reason the panel is closing.
-     * @return Whether the selection modification should be handled.
-     */
-    private boolean shouldPreventHandlingCurrentSelectionModification(
-            @OverlayPanel.StateChangeReason int reason) {
-        return getSelectionType() == SelectionType.LONG_PRESS
-                && (reason == OverlayPanel.StateChangeReason.BACK_PRESS
-                || reason == OverlayPanel.StateChangeReason.BASE_PAGE_SCROLL
-                || reason == OverlayPanel.StateChangeReason.SWIPE
-                || reason == OverlayPanel.StateChangeReason.FLING
-                || reason == OverlayPanel.StateChangeReason.CLOSE_BUTTON);
-    }
-
-    /**
-     * Temporarily prevents the controller from handling selection modification events on the
-     * current selection. Handling will be re-enabled when a new selection is made through either a
-     * tap or long press.
-     */
-    private void preventHandlingCurrentSelectionModification() {
-        mShouldHandleSelectionModification = false;
-    }
-
-    // ============================================================================================
     // Misc.
     // ============================================================================================
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index c29743b..0eb2ed8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -3172,26 +3172,4 @@
         Assert.assertEquals("States", getSelectedText());
         waitForPanelToPeek();
     }
-
-    @Test
-    @SmallTest
-    @Feature({"ContextualSearch"})
-    @CommandLineFlags.Add({"enable-features=ContextualSearchLongpressResolve<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/FakeGroup",
-            "force-fieldtrial-params=FakeStudyName.FakeGroup:longpress_resolve_variation/"
-                    + ContextualSearchFieldTrial.LONGPRESS_RESOLVE_PRIVACY_AGGRESSIVE})
-    public void
-    testLongpressResolvesWithLongpressResolveEnabledAndVariationPrivacyAggressive()
-            throws TimeoutException {
-        mPolicy.overrideDecidedStateForTesting(false);
-        mFakeServer.setShouldUseHttps(true);
-        longPressNode("states");
-        assertLoadedNoUrl();
-        assertSearchTermRequested();
-
-        fakeResponse(false, 200, "states", "United States Intelligence", "alternate-term", false);
-        waitForPanelToPeek();
-        assertLoadedLowPriorityUrl();
-        assertContainsParameters("states", "alternate-term");
-    }
 }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 6e6aa09..67ae427 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2498,13 +2498,16 @@
 
       <!-- Cookie controls strings. -->
       <message name="IDS_COOKIE_CONTROLS_DIALOG_TITLE" desc="Title of the dialog that allows users to control cookie blocking.">
-        Third-party cookie blocking is on
+        {COUNT, plural,
+              =0 {No third-party cookies}
+              =1 {1 third-party cookie is blocked}
+              other {# third-party cookies are blocked}}
       </message>
       <message name="IDS_COOKIE_CONTROLS_DIALOG_TITLE_OFF" desc="Text shown in the dialog that allows users to control cookie blocking. Shown when cookie blocking was disabled for this specific site.">
-        Third-party cookie blocking is turned off for this site
+        Third-party cookies are allowed on this site
       </message>
       <message name="IDS_COOKIE_CONTROLS_TURN_ON_BUTTON" desc="Label shown on a button that turns cookie blocking on after it was disabled for a specific site.">
-        Turn on
+        Block cookies
       </message>
       <message name="IDS_COOKIE_CONTROLS_TURN_OFF_BUTTON" desc="Label shown on a button that turns off cookie blocking for a specific site.">
         Turn off for this site
@@ -2512,14 +2515,17 @@
       <message name="IDS_COOKIE_CONTROLS_NOT_WORKING_TITLE" desc="Label shown on a dialog that allows users to turn off third-party cookie blocking for a specific site.">
         Site not working?
       </message>
+      <message name="IDS_COOKIE_CONTROLS_TURNED_ON_TITLE" desc="Label shown to a user after cookie blocking has been turned on for a specific site.">
+        Third-party cookie blocking is on
+      </message>
      <message name="IDS_COOKIE_CONTROLS_NOT_WORKING_DESCRIPTION" desc="Label shown on a dialog that allows users to turn off third-party cookie blocking for a specific site.">
         Some sites use third-party cookies to load their pages. If a site isn't working, you can try turning off cookie blocking.
       </message>
-      <message name="IDS_COOKIE_CONTROLS_BLOCKED_MESSAGE" desc="Text shown in the dialog that allows users to control cookie blocking. Shows the number of sites for which cookies have been blocked.">
-        {COUNT, plural,
-              =0 {This site doesn't use cookies for cross-site tracking}
-              =1 {Chrome is blocking cookies from 1 site}
-              other {Chrome is blocking cookies from # sites}}
+      <message name="IDS_COOKIE_CONTROLS_NOTHING_BLOCKED_MESSAGE" desc="Text shown in the dialog that allows users to control cookie blocking.">
+        This site is free of cookies that can be used to track you across the web
+      </message>
+      <message name="IDS_COOKIE_CONTROLS_BLOCKED_MESSAGE" desc="Text shown in the dialog that allows users to control cookie blocking.">
+        Chrome is blocking cookies that can be used to track you across the web
       </message>
       <message name="IDS_COOKIE_CONTROLS_TOOLTIP" desc="Tooltip shown on a page action icon that is shown when cookie blocking is enabled.">
         Third-party cookie blocking
diff --git a/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_BLOCKED_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_BLOCKED_MESSAGE.png.sha1
new file mode 100644
index 0000000..cedf0d2
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_BLOCKED_MESSAGE.png.sha1
@@ -0,0 +1 @@
+35cf61169272da7e8e35f0b4cc77bf41082baf48
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_DIALOG_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..073b534
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+18cbca2a59506a01b12ce7e0de6ad7a5d54dfc23
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_DIALOG_TITLE_OFF.png.sha1 b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_DIALOG_TITLE_OFF.png.sha1
new file mode 100644
index 0000000..b9badb9a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_DIALOG_TITLE_OFF.png.sha1
@@ -0,0 +1 @@
+dac2e1b0f064051d79c5270a743d2ec7b3225f6c
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_NOTHING_BLOCKED_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_NOTHING_BLOCKED_MESSAGE.png.sha1
new file mode 100644
index 0000000..91e5213
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_NOTHING_BLOCKED_MESSAGE.png.sha1
@@ -0,0 +1 @@
+297129e478e4fe2073e74cde18acdbe71b455756
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURNED_ON_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURNED_ON_TITLE.png.sha1
new file mode 100644
index 0000000..6fc751e
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURNED_ON_TITLE.png.sha1
@@ -0,0 +1 @@
+f75771d80ea4534f8aee1f9e9a6786947ccdffbc
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURN_OFF_BUTTON.png.sha1 b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURN_OFF_BUTTON.png.sha1
new file mode 100644
index 0000000..9891671
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURN_OFF_BUTTON.png.sha1
@@ -0,0 +1 @@
+55ec4ca6ba6aab18bf86f0b48b708e781a507e28
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURN_ON_BUTTON.png.sha1 b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURN_ON_BUTTON.png.sha1
new file mode 100644
index 0000000..b9badb9a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COOKIE_CONTROLS_TURN_ON_BUTTON.png.sha1
@@ -0,0 +1 @@
+dac2e1b0f064051d79c5270a743d2ec7b3225f6c
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 15edad8..3003eff 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1130,19 +1130,10 @@
 const FeatureEntry::FeatureVariation kSimplifiedServerVariations[] = {
     {"and allow all CoCa cards", &kSimplifiedServerAllCocaCards, 1, nullptr}};
 
-const FeatureEntry::FeatureParam kLongpressResolveHideOnScroll = {
-    contextual_search::kLongpressResolveParamName,
-    contextual_search::kLongpressResolveHideOnScroll};
-const FeatureEntry::FeatureParam kLongpressResolvePrivacyAggressive = {
-    contextual_search::kLongpressResolveParamName,
-    contextual_search::kLongpressResolvePrivacyAggressive};
 const FeatureEntry::FeatureParam kLongpressResolvePreserveTap = {
     contextual_search::kLongpressResolveParamName,
     contextual_search::kLongpressResolvePreserveTap};
 const FeatureEntry::FeatureVariation kLongpressResolveVariations[] = {
-    {"and hide on scroll", &kLongpressResolveHideOnScroll, 1, nullptr},
-    {"and allow privacy-aggressive behavior",
-     &kLongpressResolvePrivacyAggressive, 1, nullptr},
     {"and preserve Tap behavior", &kLongpressResolvePreserveTap, 1, nullptr},
 };
 
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.cc b/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.cc
index 8e848f2..5d96f57 100644
--- a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.cc
+++ b/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.cc
@@ -26,9 +26,14 @@
     : browser_context_(browser_context),
       event_router_(EventRouter::Get(browser_context)),
       print_job_history_service_observer_(this) {
-  print_job_history_service_observer_.Add(
+  auto* history_service =
       chromeos::PrintJobHistoryServiceFactory::GetForBrowserContext(
-          browser_context));
+          browser_context);
+
+  // The print job history service is not available on the lock screen.
+  if (history_service) {
+    print_job_history_service_observer_.Add(history_service);
+  }
 }
 
 PrintJobFinishedEventDispatcher::~PrintJobFinishedEventDispatcher() {}
diff --git a/chrome/browser/chromeos/note_taking_helper.cc b/chrome/browser/chromeos/note_taking_helper.cc
index a24299b..ee4c876 100644
--- a/chrome/browser/chromeos/note_taking_helper.cc
+++ b/chrome/browser/chromeos/note_taking_helper.cc
@@ -10,6 +10,7 @@
 #include "apps/launcher.h"
 #include "ash/public/cpp/stylus_utils.h"
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
@@ -29,6 +30,7 @@
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
+#include "components/arc/mojom/file_system.mojom.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -478,6 +480,20 @@
                                            weak_ptr_factory_.GetWeakPtr()));
 }
 
+arc::mojom::ActivityNamePtr AppIdToActivityName(const std::string& id) {
+  auto name = arc::mojom::ActivityName::New();
+
+  const size_t separator = id.find('/');
+  if (separator == std::string::npos) {
+    name->package_name = id;
+    name->activity_name = std::string();
+  } else {
+    name->package_name = id.substr(0, separator);
+    name->activity_name = id.substr(separator + 1);
+  }
+  return name;
+}
+
 void NoteTakingHelper::OnGotAndroidApps(
     std::vector<arc::mojom::IntentHandlerInfoPtr> handlers) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -497,6 +513,21 @@
     observer.OnAvailableNoteTakingAppsUpdated();
 }
 
+arc::mojom::OpenUrlsRequestPtr CreateArcNoteRequest(const std::string& app_id,
+                                                    const GURL& clip_data_uri) {
+  auto request = arc::mojom::OpenUrlsRequest::New();
+  request->action_type = arc::mojom::ActionType::CREATE_NOTE;
+  request->activity_name = AppIdToActivityName(app_id);
+  if (!clip_data_uri.is_empty()) {
+    auto url_with_type = arc::mojom::ContentUrlWithMimeType::New();
+    url_with_type->content_url = clip_data_uri;
+    url_with_type->mime_type = "image/png";
+    request->urls.push_back(std::move(url_with_type));
+  }
+
+  return request;
+}
+
 NoteTakingHelper::LaunchResult NoteTakingHelper::LaunchAppInternal(
     Profile* profile,
     const std::string& app_id,
@@ -531,7 +562,15 @@
 
     // TODO(derat): Is there some way to detect whether this fails due to the
     // package no longer being available?
-    helper->HandleIntent(CreateIntentInfo(clip_data_uri), std::move(activity));
+    auto request = CreateArcNoteRequest(app_id, clip_data_uri);
+    arc::mojom::FileSystemInstance* arc_file_system =
+        ARC_GET_INSTANCE_FOR_METHOD(
+            arc::ArcServiceManager::Get()->arc_bridge_service()->file_system(),
+            OpenUrlsWithPermission);
+    if (!arc_file_system)
+      return LaunchResult::ANDROID_NOT_RUNNING;
+    arc_file_system->OpenUrlsWithPermission(std::move(request),
+                                            base::DoNothing());
 
     UMA_HISTOGRAM_ENUMERATION(
         "Arc.UserInteraction",
diff --git a/chrome/browser/chromeos/note_taking_helper_unittest.cc b/chrome/browser/chromeos/note_taking_helper_unittest.cc
index 12ec36c..e473db6 100644
--- a/chrome/browser/chromeos/note_taking_helper_unittest.cc
+++ b/chrome/browser/chromeos/note_taking_helper_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/note_taking_controller_client.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -33,9 +34,11 @@
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
+#include "components/arc/mojom/file_system.mojom.h"
 #include "components/arc/mojom/intent_helper.mojom.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/arc/test/connection_holder_util.h"
+#include "components/arc/test/fake_file_system_instance.h"
 #include "components/arc/test/fake_intent_helper_instance.h"
 #include "components/crx_file/id_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
@@ -82,21 +85,6 @@
                       info.lock_screen_support);
 }
 
-// Helper functions returning strings that can be used to compare launched
-// intents.
-std::string GetIntentString(const std::string& package,
-                            const std::string& clip_data_uri) {
-  return base::StringPrintf(
-      "%s %s", package.c_str(),
-      clip_data_uri.empty() ? "[unset]" : clip_data_uri.c_str());
-}
-std::string GetIntentString(const HandledIntent& intent) {
-  EXPECT_EQ(NoteTakingHelper::kIntentAction, intent.intent->action);
-  return GetIntentString(
-      intent.activity->package_name,
-      (intent.intent->clip_data_uri ? *intent.intent->clip_data_uri : ""));
-}
-
 // Creates an ARC IntentHandlerInfo object.
 IntentHandlerInfoPtr CreateIntentHandlerInfo(const std::string& name,
                                              const std::string& package) {
@@ -165,8 +153,13 @@
           ->arc_bridge_service()
           ->intent_helper()
           ->CloseInstance(&intent_helper_);
+      arc::ArcServiceManager::Get()
+          ->arc_bridge_service()
+          ->file_system()
+          ->CloseInstance(file_system_.get());
       NoteTakingHelper::Shutdown();
       intent_helper_bridge_.reset();
+      file_system_bridge_.reset();
       arc_test_.TearDown();
     }
     extensions::ExtensionSystem::Get(profile())->Shutdown();
@@ -218,6 +211,18 @@
     WaitForInstanceReady(
         arc::ArcServiceManager::Get()->arc_bridge_service()->intent_helper());
 
+    file_system_bridge_ = std::make_unique<arc::ArcFileSystemBridge>(
+        profile(), arc::ArcServiceManager::Get()->arc_bridge_service());
+    file_system_ = std::make_unique<arc::FakeFileSystemInstance>();
+
+    arc::ArcServiceManager::Get()
+        ->arc_bridge_service()
+        ->file_system()
+        ->SetInstance(file_system_.get());
+    WaitForInstanceReady(
+        arc::ArcServiceManager::Get()->arc_bridge_service()->file_system());
+    ASSERT_TRUE(file_system_->InitCalled());
+
     if (flags & ENABLE_PALETTE) {
       base::CommandLine::ForCurrentProcess()->AppendSwitch(
           ash::switches::kAshForceEnableStylusTools);
@@ -405,6 +410,10 @@
 
   arc::FakeIntentHelperInstance intent_helper_;
 
+  std::unique_ptr<arc::ArcFileSystemBridge> file_system_bridge_;
+
+  std::unique_ptr<arc::FakeFileSystemInstance> file_system_;
+
   // Pointer to the primary profile (returned by |profile()|) prefs - owned by
   // the profile.
   sync_preferences::TestingPrefServiceSyncable* profile_prefs_ = nullptr;
@@ -903,9 +912,16 @@
   // The installed app should be launched.
   std::unique_ptr<HistogramTester> histogram_tester(new HistogramTester());
   helper()->LaunchAppForNewNote(profile(), base::FilePath());
-  ASSERT_EQ(1u, intent_helper_.handled_intents().size());
-  EXPECT_EQ(GetIntentString(kPackage1, ""),
-            GetIntentString(intent_helper_.handled_intents()[0]));
+  ASSERT_EQ(1u, file_system_->handledUrlRequests().size());
+  EXPECT_EQ(arc::mojom::ActionType::CREATE_NOTE,
+            file_system_->handledUrlRequests().at(0)->action_type);
+  EXPECT_EQ(
+      kPackage1,
+      file_system_->handledUrlRequests().at(0)->activity_name->package_name);
+  EXPECT_EQ(
+      std::string(),
+      file_system_->handledUrlRequests().at(0)->activity_name->activity_name);
+  ASSERT_EQ(0u, file_system_->handledUrlRequests().at(0)->urls.size());
 
   histogram_tester->ExpectUniqueSample(
       NoteTakingHelper::kPreferredLaunchResultHistogramName,
@@ -926,11 +942,19 @@
 
   // The second app should be launched now.
   intent_helper_.clear_handled_intents();
+  file_system_->clear_handled_requests();
   histogram_tester.reset(new HistogramTester());
   helper()->LaunchAppForNewNote(profile(), base::FilePath());
-  ASSERT_EQ(1u, intent_helper_.handled_intents().size());
-  EXPECT_EQ(GetIntentString(kPackage2, ""),
-            GetIntentString(intent_helper_.handled_intents()[0]));
+  ASSERT_EQ(1u, file_system_->handledUrlRequests().size());
+  EXPECT_EQ(arc::mojom::ActionType::CREATE_NOTE,
+            file_system_->handledUrlRequests().at(0)->action_type);
+  EXPECT_EQ(
+      kPackage2,
+      file_system_->handledUrlRequests().at(0)->activity_name->package_name);
+  EXPECT_EQ(
+      std::string(),
+      file_system_->handledUrlRequests().at(0)->activity_name->activity_name);
+  ASSERT_EQ(0u, file_system_->handledUrlRequests().at(0)->urls.size());
 
   histogram_tester->ExpectUniqueSample(
       NoteTakingHelper::kPreferredLaunchResultHistogramName,
@@ -954,25 +978,45 @@
       file_manager::util::GetDownloadsFolderForProfile(profile()).Append(
           "image.jpg"));
   helper()->LaunchAppForNewNote(profile(), kDownloadedPath);
-  ASSERT_EQ(1u, intent_helper_.handled_intents().size());
-  EXPECT_EQ(GetIntentString(kPackage, GetArcUrl(kDownloadedPath)),
-            GetIntentString(intent_helper_.handled_intents()[0]));
+  ASSERT_EQ(1u, file_system_->handledUrlRequests().size());
+  EXPECT_EQ(arc::mojom::ActionType::CREATE_NOTE,
+            file_system_->handledUrlRequests().at(0)->action_type);
+  EXPECT_EQ(
+      kPackage,
+      file_system_->handledUrlRequests().at(0)->activity_name->package_name);
+  EXPECT_EQ(
+      std::string(),
+      file_system_->handledUrlRequests().at(0)->activity_name->activity_name);
+  ASSERT_EQ(1u, file_system_->handledUrlRequests().at(0)->urls.size());
+  ASSERT_EQ(GetArcUrl(kDownloadedPath),
+            file_system_->handledUrlRequests().at(0)->urls.at(0)->content_url);
 
   const base::FilePath kRemovablePath =
       base::FilePath(file_manager::util::kRemovableMediaPath)
           .Append("image.jpg");
   intent_helper_.clear_handled_intents();
+  file_system_->clear_handled_requests();
   helper()->LaunchAppForNewNote(profile(), kRemovablePath);
-  ASSERT_EQ(1u, intent_helper_.handled_intents().size());
-  EXPECT_EQ(GetIntentString(kPackage, GetArcUrl(kRemovablePath)),
-            GetIntentString(intent_helper_.handled_intents()[0]));
+  ASSERT_EQ(1u, file_system_->handledUrlRequests().size());
+  EXPECT_EQ(arc::mojom::ActionType::CREATE_NOTE,
+            file_system_->handledUrlRequests().at(0)->action_type);
+  EXPECT_EQ(
+      kPackage,
+      file_system_->handledUrlRequests().at(0)->activity_name->package_name);
+  EXPECT_EQ(
+      std::string(),
+      file_system_->handledUrlRequests().at(0)->activity_name->activity_name);
+  ASSERT_EQ(1u, file_system_->handledUrlRequests().at(0)->urls.size());
+  ASSERT_EQ(GetArcUrl(kRemovablePath),
+            file_system_->handledUrlRequests().at(0)->urls.at(0)->content_url);
 
   // When a path that isn't accessible to ARC is passed, the request should be
   // dropped.
   HistogramTester histogram_tester;
   intent_helper_.clear_handled_intents();
+  file_system_->clear_handled_requests();
   helper()->LaunchAppForNewNote(profile(), base::FilePath("/bad/path.jpg"));
-  EXPECT_TRUE(intent_helper_.handled_intents().empty());
+  EXPECT_TRUE(file_system_->handledUrlRequests().empty());
 
   histogram_tester.ExpectUniqueSample(
       NoteTakingHelper::kPreferredLaunchResultHistogramName,
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index b04c883..4bb130b 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -435,13 +435,17 @@
   }
 
   if (policy.has_device_login_screen_system_info_enforced()) {
-    const em::DeviceLoginScreenSystemInfoEnforcedProto& container(
+    const em::BooleanPolicyProto& container(
         policy.device_login_screen_system_info_enforced());
-    if (container.has_enabled()) {
-      policies->Set(
-          key::kDeviceLoginScreenSystemInfoEnforced, POLICY_LEVEL_MANDATORY,
-          POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
-          std::make_unique<base::Value>(container.enabled()), nullptr);
+    if (container.has_value()) {
+      PolicyLevel level;
+      if (GetPolicyLevel(container.has_policy_options(),
+                         container.policy_options(), &level)) {
+        policies->Set(key::kDeviceLoginScreenSystemInfoEnforced, level,
+                      POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
+                      std::make_unique<base::Value>(container.value()),
+                      nullptr);
+      }
     }
   }
 
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_service_factory.cc b/chrome/browser/chromeos/printing/history/print_job_history_service_factory.cc
index abe5717..6958a1b 100644
--- a/chrome/browser/chromeos/printing/history/print_job_history_service_factory.cc
+++ b/chrome/browser/chromeos/printing/history/print_job_history_service_factory.cc
@@ -4,9 +4,13 @@
 
 #include "chrome/browser/chromeos/printing/history/print_job_history_service_factory.h"
 
+#include <memory>
+#include <utility>
+
 #include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
 #include "chrome/browser/chromeos/printing/history/print_job_database_impl.h"
 #include "chrome/browser/chromeos/printing/history/print_job_history_service_impl.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "content/public/browser/storage_partition.h"
@@ -38,6 +42,13 @@
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
 
+  // We do not want an instance of PrintJobHistory on the lock screen.  The
+  // result is multiple print job notifications. https://crbug.com/1011532
+  if (ProfileHelper::IsLockScreenAppProfile(profile) ||
+      ProfileHelper::IsSigninProfile(profile)) {
+    return nullptr;
+  }
+
   leveldb_proto::ProtoDatabaseProvider* database_provider =
       content::BrowserContext::GetDefaultStoragePartition(profile)
           ->GetProtoDatabaseProvider();
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
index 2ee0568..8fd42d7 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -392,10 +392,10 @@
   }
 
   if (policy.has_device_login_screen_system_info_enforced() &&
-      policy.device_login_screen_system_info_enforced().has_enabled()) {
+      policy.device_login_screen_system_info_enforced().has_value()) {
     new_values_cache->SetBoolean(
         kDeviceLoginScreenSystemInfoEnforced,
-        policy.device_login_screen_system_info_enforced().enabled());
+        policy.device_login_screen_system_info_enforced().value());
   }
 
   if (policy.has_saml_login_authentication_type() &&
diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
index 166f4db..d8a3a85 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
@@ -313,10 +313,10 @@
 
   // Helper routine to set DeviceLoginScreenSystemInfoEnforced policy.
   void SetSystemInfoEnforced(bool enabled) {
-    em::DeviceLoginScreenSystemInfoEnforcedProto* proto =
+    em::BooleanPolicyProto* proto =
         device_policy_->payload()
             .mutable_device_login_screen_system_info_enforced();
-    proto->set_enabled(enabled);
+    proto->set_value(enabled);
     BuildAndInstallDevicePolicy();
   }
 
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
index cdc5786..4530f54 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
@@ -12,6 +12,8 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/macros.h"
+#include "base/values.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
 #include "chrome/browser/chromeos/certificate_provider/pin_dialog_manager.h"
@@ -19,6 +21,7 @@
 #include "chrome/common/extensions/api/certificate_provider.h"
 #include "chrome/common/extensions/api/certificate_provider_internal.h"
 #include "chromeos/constants/security_token_pin_types.h"
+#include "extensions/browser/quota_service.h"
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_private_key.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
@@ -75,6 +78,51 @@
     "Previous request not finished";
 const char kCertificateProviderNoUserInput[] = "No user input received";
 
+// The BucketMapper implementation for the requestPin API that avoids using the
+// quota when the current request uses the requestId that is strictly greater
+// than all previous ones.
+class RequestPinExceptFirstQuotaBucketMapper final
+    : public QuotaLimitHeuristic::BucketMapper {
+ public:
+  RequestPinExceptFirstQuotaBucketMapper() = default;
+  ~RequestPinExceptFirstQuotaBucketMapper() override = default;
+
+  void GetBucketsForArgs(const base::ListValue* args,
+                         QuotaLimitHeuristic::BucketList* buckets) override {
+    if (args->GetList().empty())
+      return;
+    const base::Value& details = args->GetList()[0];
+    if (!details.is_dict())
+      return;
+    const base::Value* sign_request_id =
+        details.FindKeyOfType("signRequestId", base::Value::Type::INTEGER);
+    if (!sign_request_id)
+      return;
+    if (sign_request_id->GetInt() > biggest_request_id_) {
+      // Either it's the first request with the newly issued requestId, or it's
+      // an invalid requestId (bigger than the real one). Return a new bucket in
+      // order to apply no quota for the former case; for the latter case the
+      // quota doesn't matter much, except that we're maybe making it stricter
+      // for future requests (which is bearable).
+      biggest_request_id_ = sign_request_id->GetInt();
+      new_request_bucket_ = std::make_unique<QuotaLimitHeuristic::Bucket>();
+      buckets->push_back(new_request_bucket_.get());
+      return;
+    }
+    // Either it's a repeatitive request for the given requestId, or the
+    // extension reordered the requests. Fall back to the default bucket (shared
+    // between all requests) in that case.
+    buckets->push_back(&default_bucket_);
+  }
+
+ private:
+  int biggest_request_id_ = -1;
+  QuotaLimitHeuristic::Bucket default_bucket_;
+  std::unique_ptr<QuotaLimitHeuristic::Bucket> new_request_bucket_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestPinExceptFirstQuotaBucketMapper);
+};
+
 }  // namespace
 
 const int api::certificate_provider::kMaxClosedDialogsPerMinute = 10;
@@ -257,19 +305,25 @@
 }
 
 void CertificateProviderRequestPinFunction::GetQuotaLimitHeuristics(
-    extensions::QuotaLimitHeuristics* heuristics) const {
+    QuotaLimitHeuristics* heuristics) const {
+  // Apply a 1-minute and a 10-minute quotas. A special bucket mapper is used in
+  // order to, approximately, skip applying quotas to the first request for each
+  // requestId (such logic cannot be done in ShouldSkipQuotaLimiting(), since
+  // it's not called with the request's parameters). The limitation constants
+  // are decremented below to account the first request.
+
   QuotaLimitHeuristic::Config short_limit_config = {
-      api::certificate_provider::kMaxClosedDialogsPerMinute,
+      api::certificate_provider::kMaxClosedDialogsPerMinute - 1,
       base::TimeDelta::FromMinutes(1)};
   heuristics->push_back(std::make_unique<QuotaService::TimedLimit>(
-      short_limit_config, new QuotaLimitHeuristic::SingletonBucketMapper(),
+      short_limit_config, new RequestPinExceptFirstQuotaBucketMapper,
       "MAX_PIN_DIALOGS_CLOSED_PER_MINUTE"));
 
   QuotaLimitHeuristic::Config long_limit_config = {
-      api::certificate_provider::kMaxClosedDialogsPer10Minutes,
+      api::certificate_provider::kMaxClosedDialogsPer10Minutes - 1,
       base::TimeDelta::FromMinutes(10)};
   heuristics->push_back(std::make_unique<QuotaService::TimedLimit>(
-      long_limit_config, new QuotaLimitHeuristic::SingletonBucketMapper(),
+      long_limit_config, new RequestPinExceptFirstQuotaBucketMapper,
       "MAX_PIN_DIALOGS_CLOSED_PER_10_MINUTES"));
 }
 
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
index cf69be6..507dbad 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
@@ -171,9 +171,9 @@
     CertificateProviderApiTest::TearDownOnMainThread();
   }
 
-  void AddFakeSignRequest() {
+  void AddFakeSignRequest(int sign_request_id) {
     cert_provider_service_->pin_dialog_manager()->AddSignRequestId(
-        extension_->id(), kFakeSignRequestId, {});
+        extension_->id(), sign_request_id, {});
   }
 
   void NavigateTo(const std::string& test_page_file_name) {
@@ -373,7 +373,7 @@
 
 // User enters the correct PIN.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, ShowPinDialogAccept) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("basic.html");
 
   // Enter the valid PIN.
@@ -386,7 +386,7 @@
 // User closes the dialog kMaxClosedDialogsPerMinute times, and the extension
 // should be blocked from showing it again.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, ShowPinDialogClose) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("basic.html");
 
   for (int i = 0;
@@ -411,7 +411,7 @@
 // User enters a wrong PIN first and a correct PIN on the second try.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest,
                        ShowPinDialogWrongPin) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("basic.html");
   EnterWrongPinAndWaitForMessage();
 
@@ -429,7 +429,7 @@
 // User enters wrong PIN three times.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest,
                        ShowPinDialogWrongPinThreeTimes) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("basic.html");
   for (int i = 0; i < kWrongPinAttemptsLimit; i++)
     EnterWrongPinAndWaitForMessage();
@@ -447,7 +447,7 @@
 // User closes the dialog while the extension is processing the request.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest,
                        ShowPinDialogCloseWhileProcessing) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommandAndWaitForMessage("Request", "request1:begun"));
@@ -473,7 +473,7 @@
        i <
        extensions::api::certificate_provider::kMaxClosedDialogsPerMinute + 1;
        i++) {
-    AddFakeSignRequest();
+    AddFakeSignRequest(kFakeSignRequestId);
     EXPECT_TRUE(SendCommandAndWaitForMessage(
         "Request", base::StringPrintf("request%d:begun", i + 1)));
 
@@ -483,7 +483,7 @@
     EXPECT_FALSE(GetActivePinDialogView());
   }
 
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   EXPECT_TRUE(SendCommandAndWaitForMessage(
       "Request",
       base::StringPrintf(
@@ -496,7 +496,7 @@
 
 // Extension erroneously attempts to close the PIN dialog twice.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, DoubleClose) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommand("Request"));
@@ -516,14 +516,14 @@
        i <
        extensions::api::certificate_provider::kMaxClosedDialogsPerMinute + 1;
        i++) {
-    AddFakeSignRequest();
+    AddFakeSignRequest(kFakeSignRequestId);
     EXPECT_TRUE(SendCommand("Request"));
     EXPECT_TRUE(SendCommandAndWaitForMessage(
         "Stop", base::StringPrintf("stop%d:success", i + 1)));
     EXPECT_FALSE(GetActivePinDialogView());
   }
 
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   EXPECT_TRUE(SendCommandAndWaitForMessage(
       "Request",
       base::StringPrintf(
@@ -538,7 +538,7 @@
 // the user provided any input.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest,
                        StopWithErrorBeforeInput) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommand("Request"));
@@ -558,7 +558,7 @@
 
 // Extension specifies zero left attempts in the very first PIN request.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, ZeroAttemptsAtStart) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommandAndWaitForMessage("RequestWithZeroAttempts",
@@ -574,7 +574,7 @@
 
 // Extension erroneously passes a negative attempts left count.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, NegativeAttempts) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommandAndWaitForMessage(
@@ -584,7 +584,7 @@
 
 // Extension erroneously attempts to close a non-existing dialog.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, CloseNonExisting) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommandAndWaitForMessage(
@@ -594,7 +594,7 @@
 
 // Extension erroneously attempts to stop a non-existing dialog with an error.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, StopNonExisting) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommandAndWaitForMessage(
@@ -606,7 +606,7 @@
 // user closed the previously stopped with an error PIN request.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest,
                        UpdateAlreadyStopped) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommandAndWaitForMessage("Request", "request1:begun"));
@@ -623,7 +623,7 @@
 // Extension starts a new PIN request after it stopped the previous one with an
 // error.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, StartAfterStop) {
-  AddFakeSignRequest();
+  AddFakeSignRequest(kFakeSignRequestId);
   NavigateTo("operated.html");
 
   EXPECT_TRUE(SendCommandAndWaitForMessage("Request", "request1:begun"));
@@ -638,3 +638,28 @@
   EXPECT_TRUE(listener.WaitUntilSatisfied());
   EXPECT_FALSE(GetActivePinDialogView()->textfield_for_testing()->GetEnabled());
 }
+
+// Test that no quota is applied to the first PIN requests for each requestId.
+IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest,
+                       RepeatedCloseWithDifferentIds) {
+  NavigateTo("operated.html");
+
+  for (int i = 0;
+       i <
+       extensions::api::certificate_provider::kMaxClosedDialogsPer10Minutes + 2;
+       i++) {
+    AddFakeSignRequest(kFakeSignRequestId + i);
+    EXPECT_TRUE(SendCommandAndWaitForMessage(
+        "Request", base::StringPrintf("request%d:begun", i + 1)));
+
+    ExtensionTestMessageListener listener(
+        base::StringPrintf("request%d:empty", i + 1), false);
+    ASSERT_TRUE(GetActivePinDialogView());
+    GetActivePinDialogView()->GetWidget()->CloseWithReason(
+        views::Widget::ClosedReason::kCloseButtonClicked);
+    EXPECT_TRUE(listener.WaitUntilSatisfied());
+    EXPECT_FALSE(GetActivePinDialogView());
+
+    EXPECT_TRUE(SendCommand("IncrementRequestId"));
+  }
+}
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index 8a6867d..afc7eb0 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -35,6 +35,7 @@
 #include "net/base/net_errors.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
@@ -65,6 +66,14 @@
                    handle->GetFrameTreeNodeId());
 }
 
+void RecordFeatureUsage(content::RenderFrameHost* rfh,
+                        blink::mojom::WebFeature web_feature) {
+  page_load_metrics::mojom::PageLoadFeatures page_load_features(
+      {web_feature}, {} /* css_properties */, {} /* animated_css_properties */);
+  page_load_metrics::MetricsWebContentsObserver::RecordFeatureUsage(
+      rfh, page_load_features);
+}
+
 using ResourceMimeType = AdsPageLoadMetricsObserver::ResourceMimeType;
 const char kDisallowedByBlocklistHistogramName[] =
     "PageLoad.Clients.Ads.HeavyAds.DisallowedByBlocklist";
@@ -202,6 +211,9 @@
       render_frame_host, render_frame_host->GetLastCommittedURL(),
       heavy_ads::PrepareHeavyAdPage(), net::ERR_BLOCKED_BY_CLIENT);
 
+  RecordFeatureUsage(render_frame_host,
+                     blink::mojom::WebFeature::kHeavyAdIntervention);
+
   ADS_HISTOGRAM("HeavyAds.InterventionType2", UMA_HISTOGRAM_ENUMERATION,
                 FrameData::FrameVisibility::kAnyVisibility,
                 frame_data->heavy_ad_status_with_noise());
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
index 11d79cd..b5e804b 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
@@ -44,6 +44,7 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
@@ -907,6 +908,9 @@
 
   histogram_tester.ExpectUniqueSample(kHeavyAdInterventionTypeHistogramId,
                                       FrameData::HeavyAdStatus::kNetwork, 1);
+  histogram_tester.ExpectBucketCount(
+      "Blink.UseCounter.Features",
+      blink::mojom::WebFeature::kHeavyAdIntervention, 1);
 }
 
 // Check that when the heavy ad feature is disabled we don't navigate
@@ -938,6 +942,10 @@
   // load is not synchronous. Instead check that we didn't log intervention UMA
   // that is always recorded when the intervention occurs.
   histogram_tester.ExpectTotalCount(kHeavyAdInterventionTypeHistogramId, 0);
+
+  histogram_tester.ExpectBucketCount(
+      "Blink.UseCounter.Features",
+      blink::mojom::WebFeature::kHeavyAdIntervention, 0);
 }
 
 // Check that we don't activate a HeavyAdIntervention field trial if we don't
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 83918c5..b1c7e21a 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -828,7 +828,6 @@
   if (PasswordGenerationController::AllowedForWebContents(web_contents())) {
     PasswordManagerDriver* driver = driver_factory_->GetDriverForFrame(
         password_generation_driver_bindings_.GetCurrentTargetFrame());
-    DCHECK(driver);
 
     PasswordGenerationController* generation_controller =
         PasswordGenerationController::GetIfExisting(web_contents());
@@ -860,7 +859,6 @@
     return;
   auto* driver = driver_factory_->GetDriverForFrame(
       password_generation_driver_bindings_.GetCurrentTargetFrame());
-  DCHECK(driver);
   gfx::RectF element_bounds_in_screen_space =
       GetBoundsInScreenSpace(TransformToRootCoordinates(
           password_generation_driver_bindings_.GetCurrentTargetFrame(),
@@ -1158,7 +1156,6 @@
     password_manager::ContentPasswordManagerDriver* driver,
     const autofill::password_generation::PasswordGenerationUIData& ui_data,
     bool is_manually_triggered) {
-  DCHECK(driver);
   gfx::RectF element_bounds_in_top_frame_space =
       TransformToRootCoordinates(driver->render_frame_host(), ui_data.bounds);
   if (!is_manually_triggered &&
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.cc b/chrome/browser/password_manager/password_accessory_controller_impl.cc
index fe6c7b7..f770783 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.cc
@@ -98,12 +98,8 @@
   password_manager::ContentPasswordManagerDriverFactory* factory =
       password_manager::ContentPasswordManagerDriverFactory::FromWebContents(
           web_contents_);
-  DCHECK(factory);
   password_manager::ContentPasswordManagerDriver* driver =
       factory->GetDriverForFrame(web_contents_->GetFocusedFrame());
-  if (!driver) {
-    return;
-  }  // |driver| can be NULL if the tab is being closed.
   driver->FillIntoFocusedField(selection.is_obfuscated(),
                                selection.display_text());
 }
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index dfab012..3d716ec 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -1900,7 +1900,6 @@
       ObservingAutofillClient::FromWebContents(WebContents());
   password_manager::ContentPasswordManagerDriver* driver =
       driver_factory->GetDriverForFrame(WebContents()->GetMainFrame());
-  DCHECK(driver);
   driver->GetPasswordAutofillManager()->set_autofill_client(
       observing_autofill_client);
 
diff --git a/chrome/browser/previews/previews_lite_page_redirect_url_loader.cc b/chrome/browser/previews/previews_lite_page_redirect_url_loader.cc
index a757dc7..fb2fc0d 100644
--- a/chrome/browser/previews/previews_lite_page_redirect_url_loader.cc
+++ b/chrome/browser/previews/previews_lite_page_redirect_url_loader.cc
@@ -12,6 +12,7 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 #include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/previews_lite_page_redirect.h"
 #include "content/public/browser/browser_context.h"
@@ -130,6 +131,9 @@
         network_loader_factory,
     int frame_tree_node_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(data_reduction_proxy::DataReductionProxyRequestOptions::
+             GetSessionKeyFromRequestHeaders(chrome_proxy_headers)
+                 .has_value());
 
   GURL original_url = modified_resource_request_.url;
   GURL lite_page_url =
diff --git a/chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor.cc b/chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor.cc
index d27ee6b..66fc4460 100644
--- a/chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor.cc
+++ b/chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "base/stl_util.h"
@@ -71,8 +72,14 @@
     uint64_t page_id) {
   net::HttpRequestHeaders headers;
   // Return empty headers for unittests.
-  if (!browser_context)
+  if (!browser_context) {
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            "add-chrome-proxy-header-for-lpr-tests")) {
+      headers.SetHeader(data_reduction_proxy::chrome_proxy_header(),
+                        "s=secret");
+    }
     return headers;
+  }
 
   auto* settings =
       DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
diff --git a/chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor_unittest.cc b/chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor_unittest.cc
index a20b2f9..1f6bdbe 100644
--- a/chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_redirect_url_loader_interceptor_unittest.cc
@@ -74,6 +74,9 @@
   void TearDown() override {}
 
   void SetUp() override {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        "add-chrome-proxy-header-for-lpr-tests");
+
     interceptor_ =
         std::make_unique<PreviewsLitePageRedirectURLLoaderInterceptor>(
             shared_factory_, 1, 2);
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index a6e6114..ec9c5b8 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -544,9 +544,6 @@
   // Set up password manager stuff.
   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
       web_contents(), nullptr);
-  password_manager::ContentPasswordManagerDriverFactory::FromWebContents(
-      web_contents())
-      ->RenderFrameCreated(web_contents()->GetMainFrame());
 
   NavigateAndCommit(GURL("http://www.foo.com/"));
   content::ContextMenuParams params = CreateParams(MenuItem::EDITABLE);
@@ -568,9 +565,6 @@
   // Set up password manager stuff.
   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
       incognito_web_contents.get(), nullptr);
-  password_manager::ContentPasswordManagerDriverFactory::FromWebContents(
-      incognito_web_contents.get())
-      ->RenderFrameCreated(incognito_web_contents->GetMainFrame());
 
   content::WebContentsTester::For(incognito_web_contents.get())
       ->NavigateAndCommit(GURL("http://www.foo.com/"));
diff --git a/chrome/browser/resources/chromeos/login/md_screen_container.css b/chrome/browser/resources/chromeos/login/md_screen_container.css
index 2c6f349..5ff1570 100644
--- a/chrome/browser/resources/chromeos/login/md_screen_container.css
+++ b/chrome/browser/resources/chromeos/login/md_screen_container.css
@@ -114,6 +114,7 @@
   height: 100%;
   max-height: 864px; /* 768 + 48*2 */
   max-width: 864px; /* 768 + 48*2 */
+  min-height: 560px; /* 464 + 48*2 */
   padding-bottom: 48px;
   padding-inline-end: 48px;
   padding-inline-start: 48px;
diff --git a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
index 3c0f854..7c87df1 100644
--- a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
+++ b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
@@ -29,6 +29,11 @@
   <template>
     <link rel="stylesheet" href="oobe_popup_overlay.css">
     <style include="shared-style multidevice-setup-shared ">
+      multidevice-setup {
+        --multidevice-setup-height: var(--oobe-dialog-host-height);
+        --multidevice-setup-width: var(--oobe-dialog-host-width);
+      }
+
       #multidevice-help-overlay-container {
         width: 768px;
       }
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
index 0d0a8da..ec5cbd7 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
@@ -10,6 +10,11 @@
 <dom-module id="multidevice-setup-post-oobe">
   <template>
     <style include="multidevice-setup-shared">
+      multidevice-setup {
+        --multidevice-setup-height: 640px;
+        --multidevice-setup-width: 768px;
+      }
+
       #backward-button,
       #cancel-button,
       #forward-button {
diff --git a/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h b/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h
index 7d3c6d3..8c67af0 100644
--- a/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h
+++ b/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h
@@ -78,10 +78,6 @@
   void RemoveRequest(InProgressRequest* request);
   void MaybeDestroySelf();
 
-  const content::WebContents::Getter& web_contents_getter() {
-    return web_contents_getter_;
-  }
-
   std::unique_ptr<HeaderModificationDelegate> delegate_;
   content::WebContents::Getter web_contents_getter_;
 
diff --git a/chrome/browser/signin/dice_response_handler.h b/chrome/browser/signin/dice_response_handler.h
index bc55f76..ab6eac1 100644
--- a/chrome/browser/signin/dice_response_handler.h
+++ b/chrome/browser/signin/dice_response_handler.h
@@ -120,11 +120,6 @@
   // Deletes the token fetcher.
   void DeleteTokenFetcher(DiceTokenFetcher* token_fetcher);
 
-  // Returns true if it is acceptable to get a new token for the account.
-  // Always returns true when using kDice.
-  bool CanGetTokenForAccount(const std::string& gaia_id,
-                             const std::string& email);
-
   // Process the Dice signin action.
   void ProcessDiceSigninHeader(
       const std::string& gaia_id,
diff --git a/chrome/browser/signin/signin_global_error.cc b/chrome/browser/signin/signin_global_error.cc
index f1f279ea..f479568 100644
--- a/chrome/browser/signin/signin_global_error.cc
+++ b/chrome/browser/signin/signin_global_error.cc
@@ -47,13 +47,6 @@
   return HasMenuItem();
 }
 
-void SigninGlobalError::AttemptToFixError(Browser* browser) {
-  if (!HasError())
-    return;
-
-  ExecuteMenuItem(browser);
-}
-
 void SigninGlobalError::Shutdown() {
   error_controller_->RemoveObserver(this);
   error_controller_ = NULL;
diff --git a/chrome/browser/signin/signin_global_error.h b/chrome/browser/signin/signin_global_error.h
index 320b39f..9240265 100644
--- a/chrome/browser/signin/signin_global_error.h
+++ b/chrome/browser/signin/signin_global_error.h
@@ -27,10 +27,6 @@
   // Returns true if there is an authentication error.
   bool HasError();
 
-  // Shows re-authentication UI to the user in an attempt to fix the error.
-  // The re-authentication UI will be shown in |browser|.
-  void AttemptToFixError(Browser* browser);
-
  private:
   FRIEND_TEST_ALL_PREFIXES(SigninGlobalErrorTest, Basic);
   FRIEND_TEST_ALL_PREFIXES(SigninGlobalErrorTest, AuthStatusEnumerateAllErrors);
diff --git a/chrome/browser/signin/signin_ui_util.cc b/chrome/browser/signin/signin_ui_util.cc
index b92d70963..9a1452c 100644
--- a/chrome/browser/signin/signin_ui_util.cc
+++ b/chrome/browser/signin/signin_ui_util.cc
@@ -175,23 +175,6 @@
 }
 }  // namespace internal
 
-std::string GetDisplayEmail(Profile* profile, const std::string& account_id) {
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(profile);
-  std::string email =
-      identity_manager
-          ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId(
-              account_id)
-          ->email;
-  if (email.empty()) {
-    DCHECK_EQ(
-        signin::IdentityManager::AccountIdMigrationState::MIGRATION_NOT_STARTED,
-        identity_manager->GetAccountIdMigrationState());
-    return account_id;
-  }
-  return email;
-}
-
 std::vector<AccountInfo> GetAccountsForDicePromos(Profile* profile) {
   // Fetch account ids for accounts that have a token.
   signin::IdentityManager* identity_manager =
diff --git a/chrome/browser/signin/signin_ui_util.h b/chrome/browser/signin/signin_ui_util.h
index 156f0b1..ca329ae 100644
--- a/chrome/browser/signin/signin_ui_util.h
+++ b/chrome/browser/signin/signin_ui_util.h
@@ -53,12 +53,6 @@
                          bool is_default_promo_account);
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
-// Returns the display email string for the given account.  If the profile
-// has not been migrated to use gaia ids, then its possible for the display
-// to not ne known yet.  In this case, use |account_id|, which is assumed to
-// be an email address.
-std::string GetDisplayEmail(Profile* profile, const std::string& account_id);
-
 // Returns the list of all accounts that have a token. The default account in
 // the Gaia cookies will be the first account in the list.
 std::vector<AccountInfo> GetAccountsForDicePromos(Profile* profile);
diff --git a/chrome/browser/touch_to_fill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
index 49f44f5..7792ea5 100644
--- a/chrome/browser/touch_to_fill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
@@ -12,6 +12,7 @@
     "//base:jni_java",
     "//chrome/android:chrome_java",
     "//chrome/browser/touch_to_fill/android:public_java",
+    "//chrome/browser/util/android:java",
     "//ui/android:ui_java",
   ]
 
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml
index 745ffca..af57e8a5 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml
@@ -8,37 +8,48 @@
     android:descendantFocusability="blocksDescendants"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="70dp"
+    android:minHeight="72dp"
     android:gravity="center_vertical"
     android:orientation="horizontal"
     android:background="@drawable/touch_to_fill_credential_background">
 
     <ImageView
         android:id="@+id/favicon"
-        android:layout_width="20dp"
-        android:layout_height="20dp"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_marginStart="12dp"
         android:importantForAccessibility="no"
         android:layout_gravity="center"/>
     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_margin="8dp"
-        android:layout_marginStart="16dp"
+        android:layout_margin="6dp"
+        android:layout_marginStart="12dp"
         android:layout_weight="1"
         android:orientation="vertical">
         <TextView
+            android:id="@+id/credential_origin"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minHeight="20dp"
+            android:ellipsize="start"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.BlackBody" />
+        <TextView
             android:id="@+id/username"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:minHeight="20dp"
             android:ellipsize="end"
             android:singleLine="true"
-            android:textAppearance="@style/TextAppearance.BlackTitle1" />
+            android:textAppearance="@style/TextAppearance.BlackBodyDefault" />
         <TextView
             android:id="@+id/password"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:minHeight="20dp"
             android:ellipsize="end"
             android:singleLine="true"
-            android:textAppearance="@style/TextAppearance.BlackBody" />
+            android:textAppearance="@style/TextAppearance.BlackBodyDefault" />
     </LinearLayout>
 </LinearLayout>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
index 644da774..d6ab53b 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
@@ -8,6 +8,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FORMATTED_URL;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VIEW_EVENT_LISTENER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.VISIBLE;
+import static org.chromium.chrome.browser.util.UrlUtilities.stripScheme;
 
 import android.text.method.PasswordTransformationMethod;
 import android.view.LayoutInflater;
@@ -41,6 +42,12 @@
      * @param credential The {@link Credential} whose data needs to be displayed.
      */
     static void bindCredentialView(View view, Credential credential) {
+        TextView pslOriginText = view.findViewById(R.id.credential_origin);
+        String formattedOrigin = stripScheme(credential.getOriginUrl());
+        formattedOrigin = formattedOrigin.replaceFirst("/$", ""); // Strip possibly trailing slash.
+        pslOriginText.setText(formattedOrigin);
+        pslOriginText.setVisibility(credential.isPublicSuffixMatch() ? View.VISIBLE : View.GONE);
+
         TextView usernameText = view.findViewById(R.id.username);
         usernameText.setText(credential.getFormattedUsername());
 
diff --git a/chrome/browser/touch_to_fill/android/java/src/org/chromium/chrome/browser/touch_to_fill/data/Credential.java b/chrome/browser/touch_to_fill/android/java/src/org/chromium/chrome/browser/touch_to_fill/data/Credential.java
index eb3ed519..934b2e2 100644
--- a/chrome/browser/touch_to_fill/android/java/src/org/chromium/chrome/browser/touch_to_fill/data/Credential.java
+++ b/chrome/browser/touch_to_fill/android/java/src/org/chromium/chrome/browser/touch_to_fill/data/Credential.java
@@ -24,6 +24,7 @@
      */
     public Credential(String username, String password, String formattedUsername, String originUrl,
             boolean isPublicSuffixMatch) {
+        assert originUrl != null : "Credential origin is null! Pass an empty one instead.";
         mUsername = username;
         mPassword = password;
         mFormattedUsername = formattedUsername;
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
index f1c82c6..d3498f7 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
@@ -18,6 +18,7 @@
 
 import android.support.test.filters.MediumTest;
 import android.text.method.PasswordTransformationMethod;
+import android.view.View;
 import android.widget.ListView;
 import android.widget.TextView;
 
@@ -111,20 +112,31 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mTouchToFillView.setVisible(true);
             mModel.get(CREDENTIAL_LIST)
-                    .addAll(Arrays.asList(new Credential("Ana", "S3cr3t", "Ana", null, false),
-                            new Credential("", "***", "No Username", "m.example.xyz", true)));
+                    .addAll(Arrays.asList(new Credential("Ana", "S3cr3t", "Ana", "", false),
+                            new Credential("", "***", "No Username", "http://m.example.xyz/", true),
+                            new Credential(
+                                    "Bob", "***", "Bob", "http://mobile.example.xyz", true)));
         });
 
         pollUiThread(() -> getBottomSheetState() == SheetState.FULL);
-        assertThat(getCredentials().getChildCount(), is(2));
+        assertThat(getCredentials().getChildCount(), is(3));
+        assertThat(getCredentialOriginAt(0).getVisibility(), is(View.GONE));
         assertThat(getCredentialNameAt(0).getText(), is("Ana"));
         assertThat(getCredentialPasswordAt(0).getText(), is("S3cr3t"));
         assertThat(getCredentialPasswordAt(0).getTransformationMethod(),
                 instanceOf(PasswordTransformationMethod.class));
+        assertThat(getCredentialOriginAt(1).getVisibility(), is(View.VISIBLE));
+        assertThat(getCredentialOriginAt(1).getText(), is("m.example.xyz"));
         assertThat(getCredentialNameAt(1).getText(), is("No Username"));
         assertThat(getCredentialPasswordAt(1).getText(), is("***"));
         assertThat(getCredentialPasswordAt(1).getTransformationMethod(),
                 instanceOf(PasswordTransformationMethod.class));
+        assertThat(getCredentialOriginAt(2).getVisibility(), is(View.VISIBLE));
+        assertThat(getCredentialOriginAt(2).getText(), is("mobile.example.xyz"));
+        assertThat(getCredentialNameAt(2).getText(), is("Bob"));
+        assertThat(getCredentialPasswordAt(2).getText(), is("***"));
+        assertThat(getCredentialPasswordAt(2).getTransformationMethod(),
+                instanceOf(PasswordTransformationMethod.class));
     }
 
     @Test
@@ -180,6 +192,10 @@
         return getCredentials().getChildAt(index).findViewById(R.id.password);
     }
 
+    private TextView getCredentialOriginAt(int index) {
+        return getCredentials().getChildAt(index).findViewById(R.id.credential_origin);
+    }
+
     TouchToFillProperties.ViewEventListener waitForEvent() {
         return verify(mMockListener,
                 timeout(ScalableTimeout.scaleTimeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL)));
diff --git a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
index 878b27c..9cd0b06 100644
--- a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
+++ b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
@@ -36,7 +36,7 @@
 public class TouchToFillControllerTest {
     private static final String TEST_URL = "www.example.xyz";
     private static final String TEST_MOBILE_URL = "www.example.xyz";
-    private static final Credential ANA = new Credential("Ana", "S3cr3t", "Ana", null, false);
+    private static final Credential ANA = new Credential("Ana", "S3cr3t", "Ana", "", false);
     private static final Credential BOB =
             new Credential("Bob", "*****", "Bob", TEST_MOBILE_URL, true);
     private static final Credential CARL = new Credential("Carl", "G3h3!m", "Carl", "", false);
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_controller.cc b/chrome/browser/ui/cookie_controls/cookie_controls_controller.cc
index 5aaefb7..f0e2456 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_controller.cc
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_controller.cc
@@ -54,8 +54,7 @@
         this, TabSpecificContentSettings::FromWebContents(web_contents));
   }
   for (auto& observer : observers_)
-    observer.OnStatusChanged(GetStatus(web_contents));
-  PresentBlockedCookieCounter();
+    observer.OnStatusChanged(GetStatus(web_contents), GetBlockedCookieCount());
 }
 
 CookieControlsController::Status CookieControlsController::GetStatus(
@@ -87,19 +86,17 @@
   Update(GetWebContents());
 }
 
-int CookieControlsController::GetBlockedDomainCount() {
+int CookieControlsController::GetBlockedCookieCount() {
   const LocalSharedObjectsContainer& blocked_objects =
       tab_observer_->tab_specific_content_settings()
           ->blocked_local_shared_objects();
-  return blocked_objects.GetDomainCount();
+  return blocked_objects.GetObjectCount();
 }
 
 void CookieControlsController::PresentBlockedCookieCounter() {
-  const LocalSharedObjectsContainer& blocked_objects =
-      tab_observer_->tab_specific_content_settings()
-          ->blocked_local_shared_objects();
+  int blocked_cookies = GetBlockedCookieCount();
   for (auto& observer : observers_)
-    observer.OnBlockedCookiesCountChanged(blocked_objects.GetObjectCount());
+    observer.OnBlockedCookiesCountChanged(blocked_cookies);
 }
 
 void CookieControlsController::OnPrefChanged() {
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_controller.h b/chrome/browser/ui/cookie_controls/cookie_controls_controller.h
index d945a7a..2481c483 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_controller.h
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_controller.h
@@ -47,8 +47,6 @@
   // blocking.
   void OnCookieBlockingEnabledForSite(bool block_third_party_cookies);
 
-  // Returns the number of registrable domains with blocked cookies.
-  int GetBlockedDomainCount();
 
   void AddObserver(CookieControlsView* obs);
   void RemoveObserver(CookieControlsView* obs);
@@ -79,6 +77,9 @@
   // Updates the blocked cookie count of |icon_|.
   void PresentBlockedCookieCounter();
 
+  // Returns the number of blocked cookies.
+  int GetBlockedCookieCount();
+
   // Callback for when the cookie controls or third-party cookie blocking
   // preference changes.
   void OnPrefChanged();
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
index f2396a8..cd0c50c 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
@@ -20,7 +20,7 @@
 
 class MockCookieControlsView : public CookieControlsView {
  public:
-  MOCK_METHOD1(OnStatusChanged, void(CookieControlsController::Status));
+  MOCK_METHOD2(OnStatusChanged, void(CookieControlsController::Status, int));
   MOCK_METHOD1(OnBlockedCookiesCountChanged, void(int));
 };
 
@@ -80,8 +80,7 @@
 
 TEST_F(CookieControlsTest, NewTabPage) {
   EXPECT_CALL(*mock(),
-              OnStatusChanged(CookieControlsController::Status::kDisabled));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+              OnStatusChanged(CookieControlsController::Status::kDisabled, 0));
   cookie_controls()->Update(web_contents());
 }
 
@@ -89,8 +88,7 @@
   // Visiting a website should enable the UI.
   NavigateAndCommit(GURL("https://example.com"));
   EXPECT_CALL(*mock(),
-              OnStatusChanged(CookieControlsController::Status::kEnabled));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+              OnStatusChanged(CookieControlsController::Status::kEnabled, 0));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -98,36 +96,31 @@
   EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
   tab_specific_content_settings()->OnWebDatabaseAccessed(
       GURL("https://example.com"), /*blocked=*/false);
-  EXPECT_EQ(0, cookie_controls()->GetBlockedDomainCount());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Blocking cookies should update the blocked cookie count.
   EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(1));
   tab_specific_content_settings()->OnWebDatabaseAccessed(
       GURL("https://thirdparty.com"), /*blocked=*/true);
-  EXPECT_EQ(1, cookie_controls()->GetBlockedDomainCount());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Navigating somewhere else should reset the cookie count.
   NavigateAndCommit(GURL("https://somethingelse.com"));
   EXPECT_CALL(*mock(),
-              OnStatusChanged(CookieControlsController::Status::kEnabled));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+              OnStatusChanged(CookieControlsController::Status::kEnabled, 0));
   cookie_controls()->Update(web_contents());
 }
 
 TEST_F(CookieControlsTest, PreferenceDisabled) {
   NavigateAndCommit(GURL("https://example.com"));
   EXPECT_CALL(*mock(),
-              OnStatusChanged(CookieControlsController::Status::kEnabled));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+              OnStatusChanged(CookieControlsController::Status::kEnabled, 0));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Disabling the feature should disable the UI.
   EXPECT_CALL(*mock(),
-              OnStatusChanged(CookieControlsController::Status::kDisabled));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+              OnStatusChanged(CookieControlsController::Status::kDisabled, 0));
   profile()->GetPrefs()->SetInteger(
       prefs::kCookieControlsMode,
       static_cast<int>(content_settings::CookieControlsMode::kOff));
@@ -137,38 +130,35 @@
 TEST_F(CookieControlsTest, DisableForSite) {
   NavigateAndCommit(GURL("https://example.com"));
   EXPECT_CALL(*mock(),
-              OnStatusChanged(CookieControlsController::Status::kEnabled));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+              OnStatusChanged(CookieControlsController::Status::kEnabled, 0));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Disabling cookie blocking for example.com should update the ui.
-  EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsController::Status::kDisabledForSite));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+  EXPECT_CALL(
+      *mock(),
+      OnStatusChanged(CookieControlsController::Status::kDisabledForSite, 0));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Visiting some other site, should switch back to kEnabled.
   NavigateAndCommit(GURL("https://somethingelse.com"));
   EXPECT_CALL(*mock(),
-              OnStatusChanged(CookieControlsController::Status::kEnabled));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+              OnStatusChanged(CookieControlsController::Status::kEnabled, 0));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Visiting example.com should set status to kDisabledForSite.
   NavigateAndCommit(GURL("https://example.com"));
-  EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsController::Status::kDisabledForSite));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+  EXPECT_CALL(
+      *mock(),
+      OnStatusChanged(CookieControlsController::Status::kDisabledForSite, 0));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Enabling example.com again should change status to kEnabled.
   EXPECT_CALL(*mock(),
-              OnStatusChanged(CookieControlsController::Status::kEnabled));
-  EXPECT_CALL(*mock(), OnBlockedCookiesCountChanged(0));
+              OnStatusChanged(CookieControlsController::Status::kEnabled, 0));
   cookie_controls()->OnCookieBlockingEnabledForSite(true);
   testing::Mock::VerifyAndClearExpectations(mock());
 }
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_view.h b/chrome/browser/ui/cookie_controls/cookie_controls_view.h
index 71f3210..9616dd3 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_view.h
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_view.h
@@ -11,7 +11,8 @@
 // Interface for the CookieControls UI.
 class CookieControlsView : public base::CheckedObserver {
  public:
-  virtual void OnStatusChanged(CookieControlsController::Status status) = 0;
+  virtual void OnStatusChanged(CookieControlsController::Status status,
+                               int blocked_cookies) = 0;
   virtual void OnBlockedCookiesCountChanged(int blocked_cookies) = 0;
 };
 
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls_bubble_view.cc b/chrome/browser/ui/views/location_bar/cookie_controls_bubble_view.cc
index 1c30735..bbcbcca 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls_bubble_view.cc
@@ -18,7 +18,7 @@
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/link.h"
-#include "ui/views/layout/fill_layout.h"
+#include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_client_view.h"
 
@@ -55,19 +55,22 @@
 }
 
 void CookieControlsBubbleView::OnStatusChanged(
-    CookieControlsController::Status new_status) {
-  if (status_ == new_status)
+    CookieControlsController::Status new_status,
+    int blocked_cookies) {
+  if (status_ == new_status) {
+    OnBlockedCookiesCountChanged(blocked_cookies);
     return;
-
-  show_disable_cookie_blocking_ui_ = false;
+  }
+  intermediate_step_ = IntermediateStep::kNone;
   status_ = new_status;
+  blocked_cookies_ = blocked_cookies;
   UpdateUi();
 }
 
 void CookieControlsBubbleView::OnBlockedCookiesCountChanged(
     int blocked_cookies) {
   // The blocked cookie count changes quite frequently, so avoid unnecessary
-  // UI updates and unnecessarily calling GetBlockedDomainCount() if possible.
+  // UI updates if possible.
   if (blocked_cookies_ == blocked_cookies)
     return;
 
@@ -75,14 +78,12 @@
       !blocked_cookies_ || (*blocked_cookies_ > 0) != (blocked_cookies > 0);
   blocked_cookies_ = blocked_cookies;
 
-  text_->SetText(
-      l10n_util::GetPluralStringFUTF16(IDS_COOKIE_CONTROLS_BLOCKED_MESSAGE,
-                                       controller_->GetBlockedDomainCount()));
-
-  // If this only incremented the number of blocked sites, no UI update is
-  // necessary besides the |text_| text.
+  // If this only incremented the number of blocked sites, no full UI update is
+  // necessary besides the title text.
   if (has_blocked_changed)
     UpdateUi();
+  else
+    GetBubbleFrameView()->UpdateWindowTitle();
 }
 
 CookieControlsBubbleView::CookieControlsBubbleView(
@@ -111,10 +112,18 @@
   text_->SetVisible(false);
   header_view_->SetVisible(false);
 
-  if (show_disable_cookie_blocking_ui_) {
+  if (intermediate_step_ == IntermediateStep::kTurnOffButton) {
     text_->SetVisible(true);
     text_->SetText(
         l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_NOT_WORKING_DESCRIPTION));
+  } else if (intermediate_step_ == IntermediateStep::kBlockingIsOn) {
+    header_view_->SetVisible(true);
+    header_view_->SetImage(
+        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+            IDR_COOKIE_BLOCKING_ON_HEADER));
+    text_->SetVisible(true);
+    text_->SetText(
+        l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_BLOCKED_MESSAGE));
   } else if (status_ == CookieControlsController::Status::kEnabled) {
     header_view_->SetVisible(true);
     header_view_->SetImage(
@@ -122,6 +131,9 @@
             has_blocked_cookies ? IDR_COOKIE_BLOCKING_ON_HEADER
                                 : IDR_COOKIE_BLOCKING_INACTIVE_HEADER));
     text_->SetVisible(true);
+    text_->SetText(l10n_util::GetStringUTF16(
+        has_blocked_cookies ? IDS_COOKIE_CONTROLS_BLOCKED_MESSAGE
+                            : IDS_COOKIE_CONTROLS_NOTHING_BLOCKED_MESSAGE));
     not_working_link_->SetVisible(has_blocked_cookies);
     blocked_cookies_.reset();
   } else {
@@ -136,7 +148,7 @@
   // The show_disable_cookie_blocking_ui_ state has a different title
   // configuration. To avoid jumping UI, don't resize the bubble. This should be
   // safe as the bubble in this state has less content than in Enabled state.
-  if (!show_disable_cookie_blocking_ui_)
+  if (intermediate_step_ != IntermediateStep::kTurnOffButton)
     SizeToContents();
 }
 
@@ -148,7 +160,7 @@
 }
 
 int CookieControlsBubbleView::GetDialogButtons() const {
-  if (show_disable_cookie_blocking_ui_ ||
+  if (intermediate_step_ == IntermediateStep::kTurnOffButton ||
       status_ == CookieControlsController::Status::kDisabledForSite) {
     return ui::DIALOG_BUTTON_OK;
   }
@@ -157,16 +169,21 @@
 
 base::string16 CookieControlsBubbleView::GetDialogButtonLabel(
     ui::DialogButton button) const {
-  if (show_disable_cookie_blocking_ui_)
+  if (intermediate_step_ == IntermediateStep::kTurnOffButton)
     return l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_TURN_OFF_BUTTON);
   DCHECK_EQ(status_, CookieControlsController::Status::kDisabledForSite);
+  DCHECK_EQ(intermediate_step_, IntermediateStep::kNone);
   return l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_TURN_ON_BUTTON);
 }
 
 void CookieControlsBubbleView::Init() {
-  SetLayoutManager(std::make_unique<views::FillLayout>());
+  // Use a BoxLayout because the view might be larger than |text_| and we want
+  // |text_| at the top.
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
   auto text = std::make_unique<views::Label>(base::string16(),
-                                             views::style::CONTEXT_LABEL);
+                                             views::style::CONTEXT_LABEL,
+                                             views::style::STYLE_SECONDARY);
   text->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
   text->SetMultiLine(true);
   text_ = AddChildView(std::move(text));
@@ -196,11 +213,19 @@
 }
 
 base::string16 CookieControlsBubbleView::GetWindowTitle() const {
-  if (show_disable_cookie_blocking_ui_)
-    return l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_NOT_WORKING_TITLE);
+  switch (intermediate_step_) {
+    case IntermediateStep::kTurnOffButton:
+      return l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_NOT_WORKING_TITLE);
+    case IntermediateStep::kBlockingIsOn:
+      return l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_TURNED_ON_TITLE);
+    case IntermediateStep::kNone: {
+      // Determine title based on status_ instead.
+    }
+  }
   switch (status_) {
     case CookieControlsController::Status::kEnabled:
-      return l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_DIALOG_TITLE);
+      return l10n_util::GetPluralStringFUTF16(IDS_COOKIE_CONTROLS_DIALOG_TITLE,
+                                              blocked_cookies_.value_or(0));
     case CookieControlsController::Status::kDisabledForSite:
       return l10n_util::GetStringUTF16(IDS_COOKIE_CONTROLS_DIALOG_TITLE_OFF);
     case CookieControlsController::Status::kUninitialized:
@@ -230,11 +255,14 @@
 }
 
 bool CookieControlsBubbleView::Accept() {
-  if (show_disable_cookie_blocking_ui_) {
+  if (intermediate_step_ == IntermediateStep::kTurnOffButton) {
     controller_->OnCookieBlockingEnabledForSite(false);
   } else {
     DCHECK_EQ(status_, CookieControlsController::Status::kDisabledForSite);
+    DCHECK_EQ(intermediate_step_, IntermediateStep::kNone);
     controller_->OnCookieBlockingEnabledForSite(true);
+    intermediate_step_ = IntermediateStep::kBlockingIsOn;
+    UpdateUi();
   }
   return false;
 }
@@ -249,6 +277,6 @@
   DCHECK_EQ(status_, CookieControlsController::Status::kEnabled);
   // Don't go through the controller as this is an intermediary state that
   // is only relevant for the bubble UI.
-  show_disable_cookie_blocking_ui_ = true;
+  intermediate_step_ = IntermediateStep::kTurnOffButton;
   UpdateUi();
 }
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls_bubble_view.h b/chrome/browser/ui/views/location_bar/cookie_controls_bubble_view.h
index 79e17f1..78b0864 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/cookie_controls_bubble_view.h
@@ -38,10 +38,19 @@
   static CookieControlsBubbleView* GetCookieBubble();
 
   // CookieControlsView:
-  void OnStatusChanged(CookieControlsController::Status status) override;
+  void OnStatusChanged(CookieControlsController::Status status,
+                       int blocked_cookies) override;
   void OnBlockedCookiesCountChanged(int blocked_cookies) override;
 
  private:
+  enum class IntermediateStep {
+    kNone,
+    // Show a button to disable cookie blocking on the current site.
+    kTurnOffButton,
+    // Show a confirmation that cookie blocking was turned on.
+    kBlockingIsOn,
+  };
+
   CookieControlsBubbleView(views::View* anchor_view,
                            content::WebContents* web_contents,
                            CookieControlsController* cookie_contols);
@@ -72,9 +81,7 @@
   CookieControlsController::Status status_ =
       CookieControlsController::Status::kUninitialized;
 
-  // If true, display an intermediate step with a button to disable cookie
-  // blocking on the current site.
-  bool show_disable_cookie_blocking_ui_ = false;
+  IntermediateStep intermediate_step_ = IntermediateStep::kNone;
 
   base::Optional<int> blocked_cookies_;
 
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls_icon_view.cc b/chrome/browser/ui/views/location_bar/cookie_controls_icon_view.cc
index 617c706..95b616f 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls_icon_view.cc
@@ -40,12 +40,14 @@
 }
 
 void CookieControlsIconView::OnStatusChanged(
-    CookieControlsController::Status status) {
+    CookieControlsController::Status status,
+    int blocked_cookies) {
   if (status_ != status) {
     status_ = status;
     SetVisible(ShouldBeVisible());
     UpdateIconImage();
   }
+  OnBlockedCookiesCountChanged(blocked_cookies);
 }
 
 void CookieControlsIconView::OnBlockedCookiesCountChanged(int blocked_cookies) {
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls_icon_view.h b/chrome/browser/ui/views/location_bar/cookie_controls_icon_view.h
index 6669394..51d7066 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls_icon_view.h
+++ b/chrome/browser/ui/views/location_bar/cookie_controls_icon_view.h
@@ -21,7 +21,8 @@
   ~CookieControlsIconView() override;
 
   // CookieControlsUI:
-  void OnStatusChanged(CookieControlsController::Status status) override;
+  void OnStatusChanged(CookieControlsController::Status status,
+                       int blocked_cookies) override;
   void OnBlockedCookiesCountChanged(int blocked_cookies) override;
 
   // PageActionIconView:
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
index adddf25..12d4f78 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
@@ -80,6 +80,37 @@
   return 20;
 }
 
+// Returns the avatar image for the current profile. May be called only in
+// "normal" states where the user is guaranteed to have an avatar image (i.e.
+// not kGenericProfile, not kGuestSession and not kIncognitoProfile).
+const gfx::Image& GetAvatarImage(Profile* profile,
+                                 const gfx::Image& user_identity_image) {
+  ProfileAttributesEntry* entry = GetProfileAttributesEntry(profile);
+  DCHECK(entry);
+  // TODO(crbug.com/1012179): If kPersistUPAInProfileInfoCache feature is on, it
+  // should suffice to call entry->GetAvatarIcon(). For this to work well, this
+  // class needs to observe ProfileAttributesStorage instead of (or on top of)
+  // IdentityManager. Only then we can rely on |entry| being up to date (as the
+  // storage also observes IdentityManager so there's no guarantee on the order
+  // of notifications).
+  if (entry->IsUsingGAIAPicture() && entry->GetGAIAPicture())
+    return *entry->GetGAIAPicture();
+
+  // Show |user_identity_image| when the following conditions are satisfied:
+  //  - the user is migrated to Dice
+  //  - the user isn't syncing
+  //  - the profile icon wasn't explicitly changed
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile);
+  if (!user_identity_image.IsEmpty() &&
+      AccountConsistencyModeManager::IsDiceEnabledForProfile(profile) &&
+      !identity_manager->HasPrimaryAccount() && entry->IsUsingDefaultAvatar()) {
+    return user_identity_image;
+  }
+
+  return entry->GetAvatarIcon();
+}
+
 }  // namespace
 
 AvatarToolbarButton::AvatarToolbarButton(Browser* browser)
@@ -155,13 +186,13 @@
   // button is not yet added to the ToolbarView's hierarchy.
   if (!GetWidget())
     return;
-  gfx::Image gaia_image = GetGaiaImage();
-  SetImage(views::Button::STATE_NORMAL, GetAvatarIcon(gaia_image));
+  gfx::Image user_identity_image = GetUserIdentityImage();
+  SetImage(views::Button::STATE_NORMAL, GetAvatarIcon(user_identity_image));
 
   // TODO(crbug.com/990286): Get rid of this logic completely when we cache the
   // Google account image in the profile cache and thus it is always available.
   if (identity_animation_state_ == IdentityAnimationState::kWaitingForImage &&
-      !gaia_image.IsEmpty()) {
+      !user_identity_image.IsEmpty()) {
     ShowIdentityAnimation();
   }
 }
@@ -230,12 +261,6 @@
   SetHighlight(text, color);
 }
 
-void AvatarToolbarButton::SetAutofillIconVisible(bool autofill_icon_visible) {
-  DCHECK_NE(GetState(), State::kIncognitoProfile);
-  autofill_icon_visible_ = autofill_icon_visible;
-  UpdateText();
-}
-
 void AvatarToolbarButton::ShowAvatarHighlightAnimation() {
   DCHECK_NE(GetState(), State::kIncognitoProfile);
   DCHECK_NE(GetState(), State::kGuestSession);
@@ -448,7 +473,7 @@
 }
 
 gfx::ImageSkia AvatarToolbarButton::GetAvatarIcon(
-    const gfx::Image& gaia_image) const {
+    const gfx::Image& user_identity_image) const {
   const int icon_size = ui::MaterialDesignController::touch_ui()
                             ? kDefaultTouchableIconSize
                             : GetIconSizeForNonTouchUi();
@@ -472,15 +497,10 @@
     case State::kSyncError:
     case State::kSyncPaused:
     case State::kNormal:
-      if (!gaia_image.IsEmpty()) {
-        return profiles::GetSizedAvatarIcon(gaia_image, true, icon_size,
-                                            icon_size, profiles::SHAPE_CIRCLE)
-            .AsImageSkia();
-      }
       // Profile attributes are non-null since the state is not kGenericProfile.
-      gfx::Image avatar_icon =
-          GetProfileAttributesEntry(profile_)->GetAvatarIcon();
-      return profiles::GetSizedAvatarIcon(avatar_icon, true, icon_size,
+      const gfx::Image& avatar_image =
+          GetAvatarImage(profile_, user_identity_image);
+      return profiles::GetSizedAvatarIcon(avatar_image, true, icon_size,
                                           icon_size, profiles::SHAPE_CIRCLE)
           .AsImageSkia();
   }
@@ -488,35 +508,10 @@
   return gfx::ImageSkia();
 }
 
-gfx::Image AvatarToolbarButton::GetGaiaImage() const {
-  ProfileAttributesEntry* entry = GetProfileAttributesEntry(profile_);
-  if (!entry) {
-    // This can happen if the user deletes the current profile.
-    return gfx::Image();
-  }
-
-  // If there is a GAIA image available, try to use that.
-  if (entry->IsUsingGAIAPicture()) {
-    // The GetGAIAPicture API call will trigger an async image load from disk if
-    // it has not been loaded.
-    const gfx::Image* gaia_image = entry->GetGAIAPicture();
-
-    if (gaia_image)
-      return *gaia_image;
-    return gfx::Image();
-  }
-
-  // Try to show the first account icon of the sync promo when the following
-  // conditions are satisfied:
-  //  - the user is migrated to Dice
-  //  - the user isn't signed in
-  //  - the profile icon wasn't explicitly changed
+gfx::Image AvatarToolbarButton::GetUserIdentityImage() const {
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_);
-  if (AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_) &&
-      !identity_manager->HasPrimaryAccount() &&
-      identity_manager->HasUnconsentedPrimaryAccount() &&
-      entry->IsUsingDefaultAvatar()) {
+  if (identity_manager && identity_manager->HasUnconsentedPrimaryAccount()) {
     base::Optional<AccountInfo> account_info =
         identity_manager
             ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId(
@@ -553,7 +548,7 @@
 
 #if !defined(OS_CHROMEOS)
   if (identity_manager->HasPrimaryAccount() && profile_->IsSyncAllowed() &&
-      error_controller_.HasAvatarError() && !autofill_icon_visible_) {
+      error_controller_.HasAvatarError()) {
     // When DICE is enabled and the error is an auth error, the sync-paused
     // icon is shown.
     int unused;
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.h b/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
index 308d550..621e08e 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
@@ -36,7 +36,6 @@
 
   void UpdateIcon();
   void UpdateText();
-  void SetAutofillIconVisible(bool autofill_icon_visible);
   void ShowAvatarHighlightAnimation();
 
  private:
@@ -109,8 +108,12 @@
 
   base::string16 GetAvatarTooltipText() const;
   base::string16 GetProfileName() const;
-  gfx::ImageSkia GetAvatarIcon(const gfx::Image& gaia_image) const;
-  gfx::Image GetGaiaImage() const;
+  gfx::ImageSkia GetAvatarIcon(const gfx::Image& user_identity_image) const;
+
+  // Returns the image of the unconsented primary account (if exists and already
+  // loaded), otherwise empty.
+  gfx::Image GetUserIdentityImage() const;
+
   State GetState() const;
 
   void SetInsets();
@@ -132,11 +135,6 @@
   // button sync paused/error state and update highlight color.
   bool highlight_animation_visible_ = false;
 
-  // Whether any autofill icon is visible in |this|'s parent container. Set by
-  // |ToolbarPageActionIconContainerView|. If true, hide avatar button sync
-  // paused/error state.
-  bool autofill_icon_visible_ = false;
-
   IdentityAnimationState identity_animation_state_ =
       IdentityAnimationState::kNotShowing;
 
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index e0c5595..f452966 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -296,7 +296,7 @@
   gfx::ImageSkia sized_image =
       image.isNull()
           ? gfx::CreateVectorIcon(kUserAccountAvatarIcon, kIdentityImageSize,
-                                  kIdentityImageSize)
+                                  GetDefaultIconColor())
           : CropCircle(SizeImage(image, kIdentityImageSize));
   gfx::ImageSkia sized_badge =
       AddCircularBackground(SizeImage(badge, kBadgeSize), SK_ColorWHITE,
@@ -409,9 +409,6 @@
             views::BoxLayout::Orientation::kVertical));
   }
 
-  account_features_container_->AddChildView(
-      std::make_unique<views::Separator>());
-
   views::Button* button =
       account_features_container_->AddChildView(std::make_unique<HoverButton>(
           this, SizeImage(ColorImage(icon, GetDefaultIconColor()), kIconSize),
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index 496e453..3d5bb3d 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -60,6 +60,7 @@
 #include "ui/events/event_utils.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/webview/webview.h"
+#include "ui/views/test/widget_test.h"
 
 namespace {
 
@@ -631,7 +632,9 @@
     ASSERT_NO_FATAL_FAILURE(OpenProfileMenu());
     AdvanceFocus(/*count=*/GetParam() + 1);
     ASSERT_TRUE(GetFocusedItem());
-    ClickFocusedItem();
+    Click(GetFocusedItem());
+    LOG(INFO) << "Clicked item at index " << GetParam();
+    base::RunLoop().RunUntilIdle();
 
     histogram_tester_.ExpectUniqueSample(
         "Profile.Menu.ClickedActionableItem",
@@ -655,15 +658,30 @@
   void OpenProfileMenu() {
     BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(
         target_browser_ ? target_browser_ : browser());
+
+    // Click the avatar button to open the menu.
     views::View* avatar_button =
         browser_view->toolbar()->GetAvatarToolbarButton();
-    DCHECK(avatar_button);
+    ASSERT_TRUE(avatar_button);
+    Click(avatar_button);
 
-    ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
-                     ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
-    avatar_button->OnMousePressed(e);
+    ASSERT_TRUE(profile_menu_view());
+    profile_menu_view()->set_close_on_deactivate(false);
 
-    ASSERT_TRUE(ProfileMenuView::IsShowing());
+#if defined(OS_MACOSX)
+    base::RunLoop().RunUntilIdle();
+#else
+    // If possible wait until the menu is active.
+    views::Widget* menu_widget = profile_menu_view()->GetWidget();
+    ASSERT_TRUE(menu_widget);
+    if (menu_widget->CanActivate()) {
+      views::test::WidgetActivationWaiter(menu_widget, /*active=*/true).Wait();
+    } else {
+      LOG(ERROR) << "menu_widget can not be activated";
+    }
+#endif
+
+    LOG(INFO) << "Opening profile menu was successful";
   }
 
   void AdvanceFocus(int count) {
@@ -675,19 +693,20 @@
     return profile_menu_view()->GetFocusManager()->GetFocusedView();
   }
 
-  void ClickFocusedItem() {
+  void Click(views::View* clickable_view) {
     // Simulate a mouse click. Note: Buttons are either fired when pressed or
     // when released, so the corresponding methods need to be called.
-    GetFocusedItem()->OnMousePressed(
+    clickable_view->OnMousePressed(
         ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
-    GetFocusedItem()->OnMouseReleased(
+    clickable_view->OnMouseReleased(
         ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
                        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
   }
 
-  views::View* profile_menu_view() {
-    return ProfileMenuViewBase::GetBubbleForTesting();
+  ProfileMenuView* profile_menu_view() {
+    return static_cast<ProfileMenuView*>(
+        ProfileMenuViewBase::GetBubbleForTesting());
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index d8ab19a..0ce5545 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1915,6 +1915,15 @@
   for (size_t i = 0; i < tabs_dragging.size(); ++i)
     tabs_dragging[i]->Paint(paint_info);
 
+  // If dragging more than one grouped tab, paint the group underline above
+  // those tabs. Otherwise, the non-active tabs will not get an underline.
+  // All dragging tabs should belong to the same group, per TabDragController.
+  if (tabs_dragging.size() > 0) {
+    const base::Optional<TabGroupId> dragged_group = tabs_dragging[0]->group();
+    if (dragged_group.has_value())
+      group_underlines_[dragged_group.value()]->Paint(paint_info);
+  }
+
   // If the active tab is being dragged, it goes last.
   if (active_tab && is_dragging)
     active_tab->Paint(paint_info);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
index 6bf4a7c..9fcf99a 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
@@ -73,18 +73,6 @@
   UpdateAllIcons();
 }
 
-void ToolbarAccountIconContainerView::ChildVisibilityChanged(View* child) {
-  // The avatar should not show UI for paused state or error state when any icon
-  // in the toolbar page action icon container view is visible.
-  // If it is in Incognito window, the avatar button shows a text "Incognito"
-  // which should not be updated in any case.
-  if (browser_->profile()->IsIncognitoProfile())
-    return;
-
-  avatar_->SetAutofillIconVisible(
-      page_action_icon_container_view_->IsAnyIconVisible());
-}
-
 SkColor ToolbarAccountIconContainerView::GetIconColor() const {
   return GetThemeProvider()->GetColor(
       ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
index 6e48c71..a0ce1a6 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h
@@ -32,7 +32,6 @@
 
   // views::View:
   void OnThemeChanged() override;
-  void ChildVisibilityChanged(View* child) override;
 
   PageActionIconContainerView* page_action_icon_container() {
     return page_action_icon_container_view_;
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.h b/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
index 0e405c5..87dc733 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
@@ -69,7 +69,6 @@
   void HandleRemoveUser(const base::ListValue* args);
   void HandleAreAllProfilesLocked(const base::ListValue* args);
   void HandleRemoveUserWarningLoadStats(const base::ListValue* args);
-  void HandleGetRemoveWarningDialogMessage(const base::ListValue* args);
 
   // Function used to gather statistics from a profile.
   void GatherStatistics(base::Time start_time, Profile* profile);
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 7351322..bf4ac9e 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -3286,11 +3286,9 @@
 
 #if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
 // Verify CheckSafeBrowsingReputation() is called when user starts filling
-// username or password field, and that this function is only called once.
-// TODO(crbug.com/1008402): Enable this test after fixing the linked bug.
-TEST_F(
-    PasswordAutofillAgentTest,
-    DISABLED_CheckSafeBrowsingReputationWhenUserStartsFillingUsernamePassword) {
+// a password field, and that this function is only called once.
+TEST_F(PasswordAutofillAgentTest,
+       CheckSafeBrowsingReputationWhenUserStartsFillingUsernamePassword) {
   ASSERT_EQ(0, fake_driver_.called_check_safe_browsing_reputation_cnt());
   // Simulate a click on password field to set its on focus,
   // CheckSafeBrowsingReputation() should be called.
@@ -3302,14 +3300,20 @@
   SimulatePasswordTyping("modify");
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, fake_driver_.called_check_safe_browsing_reputation_cnt());
+
+  // No CheckSafeBrowsingReputation() call on username field click.
   SimulateElementClick(kUsernameName);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, fake_driver_.called_check_safe_browsing_reputation_cnt());
 
-  // Navigate to another page and click on username field,
+  SimulateElementClick(kPasswordName);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, fake_driver_.called_check_safe_browsing_reputation_cnt());
+
+  // Navigate to another page and click on password field,
   // CheckSafeBrowsingReputation() should be triggered again.
   LoadHTML(kFormHTML);
-  SimulateElementClick(kUsernameName);
+  SimulateElementClick(kPasswordName);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(2, fake_driver_.called_check_safe_browsing_reputation_cnt());
 }
diff --git a/chrome/renderer/translate/translate_script_browsertest.cc b/chrome/renderer/translate/translate_script_browsertest.cc
index 80d3ac8..69740d0 100644
--- a/chrome/renderer/translate/translate_script_browsertest.cc
+++ b/chrome/renderer/translate/translate_script_browsertest.cc
@@ -79,11 +79,9 @@
 
  protected:
   void InjectElementLibrary() {
-    std::string script;
-    base::StringPiece translate_js =
-        ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
+    std::string script =
+        ui::ResourceBundle::GetSharedInstance().DecompressDataResource(
             IDR_TRANSLATE_JS);
-    translate_js.CopyToString(&script);
     script += kElementJs;
     ExecuteScript(script);
   }
diff --git a/chrome/test/data/extensions/api_test/certificate_provider/request_pin/operated.js b/chrome/test/data/extensions/api_test/certificate_provider/request_pin/operated.js
index c3e0264..f270c478 100644
--- a/chrome/test/data/extensions/api_test/certificate_provider/request_pin/operated.js
+++ b/chrome/test/data/extensions/api_test/certificate_provider/request_pin/operated.js
@@ -5,8 +5,9 @@
 // The script runs commands against the PIN API that it receives from the C++
 // side.
 
-const SIGN_REQUEST_ID = 123;
+const INITIAL_SIGN_REQUEST_ID = 123;
 
+let signRequestId = INITIAL_SIGN_REQUEST_ID;
 let pinRequestCount = 0;
 let pinRequestStopCount = 0;
 
@@ -59,20 +60,23 @@
 function processTestCommand(command) {
   switch (command) {
     case 'Request':
-      requestPin({signRequestId: SIGN_REQUEST_ID});
+      requestPin({signRequestId: signRequestId});
       break;
     case 'RequestWithZeroAttempts':
-      requestPin({signRequestId: SIGN_REQUEST_ID, attemptsLeft: 0});
+      requestPin({signRequestId: signRequestId, attemptsLeft: 0});
       break;
     case 'RequestWithNegativeAttempts':
-      requestPin({signRequestId: SIGN_REQUEST_ID, attemptsLeft: -1});
+      requestPin({signRequestId: signRequestId, attemptsLeft: -1});
       break;
     case 'Stop':
-      stopPinRequest({signRequestId: SIGN_REQUEST_ID});
+      stopPinRequest({signRequestId: signRequestId});
       break;
     case 'StopWithUnknownError':
       stopPinRequest(
-          {signRequestId: SIGN_REQUEST_ID, errorType: 'UNKNOWN_ERROR'});
+          {signRequestId: signRequestId, errorType: 'UNKNOWN_ERROR'});
+      break;
+    case 'IncrementRequestId':
+      ++signRequestId;
       break;
     default:
       chrome.test.fail();
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
index 85c1ea8..5ebb16b 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
@@ -351,7 +351,7 @@
   ]),
 };
 
-TEST_F('CrElementsFingerprintProgressArcTest', 'All', function() {
+TEST_F('CrElementsFingerprintProgressArcTest', 'DISABLED_All', function() {
   mocha.run();
 });
 
diff --git a/components/arc/mojom/intent_common.mojom b/components/arc/mojom/intent_common.mojom
index d18b11e..aedd017e 100644
--- a/components/arc/mojom/intent_common.mojom
+++ b/components/arc/mojom/intent_common.mojom
@@ -10,6 +10,7 @@
   VIEW,           // Can handle only one URL.
   SEND,           // Can handle only one URL.
   SEND_MULTIPLE,  // Can handle multiple URLs.
+  CREATE_NOTE,    // Can handle only one URL.
 };
 
 // Describes an activity.
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index 5e13fe0..cec142fd 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -32,11 +32,19 @@
 }  // namespace
 
 class ArcVmClientAdapter : public ArcClientAdapter,
-                           public chromeos::ConciergeClient::VmObserver {
+                           public chromeos::ConciergeClient::VmObserver,
+                           public chromeos::ConciergeClient::Observer {
  public:
-  ArcVmClientAdapter() { GetConciergeClient()->AddVmObserver(this); }
+  ArcVmClientAdapter() {
+    auto* client = GetConciergeClient();
+    client->AddVmObserver(this);
+    client->AddObserver(this);
+  }
+
   ~ArcVmClientAdapter() override {
-    GetConciergeClient()->RemoveVmObserver(this);
+    auto* client = GetConciergeClient();
+    client->RemoveObserver(this);
+    client->RemoveVmObserver(this);
   }
 
   // chromeos::ConciergeClient::VmObserver overrides:
@@ -95,6 +103,8 @@
 
   void StopArcInstance() override {
     VLOG(1) << "Stopping arcvm";
+    // TODO(yusukes): This method should eventually call ArcInstanceStopped()
+    // even when only the (yet nonexistent) 'mini' VM is running.
     std::vector<std::string> env{{"USER_ID_HASH=" + user_id_hash_}};
     chromeos::UpstartClient::Get()->StopJob(
         "arcvm", env,
@@ -107,6 +117,16 @@
     user_id_hash_ = hash;
   }
 
+  // chromeos::ConciergeClient::Observer overrides:
+  void ConciergeServiceStopped() override {
+    VLOG(1) << "vm_concierge stopped";
+    // At this point, all crosvm processes are gone. Notify the observer of the
+    // event.
+    OnArcInstanceStopped();
+  }
+
+  void ConciergeServiceRestarted() override {}
+
  private:
   void OnArcInstanceStopped() {
     VLOG(1) << "arcvm stopped.";
diff --git a/components/arc/test/fake_file_system_instance.cc b/components/arc/test/fake_file_system_instance.cc
index 5a10403..ef07647 100644
--- a/components/arc/test/fake_file_system_instance.cc
+++ b/components/arc/test/fake_file_system_instance.cc
@@ -565,6 +565,7 @@
     mojom::OpenUrlsRequestPtr request,
     OpenUrlsWithPermissionCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  handled_url_requests_.emplace_back(std::move(request));
 }
 
 std::string FakeFileSystemInstance::FindChildDocumentId(
diff --git a/components/arc/test/fake_file_system_instance.h b/components/arc/test/fake_file_system_instance.h
index ffe8595..47f64df 100644
--- a/components/arc/test/fake_file_system_instance.h
+++ b/components/arc/test/fake_file_system_instance.h
@@ -217,6 +217,14 @@
   // |bytes| bytes.
   std::string GetFileContent(const std::string& url, size_t bytes);
 
+  // For clearing the list of handled requests
+  void clear_handled_requests() { handled_url_requests_.clear(); }
+
+  // For validating the url requests in testing.
+  const std::vector<mojom::OpenUrlsRequestPtr>& handledUrlRequests() const {
+    return handled_url_requests_;
+  }
+
   // mojom::FileSystemInstance:
   void AddWatcher(const std::string& authority,
                   const std::string& document_id,
@@ -326,6 +334,9 @@
   // Mapping from a watcher ID to a document key.
   std::map<int64_t, DocumentKey> watcher_to_document_;
 
+  // List of all OpenUrlsRequests made to the fake_file_system_instance
+  std::vector<mojom::OpenUrlsRequestPtr> handled_url_requests_;
+
   // List of roots added by AddRoot().
   std::vector<Root> roots_;
 
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 16cb289..88ff0ea 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -736,33 +736,27 @@
   return true;
 }
 
-bool PasswordAutofillAgent::IsUsernameOrPasswordField(
+void PasswordAutofillAgent::MaybeCheckSafeBrowsingReputation(
     const WebInputElement& element) {
+  // Enabled on desktop and Android
+#if BUILDFLAG(FULL_SAFE_BROWSING) || BUILDFLAG(SAFE_BROWSING_DB_REMOTE)
   // Note: A site may use a Password field to collect a CVV or a Credit Card
   // number, but showing a slightly misleading warning here is better than
   // showing no warning at all.
-  if (element.IsPasswordFieldForAutofill())
-    return true;
+  if (!element.IsPasswordFieldForAutofill())
+    return;
+  if (checked_safe_browsing_reputation_)
+    return;
 
-  // If a field declares itself a username input, show the warning.
-  if (AutocompleteFlagForElement(element) == AutocompleteFlag::USERNAME)
-    return true;
-
-  // Otherwise, analyze the form and return true if this input element seems
-  // to be the username field.
-  std::unique_ptr<PasswordForm> password_form;
-  if (element.Form().IsNull()) {
-    // To double check that element's frame and |render_frame()->GetWebFrame()|
-    // which is used in |GetPasswordFormFromUnownedInputElements| are identical.
-    DCHECK_EQ(element.GetDocument().GetFrame(), render_frame()->GetWebFrame());
-    password_form = GetPasswordFormFromUnownedInputElements();
-  } else {
-    password_form = GetPasswordFormFromWebForm(element.Form());
-  }
-
-  if (!password_form)
-    return false;
-  return (password_form->username_element == element.NameForAutofill().Utf16());
+  checked_safe_browsing_reputation_ = true;
+  WebLocalFrame* frame = render_frame()->GetWebFrame();
+  GURL frame_url = GURL(frame->GetDocument().Url());
+  GURL action_url = element.Form().IsNull()
+                        ? GURL()
+                        : form_util::GetCanonicalActionForForm(element.Form());
+  GetPasswordManagerDriver()->CheckSafeBrowsingReputation(action_url,
+                                                          frame_url);
+#endif
 }
 
 bool PasswordAutofillAgent::TryToShowTouchToFill(
@@ -793,22 +787,7 @@
 
   if (!FindPasswordInfoForElement(element, &username_element, &password_element,
                                   &password_info)) {
-    if (IsUsernameOrPasswordField(element)) {
-      WebLocalFrame* frame = render_frame()->GetWebFrame();
-      GURL frame_url = GURL(frame->GetDocument().Url());
-// Enabled on desktop and Android
-#if BUILDFLAG(FULL_SAFE_BROWSING) || BUILDFLAG(SAFE_BROWSING_DB_REMOTE)
-      if (!checked_safe_browsing_reputation_) {
-        checked_safe_browsing_reputation_ = true;
-        GURL action_url =
-            element.Form().IsNull()
-                ? GURL()
-                : form_util::GetCanonicalActionForForm(element.Form());
-        GetPasswordManagerDriver()->CheckSafeBrowsingReputation(action_url,
-                                                                frame_url);
-      }
-#endif
-    }
+    MaybeCheckSafeBrowsingReputation(element);
     return false;
   }
 
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 8f910d4..22a03b2 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -172,8 +172,9 @@
   bool DidClearAutofillSelection(
       const blink::WebFormControlElement& control_element);
 
-  // Returns whether the element is a username or password textfield.
-  bool IsUsernameOrPasswordField(const blink::WebInputElement& element);
+  // Sends a reputation check request in case if |element| has type password and
+  // no check request were sent from this frame load.
+  void MaybeCheckSafeBrowsingReputation(const blink::WebInputElement& element);
 
   // Asks the agent to show the touch to fill UI for |control_element|. Returns
   // whether the agent was able to do so.
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index 597a023..c60a5360fb 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -33,83 +33,6 @@
 
 namespace {
 
-constexpr char kAutocompleteUsername[] = "username";
-constexpr char kAutocompleteCurrentPassword[] = "current-password";
-constexpr char kAutocompleteNewPassword[] = "new-password";
-constexpr char kAutocompleteCreditCardPrefix[] = "cc-";
-
-// Parses the string with the value of an autocomplete attribute. If any of the
-// tokens "username", "current-password" or "new-password" are present, returns
-// an appropriate enum value, picking an arbitrary one if more are applicable.
-// Otherwise, it returns CREDIT_CARD if a token with a "cc-" prefix is found.
-// Otherwise, returns NONE.
-AutocompleteFlag ExtractAutocompleteFlag(const std::string& attribute) {
-  std::vector<base::StringPiece> tokens =
-      base::SplitStringPiece(attribute, base::kWhitespaceASCII,
-                             base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  bool cc_seen = false;
-  for (base::StringPiece token : tokens) {
-    if (base::LowerCaseEqualsASCII(token, kAutocompleteUsername))
-      return AutocompleteFlag::USERNAME;
-    if (base::LowerCaseEqualsASCII(token, kAutocompleteCurrentPassword))
-      return AutocompleteFlag::CURRENT_PASSWORD;
-    if (base::LowerCaseEqualsASCII(token, kAutocompleteNewPassword))
-      return AutocompleteFlag::NEW_PASSWORD;
-
-    if (!cc_seen) {
-      cc_seen = base::StartsWith(token, kAutocompleteCreditCardPrefix,
-                                 base::CompareCase::SENSITIVE);
-    }
-  }
-  return cc_seen ? AutocompleteFlag::CREDIT_CARD : AutocompleteFlag::NONE;
-}
-
-// Helper to spare map::find boilerplate when caching field's autocomplete
-// attributes.
-class AutocompleteCache {
- public:
-  AutocompleteCache();
-
-  ~AutocompleteCache();
-
-  // Computes and stores the AutocompleteFlag for |field| based on its
-  // autocomplete attribute. Note that this cannot be done on-demand during
-  // RetrieveFor, because the cache spares space and look-up time by not storing
-  // AutocompleteFlag::NONE values, hence for all elements without an
-  // autocomplete attribute, every retrieval would result in a new computation.
-  void Store(const FormFieldData* field);
-
-  // Retrieves the value previously stored for |field|.
-  AutocompleteFlag RetrieveFor(const FormFieldData* field) const;
-
- private:
-  std::map<const FormFieldData*, AutocompleteFlag> cache_;
-
-  DISALLOW_COPY_AND_ASSIGN(AutocompleteCache);
-};
-
-AutocompleteCache::AutocompleteCache() = default;
-
-AutocompleteCache::~AutocompleteCache() = default;
-
-void AutocompleteCache::Store(const FormFieldData* field) {
-  const AutocompleteFlag flag =
-      ExtractAutocompleteFlag(field->autocomplete_attribute);
-  // Only store non-trivial flags. Most of the elements will have the NONE
-  // value, so spare storage and lookup time by assuming anything not stored in
-  // |cache_| has the NONE flag.
-  if (flag != AutocompleteFlag::NONE)
-    cache_[field] = flag;
-}
-
-AutocompleteFlag AutocompleteCache::RetrieveFor(
-    const FormFieldData* field) const {
-  auto it = cache_.find(field);
-  if (it == cache_.end())
-    return AutocompleteFlag::NONE;
-  return it->second;
-}
-
 const char kPasswordSiteUrlRegex[] =
     "passwords(?:-[a-z-]+\\.corp)?\\.google\\.com";
 
@@ -151,13 +74,6 @@
 
 }  // namespace
 
-AutocompleteFlag AutocompleteFlagForElement(const WebInputElement& element) {
-  static const base::NoDestructor<WebString> kAutocomplete(("autocomplete"));
-  return ExtractAutocompleteFlag(
-      element.GetAttribute(*kAutocomplete)
-          .Utf8(WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD));
-}
-
 re2::RE2* CreateMatcher(void* instance, const char* pattern) {
   re2::RE2::Options options;
   options.set_case_sensitive(false);
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.h b/components/autofill/content/renderer/password_form_conversion_utils.h
index 40585c4..3674d43 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.h
+++ b/components/autofill/content/renderer/password_form_conversion_utils.h
@@ -19,7 +19,6 @@
 
 namespace blink {
 class WebFormElement;
-class WebInputElement;
 class WebLocalFrame;
 }
 
@@ -30,23 +29,8 @@
 namespace autofill {
 
 struct PasswordForm;
-
 class FieldDataManager;
 
-// The susbset of autocomplete flags related to passwords.
-enum class AutocompleteFlag {
-  NONE,
-  USERNAME,
-  CURRENT_PASSWORD,
-  NEW_PASSWORD,
-  // Represents the whole family of cc-* flags.
-  CREDIT_CARD
-};
-
-// Returns the AutocompleteFlag derived from |element|'s autocomplete attribute.
-AutocompleteFlag AutocompleteFlagForElement(
-    const blink::WebInputElement& element);
-
 // The caller of this function is responsible for deleting the returned object.
 re2::RE2* CreateMatcher(void* instance, const char* pattern);
 
diff --git a/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template b/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template
index ffa1cde..309c087 100644
--- a/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template
+++ b/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template
@@ -48,8 +48,9 @@
     {% set method_name = command.name | sanitize_literal | to_title_case %}
 void {{class_name}}::{{method_name}}(
     std::unique_ptr<{{method_name}}Params> params,
+    const std::string& optional_node_frame_id,
     base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback) {
-  dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), base::BindOnce(&Domain::Handle{{method_name}}Response, std::move(callback)));
+  dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), optional_node_frame_id, base::BindOnce(&Domain::Handle{{method_name}}Response, std::move(callback)));
 }
     {# Generate convenience methods that take the required parameters directly. #}
     {% if not "parameters" in command %}{% continue %}{% endif %}
@@ -65,6 +66,7 @@
 {{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}}
     {% endfor %}
     {% if command.get("parameters", []) and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#}
+    const std::string& optional_node_frame_id,
     {% if command.get("returns", []) -%}
       base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback{##}
     {% else -%}
@@ -81,16 +83,16 @@
       .Build();
     {# Send the message. #}
     {% if command.get("returns", []) -%}
-  dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), base::BindOnce(&Domain::Handle{{method_name}}Response, std::move(callback)));
+  dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), optional_node_frame_id, base::BindOnce(&Domain::Handle{{method_name}}Response, std::move(callback)));
     {% else %}
-  dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), std::move(callback));
+  dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), optional_node_frame_id, std::move(callback));
     {% endif %}
 }
     {# If the command has no return value, generate a convenience method that #}
     {# accepts a base::OnceClosure together with the parameters object. #}
     {% if not command.get("returns", []) %}
-void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::OnceClosure callback) {
-  dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), std::move(callback));
+void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, const std::string& optional_node_frame_id, base::OnceClosure callback) {
+  dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), optional_node_frame_id, std::move(callback));
 }
     {% endif %}
   {% endfor %}
diff --git a/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template b/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template
index 785d4cd..0d0afca 100644
--- a/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template
+++ b/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template
@@ -23,7 +23,7 @@
   {% if command.description %}
   // {{ command.description.replace('\n', '\n  // ') }}
   {% endif %}
-  void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback = base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)>());
+  void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, const std::string& optional_node_frame_id, base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback = base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)>());
   {# Generate convenience methods that take the required parameters directly. #}
   {# Don't generate these for experimental commands. #}
   {% if "parameters" in command and not command.experimental %}
@@ -36,6 +36,7 @@
 {{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}}
     {% endfor %}
     {% if command.get("parameters", []) and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#}
+    const std::string& optional_node_frame_id,
     {% if command.get("returns", []) -%}
       base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback = base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)>(){##}
     {% else -%}
@@ -44,7 +45,7 @@
     {# If the command has no return value, generate a convenience method that #}
     {# accepts a base::Closure together with the parameters object. #}
     {% if not command.get("returns", []) %}
-  void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::OnceClosure callback);
+  void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, const std::string& optional_node_frame_id, base::OnceClosure callback);
     {% endif %}
   {% endif %}
 {% endmacro %}
diff --git a/components/autofill_assistant/browser/devtools/devtools_client.cc b/components/autofill_assistant/browser/devtools/devtools_client.cc
index 39911cd..964d07b 100644
--- a/components/autofill_assistant/browser/devtools/devtools_client.cc
+++ b/components/autofill_assistant/browser/devtools/devtools_client.cc
@@ -26,14 +26,18 @@
       dom_domain_(this),
       runtime_domain_(this),
       network_domain_(this),
+      target_domain_(this),
       renderer_crashed_(false),
-      next_message_id_(0) {
+      next_message_id_(0),
+      frame_tracker_(this) {
   browser_main_thread_ =
       base::CreateSingleThreadTaskRunner({content::BrowserThread::UI});
   agent_host_->AttachClient(this);
+  frame_tracker_.Start();
 }
 
 DevtoolsClient::~DevtoolsClient() {
+  frame_tracker_.Stop();
   agent_host_->DetachClient(this);
 }
 
@@ -53,27 +57,43 @@
   return &network_domain_;
 }
 
+target::ExperimentalDomain* DevtoolsClient::GetTarget() {
+  return &target_domain_;
+}
+
 void DevtoolsClient::SendMessage(
     const char* method,
     std::unique_ptr<base::Value> params,
+    const std::string& optional_node_frame_id,
     base::OnceCallback<void(const ReplyStatus&, const base::Value&)> callback) {
-  SendMessageWithParams(method, std::move(params), std::move(callback));
+  SendMessageWithParams(method, std::move(params), optional_node_frame_id,
+                        std::move(callback));
 }
 
 void DevtoolsClient::SendMessage(const char* method,
                                  std::unique_ptr<base::Value> params,
+                                 const std::string& optional_node_frame_id,
                                  base::OnceClosure callback) {
-  SendMessageWithParams(method, std::move(params), std::move(callback));
+  SendMessageWithParams(method, std::move(params), optional_node_frame_id,
+                        std::move(callback));
 }
 
 template <typename CallbackType>
-void DevtoolsClient::SendMessageWithParams(const char* method,
-                                           std::unique_ptr<base::Value> params,
-                                           CallbackType callback) {
+void DevtoolsClient::SendMessageWithParams(
+    const char* method,
+    std::unique_ptr<base::Value> params,
+    const std::string& optional_node_frame_id,
+    CallbackType callback) {
   base::DictionaryValue message;
   message.SetString("method", method);
   message.Set("params", std::move(params));
 
+  std::string optional_session_id =
+      GetSessionIdForFrame(optional_node_frame_id);
+  if (!optional_session_id.empty()) {
+    message.SetString("sessionId", optional_session_id);
+  }
+
   if (renderer_crashed_)
     return;
   int id = next_message_id_;
@@ -95,6 +115,11 @@
   event_handlers_[method] = std::move(callback);
 }
 
+void DevtoolsClient::UnregisterEventHandler(const char* method) {
+  DCHECK(event_handlers_.find(method) != event_handlers_.end());
+  event_handlers_.erase(method);
+}
+
 void DevtoolsClient::DispatchProtocolMessage(
     content::DevToolsAgentHost* agent_host,
     const std::string& json_message) {
@@ -254,6 +279,11 @@
   renderer_crashed_ = true;
 }
 
+std::string DevtoolsClient::GetSessionIdForFrame(
+    const std::string& frame_id) const {
+  return frame_tracker_.GetSessionIdForFrame(frame_id);
+}
+
 DevtoolsClient::Callback::Callback() = default;
 
 DevtoolsClient::Callback::Callback(Callback&& other) = default;
@@ -270,4 +300,114 @@
 DevtoolsClient::Callback& DevtoolsClient::Callback::operator=(
     Callback&& other) = default;
 
+DevtoolsClient::FrameTracker::FrameTracker(DevtoolsClient* client)
+    : client_(client) {}
+
+DevtoolsClient::FrameTracker::~FrameTracker() = default;
+
+void DevtoolsClient::FrameTracker::Start() {
+  client_->RegisterEventHandler(
+      "Target.attachedToTarget",
+      base::BindRepeating(&DevtoolsClient::FrameTracker::OnAttachedToTarget,
+                          weak_ptr_factory_.GetWeakPtr()));
+  client_->RegisterEventHandler(
+      "Target.detachedFromTarget",
+      base::BindRepeating(&DevtoolsClient::FrameTracker::OnDetachedFromTarget,
+                          weak_ptr_factory_.GetWeakPtr()));
+
+  started_ = true;
+
+  // Start auto attaching so that we can keep track of what session got started
+  // for what target. We use flatten = true to cover the entire frame tree.
+  client_->GetTarget()->SetAutoAttach(
+      target::SetAutoAttachParams::Builder()
+          .SetAutoAttach(true)
+          .SetWaitForDebuggerOnStart(false)
+          .SetFlatten(true)
+          .Build(),
+      /* node_frame_id= */ "",
+      base::BindOnce(&DevtoolsClient::FrameTracker::OnSetAutoAttach,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DevtoolsClient::FrameTracker::Stop() {
+  if (!started_) {
+    return;
+  }
+
+  client_->UnregisterEventHandler("Target.attachedToTarget");
+  client_->UnregisterEventHandler("Target.detachedFromTarget");
+
+  started_ = false;
+}
+
+void DevtoolsClient::FrameTracker::OnSetAutoAttach(
+    const DevtoolsClient::ReplyStatus& reply_status,
+    std::unique_ptr<target::SetAutoAttachResult> result) {
+  // This is not used since result doesn't contain anything useful. The real
+  // action is happening in the On(Attached|Detached) functions.
+  DCHECK(result);
+}
+
+std::string DevtoolsClient::FrameTracker::GetSessionIdForFrame(
+    std::string frame_id) const {
+  if (frame_id.empty()) {
+    return std::string();
+  }
+
+  auto it = sessions_map_.find(frame_id);
+  if (it != sessions_map_.end()) {
+    return it->second;
+  }
+  DVLOG(3) << "No session id for frame_id: " << frame_id;
+  return std::string();
+}
+
+std::string DevtoolsClient::FrameTracker::FindTargetId(
+    const base::Value& value) {
+  const base::Value* target_info = value.FindKey("targetInfo");
+  if (!target_info) {
+    DVLOG(3) << "No target_info found in " << value;
+    return std::string();
+  }
+  const std::string* target_id = target_info->FindStringKey("targetId");
+  if (!target_id) {
+    DVLOG(3) << "No target_id found in " << *target_info;
+    return std::string();
+  }
+
+  return *target_id;
+}
+
+std::string DevtoolsClient::FrameTracker::FindSessionId(
+    const base::Value& value) {
+  const std::string* session_id = value.FindStringKey("sessionId");
+  if (!session_id) {
+    DVLOG(3) << "No session_id found in " << value;
+    return std::string();
+  }
+
+  return *session_id;
+}
+
+void DevtoolsClient::FrameTracker::OnAttachedToTarget(
+    const base::Value& value) {
+  std::string session_id = FindSessionId(value);
+  std::string target_id = FindTargetId(value);
+
+  if (!session_id.empty() && !target_id.empty()) {
+    sessions_map_[target_id] = session_id;
+  }
+}
+
+void DevtoolsClient::FrameTracker::OnDetachedFromTarget(
+    const base::Value& value) {
+  std::string target_id = FindTargetId(value);
+
+  auto it = sessions_map_.find(target_id);
+  if (it != sessions_map_.end()) {
+    sessions_map_.erase(it);
+  }
+}
+
 }  // namespace autofill_assistant.
diff --git a/components/autofill_assistant/browser/devtools/devtools_client.h b/components/autofill_assistant/browser/devtools/devtools_client.h
index 4b86b9e..2ec9908 100644
--- a/components/autofill_assistant/browser/devtools/devtools_client.h
+++ b/components/autofill_assistant/browser/devtools/devtools_client.h
@@ -23,6 +23,7 @@
 #include "components/autofill_assistant/browser/devtools/devtools/domains/input.h"
 #include "components/autofill_assistant/browser/devtools/devtools/domains/network.h"
 #include "components/autofill_assistant/browser/devtools/devtools/domains/runtime.h"
+#include "components/autofill_assistant/browser/devtools/devtools/domains/target.h"
 #include "components/autofill_assistant/browser/devtools/message_dispatcher.h"
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/devtools_agent_host_client.h"
@@ -39,19 +40,23 @@
   dom::Domain* GetDOM();
   runtime::Domain* GetRuntime();
   network::Domain* GetNetwork();
+  target::ExperimentalDomain* GetTarget();
 
   // MessageDispatcher implementation:
   void SendMessage(
       const char* method,
       std::unique_ptr<base::Value> params,
+      const std::string& optional_node_frame_id,
       base::OnceCallback<void(const ReplyStatus&, const base::Value&)> callback)
       override;
   void SendMessage(const char* method,
                    std::unique_ptr<base::Value> params,
+                   const std::string& optional_node_frame_id,
                    base::OnceClosure callback) override;
   void RegisterEventHandler(
       const char* method,
       base::RepeatingCallback<void(const base::Value&)> callback) override;
+  void UnregisterEventHandler(const char* method) override;
 
   // content::DevToolsAgentHostClient overrides:
   void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
@@ -76,9 +81,52 @@
         callback_with_result;
   };
 
+  // Manages a map to retrieve a session id from a given frame id. Registers
+  // itself to client events.
+  class FrameTracker {
+   public:
+    // Register the event handlers and start tracking new targets. |client|
+    // must outlive this frame tracker.
+    FrameTracker(DevtoolsClient* client);
+    ~FrameTracker();
+
+    void Start();
+    void Stop();
+
+    // Returns empty string if there is no session for the given |frame_id|.
+    std::string GetSessionIdForFrame(std::string frame_id) const;
+
+   private:
+    void OnSetAutoAttach(const DevtoolsClient::ReplyStatus& reply_status,
+                         std::unique_ptr<target::SetAutoAttachResult> result);
+    void OnAttachedToTarget(const base::Value& value);
+    void OnDetachedFromTarget(const base::Value& value);
+
+    // Save find of targetInfo.targetId in the given value. Returns
+    // empty string if nothing is found.
+    std::string FindTargetId(const base::Value& value);
+
+    // Find sessionId in the given value. Returns empty string if nothing is
+    // found.
+    std::string FindSessionId(const base::Value& value);
+
+    DevtoolsClient* client_;
+    bool started_ = false;
+
+    // Holds the mappings from frame id to session id.
+    std::unordered_map<std::string, std::string> sessions_map_;
+
+    base::WeakPtrFactory<FrameTracker> weak_ptr_factory_{this};
+    DISALLOW_COPY_AND_ASSIGN(FrameTracker);
+  };
+
+  // If the frame is known to devtools, return the session id for it.
+  std::string GetSessionIdForFrame(const std::string& frame_id) const;
+
   template <typename CallbackType>
   void SendMessageWithParams(const char* method,
                              std::unique_ptr<base::Value> params,
+                             const std::string& optional_node_frame_id,
                              CallbackType callback);
   bool DispatchMessageReply(std::unique_ptr<base::Value> owning_message,
                             const base::DictionaryValue& message_dict);
@@ -105,10 +153,12 @@
   dom::ExperimentalDomain dom_domain_;
   runtime::ExperimentalDomain runtime_domain_;
   network::ExperimentalDomain network_domain_;
+  target::ExperimentalDomain target_domain_;
   std::unordered_map<int, Callback> pending_messages_;
   EventHandlerMap event_handlers_;
   bool renderer_crashed_;
   int next_message_id_;
+  FrameTracker frame_tracker_;
 
   base::WeakPtrFactory<DevtoolsClient> weak_ptr_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(DevtoolsClient);
diff --git a/components/autofill_assistant/browser/devtools/message_dispatcher.h b/components/autofill_assistant/browser/devtools/message_dispatcher.h
index a517138..71126c85 100644
--- a/components/autofill_assistant/browser/devtools/message_dispatcher.h
+++ b/components/autofill_assistant/browser/devtools/message_dispatcher.h
@@ -38,15 +38,18 @@
   virtual void SendMessage(
       const char* method,
       std::unique_ptr<base::Value> params,
+      const std::string& optional_node_frame_id,
       base::OnceCallback<void(const ReplyStatus&, const base::Value&)>
           callback) = 0;
   virtual void SendMessage(const char* method,
                            std::unique_ptr<base::Value> params,
+                           const std::string& optional_node_frame_id,
                            base::OnceClosure callback) = 0;
 
   virtual void RegisterEventHandler(
       const char* method,
       base::RepeatingCallback<void(const base::Value&)> callback) = 0;
+  virtual void UnregisterEventHandler(const char* method) = 0;
 
  protected:
   virtual ~MessageDispatcher() {}
diff --git a/components/autofill_assistant/browser/web/element_finder.cc b/components/autofill_assistant/browser/web/element_finder.cc
index 86fadcc..8e2387f 100644
--- a/components/autofill_assistant/browser/web/element_finder.cc
+++ b/components/autofill_assistant/browser/web/element_finder.cc
@@ -135,10 +135,11 @@
     SendResult(ClientStatus(INVALID_SELECTOR));
     return;
   }
+
   devtools_client_->GetRuntime()->Evaluate(
-      std::string(kGetDocumentElement),
+      std::string(kGetDocumentElement), /* node_frame_id= */ std::string(),
       base::BindOnce(&ElementFinder::OnGetDocumentElement,
-                     weak_ptr_factory_.GetWeakPtr()));
+                     weak_ptr_factory_.GetWeakPtr(), 0));
 }
 
 void ElementFinder::SendResult(const ClientStatus& status) {
@@ -148,6 +149,7 @@
 }
 
 void ElementFinder::OnGetDocumentElement(
+    size_t index,
     const DevtoolsClient::ReplyStatus& reply_status,
     std::unique_ptr<runtime::EvaluateResult> result) {
   ClientStatus status =
@@ -163,10 +165,14 @@
     SendResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return;
   }
+
+  // The frame gets set again when we encounter an iFrame, even an OOPIF.
+  // Setting the correct host frame is handled by
+  // FindCorrespondingRenderFrameHost further down.
   element_result_->container_frame_host = web_contents_->GetMainFrame();
-  element_result_->container_frame_selector_index = 0;
-  element_result_->object_id = "";
-  RecursiveFindElement(object_id, 0);
+  element_result_->container_frame_selector_index = index;
+  element_result_->object_id = std::string();
+  RecursiveFindElement(object_id, index);
 }
 
 void ElementFinder::RecursiveFindElement(const std::string& object_id,
@@ -214,6 +220,7 @@
           .SetArguments(std::move(argument))
           .SetFunctionDeclaration(function)
           .Build(),
+      element_result_->node_frame_id,
       base::BindOnce(&ElementFinder::OnQuerySelectorAll,
                      weak_ptr_factory_.GetWeakPtr(), index));
 }
@@ -273,6 +280,7 @@
 
     devtools_client_->GetDOM()->DescribeNode(
         dom::DescribeNodeParams::Builder().SetObjectId(object_id).Build(),
+        element_result_->node_frame_id,
         base::BindOnce(&ElementFinder::OnDescribeNodeForPseudoElement,
                        weak_ptr_factory_.GetWeakPtr(), pseudo_type));
     return;
@@ -280,6 +288,7 @@
 
   devtools_client_->GetDOM()->DescribeNode(
       dom::DescribeNodeParams::Builder().SetObjectId(object_id).Build(),
+      element_result_->node_frame_id,
       base::BindOnce(&ElementFinder::OnDescribeNode,
                      weak_ptr_factory_.GetWeakPtr(), object_id, index));
 }
@@ -303,6 +312,7 @@
             dom::ResolveNodeParams::Builder()
                 .SetBackendNodeId(pseudo_element->GetBackendNodeId())
                 .Build(),
+            element_result_->node_frame_id,
             base::BindOnce(&ElementFinder::OnResolveNodeForPseudoElement,
                            weak_ptr_factory_.GetWeakPtr()));
         return;
@@ -366,9 +376,14 @@
       return;
     }
   } else if (node->HasFrameId()) {
-    // TODO(crbug.com/806868): Support out-of-process iframe.
-    DVLOG(3) << "Warning (unsupported): the element is inside an OOPIF.";
-    SendResult(ClientStatus(UNSUPPORTED));
+    element_result_->container_frame_selector_index = index;
+    element_result_->node_frame_id = node->GetFrameId();
+
+    // Kick off another find element chain to walk down the OOP iFrame.
+    devtools_client_->GetRuntime()->Evaluate(
+        std::string(kGetDocumentElement), element_result_->node_frame_id,
+        base::BindOnce(&ElementFinder::OnGetDocumentElement,
+                       weak_ptr_factory_.GetWeakPtr(), index + 1));
     return;
   }
 
@@ -383,6 +398,7 @@
         dom::ResolveNodeParams::Builder()
             .SetBackendNodeId(backend_ids[0])
             .Build(),
+        element_result_->node_frame_id,
         base::BindOnce(&ElementFinder::OnResolveNode,
                        weak_ptr_factory_.GetWeakPtr(), index));
     return;
diff --git a/components/autofill_assistant/browser/web/element_finder.h b/components/autofill_assistant/browser/web/element_finder.h
index 3acc08bd..3103d6f 100644
--- a/components/autofill_assistant/browser/web/element_finder.h
+++ b/components/autofill_assistant/browser/web/element_finder.h
@@ -44,6 +44,9 @@
 
     // The object id of the element.
     std::string object_id;
+
+    // The id of the frame the element's node is in.
+    std::string node_frame_id;
   };
 
   // |web_contents| and |devtools_client| must be valid for the lifetime of the
@@ -62,7 +65,8 @@
 
  private:
   void SendResult(const ClientStatus& status);
-  void OnGetDocumentElement(const DevtoolsClient::ReplyStatus& reply_status,
+  void OnGetDocumentElement(size_t index,
+                            const DevtoolsClient::ReplyStatus& reply_status,
                             std::unique_ptr<runtime::EvaluateResult> result);
   void RecursiveFindElement(const std::string& object_id, size_t index);
   void OnQuerySelectorAll(
@@ -83,6 +87,7 @@
   void OnResolveNode(size_t index,
                      const DevtoolsClient::ReplyStatus& reply_status,
                      std::unique_ptr<dom::ResolveNodeResult> result);
+
   content::RenderFrameHost* FindCorrespondingRenderFrameHost(
       std::string name,
       std::string document_url);
@@ -90,6 +95,7 @@
   content::WebContents* const web_contents_;
   DevtoolsClient* const devtools_client_;
   const Selector selector_;
+
   const bool strict_;
   Callback callback_;
   std::unique_ptr<Result> element_result_;
diff --git a/components/autofill_assistant/browser/web/element_position_getter.cc b/components/autofill_assistant/browser/web/element_position_getter.cc
index ad63a0c..6cc0ff3 100644
--- a/components/autofill_assistant/browser/web/element_position_getter.cc
+++ b/components/autofill_assistant/browser/web/element_position_getter.cc
@@ -24,11 +24,14 @@
 
 namespace autofill_assistant {
 
-ElementPositionGetter::ElementPositionGetter(DevtoolsClient* devtools_client,
-                                             const ClientSettings& settings)
+ElementPositionGetter::ElementPositionGetter(
+    DevtoolsClient* devtools_client,
+    const ClientSettings& settings,
+    const std::string& optional_node_frame_id)
     : check_interval_(settings.box_model_check_interval),
       max_rounds_(settings.box_model_check_count),
       devtools_client_(devtools_client),
+      node_frame_id_(optional_node_frame_id),
       weak_ptr_factory_(this) {}
 
 ElementPositionGetter::~ElementPositionGetter() = default;
@@ -64,6 +67,7 @@
 void ElementPositionGetter::GetAndWaitBoxModelStable() {
   devtools_client_->GetDOM()->GetBoxModel(
       dom::GetBoxModelParams::Builder().SetObjectId(object_id_).Build(),
+      node_frame_id_,
       base::BindOnce(&ElementPositionGetter::OnGetBoxModelForStableCheck,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -124,6 +128,7 @@
             .SetFunctionDeclaration(std::string(kScrollIntoViewIfNeededScript))
             .SetReturnByValue(true)
             .Build(),
+        node_frame_id_,
         base::BindOnce(&ElementPositionGetter::OnScrollIntoView,
                        weak_ptr_factory_.GetWeakPtr()));
     return;
diff --git a/components/autofill_assistant/browser/web/element_position_getter.h b/components/autofill_assistant/browser/web/element_position_getter.h
index 6639c419..8ed1969 100644
--- a/components/autofill_assistant/browser/web/element_position_getter.h
+++ b/components/autofill_assistant/browser/web/element_position_getter.h
@@ -30,7 +30,8 @@
  public:
   // |devtools_client| must be valid for the lifetime of the instance.
   ElementPositionGetter(DevtoolsClient* devtools_client,
-                        const ClientSettings& settings);
+                        const ClientSettings& settings,
+                        const std::string& optional_node_frame_id);
   ~ElementPositionGetter() override;
 
   // Callback that receives the position that corresponds to the center
@@ -75,6 +76,8 @@
   int point_x_ = 0;
   int point_y_ = 0;
 
+  std::string node_frame_id_;
+
   base::WeakPtrFactory<ElementPositionGetter> weak_ptr_factory_;
 };
 
diff --git a/components/autofill_assistant/browser/web/web_controller.cc b/components/autofill_assistant/browser/web/web_controller.cc
index 85c2ae72..b9a30b4 100644
--- a/components/autofill_assistant/browser/web/web_controller.cc
+++ b/components/autofill_assistant/browser/web/web_controller.cc
@@ -299,6 +299,7 @@
   std::string element_object_id = result->object_id;
   WaitForDocumentToBecomeInteractive(
       settings_->document_ready_check_count, element_object_id,
+      result->node_frame_id,
       base::BindOnce(
           &WebController::OnWaitDocumentToBecomeInteractiveForClickOrTap,
           weak_ptr_factory_.GetWeakPtr(), std::move(callback), click_type,
@@ -333,6 +334,7 @@
           .SetFunctionDeclaration(std::string(kScrollIntoViewCenterScript))
           .SetReturnByValue(true)
           .Build(),
+      target_element->node_frame_id,
       base::BindOnce(&WebController::OnScrollIntoView,
                      weak_ptr_factory_.GetWeakPtr(), std::move(target_element),
                      std::move(callback), click_type));
@@ -364,20 +366,22 @@
             .SetArguments(std::move(argument))
             .SetFunctionDeclaration(kClickElement)
             .Build(),
+        target_element->node_frame_id,
         base::BindOnce(&WebController::OnClickJS,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
     return;
   }
 
   std::unique_ptr<ElementPositionGetter> getter =
-      std::make_unique<ElementPositionGetter>(devtools_client_.get(),
-                                              *settings_);
+      std::make_unique<ElementPositionGetter>(
+          devtools_client_.get(), *settings_, target_element->node_frame_id);
   auto* ptr = getter.get();
   pending_workers_.emplace_back(std::move(getter));
-  ptr->Start(target_element->container_frame_host, target_element->object_id,
-             base::BindOnce(&WebController::TapOrClickOnCoordinates,
-                            weak_ptr_factory_.GetWeakPtr(), ptr,
-                            std::move(callback), click_type));
+  ptr->Start(
+      target_element->container_frame_host, target_element->object_id,
+      base::BindOnce(&WebController::TapOrClickOnCoordinates,
+                     weak_ptr_factory_.GetWeakPtr(), ptr, std::move(callback),
+                     target_element->node_frame_id, click_type));
 }
 
 void WebController::OnClickJS(
@@ -395,6 +399,7 @@
 void WebController::TapOrClickOnCoordinates(
     ElementPositionGetter* getter_to_release,
     base::OnceCallback<void(const ClientStatus&)> callback,
+    const std::string& node_frame_id,
     ClickAction::ClickType click_type,
     bool has_coordinates,
     int x,
@@ -419,9 +424,10 @@
             .SetButton(input::DispatchMouseEventButton::LEFT)
             .SetType(input::DispatchMouseEventType::MOUSE_PRESSED)
             .Build(),
+        node_frame_id,
         base::BindOnce(&WebController::OnDispatchPressMouseEvent,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback), x,
-                       y));
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                       node_frame_id, x, y));
     return;
   }
 
@@ -434,12 +440,15 @@
           .SetType(input::DispatchTouchEventType::TOUCH_START)
           .SetTouchPoints(std::move(touch_points))
           .Build(),
+      node_frame_id,
       base::BindOnce(&WebController::OnDispatchTouchEventStart,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     node_frame_id));
 }
 
 void WebController::OnDispatchPressMouseEvent(
     base::OnceCallback<void(const ClientStatus&)> callback,
+    const std::string& node_frame_id,
     int x,
     int y,
     const DevtoolsClient::ReplyStatus& reply_status,
@@ -460,6 +469,7 @@
           .SetButton(input::DispatchMouseEventButton::LEFT)
           .SetType(input::DispatchMouseEventType::MOUSE_RELEASED)
           .Build(),
+      node_frame_id,
       base::BindOnce(&WebController::OnDispatchReleaseMouseEvent,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -479,6 +489,7 @@
 
 void WebController::OnDispatchTouchEventStart(
     base::OnceCallback<void(const ClientStatus&)> callback,
+    const std::string& node_frame_id,
     const DevtoolsClient::ReplyStatus& reply_status,
     std::unique_ptr<input::DispatchTouchEventResult> result) {
   if (!result) {
@@ -495,6 +506,7 @@
           .SetType(input::DispatchTouchEventType::TOUCH_END)
           .SetTouchPoints(std::move(touch_points))
           .Build(),
+      node_frame_id,
       base::BindOnce(&WebController::OnDispatchTouchEventEnd,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -539,6 +551,7 @@
           .SetExpression(kWaitForWindowHeightChange)
           .SetAwaitPromise(true)
           .Build(),
+      /* node_frame_id= */ std::string(),
       base::BindOnce(&WebController::OnWaitForWindowHeightChange,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -576,6 +589,7 @@
             .SetReturnByValue(true)
             .SetAwaitPromise(true)
             .Build(),
+        /* node_frame_id= */ std::string(),
         base::BindOnce(&OnWaitForDocumentReadyState<runtime::EvaluateResult>,
                        std::move(callback)));
     return;
@@ -614,6 +628,7 @@
           .SetReturnByValue(true)
           .SetAwaitPromise(true)
           .Build(),
+      element->node_frame_id,
       base::BindOnce(
           &OnWaitForDocumentReadyState<runtime::CallFunctionOnResult>,
           std::move(callback)));
@@ -655,6 +670,7 @@
   std::string element_object_id = element_result->object_id;
   WaitForDocumentToBecomeInteractive(
       settings_->document_ready_check_count, element_object_id,
+      element_result->node_frame_id,
       base::BindOnce(
           &WebController::OnWaitDocumentToBecomeInteractiveForFocusElement,
           weak_ptr_factory_.GetWeakPtr(), top_padding, std::move(callback),
@@ -690,6 +706,7 @@
           .SetFunctionDeclaration(std::string(kScrollIntoViewWithPaddingScript))
           .SetReturnByValue(true)
           .Build(),
+      target_element->node_frame_id,
       base::BindOnce(&WebController::OnFocusElement,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -830,6 +847,7 @@
           .SetFunctionDeclaration(std::string(kSelectOptionScript))
           .SetReturnByValue(true)
           .Build(),
+      element_result->node_frame_id,
       base::BindOnce(&WebController::OnSelectOption,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -891,6 +909,7 @@
           .SetFunctionDeclaration(std::string(kHighlightElementScript))
           .SetReturnByValue(true)
           .Build(),
+      element_result->node_frame_id,
       base::BindOnce(&WebController::OnHighlightElement,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -944,6 +963,7 @@
           .SetFunctionDeclaration(std::string(kGetValueAttributeScript))
           .SetReturnByValue(true)
           .Build(),
+      element_result->node_frame_id,
       base::BindOnce(&WebController::OnGetValueAttribute,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -1011,6 +1031,7 @@
 }
 
 void WebController::OnClickElementForSendKeyboardInput(
+    const std::string& node_frame_id,
     const std::vector<UChar32>& codepoints,
     int delay_in_millisecond,
     base::OnceCallback<void(const ClientStatus&)> callback,
@@ -1019,11 +1040,13 @@
     std::move(callback).Run(click_status);
     return;
   }
-  DispatchKeyboardTextDownEvent(codepoints, 0, /*delay=*/false,
-                                delay_in_millisecond, std::move(callback));
+  DispatchKeyboardTextDownEvent(node_frame_id, codepoints, 0,
+                                /* delay= */ false, delay_in_millisecond,
+                                std::move(callback));
 }
 
 void WebController::DispatchKeyboardTextDownEvent(
+    const std::string& node_frame_id,
     const std::vector<UChar32>& codepoints,
     size_t index,
     bool delay,
@@ -1037,10 +1060,10 @@
   if (delay && delay_in_millisecond > 0) {
     base::PostDelayedTask(
         FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(&WebController::DispatchKeyboardTextDownEvent,
-                       weak_ptr_factory_.GetWeakPtr(), codepoints, index,
-                       /*delay=*/false, delay_in_millisecond,
-                       std::move(callback)),
+        base::BindOnce(
+            &WebController::DispatchKeyboardTextDownEvent,
+            weak_ptr_factory_.GetWeakPtr(), node_frame_id, codepoints, index,
+            /* delay= */ false, delay_in_millisecond, std::move(callback)),
         base::TimeDelta::FromMilliseconds(delay_in_millisecond));
     return;
   }
@@ -1049,12 +1072,14 @@
       CreateKeyEventParamsForCharacter(
           autofill_assistant::input::DispatchKeyEventType::KEY_DOWN,
           codepoints[index]),
+      node_frame_id,
       base::BindOnce(&WebController::DispatchKeyboardTextUpEvent,
-                     weak_ptr_factory_.GetWeakPtr(), codepoints, index,
-                     delay_in_millisecond, std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(), node_frame_id, codepoints,
+                     index, delay_in_millisecond, std::move(callback)));
 }
 
 void WebController::DispatchKeyboardTextUpEvent(
+    const std::string& node_frame_id,
     const std::vector<UChar32>& codepoints,
     size_t index,
     int delay_in_millisecond,
@@ -1064,10 +1089,11 @@
       CreateKeyEventParamsForCharacter(
           autofill_assistant::input::DispatchKeyEventType::KEY_UP,
           codepoints[index]),
-      base::BindOnce(&WebController::DispatchKeyboardTextDownEvent,
-                     weak_ptr_factory_.GetWeakPtr(), codepoints, index + 1,
-                     /*delay=*/true, delay_in_millisecond,
-                     std::move(callback)));
+      node_frame_id,
+      base::BindOnce(
+          &WebController::DispatchKeyboardTextDownEvent,
+          weak_ptr_factory_.GetWeakPtr(), node_frame_id, codepoints, index + 1,
+          /* delay= */ true, delay_in_millisecond, std::move(callback)));
 }
 
 auto WebController::CreateKeyEventParamsForCharacter(
@@ -1115,6 +1141,7 @@
           .SetArguments(std::move(argument))
           .SetFunctionDeclaration(std::string(kSetValueAttributeScript))
           .Build(),
+      element_result->node_frame_id,
       base::BindOnce(&WebController::OnSetValueAttribute,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -1175,6 +1202,7 @@
           .SetArguments(std::move(arguments))
           .SetFunctionDeclaration(std::string(kSetAttributeScript))
           .Build(),
+      element_result->node_frame_id,
       base::BindOnce(&WebController::OnSetAttribute,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -1202,8 +1230,7 @@
 
   DCHECK(!selector.empty());
   FindElement(
-      selector,
-      /* strict_mode= */ true,
+      selector, /* strict_mode= */ true,
       base::BindOnce(&WebController::OnFindElementForSendKeyboardInput,
                      weak_ptr_factory_.GetWeakPtr(), selector, codepoints,
                      delay_in_millisecond, std::move(callback)));
@@ -1223,7 +1250,8 @@
   ClickOrTapElement(
       selector, ClickAction::CLICK,
       base::BindOnce(&WebController::OnClickElementForSendKeyboardInput,
-                     weak_ptr_factory_.GetWeakPtr(), codepoints,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     element_result->node_frame_id, codepoints,
                      delay_in_millisecond, std::move(callback)));
 }
 
@@ -1246,6 +1274,7 @@
           .SetExpression(std::string(kGetVisualViewport))
           .SetReturnByValue(true)
           .Build(),
+      /* node_frame_id= */ std::string(),
       base::BindOnce(&WebController::OnGetVisualViewport,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -1311,6 +1340,7 @@
           .SetFunctionDeclaration(std::string(kGetBoundingClientRectAsList))
           .SetReturnByValue(true)
           .Build(),
+      result->node_frame_id,
       base::BindOnce(&WebController::OnGetElementPositionResult,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -1358,6 +1388,7 @@
           .SetFunctionDeclaration(std::string(kGetOuterHtmlScript))
           .SetReturnByValue(true)
           .Build(),
+      element_result->node_frame_id,
       base::BindOnce(&WebController::OnGetOuterHtml,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -1380,7 +1411,8 @@
 
 void WebController::WaitForDocumentToBecomeInteractive(
     int remaining_rounds,
-    std::string object_id,
+    const std::string& object_id,
+    const std::string& node_frame_id,
     base::OnceCallback<void(bool)> callback) {
   devtools_client_->GetRuntime()->CallFunctionOn(
       runtime::CallFunctionOnParams::Builder()
@@ -1388,14 +1420,16 @@
           .SetFunctionDeclaration(std::string(kIsDocumentReadyForInteract))
           .SetReturnByValue(true)
           .Build(),
+      node_frame_id,
       base::BindOnce(&WebController::OnWaitForDocumentToBecomeInteractive,
                      weak_ptr_factory_.GetWeakPtr(), remaining_rounds,
-                     object_id, std::move(callback)));
+                     object_id, node_frame_id, std::move(callback)));
 }
 
 void WebController::OnWaitForDocumentToBecomeInteractive(
     int remaining_rounds,
-    std::string object_id,
+    const std::string& object_id,
+    const std::string& node_frame_id,
     base::OnceCallback<void(bool)> callback,
     const DevtoolsClient::ReplyStatus& reply_status,
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
@@ -1420,7 +1454,7 @@
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(&WebController::WaitForDocumentToBecomeInteractive,
                      weak_ptr_factory_.GetWeakPtr(), --remaining_rounds,
-                     object_id, std::move(callback)),
+                     object_id, node_frame_id, std::move(callback)),
       settings_->document_ready_check_interval);
 }
 
diff --git a/components/autofill_assistant/browser/web/web_controller.h b/components/autofill_assistant/browser/web/web_controller.h
index 31c32e2..7a98bff 100644
--- a/components/autofill_assistant/browser/web/web_controller.h
+++ b/components/autofill_assistant/browser/web/web_controller.h
@@ -247,12 +247,14 @@
   void TapOrClickOnCoordinates(
       ElementPositionGetter* getter_to_release,
       base::OnceCallback<void(const ClientStatus&)> callback,
+      const std::string& node_frame_id,
       ClickAction::ClickType click_type,
       bool has_coordinates,
       int x,
       int y);
   void OnDispatchPressMouseEvent(
       base::OnceCallback<void(const ClientStatus&)> callback,
+      const std::string& node_frame_id,
       int x,
       int y,
       const DevtoolsClient::ReplyStatus& reply_status,
@@ -263,6 +265,7 @@
       std::unique_ptr<input::DispatchMouseEventResult> result);
   void OnDispatchTouchEventStart(
       base::OnceCallback<void(const ClientStatus&)> callback,
+      const std::string& node_frame_id,
       const DevtoolsClient::ReplyStatus& reply_status,
       std::unique_ptr<input::DispatchTouchEventResult> result);
   void OnDispatchTouchEventEnd(
@@ -347,17 +350,20 @@
       base::OnceCallback<void(const ClientStatus&)> callback,
       const ClientStatus& status);
   void OnClickElementForSendKeyboardInput(
+      const std::string& node_frame_id,
       const std::vector<UChar32>& codepoints,
       int delay_in_milli,
       base::OnceCallback<void(const ClientStatus&)> callback,
       const ClientStatus& click_status);
   void DispatchKeyboardTextDownEvent(
+      const std::string& node_frame_id,
       const std::vector<UChar32>& codepoints,
       size_t index,
       bool delay,
       int delay_in_milli,
       base::OnceCallback<void(const ClientStatus&)> callback);
   void DispatchKeyboardTextUpEvent(
+      const std::string& node_frame_id,
       const std::vector<UChar32>& codepoints,
       size_t index,
       int delay_in_milli,
@@ -420,11 +426,13 @@
   // Waits for the document.readyState to be 'interactive' or 'complete'.
   void WaitForDocumentToBecomeInteractive(
       int remaining_rounds,
-      std::string object_id,
+      const std::string& object_id,
+      const std::string& node_frame_id,
       base::OnceCallback<void(bool)> callback);
   void OnWaitForDocumentToBecomeInteractive(
       int remaining_rounds,
-      std::string object_id,
+      const std::string& object_id,
+      const std::string& node_frame_id,
       base::OnceCallback<void(bool)> callback,
       const DevtoolsClient::ReplyStatus& reply_status,
       std::unique_ptr<runtime::CallFunctionOnResult> result);
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index 6358d98..9fbae121 100644
--- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -24,6 +24,8 @@
 using ::testing::AnyOf;
 using ::testing::IsEmpty;
 
+// Flag to enable site per process to enforce OOPIFs.
+const char* kSitePerProcess = "site-per-process";
 const char* kTargetWebsitePath = "/autofill_assistant_target_website.html";
 
 class WebControllerBrowserTest : public content::ContentBrowserTest,
@@ -32,13 +34,26 @@
   WebControllerBrowserTest() {}
   ~WebControllerBrowserTest() override {}
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(kSitePerProcess);
+  }
+
   void SetUpOnMainThread() override {
     ContentBrowserTest::SetUpOnMainThread();
+
+    // Start a mock server for hosting an OOPIF.
+    http_server_iframe_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTP);
+    http_server_iframe_->ServeFilesFromSourceDirectory(
+        "components/test/data/autofill_assistant/html_iframe");
+    ASSERT_TRUE(http_server_iframe_->Start(8081));
+
+    // Start the main server hosting the test page.
     http_server_ = std::make_unique<net::EmbeddedTestServer>(
         net::EmbeddedTestServer::TYPE_HTTP);
     http_server_->ServeFilesFromSourceDirectory(
-        "components/test/data/autofill_assistant");
-    ASSERT_TRUE(http_server_->Start());
+        "components/test/data/autofill_assistant/html");
+    ASSERT_TRUE(http_server_->Start(8080));
     ASSERT_TRUE(
         NavigateToURL(shell(), http_server_->GetURL(kTargetWebsitePath)));
     web_controller_ = WebController::CreateForWebContents(
@@ -460,6 +475,7 @@
 
  private:
   std::unique_ptr<net::EmbeddedTestServer> http_server_;
+  std::unique_ptr<net::EmbeddedTestServer> http_server_iframe_;
   ClientSettings settings_;
 
   DISALLOW_COPY_AND_ASSIGN(WebControllerBrowserTest);
@@ -485,6 +501,18 @@
 
   // A non-existent pseudo-element
   RunLaxElementCheck(Selector({"#button"}, AFTER), false);
+
+  // An iFrame.
+  RunLaxElementCheck(Selector({"#iframe"}), true);
+
+  // An element in a same-origin iFrame.
+  RunLaxElementCheck(Selector({"#iframe", "#button"}), true);
+
+  // An OOPIF.
+  RunLaxElementCheck(Selector({"#iframeExternal"}), true);
+
+  // An element in an OOPIF.
+  RunLaxElementCheck(Selector({"#iframeExternal", "#button"}), true);
 }
 
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, VisibilityRequirementCheck) {
@@ -506,6 +534,19 @@
 
   // A non-existent pseudo-element
   RunLaxElementCheck(Selector({"#button"}, AFTER).MustBeVisible(), false);
+
+  // An iFrame.
+  RunLaxElementCheck(Selector({"#iframe"}).MustBeVisible(), true);
+
+  // An element in a same-origin iFrame.
+  RunLaxElementCheck(Selector({"#iframe", "#button"}).MustBeVisible(), true);
+
+  // An OOPIF.
+  RunLaxElementCheck(Selector({"#iframeExternal"}).MustBeVisible(), true);
+
+  // An element in an OOPIF.
+  RunLaxElementCheck(Selector({"#iframeExternal", "#button"}).MustBeVisible(),
+                     true);
 }
 
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, MultipleVisibleElementCheck) {
@@ -634,6 +675,13 @@
   selectors.emplace_back(a_selector);
   results.emplace_back(true);
 
+  // OOPIF.
+  a_selector.selectors.clear();
+  a_selector.selectors.emplace_back("#iframeExternal");
+  a_selector.selectors.emplace_back("#button");
+  selectors.emplace_back(a_selector);
+  results.emplace_back(true);
+
   // Shadow DOM.
   a_selector.selectors.clear();
   a_selector.selectors.emplace_back("#iframe");
@@ -688,6 +736,18 @@
   WaitForElementRemove(selector);
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ClickElementInOOPIF) {
+  Selector selector;
+  selector.selectors.emplace_back("#iframeExternal");
+  selector.selectors.emplace_back("#button");
+  ClickOrTapElement(selector, ClickAction::CLICK);
+
+  selector.selectors.clear();
+  selector.selectors.emplace_back("#iframeExternal");
+  selector.selectors.emplace_back("#div");
+  WaitForElementRemove(selector);
+}
+
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
                        ClickElementInScrollContainer) {
   // Make sure #scroll_item_3 is not visible, no matter the screen height. It
@@ -1057,6 +1117,28 @@
           .proto_status());
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, GetAndSetFieldValueInIFrame) {
+  Selector a_selector;
+
+  // IFrame.
+  a_selector.selectors.clear();
+  a_selector.selectors.emplace_back("#iframe");
+  a_selector.selectors.emplace_back("#input");
+  EXPECT_EQ(ACTION_APPLIED, SetFieldValue(a_selector, "text",
+                                          /* simulate_key_presses= */ false)
+                                .proto_status());
+  GetFieldsValue({a_selector}, {"text"});
+
+  // OOPIF.
+  a_selector.selectors.clear();
+  a_selector.selectors.emplace_back("#iframeExternal");
+  a_selector.selectors.emplace_back("#input");
+  EXPECT_EQ(ACTION_APPLIED, SetFieldValue(a_selector, "text",
+                                          /* simulate_key_presses= */ false)
+                                .proto_status());
+  GetFieldsValue({a_selector}, {"text"});
+}
+
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SendKeyboardInput) {
   auto input = UTF8ToUnicode("Zürich");
   std::string expected_output = "Zürich";
diff --git a/components/embedder_support/android/delegate/web_contents_delegate_android.cc b/components/embedder_support/android/delegate/web_contents_delegate_android.cc
index 5316b98..a7de4a9 100644
--- a/components/embedder_support/android/delegate/web_contents_delegate_android.cc
+++ b/components/embedder_support/android/delegate/web_contents_delegate_android.cc
@@ -111,6 +111,7 @@
       params.should_replace_current_entry;
   load_params.is_renderer_initiated = params.is_renderer_initiated;
   load_params.has_user_gesture = params.user_gesture;
+  load_params.initiator_origin = params.initiator_origin;
 
   if (params.uses_post) {
     load_params.load_type = content::NavigationController::LOAD_TYPE_HTTP_POST;
diff --git a/components/exo/display.cc b/components/exo/display.cc
index f65d1ee..bc1fa15 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -111,22 +111,14 @@
   // Using zero-copy for optimal performance.
   bool use_zero_copy = true;
 
-#if defined(ARCH_CPU_X86_FAMILY)
-  // TODO(dcastagna): Re-enable NV12 format as HW overlay once b/113362843
-  // is addressed.
-  bool is_overlay_candidate = format != gfx::BufferFormat::YUV_420_BIPLANAR;
-#else
-  bool is_overlay_candidate = true;
-#endif
-
   return std::make_unique<Buffer>(
       std::move(gpu_memory_buffer),
       gpu::NativeBufferNeedsPlatformSpecificTextureTarget(format)
           ? gpu::GetPlatformSpecificTextureTarget()
           : GL_TEXTURE_2D,
       // COMMANDS_COMPLETED queries are required by native pixmaps.
-      GL_COMMANDS_COMPLETED_CHROMIUM, use_zero_copy, is_overlay_candidate,
-      y_invert);
+      GL_COMMANDS_COMPLETED_CHROMIUM, use_zero_copy,
+      /*is_overlay_candidate=*/true, y_invert);
 }
 #endif  // defined(USE_OZONE)
 
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index faa1d16..b80fc63a 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -669,6 +669,10 @@
     // available.
     if (!exact_keyword.empty() && !keywords.count(exact_keyword)) {
       keywords.insert(exact_keyword);
+      // If the match has an answer, it will look strange to try to display
+      // it along with a keyword hint. Prefer the keyword hint, and revert
+      // to a typical search.
+      match->answer.reset();
       match->associated_keyword.reset(new AutocompleteMatch(
           keyword_provider_->CreateVerbatimMatch(exact_keyword,
                                                  exact_keyword, input_)));
diff --git a/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc b/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
index 3421dd7..bfe608e 100644
--- a/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
+++ b/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
@@ -136,6 +136,7 @@
           WebFeature::kThirdPartyServiceWorker,
           WebFeature::kThirdPartySharedWorker,
           WebFeature::kThirdPartyBroadcastChannel,
+          WebFeature::kHeavyAdIntervention,
       }));
   return *opt_in_features;
 }
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index 1511a61..cd798f6 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -14,7 +14,6 @@
 #include "components/autofill/core/common/password_form.h"
 #include "components/password_manager/content/browser/bad_message.h"
 #include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
-#include "components/password_manager/content/browser/form_submission_tracker_util.h"
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_metrics_recorder.h"
@@ -185,19 +184,6 @@
   return render_frame_host_->GetLastCommittedURL();
 }
 
-void ContentPasswordManagerDriver::DidNavigateFrame(
-    content::NavigationHandle* navigation_handle) {
-  // Clear page specific data after main frame navigation.
-  if (navigation_handle->IsInMainFrame() &&
-      !navigation_handle->IsSameDocument()) {
-    NotifyDidNavigateMainFrame(navigation_handle->IsRendererInitiated(),
-                               navigation_handle->GetPageTransition(),
-                               navigation_handle->WasInitiatedByLinkClick(),
-                               GetPasswordManager());
-    GetPasswordAutofillManager()->DidNavigateMainFrame();
-  }
-}
-
 void ContentPasswordManagerDriver::GeneratePassword(
     autofill::mojom::PasswordGenerationAgent::
         UserTriggeredGeneratePasswordCallback callback) {
diff --git a/components/password_manager/content/browser/content_password_manager_driver.h b/components/password_manager/content/browser/content_password_manager_driver.h
index a2a62bc..b84c7ea 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.h
+++ b/components/password_manager/content/browser/content_password_manager_driver.h
@@ -27,7 +27,6 @@
 }
 
 namespace content {
-class NavigationHandle;
 class RenderFrameHost;
 }
 
@@ -78,7 +77,6 @@
   bool IsMainFrame() const override;
   const GURL& GetLastCommittedURL() const override;
 
-  void DidNavigateFrame(content::NavigationHandle* navigation_handle);
   // Notify the renderer that the user wants to generate password manually.
   void GeneratePassword(autofill::mojom::PasswordGenerationAgent::
                             UserTriggeredGeneratePasswordCallback callback);
diff --git a/components/password_manager/content/browser/content_password_manager_driver_factory.cc b/components/password_manager/content/browser/content_password_manager_driver_factory.cc
index df81c48..ebc498d 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_factory.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_factory.cc
@@ -15,6 +15,7 @@
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
+#include "components/password_manager/content/browser/form_submission_tracker_util.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_entry.h"
@@ -42,18 +43,10 @@
     return;
 
   // NOTE: Can't use |std::make_unique| due to private constructor.
-  auto new_factory = base::WrapUnique(new ContentPasswordManagerDriverFactory(
-      web_contents, password_client, autofill_client));
-  const std::vector<content::RenderFrameHost*> frames =
-      web_contents->GetAllFrames();
-  for (content::RenderFrameHost* frame : frames) {
-    if (frame->IsRenderFrameLive())
-      new_factory->RenderFrameCreated(frame);
-  }
-
   web_contents->SetUserData(
       kContentPasswordManagerDriverFactoryWebContentsUserDataKey,
-      std::move(new_factory));
+      base::WrapUnique(new ContentPasswordManagerDriverFactory(
+          web_contents, password_client, autofill_client)));
 }
 
 ContentPasswordManagerDriverFactory::ContentPasswordManagerDriverFactory(
@@ -95,29 +88,23 @@
   if (!factory)
     return;
 
-  ContentPasswordManagerDriver* driver =
-      factory->GetDriverForFrame(render_frame_host);
-  if (driver)
-    driver->BindPendingReceiver(std::move(pending_receiver));
+  factory->GetDriverForFrame(render_frame_host)
+      ->BindPendingReceiver(std::move(pending_receiver));
 }
 
 ContentPasswordManagerDriver*
 ContentPasswordManagerDriverFactory::GetDriverForFrame(
     content::RenderFrameHost* render_frame_host) {
-  auto mapping = frame_driver_map_.find(render_frame_host);
-  return mapping == frame_driver_map_.end() ? nullptr : mapping->second.get();
-}
+  DCHECK_EQ(web_contents(),
+            content::WebContents::FromRenderFrameHost(render_frame_host));
+  DCHECK(render_frame_host->IsRenderFrameCreated());
 
-void ContentPasswordManagerDriverFactory::RenderFrameCreated(
-    content::RenderFrameHost* render_frame_host) {
-  auto insertion_result =
-      frame_driver_map_.insert(std::make_pair(render_frame_host, nullptr));
-  // This is called twice for the main frame.
-  if (insertion_result.second) {  // This was the first time.
-    insertion_result.first->second =
-        std::make_unique<ContentPasswordManagerDriver>(
-            render_frame_host, password_client_, autofill_client_);
+  auto& driver = frame_driver_map_[render_frame_host];
+  if (!driver) {
+    driver = std::make_unique<ContentPasswordManagerDriver>(
+        render_frame_host, password_client_, autofill_client_);
   }
+  return driver.get();
 }
 
 void ContentPasswordManagerDriverFactory::RenderFrameDeleted(
@@ -126,12 +113,20 @@
 }
 
 void ContentPasswordManagerDriverFactory::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  if (!navigation_handle->HasCommitted())
+    content::NavigationHandle* navigation) {
+  if (!navigation->IsInMainFrame() || navigation->IsSameDocument() ||
+      !navigation->HasCommitted()) {
     return;
+  }
 
-  if (auto* driver = GetDriverForFrame(navigation_handle->GetRenderFrameHost()))
-    driver->DidNavigateFrame(navigation_handle);
+  // Clear page specific data after main frame navigation.
+  NotifyDidNavigateMainFrame(navigation->IsRendererInitiated(),
+                             navigation->GetPageTransition(),
+                             navigation->WasInitiatedByLinkClick(),
+                             password_client_->GetPasswordManager());
+  GetDriverForFrame(navigation->GetRenderFrameHost())
+      ->GetPasswordAutofillManager()
+      ->DidNavigateMainFrame();
 }
 
 void ContentPasswordManagerDriverFactory::RequestSendLoggingAvailability() {
diff --git a/components/password_manager/content/browser/content_password_manager_driver_factory.h b/components/password_manager/content/browser/content_password_manager_driver_factory.h
index 39616d1..083130d 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_factory.h
+++ b/components/password_manager/content/browser/content_password_manager_driver_factory.h
@@ -54,18 +54,17 @@
   // chrome://password-manager-internals is available.
   void RequestSendLoggingAvailability();
 
-  // content::WebContentsObserver:
-  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
-  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-
  private:
   ContentPasswordManagerDriverFactory(
       content::WebContents* web_contents,
       PasswordManagerClient* client,
       autofill::AutofillClient* autofill_client);
 
+  // content::WebContentsObserver:
+  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+
   std::map<content::RenderFrameHost*,
            std::unique_ptr<ContentPasswordManagerDriver>>
       frame_driver_map_;
diff --git a/components/password_manager/core/browser/leaked_credentials_table.cc b/components/password_manager/core/browser/leaked_credentials_table.cc
index dbc94dd..65d0a1e 100644
--- a/components/password_manager/core/browser/leaked_credentials_table.cc
+++ b/components/password_manager/core/browser/leaked_credentials_table.cc
@@ -106,17 +106,57 @@
   return s.Run();
 }
 
-bool LeakedCredentialsTable::RemoveRowsCreatedBetween(base::Time remove_begin,
-                                                      base::Time remove_end) {
-  sql::Statement s(
-      db_->GetCachedStatement(SQL_FROM_HERE,
-                              "DELETE FROM leaked_credentials WHERE "
-                              "create_time >= ? AND create_time < ?"));
-  s.BindInt64(0, remove_begin.ToDeltaSinceWindowsEpoch().InMicroseconds());
-  s.BindInt64(1, remove_end.is_null()
-                     ? std::numeric_limits<int64_t>::max()
-                     : remove_end.ToDeltaSinceWindowsEpoch().InMicroseconds());
-  return s.Run();
+bool LeakedCredentialsTable::RemoveRowsByUrlAndTime(
+    const base::RepeatingCallback<bool(const GURL&)>& url_filter,
+    base::Time remove_begin,
+    base::Time remove_end) {
+  if (remove_end.is_null())
+    remove_end = base::Time::Max();
+
+  const int64_t remove_begin_us =
+      remove_begin.ToDeltaSinceWindowsEpoch().InMicroseconds();
+  const int64_t remove_end_us =
+      remove_end.ToDeltaSinceWindowsEpoch().InMicroseconds();
+
+  // If |url_filter| is null, remove all records in given date range.
+  if (!url_filter) {
+    sql::Statement s(
+        db_->GetCachedStatement(SQL_FROM_HERE,
+                                "DELETE FROM leaked_credentials WHERE "
+                                "create_time >= ? AND create_time < ?"));
+    s.BindInt64(0, remove_begin_us);
+    s.BindInt64(1, remove_end_us);
+    return s.Run();
+  }
+
+  // Otherwise, filter urls.
+  sql::Statement s(db_->GetCachedStatement(
+      SQL_FROM_HERE,
+      "SELECT DISTINCT url FROM leaked_credentials WHERE "
+      "create_time >= ? AND create_time < ?"));
+  s.BindInt64(0, remove_begin_us);
+  s.BindInt64(1, remove_end_us);
+
+  std::vector<std::string> urls;
+  while (s.Step()) {
+    std::string url = s.ColumnString(0);
+    if (url_filter.Run(GURL(url))) {
+      urls.push_back(std::move(url));
+    }
+  }
+
+  bool success = true;
+  for (const std::string& url : urls) {
+    sql::Statement s(
+        db_->GetCachedStatement(SQL_FROM_HERE,
+                                "DELETE FROM leaked_credentials WHERE url = ? "
+                                "AND create_time >= ? AND create_time < ?"));
+    s.BindString(0, url);
+    s.BindInt64(1, remove_begin_us);
+    s.BindInt64(2, remove_end_us);
+    success = success && s.Run();
+  }
+  return success;
 }
 
 std::vector<LeakedCredentials> LeakedCredentialsTable::GetAllRows() {
diff --git a/components/password_manager/core/browser/leaked_credentials_table.h b/components/password_manager/core/browser/leaked_credentials_table.h
index f08e62b..a4ed203 100644
--- a/components/password_manager/core/browser/leaked_credentials_table.h
+++ b/components/password_manager/core/browser/leaked_credentials_table.h
@@ -54,8 +54,13 @@
 
   // Removes all leaked credentials created between |remove_begin| inclusive and
   // |remove_end| exclusive. Using a null Time value will do an unbounded delete
-  // in either direction.
-  bool RemoveRowsCreatedBetween(base::Time remove_begin, base::Time remove_end);
+  // in either direction. If |url_filter| is not null, only leaked credentials
+  // for matching urls are removed. Returns true if the SQL completed
+  // successfully.
+  bool RemoveRowsByUrlAndTime(
+      const base::RepeatingCallback<bool(const GURL&)>& url_filter,
+      base::Time remove_begin,
+      base::Time remove_end);
 
   // Returns all leaked credentials from the database.
   std::vector<LeakedCredentials> GetAllRows();
diff --git a/components/password_manager/core/browser/leaked_credentials_table_unittest.cc b/components/password_manager/core/browser/leaked_credentials_table_unittest.cc
index f4df298..35f3f63 100644
--- a/components/password_manager/core/browser/leaked_credentials_table_unittest.cc
+++ b/components/password_manager/core/browser/leaked_credentials_table_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "components/password_manager/core/browser/leaked_credentials_table.h"
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/strings/utf_string_conversions.h"
 #include "sql/database.h"
@@ -15,6 +18,7 @@
 
 const char kTestDomain[] = "http://example.com";
 const char kTestDomain2[] = "http://test.com";
+const char kTestDomain3[] = "http://google.com";
 const char kUsername[] = "user";
 const char kUsername2[] = "user2";
 const char kUsername3[] = "user3";
@@ -116,8 +120,9 @@
               ElementsAre(leaked_credentials1, leaked_credentials2,
                           leaked_credentials3));
 
-  EXPECT_TRUE(db()->RemoveRowsCreatedBetween(base::Time::FromTimeT(15),
-                                             base::Time::FromTimeT(25)));
+  EXPECT_TRUE(db()->RemoveRowsByUrlAndTime(base::NullCallback(),
+                                           base::Time::FromTimeT(15),
+                                           base::Time::FromTimeT(25)));
 
   EXPECT_THAT(db()->GetAllRows(),
               ElementsAre(leaked_credentials1, leaked_credentials3));
@@ -138,7 +143,8 @@
   EXPECT_THAT(db()->GetAllRows(),
               ElementsAre(leaked_credentials_begin, leaked_credentials_end));
 
-  EXPECT_TRUE(db()->RemoveRowsCreatedBetween(begin_time, end_time));
+  EXPECT_TRUE(
+      db()->RemoveRowsByUrlAndTime(base::NullCallback(), begin_time, end_time));
   // RemoveRowsCreatedBetween takes |begin_time| inclusive and |end_time|
   // exclusive, hence the credentials with |end_time| should remain in the
   // database.
@@ -163,11 +169,39 @@
               ElementsAre(leaked_credentials1, leaked_credentials2,
                           leaked_credentials3));
 
-  EXPECT_TRUE(db()->RemoveRowsCreatedBetween(base::Time(), base::Time()));
+  EXPECT_TRUE(db()->RemoveRowsByUrlAndTime(base::NullCallback(), base::Time(),
+                                           base::Time()));
 
   EXPECT_THAT(db()->GetAllRows(), IsEmpty());
 }
 
+TEST_F(LeakedCredentialsTableTest, RemoveRowsByUrlAndTime) {
+  LeakedCredentials leaked_credentials1 = test_data();
+  LeakedCredentials leaked_credentials2 = test_data();
+  LeakedCredentials leaked_credentials3 = test_data();
+  LeakedCredentials leaked_credentials4 = test_data();
+  leaked_credentials2.username = base::ASCIIToUTF16(kUsername2);
+  leaked_credentials3.url = GURL(kTestDomain2);
+  leaked_credentials4.url = GURL(kTestDomain3);
+
+  EXPECT_TRUE(db()->AddRow(leaked_credentials1));
+  EXPECT_TRUE(db()->AddRow(leaked_credentials2));
+  EXPECT_TRUE(db()->AddRow(leaked_credentials3));
+  EXPECT_TRUE(db()->AddRow(leaked_credentials4));
+
+  EXPECT_THAT(db()->GetAllRows(),
+              ElementsAre(leaked_credentials1, leaked_credentials2,
+                          leaked_credentials3, leaked_credentials4));
+
+  EXPECT_TRUE(db()->RemoveRowsByUrlAndTime(
+      base::BindRepeating(std::not_equal_to<GURL>(), leaked_credentials1.url),
+      base::Time(), base::Time()));
+  // With unbounded time range and given url filter all rows that are not
+  // matching the |leaked_credentials1.url| should be removed.
+  EXPECT_THAT(db()->GetAllRows(),
+              ElementsAre(leaked_credentials1, leaked_credentials2));
+}
+
 TEST_F(LeakedCredentialsTableTest, BadURL) {
   test_data().url = GURL("bad");
   EXPECT_FALSE(db()->AddRow(test_data()));
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index 9b27461..8b7f256 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -76,8 +76,10 @@
   MOCK_METHOD2(RemoveLeakedCredentialsImpl,
                void(const GURL&, const base::string16&));
   MOCK_METHOD0(GetAllLeakedCredentialsImpl, std::vector<LeakedCredentials>());
-  MOCK_METHOD2(RemoveLeakedCredentialsCreatedBetweenImpl,
-               void(base::Time, base::Time));
+  MOCK_METHOD3(RemoveLeakedCredentialsByUrlAndTimeImpl,
+               void(const base::RepeatingCallback<bool(const GURL&)>&,
+                    base::Time,
+                    base::Time));
   MOCK_CONST_METHOD0(IsAbleToSavePasswords, bool());
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 15f8dd6..0232462 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -358,14 +358,15 @@
       base::BindOnce(&PasswordStore::GetAllLeakedCredentialsImpl, this));
 }
 
-void PasswordStore::RemoveLeakedCredentialsCreatedBetween(
+void PasswordStore::RemoveLeakedCredentialsByUrlAndTime(
+    base::RepeatingCallback<bool(const GURL&)> url_filter,
     base::Time remove_begin,
     base::Time remove_end,
-    const base::Closure& completion) {
+    base::OnceClosure completion) {
   DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   ScheduleTask(base::BindOnce(
-      &PasswordStore::RemoveLeakedCredentialsCreatedBetweenInternal, this,
-      remove_begin, remove_end, completion));
+      &PasswordStore::RemoveLeakedCredentialsByUrlAndTimeInternal, this,
+      std::move(url_filter), remove_begin, remove_end, std::move(completion)));
 }
 
 void PasswordStore::AddObserver(Observer* observer) {
@@ -860,14 +861,15 @@
     main_task_runner_->PostTask(FROM_HERE, std::move(completion));
 }
 
-void PasswordStore::RemoveLeakedCredentialsCreatedBetweenInternal(
+void PasswordStore::RemoveLeakedCredentialsByUrlAndTimeInternal(
+    const base::RepeatingCallback<bool(const GURL&)>& url_filter,
     base::Time remove_begin,
     base::Time remove_end,
-    const base::Closure& completion) {
+    base::OnceClosure completion) {
   DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  RemoveLeakedCredentialsCreatedBetweenImpl(remove_begin, remove_end);
+  RemoveLeakedCredentialsByUrlAndTimeImpl(url_filter, remove_begin, remove_end);
   if (!completion.is_null())
-    main_task_runner_->PostTask(FROM_HERE, completion);
+    main_task_runner_->PostTask(FROM_HERE, std::move(completion));
 }
 
 std::vector<std::unique_ptr<autofill::PasswordForm>>
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index 52dc8d9..f610fd7 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -253,12 +253,15 @@
   // request will be cancelled if the consumer is destroyed.
   void GetAllLeakedCredentials(PasswordLeakHistoryConsumer* consumer);
 
-  // Removes all leaked credentials in the given date range. If |completion| is
-  // not null, it will be posted to the |main_task_runner_| after deletions have
-  // been completed. Should be called on the UI thread.
-  void RemoveLeakedCredentialsCreatedBetween(base::Time remove_begin,
-                                             base::Time remove_end,
-                                             const base::Closure& completion);
+  // Removes all leaked credentials in the given date range. If |url_filter| is
+  // not null, only leaked credentials for matching urls are removed. If
+  // |completion| is not null, it will be posted to the |main_task_runner_|
+  // after deletions have been completed. Should be called on the UI thread.
+  void RemoveLeakedCredentialsByUrlAndTime(
+      base::RepeatingCallback<bool(const GURL&)> url_filter,
+      base::Time remove_begin,
+      base::Time remove_end,
+      base::OnceClosure completion);
 
   // Adds an observer to be notified when the password store data changes.
   void AddObserver(Observer* observer);
@@ -460,7 +463,8 @@
   virtual void RemoveLeakedCredentialsImpl(const GURL& url,
                                            const base::string16& username) = 0;
   virtual std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() = 0;
-  virtual void RemoveLeakedCredentialsCreatedBetweenImpl(
+  virtual void RemoveLeakedCredentialsByUrlAndTimeImpl(
+      const base::RepeatingCallback<bool(const GURL&)>& url_filter,
       base::Time remove_begin,
       base::Time remove_end) = 0;
 
@@ -602,10 +606,11 @@
       const base::Closure& completion);
   void UnblacklistInternal(const PasswordStore::FormDigest& form_digest,
                            base::OnceClosure completion);
-  void RemoveLeakedCredentialsCreatedBetweenInternal(
+  void RemoveLeakedCredentialsByUrlAndTimeInternal(
+      const base::RepeatingCallback<bool(const GURL&)>& url_filter,
       base::Time remove_begin,
       base::Time remove_end,
-      const base::Closure& completion);
+      base::OnceClosure completion);
 
   // Finds all PasswordForms with a signon_realm that is equal to, or is a
   // PSL-match to that of |form|, and takes care of notifying the consumer with
diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc
index fdb7cf4..c37b5cf 100644
--- a/components/password_manager/core/browser/password_store_default.cc
+++ b/components/password_manager/core/browser/password_store_default.cc
@@ -244,12 +244,13 @@
                    : std::vector<LeakedCredentials>();
 }
 
-void PasswordStoreDefault::RemoveLeakedCredentialsCreatedBetweenImpl(
+void PasswordStoreDefault::RemoveLeakedCredentialsByUrlAndTimeImpl(
+    const base::RepeatingCallback<bool(const GURL&)>& url_filter,
     base::Time remove_begin,
     base::Time remove_end) {
   if (login_db_) {
-    login_db_->leaked_credentials_table().RemoveRowsCreatedBetween(remove_begin,
-                                                                   remove_end);
+    login_db_->leaked_credentials_table().RemoveRowsByUrlAndTime(
+        url_filter, remove_begin, remove_end);
   }
 }
 
diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h
index 6b25dbc..b48f8ee8 100644
--- a/components/password_manager/core/browser/password_store_default.h
+++ b/components/password_manager/core/browser/password_store_default.h
@@ -77,7 +77,8 @@
   void RemoveLeakedCredentialsImpl(const GURL& url,
                                    const base::string16& username) override;
   std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() override;
-  void RemoveLeakedCredentialsCreatedBetweenImpl(
+  void RemoveLeakedCredentialsByUrlAndTimeImpl(
+      const base::RepeatingCallback<bool(const GURL&)>& url_filter,
       base::Time remove_begin,
       base::Time remove_end) override;
 
diff --git a/components/password_manager/core/browser/password_store_unittest.cc b/components/password_manager/core/browser/password_store_unittest.cc
index e886c14..1326f1a 100644
--- a/components/password_manager/core/browser/password_store_unittest.cc
+++ b/components/password_manager/core/browser/password_store_unittest.cc
@@ -1303,8 +1303,9 @@
   WaitForPasswordStore();
   testing::Mock::VerifyAndClearExpectations(&consumer);
 
-  store->RemoveLeakedCredentialsCreatedBetween(
-      base::Time::FromTimeT(150), base::Time::FromTimeT(250), base::Closure());
+  store->RemoveLeakedCredentialsByUrlAndTime(
+      base::BindRepeating(std::not_equal_to<GURL>(), leaked_credentials3.url),
+      base::Time::FromTimeT(150), base::Time::FromTimeT(350), base::Closure());
 
   EXPECT_CALL(consumer, OnGetLeakedCredentials(UnorderedElementsAre(
                             leaked_credentials1, leaked_credentials3)));
diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc
index 45e06f8..872c8a8 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -242,7 +242,8 @@
   return std::vector<LeakedCredentials>();
 }
 
-void TestPasswordStore::RemoveLeakedCredentialsCreatedBetweenImpl(
+void TestPasswordStore::RemoveLeakedCredentialsByUrlAndTimeImpl(
+    const base::RepeatingCallback<bool(const GURL&)>& url_filter,
     base::Time remove_begin,
     base::Time remove_end) {
   NOTIMPLEMENTED();
diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h
index 5d3af31..53cb38e 100644
--- a/components/password_manager/core/browser/test_password_store.h
+++ b/components/password_manager/core/browser/test_password_store.h
@@ -90,7 +90,8 @@
   void RemoveLeakedCredentialsImpl(const GURL& url,
                                    const base::string16& username) override;
   std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() override;
-  void RemoveLeakedCredentialsCreatedBetweenImpl(
+  void RemoveLeakedCredentialsByUrlAndTimeImpl(
+      const base::RepeatingCallback<bool(const GURL&)>& url_filter,
       base::Time remove_begin,
       base::Time remove_end) override;
 
diff --git a/components/policy/proto/chrome_device_policy.proto b/components/policy/proto/chrome_device_policy.proto
index b1e2970..6cc3da3 100644
--- a/components/policy/proto/chrome_device_policy.proto
+++ b/components/policy/proto/chrome_device_policy.proto
@@ -1494,14 +1494,6 @@
   optional string device_login_screen_webusb_allow_devices_for_urls = 1;
 }
 
-// Settings that control if the device is enforced to show (or hide) system
-// information.
-message DeviceLoginScreenSystemInfoEnforcedProto {
-  // Determines whether the system information is always shown (or hidden) on
-  // the login screen.
-  optional bool enabled = 1;
-}
-
 message ChromeDeviceSettingsProto {
   reserved 61, 90;
   optional DevicePolicyRefreshRateProto device_policy_refresh_rate = 1;
@@ -1616,6 +1608,5 @@
   optional DevicePowerwashAllowedProto device_powerwash_allowed = 91;
   optional DeviceLoginScreenWebUsbAllowDevicesForUrlsProto
       device_login_screen_webusb_allow_devices_for_urls = 92;
-  optional DeviceLoginScreenSystemInfoEnforcedProto
-      device_login_screen_system_info_enforced = 93;
+  optional BooleanPolicyProto device_login_screen_system_info_enforced = 93;
 }
diff --git a/components/policy/test_support/policy_testserver.py b/components/policy/test_support/policy_testserver.py
index 954022d..bc4268c 100644
--- a/components/policy/test_support/policy_testserver.py
+++ b/components/policy/test_support/policy_testserver.py
@@ -208,6 +208,15 @@
 
 INVALID_ENROLLMENT_TOKEN = 'invalid_enrollment_token'
 
+POLICY_COMMON_DEFINITIONS_TYPES = [
+  'StringList',
+  'PolicyOptions',
+  'BooleanPolicyProto',
+  'IntegerPolicyProto',
+  'StringPolicyProto',
+  'StringListPolicyProto'
+]
+
 class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
   """Decodes and handles device management requests from clients.
 
@@ -988,6 +997,17 @@
       self.SetProtobufMessageField(settings, field_descriptor,
                                    field_value)
 
+  def GetMessageDefinitionSource(self, message_type):
+    """Retrieve either policy_common_defintions, or chrome_device_policy
+    proto file, which contains the definition of the message.
+
+    Args:
+      message_type: name of the message definition type.
+    """
+    if message_type in POLICY_COMMON_DEFINITIONS_TYPES:
+      return 'cd'
+    return 'dp'
+
   def GatherDevicePolicySettings(self, settings, policies):
     """Copies all the policies from a dictionary into a protobuf of type
     CloudDeviceSettingsProto.
@@ -998,7 +1018,8 @@
     """
     for group in settings.DESCRIPTOR.fields:
       # Create protobuf message for group.
-      group_message = eval('dp.' + group.message_type.name + '()')
+      group_message = eval(self.GetMessageDefinitionSource(
+          group.message_type.name) + '.' + group.message_type.name + '()')
       # Indicates if at least one field was set in |group_message|.
       got_fields = False
       # Iterate over fields of the message and feed them from the
diff --git a/components/resources/OWNERS b/components/resources/OWNERS
index 697f205..7b2b1c0 100644
--- a/components/resources/OWNERS
+++ b/components/resources/OWNERS
@@ -24,4 +24,5 @@
 per-file printing_resources.grdp=file://printing/OWNERS
 per-file security_interstitials_resources.grdp=file://components/security_interstitials/OWNERS
 per-file sync_driver_resources.grdp=file://components/sync/OWNERS
+per-file translate_resources.grdp=file://components/translate/OWNERS
 per-file version_ui*=file://ui/webui/PLATFORM_OWNERS
diff --git a/components/resources/translate_resources.grdp b/components/resources/translate_resources.grdp
index 5226e14..0a04033 100644
--- a/components/resources/translate_resources.grdp
+++ b/components/resources/translate_resources.grdp
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
-  <include name="IDR_TRANSLATE_JS" file="../translate/core/browser/resources/translate.js" type="BINDATA" />
+  <include name="IDR_TRANSLATE_JS" file="../translate/core/browser/resources/translate.js" type="BINDATA" compress="gzip" />
 </grit-part>
diff --git a/components/search_engines/prepopulated_engines.json b/components/search_engines/prepopulated_engines.json
index b134602..af61d0c 100644
--- a/components/search_engines/prepopulated_engines.json
+++ b/components/search_engines/prepopulated_engines.json
@@ -635,6 +635,7 @@
       "alternate_urls": [
 	"http://hladaj.atlas.sk/fulltext/?phrase={searchTerms}"
       ],
+      "type": "SEARCH_ENGINE_ATLAS",
       "id": 27
     },
 
@@ -1009,5 +1010,9 @@
       "type": "SEARCH_ENGINE_ZOZNAM",
       "id": 85
     }
+  },
+
+  "generate_array": {
+    "array_name": "kAllEngines"
   }
 }
diff --git a/components/search_engines/template_url_prepopulate_data.cc b/components/search_engines/template_url_prepopulate_data.cc
index 5fd11a9..52dd123 100644
--- a/components/search_engines/template_url_prepopulate_data.cc
+++ b/components/search_engines/template_url_prepopulate_data.cc
@@ -889,96 +889,6 @@
 };
 // ----------------------------------------------------------------------------
 
-// A list of all the engines that we know about.
-const PrepopulatedEngine* const kAllEngines[] = {
-    // Prepopulated engines:
-    &ask,
-    &baidu,
-    &bing,
-    &coccoc,
-    &daum,
-    &duckduckgo,
-    &google,
-    &mail_ru,
-    &naver,
-    &qwant,
-    &seznam,
-    &sogou,
-    &yahoo,
-    &yahoo_ar,
-    &yahoo_at,
-    &yahoo_au,
-    &yahoo_br,
-    &yahoo_ca,
-    &yahoo_ch,
-    &yahoo_cl,
-    &yahoo_co,
-    &yahoo_de,
-    &yahoo_dk,
-    &yahoo_es,
-    &yahoo_fi,
-    &yahoo_fr,
-    &yahoo_hk,
-    &yahoo_id,
-    &yahoo_in,
-    &yahoo_jp,
-    &yahoo_mx,
-    &yahoo_my,
-    &yahoo_nl,
-    &yahoo_nz,
-    &yahoo_pe,
-    &yahoo_ph,
-    &yahoo_qc,
-    &yahoo_se,
-    &yahoo_sg,
-    &yahoo_th,
-    &yahoo_tr,
-    &yahoo_tw,
-    &yahoo_uk,
-    &yahoo_ve,
-    &yahoo_vn,
-    &yandex_by,
-    &yandex_com,
-    &yandex_kz,
-    &yandex_ru,
-    &yandex_tr,
-    &yandex_ua,
-
-    // UMA-only engines:
-    &atlas_cz,
-    &atlas_sk,
-    &avg,
-    &babylon,
-    &conduit,
-    &delfi_lt,
-    &delfi_lv,
-    &delta,
-    &funmoods,
-    &goo,
-    &imesh,
-    &iminent,
-    &in,
-    &incredibar,
-    &libero,
-    &neti,
-    &nigma,
-    &ok,
-    &rambler,
-    &sapo,
-    &search_results,
-    &searchnu,
-    &snapdo,
-    &softonic,
-    &sweetim,
-    &sweetpacks,
-    &terra_ar,
-    &terra_es,
-    &tut,
-    &walla,
-    &wp,
-    &zoznam,
-};
-
 std::vector<std::unique_ptr<TemplateURLData>> GetPrepopulationSetFromCountryID(
     int country_id) {
   const PrepopulatedEngine* const* engines;
@@ -1404,8 +1314,8 @@
 #endif
 
 std::vector<const PrepopulatedEngine*> GetAllPrepopulatedEngines() {
-  return std::vector<const PrepopulatedEngine*>(std::begin(kAllEngines),
-                                                std::end(kAllEngines));
+  return std::vector<const PrepopulatedEngine*>(
+      &kAllEngines[0], &kAllEngines[0] + kAllEnginesLength);
 }
 
 void ClearPrepopulatedEnginesInPrefs(PrefService* prefs) {
@@ -1442,7 +1352,7 @@
     return google.type;
 
   // Now check the rest of the prepopulate data.
-  for (size_t i = 0; i < base::size(kAllEngines); ++i) {
+  for (size_t i = 0; i < kAllEnginesLength; ++i) {
     // First check the main search URL.
     if (SameDomain(url, GURL(kAllEngines[i]->search_url)))
       return kAllEngines[i]->type;
diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h
index 4a17c37..b4b9cf2e 100644
--- a/components/signin/core/browser/account_reconcilor.h
+++ b/components/signin/core/browser/account_reconcilor.h
@@ -253,8 +253,6 @@
   void UnregisterWithAllDependencies();
   void RegisterWithIdentityManager();
   void UnregisterWithIdentityManager();
-  void RegisterWithCookieManagerService();
-  void UnregisterWithCookieManagerService();
   void RegisterWithContentSettings();
   void UnregisterWithContentSettings();
 
diff --git a/components/signin/core/browser/signin_internals_util.cc b/components/signin/core/browser/signin_internals_util.cc
index 6888420..68dc2ee 100644
--- a/components/signin/core/browser/signin_internals_util.cc
+++ b/components/signin/core/browser/signin_internals_util.cc
@@ -46,27 +46,4 @@
   return std::string();
 }
 
-std::string TokenPrefPath(const std::string& token_name) {
-  return std::string(kTokenPrefPrefix) + token_name;
-}
-
-// Gets the first few hex characters of the SHA256 hash of the passed in string.
-// These are enough to perform equality checks across a single users tokens,
-// while preventing outsiders from reverse-engineering the actual token from
-// the displayed value.
-// Note that for readability (in about:signin-internals), an empty string
-// is not hashed, but simply returned as an empty string.
-std::string GetTruncatedHash(const std::string& str) {
-  if (str.empty())
-    return str;
-
-  // Since each character in the hash string generates two hex charaters
-  // we only need half as many charaters in |hash_val| as hex characters
-  // returned.
-  const int kTruncateSize = kTruncateTokenStringLength / 2;
-  char hash_val[kTruncateSize];
-  crypto::SHA256HashString(str, &hash_val[0], kTruncateSize);
-  return base::ToLowerASCII(base::HexEncode(&hash_val[0], kTruncateSize));
-}
-
 } //  namespace signin_internals_util
diff --git a/components/signin/core/browser/signin_internals_util.h b/components/signin/core/browser/signin_internals_util.h
index 576710b..dc6a0e2 100644
--- a/components/signin/core/browser/signin_internals_util.h
+++ b/components/signin/core/browser/signin_internals_util.h
@@ -19,9 +19,6 @@
 extern const char kSigninPrefPrefix[];
 extern const char kTokenPrefPrefix[];
 
-// The length of strings returned by GetTruncatedHash() below.
-const size_t kTruncateTokenStringLength = 6;
-
 // Helper enums to access fields from SigninStatus (declared below).
 enum {
   SIGNIN_FIELDS_BEGIN = 0,
@@ -54,23 +51,10 @@
   SIGNIN_FIELDS_COUNT = SIGNIN_FIELDS_END - SIGNIN_FIELDS_BEGIN
 };
 
-// Returns the root preference path for the service. The path should be
-// qualified with one of .value, .status or .time to get the respective
-// full preference path names.
-std::string TokenPrefPath(const std::string& service_name);
-
 // Returns the name of a SigninStatus field.
 std::string SigninStatusFieldToString(UntimedSigninStatusField field);
 std::string SigninStatusFieldToString(TimedSigninStatusField field);
 
-// Gets the first 6 hex characters of the SHA256 hash of the passed in string.
-// These are enough to perform equality checks across a single users tokens,
-// while preventing outsiders from reverse-engineering the actual token from
-// the displayed value.
-// Note that for readability (in about:signin-internals), an empty string
-// is not hashed, but simply returned as an empty string.
-std::string GetTruncatedHash(const std::string& str);
-
 } // namespace signin_internals_util
 
 #endif  // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_INTERNALS_UTIL_H_
diff --git a/components/test/data/autofill_assistant/autofill_assistant_target_website.html b/components/test/data/autofill_assistant/html/autofill_assistant_target_website.html
similarity index 97%
rename from components/test/data/autofill_assistant/autofill_assistant_target_website.html
rename to components/test/data/autofill_assistant/html/autofill_assistant_target_website.html
index 63e90eb..6523ec2 100644
--- a/components/test/data/autofill_assistant/autofill_assistant_target_website.html
+++ b/components/test/data/autofill_assistant/html/autofill_assistant_target_website.html
@@ -236,6 +236,9 @@
     <iframe id="iframe" name="test_iframe" width="100%" height="500" src=
         "autofill_assistant_target_website_iframe_one.html"></iframe>
 
+    <iframe id="iframeExternal" name="test_iframe_external" width="100%" height="500" src=
+        "http://localhost:8081/autofill_assistant_external_iframe.html"></iframe>
+
     <input type="checkbox" id="terms-and-conditions" />
     <label for="terms-and-conditions" >
       <a href="#">I have read and understood <br />the terms and conditions</a>
diff --git a/components/test/data/autofill_assistant/autofill_assistant_target_website_iframe_one.html b/components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_one.html
similarity index 97%
rename from components/test/data/autofill_assistant/autofill_assistant_target_website_iframe_one.html
rename to components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_one.html
index d9f33c5..2e76055 100644
--- a/components/test/data/autofill_assistant/autofill_assistant_target_website_iframe_one.html
+++ b/components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_one.html
@@ -60,7 +60,7 @@
   <body onload="load()">
     <div>
       <form name="address" id="address_section">
-        <div id='billing'>
+        <div id="billing">
           <h2>Billing Address</h2>
           Name: <input type="text" name="name"><br>
           Address: <input type="text" name="address"><br>
@@ -87,6 +87,10 @@
     </div>
 
     <div>
+      <input id="input" type="text" />
+    </div>
+
+    <div>
       <p id="touch_area" ontouchend="removeTouchArea()">Touchable Area</p>
       <br>
     </div>
@@ -107,4 +111,4 @@
     <iframe id="iframe" width="100%" height="500" src=
         "autofill_assistant_target_website_iframe_two.html"></iframe>
   </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/components/test/data/autofill_assistant/autofill_assistant_target_website_iframe_two.html b/components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_two.html
similarity index 100%
rename from components/test/data/autofill_assistant/autofill_assistant_target_website_iframe_two.html
rename to components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_two.html
diff --git a/components/test/data/autofill_assistant/html_iframe/autofill_assistant_external_iframe.html b/components/test/data/autofill_assistant/html_iframe/autofill_assistant_external_iframe.html
new file mode 100644
index 0000000..7304a41
--- /dev/null
+++ b/components/test/data/autofill_assistant/html_iframe/autofill_assistant_external_iframe.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Autofill Assistant Test</title>
+
+    <script>
+      var removeDiv = function() {
+        var div = document.getElementById("div");
+        div.parentNode.removeChild(div);
+      }
+    </script>
+  </head>
+  <body>
+    <button id="button" type="button" onclick="removeDiv()">Button</button>
+    <div id="div">Text</div>
+
+    <input id="input" type="text" />
+  </body>
+</html>
diff --git a/components/translate/core/browser/translate_script.cc b/components/translate/core/browser/translate_script.cc
index e2b43c5..5724783 100644
--- a/components/translate/core/browser/translate_script.cc
+++ b/components/translate/core/browser/translate_script.cc
@@ -154,10 +154,8 @@
         &data_, "var securityOrigin = '%s';", security_origin.spec().c_str());
 
     // Load embedded translate.js.
-    base::StringPiece str =
-        ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
-            IDR_TRANSLATE_JS);
-    str.AppendToString(&data_);
+    data_.append(ui::ResourceBundle::GetSharedInstance().DecompressDataResource(
+        IDR_TRANSLATE_JS));
 
 #if defined(OS_IOS)
     // Append snippet to install callbacks on translate.js if available.
diff --git a/components/translate/ios/browser/js_translate_manager_unittest.mm b/components/translate/ios/browser/js_translate_manager_unittest.mm
index af64932..550e3f5 100644
--- a/components/translate/ios/browser/js_translate_manager_unittest.mm
+++ b/components/translate/ios/browser/js_translate_manager_unittest.mm
@@ -21,11 +21,10 @@
   JsTranslateManagerTest() {
     receiver_ = [[CRWTestJSInjectionReceiver alloc] init];
     manager_ = [[JsTranslateManager alloc] initWithReceiver:receiver_];
-    base::StringPiece script =
-        ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
+    std::string script =
+        ui::ResourceBundle::GetSharedInstance().DecompressDataResource(
             IDR_TRANSLATE_JS);
-    [manager_ setScript:base::SysUTF8ToNSString(script.as_string() +
-                                                "('DummyKey');")];
+    [manager_ setScript:base::SysUTF8ToNSString(script + "('DummyKey');")];
   }
 
   bool IsDefined(NSString* name) {
diff --git a/components/ukm/ukm_recorder_impl.cc b/components/ukm/ukm_recorder_impl.cc
index 55f9023..cced2e4 100644
--- a/components/ukm/ukm_recorder_impl.cc
+++ b/components/ukm/ukm_recorder_impl.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "components/variations/variations_associated_data.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_decode.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -270,6 +271,7 @@
   is_webstore_extension_callback_ = callback;
 }
 
+// TODO(rkaplow): This should be refactored.
 void UkmRecorderImpl::StoreRecordingsInReport(Report* report) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -393,6 +395,24 @@
       "UKM.Sources.SerializedCount2.App",
       serialized_source_type_counts[ukm::SourceIdType::APP_ID]);
 
+  // We record a UMA metric specifically for the number of serialized events
+  // with the FCP metric. This is for data quality verification.
+  const uint64_t pageload_hash =
+      base::HashMetricName(ukm::builders::PageLoad::kEntryName);
+  const uint64_t fcp_hash = base::HashMetricName(
+      ukm::builders::PageLoad::
+          kPaintTiming_NavigationToFirstContentfulPaintName);
+  int num_recorded_fcp = 0;
+  for (const auto& entry : recordings_.entries) {
+    if (entry->event_hash == pageload_hash) {
+      if (entry->metrics.find(fcp_hash) != entry->metrics.end()) {
+        num_recorded_fcp++;
+      }
+    }
+  }
+  UMA_HISTOGRAM_COUNTS_100000("UKM.Entries.SerializedCountFCP",
+                              num_recorded_fcp);
+
   // For each matching id in obsolete_source_ids, remove the Source from
   // recordings_.sources. The remaining sources form the deferred sources for
   // the next report.
diff --git a/content/browser/appcache/appcache_group.cc b/content/browser/appcache/appcache_group.cc
index be53ed1..8120f407 100644
--- a/content/browser/appcache/appcache_group.cc
+++ b/content/browser/appcache/appcache_group.cc
@@ -232,7 +232,7 @@
 void AppCacheGroup::ScheduleUpdateRestart(int delay_ms) {
   DCHECK(restart_update_task_.IsCancelled());
   restart_update_task_.Reset(
-      base::Bind(&AppCacheGroup::RunQueuedUpdates, this));
+      base::BindOnce(&AppCacheGroup::RunQueuedUpdates, this));
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE, restart_update_task_.callback(),
       base::TimeDelta::FromMilliseconds(delay_ms));
diff --git a/content/browser/appcache/appcache_group.h b/content/browser/appcache/appcache_group.h
index a058f600..63c1bb5 100644
--- a/content/browser/appcache/appcache_group.h
+++ b/content/browser/appcache/appcache_group.h
@@ -191,7 +191,7 @@
   // Updates that have been queued for the next run.
   QueuedUpdates queued_updates_;
   base::ObserverList<UpdateObserver>::Unchecked queued_observers_;
-  base::CancelableClosure restart_update_task_;
+  base::CancelableOnceClosure restart_update_task_;
   std::unique_ptr<HostObserver> host_observer_;
 
   // True if we're in our destructor.
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc
index 06eb14cf..1bd0d55 100644
--- a/content/browser/browser_thread_unittest.cc
+++ b/content/browser/browser_thread_unittest.cc
@@ -43,23 +43,22 @@
 class SequenceManagerThreadDelegate : public base::Thread::Delegate {
  public:
   SequenceManagerThreadDelegate() {
-    sequence_manager_ =
+    ui_sequence_manager_ =
         base::sequence_manager::internal::SequenceManagerImpl::CreateUnbound(
             base::sequence_manager::SequenceManager::Settings());
     auto browser_ui_thread_scheduler =
         BrowserUIThreadScheduler::CreateForTesting(
-            sequence_manager_.get(), sequence_manager_->GetRealTimeDomain());
+            ui_sequence_manager_.get(),
+            ui_sequence_manager_->GetRealTimeDomain());
 
     default_task_runner_ =
         browser_ui_thread_scheduler->GetHandle()->GetDefaultTaskRunner();
 
-    sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
+    ui_sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
 
     BrowserTaskExecutor::CreateForTesting(
         std::move(browser_ui_thread_scheduler),
-        std::make_unique<BrowserIOThreadDelegate>(
-            BrowserIOThreadDelegate::BrowserTaskExecutorPresent::
-                kNoForTesting));
+        std::make_unique<BrowserIOThreadDelegate>());
     BrowserTaskExecutor::EnableAllQueues();
   }
 
@@ -73,13 +72,14 @@
   }
 
   void BindToCurrentThread(base::TimerSlack timer_slack) override {
-    sequence_manager_->BindToMessagePump(
+    ui_sequence_manager_->BindToMessagePump(
         base::MessagePump::Create(base::MessagePumpType::DEFAULT));
-    sequence_manager_->SetTimerSlack(timer_slack);
+    ui_sequence_manager_->SetTimerSlack(timer_slack);
+    BrowserTaskExecutor::BindToUIThreadForTesting();
   }
 
  private:
-  std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager_;
+  std::unique_ptr<base::sequence_manager::SequenceManager> ui_sequence_manager_;
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(SequenceManagerThreadDelegate);
@@ -314,9 +314,7 @@
               QueueType::kDefault));
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
-          std::make_unique<BrowserIOThreadDelegate>(
-              BrowserIOThreadDelegate::BrowserTaskExecutorPresent::
-                  kNoForTesting));
+          std::make_unique<BrowserIOThreadDelegate>());
 
       ui_thread_ = BrowserTaskExecutor::CreateIOThread();
       BrowserTaskExecutor::InitializeIOThread();
diff --git a/content/browser/cache_storage/cache_storage_context_impl.cc b/content/browser/cache_storage/cache_storage_context_impl.cc
index 79cec0a..09296f8 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.cc
+++ b/content/browser/cache_storage/cache_storage_context_impl.cc
@@ -28,7 +28,7 @@
 namespace {
 
 const base::Feature kCacheStorageSequenceFeature{
-    "CacheStorageSequence", base::FEATURE_ENABLED_BY_DEFAULT};
+    "CacheStorageSequence", base::FEATURE_DISABLED_BY_DEFAULT};
 
 scoped_refptr<base::SequencedTaskRunner> CreateSchedulerTaskRunner() {
   if (!base::FeatureList::IsEnabled(kCacheStorageSequenceFeature))
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 286abc7..23620d9 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1228,24 +1228,65 @@
 
 CanCommitStatus ChildProcessSecurityPolicyImpl::CanCommitOriginAndUrl(
     int child_id,
+    const IsolationContext& isolation_context,
     const url::Origin& origin,
     const GURL& url) {
-  // TODO(nasko): This check should be updated to apply to all URLs, not just
-  // standard ones.
-  if (url.IsStandard() && !CanAccessDataForOrigin(child_id, url))
+  const url::Origin url_origin = url::Origin::Resolve(url, origin);
+  if (!CanAccessDataForOrigin(child_id, url_origin)) {
+    // Allow opaque origins w/o precursors to commit.
+    // TODO(acolwell): Investigate all cases that trigger this path and fix
+    // them so we have precursor information. Remove this logic once that has
+    // been completed.
+    if (url_origin.opaque() &&
+        url_origin.GetTupleOrPrecursorTupleIfOpaque().IsInvalid()) {
+      return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
+    }
+
+    // Check for special cases, like blob:null/ and data: URLs, where the
+    // origin does not contain information to match against the process lock,
+    // but using the whole URL can result in a process lock match.
+    const GURL expected_origin_lock =
+        SiteInstanceImpl::DetermineProcessLockURL(isolation_context, url);
+    const GURL actual_origin_lock = GetOriginLock(child_id);
+    if (actual_origin_lock == expected_origin_lock)
+      return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
+
+    // Allow about: pages to commit in a process that does not match the opaque
+    // origin's precursor information.
+    // TODO(acolwell): Remove this once process selection for about: URLs has
+    // been fixed to always match the precursor info.
+    if (url_origin.opaque() && (url.IsAboutBlank() || url.IsAboutSrcdoc()) &&
+        !actual_origin_lock.is_empty()) {
+      return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
+    }
     return CanCommitStatus::CANNOT_COMMIT_URL;
+  }
 
-  // It is safe to commit into a opaque origin, regardless of the URL, as it is
-  // restricted from accessing other origins.
-  if (origin.opaque())
-    return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
-
-  // Standard URLs must match the reported origin.
-  if (url.IsStandard() && !origin.IsSameOriginWith(url::Origin::Create(url)))
+  if (!CanAccessDataForOrigin(child_id, origin)) {
+    // Allow opaque origins w/o precursors to commit.
+    // TODO(acolwell): Investigate all cases that trigger this path and fix
+    // them so we have precursor information. Remove this logic once that has
+    // been completed.
+    if (origin.opaque() &&
+        origin.GetTupleOrPrecursorTupleIfOpaque().IsInvalid()) {
+      return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
+    }
     return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
+  }
 
-  if (!CanAccessDataForOrigin(child_id, origin))
+  // Ensure that the origin derived from |url| is consistent with |origin|.
+  // Note: We can't use origin.IsSameOriginWith() here because opaque origins
+  // with precursors may have different nonce values.
+  const auto url_tuple_or_precursor_tuple =
+      url_origin.GetTupleOrPrecursorTupleIfOpaque();
+  const auto origin_tuple_or_precursor_tuple =
+      origin.GetTupleOrPrecursorTupleIfOpaque();
+
+  if (!url_tuple_or_precursor_tuple.IsInvalid() &&
+      !origin_tuple_or_precursor_tuple.IsInvalid() &&
+      origin_tuple_or_precursor_tuple != url_tuple_or_precursor_tuple) {
     return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
+  }
 
   return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
 }
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h
index 7e5e2a7..dedc4f1 100644
--- a/content/browser/child_process_security_policy_impl.h
+++ b/content/browser/child_process_security_policy_impl.h
@@ -123,9 +123,11 @@
   // |url| combination to the process associated with |child_id|.
   // Returns CANNOT_COMMIT_URL if |url| is not safe to commit.
   // Returns CANNOT_COMMIT_ORIGIN if |origin| is not safe to commit.
-  CanCommitStatus CanCommitOriginAndUrl(int child_id,
-                                        const url::Origin& origin,
-                                        const GURL& url);
+  CanCommitStatus CanCommitOriginAndUrl(
+      int child_id,
+      const IsolationContext& isolation_context,
+      const url::Origin& origin,
+      const GURL& url);
 
   // This function will check whether |origin| requires process isolation
   // within |isolation_context|, and if so, it will return true and put the
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 950dc9d..c480bbb 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -49,6 +49,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_package/signed_exchange_envelope.h"
 #include "content/common/view_messages.h"
+#include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -429,6 +430,13 @@
   }
   for (auto* target : protocol::TargetHandler::ForAgentHost(this))
     target->DidFinishNavigation();
+
+  // RenderFrameDevToolsAgentHost is associated with frame_tree_node, while
+  // documents in the back-forward cache share a node, therefore we can't cache
+  // them. TODO(1001087): add support long-term.
+  content::BackForwardCache::DisableForRenderFrameHost(
+      navigation_handle->GetPreviousRenderFrameHostId(),
+      "RenderFrameDevToolsAgentHost");
 }
 
 void RenderFrameDevToolsAgentHost::UpdateFrameHost(
diff --git a/content/browser/devtools/render_frame_devtools_agent_host_browsertest.cc b/content/browser/devtools/render_frame_devtools_agent_host_browsertest.cc
index 7908a8e..bd29fac2 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host_browsertest.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host_browsertest.cc
@@ -7,8 +7,10 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/devtools_agent_host_client.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/test/back_forward_cache_util.h"
 #include "content/public/test/browser_test_base.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -170,4 +172,32 @@
   devtools_agent_host->DetachClient(&devtools_agent_host_client);
 }
 
+IN_PROC_BROWSER_TEST_F(RenderFrameDevToolsAgentHostBrowserTest,
+                       DevToolsDisableBackForwardCache) {
+  content::BackForwardCacheDisabledTester tester;
+  EXPECT_TRUE(embedded_test_server()->Start());
+
+  // Navigate to a page.
+  const GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), a_url));
+  content::RenderFrameHost* main_frame =
+      shell()->web_contents()->GetMainFrame();
+  int process_id = main_frame->GetProcess()->GetID();
+  int frame_routing_id = main_frame->GetRoutingID();
+
+  // Open DevTools.
+  scoped_refptr<DevToolsAgentHost> devtools_agent_host =
+      DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
+  StubDevToolsAgentHostClient devtools_agent_host_client;
+  devtools_agent_host->AttachClient(&devtools_agent_host_client);
+
+  // Navigate away from the page. This should block bfcache.
+  const GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), b_url));
+  EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
+      process_id, frame_routing_id, "RenderFrameDevToolsAgentHost"));
+
+  devtools_agent_host->DetachClient(&devtools_agent_host_client);
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/debug_urls.cc b/content/browser/frame_host/debug_urls.cc
index 52fa902b..cf7ada7 100644
--- a/content/browser/frame_host/debug_urls.cc
+++ b/content/browser/frame_host/debug_urls.cc
@@ -174,7 +174,7 @@
   if (url == kChromeUIGpuCleanURL) {
     GpuProcessHost::CallOnIO(GPU_PROCESS_KIND_SANDBOXED,
                              false /* force_create */,
-                             base::Bind([](GpuProcessHost* host) {
+                             base::BindOnce([](GpuProcessHost* host) {
                                if (host)
                                  host->gpu_service()->DestroyAllChannels();
                              }));
@@ -184,7 +184,7 @@
   if (url == kChromeUIGpuCrashURL) {
     GpuProcessHost::CallOnIO(GPU_PROCESS_KIND_SANDBOXED,
                              false /* force_create */,
-                             base::Bind([](GpuProcessHost* host) {
+                             base::BindOnce([](GpuProcessHost* host) {
                                if (host)
                                  host->gpu_service()->Crash();
                              }));
@@ -195,7 +195,7 @@
   if (url == kChromeUIGpuJavaCrashURL) {
     GpuProcessHost::CallOnIO(GPU_PROCESS_KIND_SANDBOXED,
                              false /* force_create */,
-                             base::Bind([](GpuProcessHost* host) {
+                             base::BindOnce([](GpuProcessHost* host) {
                                if (host)
                                  host->gpu_service()->ThrowJavaException();
                              }));
@@ -206,7 +206,7 @@
   if (url == kChromeUIGpuHangURL) {
     GpuProcessHost::CallOnIO(GPU_PROCESS_KIND_SANDBOXED,
                              false /* force_create */,
-                             base::Bind([](GpuProcessHost* host) {
+                             base::BindOnce([](GpuProcessHost* host) {
                                if (host)
                                  host->gpu_service()->Hang();
                              }));
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 64f96c9..6cf3ff8 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -521,7 +521,7 @@
       is_initial_navigation_(true),
       in_navigate_to_pending_entry_(false),
       pending_reload_(ReloadType::NONE),
-      get_timestamp_callback_(base::Bind(&base::Time::Now)),
+      get_timestamp_callback_(base::BindRepeating(&base::Time::Now)),
       entry_replaced_by_post_commit_error_(nullptr) {
   DCHECK(browser_context_);
 }
@@ -3497,7 +3497,7 @@
 }
 
 void NavigationControllerImpl::SetGetTimestampCallbackForTest(
-    const base::Callback<base::Time()>& get_timestamp_callback) {
+    const base::RepeatingCallback<base::Time()>& get_timestamp_callback) {
   get_timestamp_callback_ = get_timestamp_callback;
 }
 
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h
index 8886cb2..651d239 100644
--- a/content/browser/frame_host/navigation_controller_impl.h
+++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -276,7 +276,7 @@
   static size_t max_entry_count();
 
   void SetGetTimestampCallbackForTest(
-      const base::Callback<base::Time()>& get_timestamp_callback);
+      const base::RepeatingCallback<base::Time()>& get_timestamp_callback);
 
   // Discards only the pending entry. |was_failure| should be set if the pending
   // entry is being discarded because it failed to load.
@@ -619,7 +619,7 @@
   ReloadType pending_reload_;
 
   // Used to get timestamps for newly-created navigation entries.
-  base::Callback<base::Time()> get_timestamp_callback_;
+  base::RepeatingCallback<base::Time()> get_timestamp_callback_;
 
   // Used to smooth out timestamps from |get_timestamp_callback_|.
   // Without this, whenever there is a run of redirects or
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index da6905a..a9648cb 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -7290,8 +7290,8 @@
 
     // Suspend the message.
     web_contents_observer_.SetCallback(
-        base::Bind(&RenderFrameHost::OnMessageReceived,
-                   base::Unretained(render_frame_host_), message));
+        base::BindOnce(&RenderFrameHost::OnMessageReceived,
+                       base::Unretained(render_frame_host_), message));
     return true;
   }
 
@@ -7306,7 +7306,7 @@
   // OnMessageReceived function; this is the simplest way to disambiguate.
   class : public WebContentsObserver {
    public:
-    using Callback = base::Callback<bool()>;
+    using Callback = base::OnceCallback<bool()>;
 
     using WebContentsObserver::Observe;
 
@@ -7319,7 +7319,7 @@
         return;
 
       // Resume the message.
-      callback_.Run();
+      std::move(callback_).Run();
     }
 
     Callback callback_;
@@ -7409,7 +7409,7 @@
   void SetUpOnMainThread() override {
     // Accumulate all http requests made to |embedded_test_server| into
     // |accumulated_requests_| container.
-    embedded_test_server()->RegisterRequestMonitor(base::Bind(
+    embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
         &RequestMonitoringNavigationBrowserTest::MonitorRequestOnIoThread,
         weak_factory_.GetWeakPtr(), base::SequencedTaskRunnerHandle::Get()));
 
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 6c786e3..51b13de 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -526,7 +526,7 @@
 
   // Set the clock to always return a timestamp of 1.
   controller.SetGetTimestampCallbackForTest(
-      base::Bind(&GetFixedTime, InMicrosecondsSinceEpoch(1)));
+      base::BindRepeating(&GetFixedTime, InMicrosecondsSinceEpoch(1)));
 
   const GURL url1("http://foo1");
   const GURL url2("http://foo2");
@@ -1442,13 +1442,6 @@
   EXPECT_FALSE(committed_entry->should_clear_history_list());
 }
 
-namespace {
-void SetRedirects(const std::vector<GURL>& redirects,
-                  FrameHostMsg_DidCommitProvisionalLoad_Params* params) {
-  params->redirects = redirects;
-}
-}  // namespace
-
 // Test that Redirects are preserved after a commit.
 TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
   NavigationControllerImpl& controller = controller_impl();
@@ -1461,7 +1454,6 @@
   // Set up some redirect values.
   std::vector<GURL> redirects;
   redirects.push_back(url2);
-  auto set_redirects_callback = base::Bind(SetRedirects, redirects);
 
   // Set redirects on the pending entry.
   NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 252a5c2..667e28d 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1176,8 +1176,8 @@
   // the NavigationHandle where the callback will be stored.
   // TODO(clamy): pass the method to the NavigationHandle instead of a
   // boolean.
-  WillStartRequest(base::Bind(&NavigationRequest::OnStartChecksComplete,
-                              base::Unretained(this)));
+  WillStartRequest(base::BindOnce(&NavigationRequest::OnStartChecksComplete,
+                                  base::Unretained(this)));
 }
 
 void NavigationRequest::SetWaitingForRendererResponse() {
@@ -1377,8 +1377,8 @@
     // (rather than passing the old URL).
     UpdateStateFollowingRedirect(
         GURL(redirect_info.new_referrer),
-        base::Bind(&NavigationRequest::OnRedirectChecksComplete,
-                   base::Unretained(this)));
+        base::BindOnce(&NavigationRequest::OnRedirectChecksComplete,
+                       base::Unretained(this)));
     frame_tree_node_->ResetNavigationRequest(false, true);
     return;
   }
@@ -1523,9 +1523,10 @@
 
   // It's safe to use base::Unretained because this NavigationRequest owns the
   // NavigationHandle where the callback will be stored.
-  WillRedirectRequest(common_params_->referrer->url, expected_process,
-                      base::Bind(&NavigationRequest::OnRedirectChecksComplete,
-                                 base::Unretained(this)));
+  WillRedirectRequest(
+      common_params_->referrer->url, expected_process,
+      base::BindOnce(&NavigationRequest::OnRedirectChecksComplete,
+                     base::Unretained(this)));
 }
 
 void NavigationRequest::OnResponseStarted(
@@ -1811,8 +1812,8 @@
 
   // Check if the navigation should be allowed to proceed.
   WillProcessResponse(
-      base::Bind(&NavigationRequest::OnWillProcessResponseChecksComplete,
-                 base::Unretained(this)));
+      base::BindOnce(&NavigationRequest::OnWillProcessResponseChecksComplete,
+                     base::Unretained(this)));
 }
 
 void NavigationRequest::OnRequestFailed(
diff --git a/content/browser/frame_host/navigation_request_browsertest.cc b/content/browser/frame_host/navigation_request_browsertest.cc
index 0eb1479f..f57917b 100644
--- a/content/browser/frame_host/navigation_request_browsertest.cc
+++ b/content/browser/frame_host/navigation_request_browsertest.cc
@@ -63,19 +63,19 @@
       NavigationThrottle::ThrottleCheckResult will_redirect_result,
       NavigationThrottle::ThrottleCheckResult will_fail_result,
       NavigationThrottle::ThrottleCheckResult will_process_result,
-      base::Closure did_call_will_start,
-      base::Closure did_call_will_redirect,
-      base::Closure did_call_will_fail,
-      base::Closure did_call_will_process)
+      base::OnceClosure did_call_will_start,
+      base::OnceClosure did_call_will_redirect,
+      base::OnceClosure did_call_will_fail,
+      base::OnceClosure did_call_will_process)
       : NavigationThrottle(handle),
         will_start_result_(will_start_result),
         will_redirect_result_(will_redirect_result),
         will_fail_result_(will_fail_result),
         will_process_result_(will_process_result),
-        did_call_will_start_(did_call_will_start),
-        did_call_will_redirect_(did_call_will_redirect),
-        did_call_will_fail_(did_call_will_fail),
-        did_call_will_process_(did_call_will_process) {}
+        did_call_will_start_(std::move(did_call_will_start)),
+        did_call_will_redirect_(std::move(did_call_will_redirect)),
+        did_call_will_fail_(std::move(did_call_will_fail)),
+        did_call_will_process_(std::move(did_call_will_process)) {}
   ~TestNavigationThrottle() override {}
 
   const char* GetNameForLogging() override { return "TestNavigationThrottle"; }
@@ -100,7 +100,8 @@
              navigation_request->request_context_type());
     request_context_type_ = navigation_request->request_context_type();
 
-    base::PostTask(FROM_HERE, {BrowserThread::UI}, did_call_will_start_);
+    base::PostTask(FROM_HERE, {BrowserThread::UI},
+                   std::move(did_call_will_start_));
     return will_start_result_;
   }
 
@@ -109,7 +110,8 @@
         NavigationRequest::From(navigation_handle());
     CHECK_EQ(request_context_type_, navigation_request->request_context_type());
 
-    base::PostTask(FROM_HERE, {BrowserThread::UI}, did_call_will_redirect_);
+    base::PostTask(FROM_HERE, {BrowserThread::UI},
+                   std::move(did_call_will_redirect_));
     return will_redirect_result_;
   }
 
@@ -118,7 +120,8 @@
         NavigationRequest::From(navigation_handle());
     CHECK_EQ(request_context_type_, navigation_request->request_context_type());
 
-    base::PostTask(FROM_HERE, {BrowserThread::UI}, did_call_will_fail_);
+    base::PostTask(FROM_HERE, {BrowserThread::UI},
+                   std::move(did_call_will_fail_));
     return will_fail_result_;
   }
 
@@ -127,7 +130,8 @@
         NavigationRequest::From(navigation_handle());
     CHECK_EQ(request_context_type_, navigation_request->request_context_type());
 
-    base::PostTask(FROM_HERE, {BrowserThread::UI}, did_call_will_process_);
+    base::PostTask(FROM_HERE, {BrowserThread::UI},
+                   std::move(did_call_will_process_));
     return will_process_result_;
   }
 
@@ -135,10 +139,10 @@
   NavigationThrottle::ThrottleCheckResult will_redirect_result_;
   NavigationThrottle::ThrottleCheckResult will_fail_result_;
   NavigationThrottle::ThrottleCheckResult will_process_result_;
-  base::Closure did_call_will_start_;
-  base::Closure did_call_will_redirect_;
-  base::Closure did_call_will_fail_;
-  base::Closure did_call_will_process_;
+  base::OnceClosure did_call_will_start_;
+  base::OnceClosure did_call_will_redirect_;
+  base::OnceClosure did_call_will_fail_;
+  base::OnceClosure did_call_will_process_;
   blink::mojom::RequestContextType request_context_type_ =
       blink::mojom::RequestContextType::UNSPECIFIED;
 };
@@ -286,14 +290,17 @@
     std::unique_ptr<NavigationThrottle> throttle(new TestNavigationThrottle(
         handle, will_start_result_, will_redirect_result_, will_fail_result_,
         will_process_result_,
-        base::Bind(&TestNavigationThrottleInstaller::DidCallWillStartRequest,
-                   weak_factory_.GetWeakPtr()),
-        base::Bind(&TestNavigationThrottleInstaller::DidCallWillRedirectRequest,
-                   weak_factory_.GetWeakPtr()),
-        base::Bind(&TestNavigationThrottleInstaller::DidCallWillFailRequest,
-                   weak_factory_.GetWeakPtr()),
-        base::Bind(&TestNavigationThrottleInstaller::DidCallWillProcessResponse,
-                   weak_factory_.GetWeakPtr())));
+        base::BindOnce(
+            &TestNavigationThrottleInstaller::DidCallWillStartRequest,
+            weak_factory_.GetWeakPtr()),
+        base::BindOnce(
+            &TestNavigationThrottleInstaller::DidCallWillRedirectRequest,
+            weak_factory_.GetWeakPtr()),
+        base::BindOnce(&TestNavigationThrottleInstaller::DidCallWillFailRequest,
+                       weak_factory_.GetWeakPtr()),
+        base::BindOnce(
+            &TestNavigationThrottleInstaller::DidCallWillProcessResponse,
+            weak_factory_.GetWeakPtr())));
     navigation_throttle_ = static_cast<TestNavigationThrottle*>(throttle.get());
     handle->RegisterThrottleForTesting(std::move(throttle));
     ++install_count_;
diff --git a/content/browser/frame_host/navigation_request_unittest.cc b/content/browser/frame_host/navigation_request_unittest.cc
index c0cb26b0..40769e3 100644
--- a/content/browser/frame_host/navigation_request_unittest.cc
+++ b/content/browser/frame_host/navigation_request_unittest.cc
@@ -92,8 +92,8 @@
     // It's safe to use base::Unretained since the NavigationHandle is owned by
     // the NavigationRequestTest.
     request_->WillStartRequest(
-        base::Bind(&NavigationRequestTest::UpdateThrottleCheckResult,
-                   base::Unretained(this)));
+        base::BindOnce(&NavigationRequestTest::UpdateThrottleCheckResult,
+                       base::Unretained(this)));
   }
 
   // Helper function to call WillRedirectRequest on |handle|. If this function
@@ -109,8 +109,8 @@
     // the NavigationRequestTest.
     request_->WillRedirectRequest(
         GURL(), nullptr,
-        base::Bind(&NavigationRequestTest::UpdateThrottleCheckResult,
-                   base::Unretained(this)));
+        base::BindOnce(&NavigationRequestTest::UpdateThrottleCheckResult,
+                       base::Unretained(this)));
   }
 
   // Helper function to call WillFailRequest on |handle|. If this function
@@ -126,8 +126,8 @@
     // It's safe to use base::Unretained since the NavigationHandle is owned by
     // the NavigationRequestTest.
     request_->WillFailRequest(
-        base::Bind(&NavigationRequestTest::UpdateThrottleCheckResult,
-                   base::Unretained(this)));
+        base::BindOnce(&NavigationRequestTest::UpdateThrottleCheckResult,
+                       base::Unretained(this)));
   }
 
   // Helper function to call WillProcessResponse on |handle|. If this function
@@ -144,8 +144,8 @@
     // to WillRedirectRequest to verify that it's correctly plumbed in both
     // cases.
     request_->WillProcessResponse(
-        base::Bind(&NavigationRequestTest::UpdateThrottleCheckResult,
-                   base::Unretained(this)));
+        base::BindOnce(&NavigationRequestTest::UpdateThrottleCheckResult,
+                       base::Unretained(this)));
   }
 
   // Whether the callback was called.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b24bebb..1e8c682 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -285,6 +285,26 @@
   }
 }
 
+// Returns true if |url| & |base_url| represents a WebView loadDataWithBaseUrl
+// navigation.
+bool IsLoadDataWithBaseURL(const GURL& url, const GURL& base_url) {
+  return url.SchemeIs(url::kDataScheme) && !base_url.is_empty();
+}
+
+// Returns true if |common_params| represents a WebView loadDataWithBaseUrl
+// navigation.
+bool IsLoadDataWithBaseURL(const mojom::CommonNavigationParams& common_params) {
+  return IsLoadDataWithBaseURL(common_params.url,
+                               common_params.base_url_for_data_url);
+}
+
+// Returns true if |validated_params| represents a WebView loadDataWithBaseUrl
+// navigation.
+bool IsLoadDataWithBaseURL(
+    const FrameHostMsg_DidCommitProvisionalLoad_Params& validated_params) {
+  return IsLoadDataWithBaseURL(validated_params.url, validated_params.base_url);
+}
+
 // Ensure that we reset nav_entry_id_ in DidCommitProvisionalLoad if any of
 // the validations fail and lead to an early return.  Call disable() once we
 // know the commit will be successful.  Resetting nav_entry_id_ avoids acting on
@@ -411,8 +431,7 @@
 
   // Check if this is loadDataWithBaseUrl (which needs special treatment).
   auto& common_params = navigation_request->common_params();
-  if (common_params.url.SchemeIs(url::kDataScheme) &&
-      !common_params.base_url_for_data_url.is_empty()) {
+  if (IsLoadDataWithBaseURL(common_params)) {
     // A (potentially attacker-controlled) renderer process should not be able
     // to use loadDataWithBaseUrl code path to initiate fetches on behalf of a
     // victim origin (fetches controlled by attacker-provided
@@ -4607,27 +4626,31 @@
     return CanCommitStatus::CANNOT_COMMIT_URL;
 
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  const CanCommitStatus can_commit_status =
-      policy->CanCommitOriginAndUrl(GetProcess()->GetID(), origin, url);
+  const CanCommitStatus can_commit_status = policy->CanCommitOriginAndUrl(
+      GetProcess()->GetID(), GetSiteInstance()->GetIsolationContext(), origin,
+      url);
   if (can_commit_status != CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL)
     return can_commit_status;
 
-  if (!origin.opaque()) {
-    // A non-opaque origin must be a valid URL, which allows us to safely do a
-    // conversion to GURL.
-    GURL origin_url = origin.GetURL();
-
-    // Verify that the origin is allowed to commit in this process.
+  const auto origin_tuple_or_precursor_tuple =
+      origin.GetTupleOrPrecursorTupleIfOpaque();
+  if (!origin_tuple_or_precursor_tuple.IsInvalid()) {
+    // Verify that the origin/precursor is allowed to commit in this process.
     // Note: This also handles non-standard cases for |url|, such as
     // about:blank, data, and blob URLs.
 
     // Renderer-debug URLs can never be committed.
-    if (IsRendererDebugURL(origin_url))
+    if (IsRendererDebugURL(origin_tuple_or_precursor_tuple.GetURL()))
       return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
 
-    // Give the client a chance to disallow URLs from committing.
-    if (!GetContentClient()->browser()->CanCommitURL(GetProcess(), origin_url))
+    // Give the client a chance to disallow origin URLs from committing.
+    // TODO(acolwell): Fix this code to work with opaque origins. Currently
+    // some opaque origin precursors, like chrome-extension schemes, can trigger
+    // the commit to fail. These need to be investigated.
+    if (!origin.opaque() && !GetContentClient()->browser()->CanCommitURL(
+                                GetProcess(), origin.GetURL())) {
       return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
+    }
   }
 
   return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
@@ -5948,8 +5971,7 @@
   if (!GetProcess()->IsForGuestsOnly()) {
     ChildProcessSecurityPolicyImpl::GetInstance()->GrantCommitURL(
         GetProcess()->GetID(), common_params.url);
-    if (common_params.url.SchemeIs(url::kDataScheme) &&
-        !common_params.base_url_for_data_url.is_empty()) {
+    if (IsLoadDataWithBaseURL(common_params)) {
       // When there's a base URL specified for the data URL, we also need to
       // grant access to the base URL. This allows file: and other unexpected
       // schemes to be accepted at commit time and during CORS checks (e.g., for
@@ -6789,7 +6811,23 @@
       bypass_checks_for_file_scheme = true;
   }
 
-  if (!bypass_checks_for_error_page && !bypass_checks_for_file_scheme) {
+  // WebView's loadDataWithBaseURL API is allowed to bypass normal commit
+  // checks because it is allowed to commit anything into its unlocked process
+  // and its data: URL and non-opaque origin would fail the normal commit
+  // checks.
+  bool bypass_checks_for_webview = false;
+  if ((navigation_request &&
+       IsLoadDataWithBaseURL(navigation_request->common_params())) ||
+      (is_same_document_navigation &&
+       IsLoadDataWithBaseURL(*validated_params))) {
+    // Allow bypass if the process isn't locked. Otherwise run normal checks.
+    bypass_checks_for_webview = ChildProcessSecurityPolicyImpl::GetInstance()
+                                    ->GetOriginLock(process->GetID())
+                                    .is_empty();
+  }
+
+  if (!bypass_checks_for_error_page && !bypass_checks_for_file_scheme &&
+      !bypass_checks_for_webview) {
     // Attempts to commit certain off-limits URL should be caught more strictly
     // than our FilterURL checks.  If a renderer violates this policy, it
     // should be killed.
@@ -6799,6 +6837,12 @@
         // The origin and URL are safe to commit.
         break;
       case CanCommitStatus::CANNOT_COMMIT_URL:
+        DLOG(ERROR) << "CANNOT_COMMIT_URL url '" << validated_params->url << "'"
+                    << " origin '" << validated_params->origin << "'"
+                    << " lock '"
+                    << ChildProcessSecurityPolicyImpl::GetInstance()
+                           ->GetOriginLock(process->GetID())
+                    << "'";
         VLOG(1) << "Blocked URL " << validated_params->url.spec();
         LogCannotCommitUrlCrashKeys(validated_params->url,
                                     is_same_document_navigation,
@@ -6809,6 +6853,13 @@
             process, bad_message::RFH_CAN_COMMIT_URL_BLOCKED);
         return false;
       case CanCommitStatus::CANNOT_COMMIT_ORIGIN:
+        DLOG(ERROR) << "CANNOT_COMMIT_ORIGIN url '" << validated_params->url
+                    << "'"
+                    << " origin '" << validated_params->origin << "'"
+                    << " lock '"
+                    << ChildProcessSecurityPolicyImpl::GetInstance()
+                           ->GetOriginLock(process->GetID())
+                    << "'";
         DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, validated_params->origin);
         LogCannotCommitOriginCrashKeys(is_same_document_navigation,
                                        navigation_request);
diff --git a/content/browser/manifest/manifest_browsertest.cc b/content/browser/manifest/manifest_browsertest.cc
index 20d2698..969930b 100644
--- a/content/browser/manifest/manifest_browsertest.cc
+++ b/content/browser/manifest/manifest_browsertest.cc
@@ -577,7 +577,7 @@
   std::unique_ptr<net::EmbeddedTestServer> custom_embedded_test_server(
       new net::EmbeddedTestServer());
   custom_embedded_test_server->RegisterRequestHandler(
-      base::Bind(&CustomHandleRequestForCookies));
+      base::BindRepeating(&CustomHandleRequestForCookies));
 
   ASSERT_TRUE(custom_embedded_test_server->Start());
 
@@ -637,7 +637,7 @@
   std::unique_ptr<net::EmbeddedTestServer> custom_embedded_test_server(
       new net::EmbeddedTestServer());
   custom_embedded_test_server->RegisterRequestHandler(
-      base::Bind(&CustomHandleRequestForNoCookies));
+      base::BindRepeating(&CustomHandleRequestForNoCookies));
 
   ASSERT_TRUE(custom_embedded_test_server->Start());
 
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index 6f253c5..a4b94b2 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -220,6 +220,9 @@
   if (is_remote)
     return;
 
+  BackForwardCache::DisableForRenderFrameHost(
+      render_frame_host, "MediaWebContentsObserver::OnMediaPlaying");
+
   const MediaPlayerId id(render_frame_host, delegate_id);
   if (has_audio)
     AddMediaPlayerEntry(id, &active_audio_players_);
diff --git a/content/browser/scheduler/browser_io_thread_delegate.cc b/content/browser/scheduler/browser_io_thread_delegate.cc
index 8959a52..f0e5516 100644
--- a/content/browser/scheduler/browser_io_thread_delegate.cc
+++ b/content/browser/scheduler/browser_io_thread_delegate.cc
@@ -9,6 +9,7 @@
 #include "base/task/sequence_manager/sequence_manager.h"
 #include "base/task/sequence_manager/task_queue.h"
 #include "base/task/task_executor.h"
+#include "base/task/task_observer.h"
 #include "content/browser/scheduler/browser_task_executor.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -18,40 +19,82 @@
 using ::base::sequence_manager::SequenceManager;
 using ::base::sequence_manager::TaskQueue;
 
-BrowserIOThreadDelegate::BrowserIOThreadDelegate(
-    BrowserTaskExecutorPresent browser_task_executor_present)
-    : sequence_manager_(CreateUnboundSequenceManager(
+class BrowserIOThreadDelegate::TLSMultiplexer : public base::TaskObserver {
+ public:
+  TLSMultiplexer() = default;
+  ~TLSMultiplexer() override = default;
+
+  void SetIOTaskExecutor(base::TaskExecutor* io_task_executor) {
+    io_task_executor_ = io_task_executor;
+  }
+
+  void WillProcessTask(const base::PendingTask& pending_task) override {
+    base::TaskExecutor* previous_executor =
+        base::GetTaskExecutorForCurrentThread();
+    if (previous_executor) {
+      previous_executors_.push_back(previous_executor);
+      base::SetTaskExecutorForCurrentThread(nullptr);
+    }
+    base::SetTaskExecutorForCurrentThread(io_task_executor_);
+  }
+
+  void DidProcessTask(const base::PendingTask& pending_task) override {
+    base::SetTaskExecutorForCurrentThread(nullptr);
+    if (!previous_executors_.empty()) {
+      base::SetTaskExecutorForCurrentThread(previous_executors_.back());
+      previous_executors_.pop_back();
+    }
+  }
+
+  base::TaskExecutor* io_task_executor_ = nullptr;
+  std::vector<base::TaskExecutor*> previous_executors_;
+};
+
+BrowserIOThreadDelegate::BrowserIOThreadDelegate()
+    : owned_sequence_manager_(CreateUnboundSequenceManager(
           SequenceManager::Settings::Builder()
               .SetMessagePumpType(base::MessagePumpType::IO)
               .Build())),
-      browser_task_executor_present_(browser_task_executor_present) {
-  Init(sequence_manager_.get());
+      sequence_manager_(owned_sequence_manager_.get()) {
+  Init();
 }
 
 BrowserIOThreadDelegate::BrowserIOThreadDelegate(
     SequenceManager* sequence_manager)
-    : sequence_manager_(nullptr),
-      browser_task_executor_present_(BrowserTaskExecutorPresent::kYes) {
-  Init(sequence_manager);
+    : sequence_manager_(sequence_manager),
+      tls_multiplexer_(std::make_unique<TLSMultiplexer>()) {
+  sequence_manager_->AddTaskObserver(tls_multiplexer_.get());
+  Init();
 }
 
-void BrowserIOThreadDelegate::Init(
-    base::sequence_manager::SequenceManager* sequence_manager) {
+void BrowserIOThreadDelegate::Init() {
   task_queues_ = std::make_unique<BrowserTaskQueues>(
-      BrowserThread::IO, sequence_manager,
-      sequence_manager->GetRealTimeDomain());
+      BrowserThread::IO, sequence_manager_,
+      sequence_manager_->GetRealTimeDomain());
   default_task_runner_ = task_queues_->GetHandle()->GetDefaultTaskRunner();
 }
 
+void BrowserIOThreadDelegate::SetTaskExecutor(
+    base::TaskExecutor* task_executor) {
+  if (tls_multiplexer_) {
+    tls_multiplexer_->SetIOTaskExecutor(task_executor);
+  } else {
+    task_executor_ = task_executor;
+  }
+}
+
 scoped_refptr<base::SingleThreadTaskRunner>
 BrowserIOThreadDelegate::GetDefaultTaskRunner() {
   return default_task_runner_;
 }
 
 BrowserIOThreadDelegate::~BrowserIOThreadDelegate() {
-  if (browser_task_executor_present_ == BrowserTaskExecutorPresent::kYes) {
+  if (task_executor_) {
     base::SetTaskExecutorForCurrentThread(nullptr);
   }
+  if (tls_multiplexer_) {
+    sequence_manager_->RemoveTaskObserver(tls_multiplexer_.get());
+  }
 }
 
 void BrowserIOThreadDelegate::BindToCurrentThread(
@@ -62,8 +105,8 @@
   sequence_manager_->SetTimerSlack(timer_slack);
   sequence_manager_->SetDefaultTaskRunner(GetDefaultTaskRunner());
 
-  if (browser_task_executor_present_ == BrowserTaskExecutorPresent::kYes) {
-    base::SetTaskExecutorForCurrentThread(BrowserTaskExecutor::Get());
+  if (task_executor_) {
+    base::SetTaskExecutorForCurrentThread(task_executor_);
   }
 }
 
diff --git a/content/browser/scheduler/browser_io_thread_delegate.h b/content/browser/scheduler/browser_io_thread_delegate.h
index 703e7c5..0954c5e 100644
--- a/content/browser/scheduler/browser_io_thread_delegate.h
+++ b/content/browser/scheduler/browser_io_thread_delegate.h
@@ -14,6 +14,7 @@
 
 namespace base {
 class SingleThreadTaskRunner;
+class TaskExecutor;
 
 namespace sequence_manager {
 class SequenceManager;
@@ -28,21 +29,18 @@
  public:
   using Handle = BrowserTaskQueues::Handle;
 
-  // Normally, creating a BrowserIOThreadDelegate relies on a
-  // BrowserTaskExecutor already existing to register it as the executor for the
-  // current (IO) thread. However, some tests create it in isolation, so we need
-  // to disable registering the executor to pass checks.
-  enum class BrowserTaskExecutorPresent { kYes, kNoForTesting };
+  // Creates a BrowserIOThreadDelegate for use with a real IO thread.
+  BrowserIOThreadDelegate();
+  ~BrowserIOThreadDelegate() override;
 
   static std::unique_ptr<BrowserIOThreadDelegate> CreateForTesting(
       base::sequence_manager::SequenceManager* sequence_manager) {
+    DCHECK(sequence_manager);
     return base::WrapUnique(new BrowserIOThreadDelegate(sequence_manager));
   }
 
-  explicit BrowserIOThreadDelegate(
-      BrowserTaskExecutorPresent browser_task_executor_present =
-          BrowserTaskExecutorPresent::kYes);
-  ~BrowserIOThreadDelegate() override;
+  // If called this must be done prior to calling BindToCurrentThread.
+  void SetTaskExecutor(base::TaskExecutor* task_executor);
 
   scoped_refptr<base::SingleThreadTaskRunner> GetDefaultTaskRunner() override;
   void BindToCurrentThread(base::TimerSlack timer_slack) override;
@@ -55,29 +53,37 @@
   // tests.
   void SetAllowBlockingForTesting() { allow_blocking_for_testing_ = true; }
 
-  bool browser_task_executor_present() const {
-    return browser_task_executor_present_ == BrowserTaskExecutorPresent::kYes;
-  }
-
-  scoped_refptr<Handle> CreateHandle() { return task_queues_->GetHandle(); }
+  scoped_refptr<Handle> GetHandle() { return task_queues_->GetHandle(); }
 
  private:
+  class TLSMultiplexer;
+
+  // Creates a sequence funneled BrowserIOThreadDelegate for use in testing.
+  // Installs TLSMultiplexer which allows ensures the right results for
+  // base::CurrentThread when running an "IO Thread" task.
   explicit BrowserIOThreadDelegate(
       base::sequence_manager::SequenceManager* sequence_manager);
 
   // Performs the actual initialization of all the members that require a
-  // SequenceManager. Just a convenience method to avoid code duplication as in
-  // testing |sequence_manager_| will be null;
-  void Init(base::sequence_manager::SequenceManager* sequence_manager);
+  // SequenceManager.
+  void Init();
 
   bool allow_blocking_for_testing_ = false;
   // Owned SequenceManager, null if instance created via CreateForTesting.
-  std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager_;
+  const std::unique_ptr<base::sequence_manager::SequenceManager>
+      owned_sequence_manager_;
+
+  base::sequence_manager::SequenceManager* const sequence_manager_;
 
   std::unique_ptr<BrowserTaskQueues> task_queues_;
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
 
-  const BrowserTaskExecutorPresent browser_task_executor_present_;
+  // In unit tests the IO "thread" can be sequence funneled onto the main thread
+  // so we need to multiplex the TLS binding to ensure base::CurrentThread
+  // behaves as expected.
+  std::unique_ptr<TLSMultiplexer> tls_multiplexer_;
+
+  base::TaskExecutor* task_executor_ = nullptr;
 };
 
 }  // namespace content
diff --git a/content/browser/scheduler/browser_io_thread_delegate_unittest.cc b/content/browser/scheduler/browser_io_thread_delegate_unittest.cc
index f7378f0..ed39160 100644
--- a/content/browser/scheduler/browser_io_thread_delegate_unittest.cc
+++ b/content/browser/scheduler/browser_io_thread_delegate_unittest.cc
@@ -20,9 +20,8 @@
 TEST(BrowserIOThreadDelegateTest, CanPostTasksToThread) {
   base::Thread thread("my_thread");
 
-  auto delegate = std::make_unique<BrowserIOThreadDelegate>(
-      BrowserIOThreadDelegate::BrowserTaskExecutorPresent::kNoForTesting);
-  auto handle = delegate->CreateHandle();
+  auto delegate = std::make_unique<BrowserIOThreadDelegate>();
+  auto handle = delegate->GetHandle();
   handle->EnableAllQueues();
 
   base::Thread::Options options;
@@ -41,8 +40,7 @@
 TEST(BrowserIOThreadDelegateTest, DefaultTaskRunnerIsAlwaysActive) {
   base::Thread thread("my_thread");
 
-  auto delegate = std::make_unique<BrowserIOThreadDelegate>(
-      BrowserIOThreadDelegate::BrowserTaskExecutorPresent::kNoForTesting);
+  auto delegate = std::make_unique<BrowserIOThreadDelegate>();
   auto task_runner = delegate->GetDefaultTaskRunner();
 
   base::Thread::Options options;
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc
index 37ba248..4f786a1 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -66,23 +66,121 @@
 
 }  // namespace
 
+BaseBrowserTaskExecutor::BaseBrowserTaskExecutor() = default;
+
+BaseBrowserTaskExecutor::~BaseBrowserTaskExecutor() = default;
+
+bool BaseBrowserTaskExecutor::PostDelayedTask(const base::Location& from_here,
+                                              const base::TaskTraits& traits,
+                                              base::OnceClosure task,
+                                              base::TimeDelta delay) {
+  if (traits.extension_id() != BrowserTaskTraitsExtension::kExtensionId ||
+      traits.GetExtension<BrowserTaskTraitsExtension>().nestable()) {
+    return GetTaskRunner(traits)->PostDelayedTask(from_here, std::move(task),
+                                                  delay);
+  } else {
+    return GetTaskRunner(traits)->PostNonNestableDelayedTask(
+        from_here, std::move(task), delay);
+  }
+}
+
+scoped_refptr<base::TaskRunner> BaseBrowserTaskExecutor::CreateTaskRunner(
+    const base::TaskTraits& traits) {
+  return GetTaskRunner(traits);
+}
+
+scoped_refptr<base::SequencedTaskRunner>
+BaseBrowserTaskExecutor::CreateSequencedTaskRunner(
+    const base::TaskTraits& traits) {
+  return GetTaskRunner(traits);
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+BaseBrowserTaskExecutor::CreateSingleThreadTaskRunner(
+    const base::TaskTraits& traits,
+    base::SingleThreadTaskRunnerThreadMode thread_mode) {
+  return GetTaskRunner(traits);
+}
+
+#if defined(OS_WIN)
+scoped_refptr<base::SingleThreadTaskRunner>
+BaseBrowserTaskExecutor::CreateCOMSTATaskRunner(
+    const base::TaskTraits& traits,
+    base::SingleThreadTaskRunnerThreadMode thread_mode) {
+  return GetTaskRunner(traits);
+}
+#endif  // defined(OS_WIN)
+
+scoped_refptr<base::SingleThreadTaskRunner>
+BaseBrowserTaskExecutor::GetTaskRunner(const base::TaskTraits& traits) const {
+  auto id_and_queue = GetThreadIdAndQueueType(traits);
+
+  switch (id_and_queue.thread_id) {
+    case BrowserThread::UI: {
+      return browser_ui_thread_handle_->GetBrowserTaskRunner(
+          id_and_queue.queue_type);
+    }
+    case BrowserThread::IO:
+      return browser_io_thread_handle_->GetBrowserTaskRunner(
+          id_and_queue.queue_type);
+    case BrowserThread::ID_COUNT:
+      NOTREACHED();
+  }
+  return nullptr;
+}
+
+BaseBrowserTaskExecutor::ThreadIdAndQueueType
+BaseBrowserTaskExecutor::GetThreadIdAndQueueType(
+    const base::TaskTraits& traits) const {
+  BrowserTaskType task_type;
+  BrowserThread::ID thread_id;
+
+  if (traits.use_current_thread()) {
+    thread_id = GetCurrentThreadID();
+
+    // BrowserTaskTraitsExtension is optional if use_current_thread() is true.
+    if (traits.extension_id() == BrowserTaskTraitsExtension::kExtensionId) {
+      task_type = traits.GetExtension<BrowserTaskTraitsExtension>().task_type();
+    } else {
+      task_type = BrowserTaskType::kDefault;
+    }
+  } else {
+    // Otherwise BrowserTaskTraitsExtension is mandatory.
+    DCHECK_EQ(BrowserTaskTraitsExtension::kExtensionId, traits.extension_id());
+    BrowserTaskTraitsExtension extension =
+        traits.GetExtension<BrowserTaskTraitsExtension>();
+
+    thread_id = extension.browser_thread();
+    DCHECK_GE(thread_id, 0);
+
+    task_type = extension.task_type();
+    DCHECK_LT(task_type, BrowserTaskType::kBrowserTaskType_Last);
+  }
+
+  return {thread_id, GetQueueType(traits, task_type)};
+}
+
 BrowserTaskExecutor::BrowserTaskExecutor(
     std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
     std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate)
-    : browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)),
-      browser_ui_thread_handle_(browser_ui_thread_scheduler_->GetHandle()),
-      browser_io_thread_delegate_(std::move(browser_io_thread_delegate)),
-      browser_io_thread_handle_(browser_io_thread_delegate_->CreateHandle()) {}
-
-BrowserTaskExecutor::~BrowserTaskExecutor() {
-  base::SetTaskExecutorForCurrentThread(nullptr);
+    : ui_thread_executor_(std::make_unique<UIThreadExecutor>(
+          std::move(browser_ui_thread_scheduler))),
+      io_thread_executor_(std::make_unique<IOThreadExecutor>(
+          std::move(browser_io_thread_delegate))) {
+  browser_ui_thread_handle_ = ui_thread_executor_->GetUIThreadHandle();
+  browser_io_thread_handle_ = io_thread_executor_->GetIOThreadHandle();
+  ui_thread_executor_->SetIOThreadHandle(browser_io_thread_handle_);
+  io_thread_executor_->SetUIThreadHandle(browser_ui_thread_handle_);
 }
 
+BrowserTaskExecutor::~BrowserTaskExecutor() = default;
+
 // static
 void BrowserTaskExecutor::Create() {
   DCHECK(!base::ThreadTaskRunnerHandle::IsSet());
   CreateInternal(std::make_unique<BrowserUIThreadScheduler>(),
                  std::make_unique<BrowserIOThreadDelegate>());
+  g_browser_task_executor->ui_thread_executor_->BindToCurrentThread();
 }
 
 // static
@@ -94,6 +192,11 @@
 }
 
 // static
+void BrowserTaskExecutor::BindToUIThreadForTesting() {
+  g_browser_task_executor->ui_thread_executor_->BindToCurrentThread();
+}
+
+// static
 void BrowserTaskExecutor::CreateInternal(
     std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
     std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate) {
@@ -103,13 +206,10 @@
                               std::move(browser_io_thread_delegate));
   base::RegisterTaskExecutor(BrowserTaskTraitsExtension::kExtensionId,
                              g_browser_task_executor);
+
   g_browser_task_executor->browser_ui_thread_handle_
       ->EnableAllExceptBestEffortQueues();
 
-  // Here we register the BrowserTaskExecutor for the UI thread; registration
-  // for the IO thread happens in BrowserIOThreadDelegate::BindToCurrentThread.
-  base::SetTaskExecutorForCurrentThread(g_browser_task_executor);
-
 #if defined(OS_ANDROID)
   base::PostTaskAndroid::SignalNativeSchedulerReady();
 #endif
@@ -134,14 +234,13 @@
     delete g_browser_task_executor;
     g_browser_task_executor = nullptr;
   }
-  base::SetTaskExecutorForCurrentThread(nullptr);
 }
 
 // static
 void BrowserTaskExecutor::PostFeatureListSetup() {
   DCHECK(g_browser_task_executor);
-  DCHECK(g_browser_task_executor->browser_ui_thread_scheduler_);
-  DCHECK(g_browser_task_executor->browser_io_thread_delegate_);
+  DCHECK(g_browser_task_executor->ui_thread_executor_);
+  DCHECK(g_browser_task_executor->io_thread_executor_);
   g_browser_task_executor->browser_ui_thread_handle_
       ->PostFeatureListInitializationSetup();
   g_browser_task_executor->browser_io_thread_handle_
@@ -153,7 +252,8 @@
   if (!g_browser_task_executor)
     return;
 
-  DCHECK(g_browser_task_executor->browser_ui_thread_scheduler_);
+  DCHECK(g_browser_task_executor->ui_thread_executor_);
+  DCHECK(g_browser_task_executor->io_thread_executor_);
   // We don't delete |g_browser_task_executor| because other threads may
   // PostTask or call BrowserTaskExecutor::GetTaskRunner while we're tearing
   // things down. We don't want to add locks so we just leak instead of dealing
@@ -161,8 +261,8 @@
   // PostTaskAndroid::SignalNativeSchedulerShutdown on Android. In tests however
   // we need to clean up, so BrowserTaskExecutor::ResetForTesting should be
   // called.
-  g_browser_task_executor->browser_ui_thread_scheduler_.reset();
-  g_browser_task_executor->browser_io_thread_delegate_.reset();
+  g_browser_task_executor->ui_thread_executor_.reset();
+  g_browser_task_executor->io_thread_executor_.reset();
 }
 
 // static
@@ -179,9 +279,9 @@
       break;
     case BrowserThread::IO: {
       // In tests there may not be a functional IO thread.
-      if (!g_browser_task_executor->browser_io_thread_delegate_ ||
-          !g_browser_task_executor->browser_io_thread_delegate_
-               ->browser_task_executor_present()) {
+      if (!g_browser_task_executor->io_thread_executor_ ||
+          !g_browser_task_executor->io_thread_executor_
+               ->HasDelegateForTesting()) {
         return;
       }
       g_browser_task_executor->browser_io_thread_handle_
@@ -195,82 +295,6 @@
   run_loop.Run();
 }
 
-bool BrowserTaskExecutor::PostDelayedTask(const base::Location& from_here,
-                                          const base::TaskTraits& traits,
-                                          base::OnceClosure task,
-                                          base::TimeDelta delay) {
-  DCHECK_EQ(BrowserTaskTraitsExtension::kExtensionId, traits.extension_id());
-  const BrowserTaskTraitsExtension& extension =
-      traits.GetExtension<BrowserTaskTraitsExtension>();
-  if (extension.nestable()) {
-    return GetTaskRunner(traits)->PostDelayedTask(from_here, std::move(task),
-                                                  delay);
-  } else {
-    return GetTaskRunner(traits)->PostNonNestableDelayedTask(
-        from_here, std::move(task), delay);
-  }
-}
-
-scoped_refptr<base::TaskRunner> BrowserTaskExecutor::CreateTaskRunner(
-    const base::TaskTraits& traits) {
-  return GetTaskRunner(traits);
-}
-
-scoped_refptr<base::SequencedTaskRunner>
-BrowserTaskExecutor::CreateSequencedTaskRunner(const base::TaskTraits& traits) {
-  return GetTaskRunner(traits);
-}
-
-scoped_refptr<base::SingleThreadTaskRunner>
-BrowserTaskExecutor::CreateSingleThreadTaskRunner(
-    const base::TaskTraits& traits,
-    base::SingleThreadTaskRunnerThreadMode thread_mode) {
-  return GetTaskRunner(traits);
-}
-
-#if defined(OS_WIN)
-scoped_refptr<base::SingleThreadTaskRunner>
-BrowserTaskExecutor::CreateCOMSTATaskRunner(
-    const base::TaskTraits& traits,
-    base::SingleThreadTaskRunnerThreadMode thread_mode) {
-  return GetTaskRunner(traits);
-}
-#endif  // defined(OS_WIN)
-
-scoped_refptr<base::SingleThreadTaskRunner> BrowserTaskExecutor::GetTaskRunner(
-    const base::TaskTraits& traits) const {
-  auto id_and_queue = GetThreadIdAndQueueType(traits);
-
-  switch (id_and_queue.thread_id) {
-    case BrowserThread::UI: {
-      return browser_ui_thread_handle_->GetBrowserTaskRunner(
-          id_and_queue.queue_type);
-    }
-    case BrowserThread::IO:
-      return browser_io_thread_handle_->GetBrowserTaskRunner(
-          id_and_queue.queue_type);
-    case BrowserThread::ID_COUNT:
-      NOTREACHED();
-  }
-  return nullptr;
-}
-
-// static
-BrowserTaskExecutor::ThreadIdAndQueueType
-BrowserTaskExecutor::GetThreadIdAndQueueType(const base::TaskTraits& traits) {
-  DCHECK_EQ(BrowserTaskTraitsExtension::kExtensionId, traits.extension_id());
-  BrowserTaskTraitsExtension extension =
-      traits.GetExtension<BrowserTaskTraitsExtension>();
-
-  BrowserThread::ID thread_id = extension.browser_thread();
-  DCHECK_GE(thread_id, 0);
-
-  BrowserTaskType task_type = extension.task_type();
-  DCHECK_LT(task_type, BrowserTaskType::kBrowserTaskType_Last);
-
-  return {thread_id, GetQueueType(traits, task_type)};
-}
-
 // static
 void BrowserTaskExecutor::EnableAllQueues() {
   DCHECK(g_browser_task_executor);
@@ -287,20 +311,23 @@
 
 std::unique_ptr<BrowserProcessSubThread> BrowserTaskExecutor::CreateIOThread() {
   DCHECK(g_browser_task_executor);
-  DCHECK(g_browser_task_executor->browser_io_thread_delegate_);
+  DCHECK(g_browser_task_executor->io_thread_executor_);
+
+  std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate =
+      g_browser_task_executor->io_thread_executor_->TakeDelegate();
+
+  DCHECK(browser_io_thread_delegate);
   TRACE_EVENT0("startup", "BrowserTaskExecutor::CreateIOThread");
 
   auto io_thread = std::make_unique<BrowserProcessSubThread>(BrowserThread::IO);
 
-  if (g_browser_task_executor->browser_io_thread_delegate_
-          ->allow_blocking_for_testing()) {
+  if (browser_io_thread_delegate->allow_blocking_for_testing()) {
     io_thread->AllowBlockingForTesting();
   }
 
   base::Thread::Options options;
   options.message_pump_type = base::MessagePumpType::IO;
-  options.delegate =
-      g_browser_task_executor->browser_io_thread_delegate_.release();
+  options.delegate = browser_io_thread_delegate.release();
   // Up the priority of the |io_thread_| as some of its IPCs relate to
   // display tasks.
   if (base::FeatureList::IsEnabled(features::kBrowserUseDisplayThreadPriority))
@@ -364,4 +391,68 @@
 
 #endif
 
+BrowserThread::ID BrowserTaskExecutor::GetCurrentThreadID() const {
+  NOTREACHED()
+      << "Should have been routed to UIThreadExecutor or IOThreadExecutor";
+  return BrowserThread::UI;
+}
+
+BrowserTaskExecutor::UIThreadExecutor::UIThreadExecutor(
+    std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler)
+    : browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)) {
+  browser_ui_thread_handle_ = browser_ui_thread_scheduler_->GetHandle();
+}
+
+BrowserTaskExecutor::UIThreadExecutor::~UIThreadExecutor() {
+  if (bound_to_thread_)
+    base::SetTaskExecutorForCurrentThread(nullptr);
+}
+
+void BrowserTaskExecutor::UIThreadExecutor::BindToCurrentThread() {
+  bound_to_thread_ = true;
+  base::SetTaskExecutorForCurrentThread(this);
+}
+
+scoped_refptr<BrowserUIThreadScheduler::Handle>
+BrowserTaskExecutor::UIThreadExecutor::GetUIThreadHandle() {
+  return browser_ui_thread_handle_;
+}
+
+void BrowserTaskExecutor::UIThreadExecutor::SetIOThreadHandle(
+    scoped_refptr<BrowserUIThreadScheduler::Handle> io_thread_handle) {
+  browser_io_thread_handle_ = std::move(io_thread_handle);
+}
+
+BrowserThread::ID BrowserTaskExecutor::UIThreadExecutor::GetCurrentThreadID()
+    const {
+  return BrowserThread::UI;
+}
+
+BrowserTaskExecutor::IOThreadExecutor::IOThreadExecutor(
+    std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate)
+    : browser_io_thread_delegate_(std::move(browser_io_thread_delegate)) {
+  // |browser_io_thread_delegate_| can be null in tests.
+  if (!browser_io_thread_delegate_)
+    return;
+  browser_io_thread_delegate_->SetTaskExecutor(this);
+  browser_io_thread_handle_ = browser_io_thread_delegate_->GetHandle();
+}
+
+BrowserTaskExecutor::IOThreadExecutor::~IOThreadExecutor() = default;
+
+scoped_refptr<BrowserUIThreadScheduler::Handle>
+BrowserTaskExecutor::IOThreadExecutor::GetIOThreadHandle() {
+  return browser_io_thread_handle_;
+}
+
+void BrowserTaskExecutor::IOThreadExecutor::SetUIThreadHandle(
+    scoped_refptr<BrowserUIThreadScheduler::Handle> ui_thread_handle) {
+  browser_ui_thread_handle_ = std::move(ui_thread_handle);
+}
+
+BrowserThread::ID BrowserTaskExecutor::IOThreadExecutor::GetCurrentThreadID()
+    const {
+  return BrowserThread::IO;
+}
+
 }  // namespace content
diff --git a/content/browser/scheduler/browser_task_executor.h b/content/browser/scheduler/browser_task_executor.h
index 720d6d7..8754d97d 100644
--- a/content/browser/scheduler/browser_task_executor.h
+++ b/content/browser/scheduler/browser_task_executor.h
@@ -17,14 +17,66 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
+// The BrowserTaskExecutor's job is to map base::TaskTraits to actual task
+// queues for the browser process.
+//
+// We actually have three TaskExecutors:
+// * BrowserTaskExecutor registered for BrowserTaskTraitsExtension.
+// * BrowserTaskExecutor::UIThreadExecutor registered with UI thread TLS.
+// * BrowserTaskExecutor::IOThreadExecutor registered with IO thread TLS.
+//
+// This lets us efficiently implement base::CurrentThread on UI and IO threads.
 namespace content {
 
 class BrowserTaskExecutorTest;
 class BrowserProcessSubThread;
 
-// This class's job is to map base::TaskTraits to actual task queues for the
-// browser process.
-class CONTENT_EXPORT BrowserTaskExecutor : public base::TaskExecutor {
+class CONTENT_EXPORT BaseBrowserTaskExecutor : public base::TaskExecutor {
+ public:
+  BaseBrowserTaskExecutor();
+  ~BaseBrowserTaskExecutor() override;
+
+  // base::TaskExecutor implementation.
+  bool PostDelayedTask(const base::Location& from_here,
+                       const base::TaskTraits& traits,
+                       base::OnceClosure task,
+                       base::TimeDelta delay) override;
+
+  scoped_refptr<base::TaskRunner> CreateTaskRunner(
+      const base::TaskTraits& traits) override;
+
+  scoped_refptr<base::SequencedTaskRunner> CreateSequencedTaskRunner(
+      const base::TaskTraits& traits) override;
+
+  scoped_refptr<base::SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
+      const base::TaskTraits& traits,
+      base::SingleThreadTaskRunnerThreadMode thread_mode) override;
+
+#if defined(OS_WIN)
+  scoped_refptr<base::SingleThreadTaskRunner> CreateCOMSTATaskRunner(
+      const base::TaskTraits& traits,
+      base::SingleThreadTaskRunnerThreadMode thread_mode) override;
+#endif  // defined(OS_WIN)
+
+  struct ThreadIdAndQueueType {
+    BrowserThread::ID thread_id;
+    BrowserTaskQueues::QueueType queue_type;
+  };
+
+  ThreadIdAndQueueType GetThreadIdAndQueueType(
+      const base::TaskTraits& traits) const;
+
+ protected:
+  virtual BrowserThread::ID GetCurrentThreadID() const = 0;
+
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
+      const base::TaskTraits& traits) const;
+
+  scoped_refptr<BrowserUIThreadScheduler::Handle> browser_ui_thread_handle_;
+  scoped_refptr<BrowserIOThreadDelegate::Handle> browser_io_thread_handle_;
+};
+
+class CONTENT_EXPORT BrowserTaskExecutor : public BaseBrowserTaskExecutor {
  public:
   // Creates and registers a BrowserTaskExecutor on the current thread which
   // owns a BrowserUIThreadScheduler. This facilitates posting tasks to a
@@ -74,11 +126,15 @@
   // Can be called multiple times.
   static void EnableAllQueues();
 
-  // As Create but with the user provided objects.
+  // As Create but with the user provided objects. Must call
+  // BindToUIThreadForTesting before tasks can be run on the UI thread.
   static void CreateForTesting(
       std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
       std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate);
 
+  // Completes ui-thread set up. Must be called on the UI thread.
+  static void BindToUIThreadForTesting();
+
   // This must be called after the FeatureList has been initialized in order
   // for scheduling experiments to function.
   static void PostFeatureListSetup();
@@ -104,36 +160,6 @@
   static void RunAllPendingTasksOnThreadForTesting(
       BrowserThread::ID identifier);
 
-  struct ThreadIdAndQueueType {
-    BrowserThread::ID thread_id;
-    BrowserTaskQueues::QueueType queue_type;
-  };
-
-  static ThreadIdAndQueueType GetThreadIdAndQueueType(
-      const base::TaskTraits& traits);
-
-  // base::TaskExecutor implementation.
-  bool PostDelayedTask(const base::Location& from_here,
-                       const base::TaskTraits& traits,
-                       base::OnceClosure task,
-                       base::TimeDelta delay) override;
-
-  scoped_refptr<base::TaskRunner> CreateTaskRunner(
-      const base::TaskTraits& traits) override;
-
-  scoped_refptr<base::SequencedTaskRunner> CreateSequencedTaskRunner(
-      const base::TaskTraits& traits) override;
-
-  scoped_refptr<base::SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
-      const base::TaskTraits& traits,
-      base::SingleThreadTaskRunnerThreadMode thread_mode) override;
-
-#if defined(OS_WIN)
-  scoped_refptr<base::SingleThreadTaskRunner> CreateCOMSTATaskRunner(
-      const base::TaskTraits& traits,
-      base::SingleThreadTaskRunnerThreadMode thread_mode) override;
-#endif  // defined(OS_WIN)
-
 #if DCHECK_IS_ON()
   // Adds a Validator for |traits|. It is assumed the lifetime of |validator| is
   // is longer than that of the BrowserTaskExecutor unless RemoveValidator
@@ -151,6 +177,55 @@
   friend class BrowserIOThreadDelegate;
   friend class BrowserTaskExecutorTest;
 
+  // Constructed on UI thread and registered with UI thread TLS. This backs the
+  // implementation of base::CurrentThread for the browser UI thread.
+  class UIThreadExecutor : public BaseBrowserTaskExecutor {
+   public:
+    explicit UIThreadExecutor(
+        std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler);
+
+    ~UIThreadExecutor() override;
+
+    scoped_refptr<BrowserUIThreadScheduler::Handle> GetUIThreadHandle();
+
+    void SetIOThreadHandle(
+        scoped_refptr<BrowserUIThreadScheduler::Handle> io_thread_handle);
+
+    void BindToCurrentThread();
+
+   private:
+    BrowserThread::ID GetCurrentThreadID() const override;
+
+    std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler_;
+    bool bound_to_thread_ = false;
+  };
+
+  // Constructed on UI thread and later registered with IO thread TLS. This
+  // backs the implementation of base::CurrentThread for the browser IO thread.
+  class IOThreadExecutor : public BaseBrowserTaskExecutor {
+   public:
+    explicit IOThreadExecutor(
+        std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate);
+
+    ~IOThreadExecutor() override;
+
+    scoped_refptr<BrowserUIThreadScheduler::Handle> GetIOThreadHandle();
+
+    void SetUIThreadHandle(
+        scoped_refptr<BrowserUIThreadScheduler::Handle> ui_thread_handle);
+
+    std::unique_ptr<BrowserIOThreadDelegate> TakeDelegate() {
+      return std::move(browser_io_thread_delegate_);
+    }
+
+    bool HasDelegateForTesting() const { return !!browser_io_thread_delegate_; }
+
+   private:
+    BrowserThread::ID GetCurrentThreadID() const override;
+
+    std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate_;
+  };
+
   static void CreateInternal(
       std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
       std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate);
@@ -166,22 +241,17 @@
   // For Get();
   FRIEND_TEST_ALL_PREFIXES(BrowserTaskExecutorTest,
                            RegisterExecutorForBothThreads);
-
   explicit BrowserTaskExecutor(
       std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
       std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate);
   ~BrowserTaskExecutor() override;
 
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
-      const base::TaskTraits& traits) const;
+  BrowserThread::ID GetCurrentThreadID() const override;
 
   static BrowserTaskExecutor* Get();
 
-  std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler_;
-  scoped_refptr<BrowserUIThreadScheduler::Handle> browser_ui_thread_handle_;
-
-  std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate_;
-  scoped_refptr<BrowserIOThreadDelegate::Handle> browser_io_thread_handle_;
+  std::unique_ptr<UIThreadExecutor> ui_thread_executor_;
+  std::unique_ptr<IOThreadExecutor> io_thread_executor_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserTaskExecutor);
 };
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc
index 7d3701b..9ef50ad 100644
--- a/content/browser/scheduler/browser_task_executor_unittest.cc
+++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -33,6 +33,7 @@
 using ::testing::ElementsAre;
 using ::testing::Invoke;
 using ::testing::Mock;
+using ::testing::NotNull;
 using ::testing::SizeIs;
 
 using QueueType = BrowserTaskQueues::QueueType;
@@ -49,13 +50,13 @@
 
 TEST_F(BrowserTaskExecutorTest, RegisterExecutorForBothThreads) {
   base::PostTask(FROM_HERE, {BrowserThread::UI}, base::BindOnce([]() {
-                   EXPECT_EQ(BrowserTaskExecutor::Get(),
-                             base::GetTaskExecutorForCurrentThread());
+                   EXPECT_THAT(base::GetTaskExecutorForCurrentThread(),
+                               NotNull());
                  }));
 
   base::PostTask(FROM_HERE, {BrowserThread::IO}, base::BindOnce([]() {
-                   EXPECT_EQ(BrowserTaskExecutor::Get(),
-                             base::GetTaskExecutorForCurrentThread());
+                   EXPECT_THAT(base::GetTaskExecutorForCurrentThread(),
+                               NotNull());
                  }));
 
   BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::UI);
@@ -123,6 +124,16 @@
 // Helper to perform the same tets for all BrowserThread::ID values.
 class BrowserTaskTraitsMappingTest : public BrowserTaskExecutorTest {
  protected:
+  class TestExecutor : public BaseBrowserTaskExecutor {
+   public:
+    ~TestExecutor() override = default;
+
+    BrowserThread::ID GetCurrentThreadID() const override {
+      NOTREACHED();
+      return BrowserThread::UI;
+    }
+  };
+
   template <BrowserThread::ID ID>
   void CheckExpectations() {
     EXPECT_EQ(GetQueueType({ID, TaskPriority::BEST_EFFORT}),
@@ -146,8 +157,10 @@
 
  private:
   QueueType GetQueueType(const base::TaskTraits& traits) {
-    return BrowserTaskExecutor::GetThreadIdAndQueueType(traits).queue_type;
+    return test_executor_.GetThreadIdAndQueueType(traits).queue_type;
   }
+
+  TestExecutor test_executor_;
 };
 
 TEST_F(BrowserTaskTraitsMappingTest, BrowserTaskTraitsMapToProperPriorities) {
@@ -194,9 +207,8 @@
               QueueType::kDefault));
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
-          std::make_unique<BrowserIOThreadDelegate>(
-              BrowserIOThreadDelegate::BrowserTaskExecutorPresent::
-                  kNoForTesting));
+          BrowserIOThreadDelegate::CreateForTesting(sequence_manager()));
+      BrowserTaskExecutor::BindToUIThreadForTesting();
     }
   };
 
@@ -256,4 +268,46 @@
   task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(100));
 }
 
+TEST_F(BrowserTaskExecutorTest, CurrentThread) {
+  base::PostTask(
+      FROM_HERE, {BrowserThread::UI}, base::BindOnce([]() {
+        base::PostTask(
+            FROM_HERE, {base::CurrentThread()}, base::BindOnce([]() {
+              EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
+            }));
+      }));
+  BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::UI);
+
+  base::PostTask(
+      FROM_HERE, {BrowserThread::IO}, base::BindOnce([]() {
+        base::PostTask(
+            FROM_HERE, {base::CurrentThread()}, base::BindOnce([]() {
+              EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+            }));
+      }));
+
+  BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
+}
+
+TEST_F(BrowserTaskExecutorTest, CurrentThreadAndOtherTraits) {
+  EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  auto ui_task_runner = base::CreateSingleThreadTaskRunner({BrowserThread::UI});
+  auto ui_best_effort_runner = base::CreateSingleThreadTaskRunner(
+      {BrowserThread::UI, base::TaskPriority::BEST_EFFORT});
+  auto ui_best_navigation_runner = base::CreateSingleThreadTaskRunner(
+      {BrowserThread::UI, BrowserTaskType::kNavigation});
+
+  EXPECT_EQ(ui_task_runner,
+            base::CreateSingleThreadTaskRunner({base::CurrentThread()}));
+
+  EXPECT_EQ(ui_best_effort_runner,
+            base::CreateSingleThreadTaskRunner(
+                {base::CurrentThread(), base::TaskPriority::BEST_EFFORT}));
+
+  EXPECT_EQ(ui_best_navigation_runner,
+            base::CreateSingleThreadTaskRunner(
+                {base::CurrentThread(), BrowserTaskType::kNavigation}));
+}
+
 }  // namespace content
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
index 1e9a9f9..076fa93 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
@@ -20,6 +20,7 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.task.PostTask;
+import org.chromium.base.task.SingleThreadTaskRunner;
 import org.chromium.base.task.TaskRunner;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
@@ -159,6 +160,103 @@
 
     @Test
     @MediumTest
+    public void testPostTaskCurrentThreadBeforeNativeLoaded() throws Exception {
+        // This should not timeout.
+        final Object lock = new Object();
+        final AtomicBoolean taskExecuted = new AtomicBoolean();
+        PostTask.postTask(UiThreadTaskTraits.DEFAULT, new Runnable() {
+            @Override
+            public void run() {
+                PostTask.postTask(TaskTraits.CURRENT_THREAD, new Runnable() {
+                    @Override
+                    public void run() {
+                        synchronized (lock) {
+                            taskExecuted.set(true);
+                            lock.notify();
+                        }
+                    }
+                });
+            }
+        });
+
+        synchronized (lock) {
+            while (!taskExecuted.get()) {
+                lock.wait();
+            }
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testPostTaskCurrentThreadAfterNativeLoaded() throws Exception {
+        startContentMainOnUiThread();
+
+        // This should not timeout.
+        final Object lock = new Object();
+        final AtomicBoolean taskExecuted = new AtomicBoolean();
+        PostTask.postTask(UiThreadTaskTraits.DEFAULT, new Runnable() {
+            @Override
+            public void run() {
+                PostTask.postTask(TaskTraits.CURRENT_THREAD, new Runnable() {
+                    @Override
+                    public void run() {
+                        synchronized (lock) {
+                            taskExecuted.set(true);
+                            lock.notify();
+                        }
+                    }
+                });
+            }
+        });
+
+        synchronized (lock) {
+            while (!taskExecuted.get()) {
+                lock.wait();
+            }
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testPostTaskCurrentThreadInThreadpoolAfterNativeLoaded() throws Exception {
+        startContentMainOnUiThread();
+        SingleThreadTaskRunner threadpoolTaskRunner =
+                PostTask.createSingleThreadTaskRunner(TaskTraits.THREAD_POOL_USER_BLOCKING);
+        SingleThreadTaskRunner uiThreadTaskRunner =
+                PostTask.createSingleThreadTaskRunner(UiThreadTaskTraits.DEFAULT);
+
+        // This should not timeout.
+        final Object lock = new Object();
+        final AtomicBoolean taskExecuted = new AtomicBoolean();
+        threadpoolTaskRunner.postTask(new Runnable() {
+            @Override
+            public void run() {
+                PostTask.postTask(TaskTraits.CURRENT_THREAD, new Runnable() {
+                    @Override
+                    public void run() {
+                        synchronized (lock) {
+                            Assert.assertTrue(threadpoolTaskRunner.belongsToCurrentThread());
+                            Assert.assertFalse(uiThreadTaskRunner.belongsToCurrentThread());
+                            taskExecuted.set(true);
+                            lock.notify();
+                        }
+                    }
+                });
+            }
+        });
+
+        synchronized (lock) {
+            while (!taskExecuted.get()) {
+                lock.wait();
+            }
+        }
+
+        uiThreadTaskRunner.destroy();
+        threadpoolTaskRunner.destroy();
+    }
+
+    @Test
+    @MediumTest
     public void testTaskNotRunOnUiThreadWithoutUiThreadTaskTraits() {
         TaskRunner uiThreadTaskRunner =
                 PostTask.createSingleThreadTaskRunner(TaskTraits.USER_BLOCKING);
diff --git a/content/public/browser/browser_task_traits.h b/content/public/browser/browser_task_traits.h
index fb283f7..9695496 100644
--- a/content/public/browser/browser_task_traits.h
+++ b/content/public/browser/browser_task_traits.h
@@ -80,11 +80,21 @@
           base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
   constexpr BrowserTaskTraitsExtension(ArgTypes... args)
       : browser_thread_(
-            base::trait_helpers::GetEnum<BrowserThread::ID>(args...)),
+            base::trait_helpers::GetEnum<BrowserThread::ID, BrowserThread::UI>(
+                args...)),
         task_type_(
             base::trait_helpers::GetEnum<BrowserTaskType,
                                          BrowserTaskType::kDefault>(args...)),
-        nestable_(!base::trait_helpers::HasTrait<NonNestable, ArgTypes...>()) {}
+        nestable_(!base::trait_helpers::HasTrait<NonNestable, ArgTypes...>()) {
+    constexpr bool has_current_thread =
+        base::trait_helpers::HasTrait<base::CurrentThread, ArgTypes...>();
+    constexpr bool has_browser_thread =
+        base::trait_helpers::HasTrait<BrowserThread::ID, ArgTypes...>();
+    static_assert(
+        has_current_thread != has_browser_thread,
+        "Either content::BrowserThread::ID or base::CurrentThread must be set, "
+        "but not both");
+  }
 
   // Keep in sync with UiThreadTaskTraits.java
   constexpr base::TaskTraitsExtensionStorage Serialize() const {
@@ -105,7 +115,9 @@
         static_cast<bool>(extension.data[2]));
   }
 
+  // This must be ignored if base::CurrentThread is specified.
   constexpr BrowserThread::ID browser_thread() const { return browser_thread_; }
+
   constexpr BrowserTaskType task_type() const { return task_type_; }
 
   // Returns true if tasks with these traits may run in a nested RunLoop.
diff --git a/content/public/browser/browser_task_traits_unittest.nc b/content/public/browser/browser_task_traits_unittest.nc
index 8738feb..7f2aa05 100644
--- a/content/public/browser/browser_task_traits_unittest.nc
+++ b/content/public/browser/browser_task_traits_unittest.nc
@@ -7,14 +7,17 @@
 
 #include "base/task/task_traits.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 
 namespace content {
 
-#if defined(NCTEST_BROWSER_TASK_TRAITS_NO_THREAD)  // [r"The traits bag is missing a required trait."]
-constexpr base::TaskTraits traits = {NonNestable()};
+#if defined(NCTEST_BROWSER_TASK_TRAITS_NO_THREAD)  // [r"Either content::BrowserThread::ID or base::CurrentThread must be set, but not both"]
+constexpr base::TaskTraits traits = {BrowserTaskType::kNavigation};
 #elif defined(NCTEST_BROWSER_TASK_TRAITS_MULTIPLE_THREADS)  // [r"The traits bag contains multiple traits of the same type."]
 constexpr base::TaskTraits traits = {BrowserThread::UI,
                                      BrowserThread::IO};
+#elif defined(NCTEST_BROWSER_TASK_TRAITS_BROWSER_THREAD_AND_CURRENT_THREAD)  // [r"Either content::BrowserThread::ID or base::CurrentThread must be set, but not both"]
+constexpr base::TaskTraits traits = {BrowserThread::UI, base::CurrentThread()};
 #endif
 
 }  // namespace content
diff --git a/content/public/test/browser_task_environment.cc b/content/public/test/browser_task_environment.cc
index a7aee30..b877abd 100644
--- a/content/public/test/browser_task_environment.cc
+++ b/content/public/test/browser_task_environment.cc
@@ -99,6 +99,7 @@
 
   BrowserTaskExecutor::CreateForTesting(std::move(browser_ui_thread_scheduler),
                                         std::move(browser_io_thread_delegate));
+  BrowserTaskExecutor::BindToUIThreadForTesting();
   DeferredInitFromSubclass(std::move(default_ui_task_runner));
 
   if (HasIOMainLoop()) {
diff --git a/content/public/test/browser_task_environment_unittest.cc b/content/public/test/browser_task_environment_unittest.cc
index ab7adf3..cca1c2c 100644
--- a/content/public/test/browser_task_environment_unittest.cc
+++ b/content/public/test/browser_task_environment_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/message_loop/message_loop_current.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
+#include "base/test/bind_test_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -176,4 +177,50 @@
   EXPECT_THAT(task_environment.GetMockClock(), testing::NotNull());
 }
 
+TEST(BrowserTaskEnvironmentTest, CurrentThread) {
+  BrowserTaskEnvironment task_environment;
+  base::RunLoop run_loop;
+
+  base::PostTask(FROM_HERE, {base::CurrentThread()},
+                 base::BindLambdaForTesting([&]() {
+                   base::PostTask(FROM_HERE, {base::CurrentThread()},
+                                  run_loop.QuitClosure());
+                 }));
+
+  run_loop.Run();
+}
+
+TEST(BrowserTaskEnvironmentTest, CurrentThreadIO) {
+  BrowserTaskEnvironment task_environment;
+  base::RunLoop run_loop;
+
+  auto io_task_runner = base::CreateSingleThreadTaskRunner({BrowserThread::IO});
+
+  base::PostTask(
+      FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+        EXPECT_EQ(io_task_runner,
+                  base::CreateSingleThreadTaskRunner({base::CurrentThread()}));
+        run_loop.Quit();
+      }));
+
+  run_loop.Run();
+}
+
+TEST(BrowserTaskEnvironmentTest, CurrentThreadIOWithRealIOThread) {
+  BrowserTaskEnvironment task_environment(
+      BrowserTaskEnvironment::Options::REAL_IO_THREAD);
+  base::RunLoop run_loop;
+
+  auto io_task_runner = base::CreateSingleThreadTaskRunner({BrowserThread::IO});
+
+  base::PostTask(
+      FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+        EXPECT_EQ(io_task_runner,
+                  base::CreateSingleThreadTaskRunner({base::CurrentThread()}));
+        run_loop.Quit();
+      }));
+
+  run_loop.Run();
+}
+
 }  // namespace content
diff --git a/content/shell/browser/web_test/blink_test_controller.cc b/content/shell/browser/web_test/blink_test_controller.cc
index 9c00f90..befb67f 100644
--- a/content/shell/browser/web_test/blink_test_controller.cc
+++ b/content/shell/browser/web_test/blink_test_controller.cc
@@ -448,18 +448,18 @@
     // Shell::SizeTo is not implemented on all platforms.
     main_window_->SizeTo(initial_size_);
 #endif
-    main_window_->web_contents()
-        ->GetRenderViewHost()
-        ->GetWidget()
-        ->GetView()
-        ->SetSize(initial_size_);
-    // Try to reset the window size. This can fail, see crbug.com/772811
-    main_window_->web_contents()
-        ->GetRenderViewHost()
-        ->GetWidget()
-        ->SynchronizeVisualProperties();
     RenderViewHost* render_view_host =
         main_window_->web_contents()->GetRenderViewHost();
+    RenderWidgetHost* render_widget_host = render_view_host->GetWidget();
+    // Set a different size first to reset the possibly inconsistent state
+    // caused by the previous test using unfortunate synchronous resize mode.
+    // This forces SetSize() not to early return which would otherwise happen
+    // when we set the size to initial_size_ which is the same as its current
+    // size. See http://crbug.com/1011191 for more details.
+    render_widget_host->GetView()->SetSize(
+        gfx::Size(initial_size_.width() / 2, initial_size_.height()));
+    render_widget_host->GetView()->SetSize(initial_size_);
+    render_widget_host->SynchronizeVisualProperties();
 
     if (is_devtools_protocol_test) {
       devtools_protocol_test_bindings_.reset(
@@ -476,13 +476,10 @@
 
     // Focus the RenderWidgetHost. This will send an IPC message to the
     // renderer to propagate the state change.
-    main_window_->web_contents()->GetRenderViewHost()->GetWidget()->Focus();
+    render_widget_host->Focus();
 
     // Flush various interfaces to ensure a test run begins from a known state.
-    main_window_->web_contents()
-        ->GetRenderViewHost()
-        ->GetWidget()
-        ->FlushForTesting();
+    render_widget_host->FlushForTesting();
     GetWebTestControlRemote(render_view_host->GetMainFrame()).FlushForTesting();
 
     if (is_devtools_js_test) {
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 3bd9cb1..326d8a6 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -157,10 +157,6 @@
 crbug.com/angleproject/1465 [ win ] conformance2/glsl3/tricky-loop-conditions.html [ Failure ]
 crbug.com/951628 [ win no-passthrough ] conformance/rendering/blending.html [ Failure ]
 
-# Win / D3D11 backend
-# This test seems to flakily pass sometimes (at least on AMD and Intel).
-crbug.com/angleproject/3388 [ win d3d11 ] conformance2/uniforms/large-uniform-buffers.html [ Failure ]
-
 # Win / NVidia
 crbug.com/631317 [ d3d11 win nvidia ] deqp/functional/gles3/fbomultisample* [ RetryOnFailure ]
 crbug.com/679639 [ d3d11 win nvidia ] conformance2/rendering/draw-with-integer-texture-base-level.html [ Failure ]
diff --git a/docs/clang.md b/docs/clang.md
index d901dad..259560c1 100644
--- a/docs/clang.md
+++ b/docs/clang.md
@@ -74,6 +74,12 @@
 is_component_build = true
 ```
 
+On Windows, for `clang_base_path` use something like this instead:
+
+```
+clang_base_path = "c:/src/llvm-build"
+```
+
 You can then run `head out/gn/toolchain.ninja` and check that the first to
 lines set `cc` and `cxx` to your clang binary. If things look good, run `ninja
 -C out/gn` to build.
diff --git a/ios/build/bots/chromium.fyi/ios12-beta-simulator.json b/ios/build/bots/chromium.fyi/ios12-beta-simulator.json
index d18f358..9ba6335 100644
--- a/ios/build/bots/chromium.fyi/ios12-beta-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios12-beta-simulator.json
@@ -13,5 +13,95 @@
     "use_goma=true"
   ],
   "tests": [
+    {
+      "include": "common_tests.json",
+      "device type": "iPhone X",
+      "os": "13.1",
+      "xcode build version": "11a1027",
+      "host os": "Mac-10.14.6",
+      "pool":"Chrome",
+      "test args": [
+        "--run-with-custom-webkit"
+      ]
+    },
+    {
+      "include": "screen_size_dependent_tests.json",
+      "device type": "iPhone X",
+      "os": "13.1",
+      "xcode build version": "11a1027",
+      "host os": "Mac-10.14.6",
+      "pool":"Chrome",
+      "test args": [
+        "--run-with-custom-webkit"
+      ]
+    },
+    {
+      "include": "eg_tests.json",
+      "device type": "iPhone X",
+      "os": "13.1",
+      "xcode build version": "11a1027",
+      "host os": "Mac-10.14.6",
+      "pool":"Chrome",
+      "test args": [
+        "--run-with-custom-webkit"
+      ]
+
+    },
+    {
+      "include": "eg_cq_tests.json",
+      "device type": "iPhone X",
+      "os": "13.1",
+      "xcode build version": "11a1027",
+      "host os": "Mac-10.14.6",
+      "pool":"Chrome",
+      "test args": [
+        "--run-with-custom-webkit"
+      ]
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPad Air 2",
+      "os": "13.1",
+      "xcode build version": "11a1027",
+      "host os": "Mac-10.14.6",
+      "pool":"Chrome",
+      "test args": [
+        "--run-with-custom-webkit"
+      ]
+    },
+    {
+      "include": "screen_size_dependent_tests.json",
+      "device type": "iPad Air 2",
+      "os": "13.1",
+      "xcode build version": "11a1027",
+      "host os": "Mac-10.14.6",
+      "pool":"Chrome",
+      "test args": [
+        "--run-with-custom-webkit"
+      ]
+    },
+    {
+      "include": "eg_tests.json",
+      "device type": "iPad Air 2",
+      "os": "13.1",
+      "xcode build version": "11a1027",
+      "host os": "Mac-10.14.6",
+      "pool":"Chrome",
+      "test args": [
+        "--run-with-custom-webkit"
+      ]
+
+    },
+    {
+      "include": "eg_cq_tests.json",
+      "device type": "iPad Air 2",
+      "os": "13.1",
+      "xcode build version": "11a1027",
+      "host os": "Mac-10.14.6",
+      "pool":"Chrome",
+      "test args": [
+        "--run-with-custom-webkit"
+      ]
+    }
   ]
 }
diff --git a/ios/chrome/browser/autofill/automation/automation_action.mm b/ios/chrome/browser/autofill/automation/automation_action.mm
index 37937e9..f57c6cd 100644
--- a/ios/chrome/browser/autofill/automation/automation_action.mm
+++ b/ios/chrome/browser/autofill/automation/automation_action.mm
@@ -4,25 +4,18 @@
 
 #import "ios/chrome/browser/autofill/automation/automation_action.h"
 
-#import <EarlGrey/EarlGrey.h>
-
-#include "base/guid.h"
 #include "base/mac/foundation_util.h"
-#include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#include "components/autofill/core/browser/autofill_manager.h"
-#include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/ios/browser/autofill_driver_ios.h"
+#include "base/values.h"
 #import "ios/chrome/browser/autofill/form_suggestion_constants.h"
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
-#import "ios/chrome/test/app/tab_test_util.h"
+#import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/web/public/js_messaging/web_frames_manager.h"
-#import "ios/web/public/test/earl_grey/web_view_actions.h"
-#import "ios/web/public/test/earl_grey/web_view_matchers.h"
-#include "ios/web/public/test/element_selector.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
+#import "ios/web/public/test/element_selector.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -198,27 +191,26 @@
 // A shared flow across many actions, this waits for the target element to be
 // visible, scrolls it into view, then taps on it.
 - (void)tapOnTarget:(ElementSelector*)selector {
-  web::WebState* web_state = chrome_test_util::GetCurrentWebState();
 
   // Wait for the element to be visible on the page.
   [ChromeEarlGrey waitForWebStateContainingElement:selector];
 
   // Potentially scroll into view if below the fold.
-  [[EarlGrey selectElementWithMatcher:web::WebViewInWebState(web_state)]
-      performAction:WebViewScrollElementToVisible(web_state, selector)];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::ScrollElementToVisible(selector)];
 
   // Calling WebViewTapElement right after WebViewScrollElement caused flaky
   // issues with the wrong location being provided for the tap target,
   // seemingly caused by the screen not redrawing in-between these two actions.
-  // We force a brief wait here to avoid this issue.
-  [[GREYCondition conditionWithName:@"forced wait to allow for redraw"
-                              block:^BOOL {
-                                return false;
-                              }] waitWithTimeout:0.1];
-
+  // We force a brief wait here to avoid this issue. |waitWithTimeout| requires
+  // its result to be used. Void the result as it's always false.
+  (void)[[GREYCondition conditionWithName:@"forced wait to allow for redraw"
+                                    block:^BOOL {
+                                      return false;
+                                    }] waitWithTimeout:0.1];
   // Tap on the element.
-  [[EarlGrey selectElementWithMatcher:web::WebViewInWebState(web_state)]
-      performAction:web::WebViewTapElement(web_state, selector)];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElement(selector)];
 }
 
 // Creates a selector targeting the element specified in the action.
diff --git a/ios/chrome/browser/prerender/preload_controller.mm b/ios/chrome/browser/prerender/preload_controller.mm
index 37f3274..9debfbf 100644
--- a/ios/chrome/browser/prerender/preload_controller.mm
+++ b/ios/chrome/browser/prerender/preload_controller.mm
@@ -272,6 +272,7 @@
   return !IsPrerenderTabEvictionExperimentalGroup() && self.preferenceEnabled &&
          !ios::device_util::IsSingleCoreDevice() &&
          ios::device_util::RamIsAtLeast512Mb() &&
+         !net::NetworkChangeNotifier::IsOffline() &&
          (!self.wifiOnly || !self.usingWWAN);
 }
 
diff --git a/ios/chrome/browser/prerender/preload_controller_unittest.mm b/ios/chrome/browser/prerender/preload_controller_unittest.mm
index 78b1584..f36bfc7 100644
--- a/ios/chrome/browser/prerender/preload_controller_unittest.mm
+++ b/ios/chrome/browser/prerender/preload_controller_unittest.mm
@@ -90,6 +90,11 @@
         net::NetworkChangeNotifier::CONNECTION_WIFI);
   }
 
+  void SimulateOffline() {
+    network_change_notifier_->SimulateNetworkConnectionChange(
+        net::NetworkChangeNotifier::CONNECTION_NONE);
+  }
+
   void SimulateCellularConnection() {
     network_change_notifier_->SimulateNetworkConnectionChange(
         net::NetworkChangeNotifier::CONNECTION_3G);
@@ -133,13 +138,16 @@
 
 TEST_F(PreloadControllerTest, TestIsPrerenderingEnabled_preloadAlways) {
   // With the "Preload Webpages" setting set to "Always", prerendering is
-  // enabled regardless of network type.
+  // enabled regardless of network type, unless offline.
   PreloadWebpagesAlways();
 
   SimulateWiFiConnection();
   EXPECT_TRUE(controller_.enabled || ios::device_util::IsSingleCoreDevice() ||
               !ios::device_util::RamIsAtLeast512Mb());
 
+  SimulateOffline();
+  EXPECT_FALSE(controller_.enabled);
+
   SimulateCellularConnection();
   EXPECT_TRUE(controller_.enabled || ios::device_util::IsSingleCoreDevice() ||
               !ios::device_util::RamIsAtLeast512Mb());
@@ -154,6 +162,9 @@
   EXPECT_TRUE(controller_.enabled || ios::device_util::IsSingleCoreDevice() ||
               !ios::device_util::RamIsAtLeast512Mb());
 
+  SimulateOffline();
+  EXPECT_FALSE(controller_.enabled);
+
   SimulateCellularConnection();
   EXPECT_FALSE(controller_.enabled);
 }
@@ -166,6 +177,9 @@
   SimulateWiFiConnection();
   EXPECT_FALSE(controller_.enabled);
 
+  SimulateOffline();
+  EXPECT_FALSE(controller_.enabled);
+
   SimulateCellularConnection();
   EXPECT_FALSE(controller_.enabled);
 }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.mm
index 2b942bf..9498d9c 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.mm
@@ -75,9 +75,8 @@
 }
 
 - (void)stop {
-  [self.passwordViewController.presentingViewController
-      dismissViewControllerAnimated:YES
-                         completion:nil];
+  [self.passwordViewController dismissViewControllerAnimated:YES
+                                                  completion:nil];
   self.passwordViewController = nil;
   self.passwordMediator = nil;
   [super stop];
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
index 1b2ba38..5b737d8 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
@@ -31,7 +31,6 @@
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/web/public/test/earl_grey/web_view_matchers.h"
 #include "ios/web/public/test/element_selector.h"
-#import "ios/web/public/web_client.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
@@ -528,45 +527,6 @@
   }
 }
 
-// Test that after searching in "Use Other Password" and selecting an option,
-// the screen is dismissed.
-- (void)testOtherPasswordsDismissAfterSearch {
-  // This test is failing on a legacy bot. Return early if running there.
-  if (![ChromeEarlGrey isSlimNavigationManagerEnabled]) {
-    EARL_GREY_TEST_SKIPPED(@"Skipped for Slim Navigation off.");
-  }
-
-  // Bring up the keyboard.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
-      performAction:TapWebElementWithId(kFormElementUsername)];
-
-  // Tap the passwords icon.
-  [[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
-      performAction:grey_tap()];
-
-  // Tap "Use Other Password...".
-  [[EarlGrey selectElementWithMatcher:OtherPasswordsMatcher()]
-      performAction:grey_tap()];
-
-  // Verify "Use Other Passwords" opened.
-  [[EarlGrey selectElementWithMatcher:OtherPasswordsDismissMatcher()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  // Tap the password search bar.
-  [[EarlGrey selectElementWithMatcher:PasswordSearchBarMatcher()]
-      performAction:grey_typeText(@"user")];
-
-  // Select a username.
-  [[EarlGrey selectElementWithMatcher:UsernameButtonMatcher()]
-      performAction:grey_tap()];
-
-  // Verify the password list disappears.
-  [[EarlGrey selectElementWithMatcher:PasswordSearchBarMatcher()]
-      assertWithMatcher:grey_nil()];
-  [[EarlGrey selectElementWithMatcher:OtherPasswordsMatcher()]
-      assertWithMatcher:grey_nil()];
-}
-
 // Tests that the Password View Controller is dismissed when tapping the
 // keyboard icon.
 - (void)testKeyboardIconDismissPasswordController {
diff --git a/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm b/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm
index 2dc516a..6c92547 100644
--- a/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm
+++ b/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm
@@ -37,6 +37,7 @@
 }
 
 - (void)stop {
+  [self.mediator disconnect];
   self.mediator = nil;
   [self.viewController.presentingViewController
       dismissViewControllerAnimated:YES
diff --git a/ios/chrome/browser/ui/passwords/password_breach_mediator.h b/ios/chrome/browser/ui/passwords/password_breach_mediator.h
index 4d1a69c..a111a4a 100644
--- a/ios/chrome/browser/ui/passwords/password_breach_mediator.h
+++ b/ios/chrome/browser/ui/passwords/password_breach_mediator.h
@@ -36,6 +36,10 @@
 
 - (instancetype)initWithCoder NS_UNAVAILABLE;
 
+// Informs the mediator that its about to be destroyed, so it can perform any
+// logging or clean up needed.
+- (void)disconnect;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BREACH_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/passwords/password_breach_mediator.mm b/ios/chrome/browser/ui/passwords/password_breach_mediator.mm
index 20ddc19..427fa96 100644
--- a/ios/chrome/browser/ui/passwords/password_breach_mediator.mm
+++ b/ios/chrome/browser/ui/passwords/password_breach_mediator.mm
@@ -6,6 +6,7 @@
 
 #include "base/strings/sys_string_conversions.h"
 #include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/passwords/password_breach_consumer.h"
@@ -19,12 +20,22 @@
 using password_manager::GetAcceptButtonLabel;
 using password_manager::GetCancelButtonLabel;
 using password_manager::GetDescription;
+using password_manager::GetLeakDialogType;
 using password_manager::GetTitle;
-using password_manager::ShouldCheckPasswords;
 using password_manager::GetPasswordCheckupURL;
+using password_manager::ShouldCheckPasswords;
+using password_manager::metrics_util::LeakDialogDismissalReason;
+using password_manager::metrics_util::LeakDialogType;
+using password_manager::metrics_util::LogLeakDialogTypeAndDismissalReason;
 
 @interface PasswordBreachMediator ()
 
+// Leak type of the dialog.
+@property(nonatomic, assign) LeakDialogType leakType;
+
+// Dismiss reason, used for metrics.
+@property(nonatomic, assign) LeakDialogDismissalReason dismissReason;
+
 // The presenter of the feature.
 @property(nonatomic, weak) id<PasswordBreachPresenter> presenter;
 
@@ -44,6 +55,9 @@
   if (self) {
     _presenter = presenter;
     _dispatcher = dispatcher;
+    _leakType = GetLeakDialogType(leakType);
+    _dismissReason = LeakDialogDismissalReason::kNoDirectInteraction;
+
     NSString* subtitle = SysUTF16ToNSString(GetDescription(leakType, URL));
     NSString* primaryActionString =
         SysUTF16ToNSString(GetAcceptButtonLabel(leakType));
@@ -55,13 +69,21 @@
   return self;
 }
 
+- (void)disconnect {
+  LogLeakDialogTypeAndDismissalReason(self.leakType, self.dismissReason);
+}
+
 #pragma mark - PasswordBreachConsumerDelegate
 
 - (void)passwordBreachDone {
+  self.dismissReason = LeakDialogDismissalReason::kClickedOk;
   [self.presenter stop];
 }
 
 - (void)passwordBreachPrimaryAction {
+  // Opening a new tab already stops the presentation in the presenter.
+  // No need to send |stop|.
+  self.dismissReason = LeakDialogDismissalReason::kClickedCheckPasswords;
   OpenNewTabCommand* newTabCommand =
       [OpenNewTabCommand commandWithURLFromChrome:GetPasswordCheckupURL()];
   [self.dispatcher openURLInNewTab:newTabCommand];
diff --git a/ios/third_party/webkit/BUILD.gn b/ios/third_party/webkit/BUILD.gn
index 0b3b89c..1e5e9b9 100644
--- a/ios/third_party/webkit/BUILD.gn
+++ b/ios/third_party/webkit/BUILD.gn
@@ -64,6 +64,14 @@
       "$_framework_out_dir/WebKit.framework/WebKit",
       "$_framework_out_dir/WebKitLegacy.framework",
       "$_framework_out_dir/WebKitLegacy.framework/WebKitLegacy",
+      "$_framework_out_dir/com.apple.WebKit.Networking.xpc",
+      "$_framework_out_dir/com.apple.WebKit.Networking.xpc/com.apple.WebKit.Networking.Development",
+      "$_framework_out_dir/com.apple.WebKit.Plugin.64.xpc",
+      "$_framework_out_dir/com.apple.WebKit.Plugin.64.xpc/com.apple.WebKit.Plugin.64.Development",
+      "$_framework_out_dir/com.apple.WebKit.WebContent.Development.xpc",
+      "$_framework_out_dir/com.apple.WebKit.WebContent.Development.xpc/com.apple.WebKit.WebContent.Development.Development",
+      "$_framework_out_dir/com.apple.WebKit.WebContent.xpc",
+      "$_framework_out_dir/com.apple.WebKit.WebContent.xpc/com.apple.WebKit.WebContent.Development",
     ]
 
     # TODO(crbug.com/934252): "-j 4" restricts xcodebuild to four simultaneous
@@ -91,6 +99,10 @@
       "$_framework_out_dir/WebCore.framework",
       "$_framework_out_dir/WebKit.framework",
       "$_framework_out_dir/WebKitLegacy.framework",
+      "$_framework_out_dir/com.apple.WebKit.Networking.xpc",
+      "$_framework_out_dir/com.apple.WebKit.Plugin.64.xpc",
+      "$_framework_out_dir/com.apple.WebKit.WebContent.Development.xpc",
+      "$_framework_out_dir/com.apple.WebKit.WebContent.xpc",
     ]
     outputs = [
       "{{bundle_contents_dir}}/WebKitFrameworks/{{source_file_part}}",
diff --git a/ios/web/common/features.h b/ios/web/common/features.h
index 533239b..32213d9 100644
--- a/ios/web/common/features.h
+++ b/ios/web/common/features.h
@@ -31,6 +31,11 @@
 // Used to ensure that the render is not suspended.
 extern const base::Feature kKeepsRenderProcessAlive;
 
+// Used to enable the workaround for a WKWebView WKNavigation leak.
+// (crbug.com/1010765).  Clear older pending navigation records when a
+// navigation finishes.
+extern const base::Feature kClearOldNavigationRecordsWorkaround;
+
 // Used to enable committed interstitials for SSL errors.
 extern const base::Feature kSSLCommittedInterstitials;
 
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index 357e0e6..f062aed 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -29,6 +29,9 @@
 const base::Feature kKeepsRenderProcessAlive{"KeepsRenderProcessAlive",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kClearOldNavigationRecordsWorkaround{
+    "ClearOldNavigationRecordsWorkaround", base::FEATURE_ENABLED_BY_DEFAULT};
+
 const base::Feature kSSLCommittedInterstitials{
     "SSLCommittedInterstitials", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ios/web/navigation/crw_web_view_navigation_observer.h b/ios/web/navigation/crw_web_view_navigation_observer.h
index 7ecebee..ebe436e 100644
--- a/ios/web/navigation/crw_web_view_navigation_observer.h
+++ b/ios/web/navigation/crw_web_view_navigation_observer.h
@@ -18,6 +18,9 @@
 // The webView to observe.
 @property(nonatomic, weak) WKWebView* webView;
 
+// Instructs this handler to close.
+- (void)close;
+
 @end
 
 #endif  // IOS_WEB_NAVIGATION_CRW_WEB_VIEW_NAVIGATION_OBSERVER_H_
diff --git a/ios/web/navigation/crw_web_view_navigation_observer.mm b/ios/web/navigation/crw_web_view_navigation_observer.mm
index 81e35541..5e752c4 100644
--- a/ios/web/navigation/crw_web_view_navigation_observer.mm
+++ b/ios/web/navigation/crw_web_view_navigation_observer.mm
@@ -52,10 +52,19 @@
 // The NavigationManagerImpl associated with the web state.
 @property(nonatomic, readonly) NavigationManagerImpl* navigationManagerImpl;
 
+// Set to YES when [self close] is called.
+@property(nonatomic, assign) BOOL beingDestroyed;
+
 @end
 
 @implementation CRWWebViewNavigationObserver
 
+#pragma mark - Public
+
+- (void)close {
+  self.beingDestroyed = YES;
+}
+
 #pragma mark - Property
 
 - (void)setWebView:(WKWebView*)webView {
@@ -103,6 +112,7 @@
                       ofObject:(id)object
                         change:(NSDictionary*)change
                        context:(void*)context {
+  DCHECK(!self.beingDestroyed);
   NSString* dispatcherSelectorName = self.WKWebViewObservers[keyPath];
   DCHECK(dispatcherSelectorName);
   if (dispatcherSelectorName) {
diff --git a/ios/web/navigation/crw_wk_navigation_states.mm b/ios/web/navigation/crw_wk_navigation_states.mm
index 1bb1ec5..af3f0dc 100644
--- a/ios/web/navigation/crw_wk_navigation_states.mm
+++ b/ios/web/navigation/crw_wk_navigation_states.mm
@@ -4,8 +4,12 @@
 
 #import "ios/web/navigation/crw_wk_navigation_states.h"
 
+#include "base/feature_list.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "ios/web/common/features.h"
 #import "ios/web/navigation/navigation_context_impl.h"
+#import "ios/web/public/web_client.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -145,6 +149,33 @@
   if (state == web::WKNavigationState::COMMITTED) {
     record.committed = YES;
   }
+
+  // Workaround for a WKWebView bug where WKNavigation's can leak, leaving a
+  // permanent pending URL, thus breaking the omnibox.  While it is possible
+  // for navigations to finish out-of-order, it's an edge case that should be
+  // handled gracefully, as last committed will appear in the omnibox instead
+  // of the pending URL.  See crbug.com/1010765 for details and a reproducible
+  // example.
+  if (state == web::WKNavigationState::FINISHED &&
+      web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
+      base::FeatureList::IsEnabled(
+          web::features::kClearOldNavigationRecordsWorkaround)) {
+    NSUInteger finishedIndex = record.index;
+    NSMutableSet* navigationsToRemove = [NSMutableSet set];
+    for (id navigation in _records) {
+      CRWWKNavigationsStateRecord* record = [_records objectForKey:navigation];
+      if (record.index < finishedIndex) {
+        [navigationsToRemove addObject:navigation];
+      }
+    }
+    for (id navigation in navigationsToRemove) {
+      [_records removeObjectForKey:navigation];
+    }
+
+    UMA_HISTOGRAM_BOOLEAN("IOS.CRWWKNavigationStatesRemoveOldPending",
+                          navigationsToRemove.count > 0);
+  }
+
   [_records setObject:record forKey:key];
 }
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 34d2ad9..861828d 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -603,6 +603,7 @@
   self.swipeRecognizerProvider = nil;
   [self.legacyNativeController close];
   [self.requestController close];
+  [self.webViewNavigationObserver close];
 
   // Mark the destruction sequence has started, in case someone else holds a
   // strong reference and tries to continue using the tab.
@@ -1792,6 +1793,7 @@
                       ofObject:(id)object
                         change:(NSDictionary*)change
                        context:(void*)context {
+  DCHECK(!self.beingDestroyed);
   NSString* dispatcherSelectorName = self.WKWebViewObservers[keyPath];
   DCHECK(dispatcherSelectorName);
   if (dispatcherSelectorName) {
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 0f74c4f..7694748 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -77,6 +77,46 @@
 using base::test::ios::WaitUntilConditionOrTimeout;
 using base::test::ios::kWaitForPageLoadTimeout;
 
+// Subclass of WKWebView to check that the observers are removed when the web
+// state is destroyed.
+@interface CRWFakeWKWebViewObserverCount : WKWebView
+
+// Array storing the different key paths observed.
+@property(nonatomic, strong) NSMutableArray<NSString*>* keyPaths;
+// Whether there was observers when the WebView was stopped.
+@property(nonatomic, assign) BOOL hadObserversWhenStopping;
+
+@end
+
+@implementation CRWFakeWKWebViewObserverCount
+
+- (void)stopLoading {
+  [super stopLoading];
+  self.hadObserversWhenStopping =
+      self.hadObserversWhenStopping || self.keyPaths.count > 0;
+}
+
+- (void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath {
+  [super removeObserver:observer forKeyPath:keyPath];
+  [self.keyPaths removeObject:keyPath];
+}
+
+- (void)addObserver:(NSObject*)observer
+         forKeyPath:(NSString*)keyPath
+            options:(NSKeyValueObservingOptions)options
+            context:(void*)context {
+  [super addObserver:observer
+          forKeyPath:keyPath
+             options:options
+             context:context];
+  if (!self.keyPaths) {
+    self.keyPaths = [[NSMutableArray alloc] init];
+  }
+  [self.keyPaths addObject:keyPath];
+}
+
+@end
+
 namespace web {
 namespace {
 
@@ -1348,6 +1388,42 @@
 
 INSTANTIATE_TEST_SUITES(CRWWebControllerWebProcessTest);
 
+// Fixture class to test WKWebView crashes.
+class CRWWebControllerWebViewTest
+    : public ProgrammaticWebTestWithWebController {
+ protected:
+  void SetUp() override {
+    ProgrammaticWebTestWithWebController::SetUp();
+    web::TestBrowserState browser_state;
+    web_view_ = [[CRWFakeWKWebViewObserverCount alloc] init];
+    TestWebViewContentView* webViewContentView = [[TestWebViewContentView alloc]
+        initWithMockWebView:web_view_
+                 scrollView:web_view_.scrollView];
+    [web_controller() injectWebViewContentView:webViewContentView];
+  }
+  CRWFakeWKWebViewObserverCount* web_view_;
+};
+
+// Tests that the KVO for the WebView are removed when the WebState is
+// destroyed. See crbug.com/1002786.
+TEST_P(CRWWebControllerWebViewTest, CheckNoKVOWhenWebStateDestroyed) {
+  // Load a first URL.
+  NSURL* URL = [NSURL URLWithString:@"about:blank"];
+  NSURLRequest* request = [NSURLRequest requestWithURL:URL];
+  [web_view_ loadRequest:request];
+  base::test::ios::WaitUntilCondition(^bool() {
+    return !web_view_.loading;
+  });
+
+  // Destroying the WebState should call stop at a point where all observers are
+  // supposed to be removed.
+  DestroyWebState();
+
+  EXPECT_FALSE(web_view_.hadObserversWhenStopping);
+}
+
+INSTANTIATE_TEST_SUITES(CRWWebControllerWebViewTest);
+
 #undef INSTANTIATE_TEST_SUITES
 
 }  // namespace web
diff --git a/ios/web_view/internal/translate/cwv_translation_controller.mm b/ios/web_view/internal/translate/cwv_translation_controller.mm
index 13bee0b..08ef08a 100644
--- a/ios/web_view/internal/translate/cwv_translation_controller.mm
+++ b/ios/web_view/internal/translate/cwv_translation_controller.mm
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/time/time.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #import "ios/web_view/internal/cwv_web_view_configuration_internal.h"
 #import "ios/web_view/internal/translate/cwv_translation_language_internal.h"
@@ -74,6 +75,7 @@
 @implementation CWVTranslationController {
   ios_web_view::WebViewTranslateClient* _translateClient;
   std::unique_ptr<translate::TranslatePrefs> _translatePrefs;
+  base::Time _languagesLastUpdatedTime;
 }
 
 @synthesize delegate = _delegate;
@@ -245,7 +247,10 @@
 #pragma mark - Private Methods
 
 - (NSDictionary<NSString*, CWVTranslationLanguage*>*)supportedLanguagesByCode {
-  if (!_supportedLanguagesByCode) {
+  base::Time languagesLastUpdatedTime =
+      translate::TranslateDownloadManager::GetSupportedLanguagesLastUpdated();
+  if (!_supportedLanguagesByCode ||
+      _languagesLastUpdatedTime < languagesLastUpdatedTime) {
     NSMutableDictionary<NSString*, CWVTranslationLanguage*>*
         supportedLanguagesByCode = [NSMutableDictionary dictionary];
     std::vector<std::string> languageCodes;
@@ -265,7 +270,7 @@
 
       supportedLanguagesByCode[language.languageCode] = language;
     }
-
+    _languagesLastUpdatedTime = languagesLastUpdatedTime;
     _supportedLanguagesByCode = [supportedLanguagesByCode copy];
   }
   return _supportedLanguagesByCode;
diff --git a/media/mojo/clients/mojo_video_decoder.cc b/media/mojo/clients/mojo_video_decoder.cc
index 15c76d5..77b426e 100644
--- a/media/mojo/clients/mojo_video_decoder.cc
+++ b/media/mojo/clients/mojo_video_decoder.cc
@@ -352,15 +352,17 @@
   mojom::MediaLogAssociatedPtrInfo media_log_ptr_info;
   media_log_binding_.Bind(mojo::MakeRequest(&media_log_ptr_info));
 
-  // Create |video_frame_handle_releaser| interface request, and bind
+  // Create |video_frame_handle_releaser| interface receiver, and bind
   // |mojo_video_frame_handle_releaser_| to it.
-  mojom::VideoFrameHandleReleaserRequest video_frame_handle_releaser_request;
-  mojom::VideoFrameHandleReleaserPtrInfo video_frame_handle_releaser_ptr_info;
-  video_frame_handle_releaser_request =
-      mojo::MakeRequest(&video_frame_handle_releaser_ptr_info);
+  mojo::PendingRemote<mojom::VideoFrameHandleReleaser>
+      video_frame_handle_releaser_pending_remote;
+  mojo::PendingReceiver<mojom::VideoFrameHandleReleaser>
+      video_frame_handle_releaser_receiver =
+          video_frame_handle_releaser_pending_remote
+              .InitWithNewPipeAndPassReceiver();
   mojo_video_frame_handle_releaser_ =
       base::MakeRefCounted<MojoVideoFrameHandleReleaser>(
-          std::move(video_frame_handle_releaser_ptr_info), task_runner_);
+          std::move(video_frame_handle_releaser_pending_remote), task_runner_);
 
   mojo::ScopedDataPipeConsumerHandle remote_consumer_handle;
   mojo_decoder_buffer_writer_ = MojoDecoderBufferWriter::Create(
@@ -379,7 +381,7 @@
 
   remote_decoder_->Construct(
       std::move(client_ptr_info), std::move(media_log_ptr_info),
-      std::move(video_frame_handle_releaser_request),
+      std::move(video_frame_handle_releaser_receiver),
       std::move(remote_consumer_handle), std::move(command_buffer_id),
       video_decoder_implementation_, target_color_space_);
 }
diff --git a/media/mojo/mojom/video_decoder.mojom b/media/mojo/mojom/video_decoder.mojom
index 12b0e59..2fdaddd 100644
--- a/media/mojo/mojom/video_decoder.mojom
+++ b/media/mojo/mojom/video_decoder.mojom
@@ -99,13 +99,14 @@
   //
   // TODO(sandersd): Rename to Initialize() if/when
   // media::VideoDecoder::Initialize() is renamed to Configure().
-  Construct(associated VideoDecoderClient client,
-            associated MediaLog media_log,
-            VideoFrameHandleReleaser& video_frame_handle_releaser,
-            handle<data_pipe_consumer> decoder_buffer_pipe,
-            CommandBufferId? command_buffer_id,
-            VideoDecoderImplementation implementation,
-            gfx.mojom.ColorSpace target_color_space);
+  Construct(
+      pending_associated_remote<VideoDecoderClient> client,
+      pending_associated_remote<MediaLog> media_log,
+      pending_receiver<VideoFrameHandleReleaser> video_frame_handle_releaser,
+      handle<data_pipe_consumer> decoder_buffer_pipe,
+      CommandBufferId? command_buffer_id,
+      VideoDecoderImplementation implementation,
+      gfx.mojom.ColorSpace target_color_space);
 
   // Configure (or reconfigure) the decoder. This must be called before decoding
   // any frames, and must not be called while there are pending Initialize(),
diff --git a/media/mojo/services/mojo_media_log.cc b/media/mojo/services/mojo_media_log.cc
index 732ffa6..1eefbe3 100644
--- a/media/mojo/services/mojo_media_log.cc
+++ b/media/mojo/services/mojo_media_log.cc
@@ -10,8 +10,9 @@
 
 namespace media {
 
-MojoMediaLog::MojoMediaLog(mojom::MediaLogAssociatedPtrInfo remote_media_log,
-                           scoped_refptr<base::SequencedTaskRunner> task_runner)
+MojoMediaLog::MojoMediaLog(
+    mojo::PendingAssociatedRemote<mojom::MediaLog> remote_media_log,
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
     : remote_media_log_(std::move(remote_media_log)),
       task_runner_(std::move(task_runner)) {
   weak_this_ = weak_ptr_factory_.GetWeakPtr();
diff --git a/media/mojo/services/mojo_media_log.h b/media/mojo/services/mojo_media_log.h
index 15835c3..da879c0 100644
--- a/media/mojo/services/mojo_media_log.h
+++ b/media/mojo/services/mojo_media_log.h
@@ -13,6 +13,8 @@
 #include "base/sequenced_task_runner.h"
 #include "media/base/media_log.h"
 #include "media/mojo/mojom/media_log.mojom.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 
 namespace media {
 
@@ -20,8 +22,9 @@
 class MojoMediaLog final : public MediaLog {
  public:
   // TODO(sandersd): Template on Ptr type to support non-associated.
-  explicit MojoMediaLog(mojom::MediaLogAssociatedPtrInfo remote_media_log,
-                        scoped_refptr<base::SequencedTaskRunner> task_runner);
+  explicit MojoMediaLog(
+      mojo::PendingAssociatedRemote<mojom::MediaLog> remote_media_log,
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
   ~MojoMediaLog() final;
 
  protected:
@@ -30,7 +33,7 @@
   void AddEventLocked(std::unique_ptr<MediaLogEvent> event) override;
 
  private:
-  mojom::MediaLogAssociatedPtr remote_media_log_;
+  mojo::AssociatedRemote<mojom::MediaLog> remote_media_log_;
 
   // The mojo service thread on which we'll access |remote_media_log_|.
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/media/mojo/services/mojo_video_decoder_service.cc b/media/mojo/services/mojo_video_decoder_service.cc
index 6602500..ffa83a6 100644
--- a/media/mojo/services/mojo_video_decoder_service.cc
+++ b/media/mojo/services/mojo_video_decoder_service.cc
@@ -118,9 +118,10 @@
 }
 
 void MojoVideoDecoderService::Construct(
-    mojom::VideoDecoderClientAssociatedPtrInfo client,
-    mojom::MediaLogAssociatedPtrInfo media_log,
-    mojom::VideoFrameHandleReleaserRequest video_frame_handle_releaser,
+    mojo::PendingAssociatedRemote<mojom::VideoDecoderClient> client,
+    mojo::PendingAssociatedRemote<mojom::MediaLog> media_log,
+    mojo::PendingReceiver<mojom::VideoFrameHandleReleaser>
+        video_frame_handle_releaser_receiver,
     mojo::ScopedDataPipeConsumerHandle decoder_buffer_pipe,
     mojom::CommandBufferIdPtr command_buffer_id,
     VideoDecoderImplementation implementation,
@@ -141,9 +142,9 @@
   media_log_ =
       std::make_unique<MojoMediaLog>(std::move(media_log), task_runner);
 
-  video_frame_handle_releaser_ =
-      mojo::MakeStrongBinding(std::make_unique<VideoFrameHandleReleaserImpl>(),
-                              std::move(video_frame_handle_releaser));
+  video_frame_handle_releaser_ = mojo::MakeSelfOwnedReceiver(
+      std::make_unique<VideoFrameHandleReleaserImpl>(),
+      std::move(video_frame_handle_releaser_receiver));
 
   mojo_decoder_buffer_reader_.reset(
       new MojoDecoderBufferReader(std::move(decoder_buffer_pipe)));
diff --git a/media/mojo/services/mojo_video_decoder_service.h b/media/mojo/services/mojo_video_decoder_service.h
index 1c1f26c..95e1034 100644
--- a/media/mojo/services/mojo_video_decoder_service.h
+++ b/media/mojo/services/mojo_video_decoder_service.h
@@ -18,6 +18,9 @@
 #include "media/mojo/mojom/video_decoder.mojom.h"
 #include "media/mojo/services/media_mojo_export.h"
 #include "media/mojo/services/mojo_media_client.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace media {
@@ -43,9 +46,10 @@
   // mojom::VideoDecoder implementation
   void GetSupportedConfigs(GetSupportedConfigsCallback callback) final;
   void Construct(
-      mojom::VideoDecoderClientAssociatedPtrInfo client,
-      mojom::MediaLogAssociatedPtrInfo media_log,
-      mojom::VideoFrameHandleReleaserRequest video_frame_handle_releaser,
+      mojo::PendingAssociatedRemote<mojom::VideoDecoderClient> client,
+      mojo::PendingAssociatedRemote<mojom::MediaLog> media_log,
+      mojo::PendingReceiver<mojom::VideoFrameHandleReleaser>
+          video_frame_handle_receiver,
       mojo::ScopedDataPipeConsumerHandle decoder_buffer_pipe,
       mojom::CommandBufferIdPtr command_buffer_id,
       VideoDecoderImplementation implementation,
@@ -93,14 +97,14 @@
   MojoCdmServiceContext* const mojo_cdm_service_context_ = nullptr;
 
   // Channel for sending async messages to the client.
-  mojom::VideoDecoderClientAssociatedPtr client_;
+  mojo::AssociatedRemote<mojom::VideoDecoderClient> client_;
 
   // Proxy object for providing media log services.
   std::unique_ptr<MojoMediaLog> media_log_;
 
   // Holds VideoFrame references on behalf of the client, until the client
   // releases them or is disconnected.
-  mojo::StrongBindingPtr<mojom::VideoFrameHandleReleaser>
+  mojo::SelfOwnedReceiverRef<mojom::VideoFrameHandleReleaser>
       video_frame_handle_releaser_;
 
   // Helper for reading DecoderBuffer data from the DataPipe.
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn
index 3f94385..b79ba2c 100644
--- a/mojo/public/js/BUILD.gn
+++ b/mojo/public/js/BUILD.gn
@@ -38,7 +38,7 @@
 
   # TODO(yzshen): Eventually we would like to use Closure Compiler to minify the
   # bindings instead of simply concatenating the files.
-  script = "//v8/tools/concatenate-files.py"
+  script = "//mojo/public/tools/bindings/concatenate-files.py"
 
   sources = bindings_js_files
   outputs = [
diff --git a/mojo/public/tools/bindings/concatenate-files.py b/mojo/public/tools/bindings/concatenate-files.py
new file mode 100755
index 0000000..48bc66f
--- /dev/null
+++ b/mojo/public/tools/bindings/concatenate-files.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This utility concatenates several files into one. On Unix-like systems
+# it is equivalent to:
+#   cat file1 file2 file3 ...files... > target
+#
+# The reason for writing a separate utility is that 'cat' is not available
+# on all supported build platforms, but Python is, and hence this provides
+# us with an easy and uniform way of doing this on all platforms.
+
+# for py2/py3 compatibility
+from __future__ import print_function
+
+import optparse
+
+
+def Concatenate(filenames):
+  """Concatenate files.
+
+  Args:
+    files: Array of file names.
+           The last name is the target; all earlier ones are sources.
+
+  Returns:
+    True, if the operation was successful.
+  """
+  if len(filenames) < 2:
+    print("An error occurred generating %s:\nNothing to do." % filenames[-1])
+    return False
+
+  try:
+    with open(filenames[-1], "wb") as target:
+      for filename in filenames[:-1]:
+        with open(filename, "rb") as current:
+          target.write(current.read())
+    return True
+  except IOError as e:
+    print("An error occurred when writing %s:\n%s" % (filenames[-1], e))
+    return False
+
+
+def main():
+  parser = optparse.OptionParser()
+  parser.set_usage("""Concatenate several files into one.
+      Equivalent to: cat file1 ... > target.""")
+  (_options, args) = parser.parse_args()
+  exit(0 if Concatenate(args) else 1)
+
+
+if __name__ == "__main__":
+  main()
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 8c6bd3c..66f481c 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -554,6 +554,8 @@
       "cert/cert_verify_proc_nss.h",
       "cert/cert_verify_proc_win.cc",
       "cert/cert_verify_proc_win.h",
+      "cert/coalescing_cert_verifier.cc",
+      "cert/coalescing_cert_verifier.h",
       "cert/ct_log_response_parser.cc",
       "cert/ct_log_response_parser.h",
       "cert/ct_log_verifier.cc",
@@ -5113,6 +5115,7 @@
     "cert/cert_verify_proc_ios_unittest.cc",
     "cert/cert_verify_proc_mac_unittest.cc",
     "cert/cert_verify_proc_unittest.cc",
+    "cert/coalescing_cert_verifier_unittest.cc",
     "cert/crl_set_unittest.cc",
     "cert/ct_log_response_parser_unittest.cc",
     "cert/ct_log_verifier_unittest.cc",
diff --git a/net/cert/cert_verifier.cc b/net/cert/cert_verifier.cc
index fbf17c71..ec0ee94 100644
--- a/net/cert/cert_verifier.cc
+++ b/net/cert/cert_verifier.cc
@@ -18,6 +18,7 @@
 #include "base/logging.h"
 #else
 #include "net/cert/caching_cert_verifier.h"
+#include "net/cert/coalescing_cert_verifier.h"
 #include "net/cert/multi_threaded_cert_verifier.h"
 #endif
 
@@ -101,7 +102,8 @@
 #endif
 
   return std::make_unique<CachingCertVerifier>(
-      std::make_unique<MultiThreadedCertVerifier>(std::move(verify_proc)));
+      std::make_unique<CoalescingCertVerifier>(
+          std::make_unique<MultiThreadedCertVerifier>(std::move(verify_proc))));
 #endif
 }
 
diff --git a/net/cert/coalescing_cert_verifier.cc b/net/cert/coalescing_cert_verifier.cc
new file mode 100644
index 0000000..0609990
--- /dev/null
+++ b/net/cert/coalescing_cert_verifier.cc
@@ -0,0 +1,463 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/coalescing_cert_verifier.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/containers/linked_list.h"
+#include "base/containers/unique_ptr_adapters.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/time.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate_net_log_param.h"
+#include "net/log/net_log_event_type.h"
+#include "net/log/net_log_source.h"
+#include "net/log/net_log_source_type.h"
+#include "net/log/net_log_with_source.h"
+
+namespace net {
+
+// DESIGN OVERVIEW:
+//
+// The CoalescingCertVerifier implements an algorithm to group multiple calls
+// to Verify() into a single Job. This avoids overloading the underlying
+// CertVerifier, particularly those that are expensive to talk to (e.g.
+// talking to the system verifier or across processes), batching multiple
+// requests to CoaleacingCertVerifier::Verify() into a single underlying call.
+//
+// However, this makes lifetime management a bit more complex.
+//   - The Job object represents all of the state for a single verification to
+//     the CoalescingCertVerifier's underlying CertVerifier.
+//       * It keeps the CertVerifyResult alive, which is required as long as
+//         there is a pending verification.
+//       * It keeps the CertVerify::Request to the underlying verifier alive,
+//         as long as there is a pending Request attached to the Job.
+//       * It keeps track of every CoalescingCertVerifier::Request that is
+//         interested in receiving notification. However, it does NOT own
+//         these objects, and thus needs to coordinate with the Request (via
+//         AddRequest/AbortRequest) to make sure it never has a stale
+//         pointer.
+//         NB: It would have also been possible for the Job to only
+//         hold WeakPtr<Request>s, rather than Request*, but that seemed less
+//         clear as to the lifetime invariants, even if it was more clear
+//         about how the pointers are used.
+//  - The Job object is always owned by the CoalescingCertVerifier. If the
+//    CoalescingCertVerifier is deleted, all in-flight requests to the
+//    underlying verifier should be cancelled. When the Job goes away, all the
+//    Requests will be orphaned.
+//  - The Request object is always owned by the CALLER. It is a handle to
+//    allow a caller to cancel a request, per the CertVerifier interface. If
+//    the Request goes away, no caller callbacks should be invoked if the Job
+//    it was (previously) attached to completes.
+//  - Per the CertVerifier interface, when the CoalescingCertVerifier is
+//    deleted, then regardless of there being any live Requests, none of those
+//    caller callbacks should be invoked.
+//
+// Finally, to add to the complexity, it's possible that, during the handling
+// of a result from the underlying CertVerifier, a Job may begin dispatching
+// to its Requests. The Request may delete the CoalescingCertVerifier. If that
+// happens, then the Job being processed is also deleted, and none of the
+// other Requests should be notified.
+
+namespace {
+
+base::Value CertVerifyResultParams(const CertVerifyResult& verify_result) {
+  base::DictionaryValue results;
+  results.SetBoolean("has_md5", verify_result.has_md5);
+  results.SetBoolean("has_md2", verify_result.has_md2);
+  results.SetBoolean("has_md4", verify_result.has_md4);
+  results.SetBoolean("is_issued_by_known_root",
+                     verify_result.is_issued_by_known_root);
+  results.SetBoolean("is_issued_by_additional_trust_anchor",
+                     verify_result.is_issued_by_additional_trust_anchor);
+  results.SetInteger("cert_status", verify_result.cert_status);
+  results.SetKey("verified_cert", NetLogX509CertificateParams(
+                                      verify_result.verified_cert.get()));
+
+  std::unique_ptr<base::ListValue> hashes(new base::ListValue());
+  for (auto it = verify_result.public_key_hashes.begin();
+       it != verify_result.public_key_hashes.end(); ++it) {
+    hashes->AppendString(it->ToString());
+  }
+  results.Set("public_key_hashes", std::move(hashes));
+
+  return std::move(results);
+}
+
+}  // namespace
+
+// Job contains all the state for a single verification using the underlying
+// verifier.
+class CoalescingCertVerifier::Job {
+ public:
+  Job(CoalescingCertVerifier* parent,
+      const CertVerifier::RequestParams& params,
+      NetLog* net_log,
+      bool is_first_job);
+  ~Job();
+
+  const CertVerifier::RequestParams& params() const { return params_; }
+  const CertVerifyResult& verify_result() const { return verify_result_; }
+
+  // Attaches |request|, causing it to be notified once this Job completes.
+  void AddRequest(CoalescingCertVerifier::Request* request);
+
+  // Stops |request| from being notified. If there are no Requests remaining,
+  // the Job will be cancelled.
+  // NOTE: It's only necessary to call this if the Job has not yet completed.
+  // If the Request has been notified of completion, this should not be called.
+  void AbortRequest(CoalescingCertVerifier::Request* request);
+
+  // Starts a verification using |underlying_verifier|. If this completes
+  // synchronously, returns the result code, with the associated result being
+  // available via |verify_result()|. Otherwise, it will complete
+  // asynchronously, notifying any Requests associated via |AttachRequest|.
+  int Start(CertVerifier* underlying_verifier);
+
+ private:
+  void OnVerifyComplete(int result);
+
+  void LogMetrics();
+
+  CoalescingCertVerifier* parent_verifier_;
+  const CertVerifier::RequestParams params_;
+  const NetLogWithSource net_log_;
+  bool is_first_job_ = false;
+  CertVerifyResult verify_result_;
+
+  base::TimeTicks start_time_;
+  std::unique_ptr<CertVerifier::Request> pending_request_;
+
+  base::LinkedList<CoalescingCertVerifier::Request> attached_requests_;
+  base::WeakPtrFactory<Job> weak_ptr_factory_{this};
+};
+
+// Tracks the state associated with a single CoalescingCertVerifier::Verify
+// request.
+//
+// There are two ways for requests to be cancelled:
+//   - The caller of Verify() can delete the Request object, indicating
+//     they are no longer interested in this particular request.
+//   - The caller can delete the CoalescingCertVerifier, which should cause
+//     all in-process Jobs to be aborted and deleted. Any Requests attached to
+//     Jobs should be orphaned, and do nothing when the Request is (eventually)
+//     deleted.
+class CoalescingCertVerifier::Request
+    : public base::LinkNode<CoalescingCertVerifier::Request>,
+      public CertVerifier::Request {
+ public:
+  // Create a request that will be attached to |job|, and will notify
+  // |callback| and fill |verify_result| if the Job completes successfully.
+  // If the Request is deleted, or the Job is deleted, |callback| will not
+  // be notified.
+  Request(CoalescingCertVerifier::Job* job,
+          CertVerifyResult* verify_result,
+          CompletionOnceCallback callback,
+          const NetLogWithSource& net_log);
+
+  ~Request() override;
+
+  const NetLogWithSource& net_log() const { return net_log_; }
+
+  // Called by Job to complete the requests (either successfully or as a sign
+  // that the underlying Job is going away).
+  void Complete(int result);
+
+  // Called when |job_| is being deleted, to ensure that the Request does not
+  // attempt to access the Job further. No callbacks will be invoked,
+  // consistent with the CoalescingCertVerifier's contract.
+  void OnJobAbort();
+
+ private:
+  CoalescingCertVerifier::Job* job_;
+
+  CertVerifyResult* verify_result_;
+  CompletionOnceCallback callback_;
+  const NetLogWithSource net_log_;
+};
+
+CoalescingCertVerifier::Job::Job(CoalescingCertVerifier* parent,
+                                 const CertVerifier::RequestParams& params,
+                                 NetLog* net_log,
+                                 bool is_first_job)
+    : parent_verifier_(parent),
+      params_(params),
+      net_log_(
+          NetLogWithSource::Make(net_log, NetLogSourceType::CERT_VERIFIER_JOB)),
+      is_first_job_(is_first_job) {}
+
+CoalescingCertVerifier::Job::~Job() {
+  // If there was at least one outstanding Request still pending, then this
+  // Job was aborted, rather than being completed normally and cleaned up.
+  if (!attached_requests_.empty() && pending_request_) {
+    net_log_.AddEvent(NetLogEventType::CANCELLED);
+    net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB);
+  }
+
+  while (!attached_requests_.empty()) {
+    auto* link_node = attached_requests_.head();
+    link_node->RemoveFromList();
+    link_node->value()->OnJobAbort();
+  }
+}
+
+void CoalescingCertVerifier::Job::AddRequest(
+    CoalescingCertVerifier::Request* request) {
+  // There must be a pending asynchronous verification in process.
+  DCHECK(pending_request_);
+
+  request->net_log().AddEventReferencingSource(
+      NetLogEventType::CERT_VERIFIER_REQUEST_BOUND_TO_JOB, net_log_.source());
+  attached_requests_.Append(request);
+}
+
+void CoalescingCertVerifier::Job::AbortRequest(
+    CoalescingCertVerifier::Request* request) {
+  // Check to make sure |request| hasn't already been removed.
+  DCHECK(request->previous() || request->next());
+
+  request->RemoveFromList();
+
+  // If there are no more pending requests, abort. This isn't strictly
+  // necessary; the request could be allowed to run to completion (and
+  // potentially to allow later Requests to join in), but in keeping with the
+  // idea of providing more stable guarantees about resources, clean up early.
+  if (attached_requests_.empty()) {
+    // If this was the last Request, then the Job had not yet completed; this
+    // matches the logic in the dtor, which handles when it's the Job that is
+    // deleted first, rather than the last Request.
+    net_log_.AddEvent(NetLogEventType::CANCELLED);
+    net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB);
+
+    // DANGER: This will cause |this_| to be deleted!
+    parent_verifier_->RemoveJob(this);
+    return;
+  }
+}
+
+int CoalescingCertVerifier::Job::Start(CertVerifier* underlying_verifier) {
+  // Requests are only attached for asynchronous completion, so they must
+  // always be attached after Start() has been called.
+  DCHECK(attached_requests_.empty());
+  // There should not be a pending request already started (e.g. Start called
+  // multiple times).
+  DCHECK(!pending_request_);
+
+  net_log_.BeginEvent(NetLogEventType::CERT_VERIFIER_JOB, [&] {
+    return NetLogX509CertificateParams(params_.certificate().get());
+  });
+
+  verify_result_.Reset();
+
+  start_time_ = base::TimeTicks::Now();
+  int result = underlying_verifier->Verify(
+      params_, &verify_result_,
+      // Safe, because |verify_request_| is self-owned and guarantees the
+      // callback won't be called if |this| is deleted.
+      base::Bind(&CoalescingCertVerifier::Job::OnVerifyComplete,
+                 base::Unretained(this)),
+      &pending_request_, net_log_);
+  if (result != ERR_IO_PENDING) {
+    LogMetrics();
+    net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB,
+                      [&] { return CertVerifyResultParams(verify_result_); });
+  }
+
+  return result;
+}
+
+void CoalescingCertVerifier::Job::OnVerifyComplete(int result) {
+  LogMetrics();
+
+  pending_request_.reset();  // Reset to signal clean completion.
+  net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB,
+                    [&] { return CertVerifyResultParams(verify_result_); });
+
+  // It's possible that during the process of invoking a callback for a
+  // Request, |this| may get deleted (along with the associated parent). If
+  // that happens, it's important to ensure that processing of the Job is
+  // stopped - i.e. no other callbacks are invoked for other Requests, nor is
+  // |this| accessed.
+  //
+  // To help detect and protect against this, a WeakPtr to |this| is taken. If
+  // |this| is deleted, the destructor will have invalidated the WeakPtr.
+  //
+  // Note that if a Job had already been deleted, this method would not have
+  // been invoked in the first place, as the Job (via |pending_request_|) owns
+  // the underlying CertVerifier::Request that this method was bound to as a
+  // callback. This is why it's OK to grab the WeakPtr from |this| initially.
+  base::WeakPtr<Job> weak_this = weak_ptr_factory_.GetWeakPtr();
+  while (!attached_requests_.empty()) {
+    // Note: It's also possible for additional Requests to be attached to the
+    // current Job while processing a Request.
+    auto* link_node = attached_requests_.head();
+    link_node->RemoveFromList();
+
+    // Note: |this| MAY be deleted here.
+    //   - If the CoalescingCertVerifier is deleted, it will delete the
+    //     Jobs (including |this|)
+    //   - If this is the second-to-last Request, and the completion of this
+    //     event causes the other Request to be deleted, detaching that Request
+    //     from this Job will lead to this Job being deleted (via
+    //     Job::AbortRequest())
+    link_node->value()->Complete(result);
+
+    // Check if |this| has been deleted (which implicitly includes
+    // |parent_verifier_|), and abort if so, since no further cleanup is
+    // needed.
+    if (!weak_this)
+      return;
+  }
+
+  // DANGER: |this| will be invalidated (deleted) after this point.
+  return parent_verifier_->RemoveJob(this);
+}
+
+void CoalescingCertVerifier::Job::LogMetrics() {
+  base::TimeDelta latency = base::TimeTicks::Now() - start_time_;
+  UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency", latency,
+                             base::TimeDelta::FromMilliseconds(1),
+                             base::TimeDelta::FromMinutes(10), 100);
+  if (is_first_job_) {
+    UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_First_Job_Latency", latency,
+                               base::TimeDelta::FromMilliseconds(1),
+                               base::TimeDelta::FromMinutes(10), 100);
+  }
+}
+
+CoalescingCertVerifier::Request::Request(CoalescingCertVerifier::Job* job,
+                                         CertVerifyResult* verify_result,
+                                         CompletionOnceCallback callback,
+                                         const NetLogWithSource& net_log)
+    : job_(job),
+      verify_result_(verify_result),
+      callback_(std::move(callback)),
+      net_log_(net_log) {
+  net_log_.BeginEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
+}
+
+CoalescingCertVerifier::Request::~Request() {
+  if (job_) {
+    net_log_.AddEvent(NetLogEventType::CANCELLED);
+    net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
+
+    // If the Request is deleted before the Job, then detach from the Job.
+    // Note: This may cause |job_| to be deleted.
+    job_->AbortRequest(this);
+    job_ = nullptr;
+  }
+}
+
+void CoalescingCertVerifier::Request::Complete(int result) {
+  *verify_result_ = job_->verify_result();
+
+  // On successful completion, the Job removes the Request from its set;
+  // similarly, break the association here so that when the Request is
+  // deleted, it does not try to abort the (now-completed) Job.
+  job_ = nullptr;
+
+  net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
+
+  // Run |callback_|, which may delete |this|.
+  std::move(callback_).Run(result);
+}
+
+void CoalescingCertVerifier::Request::OnJobAbort() {
+  // If the Job is deleted before the Request, just clean up. The Request will
+  // eventually be deleted by the caller.
+  net_log_.AddEvent(NetLogEventType::CANCELLED);
+  net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
+
+  job_ = nullptr;
+}
+
+CoalescingCertVerifier::CoalescingCertVerifier(
+    std::unique_ptr<CertVerifier> verifier)
+    : verifier_(std::move(verifier)),
+      config_id_(0),
+      requests_(0),
+      inflight_joins_(0) {}
+
+CoalescingCertVerifier::~CoalescingCertVerifier() = default;
+
+int CoalescingCertVerifier::Verify(
+    const RequestParams& params,
+    CertVerifyResult* verify_result,
+    CompletionOnceCallback callback,
+    std::unique_ptr<CertVerifier::Request>* out_req,
+    const NetLogWithSource& net_log) {
+  DCHECK(verify_result);
+  DCHECK(!callback.is_null());
+
+  out_req->reset();
+  ++requests_;
+
+  Job* job = FindJob(params);
+  if (job) {
+    // An identical request is in-flight and joinable, so just attach the
+    // callback.
+    ++inflight_joins_;
+  } else {
+    // No existing Jobs can be used. Create and start a new one.
+    std::unique_ptr<Job> new_job =
+        std::make_unique<Job>(this, params, net_log.net_log(), requests_ == 1);
+    int result = new_job->Start(verifier_.get());
+    if (result != ERR_IO_PENDING) {
+      *verify_result = new_job->verify_result();
+      return result;
+    }
+
+    job = new_job.get();
+    joinable_jobs_[params] = std::move(new_job);
+  }
+
+  std::unique_ptr<CoalescingCertVerifier::Request> request =
+      std::make_unique<CoalescingCertVerifier::Request>(
+          job, verify_result, std::move(callback), net_log);
+  job->AddRequest(request.get());
+  *out_req = std::move(request);
+  return ERR_IO_PENDING;
+}
+
+void CoalescingCertVerifier::SetConfig(const CertVerifier::Config& config) {
+  ++config_id_;
+  verifier_->SetConfig(config);
+
+  for (auto& job : joinable_jobs_) {
+    inflight_jobs_.emplace_back(std::move(job.second));
+  }
+  joinable_jobs_.clear();
+}
+
+CoalescingCertVerifier::Job* CoalescingCertVerifier::FindJob(
+    const RequestParams& params) {
+  auto it = joinable_jobs_.find(params);
+  if (it != joinable_jobs_.end())
+    return it->second.get();
+  return nullptr;
+}
+
+void CoalescingCertVerifier::RemoveJob(Job* job) {
+  // See if this was a job from the current configuration generation.
+  // Note: It's also necessary to compare that the underlying pointer is the
+  // same, and not merely a Job with the same parameters.
+  auto joinable_it = joinable_jobs_.find(job->params());
+  if (joinable_it != joinable_jobs_.end() && joinable_it->second.get() == job) {
+    joinable_jobs_.erase(joinable_it);
+    return;
+  }
+
+  // Otherwise, it MUST have been a job from a previous generation.
+  auto inflight_it = std::find_if(inflight_jobs_.begin(), inflight_jobs_.end(),
+                                  base::MatchesUniquePtr(job));
+  DCHECK(inflight_it != inflight_jobs_.end());
+  inflight_jobs_.erase(inflight_it);
+  return;
+}
+
+}  // namespace net
diff --git a/net/cert/coalescing_cert_verifier.h b/net/cert/coalescing_cert_verifier.h
new file mode 100644
index 0000000..60b4682
--- /dev/null
+++ b/net/cert/coalescing_cert_verifier.h
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_COALESCING_CERT_VERIFIER_H_
+#define NET_CERT_COALESCING_CERT_VERIFIER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/cert/cert_verifier.h"
+
+namespace net {
+
+// CoalescingCertVerifier is a CertVerifier that keeps track of in-flight
+// CertVerifier Verify() requests. If a new call to Verify() is started that
+// matches the same parameters as an in-progress verification, the new
+// Verify() call will be joined to the existing, in-progress verification,
+// completing when it does. If no in-flight requests match, a new request to
+// the underlying verifier will be started.
+//
+// If the underlying configuration changes, existing requests are allowed to
+// complete, but any new requests will not be seen as matching, even if they
+// share the same parameters. This ensures configuration changes propagate
+// "immediately" for all new requests.
+class NET_EXPORT CoalescingCertVerifier : public CertVerifier {
+ public:
+  // Create a new verifier that will forward calls to |verifier|, coalescing
+  // any in-flight, not-yet-completed calls to Verify().
+  explicit CoalescingCertVerifier(std::unique_ptr<CertVerifier> verifier);
+
+  ~CoalescingCertVerifier() override;
+
+  // CertVerifier implementation:
+  int Verify(const RequestParams& params,
+             CertVerifyResult* verify_result,
+             CompletionOnceCallback callback,
+             std::unique_ptr<CertVerifier::Request>* out_req,
+             const NetLogWithSource& net_log) override;
+  void SetConfig(const CertVerifier::Config& config) override;
+
+  uint64_t requests_for_testing() const { return requests_; }
+  uint64_t inflight_joins_for_testing() const { return inflight_joins_; }
+
+ private:
+  class Job;
+  class Request;
+
+  // If there is a pending request that matches |params|, and which can be
+  // joined (it shares the same config), returns that Job.
+  // Otherwise, returns nullptr, meaning a new Job should be started.
+  Job* FindJob(const RequestParams& params);
+  void RemoveJob(Job* job);
+
+  // Contains the set of Jobs for which an active verification is taking
+  // place and which can be used for new requests (e.g. the config is the
+  // same).
+  std::map<CertVerifier::RequestParams, std::unique_ptr<Job>> joinable_jobs_;
+
+  // Contains all pending Jobs that are in-flight, but cannot be joined, due
+  // to the configuration having changed since they were started.
+  std::vector<std::unique_ptr<Job>> inflight_jobs_;
+
+  std::unique_ptr<CertVerifier> verifier_;
+
+  uint32_t config_id_;
+  uint64_t requests_;
+  uint64_t inflight_joins_;
+
+  DISALLOW_COPY_AND_ASSIGN(CoalescingCertVerifier);
+};
+
+}  // namespace net
+
+#endif  // NET_CERT_COALESCING_CERT_VERIFIER_H_
diff --git a/net/cert/coalescing_cert_verifier_unittest.cc b/net/cert/coalescing_cert_verifier_unittest.cc
new file mode 100644
index 0000000..c90f989
--- /dev/null
+++ b/net/cert/coalescing_cert_verifier_unittest.cc
@@ -0,0 +1,518 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/coalescing_cert_verifier.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/cert/mock_cert_verifier.h"
+#include "net/cert/x509_certificate.h"
+#include "net/log/net_log_with_source.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/gtest_util.h"
+#include "net/test/test_data_directory.h"
+#include "net/test/test_with_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using net::test::IsError;
+using net::test::IsOk;
+
+namespace net {
+
+using CoalescingCertVerifierTest = TestWithTaskEnvironment;
+
+// Tests that synchronous completion does not cause any issues.
+TEST_F(CoalescingCertVerifierTest, SyncCompletion) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(false);  // Force sync completion.
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1, result2;
+  TestCompletionCallback callback1, callback2;
+  std::unique_ptr<CertVerifier::Request> request1, request2;
+
+  // Start an (asynchronous) initial request.
+  int error = verifier.Verify(request_params, &result1, callback1.callback(),
+                              &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsOk());
+  ASSERT_FALSE(request1);
+  ASSERT_TRUE(result1.verified_cert);
+}
+
+// Test that requests with identical parameters only result in a single
+// underlying verification; that is, the second Request is joined to the
+// in-progress first Request.
+TEST_F(CoalescingCertVerifierTest, InflightJoin) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  base::HistogramTester histograms;
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(true);  // Always complete via PostTask
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1, result2;
+  TestCompletionCallback callback1, callback2;
+  std::unique_ptr<CertVerifier::Request> request1, request2;
+
+  // Start an (asynchronous) initial request.
+  int error = verifier.Verify(request_params, &result1, callback1.callback(),
+                              &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request1);
+
+  // Simulate the underlying verifier returning different results if another
+  // verification is done.
+  mock_verifier->ClearRules();
+  mock_verifier->AddResultForCert(test_cert, fake_result, ERR_CERT_REVOKED);
+
+  // Start a second request; this should join the first request.
+  error = verifier.Verify(request_params, &result2, callback2.callback(),
+                          &request2, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request2);
+
+  // Ensure only one request was ever started.
+  EXPECT_EQ(2u, verifier.requests_for_testing());
+  EXPECT_EQ(1u, verifier.inflight_joins_for_testing());
+
+  // Make sure both results completed.
+  EXPECT_THAT(callback1.WaitForResult(), IsOk());
+  EXPECT_THAT(callback2.WaitForResult(), IsOk());
+
+  // There should only have been one Job started.
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms.ExpectTotalCount("Net.CertVerifier_First_Job_Latency", 1);
+}
+
+// Test that changing configurations between Requests prevents the second
+// Request from being attached to the first Request. There should be two
+// Requests to the underlying CertVerifier, and the correct results should be
+// received by each.
+TEST_F(CoalescingCertVerifierTest, DoesNotJoinAfterConfigChange) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  base::HistogramTester histograms;
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(true);  // Always complete via PostTask
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
+
+  CertVerifier::Config config1;
+  verifier.SetConfig(config1);
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1, result2;
+  TestCompletionCallback callback1, callback2;
+  std::unique_ptr<CertVerifier::Request> request1, request2;
+
+  // Start an (asynchronous) initial request.
+  int error = verifier.Verify(request_params, &result1, callback1.callback(),
+                              &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request1);
+
+  // Change the configuration, and change the result to to simulate the
+  // configuration change affecting behavior.
+  CertVerifier::Config config2;
+  config2.enable_rev_checking = !config1.enable_rev_checking;
+  verifier.SetConfig(config2);
+  mock_verifier->ClearRules();
+  mock_verifier->AddResultForCert(test_cert, fake_result, ERR_CERT_REVOKED);
+
+  // Start a second request; this should not join the first request, as the
+  // config is different.
+  error = verifier.Verify(request_params, &result2, callback2.callback(),
+                          &request2, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request2);
+
+  // Ensure a total of two requests were started, and neither were joined.
+  EXPECT_EQ(2u, verifier.requests_for_testing());
+  EXPECT_EQ(0u, verifier.inflight_joins_for_testing());
+
+  // Make sure both results completed.
+  EXPECT_THAT(callback1.WaitForResult(), IsOk());
+  EXPECT_THAT(callback2.WaitForResult(), IsError(ERR_CERT_REVOKED));
+
+  // There should have been two separate Jobs.
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
+  histograms.ExpectTotalCount("Net.CertVerifier_First_Job_Latency", 1);
+}
+
+// Test that when two Requests are attached to the same Job, it's safe to
+// delete the second Request while processing the response to the first. The
+// second Request should not cause the second callback to be called.
+TEST_F(CoalescingCertVerifierTest, DeleteSecondRequestDuringFirstCompletion) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(true);  // Always complete via PostTask
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1, result2;
+  TestCompletionCallback callback1, callback2;
+  std::unique_ptr<CertVerifier::Request> request1, request2;
+
+  // Start an (asynchronous) initial request. When this request is completed,
+  // it will delete (reset) |request2|, which should prevent it from being
+  // called.
+  int error = verifier.Verify(
+      request_params, &result1,
+      base::BindLambdaForTesting([&callback1, &request2](int result) {
+        request2.reset();
+        callback1.callback().Run(result);
+      }),
+      &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request1);
+
+  // Start a second request; this should join the first request.
+  error = verifier.Verify(request_params, &result2, callback2.callback(),
+                          &request2, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request2);
+
+  // Ensure only one underlying verification was started.
+  ASSERT_EQ(2u, verifier.requests_for_testing());
+  ASSERT_EQ(1u, verifier.inflight_joins_for_testing());
+
+  // Make sure that only the first callback is invoked; because the second
+  // CertVerifier::Request was deleted during processing the first's callback,
+  // the second callback should not be invoked.
+  EXPECT_THAT(callback1.WaitForResult(), IsOk());
+  ASSERT_FALSE(callback2.have_result());
+  ASSERT_FALSE(request2);
+
+  // While CoalescingCertVerifier doesn't use PostTask, make sure to flush the
+  // tasks as well, in case the implementation changes in the future.
+  RunUntilIdle();
+  ASSERT_FALSE(callback2.have_result());
+  ASSERT_FALSE(request2);
+}
+
+// Test that it's safe to delete the CoalescingCertVerifier during completion,
+// even when there are outstanding Requests to be processed. The additional
+// Requests should not invoke the user callback once the
+// CoalescingCertVerifier is deleted.
+TEST_F(CoalescingCertVerifierTest, DeleteVerifierDuringCompletion) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(true);  // Always complete via PostTask
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  auto verifier =
+      std::make_unique<CoalescingCertVerifier>(std::move(mock_verifier_owner));
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1, result2;
+  TestCompletionCallback callback1, callback2;
+  std::unique_ptr<CertVerifier::Request> request1, request2;
+
+  // Start an (asynchronous) initial request. When this request is completed,
+  // it will delete (reset) |request2|, which should prevent it from being
+  // called.
+  int error = verifier->Verify(
+      request_params, &result1,
+      base::BindLambdaForTesting([&callback1, &verifier](int result) {
+        verifier.reset();
+        callback1.callback().Run(result);
+      }),
+      &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request1);
+
+  // Start a second request; this should join the first request.
+  error = verifier->Verify(request_params, &result2, callback2.callback(),
+                           &request2, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request2);
+
+  // Ensure only one underlying verification was started.
+  ASSERT_EQ(2u, verifier->requests_for_testing());
+  ASSERT_EQ(1u, verifier->inflight_joins_for_testing());
+
+  // Make sure that only the first callback is invoked. This will delete the
+  // underlying CoalescingCertVerifier, which should prevent the second
+  // request's callback from being invoked.
+  EXPECT_THAT(callback1.WaitForResult(), IsOk());
+  ASSERT_FALSE(callback2.have_result());
+  ASSERT_TRUE(request2);
+
+  // While CoalescingCertVerifier doesn't use PostTask, make sure to flush the
+  // tasks as well, in case the implementation changes in the future.
+  RunUntilIdle();
+  ASSERT_FALSE(callback2.have_result());
+  ASSERT_TRUE(request2);
+}
+
+// Test that it's safe to delete a Request before the underlying verifier has
+// completed. This is a guard against memory safety (e.g. when this Request
+// is the last/only Request remaining).
+TEST_F(CoalescingCertVerifierTest, DeleteRequestBeforeCompletion) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(true);  // Always complete via PostTask
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1;
+  TestCompletionCallback callback1;
+  std::unique_ptr<CertVerifier::Request> request1;
+
+  // Start an (asynchronous) initial request.
+  int error = verifier.Verify(request_params, &result1, callback1.callback(),
+                              &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request1);
+
+  // Abandon the request before it's completed.
+  request1.reset();
+  EXPECT_FALSE(callback1.have_result());
+
+  // Make sure the request never completes / the callback is never invoked.
+  RunUntilIdle();
+  EXPECT_FALSE(callback1.have_result());
+}
+
+// Test that it's safe to delete a Request before the underlying verifier has
+// completed. This is a correctness test, to ensure that other Requests are
+// still notified.
+TEST_F(CoalescingCertVerifierTest,
+       DeleteFirstRequestBeforeCompletionStillCompletesSecondRequest) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(true);  // Always complete via PostTask
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1, result2;
+  TestCompletionCallback callback1, callback2;
+  std::unique_ptr<CertVerifier::Request> request1, request2;
+
+  // Start an (asynchronous) initial request.
+  int error = verifier.Verify(request_params, &result1, callback1.callback(),
+                              &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request1);
+
+  // Start a second request; this should join the first request.
+  error = verifier.Verify(request_params, &result2, callback2.callback(),
+                          &request2, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request2);
+
+  // Ensure only one underlying verification was started.
+  ASSERT_EQ(2u, verifier.requests_for_testing());
+  ASSERT_EQ(1u, verifier.inflight_joins_for_testing());
+
+  // Abandon the first request before it's completed.
+  request1.reset();
+
+  // Make sure the first request never completes / the callback is never
+  // invoked, while the second request completes normally.
+  EXPECT_THAT(callback2.WaitForResult(), IsOk());
+  EXPECT_FALSE(callback1.have_result());
+
+  // Simulate the second request going away during processing.
+  request2.reset();
+
+  // Flush any events, although there should not be any.
+  RunUntilIdle();
+  EXPECT_FALSE(callback1.have_result());
+}
+
+TEST_F(CoalescingCertVerifierTest, DeleteRequestDuringCompletion) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(true);  // Always complete via PostTask
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1;
+  TestCompletionCallback callback1;
+  std::unique_ptr<CertVerifier::Request> request1;
+
+  // Start an (asynchronous) initial request.
+  int error = verifier.Verify(
+      request_params, &result1,
+      base::BindLambdaForTesting([&callback1, &request1](int result) {
+        // Delete the Request during the completion callback. This should be
+        // perfectly safe, and not cause any memory trouble, because the
+        // Request was already detached from the Job prior to being invoked.
+        request1.reset();
+        callback1.callback().Run(result);
+      }),
+      &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request1);
+
+  // The result should be available, even though the request is deleted
+  // during the result processing. This should not cause any memory errors.
+  EXPECT_THAT(callback1.WaitForResult(), IsOk());
+}
+
+TEST_F(CoalescingCertVerifierTest, DeleteVerifierBeforeRequest) {
+  scoped_refptr<X509Certificate> test_cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+  ASSERT_TRUE(test_cert);
+
+  base::HistogramTester histograms;
+
+  CertVerifyResult fake_result;
+  fake_result.verified_cert = test_cert;
+
+  std::unique_ptr<MockCertVerifier> mock_verifier_owner =
+      std::make_unique<MockCertVerifier>();
+  MockCertVerifier* mock_verifier = mock_verifier_owner.get();
+  mock_verifier->set_async(true);  // Always complete via PostTask
+  mock_verifier->AddResultForCert(test_cert, fake_result, OK);
+
+  auto verifier =
+      std::make_unique<CoalescingCertVerifier>(std::move(mock_verifier_owner));
+
+  CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
+                                             /*ocsp_response=*/std::string(),
+                                             /*sct_list=*/std::string());
+
+  CertVerifyResult result1;
+  TestCompletionCallback callback1;
+  std::unique_ptr<CertVerifier::Request> request1;
+
+  // Start an (asynchronous) initial request.
+  int error = verifier->Verify(request_params, &result1, callback1.callback(),
+                               &request1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request1);
+
+  // Delete the CoalescingCertVerifier first. This should orphan all
+  // outstanding Requests and delete all associated Jobs.
+  verifier.reset();
+
+  // Flush any pending tasks; there should not be any, at this point, but use
+  // it in case the implementation changes.
+  RunUntilIdle();
+
+  // Make sure the callback was never called.
+  EXPECT_FALSE(callback1.have_result());
+
+  // Delete the Request. This should be a no-op as the Request was orphaned
+  // when the CoalescingCertVerifier was deleted.
+  request1.reset();
+
+  // There should not have been any histograms logged.
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 0);
+  histograms.ExpectTotalCount("Net.CertVerifier_First_Job_Latency", 0);
+}
+
+}  // namespace net
diff --git a/net/cert/mock_cert_verifier.cc b/net/cert/mock_cert_verifier.cc
index e65dadb..1660818 100644
--- a/net/cert/mock_cert_verifier.cc
+++ b/net/cert/mock_cert_verifier.cc
@@ -99,6 +99,10 @@
   rules_.push_back(Rule(std::move(cert), host_pattern, verify_result, rv));
 }
 
+void MockCertVerifier::ClearRules() {
+  rules_.clear();
+}
+
 int MockCertVerifier::VerifyImpl(const RequestParams& params,
                                  CertVerifyResult* verify_result) {
   for (const Rule& rule : rules_) {
diff --git a/net/cert/mock_cert_verifier.h b/net/cert/mock_cert_verifier.h
index 6c34a06..66b82c7 100644
--- a/net/cert/mock_cert_verifier.h
+++ b/net/cert/mock_cert_verifier.h
@@ -58,6 +58,9 @@
                                const CertVerifyResult& verify_result,
                                int rv);
 
+  // Clear all existing rules.
+  void ClearRules();
+
  private:
   struct Rule;
   using RuleList = std::list<Rule>;
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index 32222db..7c76599 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -4,34 +4,18 @@
 
 #include "net/cert/multi_threaded_cert_verifier.h"
 
-#include <algorithm>
-#include <iterator>
-#include <utility>
-
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/compiler_specific.h"
-#include "base/containers/linked_list.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/task/post_task.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
-#include "base/values.h"
-#include "net/base/hash_value.h"
 #include "net/base/net_errors.h"
 #include "net/base/trace_constants.h"
 #include "net/cert/cert_verify_proc.h"
 #include "net/cert/cert_verify_result.h"
 #include "net/cert/crl_set.h"
 #include "net/cert/x509_certificate.h"
-#include "net/cert/x509_certificate_net_log_param.h"
-#include "net/log/net_log_capture_mode.h"
-#include "net/log/net_log_event_type.h"
-#include "net/log/net_log_source.h"
-#include "net/log/net_log_source_type.h"
-#include "net/log/net_log_with_source.h"
 
 namespace net {
 
@@ -43,67 +27,8 @@
 class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives
     : public base::ScopedAllowBaseSyncPrimitives {};
 
-////////////////////////////////////////////////////////////////////////////
-//
-// MultiThreadedCertVerifier is a thread-unsafe object which lives, dies, and is
-// operated on a single thread, henceforth referred to as the "origin" thread.
-//
-// When an incoming Verify() request is received, MultiThreadedCertVerifier
-// checks if there is an outstanding "job" (CertVerifierJob) in progress that
-// can service the request. If there is, the request is attached to that job.
-// Otherwise a new job is started.
-//
-// A job (CertVerifierJob) is a way to de-duplicate requests that are
-// fundamentally doing the same verification. CertVerifierJob is similarly
-// thread-unsafe and lives on the origin thread.
-//
-// To do the actual work, CertVerifierJob posts a task to ThreadPool
-// (PostTaskAndReply), and on completion notifies all requests attached to it.
-//
-// Cancellation:
-//
-// There are two ways for a request to be cancelled.
-//
-// (1) When the caller explicitly frees the Request.
-//
-//     If the request was in-flight (attached to a job), then it is detached.
-//     Note that no effort is made to reap jobs which have no attached requests.
-//     (Because the worker task isn't cancelable).
-//
-// (2) When the MultiThreadedCertVerifier is deleted.
-//
-//     This automatically cancels all outstanding requests. This is accomplished
-//     by deleting each of the jobs owned by the MultiThreadedCertVerifier,
-//     whose destructor in turn marks each attached request as canceled.
-//
-// TODO(eroman): If the MultiThreadedCertVerifier is deleted from within a
-// callback, the remaining requests in the completing job will NOT be cancelled.
-
 namespace {
 
-base::Value CertVerifyResultParams(const CertVerifyResult& verify_result) {
-  base::DictionaryValue results;
-  results.SetBoolean("has_md5", verify_result.has_md5);
-  results.SetBoolean("has_md2", verify_result.has_md2);
-  results.SetBoolean("has_md4", verify_result.has_md4);
-  results.SetBoolean("is_issued_by_known_root",
-                     verify_result.is_issued_by_known_root);
-  results.SetBoolean("is_issued_by_additional_trust_anchor",
-                     verify_result.is_issued_by_additional_trust_anchor);
-  results.SetInteger("cert_status", verify_result.cert_status);
-  results.SetKey("verified_cert", NetLogX509CertificateParams(
-                                      verify_result.verified_cert.get()));
-
-  std::unique_ptr<base::ListValue> hashes(new base::ListValue());
-  for (auto it = verify_result.public_key_hashes.begin();
-       it != verify_result.public_key_hashes.end(); ++it) {
-    hashes->AppendString(it->ToString());
-  }
-  results.Set("public_key_hashes", std::move(hashes));
-
-  return std::move(results);
-}
-
 // Used to pass the result of CertVerifierJob::DoVerifyOnWorkerThread() to
 // CertVerifierJob::OnJobCompleted().
 struct ResultHelper {
@@ -126,65 +51,6 @@
   return flags;
 }
 
-}  // namespace
-
-// Represents the output and result callback of a request. The
-// CertVerifierRequest is owned by the caller that initiated the call to
-// CertVerifier::Verify().
-class CertVerifierRequest : public base::LinkNode<CertVerifierRequest>,
-                            public CertVerifier::Request {
- public:
-  CertVerifierRequest(CertVerifierJob* job,
-                      CompletionOnceCallback callback,
-                      CertVerifyResult* verify_result,
-                      const NetLogWithSource& net_log)
-      : job_(job),
-        callback_(std::move(callback)),
-        verify_result_(verify_result),
-        net_log_(net_log) {
-    net_log_.BeginEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
-  }
-
-  // Cancels the request.
-  ~CertVerifierRequest() override {
-    if (job_) {
-      // Cancel the outstanding request.
-      net_log_.AddEvent(NetLogEventType::CANCELLED);
-      net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
-
-      // Remove the request from the Job. No attempt is made to cancel the job
-      // even though it may no longer have any requests attached to it. Because
-      // it is running on a worker thread aborting it isn't feasible.
-      RemoveFromList();
-    }
-  }
-
-  // Copies the contents of |verify_result| to the caller's
-  // CertVerifyResult and calls the callback.
-  void Post(const ResultHelper& verify_result) {
-    DCHECK(job_);
-    job_ = nullptr;
-
-    net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
-    *verify_result_ = verify_result.result;
-
-    std::move(callback_).Run(verify_result.error);
-  }
-
-  void OnJobCancelled() {
-    job_ = nullptr;
-    callback_.Reset();
-  }
-
-  const NetLogWithSource& net_log() const { return net_log_; }
-
- private:
-  CertVerifierJob* job_;  // Not owned.
-  CompletionOnceCallback callback_;
-  CertVerifyResult* verify_result_;
-  const NetLogWithSource net_log_;
-};
-
 // DoVerifyOnWorkerThread runs the verification synchronously on a worker
 // thread.
 std::unique_ptr<ResultHelper> DoVerifyOnWorkerThread(
@@ -210,149 +76,69 @@
   return verify_result;
 }
 
-// CertVerifierJob lives only on the verifier's origin message loop.
-class CertVerifierJob {
+// Helper to allow callers to cancel pending CertVerifier::Verify requests.
+// Note that because the CertVerifyProc is blocking, it's not actually
+// possible to cancel the in-progress request; instead, this simply guarantees
+// that the provided callback will not be invoked if the Request is deleted.
+class InternalRequest : public CertVerifier::Request {
  public:
-  CertVerifierJob(const CertVerifier::RequestParams& key,
-                  NetLog* net_log,
-                  MultiThreadedCertVerifier* cert_verifier)
-      : key_(key),
-        start_time_(base::TimeTicks::Now()),
-        net_log_(NetLogWithSource::Make(net_log,
-                                        NetLogSourceType::CERT_VERIFIER_JOB)),
-        cert_verifier_(cert_verifier),
-        is_first_job_(false) {
-    net_log_.BeginEvent(NetLogEventType::CERT_VERIFIER_JOB, [&] {
-      return NetLogX509CertificateParams(key.certificate().get());
-    });
-  }
+  InternalRequest(CompletionOnceCallback callback,
+                  CertVerifyResult* caller_result);
+  ~InternalRequest() override;
 
-  // Indicates whether this was the first job started by the CertVerifier. This
-  // is only used for logging certain UMA stats.
-  void set_is_first_job(bool is_first_job) { is_first_job_ = is_first_job; }
-
-  const CertVerifier::RequestParams& key() const { return key_; }
-
-  // Posts a task to ThreadPool to do the verification. Once the verification
-  // has completed, it will call OnJobCompleted() on the origin thread.
   void Start(const scoped_refptr<CertVerifyProc>& verify_proc,
              const CertVerifier::Config& config,
-             uint32_t config_id) {
-    int flags = GetFlagsForConfig(config);
-    if (key_.flags() & CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES) {
-      flags &= ~CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
-      flags &= ~CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
-    }
-    DCHECK(config.crl_set);
-    base::PostTaskAndReplyWithResult(
-        FROM_HERE,
-        {base::ThreadPool(), base::MayBlock(),
-         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-        base::BindOnce(&DoVerifyOnWorkerThread, verify_proc, key_.certificate(),
-                       key_.hostname(), key_.ocsp_response(), key_.sct_list(),
-                       flags, config.crl_set, config.additional_trust_anchors),
-        base::BindOnce(&CertVerifierJob::OnJobCompleted,
-                       weak_ptr_factory_.GetWeakPtr(), config_id));
-  }
-
-  ~CertVerifierJob() {
-    // If the job is in progress, cancel it.
-    if (cert_verifier_) {
-      cert_verifier_ = nullptr;
-
-      net_log_.AddEvent(NetLogEventType::CANCELLED);
-      net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB);
-
-      // Notify each request of the cancellation.
-      for (base::LinkNode<CertVerifierRequest>* it = requests_.head();
-           it != requests_.end(); it = it->next()) {
-        it->value()->OnJobCancelled();
-      }
-    }
-  }
-
-  // Creates and attaches a request to the Job.
-  std::unique_ptr<CertVerifierRequest> CreateRequest(
-      CompletionOnceCallback callback,
-      CertVerifyResult* verify_result,
-      const NetLogWithSource& net_log) {
-    std::unique_ptr<CertVerifierRequest> request(new CertVerifierRequest(
-        this, std::move(callback), verify_result, net_log));
-
-    request->net_log().AddEventReferencingSource(
-        NetLogEventType::CERT_VERIFIER_REQUEST_BOUND_TO_JOB, net_log_.source());
-
-    requests_.Append(request.get());
-    return request;
-  }
+             const CertVerifier::RequestParams& params);
 
  private:
-  using RequestList = base::LinkedList<CertVerifierRequest>;
+  void OnJobComplete(std::unique_ptr<ResultHelper> verify_result);
 
-  // Called on completion of the Job to log UMA metrics and NetLog events.
-  void LogMetrics(const ResultHelper& verify_result) {
-    net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB, [&] {
-      return CertVerifyResultParams(verify_result.result);
-    });
-    base::TimeDelta latency = base::TimeTicks::Now() - start_time_;
-    if (cert_verifier_->should_record_histograms_) {
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency", latency,
-                                 base::TimeDelta::FromMilliseconds(1),
-                                 base::TimeDelta::FromMinutes(10), 100);
-      if (is_first_job_) {
-        UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_First_Job_Latency",
-                                   latency,
-                                   base::TimeDelta::FromMilliseconds(1),
-                                   base::TimeDelta::FromMinutes(10), 100);
-      }
-    }
-  }
+  CompletionOnceCallback callback_;
+  CertVerifyResult* caller_result_;
 
-  void OnJobCompleted(uint32_t config_id,
-                      std::unique_ptr<ResultHelper> verify_result) {
-    TRACE_EVENT0(NetTracingCategory(), "CertVerifierJob::OnJobCompleted");
-    std::unique_ptr<CertVerifierJob> keep_alive =
-        cert_verifier_->RemoveJob(this);
-
-    LogMetrics(*verify_result);
-    if (cert_verifier_->verify_complete_callback_ &&
-        config_id == cert_verifier_->config_id_) {
-      cert_verifier_->verify_complete_callback_.Run(
-          key_, net_log_, verify_result->error, verify_result->result,
-          base::TimeTicks::Now() - start_time_, is_first_job_);
-    }
-    cert_verifier_ = nullptr;
-
-    // TODO(eroman): If the cert_verifier_ is deleted from within one of the
-    // callbacks, any remaining requests for that job should be cancelled. Right
-    // now they will be called.
-    while (!requests_.empty()) {
-      base::LinkNode<CertVerifierRequest>* request = requests_.head();
-      request->RemoveFromList();
-      request->value()->Post(*verify_result);
-    }
-  }
-
-  const CertVerifier::RequestParams key_;
-  // The tick count of when the job started. This is used to measure how long
-  // the job actually took to complete.
-  const base::TimeTicks start_time_;
-
-  RequestList requests_;  // Non-owned.
-
-  const NetLogWithSource net_log_;
-  MultiThreadedCertVerifier* cert_verifier_;  // Non-owned.
-
-  bool is_first_job_;
-  base::WeakPtrFactory<CertVerifierJob> weak_ptr_factory_{this};
+  base::WeakPtrFactory<InternalRequest> weak_factory_{this};
 };
 
+InternalRequest::InternalRequest(CompletionOnceCallback callback,
+                                 CertVerifyResult* caller_result)
+    : callback_(std::move(callback)), caller_result_(caller_result) {}
+
+InternalRequest::~InternalRequest() = default;
+
+void InternalRequest::Start(const scoped_refptr<CertVerifyProc>& verify_proc,
+                            const CertVerifier::Config& config,
+                            const CertVerifier::RequestParams& params) {
+  int flags = GetFlagsForConfig(config);
+  if (params.flags() & CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES) {
+    flags &= ~CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+    flags &= ~CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
+  }
+  DCHECK(config.crl_set);
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(),
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(&DoVerifyOnWorkerThread, verify_proc, params.certificate(),
+                     params.hostname(), params.ocsp_response(),
+                     params.sct_list(), flags, config.crl_set,
+                     config.additional_trust_anchors),
+      base::BindOnce(&InternalRequest::OnJobComplete,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void InternalRequest::OnJobComplete(
+    std::unique_ptr<ResultHelper> verify_result) {
+  *caller_result_ = verify_result->result;
+  // Note: May delete |this|.
+  std::move(callback_).Run(verify_result->error);
+}
+
+}  // namespace
+
 MultiThreadedCertVerifier::MultiThreadedCertVerifier(
     scoped_refptr<CertVerifyProc> verify_proc)
-    : config_id_(0),
-      requests_(0),
-      inflight_joins_(0),
-      verify_proc_(verify_proc) {
+    : verify_proc_(std::move(verify_proc)) {
+  // Guarantee there is always a CRLSet (this can be overridden via SetConfig).
   config_.crl_set = CRLSet::BuiltinCRLSet();
 }
 
@@ -360,17 +146,6 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
-// static
-std::unique_ptr<MultiThreadedCertVerifier>
-MultiThreadedCertVerifier::CreateForDualVerificationTrial(
-    scoped_refptr<CertVerifyProc> verify_proc,
-    VerifyCompleteCallback verify_complete_callback,
-    bool should_record_histograms) {
-  return base::WrapUnique(new net::MultiThreadedCertVerifier(
-      std::move(verify_proc), std::move(verify_complete_callback),
-      should_record_histograms));
-}
-
 int MultiThreadedCertVerifier::Verify(const RequestParams& params,
                                       CertVerifyResult* verify_result,
                                       CompletionOnceCallback callback,
@@ -383,100 +158,17 @@
   if (callback.is_null() || !verify_result || params.hostname().empty())
     return ERR_INVALID_ARGUMENT;
 
-  requests_++;
-
-  // See if an identical request is currently in flight.
-  CertVerifierJob* job = FindJob(params);
-  if (job) {
-    // An identical request is in flight already. We'll just attach our
-    // callback.
-    inflight_joins_++;
-  } else {
-    // Need to make a new job.
-    std::unique_ptr<CertVerifierJob> new_job =
-        std::make_unique<CertVerifierJob>(params, net_log.net_log(), this);
-
-    new_job->Start(verify_proc_, config_, config_id_);
-
-    job = new_job.get();
-    joinable_[job] = std::move(new_job);
-
-    if (requests_ == 1)
-      job->set_is_first_job(true);
-  }
-
-  std::unique_ptr<CertVerifierRequest> request =
-      job->CreateRequest(std::move(callback), verify_result, net_log);
+  std::unique_ptr<InternalRequest> request =
+      std::make_unique<InternalRequest>(std::move(callback), verify_result);
+  request->Start(verify_proc_, config_, params);
   *out_req = std::move(request);
   return ERR_IO_PENDING;
 }
 
 void MultiThreadedCertVerifier::SetConfig(const CertVerifier::Config& config) {
-  ++config_id_;
   config_ = config;
   if (!config_.crl_set)
     config_.crl_set = CRLSet::BuiltinCRLSet();
-
-  // In C++17, this would be a .merge() call to combine |joinable_| into
-  // |inflight_|.
-  inflight_.insert(std::make_move_iterator(joinable_.begin()),
-                   std::make_move_iterator(joinable_.end()));
-  joinable_.clear();
-}
-
-bool MultiThreadedCertVerifier::JobComparator::operator()(
-    const CertVerifierJob* job1,
-    const CertVerifierJob* job2) const {
-  return job1->key() < job2->key();
-}
-
-MultiThreadedCertVerifier::MultiThreadedCertVerifier(
-    scoped_refptr<CertVerifyProc> verify_proc,
-    VerifyCompleteCallback verify_complete_callback,
-    bool should_record_histograms)
-    : MultiThreadedCertVerifier(verify_proc) {
-  verify_complete_callback_ = std::move(verify_complete_callback);
-  should_record_histograms_ = should_record_histograms;
-}
-
-std::unique_ptr<CertVerifierJob> MultiThreadedCertVerifier::RemoveJob(
-    CertVerifierJob* job) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  // See if it's a job from the current generation.
-  auto joinable_it = joinable_.find(job);
-  if (joinable_it != joinable_.end()) {
-    std::unique_ptr<CertVerifierJob> job_ptr = std::move(joinable_it->second);
-    joinable_.erase(joinable_it);
-    return job_ptr;
-  }
-
-  // Otherwise, find it and remove it from previous generations.
-  auto it = inflight_.find(job);
-  DCHECK(it != inflight_.end());
-  std::unique_ptr<CertVerifierJob> job_ptr = std::move(it->second);
-  inflight_.erase(it);
-  return job_ptr;
-}
-
-struct MultiThreadedCertVerifier::JobToRequestParamsComparator {
-  bool operator()(const std::pair<CertVerifierJob* const,
-                                  std::unique_ptr<CertVerifierJob>>& item,
-                  const CertVerifier::RequestParams& value) const {
-    return item.first->key() < value;
-  }
-};
-
-CertVerifierJob* MultiThreadedCertVerifier::FindJob(const RequestParams& key) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  // The JobSet is kept in sorted order so items can be found using binary
-  // search.
-  auto it = std::lower_bound(joinable_.begin(), joinable_.end(), key,
-                             JobToRequestParamsComparator());
-  if (it != joinable_.end() && !(key < it->first->key()))
-    return it->first;
-  return nullptr;
 }
 
 }  // namespace net
diff --git a/net/cert/multi_threaded_cert_verifier.h b/net/cert/multi_threaded_cert_verifier.h
index 03804ea..28c7bf7 100644
--- a/net/cert/multi_threaded_cert_verifier.h
+++ b/net/cert/multi_threaded_cert_verifier.h
@@ -10,55 +10,27 @@
 
 #include <map>
 #include <memory>
-#include <string>
-#include <vector>
 
-#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
-#include "net/base/completion_once_callback.h"
 #include "net/base/net_export.h"
 #include "net/cert/cert_verifier.h"
 
 namespace net {
 
-class CertVerifierJob;
-class CertVerifierRequest;
 class CertVerifyProc;
 
 // MultiThreadedCertVerifier is a CertVerifier implementation that runs
 // synchronous CertVerifier implementations on worker threads.
 class NET_EXPORT_PRIVATE MultiThreadedCertVerifier : public CertVerifier {
  public:
-  using VerifyCompleteCallback =
-      base::RepeatingCallback<void(const RequestParams&,
-                                   const NetLogWithSource&,
-                                   int,
-                                   const CertVerifyResult&,
-                                   base::TimeDelta,
-                                   bool)>;
-
   explicit MultiThreadedCertVerifier(scoped_refptr<CertVerifyProc> verify_proc);
 
   // When the verifier is destroyed, all certificate verifications requests are
   // canceled, and their completion callbacks will not be called.
   ~MultiThreadedCertVerifier() override;
 
-  // Creates a MultiThreadedCertVerifier that will call
-  // |verify_complete_callback| once for each verification that has completed
-  // (if it is non-null). Histograms will have |histogram_suffix| appended, if
-  // it is non-empty.
-  // This factory method is temporary, and should not be used without
-  // consulting with OWNERS.
-  // TODO(mattm): remove this once the dual verification trial is complete.
-  // (See https://crbug.com/649026.)
-  static std::unique_ptr<MultiThreadedCertVerifier>
-  CreateForDualVerificationTrial(
-      scoped_refptr<CertVerifyProc> verify_proc,
-      VerifyCompleteCallback verify_complete_callback,
-      bool should_record_histograms);
-
   // CertVerifier implementation
   int Verify(const RequestParams& params,
              CertVerifyResult* verify_result,
@@ -68,64 +40,9 @@
   void SetConfig(const CertVerifier::Config& config) override;
 
  private:
-  struct JobToRequestParamsComparator;
-  friend class CertVerifierRequest;
-  friend class CertVerifierJob;
-  friend class MultiThreadedCertVerifierTest;
-  FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, InflightJoin);
-  FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, MultipleInflightJoin);
-  FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, CancelRequest);
-
-  struct JobComparator {
-    bool operator()(const CertVerifierJob* job1,
-                    const CertVerifierJob* job2) const;
-  };
-
-  // TODO(mattm): remove this once the dual verification trial is complete.
-  // (See https://crbug.com/649026.)
-  MultiThreadedCertVerifier(scoped_refptr<CertVerifyProc> verify_proc,
-                            VerifyCompleteCallback verify_complete_callback,
-                            bool should_record_histograms);
-
-  // Returns an inflight job for |key|, if it can be joined. If there is no
-  // such job then returns null.
-  CertVerifierJob* FindJob(const RequestParams& key);
-
-  // Removes |job| from the inflight set, and passes ownership back to the
-  // caller. |job| must already be |inflight_|.
-  std::unique_ptr<CertVerifierJob> RemoveJob(CertVerifierJob* job);
-
-  // For unit testing.
-  uint64_t requests() const { return requests_; }
-  uint64_t inflight_joins() const { return inflight_joins_; }
-
-  // |joinable_| holds the jobs for which an active verification is taking
-  // place and can be joined by new requests (e.g. the config is the same),
-  // mapping the job's raw pointer to an owned pointer.
-  // TODO(rsleevi): Once C++17 is supported, switch this to be a std::set<>,
-  // which supports extracting owned objects from the set.
-  std::map<CertVerifierJob*, std::unique_ptr<CertVerifierJob>, JobComparator>
-      joinable_;
-
-  // |inflight_| contains all jobs that are still undergoing active
-  // verification, but which can no longer be joined - such as due to the
-  // underlying configuration changing.
-  std::map<CertVerifierJob*, std::unique_ptr<CertVerifierJob>, JobComparator>
-      inflight_;
-
-  uint32_t config_id_;
   Config config_;
-
-  uint64_t requests_;
-  uint64_t inflight_joins_;
-
   scoped_refptr<CertVerifyProc> verify_proc_;
 
-  // Members for dual verification trial. TODO(mattm): Remove these.
-  // (See https://crbug.com/649026.)
-  VerifyCompleteCallback verify_complete_callback_;
-  bool should_record_histograms_ = true;
-
   THREAD_CHECKER(thread_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(MultiThreadedCertVerifier);
diff --git a/net/cert/multi_threaded_cert_verifier_unittest.cc b/net/cert/multi_threaded_cert_verifier_unittest.cc
index 789ce74..933f84b 100644
--- a/net/cert/multi_threaded_cert_verifier_unittest.cc
+++ b/net/cert/multi_threaded_cert_verifier_unittest.cc
@@ -90,43 +90,6 @@
   MultiThreadedCertVerifier verifier_;
 };
 
-// Tests an inflight join.
-TEST_F(MultiThreadedCertVerifierTest, InflightJoin) {
-  base::FilePath certs_dir = GetTestCertsDirectory();
-  scoped_refptr<X509Certificate> test_cert(
-      ImportCertFromFile(certs_dir, "ok_cert.pem"));
-  ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
-
-  int error;
-  CertVerifyResult verify_result;
-  TestCompletionCallback callback;
-  std::unique_ptr<CertVerifier::Request> request;
-  CertVerifyResult verify_result2;
-  TestCompletionCallback callback2;
-  std::unique_ptr<CertVerifier::Request> request2;
-
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result, callback.callback(), &request, NetLogWithSource());
-  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result2, callback2.callback(), &request2, NetLogWithSource());
-  EXPECT_THAT(error, IsError(ERR_IO_PENDING));
-  EXPECT_TRUE(request2);
-  error = callback.WaitForResult();
-  EXPECT_TRUE(IsCertificateError(error));
-  error = callback2.WaitForResult();
-  ASSERT_TRUE(IsCertificateError(error));
-  ASSERT_EQ(2u, verifier_.requests());
-  ASSERT_EQ(1u, verifier_.inflight_joins());
-}
-
 // Tests that the callback of a canceled request is never made.
 TEST_F(MultiThreadedCertVerifierTest, CancelRequest) {
   base::FilePath certs_dir = GetTestCertsDirectory();
@@ -195,89 +158,6 @@
   // Destroy |verifier| by going out of scope.
 }
 
-// Tests de-duplication of requests.
-// Starts up 5 requests, of which 3 are unique.
-TEST_F(MultiThreadedCertVerifierTest, MultipleInflightJoin) {
-  base::FilePath certs_dir = GetTestCertsDirectory();
-  scoped_refptr<X509Certificate> test_cert(
-      ImportCertFromFile(certs_dir, "ok_cert.pem"));
-  ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
-
-  int error;
-  CertVerifyResult verify_result1;
-  TestCompletionCallback callback1;
-  std::unique_ptr<CertVerifier::Request> request1;
-  CertVerifyResult verify_result2;
-  TestCompletionCallback callback2;
-  std::unique_ptr<CertVerifier::Request> request2;
-  CertVerifyResult verify_result3;
-  TestCompletionCallback callback3;
-  std::unique_ptr<CertVerifier::Request> request3;
-  CertVerifyResult verify_result4;
-  TestCompletionCallback callback4;
-  std::unique_ptr<CertVerifier::Request> request4;
-  CertVerifyResult verify_result5;
-  TestCompletionCallback callback5;
-  std::unique_ptr<CertVerifier::Request> request5;
-
-  const char domain1[] = "www.example1.com";
-  const char domain2[] = "www.exampleB.com";
-  const char domain3[] = "www.example3.com";
-
-  // Start 3 unique requests.
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result1, callback1.callback(), &request1, NetLogWithSource());
-  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
-  EXPECT_TRUE(request1);
-
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result2, callback2.callback(), &request2, NetLogWithSource());
-  EXPECT_THAT(error, IsError(ERR_IO_PENDING));
-  EXPECT_TRUE(request2);
-
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain3, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result3, callback3.callback(), &request3, NetLogWithSource());
-  EXPECT_THAT(error, IsError(ERR_IO_PENDING));
-  EXPECT_TRUE(request3);
-
-  // Start duplicate requests (which should join to existing jobs).
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain1, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result4, callback4.callback(), &request4, NetLogWithSource());
-  EXPECT_THAT(error, IsError(ERR_IO_PENDING));
-  EXPECT_TRUE(request4);
-
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0,
-                                  /*ocsp_response=*/std::string(),
-                                  /*sct_list=*/std::string()),
-      &verify_result5, callback5.callback(), &request5, NetLogWithSource());
-  EXPECT_THAT(error, IsError(ERR_IO_PENDING));
-  EXPECT_TRUE(request5);
-
-  error = callback1.WaitForResult();
-  EXPECT_TRUE(IsCertificateError(error));
-  error = callback2.WaitForResult();
-  ASSERT_TRUE(IsCertificateError(error));
-  error = callback4.WaitForResult();
-  ASSERT_TRUE(IsCertificateError(error));
-
-  // Let the other requests automatically cancel.
-  ASSERT_EQ(5u, verifier_.requests());
-  ASSERT_EQ(2u, verifier_.inflight_joins());
-}
-
 // Tests propagation of configuration options into CertVerifyProc flags
 TEST_F(MultiThreadedCertVerifierTest, ConvertsConfigToFlags) {
   base::FilePath certs_dir = GetTestCertsDirectory();
diff --git a/net/cert/trial_comparison_cert_verifier.cc b/net/cert/trial_comparison_cert_verifier.cc
index f3bd119c..d91f542 100644
--- a/net/cert/trial_comparison_cert_verifier.cc
+++ b/net/cert/trial_comparison_cert_verifier.cc
@@ -31,12 +31,16 @@
 
 namespace {
 
-base::Value TrialVerificationJobResultParams(bool trial_success) {
+base::Value JobResultParams(bool trial_success) {
   base::Value results(base::Value::Type::DICTIONARY);
   results.SetBoolKey("trial_success", trial_success);
   return results;
 }
 
+// Note: This ignores the result of stapled OCSP (which is the same for both
+// verifiers) and informational statuses about the certificate algorithms and
+// the hashes, since they will be the same if the certificate chains are the
+// same.
 bool CertVerifyResultEqual(const CertVerifyResult& a,
                            const CertVerifyResult& b) {
   return std::tie(a.cert_status, a.is_issued_by_known_root) ==
@@ -120,302 +124,525 @@
 
 }  // namespace
 
-class TrialComparisonCertVerifier::TrialVerificationJob {
+// The Job represents the state machine for a trial cert verification.
+// The Job is always owned by the TrialComparisonCertVerifier. However, a
+// reference to the Job is given by the CertVerifier::Request returned by
+// Start(), allowing the caller to indicate they're no longer interested in
+// the Job if it's not yet completed.
+//
+// The Job may be deleted while processing the initial verification completion,
+// by the client callback deleting the associated TrialComparisonCertVerifier.
+class TrialComparisonCertVerifier::Job {
  public:
-  TrialVerificationJob(const CertVerifier::Config& config,
-                       const CertVerifier::RequestParams& params,
-                       const NetLogWithSource& source_net_log,
-                       TrialComparisonCertVerifier* cert_verifier,
-                       int primary_error,
-                       const CertVerifyResult& primary_result)
-      : config_(config),
-        config_changed_(false),
-        params_(params),
-        net_log_(
-            NetLogWithSource::Make(source_net_log.net_log(),
-                                   NetLogSourceType::TRIAL_CERT_VERIFIER_JOB)),
-        cert_verifier_(cert_verifier),
-        primary_error_(primary_error),
-        primary_result_(primary_result) {
-    net_log_.BeginEvent(NetLogEventType::TRIAL_CERT_VERIFIER_JOB);
-    source_net_log.AddEventReferencingSource(
-        NetLogEventType::TRIAL_CERT_VERIFIER_JOB_COMPARISON_STARTED,
-        net_log_.source());
-  }
+  Job(const CertVerifier::Config& config,
+      const CertVerifier::RequestParams& params,
+      const NetLogWithSource& source_net_log,
+      TrialComparisonCertVerifier* parent);
+  ~Job();
 
-  ~TrialVerificationJob() {
-    if (cert_verifier_) {
-      net_log_.AddEvent(NetLogEventType::CANCELLED);
-      net_log_.EndEvent(NetLogEventType::TRIAL_CERT_VERIFIER_JOB);
-    }
-  }
+  // Start the Job, attempting first to verify with the parent's primary
+  // verifier. |client_result|, |client_callback|, and |client_request| are
+  // the parameters to the TrialComparisonCertVerifier::Verify(), allowing the
+  // caller to register interest in the primary results. |client_request| will
+  // be filled with a handle that the caller can use to abort the request.
+  int Start(CertVerifyResult* client_result,
+            CompletionOnceCallback client_callback,
+            std::unique_ptr<CertVerifier::Request>* client_request);
+  void OnConfigChanged();
 
-  void Start() {
-    // Unretained is safe because trial_request_ will cancel the callback on
-    // destruction.
-    int rv = cert_verifier_->trial_verifier()->Verify(
-        params_, &trial_result_,
-        base::BindOnce(&TrialVerificationJob::OnJobCompleted,
-                       base::Unretained(this)),
-        &trial_request_, net_log_);
-    if (rv != ERR_IO_PENDING)
-      OnJobCompleted(rv);
-  }
+ private:
+  class Request;
+  friend class Request;
 
-  void OnConfigChanged() { config_changed_ = true; }
+  // If the Job has not yet completed the primary verification, this can be
+  // called to indicate that the Request is no longer interested (e.g. the
+  // Request is being deleted).
+  void DetachRequest();
 
-  void Finish(bool is_success, TrialComparisonResult result_code) {
-    TrialComparisonCertVerifier* cert_verifier = cert_verifier_;
-    cert_verifier_ = nullptr;
+  void Finish(bool is_success, TrialComparisonResult result_code);
+  void FinishSuccess(TrialComparisonResult result_code);
+  void FinishWithError();
 
-    UMA_HISTOGRAM_ENUMERATION("Net.CertVerifier_TrialComparisonResult",
-                              result_code);
+  // Called when the primary verifier is completed.
+  // DANGER: |this| may be deleted when calling this.
+  void OnPrimaryJobCompleted(int result);
 
-    net_log_.EndEvent(NetLogEventType::TRIAL_CERT_VERIFIER_JOB, [&] {
-      return TrialVerificationJobResultParams(is_success);
-    });
-
-    if (!is_success) {
-      cert_verifier->report_callback_.Run(
-          params_.hostname(), params_.certificate(),
-          config_.enable_rev_checking,
-          config_.require_rev_checking_local_anchors,
-          config_.enable_sha1_local_anchors,
-          config_.disable_symantec_enforcement, primary_result_, trial_result_);
-    }
-
-    // |this| is deleted after RemoveJob returns.
-    cert_verifier->RemoveJob(this);
-  }
-
-  void FinishSuccess(TrialComparisonResult result_code) {
-    Finish(true /* is_success */, result_code);
-  }
-
-  void FinishWithError() {
-    DCHECK(trial_error_ != primary_error_ ||
-           !CertVerifyResultEqual(trial_result_, primary_result_));
-
-    TrialComparisonResult result_code = kInvalid;
-
-    if (primary_error_ == OK && trial_error_ == OK) {
-      result_code = kBothValidDifferentDetails;
-    } else if (primary_error_ == OK) {
-      result_code = kPrimaryValidSecondaryError;
-    } else if (trial_error_ == OK) {
-      result_code = kPrimaryErrorSecondaryValid;
-    } else {
-      result_code = kBothErrorDifferentDetails;
-    }
-    Finish(false /* is_success */, result_code);
-  }
-
-  void OnJobCompleted(int trial_result_error) {
-    DCHECK(primary_result_.verified_cert);
-    DCHECK(trial_result_.verified_cert);
-
-    trial_error_ = trial_result_error;
-
-    bool errors_equal = trial_result_error == primary_error_;
-    bool details_equal = CertVerifyResultEqual(trial_result_, primary_result_);
-    bool trial_success = errors_equal && details_equal;
-
-    if (trial_success) {
-      FinishSuccess(kEqual);
-      return;
-    }
+  // Called when the initial trial comparison is completed.
+  void OnTrialJobCompleted(int result);
 
 #if defined(OS_MACOSX)
-    if (primary_error_ == ERR_CERT_REVOKED && !config_.enable_rev_checking &&
-        !(primary_result_.cert_status & CERT_STATUS_REV_CHECKING_ENABLED) &&
-        !(trial_result_.cert_status &
-          (CERT_STATUS_REVOKED | CERT_STATUS_REV_CHECKING_ENABLED))) {
-      if (config_changed_) {
-        FinishSuccess(kIgnoredConfigurationChanged);
-        return;
-      }
-      // CertVerifyProcMac does some revocation checking even if we didn't want
-      // it. Try verifying with the trial verifier with revocation checking
-      // enabled, see if it then returns REVOKED.
-
-      int rv = cert_verifier_->revocation_trial_verifier()->Verify(
-          params_, &reverification_result_,
-          base::BindOnce(
-              &TrialVerificationJob::OnMacRevcheckingReverificationJobCompleted,
-              base::Unretained(this)),
-          &reverification_request_, net_log_);
-      if (rv != ERR_IO_PENDING)
-        OnMacRevcheckingReverificationJobCompleted(rv);
-      return;
-    }
+  // On some versions of macOS, revocation checking is always force-enabled
+  // for the system. For comparing with the built-in verifier to rule out
+  // "expected" differences, it's necessary to retry verification with
+  // revocation checking enabled, to match the (effective) configuration of
+  // the system verifier.
+  void OnMacRevCheckingReverificationJobCompleted(int result);
 #endif