diff --git a/DEPS b/DEPS
index fe1bb0db..b804e8b2 100644
--- a/DEPS
+++ b/DEPS
@@ -162,11 +162,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': 'e9d9a3278fdf6708942df94b80f0b269602bc580',
+  'skia_revision': '6be3ea7a7d72a6a76efcbedf67dbfde465b1a9d9',
   # 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': 'e576e7591361f0f32a9b02c8f67a63ef1963a33d',
+  'v8_revision': '6b93116584d01dc06c27975d3968739f2961b91d',
   # 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.
@@ -174,7 +174,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': '050b124d7838d8a13ae2d3c052fd1be9c369e37d',
+  'angle_revision': 'c3f7873b9a3af998ec6e9132f07f331f3f14d306',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -225,7 +225,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': '5198ea1a702a7284a9c02c692e45bcdbfd8be8ba',
+  'catapult_revision': 'e7c719c3e85f76938bf4fef0ba37c27f89246f71',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -297,7 +297,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '086835f3ffbf2cbdb63bafe120f23f36d3a08f35',
+  'dawn_revision': 'ce2adba679a3b2511f153a75c0f9ebff44d41bdb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -842,7 +842,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '24a3df72b9593a9d38d52b539d01f200e5069266',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ac9e322afcdccc237f4dcb0136420a0adeb3965f',
       'condition': 'checkout_linux',
   },
 
@@ -867,7 +867,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1db68ea0ba175a62889d15dc420c4bd481db2513',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f38bc1796282c61087dcf15abc61b8fd18a68402',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1455,7 +1455,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@bb6fc83a07776466007000dd877b5df3097d0217',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f4e32bb255038d6ca3969595daad3797292aeb4b',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index b19a6de..4711f1c 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -668,8 +668,8 @@
     const GURL& origin,
     base::OnceCallback<void(bool)> callback) {
   permission_request_handler_->SendRequest(
-      std::unique_ptr<AwPermissionRequestDelegate>(new SimplePermissionRequest(
-          origin, AwPermissionRequest::ProtectedMediaId, std::move(callback))));
+      std::make_unique<SimplePermissionRequest>(
+          origin, AwPermissionRequest::ProtectedMediaId, std::move(callback)));
 }
 
 void AwContents::CancelProtectedMediaIdentifierPermissionRequests(
@@ -691,8 +691,8 @@
     return;
   }
   permission_request_handler_->SendRequest(
-      std::unique_ptr<AwPermissionRequestDelegate>(new SimplePermissionRequest(
-          origin, AwPermissionRequest::Geolocation, std::move(callback))));
+      std::make_unique<SimplePermissionRequest>(
+          origin, AwPermissionRequest::Geolocation, std::move(callback)));
 }
 
 void AwContents::CancelGeolocationPermissionRequests(const GURL& origin) {
@@ -713,8 +713,8 @@
     const GURL& origin,
     base::OnceCallback<void(bool)> callback) {
   permission_request_handler_->SendRequest(
-      std::unique_ptr<AwPermissionRequestDelegate>(new SimplePermissionRequest(
-          origin, AwPermissionRequest::MIDISysex, std::move(callback))));
+      std::make_unique<SimplePermissionRequest>(
+          origin, AwPermissionRequest::MIDISysex, std::move(callback)));
 }
 
 void AwContents::CancelMIDISysexPermissionRequests(const GURL& origin) {
diff --git a/android_webview/browser/aw_devtools_server.cc b/android_webview/browser/aw_devtools_server.cc
index 81bbf47..59edbc4 100644
--- a/android_webview/browser/aw_devtools_server.cc
+++ b/android_webview/browser/aw_devtools_server.cc
@@ -49,7 +49,7 @@
             base::BindRepeating(&content::CanUserConnectToDevTools),
             true /* use_abstract_namespace */));
     if (socket->BindAndListen(socket_name_, kBackLog) != net::OK)
-      return std::unique_ptr<net::ServerSocket>();
+      return nullptr;
 
     return std::move(socket);
   }
@@ -63,7 +63,7 @@
             base::BindRepeating(&content::CanUserConnectToDevTools),
             true /* use_abstract_namespace */));
     if (socket->BindAndListen(*name, kBackLog) != net::OK)
-      return std::unique_ptr<net::ServerSocket>();
+      return nullptr;
 
     return std::move(socket);
   }
diff --git a/android_webview/browser/aw_web_contents_delegate.cc b/android_webview/browser/aw_web_contents_delegate.cc
index 91961fa..b983f69 100644
--- a/android_webview/browser/aw_web_contents_delegate.cc
+++ b/android_webview/browser/aw_web_contents_delegate.cc
@@ -278,12 +278,12 @@
     std::move(callback).Run(
         blink::MediaStreamDevices(),
         blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
-        std::unique_ptr<content::MediaStreamUI>());
+        nullptr);
     return;
   }
   aw_contents->GetPermissionRequestHandler()->SendRequest(
-      std::unique_ptr<AwPermissionRequestDelegate>(
-          new MediaAccessPermissionRequest(request, std::move(callback))));
+      std::make_unique<MediaAccessPermissionRequest>(request,
+                                                     std::move(callback)));
 }
 
 void AwWebContentsDelegate::EnterFullscreenModeForTab(
diff --git a/ash/app_list/model/folder_image.cc b/ash/app_list/model/folder_image.cc
index 23da1f9..1f5e2e79 100644
--- a/ash/app_list/model/folder_image.cc
+++ b/ash/app_list/model/folder_image.cc
@@ -21,6 +21,7 @@
 #include "ui/gfx/image/canvas_image_source.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/scoped_canvas.h"
 
 namespace app_list {
 
@@ -75,17 +76,27 @@
       icon, skia::ImageOperations::RESIZE_BEST, icon_size));
 
   // Draw a shadowed icon on the specified location.
+  const gfx::ShadowValues shadow = {
+      gfx::ShadowValue(gfx::Vector2d(), kIconShadowBlur, kIconShadowColor)};
   const gfx::ImageSkia shadowed(
-      gfx::ImageSkiaOperations::CreateImageWithDropShadow(
-          resized, gfx::ShadowValues(
-                       1, gfx::ShadowValue(gfx::Vector2d(), kIconShadowBlur,
-                                           kIconShadowColor))));
-  const gfx::Size shadow_size = shadowed.size();
-  x -= (shadow_size.width() - icon_size.width()) / 2;
-  y -= (shadow_size.height() - icon_size.height()) / 2;
-  canvas->DrawImageInt(shadowed, 0, 0, shadow_size.width(),
-                       shadow_size.height(), x, y, shadow_size.width(),
-                       shadow_size.height(), true);
+      gfx::ImageSkiaOperations::CreateImageWithDropShadow(resized, shadow));
+
+  // Offset the shadowed image so the actual image position matches its original
+  // bounds. The offset has to be calculated in pixels, as the shadow margin
+  // might not match the 1x margin scaled to the target scale factor (when the
+  // drawing the shadow, the shadow margin is first scaled then rounded, which
+  // might introduce different rounding error depending on the scale factor).
+  gfx::ScopedCanvas scoped_canvas(canvas);
+  const float scale = canvas->UndoDeviceScaleFactor();
+
+  gfx::Insets shadow_margin =
+      gfx::ShadowValue::GetMargin({shadow[0].Scale(scale)});
+  const gfx::ImageSkiaRep& shadowed_rep = shadowed.GetRepresentation(scale);
+
+  canvas->DrawImageIntInPixel(
+      shadowed_rep, x * scale + shadow_margin.left(),
+      y * scale + shadow_margin.top(), shadowed_rep.pixel_width(),
+      shadowed_rep.pixel_height(), true, cc::PaintFlags());
 }
 
 void FolderImageSource::Draw(gfx::Canvas* canvas) {
diff --git a/ash/display/window_tree_host_manager_unittest.cc b/ash/display/window_tree_host_manager_unittest.cc
index 3e3cd2e..a106420 100644
--- a/ash/display/window_tree_host_manager_unittest.cc
+++ b/ash/display/window_tree_host_manager_unittest.cc
@@ -1418,6 +1418,7 @@
     shelf_display_bounds_ = screen_util::GetDisplayBoundsWithShelf(window);
   }
 
+  // Returns the shelf display bounds, in screen coordinates.
   const gfx::Rect& shelf_display_bounds() const {
     return shelf_display_bounds_;
   }
@@ -1460,7 +1461,8 @@
 
   display_info_list.push_back(new_first_display_info);
   display_manager()->OnNativeDisplaysChanged(display_info_list);
-  EXPECT_EQ("0,0 500x500", test_observer.shelf_display_bounds().ToString());
+  // The shelf is now on the second display.
+  EXPECT_EQ("400,0 500x500", test_observer.shelf_display_bounds().ToString());
   primary_root->RemoveObserver(&test_observer);
 }
 
diff --git a/ash/public/cpp/app_list/internal_app_id_constants.h b/ash/public/cpp/app_list/internal_app_id_constants.h
index 809885c6..27c49d8 100644
--- a/ash/public/cpp/app_list/internal_app_id_constants.h
+++ b/ash/public/cpp/app_list/internal_app_id_constants.h
@@ -28,11 +28,6 @@
 // Generated as crx_file::id_util::GenerateId("org.chromium.discover").
 constexpr char kInternalAppIdDiscover[] = "pjdncmlmjhcebmcacdddfacepcjmfaoo";
 
-// Generated as
-// web_app::GenerateAppIdFromURL(GURL(
-// "https://google.com/chromebook/whatsnew/embedded/")).
-constexpr char kReleaseNotesAppId[] = "kddjchdmnnpakappplfnloipgcbioilo";
-
 }  // namespace app_list
 
 #endif  // ASH_PUBLIC_CPP_APP_LIST_INTERNAL_APP_ID_CONSTANTS_H_
diff --git a/ash/screen_util.cc b/ash/screen_util.cc
index 1514b4b..8b0c164 100644
--- a/ash/screen_util.cc
+++ b/ash/screen_util.cc
@@ -79,8 +79,11 @@
 }
 
 gfx::Rect GetDisplayBoundsWithShelf(aura::Window* window) {
-  if (!Shell::Get()->display_manager()->IsInUnifiedMode())
-    return window->GetRootWindow()->bounds();
+  if (!Shell::Get()->display_manager()->IsInUnifiedMode()) {
+    return display::Screen::GetScreen()
+        ->GetDisplayNearestWindow(window)
+        .bounds();
+  }
 
   // In Unified Mode, the display that should contain the shelf depends on the
   // current shelf alignment.
diff --git a/ash/shelf/overflow_bubble_view.cc b/ash/shelf/overflow_bubble_view.cc
index 35dbf0b..8012d19e 100644
--- a/ash/shelf/overflow_bubble_view.cc
+++ b/ash/shelf/overflow_bubble_view.cc
@@ -406,16 +406,9 @@
   shelf_view()->layer()->SetTransform(current_transform);
 }
 
-gfx::Size OverflowBubbleView::CalculatePreferredSize() const {
-  gfx::Rect monitor_rect =
-      display::Screen::GetScreen()
-          ->GetDisplayNearestPoint(GetAnchorRect().CenterPoint())
-          .work_area();
-  monitor_rect.Inset(gfx::Insets(kMinimumMargin));
-  int available_length = GetShelf()->IsHorizontalAlignment()
-                             ? monitor_rect.width()
-                             : monitor_rect.height();
-
+void OverflowBubbleView::UpdateLayoutStrategy() {
+  const int available_length =
+      GetShelf()->IsHorizontalAlignment() ? width() : height();
   gfx::Size preferred_size = shelf_container_view_->GetPreferredSize();
   int preferred_length = GetShelf()->IsHorizontalAlignment()
                              ? preferred_size.width()
@@ -439,6 +432,37 @@
     // There are invisible shelf buttons at both sides. So show two buttons.
     layout_strategy_ = SHOW_BUTTONS;
   }
+}
+
+void OverflowBubbleView::ScrollToNewPage(bool forward) {
+  // Implement the arrow button handler in the same way with scrolling the
+  // bubble view. The key is to calculate the suitable scroll distance.
+  int offset = (GetShelf()->IsHorizontalAlignment() ? bounds().width()
+                                                    : bounds().height()) -
+               2 * GetUnit();
+  DCHECK_GT(offset, 0);
+
+  if (!forward)
+    offset = -offset;
+
+  if (GetShelf()->IsHorizontalAlignment())
+    ScrollByXOffset(offset, true);
+  else
+    ScrollByYOffset(offset, true);
+}
+
+gfx::Size OverflowBubbleView::CalculatePreferredSize() const {
+  gfx::Rect monitor_rect =
+      display::Screen::GetScreen()
+          ->GetDisplayNearestPoint(GetAnchorRect().CenterPoint())
+          .work_area();
+  monitor_rect.Inset(gfx::Insets(kMinimumMargin));
+
+  gfx::Size preferred_size = shelf_container_view_->GetPreferredSize();
+  int preferred_length = GetShelf()->IsHorizontalAlignment()
+                             ? preferred_size.width()
+                             : preferred_size.height();
+  preferred_length += 2 * kEndPadding;
 
   if (GetShelf()->IsHorizontalAlignment()) {
     preferred_size.set_width(std::min(preferred_length, monitor_rect.width()));
@@ -450,6 +474,8 @@
 }
 
 void OverflowBubbleView::Layout() {
+  UpdateLayoutStrategy();
+
   const gfx::Size shelf_button_size(ShelfConstants::button_size(),
                                     ShelfConstants::button_size());
   const gfx::Size arrow_button_size(GetArrowButtonSize(), GetArrowButtonSize());
@@ -567,22 +593,7 @@
   views::View* sender_view = sender;
   DCHECK((sender_view == left_arrow_) || (sender_view == right_arrow_));
 
-  // Implement the arrow button handler in the same way with scrolling the
-  // bubble view. The key is to calculate the suitable scroll distance.
-  int offset = (GetShelf()->IsHorizontalAlignment() ? bounds().width()
-                                                    : bounds().height()) -
-               2 * GetUnit();
-  DCHECK_GT(offset, 0);
-
-  // If |forward| is true, scroll the overflow bubble view rightward.
-  bool forward = sender_view == right_arrow_;
-  if (!forward)
-    offset = -offset;
-
-  if (GetShelf()->IsHorizontalAlignment())
-    ScrollByXOffset(offset, true);
-  else
-    ScrollByYOffset(offset, true);
+  ScrollToNewPage(sender_view == right_arrow_);
 }
 
 void OverflowBubbleView::OnScrollEvent(ui::ScrollEvent* event) {
diff --git a/ash/shelf/overflow_bubble_view.h b/ash/shelf/overflow_bubble_view.h
index 13208f2..dfe4877d 100644
--- a/ash/shelf/overflow_bubble_view.h
+++ b/ash/shelf/overflow_bubble_view.h
@@ -102,6 +102,13 @@
   // Creates the animation for scrolling shelf by |scroll_distance|.
   void StartShelfScrollAnimation(float scroll_distance);
 
+  // Update the layout strategy based on the available space.
+  void UpdateLayoutStrategy();
+
+  // Scrolls to a new page of shelf icons. |forward| indicates whether the next
+  // page or previous page is shown.
+  void ScrollToNewPage(bool forward);
+
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
   void Layout() override;
diff --git a/ash/shelf/shelf_constants.h b/ash/shelf/shelf_constants.h
index 13a791d..3de417b9 100644
--- a/ash/shelf/shelf_constants.h
+++ b/ash/shelf/shelf_constants.h
@@ -6,6 +6,7 @@
 #define ASH_SHELF_SHELF_CONSTANTS_H_
 
 #include "ash/ash_export.h"
+#include "base/macros.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 1c6840f..1ff2dd0 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -650,9 +650,7 @@
 void ShelfLayoutManager::OnOverviewModeStartingAnimationComplete(
     bool canceled) {
   suspend_visibility_update_ = false;
-  UpdateVisibilityState();
-  LayoutShelf();
-  MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
+  OnVisibilityUpdateResumed(/*animate=*/true);
 }
 
 void ShelfLayoutManager::OnOverviewModeEnding(
@@ -662,9 +660,7 @@
 
 void ShelfLayoutManager::OnOverviewModeEndingAnimationComplete(bool canceled) {
   suspend_visibility_update_ = false;
-  UpdateVisibilityState();
-  LayoutShelf();
-  MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
+  OnVisibilityUpdateResumed(/*animate=*/true);
 }
 
 void ShelfLayoutManager::OnAppListVisibilityChanged(bool shown,
@@ -782,9 +778,7 @@
 void ShelfLayoutManager::OnScreenCopiedBeforeRotation() {
   if (suspend_visibility_update_) {
     suspend_visibility_update_ = false;
-    UpdateVisibilityState();
-    LayoutShelf();
-    MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE);
+    OnVisibilityUpdateResumed(/*animate=*/false);
   }
 }
 
@@ -978,8 +972,6 @@
       status_animation_setter.AddObserver(observer);
 
     gfx::Rect shelf_bounds = target_bounds.shelf_bounds;
-    ::wm::ConvertRectToScreen(shelf_widget_->GetNativeWindow()->parent(),
-                              &shelf_bounds);
     shelf_widget_->SetBounds(shelf_bounds);
 
     GetLayer(nav_widget)->SetOpacity(target_bounds.opacity);
@@ -999,23 +991,17 @@
 
     gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
     status_bounds.Offset(target_bounds.shelf_bounds.OffsetFromOrigin());
-    ::wm::ConvertRectToScreen(status_widget->GetNativeWindow()->parent(),
-                              &status_bounds);
     status_widget->SetBounds(status_bounds);
 
     gfx::Vector2d nav_offset = target_bounds.shelf_bounds.OffsetFromOrigin();
     gfx::Rect nav_bounds = target_bounds.nav_bounds_in_shelf;
     nav_bounds.Offset(nav_offset);
-    ::wm::ConvertRectToScreen(nav_widget->GetNativeWindow()->parent(),
-                              &nav_bounds);
     nav_widget->SetBounds(nav_bounds);
 
     gfx::Vector2d hotseat_offset =
         target_bounds.shelf_bounds.OffsetFromOrigin();
     gfx::Rect hotseat_bounds = target_bounds.hotseat_bounds_in_shelf;
     hotseat_bounds.Offset(hotseat_offset);
-    ::wm::ConvertRectToScreen(hotseat_widget->GetNativeWindow()->parent(),
-                              &hotseat_bounds);
     hotseat_widget->SetBounds(hotseat_bounds);
 
     // Do not update the work area when the alignment changes to BOTTOM_LOCKED
@@ -1908,4 +1894,17 @@
   previous_workspace_window_state_ = current_workspace_window_state;
 }
 
+void ShelfLayoutManager::OnVisibilityUpdateResumed(bool animate) {
+  DCHECK(!suspend_visibility_update_);
+
+  UpdateVisibilityState();
+
+  TargetBounds target_bounds;
+  CalculateTargetBoundsAndUpdateWorkArea(&target_bounds);
+  UpdateBoundsAndOpacity(target_bounds, animate, nullptr);
+
+  MaybeUpdateShelfBackground(animate ? AnimationChangeType::ANIMATE
+                                     : AnimationChangeType::IMMEDIATE);
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index dd4d79a39..d46329a 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -402,6 +402,9 @@
   void SendA11yAlertForFullscreenWorkspaceState(
       WorkspaceWindowState current_workspace_window_state);
 
+  // Invoked after |suspend_visibility_update_| resets.
+  void OnVisibilityUpdateResumed(bool animate);
+
   // True when inside UpdateBoundsAndOpacity() method. Used to prevent calling
   // UpdateBoundsAndOpacity() again from SetChildBounds().
   bool updating_bounds_ = false;
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 0e8b0ce..d9dcdaa 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -3266,7 +3266,7 @@
   EXPECT_EQ(0, observer.metrics_change_count());
 }
 
-// Tests that shelf bounds is updated properly after overview animation.
+// Tests that shelf bounds are updated properly after overview animation.
 TEST_F(ShelfLayoutManagerTest, ShelfBoundsUpdateAfterOverviewAnimation) {
   // Run overview animations.
   ui::ScopedAnimationDurationScaleMode regular_animations(
@@ -3277,6 +3277,13 @@
   const gfx::Rect bottom_shelf_bounds =
       GetShelfWidget()->GetWindowBoundsInScreen();
 
+  const int shelf_size = bottom_shelf_bounds.height();
+  const gfx::Rect display_bounds =
+      display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
+  const gfx::Rect left_shelf_bounds =
+      gfx::Rect(display_bounds.x(), display_bounds.y(), shelf_size,
+                display_bounds.height());
+
   // Change alignment during overview enter animation.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
   {
@@ -3285,7 +3292,8 @@
     shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
     waiter.Wait();
   }
-  EXPECT_NE(bottom_shelf_bounds, GetShelfWidget()->GetWindowBoundsInScreen());
+  ShelfAnimationWaiter(left_shelf_bounds).WaitTillDoneAnimating();
+  EXPECT_EQ(left_shelf_bounds, GetShelfWidget()->GetWindowBoundsInScreen());
 
   // Change alignment during overview exit animation.
   {
@@ -3294,7 +3302,54 @@
     shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
     waiter.Wait();
   }
+  ShelfAnimationWaiter(bottom_shelf_bounds).WaitTillDoneAnimating();
   EXPECT_EQ(bottom_shelf_bounds, GetShelfWidget()->GetWindowBoundsInScreen());
 }
 
+// Tests that the shelf on a second display is properly centered.
+TEST_F(ShelfLayoutManagerTest, ShelfRemainsCenteredOnSecondDisplay) {
+  // Create two displays.
+  UpdateDisplay("600x400,1000x700");
+  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
+  EXPECT_EQ(2U, root_windows.size());
+
+  Shelf* shelf_1 = Shelf::ForWindow(root_windows[0]);
+  Shelf* shelf_2 = Shelf::ForWindow(root_windows[1]);
+  EXPECT_NE(shelf_1, shelf_2);
+  EXPECT_NE(shelf_1->GetWindow()->GetRootWindow(),
+            shelf_2->GetWindow()->GetRootWindow());
+
+  ShelfView* shelf_view_1 = shelf_1->GetShelfViewForTesting();
+  ShelfView* shelf_view_2 = shelf_2->GetShelfViewForTesting();
+
+  const display::Display display_1 =
+      display::Screen::GetScreen()->GetPrimaryDisplay();
+  const display::Display display_2 =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]);
+  EXPECT_NE(display_1, display_2);
+
+  // Add an app shortcut.
+  ShelfController* controller = Shell::Get()->shelf_controller();
+  const std::string app_id("app_id");
+  ShelfItem item;
+  item.type = TYPE_PINNED_APP;
+  item.id = ShelfID(app_id);
+  controller->model()->Add(item);
+  gfx::Point app_center_1 = shelf_1->GetShelfViewForTesting()
+                                ->first_visible_button_for_testing()
+                                ->bounds()
+                                .CenterPoint();
+  views::View::ConvertPointToScreen(shelf_view_1, &app_center_1);
+
+  gfx::Point app_center_2 = shelf_2->GetShelfViewForTesting()
+                                ->first_visible_button_for_testing()
+                                ->bounds()
+                                .CenterPoint();
+  views::View::ConvertPointToScreen(shelf_view_2, &app_center_2);
+
+  // The app icon should be at the horizontal center of each display.
+  EXPECT_EQ(display_1.bounds().CenterPoint().x(), app_center_1.x());
+  EXPECT_EQ(display_2.bounds().CenterPoint().x(), app_center_2.x());
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index cea3c03d..082565a1 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -946,11 +946,10 @@
   // ScrollableShelfView.
   if (!is_overflow_mode() && !chromeos::switches::ShouldShowScrollableShelf()) {
     // Now add the necessary padding to center app icons.
-    const gfx::Size screen_size =
-        screen_util::GetDisplayBoundsWithShelf(GetWidget()->GetNativeWindow())
-            .size();
-    const int screen_size_primary =
-        shelf()->PrimaryAxisValue(screen_size.width(), screen_size.height());
+    const gfx::Rect display_bounds =
+        screen_util::GetDisplayBoundsWithShelf(GetWidget()->GetNativeWindow());
+    const int display_size_primary = shelf()->PrimaryAxisValue(
+        display_bounds.size().width(), display_bounds.size().height());
 
     const int available_size_for_app_icons = GetAvailableSpaceForAppIcons();
     const int icons_size = GetSizeOfAppIcons(number_of_visible_apps(),
@@ -959,14 +958,15 @@
 
     if (app_centering_strategy.center_on_screen) {
       // This is how far the first icon needs to be from the screen edge.
-      padding_for_centering = (screen_size_primary - icons_size) / 2;
+      padding_for_centering = (display_size_primary - icons_size) / 2;
 
-      // Let's see how far this view is from the edge of the screen to
+      // Let's see how far this view is from the edge of this display to
       // compute how much extra padding is needed.
       gfx::Point origin = gfx::Point(0, 0);
       views::View::ConvertPointToScreen(this, &origin);
-
-      padding_for_centering -= origin.x();
+      padding_for_centering -= shelf_->IsHorizontalAlignment()
+                                   ? (origin.x() - display_bounds.x())
+                                   : (origin.y() - display_bounds.y());
     } else {
       padding_for_centering =
           (available_size_for_app_icons - icons_size) / 2;
diff --git a/ash/style/ash_color_provider.cc b/ash/style/ash_color_provider.cc
index 863292b..eee9bef 100644
--- a/ash/style/ash_color_provider.cc
+++ b/ash/style/ash_color_provider.cc
@@ -25,6 +25,9 @@
 // The disabled color is always 38% opacity of the enabled color.
 constexpr float kDisabledColorOpacity = 0.38f;
 
+// Color of second tone is always 30% opacity of the color of first tone.
+constexpr float kSecondToneOpacity = 0.3f;
+
 // Gets the color mode value from feature flag "--ash-color-mode".
 AshColorProvider::AshColorMode GetColorModeFromCommandLine() {
   const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
@@ -55,6 +58,19 @@
   return Shell::Get()->ash_color_provider();
 }
 
+// static
+SkColor AshColorProvider::GetDisabledColor(SkColor enabled_color) {
+  return SkColorSetA(enabled_color, std::round(SkColorGetA(enabled_color) *
+                                               kDisabledColorOpacity));
+}
+
+// static
+SkColor AshColorProvider::GetSecondToneColor(SkColor color_of_first_tone) {
+  return SkColorSetA(
+      color_of_first_tone,
+      std::round(SkColorGetA(color_of_first_tone) * kSecondToneOpacity));
+}
+
 SkColor AshColorProvider::DeprecatedGetShieldLayerColor(
     ShieldLayerType type,
     SkColor default_color) const {
@@ -136,11 +152,6 @@
   return RippleAttributes(base_color, opacity, opacity);
 }
 
-SkColor AshColorProvider::GetDisabledColor(SkColor enabled_color) const {
-  return SkColorSetA(enabled_color, std::round(SkColorGetA(enabled_color) *
-                                               kDisabledColorOpacity));
-}
-
 SkColor AshColorProvider::GetShieldLayerColorImpl(
     ShieldLayerType type,
     AshColorMode color_mode) const {
diff --git a/ash/style/ash_color_provider.h b/ash/style/ash_color_provider.h
index 40a03fe..ff33dd82 100644
--- a/ash/style/ash_color_provider.h
+++ b/ash/style/ash_color_provider.h
@@ -97,6 +97,14 @@
 
   static AshColorProvider* Get();
 
+  // Gets the disabled color on |enabled_color|. It can be disabled background,
+  // an disabled icon, etc.
+  static SkColor GetDisabledColor(SkColor enabled_color);
+
+  // Gets the color of second tone on the given |color_of_first_tone|. e.g,
+  // power status icon inside status area is a dual tone icon.
+  static SkColor GetSecondToneColor(SkColor color_of_first_tone);
+
   // Gets color of Shield layer. See details at the corresponding function of
   // Base layer.
   SkColor DeprecatedGetShieldLayerColor(ShieldLayerType type,
@@ -135,10 +143,6 @@
   // color of the UI element that wants to show inkdrop.
   RippleAttributes GetRippleAttributes(SkColor bg_color) const;
 
-  // Gets the disabled color on |enabled_color|. It can be disabled background,
-  // an disabled icon, etc.
-  SkColor GetDisabledColor(SkColor enabled_color) const;
-
   AshColorMode color_mode() const { return color_mode_; }
 
  private:
diff --git a/ash/system/message_center/notifier_settings_view.cc b/ash/system/message_center/notifier_settings_view.cc
index ad85f94..8df356b6 100644
--- a/ash/system/message_center/notifier_settings_view.cc
+++ b/ash/system/message_center/notifier_settings_view.cc
@@ -517,7 +517,7 @@
   } else {
     quiet_mode_icon_->SetImage(gfx::CreateVectorIcon(
         kNotificationCenterDoNotDisturbOffIcon, kMenuIconSize,
-        AshColorProvider::Get()->GetDisabledColor(icon_color)));
+        AshColorProvider::GetDisabledColor(icon_color)));
   }
 }
 
diff --git a/ash/system/network/network_list_view.cc b/ash/system/network/network_list_view.cc
index 3fdba3c..0d14b86 100644
--- a/ash/system/network/network_list_view.cc
+++ b/ash/system/network/network_list_view.cc
@@ -385,14 +385,14 @@
     return nullptr;
 
   views::ImageView* icon = new views::ImageView;
+  const SkColor icon_color = GetIconColor();
   icon->SetPreferredSize(gfx::Size(kMenuIconSize, kMenuIconSize));
   icon->EnableCanvasFlippingForRTLUI(true);
   PowerStatus::BatteryImageInfo icon_info;
   icon_info.charge_percent = info.battery_percentage;
   icon->SetImage(PowerStatus::GetBatteryImage(
       icon_info, kMobileNetworkBatteryIconSize,
-      AshColorProvider::Get()->GetDisabledColor(GetIconColor()),
-      GetIconColor()));
+      AshColorProvider::GetSecondToneColor(icon_color), icon_color));
 
   // Show the numeric battery percentage on hover.
   icon->set_tooltip_text(base::FormatPercent(info.battery_percentage));
diff --git a/ash/system/power/power_status.cc b/ash/system/power/power_status.cc
index 78a100a..3ff623e 100644
--- a/ash/system/power/power_status.cc
+++ b/ash/system/power/power_status.cc
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/power_utils.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/style/ash_color_provider.h"
 #include "base/i18n/number_formatting.h"
 #include "base/i18n/time_formatting.h"
 #include "base/logging.h"
@@ -44,10 +45,6 @@
 // The color of the battery's badge (bolt, unreliable, X).
 const SkColor kBatteryBadgeColor = gfx::kGoogleGrey900;
 
-// The color used for the battery's badge and charged color when the battery
-// charge level is critically low and the device is not plugged in.
-const SkColor kBatteryAlertColor = gfx::kGoogleRed300;
-
 class BatteryImageSource : public gfx::CanvasImageSource {
  public:
   BatteryImageSource(const PowerStatus::BatteryImageInfo& info,
@@ -108,9 +105,12 @@
                          size().height() * dsf);
     canvas->ClipRect(clip_rect);
 
+    const SkColor alert_color = AshColorProvider::Get()->GetContentLayerColor(
+        AshColorProvider::ContentLayerType::kIconRed,
+        AshColorProvider::AshColorMode::kDark);
     const bool use_alert_color =
         charge_level == min_charge_level && info_.alert_if_low;
-    flags.setColor(use_alert_color ? kBatteryAlertColor : fg_color_);
+    flags.setColor(use_alert_color ? alert_color : fg_color_);
     canvas->DrawPath(path, flags);
 
     canvas->Restore();
@@ -118,7 +118,7 @@
     // Paint the badge over top of the battery, if applicable.
     if (info_.icon_badge) {
       const SkColor badge_color =
-          use_alert_color ? kBatteryAlertColor : kBatteryBadgeColor;
+          use_alert_color ? alert_color : kBatteryBadgeColor;
       PaintVectorIcon(canvas, *info_.icon_badge, badge_color);
     }
   }
diff --git a/ash/system/power/power_status_unittest.cc b/ash/system/power/power_status_unittest.cc
index bdc63df8..5352ffd 100644
--- a/ash/system/power/power_status_unittest.cc
+++ b/ash/system/power/power_status_unittest.cc
@@ -7,10 +7,8 @@
 #include <memory>
 
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/test/ash_test_base.h"
 #include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "chromeos/dbus/power/power_manager_client.h"
-#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_unittest_util.h"
@@ -38,16 +36,13 @@
 
 }  // namespace
 
-class PowerStatusTest : public testing::Test {
+class PowerStatusTest : public AshTestBase {
  public:
-  PowerStatusTest()
-      : task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
-        power_status_(NULL) {}
+  PowerStatusTest() = default;
   ~PowerStatusTest() override = default;
 
   void SetUp() override {
-    chromeos::PowerManagerClient::InitializeFake();
-    PowerStatus::Initialize();
+    AshTestBase::SetUp();
     power_status_ = PowerStatus::Get();
     test_observer_.reset(new TestObserver);
     power_status_->AddObserver(test_observer_.get());
@@ -56,13 +51,11 @@
   void TearDown() override {
     power_status_->RemoveObserver(test_observer_.get());
     test_observer_.reset();
-    PowerStatus::Shutdown();
-    chromeos::PowerManagerClient::Shutdown();
+    AshTestBase::TearDown();
   }
 
  protected:
-  base::test::TaskEnvironment task_environment_;
-  PowerStatus* power_status_;  // Not owned.
+  PowerStatus* power_status_ = nullptr;  // Not owned.
   std::unique_ptr<TestObserver> test_observer_;
 
  private:
diff --git a/ash/system/power/tray_power.cc b/ash/system/power/tray_power.cc
index ed90bf1f..26c7170 100644
--- a/ash/system/power/tray_power.cc
+++ b/ash/system/power/tray_power.cc
@@ -12,6 +12,7 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/style/ash_color_provider.h"
 #include "ash/system/power/battery_notification.h"
 #include "ash/system/power/dual_role_notification.h"
 #include "ash/system/time/time_view.h"
@@ -110,8 +111,9 @@
   icon_session_state_color_ = session_state;
 
   // Note: The icon color (both fg and bg) changes when the UI in in OOBE mode.
-  SkColor icon_fg_color = TrayIconColor(session_state);
-  SkColor icon_bg_color = SkColorSetA(icon_fg_color, kTrayIconBackgroundAlpha);
+  const SkColor icon_fg_color = TrayIconColor(session_state);
+  const SkColor icon_bg_color =
+      AshColorProvider::GetSecondToneColor(icon_fg_color);
   image_view()->SetImage(PowerStatus::GetBatteryImage(
       info, kUnifiedTrayIconSize, icon_bg_color, icon_fg_color));
 }
diff --git a/ash/system/tray/system_menu_button.cc b/ash/system/tray/system_menu_button.cc
index 0af2364c..794c26db 100644
--- a/ash/system/tray/system_menu_button.cc
+++ b/ash/system/tray/system_menu_button.cc
@@ -55,7 +55,7 @@
            gfx::CreateVectorIcon(icon, icon_color));
   SetImage(views::Button::STATE_DISABLED,
            gfx::CreateVectorIcon(
-               icon, AshColorProvider::Get()->GetDisabledColor(icon_color)));
+               icon, AshColorProvider::GetDisabledColor(icon_color)));
 }
 
 SystemMenuButton::~SystemMenuButton() = default;
diff --git a/ash/system/tray/tray_constants.cc b/ash/system/tray/tray_constants.cc
index 5e7abf2..6ea396a5 100644
--- a/ash/system/tray/tray_constants.cc
+++ b/ash/system/tray/tray_constants.cc
@@ -31,10 +31,6 @@
 
 const int kTrayToggleButtonWidth = 68;
 
-// Note that the alpha value should match kSignalStrengthImageBgAlpha in
-// ash/public/cpp/network_icon_image_source.cc
-const int kTrayIconBackgroundAlpha = 0x4D /* 30% */;
-
 const int kMenuIconSize = 20;
 const int kMenuButtonSize = 48;
 const int kMenuSeparatorVerticalPadding = 4;
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 1d0ed74..0ef3675 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -74,7 +74,6 @@
 
 // The size of the icons appearing in the material design system tray.
 constexpr int kTrayIconSize = 16;
-extern const int kTrayIconBackgroundAlpha;
 
 // The padding around network tray icon in dip.
 constexpr int kTrayNetworkIconPadding = 2;
diff --git a/ash/system/unified/collapse_button.cc b/ash/system/unified/collapse_button.cc
index 690549d2..699c284 100644
--- a/ash/system/unified/collapse_button.cc
+++ b/ash/system/unified/collapse_button.cc
@@ -69,9 +69,8 @@
   SetImage(views::Button::STATE_NORMAL,
            gfx::CreateVectorIcon(
                kUnifiedMenuExpandIcon,
-               GetEnabled()
-                   ? icon_color
-                   : AshColorProvider::Get()->GetDisabledColor(icon_color)));
+               GetEnabled() ? icon_color
+                            : AshColorProvider::GetDisabledColor(icon_color)));
 }
 
 }  // namespace ash
diff --git a/ash/system/unified/custom_shape_button.cc b/ash/system/unified/custom_shape_button.cc
index ef115d3e..58999d712 100644
--- a/ash/system/unified/custom_shape_button.cc
+++ b/ash/system/unified/custom_shape_button.cc
@@ -63,7 +63,7 @@
           kUnifiedMenuButtonColor);
   flags.setColor(GetEnabled()
                      ? button_color
-                     : AshColorProvider::Get()->GetDisabledColor(button_color));
+                     : AshColorProvider::GetDisabledColor(button_color));
   flags.setStyle(cc::PaintFlags::kFill_Style);
 
   canvas->DrawPath(CreateCustomShapePath(GetLocalBounds()), flags);
diff --git a/ash/system/unified/feature_pod_button.cc b/ash/system/unified/feature_pod_button.cc
index f8d23cc0..0dd5fbd6 100644
--- a/ash/system/unified/feature_pod_button.cc
+++ b/ash/system/unified/feature_pod_button.cc
@@ -65,17 +65,18 @@
   cc::PaintFlags flags;
   flags.setAntiAlias(true);
 
-  SkColor color = AshColorProvider::Get()->DeprecatedGetControlsLayerColor(
+  const AshColorProvider* color_provider = AshColorProvider::Get();
+  SkColor color = color_provider->DeprecatedGetControlsLayerColor(
       AshColorProvider::ControlsLayerType::kInactiveControlBackground,
       kUnifiedMenuButtonColor);
   if (GetEnabled()) {
     if (toggled_) {
-      color = AshColorProvider::Get()->DeprecatedGetControlsLayerColor(
+      color = color_provider->DeprecatedGetControlsLayerColor(
           AshColorProvider::ControlsLayerType::kActiveControlBackground,
           kUnifiedMenuButtonColorActive);
     }
   } else {
-    color = AshColorProvider::Get()->GetDisabledColor(color);
+    color = AshColorProvider::GetDisabledColor(color);
   }
   flags.setColor(color);
 
@@ -239,27 +240,25 @@
 }
 
 void FeaturePodLabelButton::OnEnabledChanged() {
+  const AshColorProvider* color_provider = AshColorProvider::Get();
   const SkColor primary_text_color =
-      AshColorProvider::Get()->DeprecatedGetContentLayerColor(
+      color_provider->DeprecatedGetContentLayerColor(
           ContentLayerType::kTextPrimary, kUnifiedMenuTextColor);
-  const SkColor secondary_text_color =
-      AshColorProvider::Get()->GetContentLayerColor(
-          ContentLayerType::kTextSecondary, AshColorMode::kDark);
+  const SkColor secondary_text_color = color_provider->GetContentLayerColor(
+      ContentLayerType::kTextSecondary, AshColorMode::kDark);
   label_->SetEnabledColor(
-      GetEnabled()
-          ? primary_text_color
-          : AshColorProvider::Get()->GetDisabledColor(primary_text_color));
+      GetEnabled() ? primary_text_color
+                   : AshColorProvider::GetDisabledColor(primary_text_color));
   sub_label_->SetEnabledColor(
-      GetEnabled()
-          ? secondary_text_color
-          : AshColorProvider::Get()->GetDisabledColor(secondary_text_color));
+      GetEnabled() ? secondary_text_color
+                   : AshColorProvider::GetDisabledColor(secondary_text_color));
 
-  const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
+  const SkColor icon_color = color_provider->GetContentLayerColor(
       ContentLayerType::kIconPrimary, AshColorMode::kDark);
   detailed_view_arrow_->SetImage(gfx::CreateVectorIcon(
       kUnifiedMenuMoreIcon,
       GetEnabled() ? icon_color
-                   : AshColorProvider::Get()->GetDisabledColor(icon_color)));
+                   : AshColorProvider::GetDisabledColor(icon_color)));
 }
 
 void FeaturePodLabelButton::LayoutInCenter(views::View* child, int y) {
@@ -303,8 +302,8 @@
                          gfx::CreateVectorIcon(icon, icon_color));
   icon_button_->SetImage(
       views::Button::STATE_DISABLED,
-      gfx::CreateVectorIcon(
-          icon, AshColorProvider::Get()->GetDisabledColor(icon_color)));
+      gfx::CreateVectorIcon(icon,
+                            AshColorProvider::GetDisabledColor(icon_color)));
 }
 
 void FeaturePodButton::SetLabel(const base::string16& label) {
diff --git a/ash/system/unified/page_indicator_view.cc b/ash/system/unified/page_indicator_view.cc
index 16688411..f57b402 100644
--- a/ash/system/unified/page_indicator_view.cc
+++ b/ash/system/unified/page_indicator_view.cc
@@ -86,16 +86,16 @@
   void PaintButtonContents(gfx::Canvas* canvas) override {
     gfx::Rect rect(GetContentsBounds());
 
-    AshColorProvider* color_provider = AshColorProvider::Get();
-    const SkColor selected_color = color_provider->GetContentLayerColor(
-        AshColorProvider::ContentLayerType::kIconPrimary,
-        AshColorProvider::AshColorMode::kDark);
+    const SkColor selected_color =
+        AshColorProvider::Get()->GetContentLayerColor(
+            AshColorProvider::ContentLayerType::kIconPrimary,
+            AshColorProvider::AshColorMode::kDark);
     cc::PaintFlags flags;
     flags.setAntiAlias(true);
     flags.setStyle(cc::PaintFlags::kFill_Style);
     flags.setColor(selected_
                        ? selected_color
-                       : color_provider->GetDisabledColor(selected_color));
+                       : AshColorProvider::GetDisabledColor(selected_color));
     canvas->DrawCircle(rect.CenterPoint(), kUnifiedPageIndicatorButtonRadius,
                        flags);
   }
diff --git a/ash/system/unified/top_shortcut_button.cc b/ash/system/unified/top_shortcut_button.cc
index c68b1e3d..d8ef80b7 100644
--- a/ash/system/unified/top_shortcut_button.cc
+++ b/ash/system/unified/top_shortcut_button.cc
@@ -41,10 +41,10 @@
   SetImage(
       views::Button::STATE_NORMAL,
       gfx::CreateVectorIcon(icon, kTrayTopShortcutButtonIconSize, icon_color));
-  SetImage(views::Button::STATE_DISABLED,
-           gfx::CreateVectorIcon(
-               icon, kTrayTopShortcutButtonIconSize,
-               AshColorProvider::Get()->GetDisabledColor(icon_color)));
+  SetImage(
+      views::Button::STATE_DISABLED,
+      gfx::CreateVectorIcon(icon, kTrayTopShortcutButtonIconSize,
+                            AshColorProvider::GetDisabledColor(icon_color)));
 }
 
 TopShortcutButton::TopShortcutButton(views::ButtonListener* listener,
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 5fb53f8a..399cc1df 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -1348,7 +1348,7 @@
           AshColorProvider::ControlsLayerType::kInactiveControlBackground,
           AshColorProvider::AshColorMode::kDark);
   const SkColor disabled_background_color =
-      AshColorProvider::Get()->GetDisabledColor(background_color);
+      AshColorProvider::GetDisabledColor(background_color);
   EXPECT_TRUE(new_desk_button->GetEnabled());
   EXPECT_EQ(background_color, new_desk_button->background()->get_color());
 
diff --git a/ash/wm/desks/new_desk_button.cc b/ash/wm/desks/new_desk_button.cc
index 9b46494..301b59fb 100644
--- a/ash/wm/desks/new_desk_button.cc
+++ b/ash/wm/desks/new_desk_button.cc
@@ -90,7 +90,7 @@
           AshColorProvider::ControlsLayerType::kInactiveControlBackground,
           AshColorProvider::AshColorMode::kDark);
   const SkColor disabled_background_color =
-      AshColorProvider::Get()->GetDisabledColor(background_color);
+      AshColorProvider::GetDisabledColor(background_color);
   SetBackground(views::CreateRoundedRectBackground(
       enabled ? background_color : disabled_background_color, kCornerRadius));
 }
@@ -168,7 +168,7 @@
 }
 
 void NewDeskButton::UpdateBorderState() {
-  if (IsViewHighlighted() && DesksController::Get()->CanCreateDesks()) {
+  if (IsViewHighlighted()) {
     SetBorder(views::CreateRoundedRectBorder(
         kHighlightThicknessDp, kCornerRadius,
         GetNativeTheme()->GetSystemColor(
diff --git a/ash/wm/lock_action_handler_layout_manager_unittest.cc b/ash/wm/lock_action_handler_layout_manager_unittest.cc
index 60dcce4..b44aeedc 100644
--- a/ash/wm/lock_action_handler_layout_manager_unittest.cc
+++ b/ash/wm/lock_action_handler_layout_manager_unittest.cc
@@ -459,7 +459,7 @@
   window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40));
   window_state->Maximize();
 
-  // Maximize the window with as the restore bounds is inside 2nd display but
+  // Maximize the window width as the restore bounds is inside 2nd display but
   // lock container windows are always on primary display.
   EXPECT_EQ(root_windows[0], window->GetRootWindow());
   target_bounds = gfx::Rect(300, 400);
diff --git a/ash/wm/lock_layout_manager_unittest.cc b/ash/wm/lock_layout_manager_unittest.cc
index c907c53..ac35ed9 100644
--- a/ash/wm/lock_layout_manager_unittest.cc
+++ b/ash/wm/lock_layout_manager_unittest.cc
@@ -373,7 +373,7 @@
   // for the primary shelf, so it should not influence the screen bounds.
   window->SetBoundsInScreen(gfx::Rect(0, 0, 30, 40), GetSecondaryDisplay());
 
-  target_bounds = gfx::Rect(300, 0, 400, 500);
+  target_bounds = gfx::Rect(600, 0, 400, 500);
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
   EXPECT_EQ(target_bounds, window->GetBoundsInScreen());
 }
diff --git a/ash/wm/overview/overview_highlight_controller.cc b/ash/wm/overview/overview_highlight_controller.cc
index 03c0f2d..d1e6316 100644
--- a/ash/wm/overview/overview_highlight_controller.cc
+++ b/ash/wm/overview/overview_highlight_controller.cc
@@ -260,7 +260,6 @@
   DCHECK_GE(current_index, 0);
   deleted_index_ = base::make_optional(current_index);
   highlight_widget_.reset();
-  highlighted_view_->OnViewUnhighlighted();
   highlighted_view_ = nullptr;
 }
 
diff --git a/ash/wm/pip/pip_window_resizer_unittest.cc b/ash/wm/pip/pip_window_resizer_unittest.cc
index 3819193..468a7d1 100644
--- a/ash/wm/pip/pip_window_resizer_unittest.cc
+++ b/ash/wm/pip/pip_window_resizer_unittest.cc
@@ -526,7 +526,7 @@
 
 TEST_P(PipWindowResizerTest, PipWindowDoesNotChangeDisplayOnDrag) {
   PreparePipWindow(gfx::Rect(200, 200, 100, 100));
-  display::Display display = WindowState::Get(window())->GetDisplay();
+  const display::Display display = WindowState::Get(window())->GetDisplay();
   gfx::Rect rect_in_screen = window()->bounds();
   ::wm::ConvertRectToScreen(window()->parent(), &rect_in_screen);
   EXPECT_TRUE(display.bounds().Contains(rect_in_screen));
diff --git a/ash/wm/work_area_insets.cc b/ash/wm/work_area_insets.cc
index ead62b1..b620fad 100644
--- a/ash/wm/work_area_insets.cc
+++ b/ash/wm/work_area_insets.cc
@@ -26,11 +26,10 @@
   // The virtual keyboard always hides the shelf (in any orientation).
   // Therefore, if the keyboard is shown, there is no need to reduce the work
   // area by the size of the shelf.
-  if (!keyboard_bounds.IsEmpty()) {
-    work_area_insets += gfx::Insets(0, 0, keyboard_bounds.height(), 0);
-  } else {
+  if (keyboard_bounds.IsEmpty())
     work_area_insets += shelf_insets;
-  }
+  else
+    work_area_insets += gfx::Insets(0, 0, keyboard_bounds.height(), 0);
   return work_area_insets;
 }
 
@@ -40,10 +39,13 @@
                                   const gfx::Rect shelf_bounds,
                                   const gfx::Rect keyboard_bounds_in_screen,
                                   aura::Window* window) {
+  // The shelf bounds are not in screen coordinates.
+  gfx::Rect shelf_bounds_in_screen(shelf_bounds);
+  ::wm::ConvertRectToScreen(window, &shelf_bounds_in_screen);
+
   gfx::Rect work_area_bounds = screen_util::GetDisplayBoundsWithShelf(window);
   work_area_bounds.Inset(accessibility_insets);
-  work_area_bounds.Subtract(shelf_bounds);
-  ::wm::ConvertRectToScreen(window, &work_area_bounds);
+  work_area_bounds.Subtract(shelf_bounds_in_screen);
   work_area_bounds.Subtract(keyboard_bounds_in_screen);
   return work_area_bounds;
 }
diff --git a/base/BUILD.gn b/base/BUILD.gn
index b8d7eaa..13ab56f9 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3370,6 +3370,7 @@
       "//third_party/android_support_test_runner:rules_java",
       "//third_party/android_support_test_runner:runner_java",
       "//third_party/hamcrest:hamcrest_core_java",
+      "//third_party/hamcrest:hamcrest_java",
       "//third_party/junit",
       "//third_party/ub-uiautomator:ub_uiautomator_java",
     ]
diff --git a/base/android/java/src/org/chromium/base/Log.java b/base/android/java/src/org/chromium/base/Log.java
index 97e5fe58..399f16d 100644
--- a/base/android/java/src/org/chromium/base/Log.java
+++ b/base/android/java/src/org/chromium/base/Log.java
@@ -82,8 +82,10 @@
     }
 
     /**
-     * In debug: Forwards to {@link android.util.Log#isLoggable(String, int)}, but alway
-     * In release: Always returns false (via @RemovableInRelease).
+     * Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}.
+     *
+     * Note: Has no effect on whether logs are sent or not. Use a method with
+     * {@link RemovableInRelease} to log something in Debug builds only.
      */
     public static boolean isLoggable(String tag, int level) {
         return android.util.Log.isLoggable(tag, level);
@@ -103,7 +105,6 @@
      * @param args Arguments referenced by the format specifiers in the format string. If the last
      *             one is a {@link Throwable}, its trace will be printed.
      */
-    @RemovableInRelease
     private static void verbose(String tag, String messageTemplate, Object... args) {
         String message = formatLogWithStack(messageTemplate, args);
         Throwable tr = getThrowableToLog(args);
@@ -189,7 +190,6 @@
      * @param args Arguments referenced by the format specifiers in the format string. If the last
      *             one is a {@link Throwable}, its trace will be printed.
      */
-    @RemovableInRelease
     private static void debug(String tag, String messageTemplate, Object... args) {
         String message = formatLogWithStack(messageTemplate, args);
         Throwable tr = getThrowableToLog(args);
@@ -362,7 +362,6 @@
     }
 
     /** Returns a string form of the origin of the log call, to be used as secondary tag.*/
-    @RemovableInRelease
     private static String getCallOrigin() {
         StackTraceElement[] st = Thread.currentThread().getStackTrace();
 
diff --git a/base/android/java/src/org/chromium/base/annotations/RemovableInRelease.java b/base/android/java/src/org/chromium/base/annotations/RemovableInRelease.java
index 911c126..2191334 100644
--- a/base/android/java/src/org/chromium/base/annotations/RemovableInRelease.java
+++ b/base/android/java/src/org/chromium/base/annotations/RemovableInRelease.java
@@ -8,15 +8,11 @@
 import java.lang.annotation.Target;
 
 /**
- * Methods with this annotation will always be removed in release builds.
- * If they cannot be safely removed, then the build will break.
+ * The annotated function can be removed in release builds.
  *
- * "Safely removed" refers to how return values are used. The current
- * ProGuard rules are configured such that:
- *  - methods that return an object will always return null.
- *  - methods that return boolean will always return false.
- *  - methods that return other primitives are removed so long as their return
- *    values are not used.
+ * Calls to this function will be removed if its return value is not used. If all calls are removed,
+ * the function definition itself will be candidate for removal.
+ * It works by indicating to Proguard that the function has no side effects.
  */
-@Target({ElementType.METHOD})
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
 public @interface RemovableInRelease {}
diff --git a/base/android/proguard/chromium_apk.flags b/base/android/proguard/chromium_apk.flags
index 36052e1..354a887d 100644
--- a/base/android/proguard/chromium_apk.flags
+++ b/base/android/proguard/chromium_apk.flags
@@ -40,11 +40,20 @@
     public static **[] values();
 }
 
-# Allows Proguard freedom in removing these log related calls.
+# Allows Proguard freedom in removing these log related calls. We ask for debug
+# and verbose logs to be stripped out in base.Log, so we are just ensuring we
+# get rid of all other debug/verbose logs.
 -assumenosideeffects class android.util.Log {
   static *** d(...);
   static *** v(...);
-  static boolean isLoggable(...);
+  static *** isLoggable(...);
+}
+-checkdiscard class org.chromium.base.Log {
+  public static void d(...);
+  public static void v(...);
+  private static void debug(...);
+  private static void verbose(...);
+  private static String getCallOrigin(...);
 }
 
 # The following chart was created on July 20, 2016, to decide on 3 optimization
@@ -69,8 +78,3 @@
 # Don't warn about those in case this app is linking against an older
 # platform version.  We know about them, and they are safe.
 -dontwarn android.support.**
-
-# Ensure @RemovableInRelease actually works.
--checkdiscard class ** {
-  @org.chromium.base.annotations.RemovableInRelease *;
-}
diff --git a/base/android/proguard/chromium_code.flags b/base/android/proguard/chromium_code.flags
index 2a30627..19f45e867a 100644
--- a/base/android/proguard/chromium_code.flags
+++ b/base/android/proguard/chromium_code.flags
@@ -42,13 +42,8 @@
   native <methods>;
 }
 
+# Remove methods annotated with this if their return value is unused.
 -assumenosideeffects class ** {
-  # Remove boolean @RemovableInRelease methods even when return value is used.
-  @org.chromium.base.annotations.RemovableInRelease boolean *(...) return false;
-  # Remove object @RemovableInRelease methods even when return value is used.
-  # Note: * in return type does not match primitives.
-  @org.chromium.base.annotations.RemovableInRelease * *(...) return null;
-  # Remove @RemovableInRelease methods so long as return values are unused.
   @org.chromium.base.annotations.RemovableInRelease <methods>;
 }
 
diff --git a/base/task/task_executor.cc b/base/task/task_executor.cc
index 57ca5cd..8b527b0 100644
--- a/base/task/task_executor.cc
+++ b/base/task/task_executor.cc
@@ -6,10 +6,8 @@
 
 #include <type_traits>
 
-#include "base/no_destructor.h"
 #include "base/task/task_traits.h"
 #include "base/task/task_traits_extension.h"
-#include "base/threading/thread_local.h"
 
 namespace base {
 
@@ -32,21 +30,6 @@
 
 }  // namespace
 
-ThreadLocalPointer<TaskExecutor>* GetTLSForCurrentTaskExecutor() {
-  static NoDestructor<ThreadLocalPointer<TaskExecutor>> instance;
-  return instance.get();
-}
-
-void SetTaskExecutorForCurrentThread(TaskExecutor* task_executor) {
-  DCHECK(!GetTLSForCurrentTaskExecutor()->Get() || !task_executor ||
-         GetTLSForCurrentTaskExecutor()->Get() == task_executor);
-  GetTLSForCurrentTaskExecutor()->Set(task_executor);
-}
-
-TaskExecutor* GetTaskExecutorForCurrentThread() {
-  return GetTLSForCurrentTaskExecutor()->Get();
-}
-
 void RegisterTaskExecutor(uint8_t extension_id, TaskExecutor* task_executor) {
   DCHECK_NE(extension_id, TaskTraitsExtensionStorage::kInvalidExtensionId);
   DCHECK_LE(extension_id, TaskTraitsExtensionStorage::kMaxExtensionId);
diff --git a/base/task/task_executor.h b/base/task/task_executor.h
index a062fdc..b4e79e14 100644
--- a/base/task/task_executor.h
+++ b/base/task/task_executor.h
@@ -74,13 +74,6 @@
                                       TaskExecutor* task_executor);
 void BASE_EXPORT UnregisterTaskExecutorForTesting(uint8_t extension_id);
 
-// Stores the provided TaskExecutor in TLS for the current thread, to be used by
-// tasks with the CurrentThread() trait.
-void BASE_EXPORT SetTaskExecutorForCurrentThread(TaskExecutor* task_executor);
-
-// Returns the task executor registered for the current thread.
-BASE_EXPORT TaskExecutor* GetTaskExecutorForCurrentThread();
-
 // Determines whether a registered TaskExecutor will handle tasks with the given
 // |traits| and, if so, returns a pointer to it. Otherwise, returns |nullptr|.
 TaskExecutor* GetRegisteredTaskExecutorForTraits(const TaskTraits& traits);
diff --git a/base/test/android/javatests/src/org/chromium/base/test/DestroyActivitiesRule.java b/base/test/android/javatests/src/org/chromium/base/test/DestroyActivitiesRule.java
index b1104b3..0c0b8b26 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/DestroyActivitiesRule.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/DestroyActivitiesRule.java
@@ -81,7 +81,7 @@
             }
         });
         try {
-            allDestroyedCalledback.waitForCallback();
+            allDestroyedCalledback.waitForFirst();
         } catch (InterruptedException | TimeoutException e) {
             // There appears to be a framework bug on K and L where onStop and onDestroy are not
             // called for a handful of tests. We ignore these exceptions.
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java b/base/test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java
index 86934b4a..a5e4a28 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java
@@ -6,6 +6,8 @@
 
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
 import org.junit.Assert;
 
 import java.util.concurrent.TimeUnit;
@@ -131,6 +133,7 @@
     private final Object mLock = new Object();
     private int mCallCount;
     private String mFailureString;
+    private boolean mSingleShotMode;
 
     /**
      * Gets the number of times the callback has been called.
@@ -163,7 +166,7 @@
      * block until the specified call count is reached.
      *
      * @param msg The error message to use if the callback times out.
-     * @param currentCallCount the value obtained by calling getCallCount().
+     * @param currentCallCount Wait until |notifyCalled| has been called this many times in total.
      * @param numberOfCallsToWaitFor number of calls (counting since
      *                               currentCallCount was obtained) that we will wait for.
      * @param timeout timeout value for all callbacks to occur.
@@ -226,27 +229,44 @@
     }
 
     /**
-     * @see #waitForCallback(String, int, int, long, TimeUnit)
+     * @deprecated Use waitForAnyCallback() instead.
      */
     public void waitForCallback(String msg) throws InterruptedException, TimeoutException {
         waitForCallback(msg, getCallCount());
     }
 
     /**
-     * @see #waitForCallback(String, int, int, long, TimeUnit)
+     * @deprecated Use waitForAnyCallback() instead.
      */
     public void waitForCallback() throws InterruptedException, TimeoutException {
         waitForCallback(getCallCount());
     }
 
     /**
+     * Wait until the callback has been called once.
+     */
+    public void waitForFirst(String msg) throws InterruptedException, TimeoutException {
+        MatcherAssert.assertThat(
+                "Use waitForCallback(currentCallCount) for callbacks that are called multiple "
+                        + "times.",
+                mCallCount, Matchers.lessThanOrEqualTo(1));
+        mSingleShotMode = true;
+        waitForCallback(msg, 0);
+    }
+
+    /**
+     * Wait until the callback has been called at least once.
+     */
+    public void waitForFirst() throws InterruptedException, TimeoutException {
+        mSingleShotMode = true;
+        waitForFirst(null);
+    }
+
+    /**
      * Should be called when the callback associated with this helper object is called.
      */
     public void notifyCalled() {
-        synchronized (mLock) {
-            mCallCount++;
-            mLock.notifyAll();
-        }
+        notifyInternal(null);
     }
 
     /**
@@ -256,8 +276,16 @@
      * @param s The failure message.
      */
     public void notifyFailed(String s) {
+        notifyInternal(s);
+    }
+
+    private void notifyInternal(String failureString) {
         synchronized (mLock) {
-            mFailureString = s;
+            mCallCount++;
+            mFailureString = failureString;
+            if (mSingleShotMode && mCallCount > 1) {
+                Assert.fail("Single-use callback called multiple times.");
+            }
             mLock.notifyAll();
         }
     }
diff --git a/base/win/startup_information_unittest.cc b/base/win/startup_information_unittest.cc
index 47a0abf..4749797 100644
--- a/base/win/startup_information_unittest.cc
+++ b/base/win/startup_information_unittest.cc
@@ -7,73 +7,78 @@
 #include <windows.h>
 #include <stddef.h>
 
-#include <string>
-
-#include "base/command_line.h"
-#include "base/strings/string16.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
 #include "base/strings/string_util.h"
-#include "base/test/multiprocess_test.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/scoped_process_information.h"
-#include "testing/multiprocess_func_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
-const wchar_t kSectionName[] = L"EventTestSection";
-const size_t kSectionSize = 4096;
+namespace {
+class ScopedProcessTerminator {
+ public:
+  ScopedProcessTerminator(const PROCESS_INFORMATION& process_info)
+      : process_info_(process_info) {}
 
-MULTIPROCESS_TEST_MAIN(FireInheritedEvents) {
-  HANDLE section = ::OpenFileMappingW(PAGE_READWRITE, false, kSectionName);
-  HANDLE* events = reinterpret_cast<HANDLE*>(::MapViewOfFile(section,
-      PAGE_READWRITE, 0, 0, kSectionSize));
-  // This event should not be valid because it wasn't explicitly inherited.
-  if (::SetEvent(events[1]))
-    return -1;
-  // This event should be valid because it was explicitly inherited.
-  if (!::SetEvent(events[0]))
-    return -1;
+  ~ScopedProcessTerminator() {
+    if (process_info_.IsValid())
+      ::TerminateProcess(process_info_.process_handle(), 0);
+  }
 
-  return 0;
+ private:
+  base::win::ScopedProcessInformation process_info_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedProcessTerminator);
+};
+
+base::win::ScopedHandle CreateInheritedHandle() {
+  HANDLE handle;
+  if (!::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentProcess(),
+                         ::GetCurrentProcess(), &handle,
+                         PROCESS_QUERY_LIMITED_INFORMATION, TRUE, 0)) {
+    return base::win::ScopedHandle();
+  }
+  return base::win::ScopedHandle(handle);
 }
 
-class StartupInformationTest : public base::MultiProcessTest {};
+bool CheckInheritedHandle(HANDLE process, HANDLE check_handle) {
+  HANDLE temp_handle;
+  if (!::DuplicateHandle(process, check_handle, ::GetCurrentProcess(),
+                         &temp_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+    return false;
+  }
+  base::win::ScopedHandle dup_handle(temp_handle);
+  return ::GetProcessId(temp_handle) == ::GetCurrentProcessId();
+}
+}  // namespace
 
-// Verify that only the explicitly specified event is inherited.
-TEST_F(StartupInformationTest, InheritStdOut) {
+// Verify that only the explicitly specified process handle is inherited.
+TEST(StartupInformationTest, InheritStdOut) {
+  base::win::ScopedHandle handle_0 = CreateInheritedHandle();
+  ASSERT_TRUE(handle_0.IsValid());
+  base::win::ScopedHandle handle_1 = CreateInheritedHandle();
+  ASSERT_TRUE(handle_1.IsValid());
+  ASSERT_NE(handle_0.Get(), handle_1.Get());
+
   base::win::StartupInformation startup_info;
-
-  HANDLE section = ::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
-                                        PAGE_READWRITE, 0, kSectionSize,
-                                        kSectionName);
-  ASSERT_TRUE(section);
-
-  HANDLE* events = reinterpret_cast<HANDLE*>(::MapViewOfFile(section,
-      FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, kSectionSize));
-
-  // Make two inheritable events.
-  SECURITY_ATTRIBUTES security_attributes = { sizeof(security_attributes),
-                                              NULL, true };
-  events[0] = ::CreateEvent(&security_attributes, false, false, NULL);
-  ASSERT_TRUE(events[0]);
-  events[1] = ::CreateEvent(&security_attributes, false, false, NULL);
-  ASSERT_TRUE(events[1]);
-
   ASSERT_TRUE(startup_info.InitializeProcThreadAttributeList(1));
-  ASSERT_TRUE(startup_info.UpdateProcThreadAttribute(
-      PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &events[0],
-      sizeof(events[0])));
 
-  base::string16 cmd_line =
-      MakeCmdLine("FireInheritedEvents").GetCommandLineString();
+  HANDLE inherit_process = handle_0.Get();
+  ASSERT_TRUE(startup_info.UpdateProcThreadAttribute(
+      PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &inherit_process,
+      sizeof(inherit_process)));
+
+  base::FilePath exe_path;
+  ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &exe_path));
+  WCHAR cmd_line[] = L"dummy";
 
   PROCESS_INFORMATION temp_process_info = {};
-  ASSERT_TRUE(::CreateProcess(NULL, base::as_writable_wcstr(cmd_line), NULL,
-                              NULL, true, EXTENDED_STARTUPINFO_PRESENT, NULL,
-                              NULL, startup_info.startup_info(),
-                              &temp_process_info))
+  ASSERT_TRUE(::CreateProcess(
+      base::as_wcstr(exe_path.value()), cmd_line, nullptr, nullptr, TRUE,
+      EXTENDED_STARTUPINFO_PRESENT | CREATE_SUSPENDED, nullptr, nullptr,
+      startup_info.startup_info(), &temp_process_info))
       << ::GetLastError();
-  base::win::ScopedProcessInformation process_info(temp_process_info);
-
-  // Only the first event should be signalled
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForMultipleObjects(2, events, false,
-                                                    4000));
+  ScopedProcessTerminator process(temp_process_info);
+  EXPECT_TRUE(CheckInheritedHandle(temp_process_info.hProcess, handle_0.Get()));
+  EXPECT_FALSE(
+      CheckInheritedHandle(temp_process_info.hProcess, handle_1.Get()));
 }
-
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 127c4afa..fe78ec3 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8903320461154356992
\ No newline at end of file
+8903293567242691744
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index bf5aa478..a83a27c 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8903322704777449744
\ No newline at end of file
+8903295702729103200
\ No newline at end of file
diff --git a/chrome/VERSION b/chrome/VERSION
index a2c9a7f..83d12ca 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=78
 MINOR=0
-BUILD=3902
+BUILD=3903
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 72c3c0d..b6d2956 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1704,7 +1704,6 @@
   "java/src/org/chromium/chrome/browser/webapps/SplashController.java",
   "java/src/org/chromium/chrome/browser/webapps/SplashDelegate.java",
   "java/src/org/chromium/chrome/browser/webapps/SplashscreenObserver.java",
-  "java/src/org/chromium/chrome/browser/webapps/TouchlessAddToHomescreenDialog.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkActivity0.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkActivity1.java",
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandlerTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandlerTest.java
index 7c0b190..2d36b02c 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandlerTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandlerTest.java
@@ -219,7 +219,7 @@
         }
 
         synchronized T waitForResult(String msg) throws Exception {
-            if (!mHasResult) mHelper.waitForCallback(msg);
+            if (!mHasResult) mHelper.waitForFirst(msg);
             assertTrue(mHasResult);
             return mResult;
         }
diff --git a/chrome/android/java/monochrome_public_bundle.proguard_flags.expected b/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
index 8fc2cfc..29e834b 100644
--- a/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
+++ b/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
@@ -129,11 +129,20 @@
     public static **[] values();
 }
 
-# Allows Proguard freedom in removing these log related calls.
+# Allows Proguard freedom in removing these log related calls. We ask for debug
+# and verbose logs to be stripped out in base.Log, so we are just ensuring we
+# get rid of all other debug/verbose logs.
 -assumenosideeffects class android.util.Log {
   static *** d(...);
   static *** v(...);
-  static boolean isLoggable(...);
+  static *** isLoggable(...);
+}
+-checkdiscard class org.chromium.base.Log {
+  public static void d(...);
+  public static void v(...);
+  private static void debug(...);
+  private static void verbose(...);
+  private static String getCallOrigin(...);
 }
 
 # The following chart was created on July 20, 2016, to decide on 3 optimization
@@ -159,11 +168,6 @@
 # platform version.  We know about them, and they are safe.
 -dontwarn android.support.**
 
-# Ensure @RemovableInRelease actually works.
--checkdiscard class ** {
-  @org.chromium.base.annotations.RemovableInRelease *;
-}
-
 ################################################################################
 # ../../base/android/proguard/chromium_code.flags
 ################################################################################
@@ -211,13 +215,8 @@
   native <methods>;
 }
 
+# Remove methods annotated with this if their return value is unused.
 -assumenosideeffects class ** {
-  # Remove boolean @RemovableInRelease methods even when return value is used.
-  @org.chromium.base.annotations.RemovableInRelease boolean *(...) return false;
-  # Remove object @RemovableInRelease methods even when return value is used.
-  # Note: * in return type does not match primitives.
-  @org.chromium.base.annotations.RemovableInRelease * *(...) return null;
-  # Remove @RemovableInRelease methods so long as return values are unused.
   @org.chromium.base.annotations.RemovableInRelease <methods>;
 }
 
diff --git a/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml b/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
index e1ec8aa..f8a55dd 100644
--- a/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
+++ b/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
@@ -166,6 +166,19 @@
                 android:text="@string/autofill_card_unmask_prompt_storage_checkbox" />
 
         </RelativeLayout>
+
+        <CheckBox
+            android:id="@+id/use_screenlock_checkbox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="16dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginStart="17dp"
+            android:layout_marginEnd="10dp"
+            android:enabled="true"
+            android:textAppearance="@style/TextAppearance.AutofillCardCheckBoxText"
+            android:text="@string/autofill_card_unmask_prompt_enable_fido_auth_checkbox" />
+
     </LinearLayout>
 
     <LinearLayout
diff --git a/chrome/android/java/res/layout/touchless_add_to_apps.xml b/chrome/android/java/res/layout/touchless_add_to_apps.xml
deleted file mode 100644
index 952e7f2c..0000000
--- a/chrome/android/java/res/layout/touchless_add_to_apps.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:gravity="center_vertical"
-    android:padding="8dp" >
-
-    <ProgressBar
-        android:id="@+id/spinny"
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:padding="8dp" />
-
-    <!-- The icon that will be used on the home screen. -->
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="36dp"
-        android:layout_height="36dp"
-        android:importantForAccessibility="no"
-        android:visibility="gone" />
-
-    <org.chromium.chrome.browser.widget.AlertDialogEditText
-        android:id="@+id/app_title"
-        android:inputType="text"
-        android:layout_width="match_parent"
-        android:layout_height="36dp"
-        android:singleLine="true"
-        android:paddingTop="0dp"
-        android:layout_marginStart="48dp"
-        style="@style/Theme.AppCompat.Light"
-        android:visibility="gone"/>
-
-</FrameLayout>
-
diff --git a/chrome/android/java/res/values-v21/styles.xml b/chrome/android/java/res/values-v21/styles.xml
index 9fd2500..8c17386 100644
--- a/chrome/android/java/res/values-v21/styles.xml
+++ b/chrome/android/java/res/values-v21/styles.xml
@@ -18,7 +18,7 @@
 
     <!-- Preferences -->
     <style name="Base.V21.Theme.Chromium.Preferences" parent="Base.Theme.Chromium.Preferences">
-        <item name="alertDialogTheme">@style/Theme.Chromium.Preferences.AlertDialog</item>
+        <item name="alertDialogTheme">@style/Theme.Chromium.AlertDialog</item>
     </style>
 
     <style name="Theme.Chromium.Preferences" parent="Base.V21.Theme.Chromium.Preferences" />
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml
index c4f3056..ce8c6c1 100644
--- a/chrome/android/java/res/values/styles.xml
+++ b/chrome/android/java/res/values/styles.xml
@@ -280,18 +280,6 @@
         <item name="android:widgetLayout">@layout/preference_widget_checkbox</item>
     </style>
 
-    <style name="Theme.Chromium.Preferences.AlertDialog" parent="Theme.Chromium.AlertDialog">
-        <item name="buttonBarButtonStyle">@style/TextAppearance.DialogButtonCompat</item>
-    </style>
-
-    <!-- TODO(crbug.com/971791): Using all caps for support library preference dialog buttons to
-         match existing non-compat style, however we should later reexamine whether this is still
-         the right choice since it looks like most of our other AlertDialogs use the default title
-         case. -->
-    <style name="TextAppearance.DialogButtonCompat" parent="AlertDialogButtonStyle">
-        <item name="android:textAllCaps">true</item>
-    </style>
-
     <style name="Theme.Chromium.SwitchPreference">
         <item name="android:layout">@layout/preference_compat</item>
         <item name="android:widgetLayout">@layout/preference_widget_switch_compat</item>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 71e29045..822d4a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -155,6 +155,8 @@
             "AllowRemoteContextForNotifications";
     public static final String AUTOFILL_ALLOW_NON_HTTP_ACTIVATION =
             "AutofillAllowNonHttpActivation";
+    public static final String AUTOFILL_CREDIT_CARD_AUTHENTICATION =
+            "AutofillCreditCardAuthentication";
     public static final String AUTOFILL_ENABLE_COMPANY_NAME = "AutofillEnableCompanyName";
     public static final String ADJUST_WEBAPK_INSTALLATION_SPACE = "AdjustWebApkInstallationSpace";
     public static final String ANDROID_NIGHT_MODE = "AndroidNightMode";
@@ -297,14 +299,12 @@
     public static final String PREDICTIVE_PREFETCHING_ALLOWED_ON_ALL_CONNECTION_TYPES =
             "PredictivePrefetchingAllowedOnAllConnectionTypes";
     public static final String PRIORITIZE_BOOTSTRAP_TASKS = "PrioritizeBootstrapTasks";
-    public static final String PROGRESS_BAR_THROTTLE = "ProgressBarThrottle";
     public static final String QUERY_IN_OMNIBOX = "QueryInOmnibox";
     public static final String REACHED_CODE_PROFILER = "ReachedCodeProfiler";
     public static final String READER_MODE_IN_CCT = "ReaderModeInCCT";
     public static final String REMOVE_NAVIGATION_HISTORY = "RemoveNavigationHistory";
     public static final String REORDER_BOOKMARKS = "ReorderBookmarks";
     public static final String REVAMPED_CONTEXT_MENU = "RevampedContextMenu";
-    public static final String SEARCH_READY_OMNIBOX = "SearchReadyOmnibox";
     public static final String SEND_TAB_TO_SELF = "SyncSendTabToSelf";
     public static final String SERVICE_MANAGER_FOR_DOWNLOAD = "ServiceManagerForDownload";
     public static final String SERVICE_WORKER_PAYMENT_APPS = "ServiceWorkerPaymentApps";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
index 1d498d87..a370eaf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
@@ -68,9 +68,10 @@
     }
 
     @Override
-    public void onUserInput(String cvc, String month, String year, boolean shouldStoreLocally) {
+    public void onUserInput(String cvc, String month, String year, boolean shouldStoreLocally,
+            boolean enableFidoAuth) {
         CardUnmaskBridgeJni.get().onUserInput(mNativeCardUnmaskPromptViewAndroid,
-                CardUnmaskBridge.this, cvc, month, year, shouldStoreLocally);
+                CardUnmaskBridge.this, cvc, month, year, shouldStoreLocally, enableFidoAuth);
     }
 
     @Override
@@ -144,7 +145,8 @@
         boolean checkUserInputValidity(long nativeCardUnmaskPromptViewAndroid,
                 CardUnmaskBridge caller, String userResponse);
         void onUserInput(long nativeCardUnmaskPromptViewAndroid, CardUnmaskBridge caller,
-                String cvc, String month, String year, boolean shouldStoreLocally);
+                String cvc, String month, String year, boolean shouldStoreLocally,
+                boolean enableFidoAuth);
         void onNewCardLinkClicked(long nativeCardUnmaskPromptViewAndroid, CardUnmaskBridge caller);
         int getExpectedCvcLength(long nativeCardUnmaskPromptViewAndroid, CardUnmaskBridge caller);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index 976e657..44e39a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -30,6 +30,7 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils.ErrorType;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -60,6 +61,7 @@
     private final TextView mNewCardLink;
     private final TextView mErrorMessage;
     private final CheckBox mStoreLocallyCheckbox;
+    private final CheckBox mUseScreenlockCheckbox;
     private final ImageView mStoreLocallyTooltipIcon;
     private PopupWindow mStoreLocallyTooltipPopup;
     private final ViewGroup mControlsContainer;
@@ -99,8 +101,10 @@
          * @param month The value the user selected for expiration month, if any.
          * @param year The value the user selected for expiration month, if any.
          * @param shouldStoreLocally The state of the "Save locally?" checkbox at the time.
+         * @param enableFidoAuth The value the user selected for the use lockscreen checkbox.
          */
-        void onUserInput(String cvc, String month, String year, boolean shouldStoreLocally);
+        void onUserInput(String cvc, String month, String year, boolean shouldStoreLocally,
+                boolean enableFidoAuth);
 
         /**
          * Called when the "New card?" link has been clicked.
@@ -161,6 +165,13 @@
         mErrorMessage = (TextView) v.findViewById(R.id.error_message);
         mStoreLocallyCheckbox = (CheckBox) v.findViewById(R.id.store_locally_checkbox);
         mStoreLocallyCheckbox.setChecked(canStoreLocally && defaultToStoringLocally);
+        mUseScreenlockCheckbox = (CheckBox) v.findViewById(R.id.use_screenlock_checkbox);
+        if (canStoreLocally
+                || !ChromeFeatureList.isEnabled(
+                        ChromeFeatureList.AUTOFILL_CREDIT_CARD_AUTHENTICATION)) {
+            mUseScreenlockCheckbox.setVisibility(View.GONE);
+            mUseScreenlockCheckbox.setChecked(false);
+        }
         mStoreLocallyTooltipIcon = (ImageView) v.findViewById(R.id.store_locally_tooltip_icon);
         mStoreLocallyTooltipIcon.setOnClickListener(this);
         if (!canStoreLocally) v.findViewById(R.id.store_locally_container).setVisibility(View.GONE);
@@ -548,7 +559,8 @@
             mDelegate.onUserInput(mCardUnmaskInput.getText().toString(),
                     mMonthInput.getText().toString(),
                     Integer.toString(AutofillUiUtils.getFourDigitYear(mYearInput)),
-                    mStoreLocallyCheckbox != null && mStoreLocallyCheckbox.isChecked());
+                    mStoreLocallyCheckbox != null && mStoreLocallyCheckbox.isChecked(),
+                    mUseScreenlockCheckbox.isChecked());
         } else if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
             mModalDialogManager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerUiDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerUiDelegateAndroid.java
index d2381a6..8b0dae5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerUiDelegateAndroid.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerUiDelegateAndroid.java
@@ -16,9 +16,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.webapps.AddToHomescreenDialog;
-import org.chromium.chrome.browser.webapps.TouchlessAddToHomescreenDialog;
 
 /**
  * Handles the promotion and installation of an app specified by the current web page. This object
@@ -113,10 +111,6 @@
      * @return A version of the {@link AddToHomescreenDialog}.
      */
     private AddToHomescreenDialog buildDialog() {
-        if (FeatureUtilities.isNoTouchModeEnabled()) {
-            return new TouchlessAddToHomescreenDialog(
-                    mTab.getActivity(), mTab.getActivity().getModalDialogManager(), this);
-        }
         return new AddToHomescreenDialog(mTab.getActivity(), this);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gcore/ConnectedTask.java b/chrome/android/java/src/org/chromium/chrome/browser/gcore/ConnectedTask.java
index 355d744..481afa2b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gcore/ConnectedTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gcore/ConnectedTask.java
@@ -73,7 +73,8 @@
     protected abstract void doWhenConnected(T client);
 
     /**
-     * Returns a name of a task (for debug logging).
+     * Returns a name of a task. Implementations should not have side effects
+     * as we want to have the logging related calls removed.
      */
     @RemovableInRelease
     protected abstract String getName();
@@ -92,12 +93,6 @@
      */
     protected void connectionFailed() {}
 
-    private void debugLog(String message) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "%s:%s %s", mLogPrefix, getName(), message);
-        }
-    }
-
     @Override
     @VisibleForTesting
     // We always only pass in a string literal here.
@@ -105,30 +100,34 @@
     public final void run() {
         TraceEvent.begin("GCore:" + mLogPrefix + ":run");
         try {
-            debugLog("started");
+            Log.d(TAG, "%s:%s started", mLogPrefix, getName());
             if (mClient.connectWithTimeout(CONNECTION_TIMEOUT_MS)) {
                 try {
-                    debugLog("connected");
+                    Log.d(TAG, "%s:%s connected", mLogPrefix, getName());
                     doWhenConnected(mClient);
-                    debugLog("finished");
+                    Log.d(TAG, "%s:%s finished", mLogPrefix, getName());
                 } finally {
                     mClient.disconnect();
-                    debugLog("disconnected");
+                    Log.d(TAG, "%s:%s disconnected", mLogPrefix, getName());
                     cleanUp();
-                    debugLog("cleaned up");
+                    Log.d(TAG, "%s:%s cleaned up", mLogPrefix, getName());
                 }
             } else {
                 mRetryNumber++;
                 if (mRetryNumber < RETRY_NUMBER_LIMIT && mClient.isGooglePlayServicesAvailable()) {
-                    debugLog("calling retry");
+                    Log.d(TAG, "%s:%s calling retry", mLogPrefix, getName());
                     retry(this, CONNECTION_RETRY_TIME_MS);
                 } else {
                     connectionFailed();
-                    debugLog("number of retries exceeded");
+                    Log.d(TAG, "%s:%s number of retries exceeded", mLogPrefix, getName());
                     cleanUp();
-                    debugLog("cleaned up");
+                    Log.d(TAG, "%s:%s cleaned up", mLogPrefix, getName());
                 }
             }
+        } catch (RuntimeException e) {
+            Log.e(TAG, "%s:%s runtime exception %s: %s", mLogPrefix, getName(),
+                    e.getClass().getName(), e.getMessage());
+            throw e;
         } finally {
             TraceEvent.end("GCore:" + mLogPrefix + ":run");
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
index a7109ba..69c6711 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
@@ -36,10 +36,10 @@
     private final ClickListener mClickListener;
     private final FaviconHelper mFaviconHelper;
     private final int mFaviconSize;
+    private final ModelList mModelList;
 
     private NavigationHistory mHistory;
     private FaviconHelper.DefaultFaviconHelper mDefaultFaviconHelper;
-    private ModelList mModelList;
 
     /**
      * Performs an action when a navigation item is clicked.
@@ -117,8 +117,9 @@
      * @param favicon the favicon data.
      */
     private void onFaviconAvailable(String pageUrl, Bitmap favicon) {
-        // This callback can come after the sheet is hidden. Do nothing if that happens.
-        if (mModelList == null) return;
+        // This callback can come after the sheet is hidden (which clears modelList).
+        // Do nothing if that happens.
+        if (mModelList.size() == 0) return;
         if (favicon == null) {
             if (mDefaultFaviconHelper == null) {
                 mDefaultFaviconHelper = new FaviconHelper.DefaultFaviconHelper();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java
index 1bb5fa8..c849b859 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java
@@ -33,6 +33,7 @@
 import org.chromium.chrome.browser.util.IntentUtils;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 
 /**
  * Used by notification scheduler to display the notification in Android UI.
@@ -54,6 +55,18 @@
             "org.chromium.chrome.browser.notifications.scheduler.EXTRA_SCHEDULER_CLIENT_TYPE ";
 
     /**
+     * Contains icon info on the notification.
+     */
+    private static class IconBundle {
+        // TODO(hesen): Support Android resource ID.
+        public final Bitmap bitmap;
+
+        public IconBundle(Bitmap bitmap) {
+            this.bitmap = bitmap;
+        }
+    }
+
+    /**
      * Contains button info on the notification.
      */
     private static class Button {
@@ -74,14 +87,12 @@
     private static class NotificationData {
         public final String title;
         public final String message;
-        public final Bitmap icon;
+        public HashMap<Integer /*@IconType*/, IconBundle> icons = new HashMap<>();
         public ArrayList<Button> buttons = new ArrayList<>();
 
-        private NotificationData(String title, String message, Bitmap icon) {
-            // TODO(xingliu): Populate custom data.
+        private NotificationData(String title, String message) {
             this.title = title;
             this.message = message;
-            this.icon = icon;
         }
     }
 
@@ -92,9 +103,14 @@
     }
 
     @CalledByNative
-    private static NotificationData buildNotificationData(
-            String title, String message, Bitmap icon) {
-        return new NotificationData(title, message, icon);
+    private static void addIconWithBitmap(
+            NotificationData notificationData, @IconType int type, Bitmap bitmap) {
+        notificationData.icons.put(type, new IconBundle(bitmap));
+    }
+
+    @CalledByNative
+    private static NotificationData buildNotificationData(String title, String message) {
+        return new NotificationData(title, message);
     }
 
     /**
@@ -208,9 +224,13 @@
         builder.setContentTitle(notificationData.title);
         builder.setContentText(notificationData.message);
 
-        // TODO(xingliu): Use the icon from native. Support big icon.
+        // TODO(hesen): Support setting small icon from native with Android resource Id.
         builder.setSmallIcon(R.drawable.ic_chrome);
 
+        if (notificationData.icons.containsKey(IconType.LARGE_ICON)) {
+            builder.setLargeIcon(notificationData.icons.get(IconType.LARGE_ICON).bitmap);
+        }
+
         // Default content click behavior.
         Intent contentIntent = buildIntent(
                 context, NotificationIntentInterceptor.IntentType.CONTENT_INTENT, systemData);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index a5098bd..bd021497 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -321,13 +321,6 @@
     void onNativeInitialized() {
         mNativeInitialized = true;
 
-        // The feature is instantiated in the constructor to simplify plumbing. If the feature is
-        // actually disabled, null out the coordinator.
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.SEARCH_READY_OMNIBOX)) {
-            mEditUrlProcessor.destroy();
-            mEditUrlProcessor = null;
-        }
-
         mShowSuggestionFavicons =
                 ChromeFeatureList.isEnabled(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenManager.java
index 8e9b25a..e6f07d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenManager.java
@@ -11,7 +11,6 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.content_public.browser.WebContents;
 
 /**
@@ -83,12 +82,7 @@
      */
     @CalledByNative
     public void showDialog() {
-        if (FeatureUtilities.isNoTouchModeEnabled()) {
-            mDialog = new TouchlessAddToHomescreenDialog(
-                    mActivity, mTab.getActivity().getModalDialogManager(), this);
-        } else {
-            mDialog = new AddToHomescreenDialog(mActivity, this);
-        }
+        mDialog = new AddToHomescreenDialog(mActivity, this);
         mDialog.show();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/TouchlessAddToHomescreenDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/TouchlessAddToHomescreenDialog.java
deleted file mode 100644
index feec210..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/TouchlessAddToHomescreenDialog.java
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.webapps;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.os.Build;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogProperties;
-import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogProperties.DialogListItemProperties;
-import org.chromium.chrome.browser.widget.AlertDialogEditText;
-import org.chromium.ui.modaldialog.DialogDismissalCause;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.modaldialog.ModalDialogProperties;
-import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
-import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
-import org.chromium.ui.modelutil.PropertyModel;
-
-/** A touchless variation of the {@link AddToHomescreenDialog}. */
-public class TouchlessAddToHomescreenDialog extends AddToHomescreenDialog {
-    private Activity mActivity;
-    private Delegate mDelegate;
-    private ModalDialogManager mDialogManager;
-    private PropertyModel mModel;
-    private PropertyModel mAddToHomescreenOption;
-
-    public TouchlessAddToHomescreenDialog(
-            Activity activity, ModalDialogManager dialogManager, Delegate delegate) {
-        super(activity, delegate);
-        mActivity = activity;
-        mDialogManager = dialogManager;
-        mDelegate = delegate;
-    }
-
-    @Override
-    public void show() {
-        mModel = buildTouchlessDialogModel();
-        mDialogManager.showDialog(mModel, ModalDialogManager.ModalDialogType.APP);
-    }
-
-    /**
-     * Build the property model for the dialog in touchless mode.
-     * @return A model to pass to a {@link ModalDialogManager}.
-     */
-    private PropertyModel buildTouchlessDialogModel() {
-        Resources res = mActivity.getResources();
-        ModalDialogProperties.Controller controller = new ModalDialogProperties.Controller() {
-            @Override
-            public void onClick(PropertyModel model, int buttonType) {}
-
-            @Override
-            public void onDismiss(PropertyModel model, int dismissalCause) {
-                mDelegate.onDialogDismissed();
-            }
-        };
-        final PropertyModel model =
-                new PropertyModel.Builder(TouchlessDialogProperties.ALL_DIALOG_KEYS)
-                        .with(TouchlessDialogProperties.IS_FULLSCREEN, false)
-                        .with(ModalDialogProperties.CONTROLLER, controller)
-                        .build();
-        model.set(TouchlessDialogProperties.PRIORITY, TouchlessDialogProperties.Priority.HIGH);
-        model.set(ModalDialogProperties.TITLE,
-                res.getString(org.chromium.chrome.R.string.menu_add_to_apps));
-        TouchlessDialogProperties.ActionNames actions = new TouchlessDialogProperties.ActionNames();
-        actions.cancel = org.chromium.chrome.R.string.cancel;
-        actions.select = org.chromium.chrome.R.string.select;
-        actions.alt = 0;
-        model.set(TouchlessDialogProperties.ACTION_NAMES, actions);
-
-        model.set(TouchlessDialogProperties.CANCEL_ACTION,
-                (v) -> mDialogManager.dismissDialog(model, DialogDismissalCause.UNKNOWN));
-
-        mAddToHomescreenOption = new PropertyModel.Builder(DialogListItemProperties.ALL_KEYS)
-                                         .with(DialogListItemProperties.TEXT,
-                                                 res.getString(org.chromium.chrome.R.string.add))
-                                         .with(DialogListItemProperties.ICON,
-                                                 ApiCompatibilityUtils.getDrawable(res,
-                                                         org.chromium.chrome.R.drawable.ic_add))
-                                         .with(DialogListItemProperties.MULTI_CLICKABLE, false)
-                                         .with(DialogListItemProperties.CLICK_LISTENER, null)
-                                         .build();
-        ModelList modelList = new ModelList();
-        modelList.add(new ListItem(
-                TouchlessDialogProperties.ListItemType.DEFAULT, mAddToHomescreenOption));
-        model.set(TouchlessDialogProperties.LIST_MODELS, modelList);
-
-        model.set(ModalDialogProperties.CUSTOM_VIEW,
-                mActivity.getLayoutInflater().inflate(R.layout.touchless_add_to_apps, null));
-
-        return model;
-    }
-
-    /**
-     * Update the custom view shown for the touchless dialog. This shows the name of the app, icon,
-     * and site.
-     */
-    private void updateModelCustomView(Bitmap icon, String title, String origin) {
-        ViewGroup group = (ViewGroup) mModel.get(ModalDialogProperties.CUSTOM_VIEW);
-
-        group.findViewById(R.id.spinny).setVisibility(View.GONE);
-        group.findViewById(R.id.icon).setVisibility(View.VISIBLE);
-        group.findViewById(R.id.app_title).setVisibility(View.VISIBLE);
-
-        if (icon != null) ((ImageView) group.findViewById(R.id.icon)).setImageBitmap(icon);
-        if (title != null) {
-            AlertDialogEditText appTitleText = group.findViewById(R.id.app_title);
-            appTitleText.setText(title);
-            appTitleText.setSelection(title.length());
-        }
-    }
-
-    @Override
-    public void onUserTitleAvailable(String title, String url, boolean isWebapp) {
-        updateModelCustomView(null, title, url);
-        mAddToHomescreenOption.set(DialogListItemProperties.CLICK_LISTENER, (v) -> {
-            ViewGroup group = (ViewGroup) mModel.get(ModalDialogProperties.CUSTOM_VIEW);
-            String customTitle =
-                    ((AlertDialogEditText) group.findViewById(R.id.app_title)).getText().toString();
-            if (TextUtils.isEmpty(customTitle)) customTitle = title;
-
-            if (TextUtils.isEmpty(customTitle)) return;
-            mDelegate.addToHomescreen(customTitle);
-            mDialogManager.dismissDialog(mModel, DialogDismissalCause.UNKNOWN);
-        });
-    }
-
-    @Override
-    public void onUserTitleAvailable(String title, String installText, float rating) {
-        updateModelCustomView(null, title, null);
-    }
-
-    @Override
-    public void onIconAvailable(Bitmap icon) {
-        updateModelCustomView(icon, null, null);
-    }
-
-    @Override
-    @TargetApi(Build.VERSION_CODES.O)
-    public void onAdaptableIconAvailable(Bitmap icon) {
-        updateModelCustomView(icon, null, null);
-    }
-
-    @Override
-    public void onClick(View v) {
-        // Intentionally do nothing.
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ToolbarProgressBar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ToolbarProgressBar.java
index 4f91a12..41a51401 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ToolbarProgressBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ToolbarProgressBar.java
@@ -5,12 +5,10 @@
 package org.chromium.chrome.browser.widget;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeAnimator;
 import android.animation.TimeAnimator.TimeListener;
-import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.graphics.Color;
 import android.os.Build;
@@ -19,7 +17,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateInterpolator;
 import android.widget.FrameLayout.LayoutParams;
 import android.widget.ProgressBar;
 
@@ -27,7 +24,6 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
@@ -72,9 +68,6 @@
     private static final float THEMED_BACKGROUND_WHITE_FRACTION = 0.2f;
     private static final float ANIMATION_WHITE_FRACTION = 0.4f;
 
-    private static final long PROGRESS_THROTTLE_UPDATE_INTERVAL = 30;
-    private static final float PROGRESS_THROTTLE_MAX_UPDATE_AMOUNT = 0.03f;
-
     private static final long PROGRESS_FRAME_TIME_CAP_MS = 50;
     private static final long ALPHA_ANIMATION_DURATION_MS = 140;
 
@@ -105,12 +98,6 @@
     /** Whether or not to use the status bar color as the background of the toolbar. */
     private boolean mUseStatusBarColorAsBackground;
 
-    /** The animator responsible for updating progress once it has been throttled. */
-    private TimeAnimator mProgressThrottle;
-
-    /** The listener for the progress throttle. */
-    private ThrottleTimeListener mProgressThrottleListener;
-
     /**
      * The indeterminate animating view for the progress bar. This will be null for Android
      * versions < K.
@@ -181,58 +168,6 @@
         });
     }
 
-    /** A {@link TimeListener} responsible for updating progress once throttling has started. */
-    private final class ThrottleTimeListener
-            extends AnimatorListenerAdapter implements TimeListener {
-        /** Time interpolator for progress updates. */
-        private final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
-
-        /** The target progress for the throttle animator. */
-        private float mThrottledProgressTarget;
-
-        /** The number of increments expected to reach the target progress since the last update. */
-        private int mExpectedIncrements;
-
-        /** Keeps track of the increment count since the last progress update. */
-        private int mCurrentIncrementCount;
-
-        /** The duration the progress update should take to complete. */
-        private long mExpectedDuration;
-
-        /** The amount of time until the next update. */
-        private long mNextUpdateTime;
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-            float progressDiff = mThrottledProgressTarget - getProgress();
-            mExpectedIncrements =
-                    (int) Math.ceil(progressDiff / PROGRESS_THROTTLE_MAX_UPDATE_AMOUNT);
-            mExpectedIncrements = Math.max(mExpectedIncrements, 1);
-            mCurrentIncrementCount = 0;
-            mNextUpdateTime = 0;
-            mExpectedDuration = PROGRESS_THROTTLE_UPDATE_INTERVAL * mExpectedIncrements;
-        }
-
-        @Override
-        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
-            if (totalTime < mNextUpdateTime || mExpectedIncrements <= 0) return;
-
-            mCurrentIncrementCount++;
-
-            float completionFraction = mCurrentIncrementCount / (float) mExpectedIncrements;
-
-            // This uses an accelerate interpolator to produce progressively longer times so the
-            // progress bar appears to slow down.
-            mNextUpdateTime = (long) (mAccelerateInterpolator.getInterpolation(completionFraction)
-                    * mExpectedDuration);
-
-            float updatedProgress = getProgress() + PROGRESS_THROTTLE_MAX_UPDATE_AMOUNT;
-            if (updatedProgress >= mThrottledProgressTarget) animation.end();
-
-            setProgressInternal(MathUtils.clamp(updatedProgress, 0f, mThrottledProgressTarget));
-        }
-    }
-
     /**
      * Creates a toolbar progress bar.
      *
@@ -286,10 +221,6 @@
         super.onDetachedFromWindow();
         mIsAttachedToWindow = false;
 
-        if (mProgressThrottle != null) {
-            mProgressThrottle.setTimeListener(null);
-            mProgressThrottle.cancel();
-        }
         mSmoothProgressAnimator.setTimeListener(null);
         mSmoothProgressAnimator.cancel();
     }
@@ -389,7 +320,6 @@
 
         removeCallbacks(mStartSmoothIndeterminate);
         if (mAnimatingView != null) mAnimatingView.cancelAnimation();
-        if (mProgressThrottle != null) mProgressThrottle.cancel();
         mSmoothProgressAnimator.cancel();
 
         if (fadeOut) {
@@ -421,8 +351,7 @@
      * @return Whether any animator that delays the showing of progress is running.
      */
     private boolean areProgressAnimatorsRunning() {
-        return (mProgressThrottle != null && mProgressThrottle.isRunning())
-                || mSmoothProgressAnimator.isRunning();
+        return mSmoothProgressAnimator.isRunning();
     }
 
     /**
@@ -464,27 +393,7 @@
     @Override
     public void setProgress(float progress) {
         ThreadUtils.assertOnUiThread();
-
-        // TODO(mdjones): Maybe subclass this to be ThrottledToolbarProgressBar.
-        if (mProgressThrottle == null && ChromeFeatureList.isInitialized()
-                && ChromeFeatureList.isEnabled(ChromeFeatureList.PROGRESS_BAR_THROTTLE)) {
-            mProgressThrottle = new TimeAnimator();
-            mProgressThrottleListener = new ThrottleTimeListener();
-            mProgressThrottle.addListener(mProgressThrottleListener);
-            mProgressThrottle.setTimeListener(mProgressThrottleListener);
-        }
-
-        // Throttle progress if the increment was greater than 5%.
-        if (mProgressThrottle != null
-                && (progress - getProgress() > PROGRESS_THROTTLE_MAX_UPDATE_AMOUNT
-                           || mProgressThrottle.isRunning())) {
-            mProgressThrottleListener.mThrottledProgressTarget = progress;
-
-            mProgressThrottle.cancel();
-            mProgressThrottle.start();
-        } else {
-            setProgressInternal(progress);
-        }
+        setProgressInternal(progress);
     }
 
     /**
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 92d8ea9..c387f8c 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2251,9 +2251,6 @@
       <message name="IDS_NOTIFICATION_WEBAPK_INSTALLED" desc="Indicates that a WebAPK has been successfully added to Home screen.">
         Added to Home screen
       </message>
-      <message name="IDS_MENU_ADD_TO_APPS" desc="Text to accompany icon that will navigate to a page showing a categorized view of different applications or sites">
-        Add to my apps
-      </message>
       <message name="IDS_ADDED_TO_APPS" desc="Text that confirms a site or app has been added to the apps screen.">
         <ph name="APP_NAME">%1$s<ex>Zomato</ex></ph> added to my apps
       </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
index ccf87f7..bc834522 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -184,7 +184,7 @@
                     NotificationUmaTracker.getInstance());
         });
 
-        mResponseHandler.mNotifyNotification.waitForCallback();
+        mResponseHandler.mNotifyNotification.waitForFirst();
     }
 
     /**
@@ -197,7 +197,7 @@
         PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT,
                 () -> mClient.cancelNotification(SCOPE, NOTIFICATION_TAG, NOTIFICATION_ID));
 
-        mResponseHandler.mCancelNotification.waitForCallback();
+        mResponseHandler.mCancelNotification.waitForFirst();
 
         Assert.assertEquals(mResponseHandler.mNotificationTag, NOTIFICATION_TAG);
         Assert.assertEquals(mResponseHandler.mNotificationId, NOTIFICATION_ID);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
index 6734ed1..e32f4d41 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
@@ -657,7 +657,7 @@
         }
 
         public void waitForRequest() throws InterruptedException, TimeoutException {
-            mRequestedWaiter.waitForCallback();
+            mRequestedWaiter.waitForFirst();
         }
 
         public void waitForRequest(int currentCallCount, int numberOfCallsToWaitFor)
@@ -666,7 +666,7 @@
         }
 
         public void waitForCompletion() throws InterruptedException, TimeoutException {
-            mCompletionWaiter.waitForCallback();
+            mCompletionWaiter.waitForFirst();
         }
 
         public void waitForCompletion(int currentCallCount, int numberOfCallsToWaitFor)
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleLoaderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleLoaderTest.java
index 3a75f8d..196660c4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleLoaderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleLoaderTest.java
@@ -86,7 +86,7 @@
             });
         });
 
-        onLoaded.waitForCallback();
+        onLoaded.waitForFirst();
     }
 
     /**
@@ -107,7 +107,7 @@
             });
         });
 
-        onLoaded.waitForCallback();
+        onLoaded.waitForFirst();
 
         assertEquals(0, mDexInputStreamProvider.getCallCount());
         assertEquals(0, mModuleLoaderFromApk.getDexDirectory().listFiles().length);
@@ -132,7 +132,7 @@
             });
         });
 
-        onLoaded.waitForCallback();
+        onLoaded.waitForFirst();
     }
 
     @Test
@@ -150,7 +150,7 @@
             });
         });
 
-        onLoaded.waitForCallback();
+        onLoaded.waitForFirst();
 
         assertEquals(1, mDexInputStreamProvider.getCallCount());
 
@@ -175,7 +175,7 @@
                     result -> onLoaded1.notifyCalled());
         });
 
-        onLoaded1.waitForCallback();
+        onLoaded1.waitForFirst();
 
         CallbackHelper onLoaded2 = new CallbackHelper();
 
@@ -186,7 +186,7 @@
                     result -> onLoaded2.notifyCalled());
         });
 
-        onLoaded2.waitForCallback();
+        onLoaded2.waitForFirst();
 
         assertEquals(1, mDexInputStreamProvider.getCallCount());
 
@@ -213,7 +213,7 @@
             });
         });
 
-        onLoaded.waitForCallback();
+        onLoaded.waitForFirst();
 
         assertEquals(1, mDexInputStreamProvider.getCallCount());
 
@@ -238,7 +238,7 @@
                     result -> onLoadedWithDex.notifyCalled());
         });
 
-        onLoadedWithDex.waitForCallback();
+        onLoadedWithDex.waitForFirst();
         CallbackHelper onLoadedWithoutDex = new CallbackHelper();
 
         runOnUiThreadBlocking(() -> {
@@ -248,7 +248,7 @@
                     result -> onLoadedWithoutDex.notifyCalled());
         });
 
-        onLoadedWithoutDex.waitForCallback();
+        onLoadedWithoutDex.waitForFirst();
 
         assertEquals(1, mDexInputStreamProvider.getCallCount());
         assertFalse(ContextUtils.getAppSharedPreferences().contains(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
index d9443ba..0f70779 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
@@ -175,8 +175,8 @@
         });
 
         assertTrue(coordinator.requestPostMessageChannel(FAKE_ORIGIN_URI));
-        messageChannelHelper.waitForCallback();
-        onPostMessageHelper.waitForCallback();
+        messageChannelHelper.waitForFirst();
+        onPostMessageHelper.waitForFirst();
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/login/ChromeHttpAuthHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/login/ChromeHttpAuthHandlerTest.java
index 5eff0966..970032b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/login/ChromeHttpAuthHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/login/ChromeHttpAuthHandlerTest.java
@@ -149,7 +149,7 @@
 
         String url = mTestServer.getURL("/auth-basic");
         ChromeTabUtils.loadUrlOnUiThread(tab, url);
-        handlerCallback.waitForCallback();
+        handlerCallback.waitForFirst();
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> { ChromeHttpAuthHandler.setTestCreationCallback(null); });
         return handlerRef.get();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
index be25a13..56d0333 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
@@ -177,6 +178,7 @@
     @Test
     @SmallTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
+    @DisabledTest(message = "https://crbug.com/1000315")
     public void testShowingVoiceSearchButtonIfUrlBarIsEmpty()
             throws ExecutionException, InterruptedException {
         setUrlBarTextAndFocus("");
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index f1c68dbb..ef561a7 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -156,8 +156,8 @@
   <message name="IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_HEADER" desc="Header for welcome page.">
     Connect to your phone
   </message>
-  <message name="IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_MESSAGE" desc="Summary to tell user that they can share their Android's capabilities with their Chromebook; includes details on specific features that can be used and a link to an information page">
-    Your Chromebook and Android phone work better together. Connect them so you can text from your computer, share your internet connection, and unlock your Chromebook with your phone.<ph name="FOOTNOTE_POINTER">$1<ex>*</ex></ph> <ph name="LINK_BEGIN">&lt;a href="$2<ex>https://support.google.com/chromebook/?p=multidevice</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+  <message name="IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_MESSAGE" desc="Summary to tell user that they can share their Android's capabilities with their Chrome OS device; includes details on specific features that can be used and a link to an information page">
+    Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> and Android phone work better together. Connect them so you can text from your computer, share your internet connection, and unlock your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> with your phone.<ph name="FOOTNOTE_POINTER">$2<ex>*</ex></ph> <ph name="LINK_BEGIN">&lt;a href="$3<ex>https://support.google.com/chromebook/?p=multidevice</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
   </message>
   <message name="IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FOOTNOTE" desc="Footnote for multi-device feature setup page, which indicates that the exact set of features available to users differs according to the device model used.">
     <ph name="FOOTNOTE_POINTER">$1<ex>*</ex></ph>Features vary by device
@@ -169,7 +169,7 @@
     Select a device
   </message>
   <message name="IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FEATURE_LIST_HEADER" desc="Header to introduce a list of the features that the user is agreeing to in the multi-device setup.">
-    When you connect your devices, you agree that your Chromebook can:
+    When you connect your devices, you agree that your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> can:
   </message>
   <message name="IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_AWM_DESCRIPTION" desc="Description of a feature that shows text messages received by the user's phone as notification on their Chromebook and a link to an information page.">
     Send you notifications and default to remembering this computer for Messages. <ph name="LINK_BEGIN">&lt;a href="$1<ex>https://support.google.com/chromebook/?p=messages</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
@@ -1076,31 +1076,31 @@
     Battery isn't charging
   </message>
   <message name="IDS_WILCO_NOTIFICATION_BATTERY_AUTH_MESSAGE" desc="The message of the notification shown to inform the user that an unauthorized battery is connected to a Chromebook.">
-    To charge this Chromebook, use a battery from the same brand.
+    To charge this Chromebook, use a compatible Dell battery.
   </message>
   <message name="IDS_WILCO_NOTIFICATION_NON_WILCO_CHARGER_TITLE" desc="The title of the notification shown to inform the user that charger is unauthorized and battery will not charge.">
     Power adapter issue
   </message>
   <message name="IDS_WILCO_NOTIFICATION_NON_WILCO_CHARGER_MESSAGE" desc="The message of the notification shown to inform the user that charger is unauthorized and battery will not charge.">
-    Please use a USB Type-C power adapter or an adapter from the same brand as your Chromebook.
+    To avoid charging and performance issues, use a compatible Dell or USB Type-C power adapter.
   </message>
   <message name="IDS_WILCO_NOTIFICATION_INCOMPATIBLE_DOCK_TITLE" desc="The title of the notification shown to inform the user that attached dock is incompatible.">
-    Not all features on this dock are supported
+    Dock isn't fully compatible
   </message>
   <message name="IDS_WILCO_NOTIFICATION_INCOMPATIBLE_DOCK_MESSAGE" desc="The message of the notification shown to inform the user that attached dock is incompatible.">
-    Use a docking station designed to work with this Chromebook.
+    To get additional features, use a Dell docking station designed to work with this Chromebook.
   </message>
   <message name="IDS_WILCO_NOTIFICATION_DOCK_ERROR_TITLE" desc="The title of the notification shown to inform the user that attached dock presents hardware failures.">
-    Dock has an issue
+    Dock fan needs service
   </message>
   <message name="IDS_WILCO_NOTIFICATION_DOCK_ERROR_MESSAGE" desc="The message of the notification shown to inform the user that attached dock presents hardware failures.">
-    Your dock needs to be serviced. Without a working fan your dock will shut down.
+    Contact Dell for service. The dock will shut down if the fan isn't working.
   </message>
   <message name="IDS_WILCO_NOTIFICATION_DOCK_DISPLAY_TITLE" desc="The title of the notification shown to inform the user that HDMI and USB Type-C cannot be used for displays at the same time with the attached dock.">
-    Display connected to the dock has an issue
+    Dock video-port issue
   </message>
   <message name="IDS_WILCO_NOTIFICATION_DOCK_DISPLAY_MESSAGE" desc="The message of the notification shown to inform the user that HDMI and USB Type-C cannot be used for displays at the same time with the attached dock.">
-    Your dock's HDMI port can't be used when the USB Type-C port is used for video output. Please use a different port for one of the displays.
+    HDMI and USB Type-C ports can't be used for video at the same time. Use a different video port.
   </message>
   <message name="IDS_WILCO_NOTIFICATION_DOCK_THUNDERBOLT_TITLE" desc="The title of the notification shown to inform the user that attached dock will operate in USB Type-C compatible mode.">
     Thunderbolt is not supported
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 0e7623d..07d825c 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10028,9 +10028,16 @@
 
     <!-- User happiness tracking survey UI -->
     <if expr="not is_android">
-      <message name="IDS_HATS_BUBBLE_OK_LABEL" translateable="false" desc="Button label on Happiness Tracking Survey's invitation banner, clicking the button gives the user's consent on taking the survey.">
-        Take survey
-      </message>
+      <if expr="use_titlecase">
+        <message name="IDS_HATS_BUBBLE_OK_LABEL" translateable="false" desc="Button label on Happiness Tracking Survey's invitation banner, clicking the button gives the user's consent on taking the survey.">
+          Take Survey
+        </message>
+      </if>
+      <if expr="not use_titlecase">
+        <message name="IDS_HATS_BUBBLE_OK_LABEL" translateable="false" desc="Button label on Happiness Tracking Survey's invitation banner, clicking the button gives the user's consent on taking the survey.">
+          Take survey
+        </message>
+      </if>
       <message name="IDS_HATS_BUBBLE_TEXT" translateable="false" desc="The text in Happiness Tracking Survey's invitation banner, it explains why users should take our survey.">
         Your feedback is important to us.
       </message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 30f76f84..0c68024 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1354,11 +1354,11 @@
     {"enable-webrtc-hw-h264-encoding",
      flag_descriptions::kWebrtcHwH264EncodingName,
      flag_descriptions::kWebrtcHwH264EncodingDescription, kOsAndroid | kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kWebRtcHWH264Encoding)},
+     FEATURE_VALUE_TYPE(blink::features::kWebRtcHWH264Encoding)},
     {"enable-webrtc-hw-vp8-encoding",
      flag_descriptions::kWebrtcHwVP8EncodingName,
      flag_descriptions::kWebrtcHwVP8EncodingDescription, kOsAndroid | kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kWebRtcHWVP8Encoding)},
+     FEATURE_VALUE_TYPE(blink::features::kWebRtcHWVP8Encoding)},
 #if defined(WEBRTC_USE_PIPEWIRE)
     {"enable-webrtc-pipewire-capturer",
      flag_descriptions::kWebrtcPipeWireCapturerName,
@@ -1909,9 +1909,6 @@
     {"enable-homepage-tile", flag_descriptions::kHomepageTileName,
      flag_descriptions::kHomepageTileDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kHomepageTile)},
-    {"enable-search-ready-omnibox", flag_descriptions::kSearchReadyOmniboxName,
-     flag_descriptions::kSearchReadyOmniboxDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kSearchReadyOmniboxFeature)},
     {"enable-bookmark-reorder", flag_descriptions::kReorderBookmarksName,
      flag_descriptions::kReorderBookmarksDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kReorderBookmarks)},
@@ -2059,6 +2056,10 @@
      flag_descriptions::kEnableResourceLoadingHintsName,
      flag_descriptions::kEnableResourceLoadingHintsDescription, kOsAll,
      FEATURE_VALUE_TYPE(previews::features::kResourceLoadingHints)},
+    {"enable-previews-coin-flip",
+     flag_descriptions::kEnablePreviewsCoinFlipName,
+     flag_descriptions::kEnablePreviewsCoinFlipDescription, kOsAll,
+     FEATURE_VALUE_TYPE(previews::features::kCoinFlipHoldback)},
     {"allow-insecure-localhost", flag_descriptions::kAllowInsecureLocalhostName,
      flag_descriptions::kAllowInsecureLocalhostDescription, kOsAll,
      SINGLE_VALUE_TYPE(switches::kAllowInsecureLocalhost)},
@@ -2193,11 +2194,6 @@
      flag_descriptions::kSystemKeyboardLockDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kSystemKeyboardLock)},
 #if defined(OS_ANDROID)
-    {"progress-bar-throttle", flag_descriptions::kProgressBarThrottleName,
-     flag_descriptions::kProgressBarThrottleDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kProgressBarThrottleFeature)},
-#endif  // OS_ANDROID
-#if defined(OS_ANDROID)
     {"offline-pages-load-signal-collecting",
      flag_descriptions::kOfflinePagesLoadSignalCollectingName,
      flag_descriptions::kOfflinePagesLoadSignalCollectingDescription,
@@ -3936,7 +3932,7 @@
     {"enable-webrtc-hw-vp9-encoding",
      flag_descriptions::kWebrtcHwVP9EncodingName,
      flag_descriptions::kWebrtcHwVP9EncodingDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kWebRtcHWVP9Encoding)},
+     FEATURE_VALUE_TYPE(blink::features::kWebRtcHWVP9Encoding)},
 
 #if defined(OS_ANDROID)
     {"manual-password-generation-android",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 28922f2..18f2817 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/common/chrome_features.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/autofill_assistant/browser/features.h"
 #include "components/browser_sync/browser_sync_switches.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
@@ -54,6 +55,7 @@
 // this array may either refer to features defined in the header of this file or
 // in other locations in the code base (e.g. chrome/, components/, etc).
 const base::Feature* kFeaturesExposedToJava[] = {
+    &autofill::features::kAutofillCreditCardAuthentication,
     &autofill::features::kAutofillKeyboardAccessory,
     &autofill::features::kAutofillManualFallbackAndroid,
     &autofill::features::kAutofillRefreshStyleAndroid,
@@ -157,12 +159,10 @@
     &kOverlayNewLayout,
     &kPayWithGoogleV1,
     &kPhotoPickerVideoSupport,
-    &kProgressBarThrottleFeature,
     &kReachedCodeProfiler,
     &kReaderModeInCCT,
     &kReorderBookmarks,
     &kRevampedContextMenu,
-    &kSearchReadyOmniboxFeature,
     &kSearchEnginePromoExistingDevice,
     &kSearchEnginePromoNewDevice,
     &kServiceManagerForBackgroundPrefetch,
@@ -502,9 +502,6 @@
 const base::Feature kPhotoPickerVideoSupport{"PhotoPickerVideoSupport",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kProgressBarThrottleFeature{
-    "ProgressBarThrottle", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kReachedCodeProfiler{"ReachedCodeProfiler",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -517,9 +514,6 @@
 const base::Feature kRevampedContextMenu{"RevampedContextMenu",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kSearchReadyOmniboxFeature{
-    "SearchReadyOmnibox", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kServiceManagerForBackgroundPrefetch{
     "ServiceManagerForBackgroundPrefetch", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 3f5f101c..f47d6ac 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -95,14 +95,12 @@
 extern const base::Feature kOverlayNewLayout;
 extern const base::Feature kPayWithGoogleV1;
 extern const base::Feature kPhotoPickerVideoSupport;
-extern const base::Feature kProgressBarThrottleFeature;
 extern const base::Feature kReachedCodeProfiler;
 extern const base::Feature kReorderBookmarks;
 extern const base::Feature kReaderModeInCCT;
 extern const base::Feature kRevampedContextMenu;
 extern const base::Feature kSearchEnginePromoExistingDevice;
 extern const base::Feature kSearchEnginePromoNewDevice;
-extern const base::Feature kSearchReadyOmniboxFeature;
 extern const base::Feature kServiceManagerForBackgroundPrefetch;
 extern const base::Feature kServiceManagerForDownload;
 extern const base::Feature kSettingsModernStatusBar;
diff --git a/chrome/browser/android/webapk/webapk.proto b/chrome/browser/android/webapk/webapk.proto
index 8e08c9b..4ee431cc 100644
--- a/chrome/browser/android/webapk/webapk.proto
+++ b/chrome/browser/android/webapk/webapk.proto
@@ -114,12 +114,30 @@
   // image and may not match the murmur2 hash field above.
   optional bytes image_data = 6;
 
+  // Possible purposes that an icon can be used for.
+  enum Purpose {
+    // Missing purpose.
+    PURPOSE_UNDEFINED = 0;
+    // The icon can be displayed in any context.
+    ANY = 1;
+
+    // The icon can be used where space constraints and/or color requirements
+    // differ from those of the application icon.
+    // BADGE = 2; We do not plan to support Badge purpose, and will ignore it.
+
+    // The icon is designed with the intention to be masked.
+    MASKABLE = 3;
+
+    reserved 2;
+  }
+
+  // The purposes this image may be used for.
+  repeated Purpose purposes = 7;
+
   // Specifies Chrome's intended usages for the image.
   repeated Usage usages = 8;
 
-  optional bool is_primay_icon_maskable = 9;
-
-  reserved 2, 3, 4, 7;
+  reserved 2, 3, 4, 9;
 }
 
 // A proto representing a ShareTargetParamsFile
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index 78178c2..e764183 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -255,6 +255,11 @@
     webapk::Image* best_primary_icon_image = web_app_manifest->add_icons();
     SetImageData(best_primary_icon_image, primary_icon);
     best_primary_icon_image->add_usages(webapk::Image::PRIMARY_ICON);
+    if (is_primary_icon_maskable) {
+      best_primary_icon_image->add_purposes(webapk::Image::MASKABLE);
+    } else {
+      best_primary_icon_image->add_purposes(webapk::Image::ANY);
+    }
 
     if (!badge_icon.drawsNothing()) {
       webapk::Image* best_badge_icon_image = web_app_manifest->add_icons();
@@ -268,7 +273,11 @@
     if (entry.first == shortcut_info.best_primary_icon_url.spec()) {
       SetImageData(image, primary_icon);
       image->add_usages(webapk::Image::PRIMARY_ICON);
-      image->set_is_primay_icon_maskable(is_primary_icon_maskable);
+      if (is_primary_icon_maskable) {
+        image->add_purposes(webapk::Image::MASKABLE);
+      } else {
+        image->add_purposes(webapk::Image::ANY);
+      }
     }
     if (entry.first == shortcut_info.best_badge_icon_url.spec()) {
       if (shortcut_info.best_badge_icon_url !=
diff --git a/chrome/browser/chrome_security_exploit_browsertest.cc b/chrome/browser/chrome_security_exploit_browsertest.cc
index ffdb11d..4b57cf01 100644
--- a/chrome/browser/chrome_security_exploit_browsertest.cc
+++ b/chrome/browser/chrome_security_exploit_browsertest.cc
@@ -27,6 +27,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/common/extension_urls.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -422,7 +423,7 @@
     return url_store_.get();
   }
 
-  void Register(blink::mojom::BlobPtr blob,
+  void Register(mojo::PendingRemote<blink::mojom::Blob> blob,
                 const GURL& url,
                 RegisterCallback callback) override {
     GetForwardingInterface()->Register(std::move(blob), target_url_,
diff --git a/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_browsertest.cc b/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_browsertest.cc
index 1fd9588..9aadea1 100644
--- a/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_browsertest.cc
+++ b/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_browsertest.cc
@@ -14,10 +14,10 @@
 #include "chrome/browser/chromeos/child_accounts/parent_access_code/config_source.h"
 #include "chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service.h"
 #include "chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_test_utils.h"
+#include "chrome/browser/chromeos/policy/login_policy_test_base.h"
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/supervised_user/supervised_user_test_base.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/policy_constants.h"
@@ -82,7 +82,7 @@
   DISALLOW_COPY_AND_ASSIGN(TestParentAccessServiceObserver);
 };
 
-class ParentAccessServiceTest : public SupervisedUserTestBase {
+class ParentAccessServiceTest : public policy::LoginPolicyTestBase {
  public:
   ParentAccessServiceTest()
       : test_observer_(
@@ -95,18 +95,18 @@
     policy::BrowserPolicyConnector::SetNonEnterpriseDomainForTesting(
         "example.com");
 
-    SupervisedUserTestBase::SetUp();
+    policy::LoginPolicyTestBase::SetUp();
   }
 
   void SetUpOnMainThread() override {
     ASSERT_NO_FATAL_FAILURE(GetTestAccessCodeValues(&test_values_));
     ParentAccessService::Get().AddObserver(test_observer_.get());
-    SupervisedUserTestBase::SetUpOnMainThread();
+    policy::LoginPolicyTestBase::SetUpOnMainThread();
   }
 
   void TearDownOnMainThread() override {
     ParentAccessService::Get().RemoveObserver(test_observer_.get());
-    SupervisedUserTestBase::TearDownOnMainThread();
+    policy::LoginPolicyTestBase::TearDownOnMainThread();
   }
 
   std::string GetIdToken() const override {
@@ -114,6 +114,11 @@
   }
 
  protected:
+  void LogInChild() {
+    SkipToLoginScreen();
+    LogIn(kAccountId, kAccountPassword, test::kChildAccountServiceFlags);
+  }
+
   // Updates the policy containing the Parent Access Code config.
   void UpdatePolicy(const base::DictionaryValue& dict) {
     const user_manager::UserManager* const user_manager =
@@ -156,7 +161,7 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, NoConfigAvailable) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   auto test_value = test_values_.begin();
   EXPECT_FALSE(ValidateAccessCode(test_value->second, test_value->first));
@@ -165,7 +170,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, NoValidConfigAvailable) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   std::vector<AccessCodeConfig> old_configs;
   old_configs.emplace_back(GetInvalidTestConfig());
@@ -179,7 +184,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, ValidationWithFutureConfig) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   std::vector<AccessCodeConfig> old_configs;
   old_configs.emplace_back(GetInvalidTestConfig());
@@ -193,7 +198,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, ValidationWithCurrentConfig) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   std::vector<AccessCodeConfig> old_configs;
   old_configs.emplace_back(GetInvalidTestConfig());
@@ -207,7 +212,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, ValidationWithOldConfig) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   std::vector<AccessCodeConfig> old_configs;
   old_configs.emplace_back(GetInvalidTestConfig());
@@ -222,7 +227,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, MultipleValidationAttempts) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   AccessCodeValues::iterator test_value = test_values_.begin();
 
@@ -246,7 +251,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, NoObserver) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   ParentAccessService::Get().RemoveObserver(test_observer_.get());
 
@@ -260,7 +265,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, NoAccountId) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   ParentAccessService::Get().RemoveObserver(test_observer_.get());
 
@@ -274,7 +279,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessServiceTest, InvalidAccountId) {
-  LogInUser(LogInType::kChild);
+  LogInChild();
 
   ParentAccessService::Get().RemoveObserver(test_observer_.get());
 
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
index f7683e2..5e73bcd 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
@@ -19,10 +19,10 @@
 #include "chrome/browser/chromeos/child_accounts/time_limit_test_utils.h"
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 #include "chrome/browser/chromeos/login/lock/screen_locker_tester.h"
+#include "chrome/browser/chromeos/policy/login_policy_test_base.h"
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/supervised_user/supervised_user_test_base.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
@@ -62,7 +62,7 @@
 
 // Allows testing ScreenTimeController with UsageTimeStateNotifier enabled
 // (instantiated with |true|) or disabled (instantiated with |false|).
-class ScreenTimeControllerTest : public SupervisedUserTestBase,
+class ScreenTimeControllerTest : public policy::LoginPolicyTestBase,
                                  public testing::WithParamInterface<bool> {
  public:
   ScreenTimeControllerTest() = default;
@@ -84,7 +84,7 @@
     policy::BrowserPolicyConnector::SetNonEnterpriseDomainForTesting(
         "example.com");
 
-    SupervisedUserTestBase::SetUp();
+    policy::LoginPolicyTestBase::SetUp();
   }
 
   void GetMandatoryPoliciesValue(base::DictionaryValue* policy) const override {
@@ -102,7 +102,8 @@
  protected:
   void LogInChildAndSetupClockWithTime(const char* time) {
     SetupTaskRunnerWithTime(utils::TimeFromString(time));
-    LogInUser(LogInType::kChild);
+    SkipToLoginScreen();
+    LogIn(kAccountId, kAccountPassword, test::kChildAccountServiceFlags);
     MockClockForActiveUser();
   }
 
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index c458c08d..98a246c4 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -13,6 +13,7 @@
 #include "base/bind_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/files/file_util.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
@@ -136,6 +137,7 @@
         restart_id_(next_restart_id_++) {}
 
   void Restart() {
+    is_initial_install_ = crostini_manager_->GetInstallerViewStatus();
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     if (!IsCrostiniUIAllowedForProfile(profile_)) {
       LOG(ERROR) << "Crostini UI not allowed for profile "
@@ -173,6 +175,7 @@
     is_aborted_ = true;
     observer_list_.Clear();
     abort_callback_ = std::move(callback);
+    ReportRestarterResult(CrostiniResult::RESTART_ABORTED);
   }
 
   void OnContainerDownloading(int download_percent) {
@@ -198,8 +201,16 @@
     }
   }
 
+  void ReportRestarterResult(CrostiniResult result) {
+    // Do not record results if this restart was triggered by the installer. The
+    // crostini installer has its own histograms that should be kept separate.
+    if (!is_initial_install_)
+      base::UmaHistogramEnumeration("Crostini.RestarterResult", result);
+  }
+
   void FinishRestart(CrostiniResult result) {
     crostini_manager_->FinishRestart(this, result);
+    ReportRestarterResult(result);
   }
 
   void LoadComponentFinished(CrostiniResult result) {
@@ -447,21 +458,23 @@
                  << ", mount_path=" << mount_info.mount_path
                  << ", mount_type=" << mount_info.mount_type
                  << ", mount_condition=" << mount_info.mount_condition;
-    } else {
-      crostini_manager_->SetContainerSshfsMounted(vm_name_, container_name_,
+      FinishRestart(CrostiniResult::SSHFS_MOUNT_ERROR);
+      return;
+    }
+
+    crostini_manager_->SetContainerSshfsMounted(vm_name_, container_name_,
                                                   true);
 
-      // Register filesystem and add volume to VolumeManager.
-      base::FilePath mount_path = base::FilePath(mount_info.mount_path);
-      storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-          file_manager::util::GetCrostiniMountPointName(profile_),
-          storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
-          mount_path);
+    // Register filesystem and add volume to VolumeManager.
+    base::FilePath mount_path = base::FilePath(mount_info.mount_path);
+    storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+        file_manager::util::GetCrostiniMountPointName(profile_),
+        storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+        mount_path);
 
-      // VolumeManager is null in unittest.
-      if (auto* vmgr = file_manager::VolumeManager::Get(profile_))
-        vmgr->AddSshfsCrostiniVolume(mount_path);
-    }
+    // VolumeManager is null in unittest.
+    if (auto* vmgr = file_manager::VolumeManager::Get(profile_))
+      vmgr->AddSshfsCrostiniVolume(mount_path);
 
     // Abort not checked until end of function.  On abort, do not continue,
     // but still remove observer and add volume as per above.
@@ -481,6 +494,7 @@
   std::string vm_name_;
   std::string container_name_;
   std::string source_path_;
+  bool is_initial_install_ = true;
   CrostiniManager::CrostiniResultCallback completed_callback_;
   base::OnceClosure abort_callback_;
   base::ObserverList<CrostiniManager::RestartObserver>::Unchecked
diff --git a/chrome/browser/chromeos/crostini/crostini_simple_types.h b/chrome/browser/chromeos/crostini/crostini_simple_types.h
index c23df3f4..44a15ac5 100644
--- a/chrome/browser/chromeos/crostini/crostini_simple_types.h
+++ b/chrome/browser/chromeos/crostini/crostini_simple_types.h
@@ -43,7 +43,7 @@
   INSTALL_LINUX_PACKAGE_FAILED = 17,
   BLOCKING_OPERATION_ALREADY_ACTIVE = 18,
   UNINSTALL_PACKAGE_FAILED = 19,
-  // SSHFS_MOUNT_ERROR = 20,
+  SSHFS_MOUNT_ERROR = 20,
   OFFLINE_WHEN_UPGRADE_REQUIRED = 21,
   LOAD_COMPONENT_FAILED = 22,
   // PERMISSION_BROKER_ERROR = 23,
@@ -61,6 +61,8 @@
   CONTAINER_EXPORT_IMPORT_FAILED_SPACE = 35,
   GET_CONTAINER_SSH_KEYS_FAILED = 36,
   CONTAINER_EXPORT_IMPORT_CANCELLED = 37,
+  RESTART_ABORTED = 38,
+  kMaxValue = RESTART_ABORTED,
 };
 
 enum class InstallLinuxPackageProgressStatus {
diff --git a/chrome/browser/chromeos/first_run/goodies_displayer.cc b/chrome/browser/chromeos/first_run/goodies_displayer.cc
index 49249cd..8dd74db0 100644
--- a/chrome/browser/chromeos/first_run/goodies_displayer.cc
+++ b/chrome/browser/chromeos/first_run/goodies_displayer.cc
@@ -65,7 +65,7 @@
 }  // namespace
 
 const char GoodiesDisplayer::kGoodiesURL[] =
-    "https://www.google.com/chrome/devices/goodies.html";
+    "https://www.google.com/chromebook/offers/";
 
 GoodiesDisplayer::GoodiesDisplayer() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
index dcbc0a9..e88e6e18 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
@@ -383,8 +383,6 @@
                               embedded_test_server_.get()) {
     // Suppress regular user login to enable child user login.
     set_chromeos_user_ = false;
-    // Launch a browser instance after logging in.
-    logged_in_user_mixin_.set_should_launch_browser(true);
   }
 
   void SetUp() override {
@@ -427,14 +425,8 @@
   void SetUpOnMainThread() override {
     mixin_host_.SetUpOnMainThread();
     ExtensionWebstorePrivateApiTest::SetUpOnMainThread();
-    // Needed for resolving FakeGaiaMixin token requests.
-    // Otherwise the test times out.
-    host_resolver()->AddRule("*", "127.0.0.1");
-    logged_in_user_mixin_.LogInUser(true /* issue_any_scope_token */);
-    // Set the private |browser_| member in InProcessBrowserTest.
-    // Otherwise calls to InProcessBrowserTest::browser() returns null and leads
-    // to segmentation faults.
-    SelectFirstBrowser();
+    logged_in_user_mixin_.SetUpOnMainThreadHelper(
+        host_resolver(), this, true /* issue_any_scope_token */);
   }
 
   void TearDownOnMainThread() override {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index a9fe92c5..c5730518 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1461,7 +1461,7 @@
   {
     "name": "enable-ntp-remote-suggestions",
     "owners": [ "fgorski" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 80
   },
   {
     "name": "enable-offer-store-unmasked-wallet-cards",
@@ -1509,6 +1509,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "enable-previews-coin-flip",
+    "owners": [ "//components/data_reduction_proxy/OWNERS" ],
+    "expiry_milestone": 90
+  },
+  {
     "name": "enable-process-sharing-with-default-site-instances",
     "owners": [ "acolwell" ],
     "expiry_milestone": 81
@@ -1601,11 +1606,6 @@
     "expiry_milestone": 78
   },
   {
-    "name": "enable-search-ready-omnibox",
-    "owners": [ "mdjones" ],
-    "expiry_milestone": 75
-  },
-  {
     "name": "enable-send-tab-to-self",
     "owners": [ "//components/send_tab_to_self/OWNERS" ],
     "expiry_milestone": 77
@@ -2851,11 +2851,6 @@
     "expiry_milestone": 82
   },
   {
-    "name": "progress-bar-throttle",
-    "owners": [ "mdjones" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "pull-to-refresh",
     "owners": [ "afakhry" ],
     "expiry_milestone": 83
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index ffd8dcb..24a51bf 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -670,6 +670,10 @@
     "This requires enable-lite-page-server-previews to be enabled along with "
     "network-service.";
 
+const char kEnablePreviewsCoinFlipName[] = "Enable Previews Coin Flip";
+const char kEnablePreviewsCoinFlipDescription[] =
+    "Enable coin flip experimentation of Previews.";
+
 const char kBuiltInModuleAllName[] = "All experimental built-in modules";
 const char kBuiltInModuleAllDescription[] =
     "Enable all experimental built-in modules, as well as built-in module "
@@ -2619,10 +2623,6 @@
     "separated like strict site isolation, but process selection puts multiple "
     "site instances in a single process.";
 
-const char kProgressBarThrottleName[] = "Android progress update throttling.";
-const char kProgressBarThrottleDescription[] =
-    "Limit the maximum progress update to make progress appear smoother.";
-
 const char kQuietNotificationPromptsName[] =
     "Quieter notification permission prompts";
 const char kQuietNotificationPromptsDescription[] =
@@ -2650,11 +2650,6 @@
     "process to check the Safe Browsing reputation of URLs without calling "
     "into GmsCore for every URL.";
 
-const char kSearchReadyOmniboxName[] = "Search Ready Omnibox";
-const char kSearchReadyOmniboxDescription[] =
-    "Clears the omnibox and adds a suggestion item to share, copy, or edit the "
-    "URL.";
-
 const char kSetMarketUrlForTestingName[] = "Set market URL for testing";
 const char kSetMarketUrlForTestingDescription[] =
     "When enabled, sets the market URL for use in testing the update menu "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 1daa47b2..185e392d 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -514,6 +514,9 @@
 extern const char kEnablePixelCanvasRecordingName[];
 extern const char kEnablePixelCanvasRecordingDescription[];
 
+extern const char kEnablePreviewsCoinFlipName[];
+extern const char kEnablePreviewsCoinFlipDescription[];
+
 extern const char kEnableResamplingInputEventsName[];
 extern const char kEnableResamplingInputEventsDescription[];
 extern const char kEnableResamplingScrollEventsName[];
@@ -1547,9 +1550,6 @@
 extern const char kProcessSharingWithStrictSiteInstancesName[];
 extern const char kProcessSharingWithStrictSiteInstancesDescription[];
 
-extern const char kProgressBarThrottleName[];
-extern const char kProgressBarThrottleDescription[];
-
 extern const char kQuietNotificationPromptsName[];
 extern const char kQuietNotificationPromptsDescription[];
 
@@ -1567,9 +1567,6 @@
 extern const char kSafeBrowsingUseLocalBlacklistsV2Name[];
 extern const char kSafeBrowsingUseLocalBlacklistsV2Description[];
 
-extern const char kSearchReadyOmniboxName[];
-extern const char kSearchReadyOmniboxDescription[];
-
 extern const char kSetMarketUrlForTestingName[];
 extern const char kSetMarketUrlForTestingDescription[];
 
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn
index 9f0df50..b4083546 100644
--- a/chrome/browser/media/router/BUILD.gn
+++ b/chrome/browser/media/router/BUILD.gn
@@ -11,6 +11,7 @@
   deps = [
     "//base",
     "//chrome/common:constants",
+    "//components/cast_channel",
     "//components/keyed_service/content",
     "//components/keyed_service/core",
     "//content/public/browser",
diff --git a/chrome/browser/notifications/scheduler/display_agent_android.cc b/chrome/browser/notifications/scheduler/display_agent_android.cc
index 9aac0217..3cba9774 100644
--- a/chrome/browser/notifications/scheduler/display_agent_android.cc
+++ b/chrome/browser/notifications/scheduler/display_agent_android.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/notifications/scheduler/public/user_action_handler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
+#include "ui/gfx/android/java_bitmap.h"
 
 using base::android::ConvertUTF16ToJavaString;
 using base::android::ConvertUTF8ToJavaString;
@@ -77,8 +78,14 @@
   // test.
   auto java_notification_data = Java_DisplayAgent_buildNotificationData(
       env, ConvertUTF16ToJavaString(env, notification_data->title),
-      ConvertUTF16ToJavaString(env, notification_data->message),
-      nullptr /*icon*/);
+      ConvertUTF16ToJavaString(env, notification_data->message));
+
+  for (const auto& icon : notification_data->icons) {
+    // TODO(hesen): Support Android resource Id.
+    Java_DisplayAgent_addIconWithBitmap(
+        env, java_notification_data, static_cast<int>(icon.first /*IconType*/),
+        gfx::ConvertToJavaBitmap(&icon.second.bitmap));
+  }
 
   for (size_t i = 0; i < notification_data->buttons.size(); ++i) {
     const auto& button = notification_data->buttons[i];
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
index 7818fe88..f193f8e 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
@@ -234,7 +234,8 @@
       std::unique_ptr<NotificationEntry> entry,
       std::unique_ptr<NotificationData> updated_notification_data) {
     if (!updated_notification_data) {
-      // TODO(xingliu): Client has drop the data, track metrics here.
+      stats::LogNotificationLifeCycleEvent(
+          stats::NotificationLifeCycleEvent::kClientCancel, entry->type);
       return;
     }
 
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
index b32c75a..24311c3 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
@@ -99,6 +99,8 @@
     std::string guid = notification_params->guid;
     DCHECK(!guid.empty());
     auto type = notification_params->type;
+    stats::LogNotificationLifeCycleEvent(
+        stats::NotificationLifeCycleEvent::kScheduleRequest, type);
 
     if (!clients_.count(type) ||
         (notifications_.count(type) && notifications_[type].count(guid))) {
@@ -108,8 +110,11 @@
 
     bool valid = ValidateNotificationParams(*notification_params);
     DCHECK(valid) << "Invalid notification parameters.";
-    if (!valid)
+    if (!valid) {
+      stats::LogNotificationLifeCycleEvent(
+          stats::NotificationLifeCycleEvent::kInvalidInput, type);
       return;
+    }
 
     if (notification_params->enable_ihnr_buttons) {
       CreateInhrButtonsPair(&notification_params->notification_data.buttons);
@@ -292,6 +297,9 @@
     auto type = entry->type;
     auto guid = entry->guid;
     notifications_[type][guid] = std::move(entry);
+
+    stats::LogNotificationLifeCycleEvent(
+        stats::NotificationLifeCycleEvent::kScheduled, type);
     std::move(schedule_callback).Run(true);
   }
 
diff --git a/chrome/browser/notifications/scheduler/internal/stats.cc b/chrome/browser/notifications/scheduler/internal/stats.cc
index 621069d..6f752f2e 100644
--- a/chrome/browser/notifications/scheduler/internal/stats.cc
+++ b/chrome/browser/notifications/scheduler/internal/stats.cc
@@ -140,6 +140,15 @@
     LogHistogramEnumWithSuffix(kIhnrActionButtonEventHistogram,
                                ActionButtonEvent::kShown, client_type);
   }
+
+  LogNotificationLifeCycleEvent(NotificationLifeCycleEvent::kShown,
+                                client_type);
+}
+
+void LogNotificationLifeCycleEvent(NotificationLifeCycleEvent event,
+                                   SchedulerClientType client_type) {
+  LogHistogramEnumWithSuffix(
+      "Notifications.Scheduler.NotificationLifeCycleEvent", event, client_type);
 }
 
 void LogPngIconConverterEncodeResult(bool success) {
diff --git a/chrome/browser/notifications/scheduler/internal/stats.h b/chrome/browser/notifications/scheduler/internal/stats.h
index 6a570305..0e8ffdb 100644
--- a/chrome/browser/notifications/scheduler/internal/stats.h
+++ b/chrome/browser/notifications/scheduler/internal/stats.h
@@ -49,6 +49,23 @@
   kMaxValue = kSuppressionExpired
 };
 
+// Event to track the life cycle of a scheduled notification. Don't reuse or
+// delete values. Needs to match NotificationSchedulerNotificationLifeCycleEvent
+// in enums.xml.
+enum class NotificationLifeCycleEvent {
+  // The client requests to schedule the notification.
+  kScheduleRequest = 0,
+  // The notification is successfully scheduled.
+  kScheduled = 1,
+  // The notification is dropped due to invalid input parameters.
+  kInvalidInput = 2,
+  // The notification is shown to the user.
+  kShown = 3,
+  // The notification is canceled by the client before showing the notification.
+  kClientCancel = 4,
+  kMaxValue = kClientCancel
+};
+
 // Logs the user action when the user interacts with notification sent from the
 // scheduling system.
 void LogUserAction(const UserActionData& user_action_data);
@@ -82,6 +99,10 @@
 void LogNotificationShow(const NotificationData& notification_data,
                          SchedulerClientType client_type);
 
+// Logs scheduled notification life cycle event.
+void LogNotificationLifeCycleEvent(NotificationLifeCycleEvent event,
+                                   SchedulerClientType client_type);
+
 // Logs png icon converter encode result.
 void LogPngIconConverterEncodeResult(bool success);
 
diff --git a/chrome/browser/notifications/scheduler/internal/stats_unittest.cc b/chrome/browser/notifications/scheduler/internal/stats_unittest.cc
index 3ea4820..537a8227 100644
--- a/chrome/browser/notifications/scheduler/internal/stats_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/stats_unittest.cc
@@ -34,7 +34,8 @@
 
 void TestNotificationShow(const NotificationData& notification_data,
                           SchedulerClientType client_type,
-                          bool expect_ihnr_histogram) {
+                          bool expect_ihnr_histogram,
+                          bool expect_life_cycle_histogram) {
   base::HistogramTester histograms;
   LogNotificationShow(notification_data, client_type);
   if (expect_ihnr_histogram) {
@@ -46,6 +47,20 @@
     histograms.ExpectTotalCount(kIhnrActionButtonEventHistogram, 0);
     histograms.ExpectTotalCount(kIhnrActionButtonEventTestHistogram, 0);
   }
+
+  if (expect_life_cycle_histogram) {
+    histograms.ExpectBucketCount(
+        "Notifications.Scheduler.NotificationLifeCycleEvent",
+        NotificationLifeCycleEvent::kShown, 1);
+    histograms.ExpectBucketCount(
+        "Notifications.Scheduler.NotificationLifeCycleEvent.__Test__",
+        NotificationLifeCycleEvent::kShown, 1);
+  } else {
+    histograms.ExpectTotalCount(
+        "Notifications.Scheduler.NotificationLifeCycleEvent", 0);
+    histograms.ExpectTotalCount(
+        "Notifications.Scheduler.NotificationLifeCycleEvent.__Test__", 0);
+  }
 }
 
 // Verifies that ihnr buttons clicks are logged.
@@ -64,9 +79,11 @@
 TEST(NotificationSchedulerStatsTest, LogNotificationShow) {
   NotificationData notification_data;
   notification_data.buttons.emplace_back(NotificationData::Button());
+
   // Notification without ihnr buttons.
   TestNotificationShow(notification_data, SchedulerClientType::kTest1,
-                       false /*expect_ihnr_histogram*/);
+                       false /*expect_ihnr_histogram*/,
+                       true /*expect_life_cycle_histogram*/);
 
   std::vector<ActionButtonType> types{ActionButtonType::kHelpful,
                                       ActionButtonType::kUnhelpful};
@@ -74,7 +91,8 @@
     notification_data.buttons.front().type = action_button_type;
     // Notification with ihnr buttons.
     TestNotificationShow(notification_data, SchedulerClientType::kTest1,
-                         true /*expect_ihnr_histogram*/);
+                         true /*expect_ihnr_histogram*/,
+                         true /*expect_life_cycle_histogram*/);
   }
 }
 
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h
index 3b69178a..593912a 100644
--- a/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h
+++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h
@@ -148,6 +148,9 @@
 };
 
 // Categorizes type of notification icons.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.chrome.browser.notifications.scheduler)
 enum class IconType {
   kUnknownType = 0,
   kSmallIcon = 1,
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 7a64c4a..9a779317 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -61,6 +61,7 @@
 #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h"
 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h"
+#include "chrome/browser/sharing/sharing_metrics.h"
 #include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/browser/translate/translate_service.h"
@@ -1823,10 +1824,13 @@
 }
 
 void RenderViewContextMenu::MaybeAppendClickToCallItem() {
+  SharingClickToCallEntryPoint entry_point;
   base::Optional<std::string> phone_number;
   if (ShouldOfferClickToCallForURL(browser_context_, params_.link_url)) {
+    entry_point = SharingClickToCallEntryPoint::kRightClickLink;
     phone_number = GetUnescapedURLContent(params_.link_url);
   } else if (!params_.selection_text.empty()) {
+    entry_point = SharingClickToCallEntryPoint::kRightClickSelection;
     phone_number = ExtractPhoneNumberForClickToCall(
         browser_context_, base::UTF16ToUTF8(params_.selection_text));
   }
@@ -1840,7 +1844,7 @@
     observers_.AddObserver(click_to_call_context_menu_observer_.get());
   }
 
-  click_to_call_context_menu_observer_->BuildMenu(*phone_number);
+  click_to_call_context_menu_observer_->BuildMenu(*phone_number, entry_point);
 }
 
 // Menu delegate functions -----------------------------------------------------
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.html b/chrome/browser/resources/chromeos/login/security_token_pin.html
index 03012d6..21ca735 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin.html
+++ b/chrome/browser/resources/chromeos/login/security_token_pin.html
@@ -52,12 +52,10 @@
         [[i18nDynamic(locale, 'securityTokenPinDialogTitle')]]
       </h1>
       <div slot="subtitle">
-        <p>
-          [[i18nDynamic(locale, 'securityTokenPinDialogSubtitle')]]
-          <a href="#" id="learnMore">
-            [[i18nDynamic(locale, 'learnMoreButton')]]
-          </a>
-        </p>
+        [[i18nDynamic(locale, 'securityTokenPinDialogSubtitle')]]
+        <a href="#" id="learnMore">
+          [[i18nDynamic(locale, 'learnMoreButton')]]
+        </a>
       </div>
       <div slot="footer">
         <div id="pinKeyboardContainer">
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
index 1d859583..4cc49b3 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
@@ -196,7 +196,8 @@
   var options = {
     frame: 'none',
     innerBounds: {width: 768, height: 512, minWidth: 768, minHeight: 512},
-    resizable: true
+    resizable: true,
+    alphaEnabled: true
   };
 
   chrome.app.window.create('main.html', options, function(window) {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
index 63a82f92..afe509d3 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
@@ -10,14 +10,25 @@
     ":api_listener",
     ":app_item",
     ":app_management_page",
+    ":app_permission_view",
+    ":arc_permission_view",
     ":browser_proxy",
+    ":chrome_app_permission_view",
     ":constants",
+    ":dom_switch",
     ":fake_page_handler",
     ":main_view",
+    ":permission_item",
+    ":permission_toggle",
+    ":pin_to_shelf_item",
+    ":pwa_permission_view",
     ":reducers",
+    ":router",
     ":store",
     ":store_client",
+    ":toggle_row",
     ":types",
+    ":uninstall_button",
     ":util",
   ]
 }
@@ -51,11 +62,32 @@
     ":actions",
     ":browser_proxy",
     ":main_view",
+    ":router",
     ":store",
     ":store_client",
   ]
 }
 
+js_library("app_permission_view") {
+  deps = [
+    ":arc_permission_view",
+    ":chrome_app_permission_view",
+    ":dom_switch",
+    ":pwa_permission_view",
+    ":store_client",
+  ]
+}
+
+js_library("arc_permission_view") {
+  deps = [
+    ":constants",
+    ":fake_page_handler",
+    ":permission_item",
+    ":pin_to_shelf_item",
+    ":store_client",
+  ]
+}
+
 js_library("browser_proxy") {
   deps = [
     ":fake_page_handler",
@@ -64,9 +96,20 @@
   ]
 }
 
+js_library("chrome_app_permission_view") {
+  deps = [
+    ":fake_page_handler",
+    ":pin_to_shelf_item",
+    ":store_client",
+  ]
+}
+
 js_library("constants") {
 }
 
+js_library("dom_switch") {
+}
+
 js_library("fake_page_handler") {
   deps = [
     ":constants",
@@ -87,6 +130,39 @@
   ]
 }
 
+js_library("permission_item") {
+  deps = [
+    ":fake_page_handler",
+    ":permission_toggle",
+    ":store_client",
+    ":util",
+  ]
+}
+
+js_library("permission_toggle") {
+  deps = [
+    ":util",
+  ]
+}
+
+js_library("pin_to_shelf_item") {
+  deps = [
+    ":browser_proxy",
+    ":toggle_row",
+    ":types",
+  ]
+}
+
+js_library("pwa_permission_view") {
+  deps = [
+    ":constants",
+    ":fake_page_handler",
+    ":permission_item",
+    ":pin_to_shelf_item",
+    ":store_client",
+  ]
+}
+
 js_library("reducers") {
   deps = [
     ":types",
@@ -95,6 +171,14 @@
   ]
 }
 
+js_library("router") {
+  deps = [
+    ":actions",
+    ":constants",
+    ":store_client",
+  ]
+}
+
 js_library("store") {
   deps = [
     ":reducers",
@@ -114,12 +198,27 @@
   ]
 }
 
+js_library("toggle_row") {
+  deps = [
+    ":types",
+    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
+  ]
+}
+
 js_library("types") {
   deps = [
     "//chrome/browser/ui/webui/app_management:mojo_bindings_js_library_for_compile",
   ]
 }
 
+js_library("uninstall_button") {
+  deps = [
+    ":store_client",
+    ":util",
+    "//ui/webui/resources/js:cr",
+  ]
+}
+
 js_library("util") {
   deps = [
     ":constants",
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
index 9f2ed4a..d015876 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
@@ -17,7 +17,7 @@
         display: flex;
         flex-direction: row;
         font-weight: 400;
-        height: 64px;
+        height: 48px;
       }
 
       #app-title {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_management_page.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_management_page.html
index cae55fa..db69cd2 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_management_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_management_page.html
@@ -3,6 +3,7 @@
 <link rel="import" href="actions.html">
 <link rel="import" href="browser_proxy.html">
 <link rel="import" href="main_view.html">
+<link rel="import" href="router.html">
 <link rel="import" href="store_client.html">
 <link rel="import" href="store.html">
 <link rel="import" href="../../../settings_shared_css.html">
@@ -15,15 +16,10 @@
   <template>
     <style include="settings-shared"></style>
     <div id="main-container">
-      <!-- TODO: enable dom-switch after main-view is working -->
-      <!-- <app-management-dom-switch id="view-selector"
-          route="[[selectRouteId_(currentPage_, searchTerm_)]]">
-        <template> -->
-          <app-management-main-view route-id="main-view">
-          </app-management-main-view>
-        <!-- </template>
-      </app-management-dom-switch> -->
+      <app-management-main-view>
+      </app-management-main-view>
     </div>
+    <app-management-router></app-management-router>
   </template>
   <script src="app_management_page.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_permission_view.html
new file mode 100644
index 0000000..10bfef9
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_permission_view.html
@@ -0,0 +1,27 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="dom_switch.html">
+<link rel="import" href="pwa_permission_view.html">
+<link rel="import" href="arc_permission_view.html">
+<link rel="import" href="chrome_app_permission_view.html">
+<link rel="import" href="../../../settings_shared_css.html">
+
+<dom-module id="app-management-app-permission-view">
+  <template>
+    <style include="settings-shared">
+    </style>
+    <app-management-dom-switch id="view-selector"
+        route="[[getSelectedRouteId_(app_)]]">
+      <template>
+        <app-management-pwa-permission-view route-id="pwa-permission-view">
+        </app-management-pwa-permission-view>
+        <app-management-arc-permission-view route-id="arc-permission-view">
+        </app-management-arc-permission-view>
+        <app-management-chrome-app-permission-view
+            route-id="chrome-app-permission-view">
+        </app-management-chrome-app-permission-view>
+      </template>
+    </app-management-dom-switch>
+  </template>
+  <script src="app_permission_view.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_permission_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_permission_view.js
new file mode 100644
index 0000000..7a403ba
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_permission_view.js
@@ -0,0 +1,52 @@
+// 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.
+
+Polymer({
+  // TODO(crbug.com/999016): change to app-management-app-detail-view.
+  is: 'app-management-app-permission-view',
+
+  behaviors: [
+    app_management.StoreClient,
+  ],
+
+  properties: {
+    /**
+     * @type {App}
+     * @private
+     */
+    app_: Object,
+  },
+
+  attached: function() {
+    if (!this.app_) {
+      const appId = settings.getQueryParameters().get('id');
+      // TODO(crbug.com/999443): move this changePage call to router.js
+      this.dispatch(app_management.actions.changePage(PageType.DETAIL, appId));
+    }
+    this.watch('app_', state => app_management.util.getSelectedApp(state));
+    this.watch('currentPage_', state => state.currentPage);
+    this.updateFromStore();
+  },
+
+  /**
+   * @private
+   */
+  getSelectedRouteId_: function(app) {
+    if (!app) {
+      return;
+    }
+
+    const selectedAppType = app.type;
+    switch (selectedAppType) {
+      case (AppType.kWeb):
+        return 'pwa-permission-view';
+      case (AppType.kExtension):
+        return 'chrome-app-permission-view';
+      case (AppType.kArc):
+        return 'arc-permission-view';
+      default:
+        assertNotReached();
+    }
+  },
+});
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
index 1ffb3a6..7fc3dc8 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
@@ -2,9 +2,7 @@
 
 <link rel="import" href="browser_proxy.html">
 <link rel="import" href="icons.html">
-<link rel="import" href="metadata_view.html">
 <link rel="import" href="permission_item.html">
-<link rel="import" href="permission_view_header.html">
 <link rel="import" href="shared_style.html">
 <link rel="import" href="store_client.html">
 <link rel="import" href="pin_to_shelf_item.html">
@@ -16,10 +14,7 @@
     <style include="app-management-shared-css">
     </style>
 
-    <app-management-permission-view-header>
-    </app-management-permission-view-header>
-
-    <div class="permission-list card-container">
+    <div class="permission-list">
       <app-management-pin-to-shelf-item
         id="pin-to-shelf-setting"
         class="permission-card-row separated-row header-text"
@@ -33,24 +28,24 @@
       <div id="permissions-card"
         class="permission-card-row"
         hidden$="[[!isArcSupported_]]">
-        <div class="subpermission-list">
-          <div id="subpermission-expand-row"
-            class="subpermission-row separated-row"
-            hidden$="[[!isArcSupported_]]">
-            <div class="header-text">$i18n{permissions}</div>
-          </div>
+        <div id="subpermission-expand-row"
+          class="permission-section-header"
+          hidden$="[[!isArcSupported_]]">
+          <div class="header-text">$i18n{permissions}</div>
+        </div>
+        <div class="indented-permission-block">
           <app-management-permission-item class="subpermission-row"
-            icon="cr:location-on"
+            icon="app-management:location"
             permission-label="$i18n{location}"
             permission-type="LOCATION">
           </app-management-permission-item>
           <app-management-permission-item class="subpermission-row"
-            icon="cr:videocam"
+            icon="app-management:camera"
             permission-label="$i18n{camera}"
             permission-type="CAMERA">
           </app-management-permission-item>
           <app-management-permission-item class="subpermission-row"
-            icon="cr:mic"
+            icon="app-management:microphone"
             permission-label="$i18n{microphone}"
             permission-type="MICROPHONE">
           </app-management-permission-item>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.js
index 0242a758..100c750 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 Polymer({
+  // TODO(crbug.com/999016): change to app-management-arc-detail-view.
   is: 'app-management-arc-permission-view',
 
   behaviors: [
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
index 64b890ff..db8f985 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
@@ -9,19 +9,18 @@
 <dom-module id="app-management-chrome-app-permission-view">
   <template>
     <style include="app-management-shared-css">
-      ul {
-        list-style: disc inside;
-        margin: 0;
-        margin-inline-start: 24px;
-        padding: 0;
+
+      .permission-text-row:first-of-type {
+        border-top: none;
       }
 
       #app-description {
-        padding: 24px;
+        padding-bottom: 12px;
+        padding-top: 24px;
       }
 
       #no-permissions {
-        padding-inline-start: 24px;
+        border-top: none;
       }
 
       #pin-to-shelf-setting {
@@ -29,10 +28,8 @@
       }
 
     </style>
-    <app-management-permission-view-header>
-    </app-management-permission-view-header>
-    <div class="card-container">
-      <div id="app-description" class="permission-card-row secondary-text">
+    <div>
+      <div id="app-description" class="permission-card-row">
         [[app_.description]]
       </div>
       <div class="permission-list">
@@ -43,29 +40,36 @@
         </app-management-pin-to-shelf-item>
 
         <div id="permissions-card" class="permission-card-row">
-          <div class="subpermission-list">
-            <div class="subpermission-row separated-row">
-              <div class="header-text">$i18n{thisAppCan}</div>
-            </div>
-            <template is="dom-if" if="[[!hasPermissions_(messages_)]]">
-              <div id="no-permissions">
+          <div class="permission-section-header">
+            <div class="header-text">$i18n{permissions}</div>
+          </div>
+          <template is="dom-if" if="[[!hasPermissions_(messages_)]]">
+            <div id="no-permissions" class="indented-permission-block">
+              <div class="permission-text-row">
                 $i18n{appNoPermission}
               </div>
-            </template>
-            <ul>
-              <template is="dom-repeat"
-                items="[[getPermissionMessages_(messages_)]]" as="message">
-                <li>[[message]]</li>
-                <ul>
-                  <template is="dom-repeat"
+            </div>
+          </template>
+          <div id="top-level-permissions" class="indented-permission-block">
+            <template is="dom-repeat"
+              items="[[getPermissionMessages_(messages_)]]" as="message">
+              <div class="permission-text-row">
+                [[message]]
+              </div>
+              <div id="second-level-permissions"
+                  class="indented-permission-block">
+                <template is="dom-repeat"
                     items="[[getPermissionSubmessagesByMessage_(index, messages_)]]"
                     as="submessage">
-                    <li>[[submessage]]</li>
-                  </template>
-                </ul>
-              </template>
-            </ul>
-            <!-- TODO(crbug.com/906508): For apps which are hosted apps but not
+                  <div class="permission-text-row">
+                    [[submessage]]
+                  </div>
+                </template>
+              </div>
+            </template>
+          </div>
+        </div>
+        <!-- TODO(crbug.com/906508): For apps which are hosted apps but not
             bookmark apps, this button will actually open the site settings page
             corresponding to the app, since hosted apps are not listed in
             chrome://extensions.
@@ -73,19 +77,18 @@
             replaced, but in the mean time it might be necessary to change the
             text of the button depending on whether or not it is a hosted
             app. -->
-          <!-- TODO:(crbug.com/958269) Change behaviour of "more settings"
-            based on the app. -->
-          <div id="more-settings"
-            class="subpermission-row separated-row clickable"
-            on-click="onClickExtensionsSettingsButton_"
-            hidden$="[[app_.hideMoreSettings]]">
-            <div class="header-text">$i18n{moreSettings}</div>
+            <!-- TODO:(crbug.com/958269) Change behaviour of "more settings"
+              based on the app. -->
+        <div id="more-settings"
+          class="permission-card-row separated-row header-text clickable"
+          on-click="onClickExtensionsSettingsButton_">
+          <div class="header-text">$i18n{moreSettings}</div>
+          <div class="permission-row-controls">
             <cr-icon-button class="native-settings-icon icon-external"
               tabindex="0">
             </cr-icon-button>
           </div>
         </div>
-      </div>
     </div>
   </template>
   <script src="chrome_app_permission_view.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.js
index f697f557..4a977be98 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 Polymer({
+  // TODO(crbug.com/999016): change to app-management-chrome-app-detail-view.
   is: 'app-management-chrome-app-permission-view',
 
   behaviors: [
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/dom_switch.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/dom_switch.js
index b5117bf7..4440bacf 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/dom_switch.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/dom_switch.js
@@ -30,6 +30,7 @@
  *   this.$['view-selector'].route = 'view-two';
  */
 
+// TODO(crbug.com/992795) Merge with cr-view-manager.
 Polymer({
   is: 'app-management-dom-switch',
 
@@ -91,6 +92,12 @@
     for (const child of children) {
       this.children_[child.getAttribute('route-id')] = child;
     }
+
+    if (this.route) {
+      // TODO(crbug.com/999523): Add test coverage for this case.
+      // If attached is called after the route has been set.
+      this.onRouteChanged_(this.route);
+    }
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/icons.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/icons.html
index ccb1632..8997152 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/icons.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/icons.html
@@ -1,17 +1,19 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 
-<iron-iconset-svg name="app-management" size="24">
+<iron-iconset-svg name="app-management" size="20">
   <svg>
     <!--
-    These icons are copied from material.io and kept in sorted order.
+    These icons are custom and kept in sorted order.
     See http://goo.gl/Y1OdAq for instructions on adding additional icons.
+    TODO: move these icons to a generic file when other pages need to use them.
     -->
     <defs>
-      <!-- From https://material.io/tools/icons/?icon=account_box&style=baseline -->
-      <g id="contacts"><path d="M19 3H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5a2 2 0 0 0-2-2zm-7 3c1.65 0 3 1.35 3 3 0 1.66-1.35 3-3 3s-3-1.34-3-3c0-1.65 1.35-3 3-3zm6 12H6v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1z"></path><path fill="none" d="M0 0h24v24H0z"></path></g>
-      <!-- From https://material.io/tools/icons/?icon=storage&style=baseline -->
-      <g id="storage"><path d="M2 20h20v-4H2v4zm2-3h2v2H4v-2zM2 4v4h20V4H2zm4 3H4V5h2v2zm-4 7h20v-4H2v4zm2-3h2v2H4v-2z"></path></g>
+      <g id="contacts" viewBox="0 0 20 20"><rect width="20px" height="20px" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"></rect><path d="M5,14.754629 C6.36959794,13.6567183 8.10810658,13 10,13 C11.8918934,13 13.6304021,13.6567183 15,14.754629 L15,6 L5,6 L5,14.754629 Z M14.153574,16 C13.0272557,15.0633182 11.5794129,14.5 10,14.5 C8.42058712,14.5 6.97274425,15.0633182 5.84642599,16 L14.153574,16 Z M10,12 C8.61928813,12 7.5,10.8807119 7.5,9.5 C7.5,8.11928813 8.61928813,7 10,7 C11.3807119,7 12.5,8.11928813 12.5,9.5 C12.5,10.8807119 11.3807119,12 10,12 Z M10,10.5 C10.5522847,10.5 11,10.0522847 11,9.5 C11,8.94771525 10.5522847,8.5 10,8.5 C9.44771525,8.5 9,8.94771525 9,9.5 C9,10.0522847 9.44771525,10.5 10,10.5 Z M5,4 L15,4 C16.1045695,4 17,4.8954305 17,6 L17,16 C17,17.1045695 16.1045695,18 15,18 L5,18 C3.8954305,18 3,17.1045695 3,16 L3,6 C3,4.8954305 3.8954305,4 5,4 Z" id="Combined-Shape" fill="#5F6368"></path><rect id="Rectangle" fill="#5F6368" x="6" y="2" width="2" height="2"></rect><rect id="Rectangle-Copy" fill="#5F6368" x="12" y="2" width="2" height="2"></rect></g>
+      <g id="storage" viewBox="0 0 20 20"><rect width="20px" height="20px" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"></rect><path fill="#5F6368" d="M16,5 L12,5 L10,3 L4,3 C2.9,3 2.01,3.9 2.01,5 L2,15 C2,16.1 2.9,17 4,17 L16,17 C17.1,17 18,16.1 18,15 L18,7 C18,5.9 17.1,5 16,5 Z M16,15 L4,15 L4,7 L16,7 L16,15 Z"></path></g>
+      <g id="location" viewBox="0 0 20 20"><rect width="20px" height="20px" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"></rect><path d="M10,18 C6,13.5424723 4,10.209139 4,8 C4,4.6862915 6.6862915,2 10,2 C13.3137085,2 16,4.6862915 16,8 C16,10.209139 14,13.5424723 10,18 Z M6,8 C6,9.32543171 7.3180021,11.7149155 10,14.9435167 C12.6819979,11.7149155 14,9.32543171 14,8 C14,5.790861 12.209139,4 10,4 C7.790861,4 6,5.790861 6,8 Z M10,10 C8.8954305,10 8,9.1045695 8,8 C8,6.8954305 8.8954305,6 10,6 C11.1045695,6 12,6.8954305 12,8 C12,9.1045695 11.1045695,10 10,10 Z" id="Combined-Shape" fill="#5F6368"></path></g>
+      <g id="camera" viewBox="0 0 20 20"><rect width="20px" height="20px" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"></rect><path d="M2,5.99539757 C2,5.44565467 2.44851311,5 3.00247329,5 L12.9975267,5 C13.5511774,5 14,5.44910619 14,5.99539757 L14,14.0046024 C14,14.5543453 13.5514869,15 12.9975267,15 L3.00247329,15 C2.44882258,15 2,14.5508938 2,14.0046024 L2,5.99539757 Z M14,8.5 L18,5.5 L18,14.5 L14,12 L14,8.5 Z M4,7 L4,13 L12,13 L12,7 L4,7 Z" id="Combined-Shape" fill="#5F6368"></path></g>
+      <g id="microphone" viewBox="0 0 20 20"><rect width="20px" height="20px" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"></rect><path d="M12.5628571,9.5 C12.5628571,10.8833333 11.4228571,12 10,12 C8.57714286,12 7.42857143,10.8833333 7.42857143,9.5 L7.42857143,4.5 C7.42857143,3.11666667 8.57714286,2 10,2 C11.4228571,2 12.5714286,3.11666667 12.5714286,4.5 L12.5628571,9.5 Z M10.0548096,3.5 C9.5012628,3.5 9.05131398,3.94643954 9.04697987,4.49996935 L9.0078297,9.50003065 C9.00780942,9.50262015 9.00779929,9.50520971 9.00779929,9.50779929 C9.00779929,10.0557766 9.45202268,10.5 10,10.5 C10.5535468,10.5 11.0034956,10.0535605 11.0078297,9.50003065 L11.0469799,4.49996935 C11.0470001,4.49737985 11.0470103,4.49479029 11.0470103,4.49220071 C11.0470103,3.94422339 10.6027869,3.5 10.0548096,3.5 Z M10,13.8736842 C7.63428571,13.8736842 5.45714286,12.1052632 5.45714286,9.57894737 L4,9.57894737 C4,12.4589474 6.33142857,14.8252632 9.14285714,15.2378947 L9.14285714,18 L10.8571429,18 L10.8571429,15.2378947 C13.6685714,14.8336842 16,12.4589474 16,9.57894737 L14.5428571,9.57894737 C14.5428571,12.1052632 12.3657143,13.8736842 10,13.8736842 Z" fill="#5F6368"></path></g>
     </defs>
   </svg>
 </iron-iconset-svg>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_view_header.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_view_header.html
deleted file mode 100644
index d0c6f0c..0000000
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_view_header.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="shared_style.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-
-<dom-module id="app-management-permission-view-header">
-  <template>
-    <style include="app-management-shared-css">
-      :host {
-        align-items: center;
-        display: flex;
-        margin: 24px auto;
-        max-width: var(--card-max-width);
-        min-width: var(--card-min-width);
-      }
-
-      #permission-view-header-icon {
-        height: 26px;
-        margin-inline-end: 8px;
-        margin-inline-start: 24px;
-        width: 26px;
-      }
-
-      #app-title {
-        flex: 1;
-        font-size: 16px;
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
-
-      cr-icon-button {
-        margin: 0;
-      }
-
-      #uninstall-button {
-        background: white;
-      }
-
-      #policy-indicator {
-        fill: var(--google-grey-refresh-700);
-        margin-inline-end: 12px;
-      }
-    </style>
-    <cr-icon-button class="icon-arrow-back" id="backButton"
-        on-click="onClickBackButton_" aria-label="$i18n{back}"></cr-icon-button>
-    <img id="permission-view-header-icon" src="[[iconUrlFromId_(app_)]]">
-    <div class="page-title">[[app_.title]]</div>
-
-    <div id="uninstall-wrapper" title$="[[getUninstallButtonHoverText_(app_)]]">
-      <template is="dom-if" if="[[isPolicyApp_(app_)]]">
-        <iron-icon id="policy-indicator" icon="cr:domain"></iron-icon>
-      </template>
-      <cr-button id="uninstall-button" on-click="onClickUninstallButton_"
-          disabled$="[[getUninstallButtonDisableState_(app_)]]">
-        $i18n{uninstall}
-      </cr-button>
-    </div>
-  </template>
-  <script src="permission_view_header.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
index 17952e85..260da0d 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
@@ -1,12 +1,11 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="browser_proxy.html">
-<link rel="import" href="metadata_view.html">
+<link rel="import" href="icons.html">
 <link rel="import" href="permission_item.html">
-<link rel="import" href="permission_view_header.html">
+<link rel="import" href="pin_to_shelf_item.html">
 <link rel="import" href="shared_style.html">
 <link rel="import" href="store_client.html">
-<link rel="import" href="pin_to_shelf_item.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 
@@ -14,11 +13,7 @@
   <template>
     <style include="app-management-shared-css">
     </style>
-
-    <app-management-permission-view-header>
-    </app-management-permission-view-header>
-
-    <div class="permission-list card-container">
+    <div class="permission-list">
       <app-management-pin-to-shelf-item
         id="pin-to-shelf-setting"
         class="permission-card-row separated-row header-text"
@@ -30,33 +25,34 @@
         permission-type="CONTENT_SETTINGS_TYPE_NOTIFICATIONS">
       </app-management-permission-item>
       <div id="permissions-card" class="permission-card-row">
-        <div class="subpermission-list">
-          <div class="subpermission-row separated-row">
-            <div class="header-text">$i18n{permissions}</div>
-          </div>
+        <div class="permission-section-header">
+          <div class="header-text">$i18n{permissions}</div>
+        </div>
+        <div class="indented-permission-block">
           <app-management-permission-item id="location"
-            class="subpermission-row" icon="cr:location-on"
+            class="subpermission-row" icon="app-management:location"
             permission-label="$i18n{location}"
             permission-type="CONTENT_SETTINGS_TYPE_GEOLOCATION">
           </app-management-permission-item>
           <app-management-permission-item id="camera" class="subpermission-row"
-            icon="cr:videocam" permission-label="$i18n{camera}"
+            icon="app-management:camera" permission-label="$i18n{camera}"
             permission-type="CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA">
           </app-management-permission-item>
           <app-management-permission-item id="microphone"
-            class="subpermission-row" icon="cr:mic"
+            class="subpermission-row" icon="app-management:microphone"
             permission-label="$i18n{microphone}"
             permission-type="CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC">
           </app-management-permission-item>
-          <div class="subpermission-row separated-row clickable"
-            on-click="onClickSiteSettingsButton_">
-            <div class="header-text">$i18n{moreSettings}</div>
-            <div class="permission-row-controls">
-              <cr-icon-button class="native-settings-icon icon-external"
-                tabindex="0">
-              </cr-icon-button>
-            </div>
-          </div>
+        </div>
+      </div>
+      <div id="more-settings"
+        class="permission-card-row separated-row header-text clickable"
+        on-click="onClickSiteSettingsButton_">
+        <div class="header-text">$i18n{moreSettings}</div>
+        <div class="permission-row-controls">
+          <cr-icon-button class="native-settings-icon icon-external"
+            tabindex="0">
+          </cr-icon-button>
         </div>
       </div>
     </div>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.js
index bc475ed9..c11dac9 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 Polymer({
+  // TODO(crbug.com/999016): change to app-management-pwa-detail-view.
   is: 'app-management-pwa-permission-view',
 
   behaviors: [
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/router.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/router.html
index bbe2548..d64e522 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/router.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/router.html
@@ -3,15 +3,9 @@
 <link rel="import" href="actions.html">
 <link rel="import" href="store_client.html">
 <link rel="import" href="constants.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-location/iron-location.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-location/iron-query-params.html">
 
 <dom-module id="app-management-router">
   <template>
-    <iron-location id="iron-location" query="{{urlQuery_}}" path="{{path_}}">
-    </iron-location>
-    <iron-query-params params-string="{{query_}}"
-        params-object="{{queryParams_}}"></iron-query-params>
   </template>
   <script src="router.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/router.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/router.js
index f0f96baf..9e4ea940 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/router.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/router.js
@@ -8,155 +8,35 @@
   behaviors: [
     app_management.StoreClient,
   ],
-
+  //TODO (crbug.com/999443): Watch URL and update state.
   properties: {
-    /** @private {string} */
-    path_: String,
-
-    /** @private {Object} */
-    queryParams_: Object,
-
-    /** @private {string} */
-    query_: {
-      type: String,
-      observer: 'onQueryChanged_',
-    },
-
-    /** @private {string} */
-    urlQuery_: {
-      type: String,
-      observer: 'onUrlQueryChanged_',
-    },
-
-    /** @private */
-    searchTerm_: {
-      type: String,
-      value: '',
-    },
-
-    /** @private {PageType} */
-    currentPageType_: {
-      type: Number,
-    },
-
-    /** @private {?string} */
-    selectedAppId_: {
-      type: String,
+    currentPage_: {
+      type: Object,
+      observer: 'onCurrentPageChanged_',
     },
   },
 
-  urlParsed_: false,
-
-  observers: [
-    'onUrlChanged_(path_, queryParams_)',
-    'onStateChanged_(currentPageType_, selectedAppId_, searchTerm_)',
-  ],
-
   attached: function() {
-    this.watch('currentPageType_', (state) => {
-      return state.currentPage.pageType;
-    });
-    this.watch('selectedAppId_', (state) => {
-      return state.currentPage.selectedAppId;
-    });
-    this.watch('searchTerm_', (state) => {
-      return state.search.term;
+    this.watch('currentPage_', state => {
+      return state.currentPage;
     });
     this.updateFromStore();
   },
 
-  /**
-   * @param {?string} current Current value of the query.
-   * @param {?string} previous Previous value of the query.
-   * @private
-   */
-  onQueryChanged_: function(current, previous) {
-    if (previous !== undefined) {
-      this.urlQuery_ = this.query_;
+  onCurrentPageChanged_: function() {
+    const pageType = this.currentPage_.pageType;
+    const appId = this.currentPage_.selectedAppId;
+    switch(pageType) {
+      case PageType.DETAIL:
+        const params = new URLSearchParams;
+        params.append('id', appId);
+        settings.navigateTo(settings.routes.APP_MANAGEMENT_DETAIL, params);
+        return;
+      case PageType.MAIN:
+        settings.navigateTo(settings.routes.APP_MANAGEMENT);
+        return;
+      default:
+        assertNotReached();
     }
-  },
-
-  /** @private */
-  onUrlQueryChanged_: function() {
-    this.query_ = this.urlQuery_;
-  },
-
-  /** @private */
-  onStateChanged_: function() {
-    if (!this.urlParsed_) {
-      return;
-    }
-    this.debounce('publishUrl', this.publishUrl_);
-  },
-
-  /** @private */
-  publishUrl_: function() {
-    // Disable pushing urls into the history stack, so that we only push one
-    // state.
-    this.$['iron-location'].dwellTime = Infinity;
-    this.publishQueryParams_();
-    // Re-enable pushing urls into the history stack.
-    this.$['iron-location'].dwellTime = 0;
-    this.publishPath_();
-  },
-
-  /** @private */
-  publishQueryParams_: function() {
-    const newQueryParams = Object.assign({}, this.queryParams_);
-
-    newQueryParams.q = this.searchTerm_ || undefined;
-    newQueryParams.id = this.selectedAppId_ || undefined;
-
-    // Can't update |this.queryParams_| every time since assigning a new object
-    // to it triggers a state change which causes the URL to change, which
-    // recurses into a loop. JSON.stringify is used here to compare objects as
-    // it is always going to be a key value (string) pair and will serialize
-    // correctly.
-    if (JSON.stringify(newQueryParams) !== JSON.stringify(this.queryParams_)) {
-      this.queryParams_ = newQueryParams;
-    }
-  },
-
-  /** @private */
-  publishPath_: function() {
-    let path = '';
-    if (this.currentPageType_ === PageType.DETAIL) {
-      path = 'detail';
-    } else if (this.currentPageType_ === PageType.NOTIFICATIONS) {
-      path = 'notifications';
-    }
-    this.path_ = '/' + path;
-  },
-
-  /** @private */
-  onUrlChanged_: function() {
-    this.debounce('parseUrl', this.parseUrl_);
-  },
-
-  /** @private */
-  parseUrl_: function() {
-    const newId = this.queryParams_.id;
-    const searchTerm = this.queryParams_.q;
-
-    const pageFromUrl = this.path_.substr(1).split('/')[0];
-    let newPage = PageType.MAIN;
-    if (pageFromUrl === 'detail') {
-      newPage = PageType.DETAIL;
-    } else if (pageFromUrl === 'notifications') {
-      newPage = PageType.NOTIFICATIONS;
-    } else {
-      newPage = PageType.MAIN;
-    }
-
-    if (newPage === PageType.DETAIL) {
-      this.dispatch(app_management.actions.changePage(PageType.DETAIL, newId));
-    } else {
-      this.dispatch(app_management.actions.changePage(newPage));
-    }
-
-    if (searchTerm) {
-      this.dispatch(app_management.actions.setSearchTerm(searchTerm));
-    }
-    this.urlParsed_ = true;
-  },
+  }
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html
index 57215df..2c4276f 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html
@@ -30,7 +30,21 @@
 
       .permission-card-row {
         border-top: var(--card-separator);
-        padding: 0 24px;
+        padding: 0 20px;
+      }
+
+      .permission-text-row {
+        border-top: var(--card-separator);
+        display: flex;
+        flex-direction: column;
+        height: var(--text-permission-list-row-height);
+        justify-content: center;
+      }
+
+      .permission-section-header {
+        line-height: 20px;
+        padding-bottom: 12px;
+        padding-top: 24px;
       }
 
       .clickable {
@@ -69,13 +83,6 @@
         height: 48px;
       }
 
-      .subpermission-list {
-        align-items: stretch;
-        display: flex;
-        flex-direction: column;
-        padding: 8px 0;
-      }
-
       .secondary-text {
         color: var(--secondary-text-color);
         font-weight: var(--secondary-font-weight);
@@ -109,5 +116,9 @@
         overflow: hidden;
         text-overflow: ellipsis;
       }
+
+      .indented-permission-block {
+        padding-inline-start: 36px;
+      }
     </style>
   </template>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html
index d42cace..0b0a5f99 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html
@@ -16,8 +16,9 @@
     --header-font-weight: 500;
     --header-text-color: #5A5A5A;
     --permission-icon-color: #757575;
-    --permission-icon-padding: 12px;
+    --permission-icon-padding: 20px;
     --permission-list-item-height: 64px;
+    --text-permission-list-row-height: 40px;
     --primary-text-color: rgba(0, 0, 0, 0.87);
     --row-item-icon-padding: 12px;
     --secondary-font-weight: 400;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html
new file mode 100644
index 0000000..04357b7
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html
@@ -0,0 +1,32 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="shared_style.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+
+<dom-module id="app-management-uninstall-button">
+  <template>
+    <style include="app-management-shared-css">
+      #uninstall-button {
+        background: white;
+      }
+
+      #policy-indicator {
+        fill: var(--google-grey-refresh-700);
+        margin-inline-end: 12px;
+      }
+    </style>
+    <!-- TODO(crbug.com/999632): rename polymer element IDs to camel case. -->
+    <div id="uninstall-wrapper" title$="[[getUninstallButtonHoverText_(app_)]]">
+      <template is="dom-if" if="[[isPolicyApp_(app_)]]">
+        <iron-icon id="policy-indicator" icon="cr:domain"></iron-icon>
+      </template>
+      <cr-button id="uninstall-button" on-click="onClickUninstallButton_"
+          disabled$="[[getUninstallButtonDisableState_(app_)]]">
+        <!-- TODO(crbug.com/999636) rename uninstall to uninstallApp.-->
+        $i18n{uninstall}
+      </cr-button>
+    </div>
+  </template>
+  <script src="uninstall_button.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_view_header.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js
similarity index 74%
rename from chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_view_header.js
rename to chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js
index a6caf89..83c954e 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_view_header.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js
@@ -1,18 +1,19 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// 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.
+
 Polymer({
-  is: 'app-management-permission-view-header',
+  is: 'app-management-uninstall-button',
 
   behaviors: [
     app_management.StoreClient,
   ],
 
   properties: {
-    /** @type {App} */
-    app_: {
-      type: Object,
-    },
+    /**
+     * @private {App}
+     */
+    app_: Object,
   },
 
   attached: function() {
@@ -21,12 +22,11 @@
   },
 
   /**
-   *
    * Returns True if the uninstall button should be disabled due to app install
    * type.
    *
    * @param {App} app
-   * @return {boolean}
+   * @return {?boolean}
    * @private
    */
   getUninstallButtonDisableState_: function(app) {
@@ -49,11 +49,11 @@
    * Returns string to be shown as a tool tip over the uninstall button.
    *
    * @param {App} app
-   * @return {string}
+   * @return {?string}
    * @private
    */
   getUninstallButtonHoverText_: function(app) {
-    // TODO(crbug.com/957795) Replace strings and add them into i18n.
+    // TODO(crbug.com/957795): Replace strings and add them into i18n.
     switch (app.installSource) {
       case InstallSource.kSystem:
         return app.title + ' cannot be uninstalled as it is part of Chrome OS.';
@@ -75,7 +75,7 @@
    * Returns true if the app was installed by a policy
    *
    * @param {App} app
-   * @returns {boolean}
+   * @returns {?boolean}
    * @private
    */
   isPolicyApp_: function(app) {
@@ -83,26 +83,6 @@
   },
 
   /**
-   * @param {App} app
-   * @return {string}
-   * @private
-   */
-  iconUrlFromId_: function(app) {
-    return app_management.util.getAppIcon(app);
-  },
-
-  /**
-   * @private
-   */
-  onClickBackButton_: function() {
-    if (!window.history.state) {
-      this.dispatch(app_management.actions.changePage(PageType.MAIN));
-    } else {
-      window.history.back();
-    }
-  },
-
-  /**
    * @private
    */
   onClickUninstallButton_: function() {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
index f98318b1..32ecca3 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
@@ -2,6 +2,8 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="app_management_page/app_management_page.html">
+<link rel="import" href="app_management_page/app_permission_view.html">
+<link rel="import" href="app_management_page/uninstall_button.html">
 <link rel="import" href="../../route.html">
 <link rel="import" href="../../settings_shared_css.html">
 
@@ -24,6 +26,16 @@
         </settings-app-management-page>
       </settings-subpage>
     </template>
+    <template is="dom-if" route-path="/app-management/detail" no-search>
+      <settings-subpage
+          page-title="[[app_.title]]"
+          title-icon="[[iconUrlFromId_(app_)]]">
+        <app-management-uninstall-button slot="subpage-title-extra">
+        </app-management-uninstall-button>
+        <app-management-app-permission-view>
+        </app-management-app-permission-view>
+      </settings-subpage>
+    </template>
   </settings-animated-pages>
   </template>
   <script src="os_apps_page.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
index 77b1233d..595328c 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
@@ -10,6 +10,10 @@
 Polymer({
   is: 'os-settings-apps-page',
 
+  behaviors: [
+    app_management.StoreClient,
+  ],
+
   properties: {
     /** @private {!Map<string, string>} */
     focusConfig_: {
@@ -23,6 +27,24 @@
       },
     },
 
+    /**
+     * @type {App}
+     * @private
+     */
+    app_: Object,
+  },
+
+  attached: function() {
+    this.watch('app_', state => app_management.util.getSelectedApp(state));
+  },
+
+  /**
+   * @param {App} app
+   * @return {string}
+   * @private
+   */
+  iconUrlFromId_: function(app) {
+    return app_management.util.getAppIcon(app);
   },
 
   /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.html b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.html
index b0d64da..3d1d7a1 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.html
@@ -133,6 +133,17 @@
         display: none;
       }
 
+      /* The default implementation of the actionable list item makes the entire
+      list item row a button such that clicking anywhere will activate the
+      action of the list item. The input method list behaves differently in that
+      clicking the list item sets that item as the input method, and the
+      selected list item should not react to selection after being selected.
+      Set the cursor to auto to override the default implementation which would
+      otherwise make the entire row appear clickable when it is not. */
+      div.list-item.selected[actionable] {
+        cursor: auto;
+      }
+
       #restartButton {
         margin-inline-start: var(--settings-controlled-by-spacing);
       }
@@ -195,9 +206,11 @@
         <template is="dom-repeat"
             items="[[languages.inputMethods.enabled]]">
           <div class$="list-item [[getInputMethodItemClass_(
-                  item.id, languages.inputMethods.currentId)]]"
-              on-click="onInputMethodTap_" on-keypress="onInputMethodTap_"
-              actionable tabindex="0">
+              item.id, languages.inputMethods.currentId)]]"
+              actionable on-click="onInputMethodTap_"
+              on-keypress="onInputMethodTap_" on-mousedown="onMouseDown_"
+              tabindex$="[[getInputMethodTabIndex_(
+                  item.id, languages.inputMethods.currentId)]]">
             <div class="start">
               <div class="display-name">[[item.displayName]]</div>
               <div class="explain-selected"
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.js b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.js
index af8f76a4..0a76c6d6 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page.js
@@ -463,5 +463,29 @@
     expandButton.expanded = !expandButton.expanded;
     cr.ui.focusWithoutInk(expandButton);
   },
+
+  /**
+   * @param {string} id The selected input method ID.
+   * @param {string} currentId The ID of the currently enabled input method.
+   * @return {string} The default tab index '0' if the selected input method is
+   *     not currently enabled; otherwise, returns an empty string which
+   *     effectively unsets the tabindex attribute.
+   * @private
+   */
+  getInputMethodTabIndex_: function(id, currentId) {
+    return id == currentId ? '' : '0';
+  },
+
+  /**
+   * Handles the mousedown even by preventing focusing an input method list
+   * item. This is only registered by the input method list item to avoid
+   * unwanted focus.
+   * @param {!Event} e
+   * @private
+   */
+  onMouseDown_: function(e) {
+    // Preventing the mousedown event from propagating prevents focus being set.
+    e.preventDefault();
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 00617fc6..8338d49 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -103,6 +103,75 @@
       <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_API_LISTENER_HTML"
                  file="chromeos/os_apps_page/app_management_page/api_listener.html"
                  type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_DOM_SWITCH_JS"
+                 file="chromeos/os_apps_page/app_management_page/dom_switch.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_DOM_SWITCH_HTML"
+                 file="chromeos/os_apps_page/app_management_page/dom_switch.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_PWA_PERMISSION_VIEW_JS"
+                 file="chromeos/os_apps_page/app_management_page/pwa_permission_view.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_PWA_PERMISSION_VIEW_HTML"
+                 file="chromeos/os_apps_page/app_management_page/pwa_permission_view.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_PERMISSION_ITEM_JS"
+                 file="chromeos/os_apps_page/app_management_page/permission_item.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_PERMISSION_ITEM_HTML"
+                 file="chromeos/os_apps_page/app_management_page/permission_item.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_PERMISSION_TOGGLE_JS"
+                 file="chromeos/os_apps_page/app_management_page/permission_toggle.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_PERMISSION_TOGGLE_HTML"
+                 file="chromeos/os_apps_page/app_management_page/permission_toggle.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_PIN_TO_SHELF_ITEM_JS"
+                 file="chromeos/os_apps_page/app_management_page/pin_to_shelf_item.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_PIN_TO_SHELF_ITEM_HTML"
+                 file="chromeos/os_apps_page/app_management_page/pin_to_shelf_item.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_TOGGLE_ROW_JS"
+                 file="chromeos/os_apps_page/app_management_page/toggle_row.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_TOGGLE_ROW_HTML"
+                 file="chromeos/os_apps_page/app_management_page/toggle_row.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_APP_PERMISSION_VIEW_JS"
+                 file="chromeos/os_apps_page/app_management_page/app_permission_view.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_APP_PERMISSION_VIEW_HTML"
+                 file="chromeos/os_apps_page/app_management_page/app_permission_view.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_ARC_PERMISSION_VIEW_JS"
+                 file="chromeos/os_apps_page/app_management_page/arc_permission_view.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_ARC_PERMISSION_VIEW_HTML"
+                 file="chromeos/os_apps_page/app_management_page/arc_permission_view.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_CHROME_APP_PERMISSION_VIEW_JS"
+                 file="chromeos/os_apps_page/app_management_page/chrome_app_permission_view.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_CHROME_APP_PERMISSION_VIEW_HTML"
+                 file="chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_ROUTER_JS"
+                 file="chromeos/os_apps_page/app_management_page/router.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_ROUTER_HTML"
+                 file="chromeos/os_apps_page/app_management_page/router.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_ICONS_HTML"
+                 file="chromeos/os_apps_page/app_management_page/icons.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_UNINSTALL_BUTTON_JS"
+                 file="chromeos/os_apps_page/app_management_page/uninstall_button.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PAGE_APP_UNINSTALL_BUTTON_HTML"
+                 file="chromeos/os_apps_page/app_management_page/uninstall_button.html"
+                 type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_MANAGE_A11Y_PAGE_JS"
                  file="a11y_page/manage_a11y_page.js"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_bio_enroll_dialog.html b/chrome/browser/resources/settings/privacy_page/security_keys_bio_enroll_dialog.html
index 436f622..7569eea 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_bio_enroll_dialog.html
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_bio_enroll_dialog.html
@@ -30,6 +30,10 @@
       .name {
         flex: 3;
       }
+
+      #dialog::part(body-container) {
+        overflow-y: hidden;
+      }
     </style>
 
     <cr-dialog id="dialog" close-text="$i18n{cancel}" ignore-popstate
@@ -73,6 +77,11 @@
                     <div class="name" aria-label="[[item.name]]">
                       [[item.name]]
                     </div>
+                    <cr-icon-button class="icon-clear"
+                        aria-label="i18n{securityKeysBioEnrollmentDelete}"
+                        on-click="deleteEnrollment_"
+                        disabled="[[deleteInProgress_]]">
+                    </cr-icon-button>
                   </div>
                 </template>
               </iron-list>
@@ -81,8 +90,7 @@
 
           <div id="enroll">
             <p>$i18n{securityKeysBioEnrollmentEnrollingLabel}</p>
-            <cr-fingerprint-progress-arc id="arc">
-            </cr-fingerprint-progress-arc>
+            <cr-fingerprint-progress-arc id="arc"></cr-fingerprint-progress-arc>
           </div>
 
           <div id="error">[[errorMsg_]]</div>
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_bio_enroll_dialog.js b/chrome/browser/resources/settings/privacy_page/security_keys_bio_enroll_dialog.js
index cce9de63..1f251c6 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_bio_enroll_dialog.js
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_bio_enroll_dialog.js
@@ -19,6 +19,15 @@
   ],
 
   properties: {
+    /** @private */
+    addButtonVisible_: Boolean,
+
+    /** @private */
+    cancelButtonVisible_: Boolean,
+
+    /** @private */
+    deleteInProgress_: Boolean,
+
     /**
      * The ID of the element currently shown in the dialog.
      * @private
@@ -29,6 +38,9 @@
       observer: 'dialogPageChanged_',
     },
 
+    /** @private */
+    doneButtonVisible_: Boolean,
+
     /**
      * The list of enrollments displayed.
      * @private {!Array<!Enrollment>}
@@ -36,16 +48,7 @@
     enrollments_: Array,
 
     /** @private */
-    addButtonVisible_: Boolean,
-
-    /** @private */
-    cancelButtonVisible_: Boolean,
-
-    /** @private */
     okButtonVisible_: Boolean,
-
-    /** @private */
-    doneButtonVisible_: Boolean,
   },
 
   /** @private {?settings.SecurityKeysBioEnrollProxyImpl} */
@@ -250,5 +253,21 @@
   hasSome_: function(list) {
     return !!(list && list.length);
   },
+
+  /**
+   * @private
+   * @param {!DomRepeatEvent} event
+   */
+  deleteEnrollment_: function(event) {
+    if (this.deleteInProgress_) {
+      return;
+    }
+    this.deleteInProgress_ = true;
+    const enrollment = this.enrollments_[event.model.index];
+    this.browserProxy_.deleteEnrollment(enrollment.id).then(enrollments => {
+      this.deleteInProgress_ = false;
+      this.onEnrollments_(enrollments);
+    });
+  }
 });
 })();
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.js b/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.js
index 2fd3f69..00a55d9 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.js
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.js
@@ -203,6 +203,14 @@
      */
     cancelEnrollment() {}
 
+    /**
+     * Deletes the enrollment with the given ID.
+     *
+     * @param {string} id
+     * @return {!Promise<!Array<!Enrollment>>} The remaining enrollments.
+     */
+    deleteEnrollment(id) {}
+
     /** Cancels all outstanding operations. */
     close() {}
   }
@@ -299,6 +307,11 @@
     }
 
     /** @override */
+    deleteEnrollment(id) {
+      return cr.sendWithPromise('securityKeyBioEnrollDelete', id);
+    }
+
+    /** @override */
     close() {
       return chrome.send('securityKeyBioEnrollClose');
     }
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index 60f493f..ee6665e 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -526,6 +526,8 @@
           loadTimeData.getBoolean('showApps')) {
         r.APPS = r.BASIC.createSection('/apps', 'apps');
         r.APP_MANAGEMENT = r.APPS.createChild('/app-management');
+        r.APP_MANAGEMENT_DETAIL =
+            r.APP_MANAGEMENT.createChild('/app-management/detail');
       }
     } else {
       assert(r.ADVANCED, 'ADVANCED route should exist');
diff --git a/chrome/browser/resources/tab_strip/tab.html b/chrome/browser/resources/tab_strip/tab.html
index 0a5c3a6f..12edbb1 100644
--- a/chrome/browser/resources/tab_strip/tab.html
+++ b/chrome/browser/resources/tab_strip/tab.html
@@ -17,7 +17,7 @@
   #title {
     align-items: center;
     background: var(--tabstrip-card-background-color);
-    border-block-end: 1px solid rgb(var(--google-grey-300-rgb));
+    border-block-end: 1px solid var(--tabstrip-separator-color);
     box-sizing: border-box;
     display: flex;
     height: 40px;
diff --git a/chrome/browser/resources/tab_strip/tab_list.js b/chrome/browser/resources/tab_strip/tab_list.js
index daea402..8b2f8ff 100644
--- a/chrome/browser/resources/tab_strip/tab_list.js
+++ b/chrome/browser/resources/tab_strip/tab_list.js
@@ -58,6 +58,7 @@
       this.tabsApiHandler_.onActivated.addListener(
           this.onTabActivated_.bind(this));
       this.tabsApiHandler_.onCreated.addListener(this.onTabCreated_.bind(this));
+      this.tabsApiHandler_.onMoved.addListener(this.onTabMoved_.bind(this));
       this.tabsApiHandler_.onRemoved.addListener(this.onTabRemoved_.bind(this));
       this.tabsApiHandler_.onUpdated.addListener(this.onTabUpdated_.bind(this));
     });
@@ -144,6 +145,22 @@
 
   /**
    * @param {number} tabId
+   * @param {!TabMovedInfo} moveInfo
+   * @private
+   */
+  onTabMoved_(tabId, moveInfo) {
+    if (moveInfo.windowId !== this.windowId_) {
+      return;
+    }
+
+    const movedTab = this.findTabElement_(tabId);
+    if (movedTab) {
+      this.insertTabOrMoveTo_(movedTab, moveInfo.toIndex);
+    }
+  }
+
+  /**
+   * @param {number} tabId
    * @param {!WindowRemoveInfo} removeInfo
    * @private
    */
diff --git a/chrome/browser/resources/tab_strip/tab_strip.html b/chrome/browser/resources/tab_strip/tab_strip.html
index 886df5d..088f0ea0 100644
--- a/chrome/browser/resources/tab_strip/tab_strip.html
+++ b/chrome/browser/resources/tab_strip/tab_strip.html
@@ -20,6 +20,18 @@
             0 0 0 1px rgb(var(--google-grey-300-rgb));
         --tabstrip-focus-color: rgb(var(--google-blue-500-rgb));
         --tabstrip-primary-text-color: rgb(var(--google-grey-900-rgb));
+        --tabstrip-separator-color: rgb(var(--google-grey-300-rgb));
+      }
+
+      @media (prefers-color-scheme: dark) {
+        html {
+          --tabstrip-background-color: rgba(var(--google-grey-900-rgb));
+          --tabstrip-card-background-color: rgba(255, 255, 255, 0.04);
+          --tabstrip-elevation-box-shadow: none;
+          --tabstrip-focus-color: rgb(var(--google-blue-300-rgb));
+          --tabstrip-primary-text-color: rgb(var(--google-grey-200-rgb));
+          --tabstrip-separator-color: rgb(255, 255, 255, 0.1);
+        }
       }
 
       body {
diff --git a/chrome/browser/resources/tab_strip/tabs_api_proxy.js b/chrome/browser/resources/tab_strip/tabs_api_proxy.js
index 6e7ce36..9ce6ad6 100644
--- a/chrome/browser/resources/tab_strip/tabs_api_proxy.js
+++ b/chrome/browser/resources/tab_strip/tabs_api_proxy.js
@@ -10,6 +10,7 @@
     this.callbackRouter = {
       onActivated: chrome.tabs.onActivated,
       onCreated: chrome.tabs.onCreated,
+      onMoved: chrome.tabs.onMoved,
       onRemoved: chrome.tabs.onRemoved,
       onUpdated: chrome.tabs.onUpdated,
     };
@@ -49,6 +50,19 @@
       chrome.tabs.remove(tabId, resolve);
     });
   }
+
+  /**
+   * @param {number} tabId
+   * @param {number} newIndex
+   * @return {!Promise<!Tab>}
+   */
+  moveTab(tabId, newIndex) {
+    return new Promise(resolve => {
+      chrome.tabs.move(tabId, {index: newIndex}, tab => {
+        resolve(tab);
+      });
+    });
+  }
 }
 
 addSingletonGetter(TabsApiProxy);
diff --git a/chrome/browser/resources/tab_strip/types.js b/chrome/browser/resources/tab_strip/types.js
index 2fd7cad..6496711 100644
--- a/chrome/browser/resources/tab_strip/types.js
+++ b/chrome/browser/resources/tab_strip/types.js
@@ -16,6 +16,15 @@
 
 /**
  * @typedef {{
+ *    fromIndex: number,
+ *    toIndex: number,
+ *    windowId: number,
+ * }}
+ */
+let TabMovedInfo;
+
+/**
+ * @typedef {{
  *    isWindowClosing: boolean,
  *    windowId: number,
  * }}
diff --git a/chrome/browser/safe_browsing/download_protection/binary_fcm_service.cc b/chrome/browser/safe_browsing/download_protection/binary_fcm_service.cc
index 26da9d5a..b943fc6 100644
--- a/chrome/browser/safe_browsing/download_protection/binary_fcm_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/binary_fcm_service.cc
@@ -22,9 +22,7 @@
 namespace {
 
 const char kBinaryFCMServiceAppId[] = "safe_browsing_fcm_service";
-// TODO(drubery): Once the server side has finalized their sender id, fill this
-// in.
-const char kBinaryFCMServiceSenderId[] = "SenderID";
+const char kBinaryFCMServiceSenderId[] = "465959725923";
 const char kBinaryFCMServiceMessageKey[] = "proto";
 
 }  // namespace
diff --git a/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc b/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
index 791d778..37065e0 100644
--- a/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/binary_upload_service.cc
@@ -23,9 +23,8 @@
 
 const size_t kMaxUploadSizeBytes = 50 * 1024 * 1024;  // 50 MB
 const int kScanningTimeoutSeconds = 5 * 60;           // 5 minutes
-
-// TODO(drubery): Once the server side has a public-facing URL, set it here.
-const char kSbBinaryUploadUrl[] = "";
+const char kSbBinaryUploadUrl[] =
+    "https://safebrowsing.google.com/safebrowsing/uploads/webprotect";
 
 }  // namespace
 
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc b/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
index 4baca324..9d68deba 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
@@ -22,16 +22,21 @@
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_device_capability.h"
 #include "chrome/browser/sharing/sharing_device_registration_result.h"
+#include "chrome/browser/sharing/sharing_metrics.h"
 #include "chrome/browser/sharing/sharing_service.h"
 #include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sync/device_info_sync_service_factory.h"
+#include "chrome/browser/sync/test/integration/sessions_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/ui/browser.h"
 #include "components/gcm_driver/fake_gcm_profile_service.h"
 #include "components/sync/driver/profile_sync_service.h"
 #include "components/sync_device_info/device_info_sync_service.h"
 #include "components/sync_device_info/fake_device_info_tracker.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "net/dns/mock_host_resolver.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "url/gurl.h"
 
 namespace {
@@ -61,13 +66,8 @@
 
     ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-    std::unique_ptr<content::WebContents> web_contents_ptr =
-        content::WebContents::Create(
-            content::WebContents::CreateParams(GetProfile(0)));
-    web_contents_ = web_contents_ptr.get();
-    Browser* browser = AddBrowser(0);
-    browser->tab_strip_model()->AppendWebContents(std::move(web_contents_ptr),
-                                                  true);
+    sessions_helper::OpenTab(0, GURL("http://www.google.com/"));
+    web_contents_ = GetBrowser(0)->tab_strip_model()->GetWebContentsAt(0);
 
     gcm_service_ = static_cast<gcm::FakeGCMProfileService*>(
         gcm::GCMProfileServiceFactory::GetForProfile(GetProfile(0)));
@@ -342,3 +342,55 @@
   EXPECT_FALSE(menu->IsItemPresent(
       IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES));
 }
+
+IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest, ContextMenu_UKM) {
+  Init({kSharingDeviceRegistration, kClickToCallUI,
+        kClickToCallContextMenuForSelectedText},
+       {});
+  SetUpDevices(/*count=*/1);
+
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  base::RunLoop run_loop;
+  ukm_recorder.SetOnAddEntryCallback(
+      ukm::builders::Sharing_ClickToCall::kEntryName, run_loop.QuitClosure());
+
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      InitRightClickMenu(GURL(kNonTelUrl), base::ASCIIToUTF16("Google"),
+                         base::ASCIIToUTF16(kTextWithPhoneNumber));
+
+  // Check click to call items in context menu
+  ASSERT_TRUE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE));
+  // Send number to device
+  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE,
+                       0);
+
+  // Expect UKM metrics to be logged
+  run_loop.Run();
+  std::vector<const ukm::mojom::UkmEntry*> ukm_entries =
+      ukm_recorder.GetEntriesByName(
+          ukm::builders::Sharing_ClickToCall::kEntryName);
+  ASSERT_EQ(1u, ukm_entries.size());
+
+  const int64_t* entry_point = ukm_recorder.GetEntryMetric(
+      ukm_entries[0], ukm::builders::Sharing_ClickToCall::kEntryPointName);
+  const int64_t* has_apps = ukm_recorder.GetEntryMetric(
+      ukm_entries[0], ukm::builders::Sharing_ClickToCall::kHasAppsName);
+  const int64_t* has_devices = ukm_recorder.GetEntryMetric(
+      ukm_entries[0], ukm::builders::Sharing_ClickToCall::kHasDevicesName);
+  const int64_t* selection = ukm_recorder.GetEntryMetric(
+      ukm_entries[0], ukm::builders::Sharing_ClickToCall::kSelectionName);
+
+  ASSERT_TRUE(entry_point);
+  ASSERT_TRUE(has_apps);
+  ASSERT_TRUE(has_devices);
+  ASSERT_TRUE(selection);
+
+  EXPECT_EQ(
+      static_cast<int64_t>(SharingClickToCallEntryPoint::kRightClickSelection),
+      *entry_point);
+  EXPECT_EQ(true, *has_devices);
+  EXPECT_EQ(static_cast<int64_t>(SharingClickToCallSelection::kDevice),
+            *selection);
+  // TODO(knollr): mock apps and verify |has_apps| here too.
+}
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
index b78a874..af199e4 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
@@ -51,10 +51,13 @@
 ClickToCallContextMenuObserver::~ClickToCallContextMenuObserver() = default;
 
 void ClickToCallContextMenuObserver::BuildMenu(
-    const std::string& phone_number) {
+    const std::string& phone_number,
+    SharingClickToCallEntryPoint entry_point) {
   DCHECK(!phone_number.empty());
 
   phone_number_ = phone_number;
+  entry_point_ = entry_point;
+
   controller_->UpdateDevices();
   const std::vector<std::unique_ptr<syncer::DeviceInfo>>& devices =
       controller_->devices();
@@ -136,6 +139,7 @@
 
 void ClickToCallContextMenuObserver::SendClickToCallMessage(
     int chosen_device_index) {
+  DCHECK(entry_point_);
   const std::vector<std::unique_ptr<syncer::DeviceInfo>>& devices =
       controller_->devices();
   if (chosen_device_index >= static_cast<int>(devices.size()))
@@ -144,5 +148,6 @@
   LogClickToCallSelectedDeviceIndex(kSharingClickToCallUiContextMenu,
                                     chosen_device_index);
 
-  controller_->OnDeviceSelected(phone_number_, *devices[chosen_device_index]);
+  controller_->OnDeviceSelected(phone_number_, *devices[chosen_device_index],
+                                *entry_point_);
 }
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h
index 74ef141..ccce41d 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h
@@ -12,6 +12,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "chrome/browser/sharing/sharing_metrics.h"
 #include "components/renderer_context_menu/render_view_context_menu_observer.h"
 #include "ui/base/models/simple_menu_model.h"
 
@@ -43,7 +44,8 @@
   bool IsCommandIdEnabled(int command_id) override;
   void ExecuteCommand(int command_id) override;
 
-  void BuildMenu(const std::string& phone_number);
+  void BuildMenu(const std::string& phone_number,
+                 SharingClickToCallEntryPoint entry_point);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(ClickToCallContextMenuObserverTest,
@@ -64,6 +66,7 @@
   SubMenuDelegate sub_menu_delegate_{this};
 
   std::string phone_number_;
+  base::Optional<SharingClickToCallEntryPoint> entry_point_;
 
   std::unique_ptr<ui::SimpleMenuModel> sub_menu_model_;
 
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
index 1721daa..c5419ac9 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_fcm_handler.h"
 #include "chrome/browser/sharing/sharing_fcm_sender.h"
+#include "chrome/browser/sharing/sharing_metrics.h"
 #include "chrome/browser/sharing/sharing_service.h"
 #include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
@@ -97,7 +98,8 @@
   }
 
   void BuildMenu(const std::string& phone_number) {
-    observer_->BuildMenu(phone_number);
+    observer_->BuildMenu(phone_number,
+                         SharingClickToCallEntryPoint::kRightClickLink);
     sharing_message.mutable_click_to_call_message()->set_phone_number(
         phone_number);
   }
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_ui_controller.cc b/chrome/browser/sharing/click_to_call/click_to_call_ui_controller.cc
index f62158e..c28053a 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_ui_controller.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_ui_controller.cc
@@ -53,10 +53,23 @@
 
 void ClickToCallUiController::OnDeviceSelected(
     const std::string& phone_number,
-    const syncer::DeviceInfo& device) {
+    const syncer::DeviceInfo& device,
+    SharingClickToCallEntryPoint entry_point) {
+  // TODO(knollr): figure out how to get a value for |has_apps|.
+  LogClickToCallUKM(web_contents(), entry_point,
+                    /*has_devices=*/true, /*has_apps=*/false,
+                    SharingClickToCallSelection::kDevice);
+
   SendNumberToDevice(device, phone_number);
 }
 
+void ClickToCallUiController::OnDialogClosed(SharingDialog* dialog) {
+  if (ukm_recorder_ && this->dialog() == dialog)
+    std::move(ukm_recorder_).Run(SharingClickToCallSelection::kNone);
+
+  SharingUiController::OnDialogClosed(dialog);
+}
+
 base::string16 ClickToCallUiController::GetTitle() {
   return l10n_util::GetStringUTF16(
       IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TITLE_LABEL);
@@ -88,6 +101,9 @@
 }
 
 void ClickToCallUiController::OnDeviceChosen(const syncer::DeviceInfo& device) {
+  if (ukm_recorder_)
+    std::move(ukm_recorder_).Run(SharingClickToCallSelection::kDevice);
+
   SendNumberToDevice(device, GetUnescapedURLContent(phone_url_));
 }
 
@@ -102,6 +118,9 @@
 }
 
 void ClickToCallUiController::OnAppChosen(const App& app) {
+  if (ukm_recorder_)
+    std::move(ukm_recorder_).Run(SharingClickToCallSelection::kApp);
+
   ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(phone_url_,
                                                          web_contents());
 }
@@ -112,6 +131,12 @@
 }
 
 SharingDialog* ClickToCallUiController::DoShowDialog(BrowserWindow* window) {
+  if (!HasSendFailed()) {
+    // Only left clicks open a dialog.
+    ukm_recorder_ = base::BindOnce(&LogClickToCallUKM, web_contents(),
+                                   SharingClickToCallEntryPoint::kLeftClickLink,
+                                   !devices().empty(), !apps().empty());
+  }
   return window->ShowClickToCallDialog(web_contents(), this);
 }
 
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h b/chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h
index c2d41a9..5900e5f 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h
+++ b/chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h
@@ -8,8 +8,10 @@
 #include <string>
 #include <vector>
 
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/sharing/sharing_metrics.h"
 #include "chrome/browser/sharing/sharing_service.h"
 #include "chrome/browser/sharing/sharing_ui_controller.h"
 #include "chrome/browser/ui/page_action/page_action_icon_container.h"
@@ -33,7 +35,8 @@
   ~ClickToCallUiController() override;
 
   void OnDeviceSelected(const std::string& phone_number,
-                        const syncer::DeviceInfo& device);
+                        const syncer::DeviceInfo& device,
+                        SharingClickToCallEntryPoint entry_point);
 
   // Overridden from SharingUiController:
   base::string16 GetTitle() override;
@@ -41,6 +44,7 @@
   int GetRequiredDeviceCapabilities() override;
   void OnDeviceChosen(const syncer::DeviceInfo& device) override;
   void OnAppChosen(const App& app) override;
+  void OnDialogClosed(SharingDialog* dialog) override;
   base::string16 GetContentType() const override;
   const gfx::VectorIcon& GetVectorIcon() const override;
   base::string16 GetTextForTooltipAndAccessibleName() const override;
@@ -57,11 +61,14 @@
 
  private:
   friend class content::WebContentsUserData<ClickToCallUiController>;
+  using UKMRecorderCallback =
+      base::OnceCallback<void(SharingClickToCallSelection)>;
 
   // Sends |phone_number| to |device| as a SharingMessage.
   void SendNumberToDevice(const syncer::DeviceInfo& device,
                           const std::string& phone_number);
 
+  UKMRecorderCallback ukm_recorder_;
   GURL phone_url_;
   bool hide_default_handler_ = false;
 
diff --git a/chrome/browser/sharing/sharing_metrics.cc b/chrome/browser/sharing/sharing_metrics.cc
index 7978055..47596ee 100644
--- a/chrome/browser/sharing/sharing_metrics.cc
+++ b/chrome/browser/sharing/sharing_metrics.cc
@@ -7,6 +7,11 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
 #include "chrome/browser/sharing/sharing_device_registration_result.h"
+#include "components/ukm/content/source_url_recorder.h"
+#include "content/public/browser/web_contents.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace {
 
@@ -117,3 +122,25 @@
 void LogSendSharingAckMessageResult(SharingSendMessageResult result) {
   base::UmaHistogramEnumeration("Sharing.SendAckMessageResult", result);
 }
+
+void LogClickToCallUKM(content::WebContents* web_contents,
+                       SharingClickToCallEntryPoint entry_point,
+                       bool has_devices,
+                       bool has_apps,
+                       SharingClickToCallSelection selection) {
+  ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
+  if (!ukm_recorder)
+    return;
+
+  ukm::SourceId source_id =
+      ukm::GetSourceIdForWebContentsDocument(web_contents);
+  if (source_id == ukm::kInvalidSourceId)
+    return;
+
+  ukm::builders::Sharing_ClickToCall(source_id)
+      .SetEntryPoint(static_cast<int64_t>(entry_point))
+      .SetHasDevices(has_devices)
+      .SetHasApps(has_apps)
+      .SetSelection(static_cast<int64_t>(selection))
+      .Record(ukm_recorder);
+}
diff --git a/chrome/browser/sharing/sharing_metrics.h b/chrome/browser/sharing/sharing_metrics.h
index 9c37e8f..e68459c 100644
--- a/chrome/browser/sharing/sharing_metrics.h
+++ b/chrome/browser/sharing/sharing_metrics.h
@@ -10,6 +10,10 @@
 #include "base/time/time.h"
 #include "chrome/browser/sharing/sharing_send_message_result.h"
 
+namespace content {
+class WebContents;
+}  // namespace content
+
 enum class SharingDeviceRegistrationResult;
 
 // Result of VAPID key creation during Sharing registration.
@@ -35,6 +39,28 @@
   kMaxValue = kErrorDialog,
 };
 
+// Entry point of a Click to Call journey.
+// These values are logged to UKM. Entries should not be renumbered and numeric
+// values should never be reused. Please keep in sync with
+// "SharingClickToCallEntryPoint" in src/tools/metrics/histograms/enums.xml.
+enum class SharingClickToCallEntryPoint {
+  kLeftClickLink = 0,
+  kRightClickLink = 1,
+  kRightClickSelection = 2,
+  kMaxValue = kRightClickSelection,
+};
+
+// Selection at the end of a Click to Call journey.
+// These values are logged to UKM. Entries should not be renumbered and numeric
+// values should never be reused. Please keep in sync with
+// "SharingClickToCallSelection" in src/tools/metrics/histograms/enums.xml.
+enum class SharingClickToCallSelection {
+  kNone = 0,
+  kDevice = 1,
+  kApp = 2,
+  kMaxValue = kApp,
+};
+
 // These histogram suffixes must match the ones in SharingClickToCallUi defined
 // in histograms.xml.
 const char kSharingClickToCallUiContextMenu[] = "ContextMenu";
@@ -95,4 +121,12 @@
 // Logs to UMA result of sendin an ack of a SharingMessage.
 void LogSendSharingAckMessageResult(SharingSendMessageResult result);
 
+// Records a Click to Call selection to UKM. This is logged after a completed
+// action like selecting an app or a device to send the phone number to.
+void LogClickToCallUKM(content::WebContents* web_contents,
+                       SharingClickToCallEntryPoint entry_point,
+                       bool has_devices,
+                       bool has_apps,
+                       SharingClickToCallSelection selection);
+
 #endif  // CHROME_BROWSER_SHARING_SHARING_METRICS_H_
diff --git a/chrome/browser/sharing/sharing_ui_controller.cc b/chrome/browser/sharing/sharing_ui_controller.cc
index 74f6300..d12ea9a 100644
--- a/chrome/browser/sharing/sharing_ui_controller.cc
+++ b/chrome/browser/sharing/sharing_ui_controller.cc
@@ -52,12 +52,18 @@
 SharingUiController::~SharingUiController() = default;
 
 void SharingUiController::CloseDialog() {
-  if (dialog_)
-    dialog_->Hide();
+  if (!dialog_)
+    return;
 
-  // Treat the dialog as closed as the process of closing the native widget
-  // might be async.
-  dialog_ = nullptr;
+  dialog_->Hide();
+
+  // SharingDialog::Hide may close the dialog asynchronously, and therefore not
+  // call OnDialogClosed immediately. If that is the case, call OnDialogClosed
+  // now to notify subclasses and clear |dialog_|.
+  if (dialog_)
+    OnDialogClosed(dialog_);
+
+  DCHECK(!dialog_);
 }
 
 void SharingUiController::ShowNewDialog() {
@@ -126,7 +132,6 @@
   send_result_ = SharingSendMessageResult::kSuccessful;
 
   CloseDialog();
-  UpdateIcon();
   DoUpdateApps(base::BindOnce(&SharingUiController::OnAppsReceived,
                               weak_ptr_factory_.GetWeakPtr(), last_dialog_id_));
 }
diff --git a/chrome/browser/sharing/sharing_ui_controller.h b/chrome/browser/sharing/sharing_ui_controller.h
index 379fce3e..5dcd45c 100644
--- a/chrome/browser/sharing/sharing_ui_controller.h
+++ b/chrome/browser/sharing/sharing_ui_controller.h
@@ -65,7 +65,7 @@
   virtual base::string16 GetTextForTooltipAndAccessibleName() const = 0;
 
   // Called by the SharingDialog when it is being closed.
-  void OnDialogClosed(SharingDialog* dialog);
+  virtual void OnDialogClosed(SharingDialog* dialog);
 
   void UpdateAndShowDialog();
 
diff --git a/chrome/browser/supervised_user/logged_in_user_mixin.cc b/chrome/browser/supervised_user/logged_in_user_mixin.cc
index bf54e04..ad9576e04 100644
--- a/chrome/browser/supervised_user/logged_in_user_mixin.cc
+++ b/chrome/browser/supervised_user/logged_in_user_mixin.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/supervised_user/logged_in_user_mixin.h"
 
 #include "chromeos/login/auth/user_context.h"
+#include "net/dns/mock_host_resolver.h"
 
 namespace chromeos {
 
@@ -27,17 +28,40 @@
 LoggedInUserMixin::LoggedInUserMixin(
     InProcessBrowserTestMixinHost* mixin_host,
     LogInType type,
-    net::EmbeddedTestServer* embedded_test_server)
+    net::EmbeddedTestServer* embedded_test_server,
+    bool should_launch_browser)
     : user_(AccountId::FromUserEmailGaiaId(kTestUserName, kTestUserGaiaId),
             ConvertUserType(type)),
       login_manager_(mixin_host, {user_}),
       policy_server_(mixin_host),
       user_policy_(mixin_host, user_.account_id, &policy_server_),
       embedded_test_server_setup_(mixin_host, embedded_test_server),
-      fake_gaia_(mixin_host, embedded_test_server) {}
+      fake_gaia_(mixin_host, embedded_test_server) {
+  // By default, LoginManagerMixin will set up user session manager not to
+  // launch browser as part of user session setup - use this to override that
+  // behavior.
+  login_manager_.set_should_launch_browser(should_launch_browser);
+}
 
 LoggedInUserMixin::~LoggedInUserMixin() = default;
 
+void LoggedInUserMixin::SetUpOnMainThreadHelper(
+    net::RuleBasedHostResolverProc* host_resolver,
+    InProcessBrowserTest* test_base,
+    bool issue_any_scope_token) {
+  // By default, browser tests block anything that doesn't go to localhost, so
+  // account.google.com requests would never reach fake GAIA server without
+  // this.
+  host_resolver->AddRule("*", "127.0.0.1");
+  LogInUser(issue_any_scope_token);
+  // Set the private |browser_| member in InProcessBrowserTest.
+  // Otherwise calls to InProcessBrowserTest::browser() returns null and leads
+  // to segmentation faults.
+  // Note: |browser_| is only non-null if should_launch_browser was set to true
+  // in the constructor.
+  test_base->SelectFirstBrowser();
+}
+
 void LoggedInUserMixin::LogInUser(bool issue_any_scope_token) {
   UserContext user_context = LoginManagerMixin::CreateDefaultUserContext(user_);
   if (user_.user_type == user_manager::USER_TYPE_CHILD) {
@@ -53,8 +77,4 @@
   login_manager_.LoginAndWaitForActiveSession(user_context);
 }
 
-void LoggedInUserMixin::set_should_launch_browser(bool value) {
-  login_manager_.set_should_launch_browser(value);
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/supervised_user/logged_in_user_mixin.h b/chrome/browser/supervised_user/logged_in_user_mixin.h
index 6809cf9..246eed8 100644
--- a/chrome/browser/supervised_user/logged_in_user_mixin.h
+++ b/chrome/browser/supervised_user/logged_in_user_mixin.h
@@ -22,22 +22,32 @@
  public:
   enum class LogInType { kRegular, kChild };
 
-  LoggedInUserMixin(InProcessBrowserTestMixinHost* host,
+  LoggedInUserMixin(InProcessBrowserTestMixinHost* mixin_host,
                     LogInType type,
-                    net::EmbeddedTestServer* embedded_test_server);
+                    net::EmbeddedTestServer* embedded_test_server,
+                    bool should_launch_browser = true);
   ~LoggedInUserMixin();
 
+  // Helper function for refactoring common setup code.
+  // Call this function in your test class's SetUpOnMainThread() after calling
+  // MixinBasedInProcessBrowserTest::SetUpOnMainThread().
+  // This functions does the following:
+  // * Reroute all requests to localhost.
+  // * Log in as regular or child account depending on the |type| argument
+  // passed to the constructor.
+  // * Call InProcessBrowserTest::SelectFirstBrowser() so that browser()
+  // returns a non-null browser instance. Note: This call will only be effective
+  // if should_launch_browser was set to true in the constructor.
+  void SetUpOnMainThreadHelper(net::RuleBasedHostResolverProc* host_resolver,
+                               InProcessBrowserTest* test_base,
+                               bool issue_any_scope_token = false);
+
   // Log in as regular or child account depending on the |type| argument passed
   // to the constructor.
   // * If |issue_any_scope_token|, FakeGaiaMixin will issue a special all-access
   // token associated with the test refresh token. Only matters for child login.
   void LogInUser(bool issue_any_scope_token = false);
 
-  // By default, LoginManagerMixin will set up user session manager not to
-  // launch browser as part of user session setup - use this to override that
-  // behavior.
-  void set_should_launch_browser(bool value);
-
  private:
   LoginManagerMixin::TestUserInfo user_;
   LoginManagerMixin login_manager_;
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
index 7bada5c..03f00ee 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
@@ -10,10 +10,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/supervised_user/logged_in_user_mixin.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
-#include "chrome/browser/supervised_user/supervised_user_test_base.h"
+#include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/navigation_entry.h"
@@ -37,13 +39,14 @@
 
 }  // namespace
 
-class SupervisedUserNavigationThrottleTest : public SupervisedUserTestBase {
+class SupervisedUserNavigationThrottleTest
+    : public MixinBasedInProcessBrowserTest {
  protected:
   SupervisedUserNavigationThrottleTest() = default;
   ~SupervisedUserNavigationThrottleTest() override = default;
 
   void BlockHost(const std::string& host) {
-    Profile* profile = GetPrimaryUserProfile();
+    Profile* profile = browser()->profile();
     SupervisedUserSettingsService* settings_service =
         SupervisedUserSettingsServiceFactory::GetForKey(
             profile->GetProfileKey());
@@ -55,8 +58,15 @@
 
   bool IsInterstitialBeingShown(Browser* browser);
 
+  virtual chromeos::LoggedInUserMixin::LogInType GetLogInType() {
+    return chromeos::LoggedInUserMixin::LogInType::kChild;
+  }
+
  private:
+  void SetUp() override;
   void SetUpOnMainThread() override;
+
+  std::unique_ptr<chromeos::LoggedInUserMixin> logged_in_user_mixin_;
 };
 
 bool SupervisedUserNavigationThrottleTest::IsInterstitialBeingShown(
@@ -69,21 +79,28 @@
          title == base::ASCIIToUTF16("Site blocked");
 }
 
-void SupervisedUserNavigationThrottleTest::SetUpOnMainThread() {
-  SupervisedUserTestBase::SetUpOnMainThread();
+void SupervisedUserNavigationThrottleTest::SetUp() {
+  // Polymorphically initiate logged_in_user_mixin_.
+  logged_in_user_mixin_ = std::make_unique<chromeos::LoggedInUserMixin>(
+      &mixin_host_, GetLogInType(), embedded_test_server());
+  MixinBasedInProcessBrowserTest::SetUp();
+}
 
+void SupervisedUserNavigationThrottleTest::SetUpOnMainThread() {
+  MixinBasedInProcessBrowserTest::SetUpOnMainThread();
   // Resolve everything to localhost.
   host_resolver()->AddIPLiteralRule("*", "127.0.0.1", "localhost");
 
   ASSERT_TRUE(embedded_test_server()->Started());
+
+  logged_in_user_mixin_->SetUpOnMainThreadHelper(host_resolver(), this);
 }
 
 // Tests that navigating to a blocked page simply fails if there is no
 // SupervisedUserNavigationObserver.
 IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleTest,
                        NoNavigationObserverBlock) {
-  LogInUser(LogInType::kChild);
-  Profile* profile = GetPrimaryUserProfile();
+  Profile* profile = browser()->profile();
   SupervisedUserSettingsService* supervised_user_settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(profile->GetProfileKey());
   supervised_user_settings_service->SetLocalSetting(
@@ -106,8 +123,6 @@
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleTest,
                        BlockMainFrameWithInterstitial) {
-  LogInUser(LogInType::kChild);
-
   BlockHost(kExampleHost2);
 
   GURL allowed_url = embedded_test_server()->GetURL(
@@ -123,8 +138,6 @@
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleTest,
                        DontBlockSubFrame) {
-  LogInUser(LogInType::kChild);
-
   BlockHost(kExampleHost2);
   BlockHost(kIframeHost2);
 
@@ -151,11 +164,14 @@
  protected:
   SupervisedUserNavigationThrottleNotSupervisedTest() = default;
   ~SupervisedUserNavigationThrottleNotSupervisedTest() override = default;
+
+  chromeos::LoggedInUserMixin::LogInType GetLogInType() override {
+    return chromeos::LoggedInUserMixin::LogInType::kRegular;
+  }
 };
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleNotSupervisedTest,
                        DontBlock) {
-  LogInUser(LogInType::kRegular);
   BlockHost(kExampleHost);
 
   GURL blocked_url = embedded_test_server()->GetURL(
diff --git a/chrome/browser/supervised_user/supervised_user_service_browsertest.cc b/chrome/browser/supervised_user/supervised_user_service_browsertest.cc
index 9d1b731..2a21102 100644
--- a/chrome/browser/supervised_user/supervised_user_service_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_service_browsertest.cc
@@ -13,23 +13,36 @@
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/supervised_user/logged_in_user_mixin.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
-#include "chrome/browser/supervised_user/supervised_user_test_base.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/common/net/safe_search_util.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/test_utils.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
-using SupervisedUserServiceTestSupervised = SupervisedUserTestBase;
+class SupervisedUserServiceTestSupervised
+    : public MixinBasedInProcessBrowserTest {
+ protected:
+  void SetUpOnMainThread() override {
+    MixinBasedInProcessBrowserTest::SetUpOnMainThread();
+    logged_in_user_mixin_.SetUpOnMainThreadHelper(host_resolver(), this);
+  }
+
+ private:
+  chromeos::LoggedInUserMixin logged_in_user_mixin_{
+      &mixin_host_, chromeos::LoggedInUserMixin::LogInType::kChild,
+      embedded_test_server()};
+};
 
 // unsupervised tests
 using SupervisedUserServiceTest = InProcessBrowserTest;
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserServiceTest, LocalPolicies) {
-  Profile* profile = SupervisedUserTestBase::GetPrimaryUserProfile();
+  Profile* profile = browser()->profile();
   PrefService* prefs = profile->GetPrefs();
   EXPECT_FALSE(prefs->GetBoolean(prefs::kForceGoogleSafeSearch));
   EXPECT_EQ(prefs->GetInteger(prefs::kForceYouTubeRestrict),
@@ -39,7 +52,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserServiceTest, ProfileName) {
-  Profile* profile = SupervisedUserTestBase::GetPrimaryUserProfile();
+  Profile* profile = browser()->profile();
   PrefService* prefs = profile->GetPrefs();
   EXPECT_TRUE(prefs->IsUserModifiablePreference(prefs::kProfileName));
 
@@ -52,8 +65,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserServiceTestSupervised, LocalPolicies) {
-  LogInUser(LogInType::kChild);
-  Profile* profile = GetPrimaryUserProfile();
+  Profile* profile = browser()->profile();
   PrefService* prefs = profile->GetPrefs();
   EXPECT_FALSE(prefs->GetBoolean(prefs::kForceGoogleSafeSearch));
   EXPECT_EQ(prefs->GetInteger(prefs::kForceYouTubeRestrict),
@@ -64,9 +76,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserServiceTestSupervised, ProfileName) {
-  LogInUser(LogInType::kChild);
-
-  Profile* profile = GetPrimaryUserProfile();
+  Profile* profile = browser()->profile();
   PrefService* prefs = profile->GetPrefs();
   std::string original_name = prefs->GetString(prefs::kProfileName);
 
diff --git a/chrome/browser/supervised_user/supervised_user_test_base.cc b/chrome/browser/supervised_user/supervised_user_test_base.cc
deleted file mode 100644
index ed2da6e..0000000
--- a/chrome/browser/supervised_user/supervised_user_test_base.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/supervised_user/supervised_user_test_base.h"
-
-#include "chrome/browser/chromeos/child_accounts/child_account_test_utils.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "components/user_manager/user_manager.h"
-
-void SupervisedUserTestBase::LogInUser(LogInType type) {
-  SkipToLoginScreen();
-  switch (type) {
-    case LogInType::kChild:
-      LogIn(kAccountId, kAccountPassword,
-            chromeos::test::kChildAccountServiceFlags);
-      break;
-    case LogInType::kRegular:
-      LogIn(kAccountId, kAccountPassword, kEmptyServices);
-      break;
-  }
-}
-
-Browser* SupervisedUserTestBase::browser() {
-  const BrowserList* active_browser_list = BrowserList::GetInstance();
-  if (active_browser_list->empty())
-    return nullptr;
-  Browser* browser = active_browser_list->get(0);
-  return browser;
-}
-
-Profile* SupervisedUserTestBase::GetPrimaryUserProfile() {
-  return chromeos::ProfileHelper::Get()->GetProfileByUser(
-      user_manager::UserManager::Get()->GetPrimaryUser());
-}
-
-SupervisedUserService* SupervisedUserTestBase::supervised_user_service() {
-  return SupervisedUserServiceFactory::GetForProfile(GetPrimaryUserProfile());
-}
diff --git a/chrome/browser/supervised_user/supervised_user_test_base.h b/chrome/browser/supervised_user/supervised_user_test_base.h
deleted file mode 100644
index 02b196d7..0000000
--- a/chrome/browser/supervised_user/supervised_user_test_base.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_TEST_BASE_H_
-#define CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_TEST_BASE_H_
-
-#include "chrome/browser/chromeos/policy/login_policy_test_base.h"
-#include "chrome/browser/supervised_user/supervised_user_service.h"
-#include "chrome/browser/ui/browser.h"
-
-// TODO (crbug.com/990909): Deprecate this class in favor of
-// LoggedInUserMixin.
-// Base class for child user browser tests. Inherit from
-// this class if logging in as child user is required for tests. This class
-// inherits from LoginPolicyTestBase because signing in a child account requires
-// fetching user policies.
-class SupervisedUserTestBase : public policy::LoginPolicyTestBase {
- public:
-  enum class LogInType { kRegular, kChild };
-
-  static Profile* GetPrimaryUserProfile();
-
- protected:
-  void LogInUser(LogInType type);
-
-  // Returns the first browser in the active browser list.
-  // Hides InProcessBrowserTest::browser() because the browser is only created
-  // after LogInUser() is called and by then it's too late to set
-  // InProcessBrowserTest::browser_ because it's a private member.
-  // Will return null if called before LogInUser().
-  static Browser* browser();
-
-  static SupervisedUserService* supervised_user_service();
-};
-
-#endif  // CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_TEST_BASE_H_
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter_browsertest.cc b/chrome/browser/supervised_user/supervised_user_url_filter_browsertest.cc
index f878398..c66ea08 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_url_filter_browsertest.cc
@@ -15,12 +15,15 @@
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/supervised_user/logged_in_user_mixin.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/browser/supervised_user/supervised_user_interstitial.h"
 #include "chrome/browser/supervised_user/supervised_user_navigation_observer.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
-#include "chrome/browser/supervised_user/supervised_user_test_base.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -70,7 +73,7 @@
 };
 
 // Tests filtering for supervised users.
-class SupervisedUserURLFilterTest : public SupervisedUserTestBase {
+class SupervisedUserURLFilterTest : public MixinBasedInProcessBrowserTest {
  public:
   // Indicates whether the interstitial should proceed or not.
   enum InterstitialAction {
@@ -121,7 +124,15 @@
                                     "MAP *.example.com " + host_port + "," +
                                         "MAP *.new-example.com " + host_port +
                                         "," + "MAP *.a.com " + host_port);
-    SupervisedUserTestBase::SetUpCommandLine(command_line);
+    MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpOnMainThread() override {
+    MixinBasedInProcessBrowserTest::SetUpOnMainThread();
+    logged_in_user_mixin_.SetUpOnMainThreadHelper(host_resolver(), this);
+
+    supervised_user_service_ =
+        SupervisedUserServiceFactory::GetForProfile(browser()->profile());
   }
 
   // Acts like a synchronous call to history's QueryHistory. Modified from
@@ -141,13 +152,20 @@
         &history_task_tracker);
     run_loop.Run();  // Will go until ...Complete calls Quit.
   }
+
+  SupervisedUserService* supervised_user_service_ = nullptr;
+
+  chromeos::LoggedInUserMixin logged_in_user_mixin_{
+      &mixin_host_, chromeos::LoggedInUserMixin::LogInType::kChild,
+      embedded_test_server()};
 };
 
 // Tests the filter mode in which all sites are blocked by default.
 class SupervisedUserBlockModeTest : public SupervisedUserURLFilterTest {
  public:
-  void BlockAllSites() {
-    Profile* profile = GetPrimaryUserProfile();
+  void SetUpOnMainThread() override {
+    SupervisedUserURLFilterTest::SetUpOnMainThread();
+    Profile* profile = browser()->profile();
     SupervisedUserSettingsService* supervised_user_settings_service =
         SupervisedUserSettingsServiceFactory::GetForKey(
             profile->GetProfileKey());
@@ -206,9 +224,6 @@
 // Navigates to a blocked URL.
 IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest,
                        SendAccessRequestOnBlockedURL) {
-  LogInUser(LogInType::kChild);
-  BlockAllSites();
-
   GURL test_url("http://www.example.com/simple.html");
   ui_test_utils::NavigateToURL(browser(), test_url);
 
@@ -231,9 +246,6 @@
 // Navigates to a blocked URL in a new tab. We expect the tab to be closed
 // automatically on pressing the "back" button on the interstitial.
 IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest, OpenBlockedURLInNewTab) {
-  LogInUser(LogInType::kChild);
-  BlockAllSites();
-
   TabStripModel* tab_strip = browser()->tab_strip_model();
   WebContents* prev_tab = tab_strip->GetActiveWebContents();
 
@@ -261,8 +273,6 @@
 // navigation is blocked before it commits). The expected behavior is the same
 // though: the tab should be closed when going back.
 IN_PROC_BROWSER_TEST_F(SupervisedUserURLFilterTest, BlockNewTabAfterLoading) {
-  LogInUser(LogInType::kChild);
-
   TabStripModel* tab_strip = browser()->tab_strip_model();
   WebContents* prev_tab = tab_strip->GetActiveWebContents();
 
@@ -280,13 +290,13 @@
     // Block the current URL.
     SupervisedUserSettingsService* supervised_user_settings_service =
         SupervisedUserSettingsServiceFactory::GetForKey(
-            GetPrimaryUserProfile()->GetProfileKey());
+            browser()->profile()->GetProfileKey());
     supervised_user_settings_service->SetLocalSetting(
         supervised_users::kContentPackDefaultFilteringBehavior,
         std::make_unique<base::Value>(SupervisedUserURLFilter::BLOCK));
 
     const SupervisedUserURLFilter* filter =
-        supervised_user_service()->GetURLFilter();
+        supervised_user_service_->GetURLFilter();
     ASSERT_EQ(SupervisedUserURLFilter::BLOCK,
               filter->GetFilteringBehaviorForURL(test_url));
 
@@ -310,8 +320,6 @@
 // Tests that we don't end up canceling an interstitial (thereby closing the
 // whole tab) by attempting to show a second one above it.
 IN_PROC_BROWSER_TEST_F(SupervisedUserURLFilterTest, DontShowInterstitialTwice) {
-  LogInUser(LogInType::kChild);
-
   TabStripModel* tab_strip = browser()->tab_strip_model();
 
   // Open URL in a new tab.
@@ -327,13 +335,13 @@
   // Block the current URL.
   SupervisedUserSettingsService* supervised_user_settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(
-          GetPrimaryUserProfile()->GetProfileKey());
+          browser()->profile()->GetProfileKey());
   supervised_user_settings_service->SetLocalSetting(
       supervised_users::kContentPackDefaultFilteringBehavior,
       std::make_unique<base::Value>(SupervisedUserURLFilter::BLOCK));
 
   const SupervisedUserURLFilter* filter =
-      supervised_user_service()->GetURLFilter();
+      supervised_user_service_->GetURLFilter();
   ASSERT_EQ(SupervisedUserURLFilter::BLOCK,
             filter->GetFilteringBehaviorForURL(test_url));
 
@@ -345,7 +353,7 @@
 
   // Trigger a no-op change to the site lists, which will notify observers of
   // the URL filter.
-  supervised_user_service()->OnSiteListUpdated();
+  supervised_user_service_->OnSiteListUpdated();
 
   EXPECT_EQ(tab, tab_strip->GetActiveWebContents());
 }
@@ -354,9 +362,6 @@
 // page.
 IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest,
                        NavigateFromBlockedPageToBlockedPage) {
-  LogInUser(LogInType::kChild);
-  BlockAllSites();
-
   GURL test_url("http://www.example.com/simple.html");
   ui_test_utils::NavigateToURL(browser(), test_url);
 
@@ -373,20 +378,17 @@
 
 // Tests whether a visit attempt adds a special history entry.
 IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest, HistoryVisitRecorded) {
-  LogInUser(LogInType::kChild);
-  BlockAllSites();
-
   GURL allowed_url("http://www.example.com/simple.html");
 
   const SupervisedUserURLFilter* filter =
-      supervised_user_service()->GetURLFilter();
+      supervised_user_service_->GetURLFilter();
 
   // Set the host as allowed.
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
   dict->SetKey(allowed_url.host(), base::Value(true));
   SupervisedUserSettingsService* supervised_user_settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(
-          GetPrimaryUserProfile()->GetProfileKey());
+          browser()->profile()->GetProfileKey());
   supervised_user_settings_service->SetLocalSetting(
       supervised_users::kContentPackManualBehaviorHosts, std::move(dict));
   EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
@@ -416,7 +418,7 @@
 
   // Query the history entry.
   history::HistoryService* history_service =
-      HistoryServiceFactory::GetForProfile(GetPrimaryUserProfile(),
+      HistoryServiceFactory::GetForProfile(browser()->profile(),
                                            ServiceAccessType::EXPLICIT_ACCESS);
   history::QueryOptions options;
   history::QueryResults results;
@@ -431,8 +433,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserURLFilterTest, GoBackOnDontProceed) {
-  LogInUser(LogInType::kChild);
-
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   // Ensure navigation completes.
@@ -450,12 +450,12 @@
   dict->SetKey(test_url.host(), base::Value(false));
   SupervisedUserSettingsService* supervised_user_settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(
-          GetPrimaryUserProfile()->GetProfileKey());
+          browser()->profile()->GetProfileKey());
   supervised_user_settings_service->SetLocalSetting(
       supervised_users::kContentPackManualBehaviorHosts, std::move(dict));
 
   const SupervisedUserURLFilter* filter =
-      supervised_user_service()->GetURLFilter();
+      supervised_user_service_->GetURLFilter();
   ASSERT_EQ(SupervisedUserURLFilter::BLOCK,
             filter->GetFilteringBehaviorForURL(test_url));
 
@@ -474,8 +474,6 @@
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserURLFilterTest,
                        ClosingBlockedTabDoesNotCrash) {
-  LogInUser(LogInType::kChild);
-
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   // Ensure navigation completes.
@@ -492,12 +490,12 @@
   dict->SetKey(test_url.host(), base::Value(false));
   SupervisedUserSettingsService* supervised_user_settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(
-          GetPrimaryUserProfile()->GetProfileKey());
+          browser()->profile()->GetProfileKey());
   supervised_user_settings_service->SetLocalSetting(
       supervised_users::kContentPackManualBehaviorHosts, std::move(dict));
 
   const SupervisedUserURLFilter* filter =
-      supervised_user_service()->GetURLFilter();
+      supervised_user_service_->GetURLFilter();
   ASSERT_EQ(SupervisedUserURLFilter::BLOCK,
             filter->GetFilteringBehaviorForURL(test_url));
 
@@ -508,8 +506,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserURLFilterTest, BlockThenUnblock) {
-  LogInUser(LogInType::kChild);
-
   GURL test_url("http://www.example.com/simple.html");
   ui_test_utils::NavigateToURL(browser(), test_url);
 
@@ -523,12 +519,12 @@
   dict->SetKey(test_url.host(), base::Value(false));
   SupervisedUserSettingsService* supervised_user_settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(
-          GetPrimaryUserProfile()->GetProfileKey());
+          browser()->profile()->GetProfileKey());
   supervised_user_settings_service->SetLocalSetting(
       supervised_users::kContentPackManualBehaviorHosts, std::move(dict));
 
   const SupervisedUserURLFilter* filter =
-      supervised_user_service()->GetURLFilter();
+      supervised_user_service_->GetURLFilter();
   ASSERT_EQ(SupervisedUserURLFilter::BLOCK,
             filter->GetFilteringBehaviorForURL(test_url));
 
@@ -553,9 +549,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest, Unblock) {
-  LogInUser(LogInType::kChild);
-  BlockAllSites();
-
   GURL test_url("http://www.example.com/simple.html");
   ui_test_utils::NavigateToURL(browser(), test_url);
 
@@ -573,12 +566,12 @@
   dict->SetKey(test_url.host(), base::Value(true));
   SupervisedUserSettingsService* supervised_user_settings_service =
       SupervisedUserSettingsServiceFactory::GetForKey(
-          GetPrimaryUserProfile()->GetProfileKey());
+          browser()->profile()->GetProfileKey());
   supervised_user_settings_service->SetLocalSetting(
       supervised_users::kContentPackManualBehaviorHosts, std::move(dict));
 
   const SupervisedUserURLFilter* filter =
-      supervised_user_service()->GetURLFilter();
+      supervised_user_service_->GetURLFilter();
   EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
             filter->GetFilteringBehaviorForURL(test_url.GetWithEmptyPath()));
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 3ce01bd..a5bb1a9 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3549,6 +3549,8 @@
     if (is_chromeos) {
       sources += [
         "app_icon_loader_delegate.h",
+        "app_list/app_service_app_icon_loader.cc",
+        "app_list/app_service_app_icon_loader.h",
         "app_list/app_service_app_item.cc",
         "app_list/app_service_app_item.h",
         "app_list/app_service_app_model_builder.cc",
diff --git a/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.cc b/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.cc
index 27314495..7c03544 100644
--- a/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.cc
+++ b/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.cc
@@ -74,11 +74,13 @@
     const JavaParamRef<jstring>& cvc,
     const JavaParamRef<jstring>& month,
     const JavaParamRef<jstring>& year,
-    jboolean should_store_locally) {
+    jboolean should_store_locally,
+    jboolean enable_fido_auth) {
   controller_->OnUnmaskPromptAccepted(
       base::android::ConvertJavaStringToUTF16(env, cvc),
       base::android::ConvertJavaStringToUTF16(env, month),
-      base::android::ConvertJavaStringToUTF16(env, year), should_store_locally);
+      base::android::ConvertJavaStringToUTF16(env, year), should_store_locally,
+      enable_fido_auth);
 }
 
 void CardUnmaskPromptViewAndroid::OnNewCardLinkClicked(
diff --git a/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.h b/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.h
index 06cb929..509b3cf 100644
--- a/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.h
+++ b/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.h
@@ -35,7 +35,8 @@
                    const base::android::JavaParamRef<jstring>& cvc,
                    const base::android::JavaParamRef<jstring>& month,
                    const base::android::JavaParamRef<jstring>& year,
-                   jboolean should_store_locally);
+                   jboolean should_store_locally,
+                   jboolean enable_fido_auth);
   void OnNewCardLinkClicked(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj);
   int GetExpectedCvcLength(JNIEnv* env,
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index 43f71a6..3e3654c8 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -169,6 +169,8 @@
                              current_model_updater_->BadgedItemCount());
   }
   display_id_ = display_id;
+  if (search_controller_)
+    search_controller_->AppListShown();
 }
 
 void AppListClientImpl::ActivateItem(int profile_id,
diff --git a/chrome/browser/ui/app_list/app_service_app_icon_loader.cc b/chrome/browser/ui/app_list/app_service_app_icon_loader.cc
new file mode 100644
index 0000000..2317780
--- /dev/null
+++ b/chrome/browser/ui/app_list/app_service_app_icon_loader.cc
@@ -0,0 +1,120 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/app_service_app_icon_loader.h"
+
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
+#include "chrome/services/app_service/public/cpp/app_update.h"
+#include "chrome/services/app_service/public/mojom/types.mojom.h"
+
+AppServiceAppIconLoader::AppServiceAppIconLoader(
+    Profile* profile,
+    int resource_size_in_dip,
+    AppIconLoaderDelegate* delegate)
+    : AppIconLoader(profile, resource_size_in_dip, delegate) {
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile);
+  if (proxy) {
+    Observe(&proxy->AppRegistryCache());
+  }
+}
+
+AppServiceAppIconLoader::~AppServiceAppIconLoader() = default;
+
+bool AppServiceAppIconLoader::CanLoadImageForApp(const std::string& app_id) {
+  return true;
+}
+
+void AppServiceAppIconLoader::FetchImage(const std::string& app_id) {
+  AppIDToIconMap::const_iterator it = icon_map_.find(app_id);
+  if (it != icon_map_.end()) {
+    delegate()->OnAppImageUpdated(app_id, it->second);
+    return;
+  }
+
+  icon_map_[app_id] = gfx::ImageSkia();
+
+  constexpr bool allow_placeholder_icon = true;
+  CallLoadIcon(app_id, allow_placeholder_icon);
+}
+
+void AppServiceAppIconLoader::ClearImage(const std::string& app_id) {
+  icon_map_.erase(app_id);
+}
+
+void AppServiceAppIconLoader::UpdateImage(const std::string& app_id) {
+  AppIDToIconMap::const_iterator it = icon_map_.find(app_id);
+  if (it == icon_map_.end()) {
+    return;
+  }
+
+  delegate()->OnAppImageUpdated(app_id, it->second);
+}
+
+void AppServiceAppIconLoader::OnAppUpdate(const apps::AppUpdate& update) {
+  if (!update.IconKeyChanged()) {
+    return;
+  }
+
+  // Only load the icon that has been added to icon_map_.
+  const std::string& app_id = update.AppId();
+  AppIDToIconMap::const_iterator it = icon_map_.find(app_id);
+  if (it == icon_map_.end()) {
+    return;
+  }
+
+  constexpr bool allow_placeholder_icon = true;
+  CallLoadIcon(app_id, allow_placeholder_icon);
+}
+
+void AppServiceAppIconLoader::OnAppRegistryCacheWillBeDestroyed(
+    apps::AppRegistryCache* cache) {
+  Observe(nullptr);
+}
+
+void AppServiceAppIconLoader::CallLoadIcon(const std::string& app_id,
+                                           bool allow_placeholder_icon) {
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile());
+  if (!proxy) {
+    return;
+  }
+
+  apps::mojom::AppType app_type = proxy->AppRegistryCache().GetAppType(app_id);
+  if (app_type == apps::mojom::AppType::kUnknown) {
+    return;
+  }
+
+  proxy->LoadIcon(app_type, app_id, apps::mojom::IconCompression::kUncompressed,
+                  icon_size_in_dip(), allow_placeholder_icon,
+                  base::BindOnce(&AppServiceAppIconLoader::OnLoadIcon,
+                                 weak_ptr_factory_.GetWeakPtr(), app_id));
+}
+
+void AppServiceAppIconLoader::OnLoadIcon(const std::string& app_id,
+                                         apps::mojom::IconValuePtr icon_value) {
+  if (icon_value->icon_compression !=
+      apps::mojom::IconCompression::kUncompressed) {
+    return;
+  }
+
+  // Only load the icon that exists in icon_map_. The App could be removed from
+  // icon_map_ after calling CallLoadIcon, so check it again.
+  AppIDToIconMap::const_iterator it = icon_map_.find(app_id);
+  if (it == icon_map_.end()) {
+    return;
+  }
+
+  gfx::ImageSkia image = icon_value->uncompressed;
+  icon_map_[app_id] = image;
+  delegate()->OnAppImageUpdated(app_id, image);
+
+  if (icon_value->is_placeholder_icon) {
+    constexpr bool allow_placeholder_icon = false;
+    CallLoadIcon(app_id, allow_placeholder_icon);
+  }
+}
diff --git a/chrome/browser/ui/app_list/app_service_app_icon_loader.h b/chrome/browser/ui/app_list/app_service_app_icon_loader.h
new file mode 100644
index 0000000..e665a2e6
--- /dev/null
+++ b/chrome/browser/ui/app_list/app_service_app_icon_loader.h
@@ -0,0 +1,58 @@
+// 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 CHROME_BROWSER_UI_APP_LIST_APP_SERVICE_APP_ICON_LOADER_H_
+#define CHROME_BROWSER_UI_APP_LIST_APP_SERVICE_APP_ICON_LOADER_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/app_icon_loader.h"
+#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
+#include "chrome/services/app_service/public/cpp/app_update.h"
+#include "ui/gfx/image/image_skia.h"
+
+class Profile;
+
+// An AppIconLoader that loads icons for app service apps.
+class AppServiceAppIconLoader : public AppIconLoader,
+                                private apps::AppRegistryCache::Observer {
+ public:
+  AppServiceAppIconLoader(Profile* profile,
+                          int resource_size_in_dip,
+                          AppIconLoaderDelegate* delegate);
+  ~AppServiceAppIconLoader() override;
+
+  // AppIconLoader overrides:
+  bool CanLoadImageForApp(const std::string& app_id) override;
+  void FetchImage(const std::string& app_id) override;
+  void ClearImage(const std::string& app_id) override;
+  void UpdateImage(const std::string& app_id) override;
+
+  // apps::AppRegistryCache::Observer overrides:
+  void OnAppUpdate(const apps::AppUpdate& update) override;
+  void OnAppRegistryCacheWillBeDestroyed(
+      apps::AppRegistryCache* cache) override;
+
+ private:
+  // Calls AppService LoadIcon to load icons.
+  void CallLoadIcon(const std::string& app_id, bool allow_placeholder_icon);
+
+  // Callback invoked when the icon is loaded.
+  void OnLoadIcon(const std::string& app_id,
+                  apps::mojom::IconValuePtr icon_value);
+
+  using AppIDToIconMap = std::map<std::string, gfx::ImageSkia>;
+
+  // Maps from app id to the icon to track the icons added via FetchImage.
+  AppIDToIconMap icon_map_;
+
+  base::WeakPtrFactory<AppServiceAppIconLoader> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(AppServiceAppIconLoader);
+};
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_APP_SERVICE_APP_ICON_LOADER_H_
diff --git a/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc b/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
index c9fbc01..609e57f 100644
--- a/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
+++ b/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
@@ -20,6 +20,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/chromeos/extensions/default_web_app_ids.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -78,8 +79,8 @@
             /*show_in_launcher=*/false, InternalAppName::kContinueReading,
             /*searchable_string_resource_id=*/0},
 
-           {kReleaseNotesAppId, IDS_RELEASE_NOTES_NOTIFICATION_TITLE,
-            IDR_RELEASE_NOTES_APP_192,
+           {chromeos::default_web_apps::kReleaseNotesAppId,
+            IDS_RELEASE_NOTES_NOTIFICATION_TITLE, IDR_RELEASE_NOTES_APP_192,
             /*recommendable=*/true,
             /*searchable=*/false,
             /*show_in_launcher=*/false, InternalAppName::kReleaseNotes,
@@ -147,8 +148,9 @@
 
 bool IsSuggestionChip(const std::string& app_id) {
   // App IDs for internal apps which should only be shown as suggestion chips.
-  static const char* kSuggestionChipIds[] = {kInternalAppIdContinueReading,
-                                             kReleaseNotesAppId};
+  static const char* kSuggestionChipIds[] = {
+      kInternalAppIdContinueReading,
+      chromeos::default_web_apps::kReleaseNotesAppId};
 
   for (size_t i = 0; i < base::size(kSuggestionChipIds); ++i) {
     if (base::LowerCaseEqualsASCII(app_id, kSuggestionChipIds[i]))
@@ -231,7 +233,7 @@
     } else {
       plugin_vm::ShowPluginVmLauncherView(profile);
     }
-  } else if (app_id == kReleaseNotesAppId) {
+  } else if (app_id == chromeos::default_web_apps::kReleaseNotesAppId) {
     base::RecordAction(
         base::UserMetricsAction("ReleaseNotes.SuggestionChipLaunched"));
     chrome::LaunchReleaseNotes(profile);
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc
index f788bbd..99ab0b1 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/chromeos/extensions/default_web_app_ids.h"
 #include "chrome/browser/chromeos/extensions/gfx_utils.h"
 #include "chrome/browser/chromeos/release_notes/release_notes_storage.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -917,7 +918,7 @@
         title = navigation_title;
         app->AddSearchableText(title);
       }
-    } else if (app->id() == kReleaseNotesAppId) {
+    } else if (app->id() == chromeos::default_web_apps::kReleaseNotesAppId) {
       auto release_notes_storage =
           std::make_unique<chromeos::ReleaseNotesStorage>(profile_);
       if (!release_notes_storage->ShouldShowSuggestionChip())
diff --git a/chrome/browser/ui/app_list/search/drive_quick_access_provider.cc b/chrome/browser/ui/app_list/search/drive_quick_access_provider.cc
index 816ef8f..a59aee56 100644
--- a/chrome/browser/ui/app_list/search/drive_quick_access_provider.cc
+++ b/chrome/browser/ui/app_list/search/drive_quick_access_provider.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/app_list/search/drive_quick_access_provider.h"
 
 #include <memory>
+#include <utility>
 
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/ui/app_list/search/drive_quick_access_result.h"
@@ -19,18 +20,49 @@
 DriveQuickAccessProvider::DriveQuickAccessProvider(Profile* profile)
     : profile_(profile),
       drive_service_(
-          drive::DriveIntegrationServiceFactory::GetForProfile(profile)),
-      weak_factory_(this) {
+          drive::DriveIntegrationServiceFactory::GetForProfile(profile)) {
   DCHECK(profile_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Warm up the cache.
+  AppListShown();
 }
 
 DriveQuickAccessProvider::~DriveQuickAccessProvider() = default;
 
 void DriveQuickAccessProvider::Start(const base::string16& query) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO(crbug.com/959679): Add latency metrics.
   ClearResultsSilently();
-  if (!query.empty() || !drive_service_)
+  // Results are launched via DriveFS, so DriveFS must be mounted.
+  if (!query.empty() || !drive_service_ || !drive_service_->IsMounted())
     return;
+
+  // If there are no items in the cache, the previous call may have failed so
+  // retry. We return no results in this case, because waiting for the new
+  // results would introduce too much latency.
+  if (results_cache_.empty()) {
+    GetQuickAccessItems();
+    return;
+  }
+
+  SearchProvider::Results results;
+  for (const auto& result : results_cache_) {
+    results.emplace_back(std::make_unique<DriveQuickAccessResult>(
+        drive_service_->GetMountPointPath().Append(result.path),
+        result.confidence, profile_));
+  }
+  SwapResults(&results);
+}
+
+void DriveQuickAccessProvider::AppListShown() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!drive_service_)
+    return;
+  GetQuickAccessItems();
+}
+
+void DriveQuickAccessProvider::GetQuickAccessItems() {
   drive_service_->GetQuickAccessItems(
       kMaxItems,
       base::BindOnce(&DriveQuickAccessProvider::OnGetQuickAccessItems,
@@ -40,18 +72,12 @@
 void DriveQuickAccessProvider::OnGetQuickAccessItems(
     drive::FileError error,
     std::vector<drive::QuickAccessItem> drive_results) {
-  // Results are launched via DriveFS, so DriveFS must be mounted.
-  if (!drive_service_ || !drive_service_->IsMounted())
-    return;
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO(crbug.com/959679): Add score distribution metrics.
-  SearchProvider::Results results;
-  for (const auto& result : drive_results) {
-    results.emplace_back(std::make_unique<DriveQuickAccessResult>(
-        drive_service_->GetMountPointPath().Append(result.path),
-        result.confidence, profile_));
-  }
-  SwapResults(&results);
+  // An empty |drive_results| is likely caused by a failed call to ItemSuggest,
+  // so don't replace the cache.
+  if (!drive_results.empty())
+    results_cache_ = std::move(drive_results);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/drive_quick_access_provider.h b/chrome/browser/ui/app_list/search/drive_quick_access_provider.h
index b2f7b607..37cc394 100644
--- a/chrome/browser/ui/app_list/search/drive_quick_access_provider.h
+++ b/chrome/browser/ui/app_list/search/drive_quick_access_provider.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
@@ -26,15 +27,21 @@
 
   // SearchProvider:
   void Start(const base::string16& query) override;
+  void AppListShown() override;
 
  private:
+  void GetQuickAccessItems();
   void OnGetQuickAccessItems(drive::FileError error,
                              std::vector<drive::QuickAccessItem> drive_results);
 
   Profile* const profile_;
   drive::DriveIntegrationService* const drive_service_;
+  // Stores the last-returned results from the QuickAccess API.
+  std::vector<drive::QuickAccessItem> results_cache_;
 
-  base::WeakPtrFactory<DriveQuickAccessProvider> weak_factory_;
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<DriveQuickAccessProvider> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(DriveQuickAccessProvider);
 };
diff --git a/chrome/browser/ui/app_list/search/internal_app_result.cc b/chrome/browser/ui/app_list/search/internal_app_result.cc
index 21c199b..ad6ef7a6 100644
--- a/chrome/browser/ui/app_list/search/internal_app_result.cc
+++ b/chrome/browser/ui/app_list/search/internal_app_result.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "chrome/browser/chromeos/extensions/default_web_app_ids.h"
 #include "chrome/browser/chromeos/release_notes/release_notes_storage.h"
 #include "chrome/browser/favicon/large_icon_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -69,7 +70,7 @@
         ash::SearchResultDisplayLocation::kSuggestionChipContainer);
   }
 
-  if (id() == kReleaseNotesAppId) {
+  if (id() == chromeos::default_web_apps::kReleaseNotesAppId) {
     SetNotifyVisibilityChange(true);
     // Make sure that if both Continue Reading and Release Notes are available,
     // Release Notes shows up first in the suggestion chip container.
@@ -195,7 +196,7 @@
 }
 
 void InternalAppResult::OnVisibilityChanged(bool visibility) {
-  DCHECK_EQ(id(), kReleaseNotesAppId);
+  DCHECK_EQ(id(), chromeos::default_web_apps::kReleaseNotesAppId);
   DCHECK(chromeos::ReleaseNotesStorage(profile()).ShouldShowSuggestionChip());
   chromeos::ReleaseNotesStorage(profile())
       .DecreaseTimesLeftToShowSuggestionChip();
diff --git a/chrome/browser/ui/app_list/search/search_controller.cc b/chrome/browser/ui/app_list/search/search_controller.cc
index 5ab2cd6..026f640 100644
--- a/chrome/browser/ui/app_list/search/search_controller.cc
+++ b/chrome/browser/ui/app_list/search/search_controller.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h"
-#include "content/public/browser/system_connector.h"
 #include "third_party/metrics_proto/chrome_os_app_list_launch_event.pb.h"
 
 using metrics::ChromeOSAppListLaunchEventProto;
@@ -70,20 +69,24 @@
 SearchController::SearchController(AppListModelUpdater* model_updater,
                                    AppListControllerDelegate* list_controller,
                                    Profile* profile)
-    : mixer_(std::make_unique<Mixer>(model_updater)),
-      list_controller_(list_controller) {
+    : profile_(profile),
+      mixer_(std::make_unique<Mixer>(model_updater)),
+      list_controller_(list_controller) {}
+
+SearchController::~SearchController() {}
+
+void SearchController::InitializeRankers(
+    service_manager::Connector* connector) {
   std::unique_ptr<SearchResultRanker> ranker =
       std::make_unique<SearchResultRanker>(
-          profile,
+          profile_,
           HistoryServiceFactory::GetForProfile(
-              profile, ServiceAccessType::EXPLICIT_ACCESS),
-          content::GetSystemConnector());
+              profile_, ServiceAccessType::EXPLICIT_ACCESS),
+          connector);
   ranker->InitializeRankers();
   mixer_->SetNonAppSearchResultRanker(std::move(ranker));
 }
 
-SearchController::~SearchController() {}
-
 void SearchController::Start(const base::string16& query) {
   dispatching_query_ = true;
   RecordLauncherIssuedSearchQueryLength(query.length());
@@ -230,4 +233,9 @@
   mixer_->Train(app_launch_data);
 }
 
+void SearchController::AppListShown() {
+  for (const auto& provider : providers_)
+    provider->AppListShown();
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_controller.h b/chrome/browser/ui/app_list/search/search_controller.h
index 84ff9f35..9abca1f 100644
--- a/chrome/browser/ui/app_list/search/search_controller.h
+++ b/chrome/browser/ui/app_list/search/search_controller.h
@@ -21,6 +21,10 @@
 class ChromeSearchResult;
 class Profile;
 
+namespace service_manager {
+class Connector;
+}
+
 namespace app_list {
 
 class SearchResultRanker;
@@ -37,6 +41,8 @@
                    Profile* profile);
   virtual ~SearchController();
 
+  void InitializeRankers(service_manager::Connector* connector);
+
   void Start(const base::string16& query);
   void ViewClosing();
 
@@ -57,6 +63,9 @@
   // Sends training signal to each |providers_|
   void Train(AppLaunchData&& app_launch_data);
 
+  // Invoked when the app list is shown.
+  void AppListShown();
+
   // Gets the search result ranker owned by the Mixer that is used for all
   // other ranking.
   SearchResultRanker* GetNonAppSearchResultRanker();
@@ -75,6 +84,8 @@
   // Invoked when the search results are changed.
   void OnResultsChanged();
 
+  Profile* profile_;
+
   bool dispatching_query_ = false;
 
   // If true, the search results are shown on the launcher start page.
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index 762f1425..49fa6185f 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -26,12 +26,12 @@
 #include "chrome/browser/ui/app_list/search/mixer.h"
 #include "chrome/browser/ui/app_list/search/omnibox_provider.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
-#include "chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h"
 #include "chrome/browser/ui/app_list/search/settings_shortcut/settings_shortcut_provider.h"
 #include "chrome/browser/ui/app_list/search/zero_state_file_provider.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/arc/arc_util.h"
+#include "content/public/browser/system_connector.h"
 
 namespace app_list {
 
@@ -78,6 +78,9 @@
       std::make_unique<SearchController>(model_updater, list_controller,
                                          profile);
 
+  // Set up rankers for search results.
+  controller->InitializeRankers(content::GetSystemConnector());
+
   // Add mixer groups. There are four main groups: answer card, apps
   // and omnibox. Each group has a "soft" maximum number of results. However, if
   // a query turns up very few results, the mixer may take more than this
diff --git a/chrome/browser/ui/app_list/search/search_provider.h b/chrome/browser/ui/app_list/search/search_provider.h
index 8300ba5..5f5809d 100644
--- a/chrome/browser/ui/app_list/search/search_provider.h
+++ b/chrome/browser/ui/app_list/search/search_provider.h
@@ -35,6 +35,9 @@
   // training signals for results of any |RankingItemType|, so it is the
   // |SearchProvider|'s responsibility to check |type| and ignore if necessary.
   virtual void Train(const std::string& id, RankingItemType type) {}
+  // Invoked when the app list is shown. This can optionally be used by a
+  // provider to eg. warm up a cache of results.
+  virtual void AppListShown() {}
 
   void set_result_changed_callback(const ResultChangedCallback& callback) {
     result_changed_callback_ = callback;
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
index 1bea5cd..81401d40 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -311,6 +311,8 @@
   std::map<std::string, float> ranking_map;
   if (using_aggregated_app_inference_)
     ranking_map = app_launch_event_logger_->RetrieveRankings();
+  int aggregated_app_rank_success_count = 0;
+  int aggregated_app_rank_fail_count = 0;
 
   for (auto& result : *results) {
     const auto& type = RankingItemTypeFromSearchResult(*result.result);
@@ -362,6 +364,9 @@
         if (ranked != ranking_map.end()) {
           result.score =
               kBoostOfApps + ReRange((ranked->second + 4.0) / 8.0, 0.67, 1.0);
+          ++aggregated_app_rank_success_count;
+        } else {
+          ++aggregated_app_rank_fail_count;
         }
       } else {
         if (app_ranker_) {
@@ -373,6 +378,14 @@
       }
     }
   }
+  if (using_aggregated_app_inference_ &&
+      (aggregated_app_rank_success_count != 0 ||
+       aggregated_app_rank_fail_count != 0)) {
+    UMA_HISTOGRAM_COUNTS_1000("Apps.AppList.AggregatedMlAppRankSuccess",
+                              aggregated_app_rank_success_count);
+    UMA_HISTOGRAM_COUNTS_1000("Apps.AppList.AggregatedMlAppRankFail",
+                              aggregated_app_rank_fail_count);
+  }
 }
 
 void SearchResultRanker::Train(const AppLaunchData& app_launch_data) {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 57b4216..c446c0c 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -18,6 +18,7 @@
 #include "ash/public/cpp/window_animation_types.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/pattern.h"
 #include "base/strings/string_util.h"
@@ -32,6 +33,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
+#include "chrome/browser/ui/app_list/app_service_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.h"
@@ -1168,34 +1170,44 @@
     launcher_controller_helper_->set_profile(profile_);
   }
 
-  // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
-  // image is associated with a profile (its loader requires the profile).
-  // Since icon size changes are possible, the icon could be requested to be
-  // reloaded. However - having it not multi profile aware would cause problems
-  // if the icon cache gets deleted upon user switch.
-  std::unique_ptr<AppIconLoader> chrome_app_icon_loader =
-      std::make_unique<extensions::ChromeAppIconLoader>(
-          profile_, extension_misc::EXTENSION_ICON_MEDIUM,
-          base::BindRepeating(&app_list::MaybeResizeAndPadIconForMd), this);
-  app_icon_loaders_.push_back(std::move(chrome_app_icon_loader));
+  bool app_service_enabled =
+      base::FeatureList::IsEnabled(features::kAppServiceShelf);
 
-  if (arc::IsArcAllowedForProfile(profile_)) {
-    std::unique_ptr<AppIconLoader> arc_app_icon_loader =
-        std::make_unique<ArcAppIconLoader>(
+  if (app_service_enabled) {
+    std::unique_ptr<AppIconLoader> app_service_app_icon_loader =
+        std::make_unique<AppServiceAppIconLoader>(
             profile_, extension_misc::EXTENSION_ICON_MEDIUM, this);
-    app_icon_loaders_.push_back(std::move(arc_app_icon_loader));
-  }
+    app_icon_loaders_.push_back(std::move(app_service_app_icon_loader));
+  } else {
+    // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
+    // image is associated with a profile (its loader requires the profile).
+    // Since icon size changes are possible, the icon could be requested to be
+    // reloaded. However - having it not multi profile aware would cause
+    // problems if the icon cache gets deleted upon user switch.
+    std::unique_ptr<AppIconLoader> chrome_app_icon_loader =
+        std::make_unique<extensions::ChromeAppIconLoader>(
+            profile_, extension_misc::EXTENSION_ICON_MEDIUM,
+            base::BindRepeating(&app_list::MaybeResizeAndPadIconForMd), this);
+    app_icon_loaders_.push_back(std::move(chrome_app_icon_loader));
 
-  std::unique_ptr<AppIconLoader> internal_app_icon_loader =
-      std::make_unique<InternalAppIconLoader>(
-          profile_, extension_misc::EXTENSION_ICON_MEDIUM, this);
-  app_icon_loaders_.push_back(std::move(internal_app_icon_loader));
+    if (arc::IsArcAllowedForProfile(profile_)) {
+      std::unique_ptr<AppIconLoader> arc_app_icon_loader =
+          std::make_unique<ArcAppIconLoader>(
+              profile_, extension_misc::EXTENSION_ICON_MEDIUM, this);
+      app_icon_loaders_.push_back(std::move(arc_app_icon_loader));
+    }
 
-  if (crostini::IsCrostiniUIAllowedForProfile(profile_)) {
-    std::unique_ptr<AppIconLoader> crostini_app_icon_loader =
-        std::make_unique<CrostiniAppIconLoader>(
+    std::unique_ptr<AppIconLoader> internal_app_icon_loader =
+        std::make_unique<InternalAppIconLoader>(
             profile_, extension_misc::EXTENSION_ICON_MEDIUM, this);
-    app_icon_loaders_.push_back(std::move(crostini_app_icon_loader));
+    app_icon_loaders_.push_back(std::move(internal_app_icon_loader));
+
+    if (crostini::IsCrostiniUIAllowedForProfile(profile_)) {
+      std::unique_ptr<AppIconLoader> crostini_app_icon_loader =
+          std::make_unique<CrostiniAppIconLoader>(
+              profile_, extension_misc::EXTENSION_ICON_MEDIUM, this);
+      app_icon_loaders_.push_back(std::move(crostini_app_icon_loader));
+    }
   }
 
   pref_change_registrar_.Init(profile()->GetPrefs());
diff --git a/chrome/browser/ui/autofill/payments/card_unmask_prompt_view_browsertest.cc b/chrome/browser/ui/autofill/payments/card_unmask_prompt_view_browsertest.cc
index 44de4be5..68887d4 100644
--- a/chrome/browser/ui/autofill/payments/card_unmask_prompt_view_browsertest.cc
+++ b/chrome/browser/ui/autofill/payments/card_unmask_prompt_view_browsertest.cc
@@ -77,10 +77,11 @@
   void OnUnmaskPromptAccepted(const base::string16& cvc,
                               const base::string16& exp_month,
                               const base::string16& exp_year,
-                              bool should_store_pan) override {
+                              bool should_store_pan,
+                              bool enable_fido_auth) override {
     // Call the original implementation.
     CardUnmaskPromptControllerImpl::OnUnmaskPromptAccepted(
-        cvc, exp_month, exp_year, should_store_pan);
+        cvc, exp_month, exp_year, should_store_pan, enable_fido_auth);
 
     // Wait some time and show verification result. An empty message means
     // success is shown.
@@ -223,9 +224,10 @@
 IN_PROC_BROWSER_TEST_F(CardUnmaskPromptViewBrowserTest,
                        EarlyCloseAfterSuccess) {
   ShowUi(kExpiryExpired);
-  controller()->OnUnmaskPromptAccepted(base::ASCIIToUTF16("123"),
-                                       base::ASCIIToUTF16("10"),
-                                       base::ASCIIToUTF16("2020"), false);
+  controller()->OnUnmaskPromptAccepted(
+      base::ASCIIToUTF16("123"), base::ASCIIToUTF16("10"),
+      base::ASCIIToUTF16("2020"), /*should_store_locally=*/false,
+      /*enable_fido_auth=*/false);
   EXPECT_EQ(base::ASCIIToUTF16("123"), delegate()->details().cvc);
   controller()->OnVerificationResult(AutofillClient::SUCCESS);
 
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 5a6a15dc..3f47499 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -496,19 +496,10 @@
 
 void ShowAppManagementPage(Profile* profile, const std::string& app_id) {
   DCHECK(base::FeatureList::IsEnabled(features::kAppManagement));
-  constexpr char kAppManagementPagePrefix[] =
-      "chrome://app-management/detail?id=";
+  constexpr char kAppManagementSubPagePrefix[] = "app-management/detail?id=";
   base::RecordAction(base::UserMetricsAction("ShowAppManagementDetailPage"));
-  GURL url(kAppManagementPagePrefix + app_id);
-
-  Browser* browser = chrome::FindTabbedBrowser(profile, false);
-  if (!browser)
-    browser = new Browser(Browser::CreateParams(profile, true));
-
-  FocusWebContents(browser);
-  NavigateParams params(GetSingletonTabNavigateParams(browser, url));
-  params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
-  ShowSingletonTabOverwritingNTP(browser, std::move(params));
+  chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
+      profile, kAppManagementSubPagePrefix + app_id);
 }
 
 GURL GetOSSettingsUrl(const std::string& sub_page) {
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
index c7c0c21..530ad98 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
@@ -328,7 +328,8 @@
       year_input_->GetVisible()
           ? year_input_->GetTextForRow(year_input_->GetSelectedIndex())
           : base::string16(),
-      storage_checkbox_ ? storage_checkbox_->GetChecked() : false);
+      storage_checkbox_ ? storage_checkbox_->GetChecked() : false,
+      /*enable_fido_auth=*/false);
   return false;
 }
 
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index 8cc3756aa..5fea13d 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -888,6 +888,10 @@
   return base::UTF16ToUTF8(str16.c_str(), str16.size(), &unused_str8);
 }
 
+void AuthenticatorClientPinEntrySheetModel::OnBack() {
+  dialog_model()->StartOver();
+}
+
 void AuthenticatorClientPinEntrySheetModel::OnAccept() {
   // TODO(martinkr): use device::pin::kMinLength once landed.
   constexpr size_t kMinPinLength = 4;
@@ -963,6 +967,10 @@
   return PossibleResidentKeyWarning(dialog_model());
 }
 
+void AuthenticatorClientPinTapAgainSheetModel::OnBack() {
+  dialog_model()->StartOver();
+}
+
 // AuthenticatorGenericErrorSheetModel -----------------------------------
 
 // static
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 762ba13..f7a415c 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -412,6 +412,7 @@
   bool IsAcceptButtonVisible() const override;
   bool IsAcceptButtonEnabled() const override;
   base::string16 GetAcceptButtonLabel() const override;
+  void OnBack() override;
   void OnAccept() override;
 
   base::string16 pin_code_;
@@ -435,6 +436,7 @@
   base::string16 GetStepTitle() const override;
   base::string16 GetStepDescription() const override;
   base::Optional<base::string16> GetAdditionalDescription() const override;
+  void OnBack() override;
 };
 
 // Generic error dialog that can only be dismissed. Backwards navigation is
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc
index 7a95459..9314ef7 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc
@@ -25,6 +25,7 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/chromeos/devicetype_utils.h"
 
 namespace chromeos {
 
@@ -47,8 +48,6 @@
      IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_MULTIPLE_DEVICE_HEADER},
     {"startSetupPageSingleDeviceHeader",
      IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_SINGLE_DEVICE_HEADER},
-    {"startSetupPageFeatureListHeader",
-     IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FEATURE_LIST_HEADER},
     {"startSetupPageFeatureListInstallApps",
      IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_INSTALL_APPS_DESCRIPTION},
     {"startSetupPageFeatureListAddFeatures",
@@ -83,6 +82,7 @@
             "startSetupPageMessage",
             l10n_util::GetStringFUTF16(
                 IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_MESSAGE,
+                ui::GetChromeOSDeviceName(),
                 base::ASCIIToUTF16(kFootnoteMarker),
                 base::UTF8ToUTF16(
                     chromeos::multidevice_setup::
@@ -96,6 +96,12 @@
                 base::ASCIIToUTF16(kFootnoteMarker)));
 
         localized_strings.emplace_back(
+            "startSetupPageFeatureListHeader",
+            l10n_util::GetStringFUTF16(
+                IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FEATURE_LIST_HEADER,
+                ui::GetChromeOSDeviceName()));
+
+        localized_strings.emplace_back(
             "startSetupPageFeatureListAwm",
             l10n_util::GetStringFUTF16(
                 IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_AWM_DESCRIPTION,
@@ -128,11 +134,15 @@
   // localization calls separately.
   builder->AddF(
       "startSetupPageMessage", IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_MESSAGE,
-      base::ASCIIToUTF16(kFootnoteMarker),
+      ui::GetChromeOSDeviceName(), base::ASCIIToUTF16(kFootnoteMarker),
       base::UTF8ToUTF16(chromeos::multidevice_setup::
                             GetBoardSpecificBetterTogetherSuiteLearnMoreUrl()
                                 .spec()));
 
+  builder->AddF("startSetupPageFeatureListHeader",
+                IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FEATURE_LIST_HEADER,
+                ui::GetChromeOSDeviceName());
+
   builder->AddF("startSetupPageFootnote",
                 IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FOOTNOTE,
                 base::ASCIIToUTF16(kFootnoteMarker));
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler.cc b/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
index 680a2ae..8f725e8 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
@@ -539,6 +539,10 @@
           &SecurityKeysBioEnrollmentHandler::HandleStartEnrolling,
           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      "securityKeyBioEnrollDelete",
+      base::BindRepeating(&SecurityKeysBioEnrollmentHandler::HandleDelete,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollCancel",
       base::BindRepeating(&SecurityKeysBioEnrollmentHandler::HandleCancel,
                           base::Unretained(this)));
@@ -725,6 +729,34 @@
   ResolveJavascriptCallback(base::Value(std::move(callback_id_)), std::move(d));
 }
 
+void SecurityKeysBioEnrollmentHandler::HandleDelete(
+    const base::ListValue* args) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(2u, args->GetSize());
+  state_ = State::kDeleting;
+  callback_id_ = args->GetList()[0].GetString();
+  std::vector<uint8_t> template_id;
+  if (!base::HexStringToBytes(args->GetList()[1].GetString(), &template_id)) {
+    NOTREACHED();
+    return;
+  }
+  bio_->DeleteTemplate(
+      std::move(template_id),
+      base::BindOnce(&SecurityKeysBioEnrollmentHandler::OnDelete,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void SecurityKeysBioEnrollmentHandler::OnDelete(
+    device::CtapDeviceResponseCode c) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(state_, State::kDeleting);
+  DCHECK(!callback_id_.empty());
+  state_ = State::kEnumerating;
+  bio_->EnumerateTemplates(
+      base::BindOnce(&SecurityKeysBioEnrollmentHandler::OnHaveEnumeration,
+                     weak_factory_.GetWeakPtr()));
+}
+
 void SecurityKeysBioEnrollmentHandler::HandleCancel(
     const base::ListValue* args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler.h b/chrome/browser/ui/webui/settings/settings_security_key_handler.h
index 38026a9d..60196df0 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler.h
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler.h
@@ -166,6 +166,10 @@
   base::WeakPtrFactory<SecurityKeysCredentialHandler> weak_factory_{this};
 };
 
+// SecurityKeysBioEnrollmentHandler processes messages from the "Manage
+// fingerprints" dialog of the "Security Keys" settings subpage. An instance of
+// this class is created for each settings tab and is destroyed when the tab is
+// closed. See SecurityKeysBioEnrollProxy about the interface.
 class SecurityKeysBioEnrollmentHandler : public SecurityKeysHandlerBase {
  public:
   SecurityKeysBioEnrollmentHandler();
@@ -179,6 +183,7 @@
     kReady,
     kEnumerating,
     kEnrolling,
+    kDeleting,
     kCancelling,
   };
 
@@ -201,6 +206,9 @@
   void OnEnrollingResponse(device::BioEnrollmentSampleStatus, uint8_t);
   void OnEnrollmentFinished(device::CtapDeviceResponseCode);
 
+  void HandleDelete(const base::ListValue* args);
+  void OnDelete(device::CtapDeviceResponseCode);
+
   void HandleCancel(const base::ListValue* args);
   void OnEnrollCancel(device::CtapDeviceResponseCode);
 
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h
index 342d13b..c4af2c9 100644
--- a/chrome/browser/web_applications/components/app_registrar.h
+++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -90,6 +90,8 @@
   virtual const GURL& GetAppLaunchURL(const AppId& app_id) const = 0;
   virtual base::Optional<GURL> GetAppScope(const AppId& app_id) const = 0;
   virtual LaunchContainer GetAppLaunchContainer(const AppId& app_id) const = 0;
+  virtual void SetAppLaunchContainer(const AppId& app_id,
+                                     LaunchContainer launch_container) = 0;
 
   virtual std::vector<AppId> GetAppIds() const = 0;
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
index c1f5e82..a8fcb63 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
@@ -164,6 +164,29 @@
   }
 }
 
+void BookmarkAppRegistrar::SetAppLaunchContainer(
+    const web_app::AppId& app_id,
+    web_app::LaunchContainer launch_container) {
+  const Extension* extension = GetExtension(app_id);
+  DCHECK(extension);
+  if (!extension)
+    return;
+
+  switch (launch_container) {
+    case web_app::LaunchContainer::kWindow:
+      extensions::SetLaunchType(profile(), extension->id(),
+                                extensions::LAUNCH_TYPE_WINDOW);
+      return;
+    case web_app::LaunchContainer::kTab:
+      extensions::SetLaunchType(profile(), extension->id(),
+                                extensions::LAUNCH_TYPE_REGULAR);
+      return;
+    case web_app::LaunchContainer::kDefault:
+      NOTREACHED();
+      return;
+  }
+}
+
 std::vector<web_app::AppId> BookmarkAppRegistrar::GetAppIds() const {
   std::vector<web_app::AppId> app_ids;
   for (scoped_refptr<const Extension> app :
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
index 74d12f3..1217153a 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
@@ -42,6 +42,9 @@
   base::Optional<GURL> GetAppScope(const web_app::AppId& app_id) const override;
   web_app::LaunchContainer GetAppLaunchContainer(
       const web_app::AppId& app_id) const override;
+  void SetAppLaunchContainer(
+      const web_app::AppId& app_id,
+      web_app::LaunchContainer launch_container) override;
   std::vector<web_app::AppId> GetAppIds() const override;
 
   // ExtensionRegistryObserver:
diff --git a/chrome/browser/web_applications/test/test_app_registrar.cc b/chrome/browser/web_applications/test/test_app_registrar.cc
index e5432936..48b98c6 100644
--- a/chrome/browser/web_applications/test/test_app_registrar.cc
+++ b/chrome/browser/web_applications/test/test_app_registrar.cc
@@ -137,6 +137,11 @@
   return LaunchContainer::kTab;
 }
 
+void TestAppRegistrar::SetAppLaunchContainer(const AppId& app_id,
+                                             LaunchContainer launch_container) {
+  NOTIMPLEMENTED();
+}
+
 std::vector<AppId> TestAppRegistrar::GetAppIds() const {
   std::vector<AppId> result;
   for (const std::pair<AppId, AppInfo>& it : installed_apps_) {
diff --git a/chrome/browser/web_applications/test/test_app_registrar.h b/chrome/browser/web_applications/test/test_app_registrar.h
index 997de7d..56bc8f60 100644
--- a/chrome/browser/web_applications/test/test_app_registrar.h
+++ b/chrome/browser/web_applications/test/test_app_registrar.h
@@ -60,6 +60,8 @@
   base::Optional<GURL> GetAppScope(const AppId& app_id) const override;
   web_app::LaunchContainer GetAppLaunchContainer(
       const web_app::AppId& app_id) const override;
+  void SetAppLaunchContainer(const AppId& app_id,
+                             LaunchContainer launch_container) override;
   std::vector<AppId> GetAppIds() const override;
 
  private:
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index 5290d9bb..4fa2a3e 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -146,6 +146,11 @@
   return web_app ? web_app->launch_container() : LaunchContainer::kDefault;
 }
 
+void WebAppRegistrar::SetAppLaunchContainer(const AppId& app_id,
+                                            LaunchContainer launch_container) {
+  NOTIMPLEMENTED();
+}
+
 std::vector<AppId> WebAppRegistrar::GetAppIds() const {
   std::vector<AppId> app_ids;
   app_ids.reserve(registry_.size());
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h
index ac3ebfce..b7b7fc4 100644
--- a/chrome/browser/web_applications/web_app_registrar.h
+++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -50,6 +50,8 @@
   const GURL& GetAppLaunchURL(const AppId& app_id) const override;
   base::Optional<GURL> GetAppScope(const AppId& app_id) const override;
   LaunchContainer GetAppLaunchContainer(const AppId& app_id) const override;
+  void SetAppLaunchContainer(const AppId& app_id,
+                             LaunchContainer launch_container) override;
   std::vector<AppId> GetAppIds() const override;
 
   // Only range-based |for| loop supported. Don't use AppSet directly.
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index cbec662..d5cc6a0 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -677,7 +677,7 @@
 
 // Enables or disables the App Management UI.
 const base::Feature kAppManagement{"AppManagement",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Disable downloads of unsafe file types over insecure transports if initiated
 // from a secure page
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index ee69ca6..12caee0a 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1290,9 +1290,6 @@
 #endif  // !OS_CHROMEOS && !OS_ANDROID
 
 #if defined(OS_CHROMEOS)
-// List of external print servers configured by policy.
-const char kExternalPrintServers[] = "printing.external_print_servers";
-
 // List of printers configured by policy.
 const char kRecommendedNativePrinters[] =
     "native_printing.recommended_printers";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 5874c07..41e6bf5 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -415,7 +415,6 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-extern const char kExternalPrintServers[];
 extern const char kRecommendedNativePrinters[];
 extern const char kRecommendedNativePrintersAccessMode[];
 extern const char kRecommendedNativePrintersBlacklist[];
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 399fdd4..952dc56 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1507,17 +1507,16 @@
       metrics::CallStackProfileParams::SERVICE_WORKER_THREAD);
 }
 
-void ChromeContentRendererClient::
-    DidInitializeServiceWorkerContextOnWorkerThread(
-        blink::WebServiceWorkerContextProxy* context_proxy,
-        v8::Local<v8::Context> v8_context,
-        int64_t service_worker_version_id,
-        const GURL& service_worker_scope,
-        const GURL& script_url) {
+void ChromeContentRendererClient::WillEvaluateServiceWorkerOnWorkerThread(
+    blink::WebServiceWorkerContextProxy* context_proxy,
+    v8::Local<v8::Context> v8_context,
+    int64_t service_worker_version_id,
+    const GURL& service_worker_scope,
+    const GURL& script_url) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   ChromeExtensionsRendererClient::GetInstance()
       ->extension_dispatcher()
-      ->DidInitializeServiceWorkerContextOnWorkerThread(
+      ->WillEvaluateServiceWorkerOnWorkerThread(
           context_proxy, v8_context, service_worker_version_id,
           service_worker_scope, script_url);
 #endif
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 90e4d371..bb19b67 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -181,7 +181,7 @@
   void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame) override;
   void SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() override;
   void WillInitializeServiceWorkerContextOnWorkerThread() override;
-  void DidInitializeServiceWorkerContextOnWorkerThread(
+  void WillEvaluateServiceWorkerOnWorkerThread(
       blink::WebServiceWorkerContextProxy* context_proxy,
       v8::Local<v8::Context> v8_context,
       int64_t service_worker_version_id,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index dc16b2dd..cc44983 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2492,8 +2492,6 @@
         "../browser/supervised_user/logged_in_user_mixin.h",
         "../browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc",
         "../browser/supervised_user/supervised_user_service_browsertest.cc",
-        "../browser/supervised_user/supervised_user_test_base.cc",
-        "../browser/supervised_user/supervised_user_test_base.h",
         "../browser/supervised_user/supervised_user_url_filter_browsertest.cc",
       ]
     }
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 6ec296d..cc9fc59 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -329,6 +329,12 @@
 #endif
 }
 
+void InProcessBrowserTest::SelectFirstBrowser() {
+  const BrowserList* browser_list = BrowserList::GetInstance();
+  if (!browser_list->empty())
+    browser_ = browser_list->get(0);
+}
+
 void InProcessBrowserTest::CloseBrowserSynchronously(Browser* browser) {
   CloseBrowserAsynchronously(browser);
   ui_test_utils::WaitForBrowserToClose(browser);
@@ -450,12 +456,6 @@
 }
 #endif  // !defined(OS_MACOSX)
 
-void InProcessBrowserTest::SelectFirstBrowser() {
-  const BrowserList* browser_list = BrowserList::GetInstance();
-  if (!browser_list->empty())
-    browser_ = browser_list->get(0);
-}
-
 void InProcessBrowserTest::AddBlankTabAndShow(Browser* browser) {
   content::WindowedNotificationObserver observer(
       content::NOTIFICATION_LOAD_STOP,
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index 020d928f..dd88908 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -152,6 +152,12 @@
   // SelectFirstBrowser() is called.
   Browser* browser() const { return browser_; }
 
+  // Set |browser_| to the first browser on the browser list.
+  // Call this when your test subclass wants to access a non-null browser
+  // instance through browser() but browser creation is delayed until after
+  // PreRunTestOnMainThread().
+  void SelectFirstBrowser();
+
  protected:
   // Closes the given browser and waits for it to release all its resources.
   void CloseBrowserSynchronously(Browser* browser);
@@ -226,12 +232,6 @@
   // the browser.
   Browser* CreateBrowserForApp(const std::string& app_name, Profile* profile);
 
-  // Set |browser_| to the first browser on the browser list.
-  // Call this when your test subclass wants to access a non-null browser
-  // instance through browser() but browser creation is delayed until after
-  // PreRunTestOnMainThread().
-  void SelectFirstBrowser();
-
   // Called from the various CreateBrowser methods to add a blank tab, wait for
   // the navigation to complete, and show the browser's window.
   void AddBlankTabAndShow(Browser* browser);
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 81f97ac..4ba8666 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -404,7 +404,13 @@
   },
 
   "ExternalPrintServers": {
-    "note": "This policy will be added in R-78"
+    "os": ["chromeos"],
+    "test_policy": {
+      "ExternalPrintServers": {
+        "url": "https://example.com/policyfile",
+        "hash": "deadbeefdeadbeefdeadbeef"
+      }
+    }
   },
 
   "NativePrinters": {
diff --git a/chrome/test/data/webui/settings/chromeos/app_management/managed_apps_test.js b/chrome/test/data/webui/settings/chromeos/app_management/managed_apps_test.js
index 4ce6338..93c93b22 100644
--- a/chrome/test/data/webui/settings/chromeos/app_management/managed_apps_test.js
+++ b/chrome/test/data/webui/settings/chromeos/app_management/managed_apps_test.js
@@ -42,7 +42,8 @@
     await test_util.flushTasks();
   });
 
-  test('Uninstall button affected by policy', () => {
+  // TODO(crbug.com/999412): rewrite test.
+  test.skip('Uninstall button affected by policy', () => {
     const uninstallWrapper =
         appDetailView.$$('app-management-permission-view-header')
             .$$('#uninstall-wrapper');
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 5a61f3e..f7b93fd 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -20,7 +20,7 @@
 GEN('#define MAYBE_AllJsTests AllJsTests');
 GEN('#endif');
 
-// Generic text fixture for CrOS Polymer Settings elements to be overridden by
+// Generic test fixture for CrOS Polymer Settings elements to be overridden by
 // individual element tests.
 const OSSettingsBrowserTest = class extends PolymerTest {
   /** @override */
@@ -143,12 +143,19 @@
   mocha.run();
 });
 
-// Test fixture for the app management settings page.
-// eslint-disable-next-line no-var
-var OSSettingsAppManagementPageTest = class extends OSSettingsBrowserTest {
+// Generic test fixture for CrOS Polymer App Management elements to be
+// overridden by individual element tests.
+const OSSettingsAppManagementBrowserTest = class extends OSSettingsBrowserTest {
   /** @override */
   get browsePreload() {
-    return super.browsePreload + 'app_management/app_management_page.html';
+    return super.browsePreload + 'os_apps_page.html';
+  }
+
+  /** @override */
+  get featureList() {
+    return {
+      enabled: super.featureList.enabled.concat(['features::kAppManagement'])
+    };
   }
 
   /** @override */
@@ -158,25 +165,120 @@
       BROWSER_SETTINGS_PATH + '../test_store.js',
       'app_management/test_util.js',
       'app_management/test_store.js',
-      'app_management/app_management_page_tests.js',
     ]);
   }
 
   /** @override */
-  get featureList() {
-    return {enabled: ['features::kAppManagement']};
-  }
-
-  /** @override */
   get runAccessibilityChecks() {
     return true;
   }
 };
 
+// Test fixture for the app management settings page.
+// eslint-disable-next-line no-var
+var OSSettingsAppManagementPageTest =
+    class extends OSSettingsAppManagementBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'app_management/app_management_page.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      'app_management/app_management_page_tests.js',
+    ]);
+  }
+};
+
 TEST_F('OSSettingsAppManagementPageTest', 'AllJsTests', () => {
   mocha.run();
 });
 
+// Test fixture for the app management pwa permission view element.
+// eslint-disable-next-line no-var
+var OSSettingsAppManagementPwaPermissionViewTest =
+    class extends OSSettingsAppManagementBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'app_management/pwa_permission_view.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      'app_management/pwa_permission_view_test.js',
+    ]);
+  }
+};
+
+TEST_F(
+    'OSSettingsAppManagementPwaPermissionViewTest', 'DISABLED_AllJsTests',
+    () => {
+      mocha.run();
+    });
+
+// Test fixture for the app management arc permission view element.
+// eslint-disable-next-line no-var
+var OSSettingsAppManagementArcPermissionViewTest =
+    class extends OSSettingsAppManagementBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'app_management/arc_permission_view.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      'app_management/arc_permission_view_test.js',
+    ]);
+  }
+};
+
+TEST_F(
+    'OSSettingsAppManagementArcPermissionViewTest', 'DISABLED_AllJsTests',
+    () => {
+      mocha.run();
+    });
+
+// Test fixture for the app management managed app view.
+// eslint-disable-next-line no-var
+var OSSettingsAppManagementManagedAppTest =
+    class extends OSSettingsAppManagementBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'app_management/pwa_permission_view.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      'app_management/managed_apps_test.js',
+    ]);
+  }
+};
+
+TEST_F('OSSettingsAppManagementManagedAppTest', 'AllJsTests', () => {
+  mocha.run();
+});
+
+
+// Test fixture for the app management reducers.
+// eslint-disable-next-line no-var
+var OSSettingsAppManagementReducersTest =
+    class extends OSSettingsAppManagementBrowserTest {
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      'app_management/reducers_test.js',
+    ]);
+  }
+};
+
+TEST_F('OSSettingsAppManagementReducersTest', 'DISABLED_AllJsTests', () => {
+  mocha.run();
+});
+
 // Tests for the Device page.
 // eslint-disable-next-line no-var
 var OSSettingsBluetoothPageTest = class extends OSSettingsBrowserTest {
diff --git a/chrome/test/data/webui/settings/security_keys_subpage_test.js b/chrome/test/data/webui/settings/security_keys_subpage_test.js
index 4edb111..951171a 100644
--- a/chrome/test/data/webui/settings/security_keys_subpage_test.js
+++ b/chrome/test/data/webui/settings/security_keys_subpage_test.js
@@ -147,6 +147,7 @@
       'enumerateEnrollments',
       'startEnrolling',
       'cancelEnrollment',
+      'deleteEnrollment',
       'close',
     ]);
   }
@@ -177,6 +178,11 @@
   }
 
   /** @override */
+  deleteEnrollment(id) {
+    return this.handleMethod('deleteEnrollment', id);
+  }
+
+  /** @override */
   close() {
     this.methodCalled('close');
   }
@@ -717,6 +723,8 @@
     const enumerateResolver = new PromiseResolver();
     browserProxy.setResponseFor(
         'enumerateEnrollments', enumerateResolver.promise);
+    const deleteResolver = new PromiseResolver();
+    browserProxy.setResponseFor('deleteEnrollment', deleteResolver.promise);
 
     document.body.appendChild(dialog);
     await browserProxy.whenCalled('startBioEnroll');
@@ -738,7 +746,7 @@
     await browserProxy.whenCalled('enumerateEnrollments');
     uiReady =
         test_util.eventToPromise('bio-enroll-dialog-ready-for-testing', dialog);
-    const enrollments = [
+    let enrollments = [
       {
         name: 'Fingerprint00',
         id: '0000',
@@ -756,6 +764,19 @@
     await uiReady;
     assertShown(allDivs, dialog, 'enrollments');
     assertEquals(dialog.$.enrollmentList.items, enrollments);
+
+    // Delete the second enrollments and refresh the list.
+    Polymer.flush();
+    Polymer.dom(dialog.$.enrollmentList)
+        .querySelectorAll('cr-icon-button')[1]
+        .click();
+    const id = await browserProxy.whenCalled('deleteEnrollment');
+    assertEquals(enrollments[1].id, id);
+    enrollments.splice(1, 1);
+    deleteResolver.resolve(enrollments);
+    await uiReady;
+    assertShown(allDivs, dialog, 'enrollments');
+    assertEquals(dialog.$.enrollmentList.items, enrollments);
   });
 
   test('AddEnrollment', async function() {
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.js b/chrome/test/data/webui/tab_strip/tab_list_test.js
index 3027dca3..f078382 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.js
@@ -237,4 +237,17 @@
     assertEquals('none', window.getComputedStyle(ghostPinnedTabs[1]).display);
     assertEquals('none', window.getComputedStyle(ghostPinnedTabs[2]).display);
   });
+
+  test('moves tab elements when tabs move', () => {
+    const tabElementsBeforeMove = getUnpinnedTabs();
+    const tabToMove = currentWindow.tabs[0];
+    callbackRouter.onMoved.dispatchEvent(tabToMove.id, {
+      toIndex: 2,
+      windowId: currentWindow.id,
+    });
+    const tabElementsAfterMove = getUnpinnedTabs();
+    assertEquals(tabElementsBeforeMove[0], tabElementsAfterMove[2]);
+    assertEquals(tabElementsBeforeMove[1], tabElementsAfterMove[0]);
+    assertEquals(tabElementsBeforeMove[2], tabElementsAfterMove[1]);
+  });
 });
diff --git a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
index 98f0acd..843c95d 100644
--- a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
+++ b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
@@ -31,6 +31,7 @@
     this.callbackRouter = {
       onActivated: new EventDispatcher(),
       onCreated: new EventDispatcher(),
+      onMoved: new EventDispatcher(),
       onRemoved: new EventDispatcher(),
       onUpdated: new EventDispatcher(),
     };
diff --git a/chromecast/media/BUILD.gn b/chromecast/media/BUILD.gn
index ed37c1e2..ec0444e6 100644
--- a/chromecast/media/BUILD.gn
+++ b/chromecast/media/BUILD.gn
@@ -64,6 +64,7 @@
     "//chromecast/base/metrics:test_support",
     "//chromecast/common/mojom",
     "//chromecast/media/audio/capture_service:unittests",
+    "//chromecast/media/base:monotonic_clock",
     "//chromecast/media/cma:test_support",
     "//chromecast/media/cma:unittests",
     "//chromecast/public",
diff --git a/chromecast/media/cma/backend/alsa/alsa_volume_control.cc b/chromecast/media/cma/backend/alsa/alsa_volume_control.cc
index 100079a7..7546967d 100644
--- a/chromecast/media/cma/backend/alsa/alsa_volume_control.cc
+++ b/chromecast/media/cma/backend/alsa/alsa_volume_control.cc
@@ -335,6 +335,8 @@
   for (const auto& amp_mixer : amp_mixers_) {
     if (!SetElementMuted(amp_mixer.get(), power_save_on)) {
       LOG(INFO) << "Amp toggle failed: no amp switch on mixer element.";
+    } else {
+      LOG(INFO) << "Set Power Save to: " << power_save_on;
     }
   }
 }
diff --git a/chromeos/components/power/dark_resume_controller.cc b/chromeos/components/power/dark_resume_controller.cc
index 12d8060..4bb485d 100644
--- a/chromeos/components/power/dark_resume_controller.cc
+++ b/chromeos/components/power/dark_resume_controller.cc
@@ -5,6 +5,7 @@
 
 #include <utility>
 
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/device/public/mojom/wake_lock_provider.mojom.h"
 
@@ -26,7 +27,6 @@
 DarkResumeController::DarkResumeController(
     service_manager::Connector* connector)
     : connector_(connector),
-      wake_lock_observer_binding_(this),
       dark_resume_hard_timeout_(kDefaultDarkResumeHardTimeout) {
   DCHECK(!dark_resume_hard_timeout_.is_zero());
   connector_->BindInterface(device::mojom::kServiceName,
@@ -81,7 +81,7 @@
 }
 
 bool DarkResumeController::IsDarkResumeStateSetForTesting() const {
-  return block_suspend_token_ && wake_lock_observer_binding_.is_bound();
+  return block_suspend_token_ && wake_lock_observer_receiver_.is_bound();
 }
 
 base::TimeDelta DarkResumeController::GetHardTimeoutForTesting() const {
@@ -92,7 +92,7 @@
   return !weak_ptr_factory_.HasWeakPtrs() &&
          !wake_lock_check_timer_.IsRunning() &&
          !hard_timeout_timer_.IsRunning() && !block_suspend_token_ &&
-         !wake_lock_observer_binding_.is_bound();
+         !wake_lock_observer_receiver_.is_bound();
 }
 
 void DarkResumeController::HandleDarkResumeWakeLockCheckTimeout() {
@@ -103,10 +103,9 @@
   // this calls back immediately, else whenever the wake lock is deactivated.
   // The device will be suspended on a deactivation notification i.e. in
   // OnDeactivation.
-  device::mojom::WakeLockObserverPtr observer;
-  wake_lock_observer_binding_.Bind(mojo::MakeRequest(&observer));
   wake_lock_provider_->NotifyOnWakeLockDeactivation(
-      device::mojom::WakeLockType::kPreventAppSuspension, std::move(observer));
+      device::mojom::WakeLockType::kPreventAppSuspension,
+      wake_lock_observer_receiver_.BindNewPipeAndPassRemote());
 
   // Schedule task that will tell the power daemon to re-suspend after a dark
   // resume irrespective of any state. This is a last resort timeout to ensure
@@ -137,7 +136,7 @@
 
   // This automatically invalidates any WakeLockObserver and associated callback
   // in this case OnDeactivation.
-  wake_lock_observer_binding_.Close();
+  wake_lock_observer_receiver_.reset();
 
   // Stops timer and invalidates HandleDarkResumeWakeLockCheckTimeout.
   wake_lock_check_timer_.Stop();
diff --git a/chromeos/components/power/dark_resume_controller.h b/chromeos/components/power/dark_resume_controller.h
index 94d8aad..1989fd4 100644
--- a/chromeos/components/power/dark_resume_controller.h
+++ b/chromeos/components/power/dark_resume_controller.h
@@ -13,7 +13,8 @@
 #include "base/timer/timer.h"
 #include "base/unguessable_token.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/wake_lock.mojom.h"
 #include "services/device/public/mojom/wake_lock_provider.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -102,8 +103,9 @@
   // after a dark resume.
   base::UnguessableToken block_suspend_token_;
 
-  // The binding used to implement device::mojom::WakeLockObserver.
-  mojo::Binding<device::mojom::WakeLockObserver> wake_lock_observer_binding_;
+  // The receiver used to implement device::mojom::WakeLockObserver.
+  mojo::Receiver<device::mojom::WakeLockObserver> wake_lock_observer_receiver_{
+      this};
 
   // Timer used to schedule HandleDarkResumeWakeLockCheckTimeout.
   base::OneShotTimer wake_lock_check_timer_;
diff --git a/chromeos/dbus/concierge_client.cc b/chromeos/dbus/concierge_client.cc
index e0d4a2ef..bc6e65f 100644
--- a/chromeos/dbus/concierge_client.cc
+++ b/chromeos/dbus/concierge_client.cc
@@ -18,6 +18,8 @@
 
 namespace chromeos {
 
+using namespace vm_tools::concierge;
+
 class ConciergeClientImpl : public ConciergeClient {
  public:
   ConciergeClientImpl() {}
@@ -52,44 +54,14 @@
       const vm_tools::concierge::CreateDiskImageRequest& request,
       DBusMethodCallback<vm_tools::concierge::CreateDiskImageResponse> callback)
       override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kCreateDiskImageMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode CreateDiskImageRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::CreateDiskImageResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kCreateDiskImageMethod, request, std::move(callback));
   }
 
   void DestroyDiskImage(
       const vm_tools::concierge::DestroyDiskImageRequest& request,
       DBusMethodCallback<vm_tools::concierge::DestroyDiskImageResponse>
           callback) override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kDestroyDiskImageMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode DestroyDiskImageRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::DestroyDiskImageResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kDestroyDiskImageMethod, request, std::move(callback));
   }
 
   void ImportDiskImage(
@@ -121,131 +93,41 @@
       const vm_tools::concierge::CancelDiskImageRequest& request,
       DBusMethodCallback<vm_tools::concierge::CancelDiskImageResponse> callback)
       override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kCancelDiskImageMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode CancelDiskImageRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::CancelDiskImageResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kCancelDiskImageMethod, request, std::move(callback));
   }
 
   void DiskImageStatus(
       const vm_tools::concierge::DiskImageStatusRequest& request,
       DBusMethodCallback<vm_tools::concierge::DiskImageStatusResponse> callback)
       override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kDiskImageStatusMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode DiskImageStatusRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::DiskImageStatusResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kDiskImageStatusMethod, request, std::move(callback));
   }
 
   void ListVmDisks(const vm_tools::concierge::ListVmDisksRequest& request,
                    DBusMethodCallback<vm_tools::concierge::ListVmDisksResponse>
                        callback) override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kListVmDisksMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode ListVmDisksRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::ListVmDisksResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kListVmDisksMethod, request, std::move(callback));
   }
 
   void StartTerminaVm(const vm_tools::concierge::StartVmRequest& request,
                       DBusMethodCallback<vm_tools::concierge::StartVmResponse>
                           callback) override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kStartVmMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode StartVmRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
     // TODO(nverne): revert to TIMEOUT_USE_DEFAULT when StartVm no longer
     // requires unnecessary long running crypto calculations.
     constexpr int kStartVmTimeoutMs = 160 * 1000;
-    concierge_proxy_->CallMethod(
-        &method_call, kStartVmTimeoutMs,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::StartVmResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kStartVmMethod, request, std::move(callback), kStartVmTimeoutMs);
   }
 
   void StopVm(const vm_tools::concierge::StopVmRequest& request,
               DBusMethodCallback<vm_tools::concierge::StopVmResponse> callback)
       override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kStopVmMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode StopVmRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::StopVmResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kStopVmMethod, request, std::move(callback));
   }
 
   void GetVmInfo(const vm_tools::concierge::GetVmInfoRequest& request,
                  DBusMethodCallback<vm_tools::concierge::GetVmInfoResponse>
                      callback) override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kGetVmInfoMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode GetVmInfoRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::GetVmInfoResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kGetVmInfoMethod, request, std::move(callback));
   }
 
   void GetVmEnterpriseReportingInfo(
@@ -253,48 +135,15 @@
       DBusMethodCallback<
           vm_tools::concierge::GetVmEnterpriseReportingInfoResponse> callback)
       override {
-    dbus::MethodCall method_call(
-        vm_tools::concierge::kVmConciergeInterface,
-        vm_tools::concierge::kGetVmEnterpriseReportingInfoMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR)
-          << "Failed to encode GetVmEnterpriseReportingInfoRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(
-            &ConciergeClientImpl::OnDBusProtoResponse<
-                vm_tools::concierge::GetVmEnterpriseReportingInfoResponse>,
-            weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kGetVmEnterpriseReportingInfoMethod, request,
+               std::move(callback));
   }
 
   void SetVmCpuRestriction(
       const vm_tools::concierge::SetVmCpuRestrictionRequest& request,
       DBusMethodCallback<vm_tools::concierge::SetVmCpuRestrictionResponse>
           callback) override {
-    dbus::MethodCall method_call(
-        vm_tools::concierge::kVmConciergeInterface,
-        vm_tools::concierge::kSetVmCpuRestrictionMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode SetVmCpuRestrictionRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::SetVmCpuRestrictionResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kSetVmCpuRestrictionMethod, request, std::move(callback));
   }
 
   void WaitForServiceToBeAvailable(
@@ -307,112 +156,35 @@
       const vm_tools::concierge::ContainerSshKeysRequest& request,
       DBusMethodCallback<vm_tools::concierge::ContainerSshKeysResponse>
           callback) override {
-    dbus::MethodCall method_call(
-        vm_tools::concierge::kVmConciergeInterface,
-        vm_tools::concierge::kGetContainerSshKeysMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode ContainerSshKeysRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::ContainerSshKeysResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kGetContainerSshKeysMethod, request, std::move(callback));
   }
 
-  void AttachUsbDevice(base::ScopedFD fd,
+  void AttachUsbDevice(
+      base::ScopedFD fd,
       const vm_tools::concierge::AttachUsbDeviceRequest& request,
       DBusMethodCallback<vm_tools::concierge::AttachUsbDeviceResponse> callback)
       override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kAttachUsbDeviceMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode AttachUsbDeviceRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    writer.AppendFileDescriptor(fd.get());
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::AttachUsbDeviceResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kAttachUsbDeviceMethod, request, std::move(callback));
   }
 
   void DetachUsbDevice(
       const vm_tools::concierge::DetachUsbDeviceRequest& request,
       DBusMethodCallback<vm_tools::concierge::DetachUsbDeviceResponse> callback)
       override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kDetachUsbDeviceMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode DetachUsbDeviceRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::DetachUsbDeviceResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kDetachUsbDeviceMethod, request, std::move(callback));
   }
 
   void ListUsbDevices(
       const vm_tools::concierge::ListUsbDeviceRequest& request,
       DBusMethodCallback<vm_tools::concierge::ListUsbDeviceResponse> callback)
       override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kListUsbDeviceMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode ListUsbDeviceRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::ListUsbDeviceResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kListUsbDeviceMethod, request, std::move(callback));
   }
 
   void StartArcVm(const vm_tools::concierge::StartArcVmRequest& request,
                   DBusMethodCallback<vm_tools::concierge::StartVmResponse>
                       callback) override {
-    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
-                                 vm_tools::concierge::kStartArcVmMethod);
-    dbus::MessageWriter writer(&method_call);
-
-    if (!writer.AppendProtoAsArrayOfBytes(request)) {
-      LOG(ERROR) << "Failed to encode StartArcVmRequest protobuf";
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
-      return;
-    }
-
-    concierge_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
-                           vm_tools::concierge::StartVmResponse>,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    CallMethod(kStartArcVmMethod, request, std::move(callback));
   }
 
  protected:
@@ -442,6 +214,27 @@
   }
 
  private:
+  template <typename RequestProto, typename ResponseProto>
+  void CallMethod(const std::string& method_name,
+                  const RequestProto& request,
+                  DBusMethodCallback<ResponseProto> callback,
+                  int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) {
+    dbus::MethodCall method_call(kVmConciergeInterface, method_name);
+    dbus::MessageWriter writer(&method_call);
+
+    if (!writer.AppendProtoAsArrayOfBytes(request)) {
+      LOG(ERROR) << "Failed to encode protobuf for " << method_name;
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+      return;
+    }
+
+    concierge_proxy_->CallMethod(
+        &method_call, timeout_ms,
+        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<ResponseProto>,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   template <typename ResponseProto>
   void OnDBusProtoResponse(DBusMethodCallback<ResponseProto> callback,
                            dbus::Response* dbus_response) {
diff --git a/components/arc/arc_features.cc b/components/arc/arc_features.cc
index 719b98f23..7f85d33 100644
--- a/components/arc/arc_features.cc
+++ b/components/arc/arc_features.cc
@@ -73,7 +73,7 @@
 // When enabled, chrome://settings and Files.app will ask if the user wants
 // to expose USB storage devices to ARC.
 const base::Feature kUsbStorageUIFeature{"ArcUsbStorageUI",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls ARC VPN integration.
 // When enabled, Chrome traffic will be routed through VPNs connected in
diff --git a/components/autofill/core/browser/payments/card_unmask_delegate.h b/components/autofill/core/browser/payments/card_unmask_delegate.h
index 6378f620..c28a28b 100644
--- a/components/autofill/core/browser/payments/card_unmask_delegate.h
+++ b/components/autofill/core/browser/payments/card_unmask_delegate.h
@@ -29,6 +29,9 @@
 
     // State of "copy to this device" checkbox.
     bool should_store_pan;
+
+    // User is opting-in for FIDO Authentication for future card unmasking.
+    bool enable_fido_auth = false;
   };
 
   // Called when the user has attempted a verification. Prompt is still
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 9b62f37..a63d89d 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -294,23 +294,25 @@
 #endif
 
 void CreditCardAccessManager::OnCVCAuthenticationComplete(
-    bool did_succeed,
-    const CreditCard* card,
-    const base::string16& cvc,
-    base::Value creation_options) {
+    const CreditCardCVCAuthenticator::CVCAuthenticationResponse& response) {
   is_authentication_in_progress_ = false;
-  accessor_->OnCreditCardFetched(did_succeed, card, cvc);
+  accessor_->OnCreditCardFetched(response.did_succeed, response.card,
+                                 response.cvc);
 
-  if (!did_succeed)
+  if (!response.did_succeed)
     return;
 
 #if defined(OS_ANDROID)
   // Now that unmask flow is complete, on Android, if GetRealPan includes
   // |creation_options|, completely hand over registration flow to
   // CreditCardFIDOAuthenticator.
-  if (creation_options.is_dict())
-    GetOrCreateFIDOAuthenticator()->Register(std::move(creation_options));
-#elif !defined(OS_IOS)  // CreditCardFIDOAuthenticator does not exist on iOS.
+  if (response.creation_options.has_value()) {
+    DCHECK(response.creation_options->is_dict());
+    GetOrCreateFIDOAuthenticator()->Register(
+        response.creation_options->Clone());
+  }
+#elif !defined(OS_IOS)
+  // CreditCardFIDOAuthenticator does not exist on iOS.
   // On desktop, prompts dialog to show the authentication offer.
   if (unmask_details_.offer_fido_opt_in)
     GetOrCreateFIDOAuthenticator()->ShowWebauthnOfferDialog();
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.h b/components/autofill/core/browser/payments/credit_card_access_manager.h
index 1c109544..0ac65d28 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.h
@@ -135,10 +135,8 @@
 
   // CreditCardCVCAuthenticator::Requester:
   void OnCVCAuthenticationComplete(
-      bool did_succeed,
-      const CreditCard* card = nullptr,
-      const base::string16& cvc = base::string16(),
-      base::Value creation_options = base::Value()) override;
+      const CreditCardCVCAuthenticator::CVCAuthenticationResponse& response)
+      override;
 
 #if !defined(OS_IOS)
   // CreditCardFIDOAuthenticator::Requester:
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index eac457d..d0f7c7dd 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -230,10 +230,10 @@
     if (fido_opt_in) {
       response.fido_creation_options =
           base::Value(base::Value::Type::DICTIONARY);
-      response.fido_creation_options.SetKey("relying_party_id",
-                                            base::Value(kGooglePaymentsRpid));
-      response.fido_creation_options.SetKey("challenge",
-                                            base::Value(kTestChallenge));
+      response.fido_creation_options->SetKey("relying_party_id",
+                                             base::Value(kGooglePaymentsRpid));
+      response.fido_creation_options->SetKey("challenge",
+                                             base::Value(kTestChallenge));
     }
 #endif
     full_card_request->OnDidGetRealPan(result,
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
index 913250c..7a021d8 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
@@ -11,6 +11,11 @@
 
 namespace autofill {
 
+CreditCardCVCAuthenticator::CVCAuthenticationResponse::
+    CVCAuthenticationResponse() {}
+CreditCardCVCAuthenticator::CVCAuthenticationResponse::
+    ~CVCAuthenticationResponse() {}
+
 CreditCardCVCAuthenticator::CreditCardCVCAuthenticator(AutofillClient* client)
     : client_(client) {}
 
@@ -37,12 +42,18 @@
     const CreditCard& card,
     const base::string16& cvc) {
   requester_->OnCVCAuthenticationComplete(
-      /*did_succeed=*/true, &card, cvc,
-      full_card_request.GetFIDOCreationOptions());
+      CVCAuthenticationResponse()
+          .with_did_succeed(true)
+          .with_card(&card)
+          .with_cvc(cvc)
+          .with_creation_options(
+              std::move(full_card_request.unmask_response_details()
+                            .fido_creation_options)));
 }
 
 void CreditCardCVCAuthenticator::OnFullCardRequestFailed() {
-  requester_->OnCVCAuthenticationComplete(/*did_succeed=*/false);
+  requester_->OnCVCAuthenticationComplete(
+      CVCAuthenticationResponse().with_did_succeed(false));
 }
 
 void CreditCardCVCAuthenticator::ShowUnmaskPrompt(
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
index 9882bbc..05ad5b7f 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
@@ -20,14 +20,38 @@
     : public payments::FullCardRequest::ResultDelegate,
       public payments::FullCardRequest::UIDelegate {
  public:
+  struct CVCAuthenticationResponse {
+    CVCAuthenticationResponse();
+    ~CVCAuthenticationResponse();
+
+    CVCAuthenticationResponse& with_did_succeed(bool b) {
+      did_succeed = b;
+      return *this;
+    }
+    // Data pointed to by |c| must outlive this object.
+    CVCAuthenticationResponse& with_card(const CreditCard* c) {
+      card = c;
+      return *this;
+    }
+    CVCAuthenticationResponse& with_cvc(const base::string16 s) {
+      cvc = base::string16(s);
+      return *this;
+    }
+    CVCAuthenticationResponse& with_creation_options(
+        base::Optional<base::Value> v) {
+      creation_options = std::move(v);
+      return *this;
+    }
+    bool did_succeed = false;
+    const CreditCard* card = nullptr;
+    base::string16 cvc = base::string16();
+    base::Optional<base::Value> creation_options = base::nullopt;
+  };
   class Requester {
    public:
     virtual ~Requester() {}
     virtual void OnCVCAuthenticationComplete(
-        bool did_succeed,
-        const CreditCard* card = nullptr,
-        const base::string16& cvc = base::string16(),
-        base::Value creation_options = base::Value()) = 0;
+        const CVCAuthenticationResponse& response) = 0;
   };
   explicit CreditCardCVCAuthenticator(AutofillClient* client);
   ~CreditCardCVCAuthenticator() override;
diff --git a/components/autofill/core/browser/payments/full_card_request.cc b/components/autofill/core/browser/payments/full_card_request.cc
index a70dcae..2d8be66 100644
--- a/components/autofill/core/browser/payments/full_card_request.cc
+++ b/components/autofill/core/browser/payments/full_card_request.cc
@@ -200,11 +200,10 @@
         personal_data_manager_->UpdateServerCreditCard(request_->card);
 
       // TODO(crbug/949269): Once |fido_opt_in| is added to
-      // UserProvidedUnmaskDetails, add a check here that
-      // |user_response.fido_opt_in| is true before copying over
-      // |creation_options|.
-      if (response_details.fido_creation_options.is_dict())
-        fido_creation_options_ = response_details.fido_creation_options.Clone();
+      // UserProvidedUnmaskDetails, clear out |creation_options| from
+      // |response_details_| if |user_response.fido_opt_in| was not set to true
+      // to avoid an unwanted registration prompt.
+      unmask_response_details_ = response_details;
       if (result_delegate_)
         result_delegate_->OnFullCardRequestSucceeded(
             *this, request_->card, request_->user_response.cvc);
@@ -218,10 +217,6 @@
   }
 }
 
-base::Value FullCardRequest::GetFIDOCreationOptions() const {
-  return fido_creation_options_.Clone();
-}
-
 void FullCardRequest::Reset() {
   weak_ptr_factory_.InvalidateWeakPtrs();
   payments_client_->CancelRequest();
@@ -229,7 +224,7 @@
   ui_delegate_ = nullptr;
   request_.reset();
   should_unmask_card_ = false;
-  fido_creation_options_ = base::Value();
+  unmask_response_details_ = payments::PaymentsClient::UnmaskResponseDetails();
 }
 
 }  // namespace payments
diff --git a/components/autofill/core/browser/payments/full_card_request.h b/components/autofill/core/browser/payments/full_card_request.h
index 1d68ef03..9d5338b 100644
--- a/components/autofill/core/browser/payments/full_card_request.h
+++ b/components/autofill/core/browser/payments/full_card_request.h
@@ -93,8 +93,10 @@
       AutofillClient::PaymentsRpcResult result,
       payments::PaymentsClient::UnmaskResponseDetails& response_details);
 
-  // Returns a copy of |fido_creation_options_|.
-  base::Value GetFIDOCreationOptions() const;
+  payments::PaymentsClient::UnmaskResponseDetails unmask_response_details()
+      const {
+    return unmask_response_details_;
+  }
 
   base::TimeTicks form_parsed_timestamp() const {
     return form_parsed_timestamp_;
@@ -161,9 +163,8 @@
   // The timestamp when the form is parsed. For histograms.
   base::TimeTicks form_parsed_timestamp_;
 
-  // Includes a challenge for enrolling user into FIDO Authentication for card
-  // unmasking.
-  base::Value fido_creation_options_;
+  // Includes all details from GetRealPan response.
+  payments::PaymentsClient::UnmaskResponseDetails unmask_response_details_;
 
   // Enables destroying FullCardRequest while CVC prompt is showing or a server
   // communication is pending.
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index acac10c..b097a05 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -376,6 +376,10 @@
     if (base::StringToInt(request_details_.user_response.exp_year, &value))
       request_dict.SetKey("expiration_year", base::Value(value));
 
+    request_dict.SetKey(
+        "opt_in_fido_auth",
+        base::Value(request_details_.user_response.enable_fido_auth));
+
     if (request_details_.fido_assertion_info.is_dict()) {
       request_dict.SetKey("fido_assertion_info",
                           std::move(request_details_.fido_assertion_info));
@@ -953,10 +957,19 @@
 PaymentsClient::UnmaskResponseDetails::UnmaskResponseDetails() {}
 PaymentsClient::UnmaskResponseDetails::UnmaskResponseDetails(
     const UnmaskResponseDetails& other) {
-  real_pan = other.real_pan;
-  fido_creation_options = other.fido_creation_options.Clone();
+  *this = other;
 }
 PaymentsClient::UnmaskResponseDetails::~UnmaskResponseDetails() {}
+PaymentsClient::UnmaskResponseDetails& PaymentsClient::UnmaskResponseDetails::
+operator=(const PaymentsClient::UnmaskResponseDetails& other) {
+  real_pan = other.real_pan;
+  if (other.fido_creation_options.has_value()) {
+    fido_creation_options = other.fido_creation_options->Clone();
+  } else {
+    fido_creation_options.reset();
+  }
+  return *this;
+}
 
 PaymentsClient::OptChangeRequestDetails::OptChangeRequestDetails() {}
 PaymentsClient::OptChangeRequestDetails::OptChangeRequestDetails(
diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h
index b1d22647..07523979 100644
--- a/components/autofill/core/browser/payments/payments_client.h
+++ b/components/autofill/core/browser/payments/payments_client.h
@@ -99,6 +99,7 @@
     UnmaskResponseDetails();
     UnmaskResponseDetails(const UnmaskResponseDetails& other);
     ~UnmaskResponseDetails();
+    UnmaskResponseDetails& operator=(const UnmaskResponseDetails& other);
 
     UnmaskResponseDetails& with_real_pan(std::string r) {
       real_pan = r;
@@ -106,7 +107,7 @@
     }
 
     std::string real_pan;
-    base::Value fido_creation_options;
+    base::Optional<base::Value> fido_creation_options;
   };
 
   // Information required to either opt-in or opt-out a user for FIDO
diff --git a/components/autofill/core/browser/payments/payments_client_unittest.cc b/components/autofill/core/browser/payments/payments_client_unittest.cc
index 92b2992..39f0e81 100644
--- a/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -434,7 +434,7 @@
   EXPECT_EQ(AutofillClient::SUCCESS, result_);
   EXPECT_EQ("1234", unmask_response_details_->real_pan);
   EXPECT_EQ("google.com",
-            *unmask_response_details_->fido_creation_options.FindStringKey(
+            *unmask_response_details_->fido_creation_options->FindStringKey(
                 "relying_party_id"));
 }
 
diff --git a/components/autofill/core/browser/payments/test_authentication_requester.cc b/components/autofill/core/browser/payments/test_authentication_requester.cc
index e7b966cb..276ae586 100644
--- a/components/autofill/core/browser/payments/test_authentication_requester.cc
+++ b/components/autofill/core/browser/payments/test_authentication_requester.cc
@@ -19,14 +19,11 @@
 }
 
 void TestAuthenticationRequester::OnCVCAuthenticationComplete(
-    bool did_succeed,
-    const CreditCard* card,
-    const base::string16& cvc,
-    base::Value creation_options) {
-  did_succeed_ = did_succeed;
+    const CreditCardCVCAuthenticator::CVCAuthenticationResponse& response) {
+  did_succeed_ = response.did_succeed;
   if (did_succeed_) {
-    DCHECK(card);
-    number_ = card->number();
+    DCHECK(response.card);
+    number_ = response.card->number();
   }
 }
 
diff --git a/components/autofill/core/browser/payments/test_authentication_requester.h b/components/autofill/core/browser/payments/test_authentication_requester.h
index 717920f4c..d2f692b8 100644
--- a/components/autofill/core/browser/payments/test_authentication_requester.h
+++ b/components/autofill/core/browser/payments/test_authentication_requester.h
@@ -37,10 +37,8 @@
 
   // CreditCardCVCAuthenticator::Requester:
   void OnCVCAuthenticationComplete(
-      bool did_succeed,
-      const CreditCard* card = nullptr,
-      const base::string16& cvc = base::string16(),
-      base::Value creation_options = base::Value()) override;
+      const CreditCardCVCAuthenticator::CVCAuthenticationResponse& response)
+      override;
 
 #if !defined(OS_IOS)
   // CreditCardFIDOAuthenticator::Requester:
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h
index ba8f500..8a33e40f 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h
@@ -21,7 +21,8 @@
   virtual void OnUnmaskPromptAccepted(const base::string16& cvc,
                                       const base::string16& exp_month,
                                       const base::string16& exp_year,
-                                      bool should_store_pan) = 0;
+                                      bool should_store_pan,
+                                      bool enable_fido_auth) = 0;
   virtual void NewCardLinkClicked() = 0;
 
   // State.
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
index 03a3e72..f4918d2 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
@@ -110,7 +110,8 @@
     const base::string16& cvc,
     const base::string16& exp_month,
     const base::string16& exp_year,
-    bool should_store_pan) {
+    bool should_store_pan,
+    bool enable_fido_auth) {
   verify_timestamp_ = AutofillClock::Now();
   unmasking_number_of_attempts_++;
   unmasking_result_ = AutofillClient::NONE;
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h
index 0716c8b1..dd174d2 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h
@@ -38,7 +38,8 @@
   void OnUnmaskPromptAccepted(const base::string16& cvc,
                               const base::string16& exp_month,
                               const base::string16& exp_year,
-                              bool should_store_pan) override;
+                              bool should_store_pan,
+                              bool enable_fido_auth) override;
   void NewCardLinkClicked() override;
   base::string16 GetWindowTitle() const override;
   base::string16 GetInstructionsMessage() const override;
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc
index e6f15e5..418ac6c 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc
@@ -108,7 +108,8 @@
   void ShowPromptAndSimulateResponse(bool should_store_pan) {
     ShowPrompt();
     controller_->OnUnmaskPromptAccepted(ASCIIToUTF16("444"), ASCIIToUTF16("01"),
-                                        ASCIIToUTF16("2050"), should_store_pan);
+                                        ASCIIToUTF16("2050"), should_store_pan,
+                                        /*enable_fido_auth=*/false);
     EXPECT_EQ(should_store_pan,
               pref_service_->GetBoolean(
                   prefs::kAutofillWalletImportStorageCheckboxState));
@@ -168,7 +169,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogClosedAbandonUnmasking) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnUnmaskDialogClosed();
@@ -179,7 +180,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogClosedFailedToUnmaskRetriable) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE);
   base::HistogramTester histogram_tester;
 
@@ -197,7 +198,7 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest,
        LogClosedFailedToUnmaskNonRetriable) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   controller_->OnVerificationResult(AutofillClient::PERMANENT_FAILURE);
   base::HistogramTester histogram_tester;
 
@@ -215,7 +216,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskedCardFirstAttempt) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnVerificationResult(AutofillClient::SUCCESS);
@@ -231,11 +232,12 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskedCardAfterFailure) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE);
   controller_->OnUnmaskPromptAccepted(ASCIIToUTF16("444"), ASCIIToUTF16("01"),
                                       ASCIIToUTF16("2050"),
-                                      false /* should_store_pan */);
+                                      /*should_store_pan=*/false,
+                                      /*enable_fido_auth=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnVerificationResult(AutofillClient::SUCCESS);
@@ -247,7 +249,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogSavedCardLocally) {
-  ShowPromptAndSimulateResponse(true);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/true);
   base::HistogramTester histogram_tester;
 
   controller_->OnVerificationResult(AutofillClient::SUCCESS);
@@ -260,7 +262,7 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogDidOptIn) {
   SetImportCheckboxState(false);
-  ShowPromptAndSimulateResponse(true);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/true);
   base::HistogramTester histogram_tester;
   controller_->OnUnmaskDialogClosed();
 
@@ -271,7 +273,7 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogDidNotOptIn) {
   SetImportCheckboxState(false);
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
   controller_->OnUnmaskDialogClosed();
 
@@ -282,7 +284,7 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogDidOptOut) {
   SetImportCheckboxState(true);
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
   controller_->OnUnmaskDialogClosed();
 
@@ -293,7 +295,7 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogDidNotOptOut) {
   SetImportCheckboxState(true);
-  ShowPromptAndSimulateResponse(true);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/true);
   base::HistogramTester histogram_tester;
   controller_->OnUnmaskDialogClosed();
 
@@ -304,7 +306,7 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest, DontLogForHiddenCheckbox) {
   controller_->set_can_store_locally(false);
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
   controller_->OnUnmaskDialogClosed();
 
@@ -334,7 +336,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogDurationAbandonUnmasking) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnUnmaskDialogClosed();
@@ -345,7 +347,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogDurationFailedToUnmaskRetriable) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE);
   base::HistogramTester histogram_tester;
 
@@ -358,7 +360,7 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest,
        LogDurationFailedToUnmaskNonRetriable) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   controller_->OnVerificationResult(AutofillClient::PERMANENT_FAILURE);
   base::HistogramTester histogram_tester;
 
@@ -370,7 +372,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogDurationCardFirstAttempt) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnVerificationResult(AutofillClient::SUCCESS);
@@ -383,11 +385,12 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest,
        LogDurationUnmaskedCardAfterFailure) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE);
   controller_->OnUnmaskPromptAccepted(
       base::ASCIIToUTF16("444"), base::ASCIIToUTF16("01"),
-      base::ASCIIToUTF16("2050"), false /* should_store_pan */);
+      base::ASCIIToUTF16("2050"), /*should_store_pan=*/false,
+      /*enable_fido_auth=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnVerificationResult(AutofillClient::SUCCESS);
@@ -399,7 +402,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogTimeBeforeAbandonUnmasking) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnUnmaskDialogClosed();
@@ -409,7 +412,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanResultSuccess) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
   controller_->OnVerificationResult(AutofillClient::SUCCESS);
 
@@ -419,7 +422,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanTryAgainFailure) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE);
@@ -430,7 +433,7 @@
 }
 
 TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskingDurationResultSuccess) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnVerificationResult(AutofillClient::SUCCESS);
@@ -443,7 +446,7 @@
 
 TEST_F(CardUnmaskPromptControllerImplTest,
        LogUnmaskingDurationTryAgainFailure) {
-  ShowPromptAndSimulateResponse(false);
+  ShowPromptAndSimulateResponse(/*should_store_pan=*/false);
   base::HistogramTester histogram_tester;
 
   controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE);
@@ -488,9 +491,9 @@
   if (!cvc_case.valid)
     return;
 
-  controller_->OnUnmaskPromptAccepted(ASCIIToUTF16(cvc_case.input),
-                                      ASCIIToUTF16("1"), ASCIIToUTF16("2050"),
-                                      false);
+  controller_->OnUnmaskPromptAccepted(
+      ASCIIToUTF16(cvc_case.input), ASCIIToUTF16("1"), ASCIIToUTF16("2050"),
+      /*should_store_pan=*/false, /*enable_fido_auth=*/false);
   EXPECT_EQ(ASCIIToUTF16(cvc_case.canonicalized_input),
             delegate_->details().cvc);
 }
@@ -530,9 +533,9 @@
   if (!cvc_case_amex.valid)
     return;
 
-  controller_->OnUnmaskPromptAccepted(ASCIIToUTF16(cvc_case_amex.input),
-                                      base::string16(), base::string16(),
-                                      false);
+  controller_->OnUnmaskPromptAccepted(
+      ASCIIToUTF16(cvc_case_amex.input), base::string16(), base::string16(),
+      /*should_store_pan=*/false, /*enable_fido_auth=*/false);
   EXPECT_EQ(ASCIIToUTF16(cvc_case_amex.canonicalized_input),
             delegate_->details().cvc);
 }
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index b180566..b993cfb 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -397,6 +397,11 @@
       </message>
     </if>
   </if>
+  <if expr="is_android">
+    <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ENABLE_FIDO_AUTH_CHECKBOX" desc="Text for checkbox in card unmasking dialog that allows the use of fingerprint to unmask cards starting next time. If checked, then a fingerprint prompt will immediately follow." formatter_data="android_java">
+      Use fingerprint to verify this card next time.
+    </message>
+  </if>
   <message name="IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON" desc="Text for button that confirms the credit card CVC entry dialog.">
     Confirm
   </message>
diff --git a/components/login/localized_values_builder.cc b/components/login/localized_values_builder.cc
index 2107b10..82f5602 100644
--- a/components/login/localized_values_builder.cc
+++ b/components/login/localized_values_builder.cc
@@ -47,6 +47,15 @@
 
 void LocalizedValuesBuilder::AddF(const std::string& key,
                                   int message_id,
+                                  const base::string16& a,
+                                  const base::string16& b,
+                                  const base::string16& c) {
+  dict_->SetString(prefix_ + key,
+                   l10n_util::GetStringFUTF16(message_id, a, b, c));
+}
+
+void LocalizedValuesBuilder::AddF(const std::string& key,
+                                  int message_id,
                                   int message_id_a) {
   AddF(key, message_id, l10n_util::GetStringUTF16(message_id_a));
 }
@@ -59,4 +68,14 @@
        l10n_util::GetStringUTF16(message_id_b));
 }
 
+void LocalizedValuesBuilder::AddF(const std::string& key,
+                                  int message_id,
+                                  int message_id_a,
+                                  int message_id_b,
+                                  int message_id_c) {
+  AddF(key, message_id, l10n_util::GetStringUTF16(message_id_a),
+       l10n_util::GetStringUTF16(message_id_b),
+       l10n_util::GetStringUTF16(message_id_c));
+}
+
 }  // namespace login
diff --git a/components/login/localized_values_builder.h b/components/login/localized_values_builder.h
index 8c1731c..a0b6392ea 100644
--- a/components/login/localized_values_builder.h
+++ b/components/login/localized_values_builder.h
@@ -51,6 +51,15 @@
 
   // Method to declare localized value. |key| is the i18n key used in html.
   // |message_id| is a resource id of message. Message is expected to have
+  // two format parameters subsituted by |a|, |b| and |c| respectively.
+  void AddF(const std::string& key,
+            int message_id,
+            const base::string16& a,
+            const base::string16& b,
+            const base::string16& c);
+
+  // Method to declare localized value. |key| is the i18n key used in html.
+  // |message_id| is a resource id of message. Message is expected to have
   // one format parameter subsituted by resource identified by |message_id_a|.
   void AddF(const std::string& key, int message_id, int message_id_a);
 
@@ -63,6 +72,16 @@
             int message_id_a,
             int message_id_b);
 
+  // Method to declare localized value. |key| is the i18n key used in html.
+  // |message_id| is a resource id of message. Message is expected to have
+  // three format parameters subsituted by resource identified by
+  // |message_id_a|, |message_id_b| and |message_id_c| respectively.
+  void AddF(const std::string& key,
+            int message_id,
+            int message_id_a,
+            int message_id_b,
+            int message_id_c);
+
  private:
   std::string prefix_;
 
diff --git a/components/ntp_snippets/features.cc b/components/ntp_snippets/features.cc
index 2b7f345..72fca90 100644
--- a/components/ntp_snippets/features.cc
+++ b/components/ntp_snippets/features.cc
@@ -72,7 +72,7 @@
     "KeepPrefetchedContentSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kOptionalImagesEnabledFeature{
-    "NTPRemoteSuggestionsOptionalImages", base::FEATURE_DISABLED_BY_DEFAULT};
+    "NTPRemoteSuggestionsOptionalImages", base::FEATURE_ENABLED_BY_DEFAULT};
 
 std::vector<const base::Feature*> GetAllFeatures() {
   // Skip the last feature as it's a nullptr.
diff --git a/components/payments/content/payment_handler_host.cc b/components/payments/content/payment_handler_host.cc
index aad54bf..fdb0a82c 100644
--- a/components/payments/content/payment_handler_host.cc
+++ b/components/payments/content/payment_handler_host.cc
@@ -40,23 +40,23 @@
 
 PaymentHandlerHost::PaymentHandlerHost(content::WebContents* web_contents,
                                        Delegate* delegate)
-    : binding_(this), web_contents_(web_contents), delegate_(delegate) {
+    : web_contents_(web_contents), delegate_(delegate) {
   DCHECK(web_contents_);
   DCHECK(delegate_);
 }
 
 PaymentHandlerHost::~PaymentHandlerHost() {}
 
-mojom::PaymentHandlerHostPtrInfo PaymentHandlerHost::Bind() {
-  mojom::PaymentHandlerHostPtrInfo host_ptr_info;
-  binding_.Close();
-  binding_.Bind(mojo::MakeRequest(&host_ptr_info));
+mojo::PendingRemote<mojom::PaymentHandlerHost> PaymentHandlerHost::Bind() {
+  receiver_.reset();
+  mojo::PendingRemote<mojom::PaymentHandlerHost> host =
+      receiver_.BindNewPipeAndPassRemote();
 
   // Connection error handler can be set only after the Bind() call.
-  binding_.set_connection_error_handler(base::BindOnce(
+  receiver_.set_disconnect_handler(base::BindOnce(
       &PaymentHandlerHost::Disconnect, weak_ptr_factory_.GetWeakPtr()));
 
-  return host_ptr_info;
+  return host;
 }
 
 void PaymentHandlerHost::UpdateWith(
@@ -115,7 +115,7 @@
 }
 
 void PaymentHandlerHost::Disconnect() {
-  binding_.Close();
+  receiver_.reset();
 }
 
 base::WeakPtr<PaymentHandlerHost> PaymentHandlerHost::AsWeakPtr() {
diff --git a/components/payments/content/payment_handler_host.h b/components/payments/content/payment_handler_host.h
index 00897e73..427a18b 100644
--- a/components/payments/content/payment_handler_host.h
+++ b/components/payments/content/payment_handler_host.h
@@ -11,7 +11,8 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "third_party/blink/public/mojom/payments/payment_handler_host.mojom.h"
 #include "url/origin.h"
 
@@ -69,7 +70,7 @@
   }
 
   // Binds to an IPC endpoint and returns it.
-  mojom::PaymentHandlerHostPtrInfo Bind();
+  mojo::PendingRemote<mojom::PaymentHandlerHost> Bind();
 
   // Notifies the payment handler of the updated details, such as updated total,
   // in response to the change of the payment method.
@@ -97,7 +98,7 @@
 
   // The end-point for the payment handler renderer process to call into the
   // browser process.
-  mojo::Binding<mojom::PaymentHandlerHost> binding_;
+  mojo::Receiver<mojom::PaymentHandlerHost> receiver_{this};
 
   // The merchant page that invoked the Payment Request API.
   content::WebContents* web_contents_;
diff --git a/components/payments/content/service_worker_payment_instrument.h b/components/payments/content/service_worker_payment_instrument.h
index bcc05d0..d288913 100644
--- a/components/payments/content/service_worker_payment_instrument.h
+++ b/components/payments/content/service_worker_payment_instrument.h
@@ -12,6 +12,7 @@
 #include "components/payments/content/web_app_manifest.h"
 #include "components/payments/core/payment_instrument.h"
 #include "content/public/browser/stored_payment_app.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/payments/payment_app.mojom.h"
 #include "third_party/blink/public/mojom/payments/payment_handler_host.mojom.h"
 #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
@@ -110,7 +111,7 @@
   gfx::ImageSkia icon_image_skia() const override;
 
   void set_payment_handler_host(
-      mojom::PaymentHandlerHostPtrInfo payment_handler_host) {
+      mojo::PendingRemote<mojom::PaymentHandlerHost> payment_handler_host) {
     payment_handler_host_ = std::move(payment_handler_host);
   }
 
@@ -143,7 +144,7 @@
   // the service worker is installed.
   base::WeakPtr<IdentityObserver> identity_observer_;
 
-  mojom::PaymentHandlerHostPtrInfo payment_handler_host_;
+  mojo::PendingRemote<mojom::PaymentHandlerHost> payment_handler_host_;
 
   // PaymentAppProvider::CanMakePayment result of this payment instrument.
   bool can_make_payment_result_;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index f1f4ca5..4e1a418 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -17107,7 +17107,7 @@
     {
       'name': 'ExternalPrintServers',
       'owners': ['file://chromeos/printing/OWNERS'],
-      'type': 'dict',
+      'type': 'external',
       'schema': {
         'type': 'object',
         'properties': {
@@ -17123,7 +17123,6 @@
       },
       'id': 572,
       'supported_on': ['chrome_os:78-'],
-      'future': True,
       'features': {
         'dynamic_refresh': True,
         'per_profile': True,
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker.cc b/components/url_formatter/spoof_checks/idn_spoof_checker.cc
index 087bf88..6394179 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker.cc
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker.cc
@@ -188,7 +188,7 @@
   //      U+04C8 (ӈ), U+04CA (ӊ), U+050B (ԋ), U+0527 (ԧ), U+0529 (ԩ)} => h
   //   - {U+0138 (ĸ), U+03BA (κ), U+043A (к), U+049B (қ), U+049D (ҝ),
   //      U+049F (ҟ), U+04A1(ҡ), U+04C4 (ӄ), U+051F (ԟ)} => k
-  //   - {U+014B (ŋ), U+043F (п), U+0525 (ԥ), U+0E01 (ก)} => n
+  //   - {U+014B (ŋ), U+043F (п), U+0525 (ԥ), U+0E01 (ก), U+05D7 (ח)} => n
   //   - U+0153 (œ) => "ce"
   //     TODO: see https://crbug.com/843352 for further work on
   //     U+0525 and U+0153.
@@ -229,7 +229,7 @@
       UNICODE_STRING_SIMPLE("ExtraConf"),
       icu::UnicodeString::fromUTF8(
           "[æӕ] > ae; [þϼҏ] > p; [ħнћңҥӈӊԋԧԩ] > h;"
-          "[ĸκкқҝҟҡӄԟ] > k; [ŋпԥก] > n; œ > ce;"
+          "[ĸκкқҝҟҡӄԟ] > k; [ŋпԥกח] > n; œ > ce;"
           "[ŧтҭԏ七丅丆] > t; [ƅьҍв] > b;  [ωшщพฟພຟ] > w;"
           "[мӎ] > m; [єҽҿၔ] > e; ґ > r; [ғӻ] > f;"
           "[ҫင] > c; [ұ丫] > y; [χҳӽӿ乂] > x;"
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
index eba64f8..e6494ea 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
@@ -564,9 +564,7 @@
     {"xn--s5a8h3a.com", L"\x04cf\x050d\x0503.com", false},
 
     // 1շ34567890.com
-    {"xn--134567890-gnk.com",
-     L"1շ34567890.com",
-     false},
+    {"xn--134567890-gnk.com", L"1շ34567890.com", false},
     // ꓲ2345б7890.com
     {"xn--23457890-e7g93622b.com",
      L"\xa4f2"
@@ -625,9 +623,7 @@
      L"4567890.com",
      false},
     // 123ㄐ567890.com
-    {"xn--123567890-dr5h.com",
-     L"123ㄐ567890.com",
-     false},
+    {"xn--123567890-dr5h.com", L"123ㄐ567890.com", false},
     // 123Ꮞ567890.com
     {"xn--123567890-dm4b.com",
      L"123\x13ce"
@@ -639,9 +635,7 @@
      L"7890.com",
      false},
     // 12345ճ7890.com
-    {"xn--123457890-fmk.com",
-     L"12345ճ7890.com",
-     false},
+    {"xn--123457890-fmk.com", L"12345ճ7890.com", false},
     // 1234567ȣ90.com
     {"xn--123456790-6od.com",
      L"1234567\x0223"
@@ -1086,6 +1080,10 @@
     {"xn--3-cq6a.com", L"丩3.com", false},
     {"xn--cxe-n68d.com", L"c丫xe.com", false},
     {"xn--cye-b98d.com", L"cy乂e.com", false},
+
+    // U+05D7 can look like Latin n in many fonts.
+    {"xn--ceba.com", L"חח.com", false},
+
 };  // namespace
 
 namespace test {
diff --git a/components/url_formatter/spoof_checks/top_domains/test_domains.list b/components/url_formatter/spoof_checks/top_domains/test_domains.list
index b0b96dc..ed7be8b 100644
--- a/components/url_formatter/spoof_checks/top_domains/test_domains.list
+++ b/components/url_formatter/spoof_checks/top_domains/test_domains.list
@@ -31,5 +31,6 @@
 43.com
 oo.com
 qq.com
+nn.com
 # A domain with the same skeleton as itself:
 test.net
diff --git a/components/url_formatter/spoof_checks/top_domains/test_domains.skeletons b/components/url_formatter/spoof_checks/top_domains/test_domains.skeletons
index 10928e7..84d9f5b 100644
--- a/components/url_formatter/spoof_checks/top_domains/test_domains.skeletons
+++ b/components/url_formatter/spoof_checks/top_domains/test_domains.skeletons
@@ -42,4 +42,5 @@
 43.corn, 43.com
 oo.corn, oo.com
 qq.corn, qq.com
+nn.corn, nn.com
 test.net, test.net
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 8102b7e..25617596 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -27,6 +27,7 @@
     "//content/app:*",
     "//content/public/browser:browser_sources",
     "//content/test/fuzzer:appcache_fuzzer",
+    "//content/test/fuzzer:browser_accessibility_fuzzer",
   ]
 
   configs += [
diff --git a/content/browser/accessibility/one_shot_accessibility_tree_search.h b/content/browser/accessibility/one_shot_accessibility_tree_search.h
index 51a07b3c..7b68dc02 100644
--- a/content/browser/accessibility/one_shot_accessibility_tree_search.h
+++ b/content/browser/accessibility/one_shot_accessibility_tree_search.h
@@ -23,9 +23,9 @@
 typedef bool (*AccessibilityMatchPredicate)(BrowserAccessibility* start_element,
                                             BrowserAccessibility* this_element);
 
-#define DECLARE_ACCESSIBILITY_PREDICATE(PredicateName)    \
-  bool PredicateName(BrowserAccessibility* start_element, \
-                     BrowserAccessibility* this_element)
+#define DECLARE_ACCESSIBILITY_PREDICATE(PredicateName)                   \
+  CONTENT_EXPORT bool PredicateName(BrowserAccessibility* start_element, \
+                                    BrowserAccessibility* this_element)
 
 DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityArticlePredicate);
 DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityBlockquotePredicate);
diff --git a/content/browser/blob_storage/blob_url_unittest.cc b/content/browser/blob_storage/blob_url_unittest.cc
index 9df2d06..b6124bbe 100644
--- a/content/browser/blob_storage/blob_url_unittest.cc
+++ b/content/browser/blob_storage/blob_url_unittest.cc
@@ -192,13 +192,13 @@
     storage::MockBlobRegistryDelegate delegate;
     storage::BlobURLStoreImpl url_store(GetStorageContext(), &delegate);
 
-    blink::mojom::BlobPtr blob_ptr;
+    mojo::PendingRemote<blink::mojom::Blob> blob_remote;
     storage::BlobImpl::Create(
         std::make_unique<storage::BlobDataHandle>(*GetHandleFromBuilder()),
-        MakeRequest(&blob_ptr));
+        blob_remote.InitWithNewPipeAndPassReceiver());
 
     base::RunLoop loop;
-    url_store.Register(std::move(blob_ptr), url, loop.QuitClosure());
+    url_store.Register(std::move(blob_remote), url, loop.QuitClosure());
     loop.Run();
 
     network::mojom::URLLoaderFactoryPtr url_loader_factory;
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc
index 2cd0a69..4ec26451 100644
--- a/content/browser/browser_thread_unittest.cc
+++ b/content/browser/browser_thread_unittest.cc
@@ -57,8 +57,7 @@
 
     BrowserTaskExecutor::CreateForTesting(
         std::move(browser_ui_thread_scheduler),
-        std::make_unique<BrowserIOThreadDelegate>(
-            BrowserIOThreadDelegate::BrowserTaskExecutorPresent::kYes));
+        std::make_unique<BrowserIOThreadDelegate>());
     BrowserTaskExecutor::EnableAllQueues();
   }
 
@@ -117,7 +116,6 @@
 
     BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::UI);
     BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::IO);
-    BrowserTaskExecutor::ResetForTesting();
   }
 
   // Prepares this BrowserThreadTest for Release() to be invoked. |on_release|
@@ -286,7 +284,7 @@
   run_loop.Run();
 }
 
-TEST_F(BrowserThreadTest, RunsTasksInCurrentSequenceDuringShutdown) {
+TEST_F(BrowserThreadTest, RunsTasksInCurrentSequencedDuringShutdown) {
   bool did_shutdown = false;
   base::RunLoop loop;
   UIThreadDestructionObserver observer(&did_shutdown, loop.QuitClosure());
@@ -313,8 +311,7 @@
               QueueType::kDefault));
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
-          std::make_unique<BrowserIOThreadDelegate>(
-              BrowserIOThreadDelegate::BrowserTaskExecutorPresent::kYes));
+          std::make_unique<BrowserIOThreadDelegate>());
 
       ui_thread_ = BrowserTaskExecutor::CreateIOThread();
       BrowserTaskExecutor::InitializeIOThread();
diff --git a/content/browser/font_unique_name_lookup/font_unique_name_lookup_service.cc b/content/browser/font_unique_name_lookup/font_unique_name_lookup_service.cc
index 55fbde1..8f6822d 100644
--- a/content/browser/font_unique_name_lookup/font_unique_name_lookup_service.cc
+++ b/content/browser/font_unique_name_lookup/font_unique_name_lookup_service.cc
@@ -13,7 +13,7 @@
 #include "content/browser/font_unique_name_lookup/font_unique_name_lookup.h"
 #include "content/public/common/content_features.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 namespace content {
 
@@ -26,9 +26,9 @@
 
 // static
 void FontUniqueNameLookupService::Create(
-    blink::mojom::FontUniqueNameLookupRequest request) {
-  mojo::MakeStrongBinding(std::make_unique<FontUniqueNameLookupService>(),
-                          std::move(request));
+    mojo::PendingReceiver<blink::mojom::FontUniqueNameLookup> receiver) {
+  mojo::MakeSelfOwnedReceiver(std::make_unique<FontUniqueNameLookupService>(),
+                              std::move(receiver));
 }
 
 // static
diff --git a/content/browser/font_unique_name_lookup/font_unique_name_lookup_service.h b/content/browser/font_unique_name_lookup/font_unique_name_lookup_service.h
index 228fc2c..515865e 100644
--- a/content/browser/font_unique_name_lookup/font_unique_name_lookup_service.h
+++ b/content/browser/font_unique_name_lookup/font_unique_name_lookup_service.h
@@ -7,7 +7,7 @@
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "third_party/blink/public/mojom/font_unique_name_lookup/font_unique_name_lookup.mojom.h"
 
 namespace content {
@@ -19,7 +19,7 @@
   FontUniqueNameLookupService();
   ~FontUniqueNameLookupService() override;
 
-  static void Create(blink::mojom::FontUniqueNameLookupRequest);
+  static void Create(mojo::PendingReceiver<blink::mojom::FontUniqueNameLookup>);
 
   static scoped_refptr<base::SequencedTaskRunner> GetTaskRunner();
 
diff --git a/content/browser/native_file_system/file_system_chooser.cc b/content/browser/native_file_system/file_system_chooser.cc
index bee4810..cff54dd 100644
--- a/content/browser/native_file_system/file_system_chooser.cc
+++ b/content/browser/native_file_system/file_system_chooser.cc
@@ -180,44 +180,6 @@
   DCHECK(isolated_context);
 
   RecordFileSelectionResult(type_, files.size());
-
-  if (type_ == blink::mojom::ChooseFileSystemEntryType::kSaveFile) {
-    // Create files if they don't yet exist, and truncate files if they do
-    // exist.
-    base::PostTask(
-        FROM_HERE,
-        {base::ThreadPool(), base::TaskPriority::USER_BLOCKING,
-         base::MayBlock()},
-        base::BindOnce(
-            [](const std::vector<base::FilePath>& files,
-               scoped_refptr<base::TaskRunner> callback_runner,
-               ResultCallback callback) {
-              for (const auto& path : files) {
-                int creation_flags =
-                    base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
-                base::File file(path, creation_flags);
-
-                if (!file.IsValid()) {
-                  callback_runner->PostTask(
-                      FROM_HERE,
-                      base::BindOnce(std::move(callback),
-                                     native_file_system_error::FromStatus(
-                                         blink::mojom::NativeFileSystemStatus::
-                                             kOperationFailed,
-                                         "Failed to create file"),
-                                     std::vector<base::FilePath>()));
-                  return;
-                }
-              }
-              callback_runner->PostTask(
-                  FROM_HERE, base::BindOnce(std::move(callback),
-                                            native_file_system_error::Ok(),
-                                            std::move(files)));
-            },
-            files, callback_runner_, std::move(callback_)));
-    delete this;
-    return;
-  }
   callback_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(callback_), native_file_system_error::Ok(),
diff --git a/content/browser/native_file_system/file_system_chooser_browsertest.cc b/content/browser/native_file_system/file_system_chooser_browsertest.cc
index 3877213..1e6fecc 100644
--- a/content/browser/native_file_system/file_system_chooser_browsertest.cc
+++ b/content/browser/native_file_system/file_system_chooser_browsertest.cc
@@ -245,6 +245,85 @@
       << result.error;
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
+                       SaveFile_SensitiveDirectory_ExistingFile) {
+  const std::string file_contents = "Hello World";
+  const base::FilePath test_file = CreateTestFile(file_contents);
+
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_file}, &dialog_params));
+
+  testing::StrictMock<MockNativeFileSystemPermissionContext> permission_context;
+  static_cast<NativeFileSystemManagerImpl*>(
+      BrowserContext::GetStoragePartition(
+          shell()->web_contents()->GetBrowserContext(),
+          shell()->web_contents()->GetSiteInstance())
+          ->GetNativeFileSystemEntryFactory())
+      ->SetPermissionContextForTesting(&permission_context);
+
+  EXPECT_CALL(permission_context, ConfirmSensitiveDirectoryAccess_(
+                                      testing::_, testing::_, testing::_,
+                                      testing::_, testing::_, testing::_))
+      .WillOnce(RunOnceCallback<5>(SensitiveDirectoryResult::kAbort));
+
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  auto result =
+      EvalJs(shell(), "self.chooseFileSystemEntries({type: 'saveFile'})");
+  EXPECT_TRUE(result.error.find("aborted") != std::string::npos)
+      << result.error;
+
+  {
+    // File should still exist, and be unmodified.
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    std::string read_contents;
+    EXPECT_TRUE(base::ReadFileToString(test_file, &read_contents));
+    EXPECT_EQ(file_contents, read_contents);
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
+                       SaveFile_SensitiveDirectory_NonExistingFile) {
+  const base::FilePath test_file = CreateTestFile("");
+  {
+    // Delete file, since SaveFile should be able to deal with non-existing
+    // files.
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    ASSERT_TRUE(base::DeleteFile(test_file, false));
+  }
+
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_file}, &dialog_params));
+
+  testing::StrictMock<MockNativeFileSystemPermissionContext> permission_context;
+  static_cast<NativeFileSystemManagerImpl*>(
+      BrowserContext::GetStoragePartition(
+          shell()->web_contents()->GetBrowserContext(),
+          shell()->web_contents()->GetSiteInstance())
+          ->GetNativeFileSystemEntryFactory())
+      ->SetPermissionContextForTesting(&permission_context);
+
+  EXPECT_CALL(permission_context, ConfirmSensitiveDirectoryAccess_(
+                                      testing::_, testing::_, testing::_,
+                                      testing::_, testing::_, testing::_))
+      .WillOnce(RunOnceCallback<5>(SensitiveDirectoryResult::kAbort));
+
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  auto result =
+      EvalJs(shell(), "self.chooseFileSystemEntries({type: 'saveFile'})");
+  EXPECT_TRUE(result.error.find("aborted") != std::string::npos)
+      << result.error;
+
+  {
+    // File should not have been created.
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    EXPECT_FALSE(base::PathExists(test_file));
+  }
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, AcceptsOptions) {
   SelectFileDialogParams dialog_params;
   ui::SelectFileDialog::SetFactory(
diff --git a/content/browser/native_file_system/native_file_system_file_writer_impl.cc b/content/browser/native_file_system/native_file_system_file_writer_impl.cc
index b34bec4..61efea5 100644
--- a/content/browser/native_file_system/native_file_system_file_writer_impl.cc
+++ b/content/browser/native_file_system/native_file_system_file_writer_impl.cc
@@ -103,9 +103,10 @@
   }
 }
 
-void NativeFileSystemFileWriterImpl::Write(uint64_t offset,
-                                           blink::mojom::BlobPtr data,
-                                           WriteCallback callback) {
+void NativeFileSystemFileWriterImpl::Write(
+    uint64_t offset,
+    mojo::PendingRemote<blink::mojom::Blob> data,
+    WriteCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   RunWithWritePermission(
@@ -163,9 +164,10 @@
       std::move(callback));
 }
 
-void NativeFileSystemFileWriterImpl::WriteImpl(uint64_t offset,
-                                               blink::mojom::BlobPtr data,
-                                               WriteCallback callback) {
+void NativeFileSystemFileWriterImpl::WriteImpl(
+    uint64_t offset,
+    mojo::PendingRemote<blink::mojom::Blob> data,
+    WriteCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK_EQ(GetWritePermissionStatus(),
             blink::mojom::PermissionStatus::GRANTED);
@@ -179,7 +181,7 @@
     return;
   }
 
-  blob_context()->GetBlobDataFromBlobPtr(
+  blob_context()->GetBlobDataFromBlobRemote(
       std::move(data),
       base::BindOnce(&NativeFileSystemFileWriterImpl::DoWriteBlob,
                      weak_factory_.GetWeakPtr(), std::move(callback), offset));
diff --git a/content/browser/native_file_system/native_file_system_file_writer_impl.h b/content/browser/native_file_system/native_file_system_file_writer_impl.h
index 025e61b..cf36fd6 100644
--- a/content/browser/native_file_system/native_file_system_file_writer_impl.h
+++ b/content/browser/native_file_system/native_file_system_file_writer_impl.h
@@ -12,6 +12,7 @@
 #include "content/browser/native_file_system/native_file_system_handle_base.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/native_file_system_permission_context.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "storage/browser/fileapi/file_system_url.h"
 #include "third_party/blink/public/mojom/native_file_system/native_file_system_file_writer.mojom.h"
 
@@ -48,7 +49,7 @@
   const storage::FileSystemURL& swap_url() const { return swap_url_; }
 
   void Write(uint64_t offset,
-             blink::mojom::BlobPtr data,
+             mojo::PendingRemote<blink::mojom::Blob> data,
              WriteCallback callback) override;
   void WriteStream(uint64_t offset,
                    mojo::ScopedDataPipeConsumerHandle stream,
@@ -76,7 +77,7 @@
   struct WriteState;
 
   void WriteImpl(uint64_t offset,
-                 blink::mojom::BlobPtr data,
+                 mojo::PendingRemote<blink::mojom::Blob> data,
                  WriteCallback callback);
   void DoWriteBlob(WriteCallback callback,
                    uint64_t position,
diff --git a/content/browser/native_file_system/native_file_system_file_writer_impl_unittest.cc b/content/browser/native_file_system/native_file_system_file_writer_impl_unittest.cc
index 8db8f7f..01a7513 100644
--- a/content/browser/native_file_system/native_file_system_file_writer_impl_unittest.cc
+++ b/content/browser/native_file_system/native_file_system_file_writer_impl_unittest.cc
@@ -103,13 +103,15 @@
     EXPECT_TRUE(dir_.Delete());
   }
 
-  blink::mojom::BlobPtr CreateBlob(const std::string& contents) {
+  mojo::PendingRemote<blink::mojom::Blob> CreateBlob(
+      const std::string& contents) {
     auto builder =
         std::make_unique<storage::BlobDataBuilder>(base::GenerateGUID());
     builder->AppendData(contents);
     auto handle = blob_context_->AddFinishedBlob(std::move(builder));
-    blink::mojom::BlobPtr result;
-    storage::BlobImpl::Create(std::move(handle), MakeRequest(&result));
+    mojo::PendingRemote<blink::mojom::Blob> result;
+    storage::BlobImpl::Create(std::move(handle),
+                              result.InitWithNewPipeAndPassReceiver());
     return result;
   }
 
@@ -159,9 +161,10 @@
     }
   }
 
-  NativeFileSystemStatus WriteBlobSync(uint64_t position,
-                                       blink::mojom::BlobPtr blob,
-                                       uint64_t* bytes_written_out) {
+  NativeFileSystemStatus WriteBlobSync(
+      uint64_t position,
+      mojo::PendingRemote<blink::mojom::Blob> blob,
+      uint64_t* bytes_written_out) {
     base::RunLoop loop;
     NativeFileSystemStatus result_out;
     handle_->Write(position, std::move(blob),
@@ -262,8 +265,8 @@
                          ::testing::Bool());
 
 TEST_F(NativeFileSystemFileWriterImplTest, WriteInvalidBlob) {
-  blink::mojom::BlobPtr blob;
-  MakeRequest(&blob);
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  ignore_result(blob.InitWithNewPipeAndPassReceiver());
 
   uint64_t bytes_written;
   NativeFileSystemStatus result =
diff --git a/content/browser/native_file_system/native_file_system_manager_impl.cc b/content/browser/native_file_system/native_file_system_manager_impl.cc
index aa027b1..1d3110a 100644
--- a/content/browser/native_file_system/native_file_system_manager_impl.cc
+++ b/content/browser/native_file_system/native_file_system_manager_impl.cc
@@ -106,6 +106,12 @@
   return rfh->HasTransientUserActivation();
 }
 
+bool CreateOrTruncateFile(const base::FilePath& path) {
+  int creation_flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
+  base::File file(path, creation_flags);
+  return file.IsValid();
+}
+
 }  // namespace
 
 NativeFileSystemManagerImpl::SharedHandleState::SharedHandleState(
@@ -469,17 +475,44 @@
     return;
   }
 
-  std::vector<blink::mojom::NativeFileSystemEntryPtr> result_entries;
-  result_entries.reserve(entries.size());
-  for (const auto& entry : entries) {
-    if (options.type() == blink::mojom::ChooseFileSystemEntryType::kSaveFile) {
-      result_entries.push_back(
-          CreateWritableFileEntryFromPath(binding_context, entry));
-    } else {
-      result_entries.push_back(CreateFileEntryFromPath(binding_context, entry));
-    }
+  if (options.type() == blink::mojom::ChooseFileSystemEntryType::kSaveFile) {
+    DCHECK_EQ(entries.size(), 1u);
+    // Create file if it doesn't yet exist, and truncate file if it does exist.
+    base::PostTaskAndReplyWithResult(
+        FROM_HERE,
+        {base::ThreadPool(), base::TaskPriority::USER_BLOCKING,
+         base::MayBlock()},
+        base::BindOnce(&CreateOrTruncateFile, entries.front()),
+        base::BindOnce(
+            &NativeFileSystemManagerImpl::DidCreateOrTruncateSaveFile, this,
+            binding_context, entries.front(), std::move(callback)));
+    return;
   }
 
+  std::vector<blink::mojom::NativeFileSystemEntryPtr> result_entries;
+  result_entries.reserve(entries.size());
+  for (const auto& entry : entries)
+    result_entries.push_back(CreateFileEntryFromPath(binding_context, entry));
+  std::move(callback).Run(native_file_system_error::Ok(),
+                          std::move(result_entries));
+}
+
+void NativeFileSystemManagerImpl::DidCreateOrTruncateSaveFile(
+    const BindingContext& binding_context,
+    const base::FilePath& path,
+    ChooseEntriesCallback callback,
+    bool success) {
+  std::vector<blink::mojom::NativeFileSystemEntryPtr> result_entries;
+  if (!success) {
+    std::move(callback).Run(
+        native_file_system_error::FromStatus(
+            blink::mojom::NativeFileSystemStatus::kOperationFailed,
+            "Failed to create or truncate file"),
+        std::move(result_entries));
+    return;
+  }
+  result_entries.push_back(
+      CreateWritableFileEntryFromPath(binding_context, path));
   std::move(callback).Run(native_file_system_error::Ok(),
                           std::move(result_entries));
 }
diff --git a/content/browser/native_file_system/native_file_system_manager_impl.h b/content/browser/native_file_system/native_file_system_manager_impl.h
index 9925a312..ea2018e2 100644
--- a/content/browser/native_file_system/native_file_system_manager_impl.h
+++ b/content/browser/native_file_system/native_file_system_manager_impl.h
@@ -187,6 +187,10 @@
       ChooseEntriesCallback callback,
       std::vector<base::FilePath> entries,
       NativeFileSystemPermissionContext::SensitiveDirectoryResult result);
+  void DidCreateOrTruncateSaveFile(const BindingContext& binding_context,
+                                   const base::FilePath& path,
+                                   ChooseEntriesCallback callback,
+                                   bool success);
   void DidChooseDirectory(
       const BindingContext& binding_context,
       const base::FilePath& path,
diff --git a/content/browser/scheduler/browser_io_thread_delegate.cc b/content/browser/scheduler/browser_io_thread_delegate.cc
index adf0fb9..ec65f10a 100644
--- a/content/browser/scheduler/browser_io_thread_delegate.cc
+++ b/content/browser/scheduler/browser_io_thread_delegate.cc
@@ -7,8 +7,6 @@
 #include "base/message_loop/message_pump_type.h"
 #include "base/task/sequence_manager/sequence_manager.h"
 #include "base/task/sequence_manager/task_queue.h"
-#include "base/task/task_executor.h"
-#include "content/browser/scheduler/browser_task_executor.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace content {
@@ -17,20 +15,17 @@
 using ::base::sequence_manager::SequenceManager;
 using ::base::sequence_manager::TaskQueue;
 
-BrowserIOThreadDelegate::BrowserIOThreadDelegate(
-    BrowserTaskExecutorPresent browser_task_executor_present)
+BrowserIOThreadDelegate::BrowserIOThreadDelegate()
     : sequence_manager_(CreateUnboundSequenceManager(
           SequenceManager::Settings::Builder()
               .SetMessagePumpType(base::MessagePumpType::IO)
-              .Build())),
-      browser_task_executor_present_(browser_task_executor_present) {
+              .Build())) {
   Init(sequence_manager_.get());
 }
 
 BrowserIOThreadDelegate::BrowserIOThreadDelegate(
     SequenceManager* sequence_manager)
-    : sequence_manager_(nullptr),
-      browser_task_executor_present_(BrowserTaskExecutorPresent::kYes) {
+    : sequence_manager_(nullptr) {
   Init(sequence_manager);
 }
 
@@ -47,11 +42,7 @@
   return default_task_runner_;
 }
 
-BrowserIOThreadDelegate::~BrowserIOThreadDelegate() {
-  if (browser_task_executor_present_ == BrowserTaskExecutorPresent::kYes) {
-    base::SetTaskExecutorForCurrentThread(nullptr);
-  }
-}
+BrowserIOThreadDelegate::~BrowserIOThreadDelegate() = default;
 
 void BrowserIOThreadDelegate::BindToCurrentThread(
     base::TimerSlack timer_slack) {
@@ -60,10 +51,6 @@
       base::MessagePump::Create(base::MessagePumpType::IO));
   sequence_manager_->SetTimerSlack(timer_slack);
   sequence_manager_->SetDefaultTaskRunner(GetDefaultTaskRunner());
-
-  if (browser_task_executor_present_ == BrowserTaskExecutorPresent::kYes) {
-    base::SetTaskExecutorForCurrentThread(BrowserTaskExecutor::Get());
-  }
 }
 
 }  // namespace content
diff --git a/content/browser/scheduler/browser_io_thread_delegate.h b/content/browser/scheduler/browser_io_thread_delegate.h
index 857290d..49c337d 100644
--- a/content/browser/scheduler/browser_io_thread_delegate.h
+++ b/content/browser/scheduler/browser_io_thread_delegate.h
@@ -28,19 +28,12 @@
  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, BrowserIOThreadDelegateTest tests it in
-  // isolation, so we need to disable registering the executor to pass checks.
-  enum class BrowserTaskExecutorPresent { kYes, kNoForTesting };
-
   static std::unique_ptr<BrowserIOThreadDelegate> CreateForTesting(
       base::sequence_manager::SequenceManager* sequence_manager) {
     return base::WrapUnique(new BrowserIOThreadDelegate(sequence_manager));
   }
 
-  BrowserIOThreadDelegate(
-      BrowserTaskExecutorPresent browser_task_executor_present);
+  BrowserIOThreadDelegate();
   ~BrowserIOThreadDelegate() override;
 
   scoped_refptr<base::SingleThreadTaskRunner> GetDefaultTaskRunner() override;
@@ -71,8 +64,6 @@
 
   std::unique_ptr<BrowserTaskQueues> task_queues_;
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
-
-  const BrowserTaskExecutorPresent browser_task_executor_present_;
 };
 
 }  // 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..2be9c02 100644
--- a/content/browser/scheduler/browser_io_thread_delegate_unittest.cc
+++ b/content/browser/scheduler/browser_io_thread_delegate_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
-#include "content/browser/scheduler/browser_task_executor.h"
 #include "content/browser/scheduler/browser_task_queues.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,8 +19,7 @@
 TEST(BrowserIOThreadDelegateTest, CanPostTasksToThread) {
   base::Thread thread("my_thread");
 
-  auto delegate = std::make_unique<BrowserIOThreadDelegate>(
-      BrowserIOThreadDelegate::BrowserTaskExecutorPresent::kNoForTesting);
+  auto delegate = std::make_unique<BrowserIOThreadDelegate>();
   auto handle = delegate->CreateHandle();
   handle->EnableAllQueues();
 
@@ -38,11 +36,10 @@
   event.Wait();
 }
 
-TEST(BrowserIOThreadDelegateTest, DefaultTaskRunnerIsAlwaysActive) {
+TEST(BrowserIOThreadDelegateTest, DefaultTaskRunnerIsAllwaysActive) {
   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 dfd65de4..f2ddb98 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -11,7 +11,6 @@
 #include "base/message_loop/message_pump_type.h"
 #include "base/no_destructor.h"
 #include "base/task/post_task.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -75,17 +74,13 @@
       browser_io_thread_delegate_(std::move(browser_io_thread_delegate)),
       browser_io_thread_handle_(browser_io_thread_delegate_->CreateHandle()) {}
 
-BrowserTaskExecutor::~BrowserTaskExecutor() {
-  base::SetTaskExecutorForCurrentThread(nullptr);
-}
+BrowserTaskExecutor::~BrowserTaskExecutor() = default;
 
 // static
 void BrowserTaskExecutor::Create() {
   DCHECK(!base::ThreadTaskRunnerHandle::IsSet());
-  CreateInternal(
-      std::make_unique<BrowserUIThreadScheduler>(),
-      std::make_unique<BrowserIOThreadDelegate>(
-          BrowserIOThreadDelegate::BrowserTaskExecutorPresent::kYes));
+  CreateInternal(std::make_unique<BrowserUIThreadScheduler>(),
+                 std::make_unique<BrowserIOThreadDelegate>());
 }
 
 // static
@@ -109,32 +104,21 @@
   g_browser_task_executor->browser_ui_thread_handle_
       ->EnableAllExceptBestEffortQueues();
 
-  base::SetTaskExecutorForCurrentThread(g_browser_task_executor);
-
 #if defined(OS_ANDROID)
   base::PostTaskAndroid::SignalNativeSchedulerReady();
 #endif
 }
 
 // static
-BrowserTaskExecutor* BrowserTaskExecutor::Get() {
-  DCHECK(g_browser_task_executor);
-  return g_browser_task_executor;
-}
-
-// static
 void BrowserTaskExecutor::ResetForTesting() {
 #if defined(OS_ANDROID)
   base::PostTaskAndroid::SignalNativeSchedulerShutdown();
 #endif
-  base::SetTaskExecutorForCurrentThread(nullptr);
+
   if (g_browser_task_executor) {
     base::UnregisterTaskExecutorForTesting(
         BrowserTaskTraitsExtension::kExtensionId);
     delete g_browser_task_executor;
-    ANNOTATE_BENIGN_RACE(
-        &g_browser_task_executor,
-        "Test-only data race in content/browser/browser_thread_unittest");
     g_browser_task_executor = nullptr;
   }
 }
diff --git a/content/browser/scheduler/browser_task_executor.h b/content/browser/scheduler/browser_task_executor.h
index b3f6bc2..c873e97 100644
--- a/content/browser/scheduler/browser_task_executor.h
+++ b/content/browser/scheduler/browser_task_executor.h
@@ -162,10 +162,6 @@
   FRIEND_TEST_ALL_PREFIXES(BrowserTaskExecutorTest,
                            BestEffortTasksRunAfterStartup);
 
-  // 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);
@@ -174,9 +170,6 @@
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
       const base::TaskTraits& traits) const;
 
-  friend class BrowserIOThreadDelegate;
-  static BrowserTaskExecutor* Get();
-
   std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler_;
   scoped_refptr<BrowserUIThreadScheduler::Handle> browser_ui_thread_handle_;
 
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc
index 12adb00..3bae309 100644
--- a/content/browser/scheduler/browser_task_executor_unittest.cc
+++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -47,23 +47,6 @@
 using StrictMockTask =
     testing::StrictMock<base::MockCallback<base::RepeatingCallback<void()>>>;
 
-TEST_F(BrowserTaskExecutorTest, RegisterExecutorForBothThreads) {
-  base::PostTask(FROM_HERE, {BrowserThread::UI},
-                 base::BindLambdaForTesting([&]() {
-                   EXPECT_EQ(BrowserTaskExecutor::Get(),
-                             base::GetTaskExecutorForCurrentThread());
-                 }));
-
-  base::PostTask(FROM_HERE, {BrowserThread::IO},
-                 base::BindLambdaForTesting([&]() {
-                   EXPECT_EQ(BrowserTaskExecutor::Get(),
-                             base::GetTaskExecutorForCurrentThread());
-                 }));
-
-  BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::UI);
-  BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
-}
-
 TEST_F(BrowserTaskExecutorTest, RunAllPendingTasksForTestingOnUI) {
   StrictMockTask task_1;
   StrictMockTask task_2;
@@ -196,8 +179,7 @@
               QueueType::kDefault));
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
-          std::make_unique<BrowserIOThreadDelegate>(
-              BrowserIOThreadDelegate::BrowserTaskExecutorPresent::kYes));
+          std::make_unique<BrowserIOThreadDelegate>());
     }
   };
 
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index a0d9dc0c..b365188 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -199,7 +199,7 @@
     return url_store_.get();
   }
 
-  void Register(blink::mojom::BlobPtr blob,
+  void Register(mojo::PendingRemote<blink::mojom::Blob> blob,
                 const GURL& url,
                 RegisterCallback callback) override {
     GetForwardingInterface()->Register(std::move(blob), target_url_,
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 08d45d6..c4287066 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -343,7 +343,11 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderFrameHostImpl* ancestor_render_frame_host =
       GetAncestorRenderFrameHost();
-  // TODO(nhiroki): Check if |ancestor_render_frame_host| is valid.
+  if (!ancestor_render_frame_host) {
+    // The ancestor frame may have already been closed. In that case, the worker
+    // will soon be terminated too, so abort the connection.
+    return;
+  }
   GetContentClient()->browser()->CreateWebUsbService(ancestor_render_frame_host,
                                                      std::move(receiver));
 }
@@ -351,12 +355,11 @@
 void DedicatedWorkerHost::CreateWebSocketConnector(
     mojo::PendingReceiver<blink::mojom::WebSocketConnector> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
   RenderFrameHostImpl* ancestor_render_frame_host =
       GetAncestorRenderFrameHost();
   if (!ancestor_render_frame_host) {
-    // In some cases |ancestor_render_frame_host| can be null. In such cases
-    // the worker will soon be terminated too, so let's abort the connection.
+    // The ancestor frame may have already been closed. In that case, the worker
+    // will soon be terminated too, so abort the connection.
     receiver.ResetWithReason(network::mojom::WebSocket::kInsufficientResources,
                              "The parent frame has already been gone.");
     return;
@@ -378,6 +381,7 @@
 
 void DedicatedWorkerHost::BindFileSystemManager(
     mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderProcessHost* worker_process_host = GetProcessHost();
   if (!worker_process_host)
     return;
@@ -389,7 +393,11 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderFrameHostImpl* ancestor_render_frame_host =
       GetAncestorRenderFrameHost();
-  // TODO(nhiroki): Check if |ancestor_render_frame_host| is valid.
+  if (!ancestor_render_frame_host) {
+    // The ancestor frame may have already been closed. In that case, the worker
+    // will soon be terminated too, so abort the connection.
+    return;
+  }
   if (!ancestor_render_frame_host->IsFeatureEnabled(
           blink::mojom::FeaturePolicyFeature::kIdleDetection)) {
     mojo::ReportBadMessage("Feature policy blocks access to IdleDetection.");
@@ -412,7 +420,7 @@
 namespace {
 // A factory for creating DedicatedWorkerHosts. Its lifetime is managed by
 // the renderer over mojo via a StrongBinding. This lives on the UI thread.
-class DedicatedWorkerHostFactoryImpl
+class DedicatedWorkerHostFactoryImpl final
     : public blink::mojom::DedicatedWorkerHostFactory {
  public:
   DedicatedWorkerHostFactoryImpl(int creator_process_id,
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 2f7e0b6d..eded5a05 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -686,30 +686,6 @@
 const base::Feature kWebRtcEcdsaDefault{"WebRTC-EnableWebRtcEcdsa",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enables HW H264 encoding on Android.
-const base::Feature kWebRtcHWH264Encoding{"WebRtcHWH264Encoding",
-                                          base::FEATURE_ENABLED_BY_DEFAULT};
-
-// Enables HW VP8 encoding on Android.
-const base::Feature kWebRtcHWVP8Encoding {
-  "WebRtcHWVP8Encoding",
-#if defined(OS_ANDROID)
-      base::FEATURE_DISABLED_BY_DEFAULT
-#else
-      base::FEATURE_ENABLED_BY_DEFAULT
-#endif
-};
-
-// Enables HW VP9 encoding on Android.
-const base::Feature kWebRtcHWVP9Encoding {
-  "WebRtcHWVP9Encoding",
-#if defined(OS_ANDROID)
-      base::FEATURE_DISABLED_BY_DEFAULT
-#else
-      base::FEATURE_DISABLED_BY_DEFAULT
-#endif
-};
-
 // Enables negotiation of experimental multiplex codec in SDP.
 const base::Feature kWebRtcMultiplexCodec{"WebRTC-MultiplexCodec",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 1d57428..0ca4208 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -142,9 +142,6 @@
 CONTENT_EXPORT extern const base::Feature kWebGLImageChromium;
 CONTENT_EXPORT extern const base::Feature kWebPayments;
 CONTENT_EXPORT extern const base::Feature kWebRtcEcdsaDefault;
-CONTENT_EXPORT extern const base::Feature kWebRtcHWH264Encoding;
-CONTENT_EXPORT extern const base::Feature kWebRtcHWVP8Encoding;
-CONTENT_EXPORT extern const base::Feature kWebRtcHWVP9Encoding;
 CONTENT_EXPORT extern const base::Feature kWebRtcMultiplexCodec;
 CONTENT_EXPORT extern const base::Feature kWebRtcUseGpuMemoryBufferVideoFrames;
 CONTENT_EXPORT extern const base::Feature kWebRtcHideLocalIpsWithMdns;
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index 66d836e..7d53414 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -327,11 +327,11 @@
   // function is called from the worker thread.
   virtual void WillInitializeServiceWorkerContextOnWorkerThread() {}
 
-  // Notifies that a service worker context has been created. This function
-  // is called from the worker thread.
+  // Notifies that the main script of a service worker is about to evaluate.
+  // This function is called from the worker thread.
   // |context_proxy| is valid until
   // WillDestroyServiceWorkerContextOnWorkerThread() is called.
-  virtual void DidInitializeServiceWorkerContextOnWorkerThread(
+  virtual void WillEvaluateServiceWorkerOnWorkerThread(
       blink::WebServiceWorkerContextProxy* context_proxy,
       v8::Local<v8::Context> v8_context,
       int64_t service_worker_version_id,
diff --git a/content/public/test/browser_task_environment.cc b/content/public/test/browser_task_environment.cc
index 1116975..a7aee30a 100644
--- a/content/public/test/browser_task_environment.cc
+++ b/content/public/test/browser_task_environment.cc
@@ -93,8 +93,7 @@
       browser_ui_thread_scheduler->GetHandle()->GetDefaultTaskRunner();
   auto browser_io_thread_delegate =
       real_io_thread_
-          ? std::make_unique<BrowserIOThreadDelegate>(
-                BrowserIOThreadDelegate::BrowserTaskExecutorPresent::kYes)
+          ? std::make_unique<BrowserIOThreadDelegate>()
           : BrowserIOThreadDelegate::CreateForTesting(sequence_manager());
   browser_io_thread_delegate->SetAllowBlockingForTesting();
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 36be674..453b45b0 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -216,8 +216,6 @@
     "media/webrtc/rtc_rtp_sender.h",
     "media/webrtc/rtc_rtp_transceiver.cc",
     "media/webrtc/rtc_rtp_transceiver.h",
-    "media/webrtc/rtc_video_encoder_factory.cc",
-    "media/webrtc/rtc_video_encoder_factory.h",
     "media/webrtc/stun_field_trial.cc",
     "media/webrtc/stun_field_trial.h",
     "media/webrtc/transceiver_state_surfacer.cc",
diff --git a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
index 1710406..859fd45 100644
--- a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
+++ b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
@@ -195,6 +195,12 @@
 bool GpuVideoAcceleratorFactoriesImpl::IsDecoderConfigSupported(
     media::VideoDecoderImplementation implementation,
     const media::VideoDecoderConfig& config) {
+  // There is no support for alpha channel hardware decoding yet.
+  if (config.alpha_mode() == media::VideoDecoderConfig::AlphaMode::kHasAlpha) {
+    DVLOG(1) << "Alpha transparency formats are not supported.";
+    return false;
+  }
+
   base::AutoLock lock(supported_decoder_configs_lock_);
 
   // If GetSupportedConfigs() has not completed (or was never started), report
diff --git a/content/renderer/media/webrtc/video_codec_factory.cc b/content/renderer/media/webrtc/video_codec_factory.cc
index 9cb4f5f..1fd03d9 100644
--- a/content/renderer/media/webrtc/video_codec_factory.cc
+++ b/content/renderer/media/webrtc/video_codec_factory.cc
@@ -9,9 +9,9 @@
 #include "base/memory/ptr_util.h"
 #include "build/build_config.h"
 #include "content/public/common/content_switches.h"
-#include "content/renderer/media/webrtc/rtc_video_encoder_factory.h"
 #include "media/base/media_switches.h"
 #include "third_party/blink/public/platform/modules/peerconnection/rtc_video_decoder_factory.h"
+#include "third_party/blink/public/platform/modules/peerconnection/rtc_video_encoder_factory.h"
 #include "third_party/webrtc/api/video_codecs/video_decoder_software_fallback_wrapper.h"
 #include "third_party/webrtc/api/video_codecs/video_encoder_software_fallback_wrapper.h"
 #include "third_party/webrtc/media/base/codec.h"
@@ -181,7 +181,7 @@
   const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   if (gpu_factories && gpu_factories->IsGpuVideoAcceleratorEnabled() &&
       !cmd_line->HasSwitch(switches::kDisableWebRtcHWEncoding)) {
-    encoder_factory.reset(new RTCVideoEncoderFactory(gpu_factories));
+    encoder_factory.reset(new blink::RTCVideoEncoderFactory(gpu_factories));
   }
 
 #if defined(OS_ANDROID)
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index a0003b12..7554f7e 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -2016,7 +2016,15 @@
 void RenderThreadImpl::DestroyView(int32_t view_id) {
   RenderViewImpl* view = RenderViewImpl::FromRoutingID(view_id);
   DCHECK(view);
-  view->Destroy();
+
+  // This IPC can be called from re-entrant contexts. We can't destroy a
+  // RenderViewImpl while references still exist on the stack, so we dispatch a
+  // non-nestable task. This method is called exactly once by the browser
+  // process, and is used to release ownership of the corresponding
+  // RenderViewImpl instance. https://crbug.com/1000035.
+  base::ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
+      FROM_HERE,
+      base::BindOnce(&RenderViewImpl::Destroy, base::Unretained(view)));
 }
 
 void RenderThreadImpl::CreateFrame(mojom::CreateFrameParamsPtr params) {
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 512d13d..0d20663f 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -260,7 +260,8 @@
   proxy_->BindControllerServiceWorker(controller_receiver_.PassPipe());
 }
 
-void ServiceWorkerContextClient::WillEvaluateScript() {
+void ServiceWorkerContextClient::WillEvaluateScript(
+    v8::Local<v8::Context> v8_context) {
   DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
   start_timing_->script_evaluation_start_time = base::TimeTicks::Now();
 
@@ -275,6 +276,11 @@
            start_timing_->script_evaluation_start_time);
 
   instance_host_->OnScriptEvaluationStart();
+
+  DCHECK(proxy_);
+  GetContentClient()->renderer()->WillEvaluateServiceWorkerOnWorkerThread(
+      proxy_, v8_context, service_worker_version_id_, service_worker_scope_,
+      script_url_);
 }
 
 void ServiceWorkerContextClient::DidEvaluateScript(bool success) {
@@ -310,17 +316,6 @@
       ->WillInitializeServiceWorkerContextOnWorkerThread();
 }
 
-void ServiceWorkerContextClient::DidInitializeWorkerContext(
-    blink::WebServiceWorkerContextProxy* context_proxy,
-    v8::Local<v8::Context> v8_context) {
-  DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
-  GetContentClient()
-      ->renderer()
-      ->DidInitializeServiceWorkerContextOnWorkerThread(
-          context_proxy, v8_context, service_worker_version_id_,
-          service_worker_scope_, script_url_);
-}
-
 void ServiceWorkerContextClient::WillDestroyWorkerContext(
     v8::Local<v8::Context> context) {
   DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index ce21a52..f86654d 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -128,12 +128,9 @@
   void WorkerContextStarted(
       blink::WebServiceWorkerContextProxy* proxy,
       scoped_refptr<base::SequencedTaskRunner> worker_task_runner) override;
-  void WillEvaluateScript() override;
+  void WillEvaluateScript(v8::Local<v8::Context> v8_context) override;
   void DidEvaluateScript(bool success) override;
   void WillInitializeWorkerContext() override;
-  void DidInitializeWorkerContext(
-      blink::WebServiceWorkerContextProxy* context_proxy,
-      v8::Local<v8::Context> v8_context) override;
   void WillDestroyWorkerContext(v8::Local<v8::Context> context) override;
   void WorkerContextDestroyed() override;
   void CountFeature(blink::mojom::WebFeature feature) override;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 65cbcf0..5b056c72 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -42,6 +42,8 @@
     "../browser/accessibility/accessibility_event_recorder_uia_win.cc",
     "../browser/accessibility/accessibility_event_recorder_uia_win.h",
     "../browser/accessibility/accessibility_event_recorder_win.cc",
+    "../browser/accessibility/test_browser_accessibility_delegate.cc",
+    "../browser/accessibility/test_browser_accessibility_delegate.h",
     "../browser/background_fetch/background_fetch_test_base.cc",
     "../browser/background_fetch/background_fetch_test_base.h",
     "../browser/background_fetch/background_fetch_test_browser_context.cc",
@@ -1500,8 +1502,6 @@
     "../browser/accessibility/browser_accessibility_unittest.cc",
     "../browser/accessibility/browser_accessibility_win_unittest.cc",
     "../browser/accessibility/one_shot_accessibility_tree_search_unittest.cc",
-    "../browser/accessibility/test_browser_accessibility_delegate.cc",
-    "../browser/accessibility/test_browser_accessibility_delegate.h",
     "../browser/appcache/appcache_database_unittest.cc",
     "../browser/appcache/appcache_disk_cache_unittest.cc",
     "../browser/appcache/appcache_group_unittest.cc",
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn
index c26e64a..cf46ec0 100644
--- a/content/test/fuzzer/BUILD.gn
+++ b/content/test/fuzzer/BUILD.gn
@@ -257,3 +257,19 @@
     "//third_party/flac",
   ]
 }
+
+fuzzer_test("browser_accessibility_fuzzer") {
+  sources = [
+    "browser_accessibility_fuzzer.cc",
+  ]
+  deps = [
+    "//content/app:both_for_content_tests",
+    "//content/browser:browser",
+    "//content/browser:browser",
+    "//content/public/browser:browser_sources",
+    "//content/test:test_support",
+    "//mojo/core/embedder",
+    "//services/network:test_support",
+    "//storage/browser:test_support",
+  ]
+}
diff --git a/content/test/fuzzer/browser_accessibility_fuzzer.cc b/content/test/fuzzer/browser_accessibility_fuzzer.cc
new file mode 100644
index 0000000..59e9388
--- /dev/null
+++ b/content/test/fuzzer/browser_accessibility_fuzzer.cc
@@ -0,0 +1,179 @@
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include "content/browser/accessibility/browser_accessibility.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/accessibility/one_shot_accessibility_tree_search.h"
+#include "content/browser/accessibility/test_browser_accessibility_delegate.h"
+
+namespace content {
+
+// Return an accessibility role to use in a tree to fuzz. Out of the
+// dozens of roles, these are the ones that tend to have special meaning
+// or otherwise affect the logic in BrowserAccessibility code.
+ax::mojom::Role GetInterestingRole(FuzzedDataProvider& fdp) {
+  switch (fdp.ConsumeIntegralInRange(0, 12)) {
+    default:
+    case 0:
+      return ax::mojom::Role::kIgnored;
+    case 1:
+      return ax::mojom::Role::kStaticText;
+    case 2:
+      return ax::mojom::Role::kInlineTextBox;
+    case 3:
+      return ax::mojom::Role::kParagraph;
+    case 4:
+      return ax::mojom::Role::kLineBreak;
+    case 5:
+      return ax::mojom::Role::kGenericContainer;
+    case 6:
+      return ax::mojom::Role::kButton;
+    case 7:
+      return ax::mojom::Role::kTextField;
+    case 8:
+      return ax::mojom::Role::kIframePresentational;
+    case 9:
+      return ax::mojom::Role::kIframe;
+    case 10:
+      return ax::mojom::Role::kHeading;
+    case 11:
+      return ax::mojom::Role::kPopUpButton;
+    case 12:
+      return ax::mojom::Role::kLink;
+  }
+}
+
+// Add some states to the node based on the FuzzedDataProvider.
+// Currently we're messing with ignored and invisible because that
+// affects a lot of the tree walking code.
+void AddStates(FuzzedDataProvider& fdp, ui::AXNodeData* node) {
+  if (fdp.ConsumeBool())
+    node->AddState(ax::mojom::State::kIgnored);
+  if (fdp.ConsumeBool())
+    node->AddState(ax::mojom::State::kInvisible);
+}
+
+// Construct an accessibility tree. The shape of the tree is static, but
+// some of the properties of the nodes in the tree are determined by
+// the fuzz input. Once the tree is constructed, fuzz by calling some
+// functions that walk the tree in various ways to ensure they don't crash.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp(data, size);
+
+  // The tree structure is always the same, only the data changes.
+  //
+  //       1
+  //    /      \
+  //  2     3    4
+  // / \   / \  / \
+  // 5 6   7 8  9 10
+  //
+  // In addition, there's a child tree that may be linked off one of
+  // the nodes.
+
+  ui::AXTreeID parent_tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  ui::AXTreeID child_tree_id = ui::AXTreeID::CreateNewAXTreeID();
+
+  ui::AXTreeUpdate tree;
+  tree.root_id = 1;
+  tree.tree_data.tree_id = parent_tree_id;
+  tree.has_tree_data = true;
+  tree.nodes.resize(10);
+
+  tree.nodes[0].id = 1;
+  tree.nodes[0].role = ax::mojom::Role::kRootWebArea;
+  tree.nodes[0].child_ids = {2, 3, 4};
+  AddStates(fdp, &tree.nodes[0]);
+
+  tree.nodes[1].id = 2;
+  tree.nodes[1].role = GetInterestingRole(fdp);
+  tree.nodes[1].child_ids = {5, 6};
+  AddStates(fdp, &tree.nodes[1]);
+
+  tree.nodes[2].id = 3;
+  tree.nodes[2].role = GetInterestingRole(fdp);
+  tree.nodes[2].child_ids = {7, 8};
+  AddStates(fdp, &tree.nodes[2]);
+
+  tree.nodes[3].id = 4;
+  tree.nodes[3].role = GetInterestingRole(fdp);
+  tree.nodes[3].child_ids = {9, 10};
+  AddStates(fdp, &tree.nodes[3]);
+
+  for (int i = 4; i < 10; i++) {
+    tree.nodes[i].id = i + 1;
+    tree.nodes[i].role = GetInterestingRole(fdp);
+    AddStates(fdp, &tree.nodes[i]);
+  }
+
+  for (int i = 0; i < 10; i++)
+    tree.nodes[i].SetName(fdp.ConsumeRandomLengthString(5));
+
+  // Optionally, embed the child tree in the parent tree.
+  int embedder_node = fdp.ConsumeIntegralInRange(0, 10);
+  if (embedder_node > 0)
+    tree.nodes[embedder_node - 1].AddStringAttribute(
+        ax::mojom::StringAttribute::kChildTreeId, child_tree_id.ToString());
+
+  VLOG(1) << tree.ToString();
+
+  // The child tree is trivial, just one node. That's still enough to exercise
+  // a lot of paths.
+
+  ui::AXTreeUpdate child_tree;
+  child_tree.root_id = 1;
+  child_tree.tree_data.tree_id = child_tree_id;
+  child_tree.has_tree_data = true;
+  child_tree.nodes.resize(1);
+
+  child_tree.nodes[0].id = 1;
+  child_tree.nodes[0].role = ax::mojom::Role::kRootWebArea;
+
+  VLOG(1) << child_tree.ToString();
+
+  TestBrowserAccessibilityDelegate delegate;
+  std::unique_ptr<BrowserAccessibilityManager> manager(
+      BrowserAccessibilityManager::Create(tree, &delegate));
+  std::unique_ptr<BrowserAccessibilityManager> child_manager(
+      BrowserAccessibilityManager::Create(child_tree, &delegate));
+
+  // We want to call a bunch of functions but we don't care what the
+  // return values are. To ensure the compiler doesn't optimize the calls
+  // away, push the return values onto a vector and make an assertion about
+  // it at the end.
+  std::vector<void*> results;
+
+  // Test some tree-walking functions.
+  BrowserAccessibility* root = manager->GetRoot();
+  results.push_back(root->PlatformDeepestFirstChild());
+  results.push_back(root->PlatformDeepestLastChild());
+  results.push_back(root->InternalDeepestFirstChild());
+  results.push_back(root->InternalDeepestLastChild());
+
+  // Test OneShotAccessibilityTreeSearch.
+  OneShotAccessibilityTreeSearch search(manager->GetRoot());
+  search.SetDirection(fdp.ConsumeBool()
+                          ? OneShotAccessibilityTreeSearch::FORWARDS
+                          : OneShotAccessibilityTreeSearch::BACKWARDS);
+  search.SetImmediateDescendantsOnly(fdp.ConsumeBool());
+  search.SetCanWrapToLastElement(fdp.ConsumeBool());
+  search.SetVisibleOnly(fdp.ConsumeBool());
+  if (fdp.ConsumeBool())
+    search.AddPredicate(AccessibilityButtonPredicate);
+  if (fdp.ConsumeBool())
+    search.SetSearchText(fdp.ConsumeRandomLengthString(5));
+  size_t matches = search.CountMatches();
+  for (size_t i = 0; i < matches; i++) {
+    results.push_back(search.GetMatchAtIndex(i));
+  }
+
+  // This is just to ensure that none of the above code gets optimized away.
+  CHECK_NE(0U, results.size());
+
+  return 0;
+}
+
+}  // namespace content
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index 4a97e98..b585a332 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -24,10 +24,6 @@
 crbug.com/497411 [ mountainlion debug ] ContextLost_WebGLContextLostFromSelectElement [ Skip ]
 crbug.com/498149 [ lion debug ] ContextLost_WebGLContextLostFromSelectElement [ Skip ]
 
-# Flaky on Mac
-crbug.com/999327 [ mac ] ContextLost_WebGLBlockedAfterJSNavigation [ Skip ]
-crbug.com/999327 [ mac ] ContextLost_WebGLContextLostFromGPUProcessExit [ Skip ]
-
 # Too difficult to make this test work on Mac and Android for now. Disabling
 # GLES3 support at the GL bindings level doesn't work there yet.
 crbug.com/923134 [ mac ] ContextLost_WebGL2Blocked [ Skip ]
diff --git a/docs/android_build_instructions.md b/docs/android_build_instructions.md
index 927931f8..bd226689 100644
--- a/docs/android_build_instructions.md
+++ b/docs/android_build_instructions.md
@@ -135,7 +135,7 @@
   configuration](https://www.chromium.org/developers/gn-build-configuration).
   The default will be a debug component build.
 * For more info on GN, run `gn help` on the command line or read the
-  [quick start guide](../tools/gn/docs/quick_start.md).
+  [quick start guide](https://gn.googlesource.com/gn/+/master/docs/quick_start.md).
 
 Also be aware that some scripts (e.g. `tombstones.py`, `adb_gdb.py`)
 require you to set `CHROMIUM_OUTPUT_DIR=out/Default`.
diff --git a/docs/android_cast_build_instructions.md b/docs/android_cast_build_instructions.md
index 076feeb..a7db4e7f 100644
--- a/docs/android_cast_build_instructions.md
+++ b/docs/android_cast_build_instructions.md
@@ -134,7 +134,7 @@
   The default will be a debug component build matching the current host
   operating system and CPU.
 * For more info on GN, run `gn help` on the command line or read the
-  [quick start guide](../tools/gn/docs/quick_start.md).
+  [quick start guide](https://gn.googlesource.com/gn/+/master/docs/quick_start.md).
 
 Also be aware that some scripts (e.g. `tombstones.py`, `adb_gdb.py`)
 require you to set `CHROMIUM_OUTPUT_DIR=out/Default`.
diff --git a/docs/linux_build_instructions.md b/docs/linux_build_instructions.md
index 6cd0ae3..5d853a1 100644
--- a/docs/linux_build_instructions.md
+++ b/docs/linux_build_instructions.md
@@ -127,7 +127,7 @@
   The default will be a debug component build matching the current host
   operating system and CPU.
 * For more info on GN, run `gn help` on the command line or read the
-  [quick start guide](../tools/gn/docs/quick_start.md).
+  [quick start guide](https://gn.googlesource.com/gn/+/master/docs/quick_start.md).
 
 ### <a name="faster-builds"></a>Faster builds
 
diff --git a/docs/linux_cast_build_instructions.md b/docs/linux_cast_build_instructions.md
index 12bc16b..bdb8df7 100644
--- a/docs/linux_cast_build_instructions.md
+++ b/docs/linux_cast_build_instructions.md
@@ -116,7 +116,7 @@
   The default will be a debug component build matching the current host
   operating system and CPU.
 * For more info on GN, run `gn help` on the command line or read the
-  [quick start guide](../tools/gn/docs/quick_start.md).
+  [quick start guide](https://gn.googlesource.com/gn/+/master/docs/quick_start.md).
 
 ### <a name="faster-builds"></a>Faster builds
 
diff --git a/docs/mac_build_instructions.md b/docs/mac_build_instructions.md
index 3ab976e..49c09f7 100644
--- a/docs/mac_build_instructions.md
+++ b/docs/mac_build_instructions.md
@@ -103,7 +103,7 @@
   The default will be a debug component build matching the current host
   operating system and CPU.
 * For more info on GN, run `gn help` on the command line or read the
-  [quick start guide](../tools/gn/docs/quick_start.md).
+  [quick start guide](https://gn.googlesource.com/gn/+/master/docs/quick_start.md).
 
 
 ### Faster builds
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 1814e7a..9caa5b6 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -322,7 +322,7 @@
       break;
     case Feature::BLESSED_EXTENSION_CONTEXT:
       // For service workers this is handled in
-      // DidInitializeServiceWorkerContextOnWorkerThread().
+      // WillEvaluateServiceWorkerOnWorkerThread().
       DCHECK(!context->IsForServiceWorker());
       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_Blessed", elapsed);
       break;
@@ -353,7 +353,7 @@
   VLOG(1) << "Num tracked contexts: " << script_context_set_->size();
 }
 
-void Dispatcher::DidInitializeServiceWorkerContextOnWorkerThread(
+void Dispatcher::WillEvaluateServiceWorkerOnWorkerThread(
     blink::WebServiceWorkerContextProxy* context_proxy,
     v8::Local<v8::Context> v8_context,
     int64_t service_worker_version_id,
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 31ee4a12..bf5cc425 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -107,7 +107,7 @@
 
   // Runs on a different thread and should only use thread safe member
   // variables.
-  void DidInitializeServiceWorkerContextOnWorkerThread(
+  void WillEvaluateServiceWorkerOnWorkerThread(
       blink::WebServiceWorkerContextProxy* context_proxy,
       v8::Local<v8::Context> v8_context,
       int64_t service_worker_version_id,
diff --git a/extensions/renderer/resources/service_worker_bindings.js b/extensions/renderer/resources/service_worker_bindings.js
index 532f51dd..e8b1880 100644
--- a/extensions/renderer/resources/service_worker_bindings.js
+++ b/extensions/renderer/resources/service_worker_bindings.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This function is returned to DidInitializeServiceWorkerContextOnWorkerThread
+// This function is returned to WillEvaluateServiceWorkerOnWorkerThread
 // then executed, passing in dependencies as function arguments.
 //
 // |backgroundUrl| is the URL of the extension's background page.
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index 90f36eb..903e7121 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -10,12 +10,6 @@
 import("//testing/test.gni")
 import("//tools/grit/repack.gni")
 
-declare_args() {
-  # Enables Vulkan in WebEngine (has effect only for context instances that have
-  # Vulkan loader service in the service directory).
-  web_engine_enable_vulkan = false
-}
-
 config("web_engine_implementation") {
   defines = [ "WEB_ENGINE_IMPLEMENTATION" ]
 }
@@ -54,6 +48,7 @@
     "//content/app/resources",
     "//content/app/strings",
     "//content/browser/tracing:resources",
+    "//gpu/command_buffer/service",
     "//mojo/public/js:resources",
     "//net:net_resources",
     "//third_party/blink/public:resources",
@@ -71,6 +66,7 @@
     ":mojom",
     ":web_engine_pak",
     "//base",
+    "//base:base_static",
     "//components/version_info",
     "//content/public/app:both",
     "//content/public/browser",
@@ -80,6 +76,7 @@
     "//fuchsia/base",
     "//fuchsia/base:message_port",
     "//fuchsia/base:modular",
+    "//gpu/command_buffer/service",
     "//media/fuchsia/cdm/service",
     "//media/fuchsia/mojom",
     "//mojo/public/cpp/bindings",
@@ -93,6 +90,7 @@
     "//third_party/fuchsia-sdk/sdk:web",
     "//third_party/widevine/cdm:headers",
     "//ui/aura",
+    "//ui/base",
     "//ui/base/ime",
     "//ui/display",
     "//ui/ozone",
@@ -163,9 +161,6 @@
     ":*",
     "//fuchsia/runners:cast_runner_browsertests__exec",
   ]
-  if (web_engine_enable_vulkan) {
-    defines = [ "WEB_ENGINE_ENABLE_VULKAN" ]
-  }
 }
 
 executable("web_engine_exe") {
diff --git a/fuchsia/engine/DEPS b/fuchsia/engine/DEPS
index a86f48c..eb2adde57 100644
--- a/fuchsia/engine/DEPS
+++ b/fuchsia/engine/DEPS
@@ -1,5 +1,8 @@
 include_rules = [
+  "+components/viz/common",
   "+content/public/app",
+  "+gpu/command_buffer/service",
   "+services/service_manager",
   "+ui/base",
+  "+ui/gl/gl_switches.h",
 ]
diff --git a/fuchsia/engine/browser/web_engine_browser_main_parts.cc b/fuchsia/engine/browser/web_engine_browser_main_parts.cc
index ae98822..ff48f43 100644
--- a/fuchsia/engine/browser/web_engine_browser_main_parts.cc
+++ b/fuchsia/engine/browser/web_engine_browser_main_parts.cc
@@ -10,12 +10,14 @@
 #include "base/command_line.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
+#include "content/public/browser/gpu_data_manager.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/common/main_function_params.h"
 #include "fuchsia/engine/browser/context_impl.h"
 #include "fuchsia/engine/browser/web_engine_browser_context.h"
 #include "fuchsia/engine/browser/web_engine_screen.h"
 #include "fuchsia/engine/common.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
 #include "ui/aura/screen_ozone.h"
 #include "ui/ozone/public/ozone_platform.h"
 
@@ -42,6 +44,17 @@
 
   display::Screen::SetScreenInstance(screen_.get());
 
+  // If Vulkan is not enabled then disable hardware acceleration. Otherwise gpu
+  // process will be restarted several times trying to initialize GL before
+  // falling back to software compositing.
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kUseVulkan)) {
+    content::GpuDataManager* gpu_data_manager =
+        content::GpuDataManager::GetInstance();
+    DCHECK(gpu_data_manager);
+    gpu_data_manager->DisableHardwareAcceleration();
+  }
+
   DCHECK(!browser_context_);
   browser_context_ = std::make_unique<WebEngineBrowserContext>(
       base::CommandLine::ForCurrentProcess()->HasSwitch(kIncognitoSwitch));
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index 19f3e2cb..5d8d9b2 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "base/base_paths_fuchsia.h"
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/scoped_file.h"
@@ -32,10 +33,13 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "components/viz/common/features.h"
 #include "content/public/common/content_switches.h"
 #include "fuchsia/engine/common.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
 #include "net/http/http_util.h"
 #include "services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h"
+#include "ui/gl/gl_switches.h"
 
 namespace {
 
@@ -192,14 +196,21 @@
                                       base::JoinString(handles_ids, ","));
   }
 
-#if defined(WEB_ENGINE_ENABLE_VULKAN)
-  // TODO(fbx/35009): Add a flag in CreateContextParams to enable/disable Vulkan
-  // and use it here.
-  launch_command.AppendSwitchASCII(
-      "--enable-features", "DefaultEnableOopRasterization,UseSkiaRenderer");
-  launch_command.AppendSwitch("--use-vulkan");
-  launch_command.AppendSwitchASCII("--use-gl", "stub");
-#endif  // WEB_ENGINE_ENABLE_VULKAN
+  fuchsia::web::ContextFeatureFlags features = {};
+  if (params.has_features())
+    features = params.features();
+
+  bool enable_vulkan = (features & fuchsia::web::ContextFeatureFlags::VULKAN) ==
+                       fuchsia::web::ContextFeatureFlags::VULKAN;
+
+  if (enable_vulkan) {
+    launch_command.AppendSwitch(switches::kUseVulkan);
+    launch_command.AppendSwitchASCII(switches::kEnableFeatures,
+                                     features::kUseSkiaRenderer.name);
+    launch_command.AppendSwitch(switches::kEnableOopRasterization);
+    launch_command.AppendSwitchASCII(switches::kUseGL,
+                                     gl::kGLImplementationStubName);
+  }
 
   // Validate embedder-supplied product, and optional version, and pass it to
   // the Context to include in the UserAgent.
diff --git a/fuchsia/runners/cast/cast_runner.cmx b/fuchsia/runners/cast/cast_runner.cmx
index 0905fd8..ddd1367 100644
--- a/fuchsia/runners/cast/cast_runner.cmx
+++ b/fuchsia/runners/cast/cast_runner.cmx
@@ -20,6 +20,7 @@
       "fuchsia.ui.input.ImeService",
       "fuchsia.ui.input.ImeVisibilityService",
       "fuchsia.ui.scenic.Scenic",
+      "fuchsia.vulkan.loader.Loader",
       "fuchsia.web.ContextProvider"
     ]
   }
diff --git a/fuchsia/runners/cast/main.cc b/fuchsia/runners/cast/main.cc
index ecafa69..65c55dd 100644
--- a/fuchsia/runners/cast/main.cc
+++ b/fuchsia/runners/cast/main.cc
@@ -16,7 +16,9 @@
 
   constexpr fuchsia::web::ContextFeatureFlags kCastRunnerFeatures =
       fuchsia::web::ContextFeatureFlags::NETWORK |
-      fuchsia::web::ContextFeatureFlags::AUDIO;
+      fuchsia::web::ContextFeatureFlags::AUDIO |
+      fuchsia::web::ContextFeatureFlags::VULKAN |
+      fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER;
 
   CastRunner runner(
       base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(),
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 418e879..dc4f9c3 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -2867,6 +2867,7 @@
       name: "Win ASan Release"
       dimensions: "os:Windows-10"
       mixins: "fuzz-ci"
+      mixins: "goma-rbe-prod-ats"
     }
     builders {
       name: "ASAN Debug"
@@ -3106,6 +3107,7 @@
       name: "Win ASan Release Media"
       dimensions: "os:Windows-10"
       mixins: "fuzz-ci"
+      mixins: "goma-rbe-prod-ats"
     }
     builders {
       name: "mac-archive-dbg"
@@ -4222,6 +4224,17 @@
       mixins: "builderless"
       name: "linux-chromeos-rel"
     }
+    # This builder replicates linux-chromeos-rel for code coverage experiment.
+    # TODO(crbug.com/1000367): Remove once linux-chromeos-coverage-rel is folded
+    # into linux-chromeos-rel.
+    builders {
+      mixins: "chromeos-try"
+      mixins: "goma-j150"
+      mixins: "builderless"
+      mixins: "code-coverage"
+      mixins: "clang-coverage"
+      name: "linux-chromeos-coverage-rel"
+    }
 
     builders {
       mixins: "linux-try"
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 1340cc5..7c3cb73 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -3241,6 +3241,15 @@
     category: "week13|android"
     short_name: "n5x"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Win ASan Release"
+    category: "win|week1|asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Win ASan Release Media"
+    category: "win|week1|asan"
+    short_name: "media"
+  }
 }
 
 consoles {
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 77c80a8..d7e729a 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -554,6 +554,22 @@
      flag_descriptions::kCollectionsCardPresentationStyleName,
      flag_descriptions::kCollectionsCardPresentationStyleDescription,
      flags_ui::kOsIos, FEATURE_VALUE_TYPE(kCollectionsCardPresentationStyle)},
+    {"enable-autofill-credit-card-upload-editable-cardholder-name",
+     flag_descriptions::
+         kEnableAutofillCreditCardUploadEditableCardholderNameName,
+     flag_descriptions::
+         kEnableAutofillCreditCardUploadEditableCardholderNameDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillUpstreamEditableCardholderName)},
+    {"enable-autofill-credit-card-upload-editable-expiration-date",
+     flag_descriptions::
+         kEnableAutofillCreditCardUploadEditableExpirationDateName,
+     flag_descriptions::
+         kEnableAutofillCreditCardUploadEditableExpirationDateDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillUpstreamEditableExpirationDate)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index ec3f851..69bc7a9 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -149,6 +149,21 @@
 const char kDragAndDropName[] = "Drag and Drop";
 const char kDragAndDropDescription[] = "Enable support for drag and drop.";
 
+const char kEnableAutofillCreditCardUploadEditableCardholderNameName[] =
+    "Make cardholder name editable in dialog during credit card upload";
+const char kEnableAutofillCreditCardUploadEditableCardholderNameDescription[] =
+    "If enabled, in certain situations when offering credit card upload to "
+    "Google Payments, the cardholder name can be edited within the "
+    "offer-to-save dialog, which is prefilled with the name from the signed-in "
+    "Google Account.";
+
+const char kEnableAutofillCreditCardUploadEditableExpirationDateName[] =
+    "Make expiration date editable in dialog during credit card upload";
+const char kEnableAutofillCreditCardUploadEditableExpirationDateDescription[] =
+    "If enabled, if a credit card's expiration date was not detected when "
+    "offering card upload to Google Payments, the offer-to-save dialog "
+    "displays an expiration date selector.";
+
 const char kEnableAutofillSaveCardShowNoThanksName[] =
     "Show explicit decline option in credit card save prompts";
 const char kEnableAutofillSaveCardShowNoThanksDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index e7ddf07..bc83dea 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -117,6 +117,18 @@
 extern const char kDragAndDropName[];
 extern const char kDragAndDropDescription[];
 
+// Title and description for the flag to control if credit card upload can
+// display a cardholder name fix flow.
+extern const char kEnableAutofillCreditCardUploadEditableCardholderNameName[];
+extern const char
+    kEnableAutofillCreditCardUploadEditableCardholderNameDescription[];
+
+// Title and description for the flag to control if credit card upload can
+// display an expiration date fix flow.
+extern const char kEnableAutofillCreditCardUploadEditableExpirationDateName[];
+extern const char
+    kEnableAutofillCreditCardUploadEditableExpirationDateDescription[];
+
 // Title and description for the flag to control if no thanks button should be
 // shown when saving a card.
 extern const char kEnableAutofillSaveCardShowNoThanksName[];
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl.h b/ios/chrome/browser/overlays/overlay_presenter_impl.h
index 8365b4d..d6e095c6 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl.h
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl.h
@@ -52,6 +52,7 @@
       OverlayPresentationContext* presentation_context) override;
   void AddObserver(OverlayPresenterObserver* observer) override;
   void RemoveObserver(OverlayPresenterObserver* observer) override;
+  bool IsShowingOverlayUI() const override;
 
  private:
   // Private constructor used by the container.
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl.mm b/ios/chrome/browser/overlays/overlay_presenter_impl.mm
index a8f09d3..af8d418 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl.mm
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl.mm
@@ -107,6 +107,10 @@
   observers_.RemoveObserver(observer);
 }
 
+bool OverlayPresenterImpl::IsShowingOverlayUI() const {
+  return presenting_;
+}
+
 #pragma mark - Private
 
 #pragma mark Accessors
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl_unittest.mm b/ios/chrome/browser/overlays/overlay_presenter_impl_unittest.mm
index 4322d24..ea68af1 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl_unittest.mm
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl_unittest.mm
@@ -126,6 +126,7 @@
   presenter().SetPresentationContext(&presentation_context());
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 }
 
 // Tests that requested overlays are presented when added to the active queue
@@ -140,6 +141,7 @@
   // Verify that the requested overlay has been presented.
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 }
 
 // Tests that requested overlays are presented when the presentation context is
@@ -159,6 +161,7 @@
   presentation_context().SetIsActive(true);
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 }
 
 // Tests that presented overlay UI is hidden when the presentation context is
@@ -172,11 +175,13 @@
   OverlayRequest* request = AddRequest(active_web_state());
   ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(request));
+  ASSERT_TRUE(presenter().IsShowingOverlayUI());
 
   // Deactivate the presentation context and verify that the UI is hidden.
   presentation_context().SetIsActive(false);
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kHidden,
             presentation_context().GetPresentationState(request));
+  EXPECT_FALSE(presenter().IsShowingOverlayUI());
 }
 
 // Tests resetting the presentation context.  The UI should be cancelled in the
@@ -193,6 +198,7 @@
 
   ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(request));
+  ASSERT_TRUE(presenter().IsShowingOverlayUI());
 
   // Reset the UI delegate and verify that the overlay UI is cancelled in the
   // previous delegate's context and presented in the new delegate's context.
@@ -205,6 +211,7 @@
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             new_presentation_context.GetPresentationState(request));
   EXPECT_EQ(request, queue->front_request());
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 
   // Reset the UI delegate to nullptr and verify that the overlay UI is
   // cancelled in |new_presentation_context|'s context.
@@ -213,6 +220,7 @@
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kCancelled,
             new_presentation_context.GetPresentationState(request));
   EXPECT_EQ(request, queue->front_request());
+  EXPECT_FALSE(presenter().IsShowingOverlayUI());
 }
 
 // Tests changing the active WebState while no overlays are presented over the
@@ -237,6 +245,7 @@
   // Verify that the new active WebState's overlay is being presented.
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 }
 
 // Tests changing the active WebState while is it presenting an overlay.
@@ -250,6 +259,7 @@
   OverlayRequest* first_request = AddRequest(first_web_state);
   ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(first_request));
+  ASSERT_TRUE(presenter().IsShowingOverlayUI());
 
   // Create a new WebState with a queued request and add it as the new active
   // WebState.
@@ -268,6 +278,7 @@
             presentation_context().GetPresentationState(first_request));
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(second_request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 
   // Reactivate the first WebState and verify that its overlay is presented
   // while the second WebState's overlay is hidden.
@@ -278,6 +289,7 @@
             presentation_context().GetPresentationState(first_request));
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kHidden,
             presentation_context().GetPresentationState(second_request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 }
 
 // Tests replacing the active WebState while it is presenting an overlay.
@@ -291,6 +303,7 @@
   OverlayRequest* first_request = AddRequest(first_web_state);
   ASSERT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(first_request));
+  ASSERT_TRUE(presenter().IsShowingOverlayUI());
 
   // Replace |first_web_state| with a new active WebState with a queued request.
   std::unique_ptr<web::WebState> passed_web_state =
@@ -306,6 +319,7 @@
             presentation_context().GetPresentationState(first_request));
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(replacement_request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 }
 
 // Tests removing the active WebState while it is presenting an overlay.
@@ -319,6 +333,7 @@
   OverlayRequest* request = AddRequest(web_state);
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 
   // Remove the WebState and verify that its overlay was cancelled.
   EXPECT_CALL(observer(), DidHideOverlay(&presenter(), request));
@@ -341,6 +356,7 @@
 
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(first_request));
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
   EXPECT_EQ(first_request, queue->front_request());
   EXPECT_EQ(2U, queue->size());
 
@@ -356,6 +372,7 @@
             presentation_context().GetPresentationState(second_request));
   EXPECT_EQ(second_request, queue->front_request());
   EXPECT_EQ(1U, queue->size());
+  EXPECT_TRUE(presenter().IsShowingOverlayUI());
 }
 
 // Tests cancelling the requests.
@@ -372,6 +389,7 @@
 
   EXPECT_EQ(FakeOverlayPresentationContext::PresentationState::kPresented,
             presentation_context().GetPresentationState(active_request));
+  ASSERT_TRUE(presenter().IsShowingOverlayUI());
 
   // Cancel the queue's requests and verify that the UI is also cancelled.
   EXPECT_CALL(observer(), DidHideOverlay(&presenter(), active_request));
diff --git a/ios/chrome/browser/overlays/public/overlay_presenter.h b/ios/chrome/browser/overlays/public/overlay_presenter.h
index fa8d4bc3a..22d7d15 100644
--- a/ios/chrome/browser/overlays/public/overlay_presenter.h
+++ b/ios/chrome/browser/overlays/public/overlay_presenter.h
@@ -33,6 +33,9 @@
   // Adds and removes observers.
   virtual void AddObserver(OverlayPresenterObserver* observer) = 0;
   virtual void RemoveObserver(OverlayPresenterObserver* observer) = 0;
+
+  // Whether overlay UI is currently shown in the presentation context.
+  virtual bool IsShowingOverlayUI() const = 0;
 };
 
 #endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTER_H_
diff --git a/ios/chrome/browser/test/BUILD.gn b/ios/chrome/browser/test/BUILD.gn
index 8dd4f768..d5138a31 100644
--- a/ios/chrome/browser/test/BUILD.gn
+++ b/ios/chrome/browser/test/BUILD.gn
@@ -17,6 +17,7 @@
     "//ios/chrome/browser/autocomplete",
     "//ios/chrome/browser/bookmarks",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/prerender",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/sessions",
diff --git a/ios/chrome/browser/test/perf_test_with_bvc_ios.h b/ios/chrome/browser/test/perf_test_with_bvc_ios.h
index c558b500..8aa0bc3 100644
--- a/ios/chrome/browser/test/perf_test_with_bvc_ios.h
+++ b/ios/chrome/browser/test/perf_test_with_bvc_ios.h
@@ -15,6 +15,7 @@
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
 #include "ios/web/public/test/scoped_testing_web_client.h"
 
+class Browser;
 @class BrowserViewController;
 @class BrowserViewControllerDependencyFactory;
 @class CommandDispatcher;
@@ -54,6 +55,9 @@
   TabModel* tab_model_;
   TabModel* otr_tab_model_;
 
+  std::unique_ptr<Browser> browser_;
+  std::unique_ptr<Browser> otr_browser_;
+
   CommandDispatcher* command_dispatcher_;
   BrowserViewControllerDependencyFactory* bvc_factory_;
   BrowserViewController* bvc_;
diff --git a/ios/chrome/browser/test/perf_test_with_bvc_ios.mm b/ios/chrome/browser/test/perf_test_with_bvc_ios.mm
index ed237fb..183bce9 100644
--- a/ios/chrome/browser/test/perf_test_with_bvc_ios.mm
+++ b/ios/chrome/browser/test/perf_test_with_bvc_ios.mm
@@ -12,6 +12,7 @@
 #include "ios/chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
+#import "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/sessions/session_ios.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
@@ -111,14 +112,18 @@
   [otr_tab_model_ restoreSessionWindow:session.sessionWindows[0]
                      forInitialRestore:YES];
 
+  browser_ =
+      std::make_unique<TestBrowser>(chrome_browser_state_.get(), tab_model_);
+  otr_browser_ = std::make_unique<TestBrowser>(
+      incognito_chrome_browser_state_.get(), otr_tab_model_);
+
   command_dispatcher_ = [[CommandDispatcher alloc] init];
   // Create the browser view controller with its testing factory.
   bvc_factory_ = [[BrowserViewControllerDependencyFactory alloc]
       initWithBrowserState:chrome_browser_state_.get()
               webStateList:[tab_model_ webStateList]];
   bvc_ = [[BrowserViewController alloc]
-                    initWithTabModel:tab_model_
-                        browserState:chrome_browser_state_.get()
+                     initWithBrowser:browser_.get()
                    dependencyFactory:bvc_factory_
           applicationCommandEndpoint:nil
                    commandDispatcher:command_dispatcher_
diff --git a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
index 751adc4..4d5b17fd 100644
--- a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
+++ b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
@@ -412,7 +412,8 @@
   controller->OnUnmaskPromptAccepted(
       base::SysNSStringToUTF16(_CVCItem.CVCText),
       base::SysNSStringToUTF16(_CVCItem.monthText),
-      base::SysNSStringToUTF16(yearText), _storageSwitchItem.on);
+      base::SysNSStringToUTF16(yearText), _storageSwitchItem.on,
+      /*enable_fido_auth=*/false);
 }
 
 - (void)onCancel:(id)sender {
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index dddbf73..1f47f3fd 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -201,8 +201,7 @@
           initWithBrowserState:self.browserState
                   webStateList:self.tabModel.webStateList];
   _viewController = [[BrowserViewController alloc]
-                    initWithTabModel:self.tabModel
-                        browserState:self.browserState
+                     initWithBrowser:self.browser
                    dependencyFactory:factory
           applicationCommandEndpoint:self.applicationCommandHandler
                    commandDispatcher:self.dispatcher
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.h b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
index 2834491c..ced0d77 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
@@ -13,6 +13,7 @@
 #import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
 #import "ios/public/provider/chrome/browser/voice/logo_animation_controller.h"
 
+class Browser;
 @protocol ApplicationCommands;
 @protocol BrowserCommands;
 @class BrowserContainerViewController;
@@ -41,8 +42,8 @@
 // webUsageSuspended property for this BVC will be based on |model|, and future
 // changes to |model|'s suspension state should be made through this BVC
 // instead of directly on the model.
-- (instancetype)initWithTabModel:(TabModel*)model
-                      browserState:(ios::ChromeBrowserState*)browserState
+// TODO(crbug.com/992582): Remove references to model objects from this class.
+- (instancetype)initWithBrowser:(Browser*)browser
                  dependencyFactory:
                      (BrowserViewControllerDependencyFactory*)factory
         applicationCommandEndpoint:
@@ -75,10 +76,10 @@
 // Returns whether or not text to speech is playing.
 @property(nonatomic, assign, readonly, getter=isPlayingTTS) BOOL playingTTS;
 
-// Returns the TabModel passed to the initializer.
+// The Browser's TabModel.
 @property(nonatomic, weak, readonly) TabModel* tabModel;
 
-// Returns the ios::ChromeBrowserState passed to the initializer.
+// The Browser's ChromeBrowserState.
 @property(nonatomic, assign, readonly) ios::ChromeBrowserState* browserState;
 
 // Whether the receiver is currently the primary BVC.
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 76eab36..862afe1f 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -41,6 +41,7 @@
 #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/language/url_language_histogram_factory.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/metrics/new_tab_page_uma.h"
 #import "ios/chrome/browser/metrics/size_class_recorder.h"
 #include "ios/chrome/browser/metrics/tab_usage_recorder.h"
@@ -374,12 +375,6 @@
   // Identifier for each animation of an NTP opening.
   NSInteger _NTPAnimationIdentifier;
 
-  // Backing ivar for the public property, strong even though the property is
-  // weak, because things explode otherwise.
-  // Do not directly access this ivar outside of object initialization; use the
-  // -tabModel property.
-  TabModel* _strongTabModel;
-
   // Facade objects used by |_toolbarCoordinator|.
   // Must outlive |_toolbarCoordinator|.
   std::unique_ptr<LocationBarModelDelegateIOS> _locationBarModelDelegate;
@@ -428,9 +423,6 @@
   // Whether or not -shutdown has been called.
   BOOL _isShutdown;
 
-  // The ChromeBrowserState associated with this BVC.
-  ios::ChromeBrowserState* _browserState;  // weak
-
   // Whether or not Incognito* is enabled.
   BOOL _isOffTheRecord;
 
@@ -501,6 +493,8 @@
 // not active, the UI will not react to changes in the tab model, so generally
 // an inactive BVC should not be visible.
 @property(nonatomic, assign, getter=isActive) BOOL active;
+// The Browser whose UI is managed by this instance.
+@property(nonatomic, assign) Browser* browser;
 // Browser container view controller.
 @property(nonatomic, strong)
     BrowserContainerViewController* browserContainerViewController;
@@ -627,14 +621,13 @@
 // In most cases, they will not, to improve startup performance.
 // In order to handle this, initialization of various aspects of BVC have been
 // broken out into the following functions, which have expectations (enforced
-// with DCHECKs) regarding |_browserState|, |self.tabModel|, and [self
+// with DCHECKs) regarding |self.browserState|, |self.tabModel|, and [self
 // isViewLoaded].
 
-// Updates non-view-related functionality with the given browser state and tab
+// Updates non-view-related functionality with the given browser and tab
 // model.
 // Does not matter whether or not the view has been loaded.
-- (void)updateWithTabModel:(TabModel*)model
-              browserState:(ios::ChromeBrowserState*)browserState;
+- (void)updateWithBrowser:(Browser*)browser;
 // On iOS7, iPad should match iOS6 status bar.  Install a simple black bar under
 // the status bar to mimic this layout.
 - (void)installFakeStatusBar;
@@ -644,7 +637,7 @@
 // Sets up the constraints on the toolbar.
 - (void)addConstraintsToToolbar;
 // Updates view-related functionality with the given tab model and browser
-// state. The view must have been loaded.  Uses |_browserState| and
+// state. The view must have been loaded.  Uses |self.browserState| and
 // |self.tabModel|.
 - (void)addUIFunctionalityForModelAndBrowserState;
 // Sets the correct frame and hierarchy for subviews and helper views.  Only
@@ -753,8 +746,7 @@
 
 #pragma mark - Object lifecycle
 
-- (instancetype)initWithTabModel:(TabModel*)model
-                      browserState:(ios::ChromeBrowserState*)browserState
+- (instancetype)initWithBrowser:(Browser*)browser
                  dependencyFactory:
                      (BrowserViewControllerDependencyFactory*)factory
         applicationCommandEndpoint:
@@ -816,8 +808,8 @@
     _footerFullscreenProgress = 1.0;
 
     _observer = [[KeyboardObserverHelper alloc] init];
-    if (model && browserState)
-      [self updateWithTabModel:model browserState:browserState];
+    if (browser)
+      [self updateWithBrowser:browser];
   }
   return self;
 }
@@ -861,11 +853,11 @@
 }
 
 - (TabModel*)tabModel {
-  return _strongTabModel;
+  return self.browser ? self.browser->GetTabModel() : nil;
 }
 
 - (ios::ChromeBrowserState*)browserState {
-  return _browserState;
+  return self.browser ? self.browser->GetBrowserState() : nullptr;
 }
 
 #pragma mark - Private Properties
@@ -874,7 +866,7 @@
   if (!_sideSwipeController) {
     _sideSwipeController =
         [[SideSwipeController alloc] initWithTabModel:self.tabModel
-                                         browserState:_browserState];
+                                         browserState:self.browserState];
     [_sideSwipeController setSnapshotDelegate:self];
     _sideSwipeController.toolbarInteractionHandler = self.toolbarInterface;
     _sideSwipeController.primaryToolbarSnapshotProvider =
@@ -950,7 +942,7 @@
   // supported.
   FullscreenController* fullscreenController =
       FullscreenControllerFactory::GetInstance()->GetForBrowserState(
-          _browserState);
+          self.browserState);
   ChromeBroadcaster* broadcaster = fullscreenController->broadcaster();
   if (_broadcasting) {
     _toolbarUIUpdater = [[LegacyToolbarUIUpdater alloc]
@@ -989,17 +981,17 @@
 }
 
 - (BOOL)isWebUsageEnabled {
-  return _browserState && !_isShutdown &&
+  return self.browserState && !_isShutdown &&
          WebStateListWebUsageEnablerFactory::GetInstance()
-             ->GetForBrowserState(_browserState)
+             ->GetForBrowserState(self.browserState)
              ->IsWebUsageEnabled();
 }
 
 - (void)setWebUsageEnabled:(BOOL)webUsageEnabled {
-  if (!_browserState || _isShutdown)
+  if (!self.browserState || _isShutdown)
     return;
   WebStateListWebUsageEnablerFactory::GetInstance()
-      ->GetForBrowserState(_browserState)
+      ->GetForBrowserState(self.browserState)
       ->SetWebUsageEnabled(webUsageEnabled);
 }
 
@@ -1254,13 +1246,13 @@
     [self.activityOverlayCoordinator start];
   }
 
-  if (_browserState) {
+  if (self.browserState) {
     ActiveStateManager* active_state_manager =
-        ActiveStateManager::FromBrowserState(_browserState);
+        ActiveStateManager::FromBrowserState(self.browserState);
     active_state_manager->SetActive(active);
 
     TextToSpeechPlaybackControllerFactory::GetInstance()
-        ->GetForBrowserState(_browserState)
+        ->GetForBrowserState(self.browserState)
         ->SetEnabled(_active);
   }
 
@@ -1369,7 +1361,7 @@
     self.inNewTabAnimation = YES;
     // Exit fullscreen if needed.
     FullscreenControllerFactory::GetInstance()
-        ->GetForBrowserState(_browserState)
+        ->GetForBrowserState(self.browserState)
         ->ExitFullscreen();
     const CGFloat kAnimatedViewSize = 50;
     BackgroundTabAnimationView* animatedView =
@@ -1396,21 +1388,21 @@
   [_paymentRequestManager close];
   _paymentRequestManager = nil;
 
-  if (_browserState) {
+  if (self.browserState) {
     TextToSpeechPlaybackController* controller =
         TextToSpeechPlaybackControllerFactory::GetInstance()
-            ->GetForBrowserState(_browserState);
+            ->GetForBrowserState(self.browserState);
     if (controller)
       controller->SetWebStateList(nullptr);
 
     WebStateListWebUsageEnabler* webUsageEnabler =
         WebStateListWebUsageEnablerFactory::GetInstance()->GetForBrowserState(
-            _browserState);
+            self.browserState);
     if (webUsageEnabler)
       webUsageEnabler->SetWebStateList(nullptr);
 
     UrlLoadingNotifier* urlLoadingNotifier =
-        UrlLoadingNotifierFactory::GetForBrowserState(_browserState);
+        UrlLoadingNotifierFactory::GetForBrowserState(self.browserState);
     if (urlLoadingNotifier)
       urlLoadingNotifier->RemoveObserver(_URLLoadingObserverBridge.get());
   }
@@ -1428,7 +1420,8 @@
   self.tabStripCoordinator = nil;
   self.tabStripView = nil;
 
-  _browserState = nullptr;
+  self.tabModel.webStateList->RemoveObserver(_webStateListObserver.get());
+  self.browser = nullptr;
   self.bubblePresenter = nil;
 
   [self.commandDispatcher stopDispatchingToTarget:self];
@@ -1448,7 +1441,6 @@
   // SideSwipeController is a tab model observer, so it needs to stop observing
   // before self.tabModel is released.
   _sideSwipeController = nil;
-  self.tabModel.webStateList->RemoveObserver(_webStateListObserver.get());
   _webStateListObserver.reset();
   _allWebStateObservationForwarder = nullptr;
   if (_voiceSearchController)
@@ -1534,7 +1526,7 @@
   [self addConstraintsToToolbar];
 
   // If the tab model and browser state are valid, finish initialization.
-  if (self.tabModel && _browserState)
+  if (self.tabModel && self.browserState)
     [self addUIFunctionalityForModelAndBrowserState];
 
   // Add a tap gesture recognizer to save the last tap location for the source
@@ -1626,7 +1618,7 @@
   [self updateDialogPresenterActiveState];
   [self updateBroadcastState];
   web::WebState* activeWebState =
-      self.tabModel.webStateList->GetActiveWebState();
+      self.tabModel ? self.tabModel.webStateList->GetActiveWebState() : nullptr;
   if (activeWebState) {
     activeWebState->WasHidden();
     if (!self.presentedViewController)
@@ -1680,13 +1672,13 @@
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
   [super traitCollectionDidChange:previousTraitCollection];
 
-  // After |-shutdown| is called, |_browserState| is invalid and will cause a
-  // crash.
-  if (!_browserState || _isShutdown)
+  // After |-shutdown| is called, |self.browserState| is invalid and will cause
+  // a crash.
+  if (!self.browserState || _isShutdown)
     return;
 
   FullscreenControllerFactory::GetInstance()
-      ->GetForBrowserState(_browserState)
+      ->GetForBrowserState(self.browserState)
       ->BrowserTraitCollectionChangedBegin();
 
   // TODO(crbug.com/527092): - traitCollectionDidChange: is not always forwarded
@@ -1734,7 +1726,7 @@
   [self setNeedsStatusBarAppearanceUpdate];
 
   FullscreenControllerFactory::GetInstance()
-      ->GetForBrowserState(_browserState)
+      ->GetForBrowserState(self.browserState)
       ->BrowserTraitCollectionChangedEnd();
 }
 
@@ -1900,17 +1892,13 @@
 
 #pragma mark - Private Methods: BVC Initialization
 
-- (void)updateWithTabModel:(TabModel*)model
-              browserState:(ios::ChromeBrowserState*)browserState {
-  DCHECK(model);
-  DCHECK(browserState);
-  DCHECK(!self.tabModel);
-  DCHECK(!_browserState);
-  _browserState = browserState;
-  _isOffTheRecord = browserState->IsOffTheRecord() ? YES : NO;
-  _strongTabModel = model;
+- (void)updateWithBrowser:(Browser*)browser {
+  DCHECK(browser);
+  DCHECK(!self.browser);
+  self.browser = browser;
+  _isOffTheRecord = self.browserState->IsOffTheRecord();
   WebStateListWebUsageEnablerFactory::GetInstance()
-      ->GetForBrowserState(_browserState)
+      ->GetForBrowserState(self.browserState)
       ->SetWebStateList(self.tabModel.webStateList);
 
   _webStateObserverBridge = std::make_unique<web::WebStateObserverBridge>(self);
@@ -1922,7 +1910,7 @@
   self.tabModel.webStateList->AddObserver(_webStateListObserver.get());
   _URLLoadingObserverBridge = std::make_unique<UrlLoadingObserverBridge>(self);
   UrlLoadingNotifier* urlLoadingNotifier =
-      UrlLoadingNotifierFactory::GetForBrowserState(_browserState);
+      UrlLoadingNotifierFactory::GetForBrowserState(self.browserState);
   urlLoadingNotifier->AddObserver(_URLLoadingObserverBridge.get());
 
   WebStateList* webStateList = self.tabModel.webStateList;
@@ -1934,7 +1922,7 @@
 
   // Set the TTS playback controller's WebStateList.
   TextToSpeechPlaybackControllerFactory::GetInstance()
-      ->GetForBrowserState(_browserState)
+      ->GetForBrowserState(self.browserState)
       ->SetWebStateList(self.tabModel.webStateList);
 
   // When starting the browser with an open tab, it is necessary to reset the
@@ -1980,7 +1968,7 @@
   self.helper = [_dependencyFactory newBrowserViewControllerHelper];
 
   PrimaryToolbarCoordinator* topToolbarCoordinator =
-      [[PrimaryToolbarCoordinator alloc] initWithBrowserState:_browserState];
+      [[PrimaryToolbarCoordinator alloc] initWithBrowser:self.browser];
   self.primaryToolbarCoordinator = topToolbarCoordinator;
   topToolbarCoordinator.delegate = self;
   topToolbarCoordinator.popupPresenterDelegate = self;
@@ -1991,7 +1979,7 @@
   [topToolbarCoordinator start];
 
   SecondaryToolbarCoordinator* bottomToolbarCoordinator =
-      [[SecondaryToolbarCoordinator alloc] initWithBrowserState:_browserState];
+      [[SecondaryToolbarCoordinator alloc] initWithBrowser:self.browser];
   self.secondaryToolbarCoordinator = bottomToolbarCoordinator;
   bottomToolbarCoordinator.webStateList = self.tabModel.webStateList;
   bottomToolbarCoordinator.dispatcher = self.dispatcher;
@@ -2027,7 +2015,7 @@
   if (IsIPadIdiom()) {
     self.tabStripCoordinator =
         [[TabStripLegacyCoordinator alloc] initWithBaseViewController:self];
-    self.tabStripCoordinator.browserState = _browserState;
+    self.tabStripCoordinator.browserState = self.browserState;
     self.tabStripCoordinator.dispatcher = self.commandDispatcher;
     self.tabStripCoordinator.tabModel = self.tabModel;
     self.tabStripCoordinator.presentationProvider = self;
@@ -2044,7 +2032,7 @@
   // Create the Infobar Container Coordinator.
   self.infobarContainerCoordinator = [[InfobarContainerCoordinator alloc]
       initWithBaseViewController:self
-                    browserState:_browserState
+                    browserState:self.browserState
                     webStateList:self.tabModel.webStateList];
   self.infobarContainerCoordinator.commandDispatcher = self.dispatcher;
   self.infobarContainerCoordinator.positioner = self;
@@ -2209,7 +2197,7 @@
 // Enable functionality that only makes sense if the views are loaded and
 // both browser state and tab model are valid.
 - (void)addUIFunctionalityForModelAndBrowserState {
-  DCHECK(_browserState);
+  DCHECK(self.browserState);
   DCHECK(_locationBarModel);
   DCHECK(self.tabModel);
   DCHECK([self isViewLoaded]);
@@ -2221,7 +2209,7 @@
       initWithBaseViewController:self];
   _activityServiceCoordinator.dispatcher = self.commandDispatcher;
   _activityServiceCoordinator.tabModel = self.tabModel;
-  _activityServiceCoordinator.browserState = _browserState;
+  _activityServiceCoordinator.browserState = self.browserState;
   _activityServiceCoordinator.positionProvider =
       [self.primaryToolbarCoordinator activityServicePositioner];
   _activityServiceCoordinator.presentationProvider = self;
@@ -2233,9 +2221,9 @@
       [NamedGuide guideWithName:kSecondaryToolbarGuide view:self.contentArea]
           .heightAnchor;
 
-  self.popupMenuCoordinator = [[PopupMenuCoordinator alloc]
-      initWithBaseViewController:self
-                    browserState:self.browserState];
+  self.popupMenuCoordinator =
+      [[PopupMenuCoordinator alloc] initWithBaseViewController:self
+                                                       browser:self.browser];
   self.popupMenuCoordinator.bubblePresenter = self.bubblePresenter;
   self.popupMenuCoordinator.dispatcher = self.commandDispatcher;
   self.popupMenuCoordinator.webStateList = self.tabModel.webStateList;
@@ -2249,7 +2237,7 @@
 
   _sadTabCoordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:self.browserContainerViewController
-                    browserState:_browserState];
+                    browserState:self.browserState];
   _sadTabCoordinator.dispatcher = self.dispatcher;
   _sadTabCoordinator.overscrollDelegate = self;
 
@@ -2265,7 +2253,7 @@
 
   _paymentRequestManager = [[PaymentRequestManager alloc]
       initWithBaseViewController:self
-                    browserState:_browserState
+                    browserState:self.browserState
                       dispatcher:self.dispatcher];
   [_paymentRequestManager setLocationBarModel:_locationBarModel.get()];
   [_paymentRequestManager setActiveWebState:self.currentWebState];
@@ -2467,7 +2455,7 @@
   if (_bookmarkInteractionController)
     return;
   _bookmarkInteractionController = [[BookmarkInteractionController alloc]
-      initWithBrowserState:_browserState
+      initWithBrowserState:self.browserState
           parentController:self
                 dispatcher:self.dispatcher
               webStateList:self.tabModel.webStateList];
@@ -2495,7 +2483,7 @@
 - (void)updateToolbar {
   // If the BVC has been partially torn down for low memory, wait for the
   // view rebuild to handle toolbar updates.
-  if (!(self.helper && _browserState))
+  if (!(self.helper && self.browserState))
     return;
 
   web::WebState* webState = self.currentWebState;
@@ -2714,7 +2702,7 @@
 
   // There should be no pre-rendered Tabs in TabModel.
   PrerenderService* prerenderService =
-      PrerenderServiceFactory::GetForBrowserState(_browserState);
+      PrerenderServiceFactory::GetForBrowserState(self.browserState);
   DCHECK(!prerenderService ||
          !prerenderService->IsWebStatePrerendered(webState));
 
@@ -2743,7 +2731,8 @@
 
   if (reading_list::IsOfflinePageWithoutNativeContentEnabled()) {
     OfflinePageTabHelper::CreateForWebState(
-        webState, ReadingListModelFactory::GetForBrowserState(_browserState));
+        webState,
+        ReadingListModelFactory::GetForBrowserState(self.browserState));
   }
 
   // DownloadManagerTabHelper cannot function without delegate.
@@ -2836,7 +2825,7 @@
         ios::GetChromeBrowserProvider()->GetVoiceSearchProvider();
     if (provider) {
       _voiceSearchController =
-          provider->CreateVoiceSearchController(_browserState);
+          provider->CreateVoiceSearchController(self.browserState);
       if (self.primaryToolbarCoordinator) {
         _voiceSearchController->SetDispatcher(
             static_cast<id<LoadQueryCommands>>(self.commandDispatcher));
@@ -2851,7 +2840,7 @@
   base::RecordAction(UserMetricsAction("MobileReadingListAdd"));
 
   ReadingListModel* readingModel =
-      ReadingListModelFactory::GetForBrowserState(_browserState);
+      ReadingListModelFactory::GetForBrowserState(self.browserState);
   readingModel->AddEntry(URL, base::SysNSStringToUTF8(title),
                          reading_list::ADDED_VIA_CURRENT_APP);
 
@@ -2866,7 +2855,7 @@
 
 - (void)sendTabToSelfTargetDeviceID:(NSString*)targetDeviceID
                    targetDeviceName:(NSString*)targetDeviceName {
-  send_tab_to_self::CreateNewEntry(_browserState, targetDeviceID);
+  send_tab_to_self::CreateNewEntry(self.browserState, targetDeviceID);
 
   [self.dispatcher triggerToolsMenuButtonAnimation];
 
@@ -3164,7 +3153,7 @@
     return;
   }
 
-  DCHECK(_browserState);
+  DCHECK(self.browserState);
 
   _contextMenuCoordinator = [[ContextMenuCoordinator alloc]
       initWithBaseViewController:self
@@ -3315,7 +3304,7 @@
     [_contextMenuCoordinator addItemWithTitle:title action:action];
 
     TemplateURLService* service =
-        ios::TemplateURLServiceFactory::GetForBrowserState(_browserState);
+        ios::TemplateURLServiceFactory::GetForBrowserState(self.browserState);
     if (search_engines::SupportsSearchByImage(service)) {
       const TemplateURL* defaultURL = service->GetDefaultSearchProvider();
       title = l10n_util::GetNSStringF(IDS_IOS_CONTEXT_MENU_SEARCHWEBFORIMAGE,
@@ -3422,7 +3411,8 @@
   web::NavigationManager::WebLoadParams loadParams =
       ImageSearchParamGenerator::LoadParamsForImageData(
           data, imageURL,
-          ios::TemplateURLServiceFactory::GetForBrowserState(_browserState));
+          ios::TemplateURLServiceFactory::GetForBrowserState(
+              self.browserState));
   [self searchByImageWithWebLoadParams:loadParams inNewTab:YES];
 }
 
@@ -3480,7 +3470,8 @@
   if (isUserInitiated) {
     // Send either the "New Tab Opened" or "New Incognito Tab" opened to the
     // feature_engagement::Tracker based on |inIncognito|.
-    feature_engagement::NotifyNewTabEvent(_browserState, self.isOffTheRecord);
+    feature_engagement::NotifyNewTabEvent(self.browserState,
+                                          self.isOffTheRecord);
   }
 }
 
@@ -3668,7 +3659,7 @@
       !reading_list::IsOfflinePageWithoutNativeContentEnabled()) {
     // Only allow offline URL that are fully specified.
     return reading_list::IsOfflineURLValid(
-        url, ReadingListModelFactory::GetForBrowserState(_browserState));
+        url, ReadingListModelFactory::GetForBrowserState(self.browserState));
   }
   return NO;
 }
@@ -3682,7 +3673,7 @@
   if (!reading_list::IsOfflinePageWithoutNativeContentEnabled() &&
       url_host == kChromeUIOfflineHost && [self hasControllerForURL:url]) {
     StaticHtmlNativeContent* staticNativeController =
-        [[OfflinePageNativeContent alloc] initWithBrowserState:_browserState
+        [[OfflinePageNativeContent alloc] initWithBrowserState:self.browserState
                                                       webState:webState
                                                            URL:url];
     [self setOverScrollActionControllerToStaticNativeContent:
@@ -3971,7 +3962,7 @@
 - (UIEdgeInsets)viewportInsetsForView:(UIView*)view {
   DCHECK(view);
   UIEdgeInsets viewportInsets = FullscreenControllerFactory::GetInstance()
-                                    ->GetForBrowserState(_browserState)
+                                    ->GetForBrowserState(self.browserState)
                                     ->GetCurrentViewportInsets();
   // TODO(crbug.com/917548): Use BVC for viewport inset coordinate space rather
   // than the content area.
@@ -4020,38 +4011,45 @@
 }
 
 - (void)focusNextTab {
-  int activeIndex = self.tabModel.webStateList->active_index();
+  WebStateList* webStateList = self.tabModel.webStateList;
+  if (!webStateList)
+    return;
+
+  int activeIndex = webStateList->active_index();
   if (activeIndex == WebStateList::kInvalidIndex)
     return;
 
   // If the active index isn't the last index, activate the next index.
   // (the last index is always |count() - 1|).
   // Otherwise activate the first index.
-  if (activeIndex < (self.tabModel.webStateList->count() - 1)) {
-    self.tabModel.webStateList->ActivateWebStateAt(activeIndex + 1);
+  if (activeIndex < (webStateList->count() - 1)) {
+    webStateList->ActivateWebStateAt(activeIndex + 1);
   } else {
-    self.tabModel.webStateList->ActivateWebStateAt(0);
+    webStateList->ActivateWebStateAt(0);
   }
 }
 
 - (void)focusPreviousTab {
-  int activeIndex = self.tabModel.webStateList->active_index();
+  WebStateList* webStateList = self.tabModel.webStateList;
+  if (!webStateList)
+    return;
+
+  int activeIndex = webStateList->active_index();
   if (activeIndex == WebStateList::kInvalidIndex)
     return;
 
   // If the active index isn't the first index, activate the prior index.
   // Otherwise index the last index (|count() - 1|).
   if (activeIndex > 0) {
-    self.tabModel.webStateList->ActivateWebStateAt(activeIndex - 1);
+    webStateList->ActivateWebStateAt(activeIndex - 1);
   } else {
-    self.tabModel.webStateList->ActivateWebStateAt(
-        self.tabModel.webStateList->count() - 1);
+    webStateList->ActivateWebStateAt(webStateList->count() - 1);
   }
 }
 
 - (void)reopenClosedTab {
   sessions::TabRestoreService* const tabRestoreService =
-      IOSChromeTabRestoreServiceFactory::GetForBrowserState(_browserState);
+      IOSChromeTabRestoreServiceFactory::GetForBrowserState(self.browserState);
   if (!tabRestoreService || tabRestoreService->entries().empty())
     return;
 
@@ -4091,7 +4089,7 @@
                      }];
   }
   [[OmniboxGeolocationController sharedInstance]
-      locationBarDidBecomeFirstResponder:_browserState];
+      locationBarDidBecomeFirstResponder:self.browserState];
 
   [self.primaryToolbarCoordinator transitionToLocationBarFocusedState:YES];
 }
@@ -4116,7 +4114,7 @@
         [self.typingShield setHidden:YES];
       }];
   [[OmniboxGeolocationController sharedInstance]
-      locationBarDidResignFirstResponder:_browserState];
+      locationBarDidResignFirstResponder:self.browserState];
 
   // If a load was cancelled by an omnibox edit, but nothing is loading when
   // editing ends (i.e., editing was cancelled), restart the cancelled load.
@@ -4334,15 +4332,18 @@
 }
 
 - (void)closeCurrentTab {
-  int active_index = self.tabModel.webStateList->active_index();
+  WebStateList* webStateList = self.tabModel.webStateList;
+  if (!webStateList)
+    return;
+
+  int active_index = webStateList->active_index();
   if (active_index == WebStateList::kInvalidIndex)
     return;
 
   UIView* snapshotView = [self.contentArea snapshotViewAfterScreenUpdates:NO];
   snapshotView.frame = self.contentArea.frame;
 
-  self.tabModel.webStateList->CloseWebStateAt(active_index,
-                                              WebStateList::CLOSE_USER_ACTION);
+  webStateList->CloseWebStateAt(active_index, WebStateList::CLOSE_USER_ACTION);
 
   if (![self canShowTabStrip]) {
     [self.contentArea addSubview:snapshotView];
@@ -4353,7 +4354,7 @@
 }
 
 - (void)prepareForPopupMenuPresentation:(PopupMenuCommandType)type {
-  DCHECK(_browserState);
+  DCHECK(self.browserState);
   DCHECK(self.visible || self.dismissingModal);
 
   // Dismiss the omnibox (if open).
@@ -4378,7 +4379,7 @@
   [self searchByImageWithWebLoadParams:
             ImageSearchParamGenerator::LoadParamsForImage(
                 image, ios::TemplateURLServiceFactory::GetForBrowserState(
-                           _browserState))
+                           self.browserState))
                               inNewTab:NO];
 }
 
@@ -4985,7 +4986,7 @@
   if (NTPHelper->IsActive()) {
     DCHECK(!_ntpCoordinatorsForWebStates[webState]);
     NewTabPageCoordinator* newTabPageCoordinator =
-        [[NewTabPageCoordinator alloc] initWithBrowserState:_browserState];
+        [[NewTabPageCoordinator alloc] initWithBrowserState:self.browserState];
     newTabPageCoordinator.dispatcher = self.dispatcher;
     newTabPageCoordinator.toolbarDelegate = self.toolbarInterface;
     newTabPageCoordinator.webState = webState;
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
index e78ae3a..347a6c8 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
@@ -13,6 +13,7 @@
 #include "components/search_engines/template_url_service.h"
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
+#import "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
 #import "ios/chrome/browser/tabs/tab_helper_util.h"
@@ -134,6 +135,9 @@
     [[tabModel stub] saveSessionImmediately:NO];
     [[tabModel stub] closeAllTabs];
 
+    browser_ =
+        std::make_unique<TestBrowser>(chrome_browser_state_.get(), tabModel_);
+
     // Create three web states.
     for (int i = 0; i < 3; i++) {
       web::WebState::CreateParams params(chrome_browser_state_.get());
@@ -152,8 +156,7 @@
 
     // Instantiate the BVC.
     bvc_ = [[BrowserViewController alloc]
-                      initWithTabModel:tabModel_
-                          browserState:chrome_browser_state_.get()
+                       initWithBrowser:browser_.get()
                      dependencyFactory:factory
             applicationCommandEndpoint:mockApplicationCommandHandler
                      commandDispatcher:command_dispatcher_
@@ -188,6 +191,7 @@
   IOSChromeScopedTestingLocalState local_state_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   TabModel* tabModel_;
+  std::unique_ptr<Browser> browser_;
   BrowserViewControllerHelper* bvcHelper_;
   PKAddPassesViewController* passKitViewController_;
   OCMockObject* dependencyFactory_;
diff --git a/ios/chrome/browser/ui/dialogs/BUILD.gn b/ios/chrome/browser/ui/dialogs/BUILD.gn
index 4ca0224..c68a978e 100644
--- a/ios/chrome/browser/ui/dialogs/BUILD.gn
+++ b/ios/chrome/browser/ui/dialogs/BUILD.gn
@@ -52,6 +52,8 @@
   sources = [
     "dialog_presenter.h",
     "dialog_presenter.mm",
+    "java_script_dialog_metrics.cc",
+    "java_script_dialog_metrics.h",
     "java_script_dialog_presenter_impl.h",
     "java_script_dialog_presenter_impl.mm",
     "nsurl_protection_space_util.h",
diff --git a/ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.cc b/ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.cc
new file mode 100644
index 0000000..fff6a89
--- /dev/null
+++ b/ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.cc
@@ -0,0 +1,12 @@
+// 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 "ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.h"
+
+#include "base/metrics/histogram_macros.h"
+
+// Records a histogram for a dialog dismissal for |cause|.
+void RecordDialogDismissalCause(IOSJavaScriptDialogDismissalCause cause) {
+  UMA_HISTOGRAM_ENUMERATION("IOS.Dialogs.JavaScriptDialogClosed", cause);
+}
diff --git a/ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.h b/ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.h
new file mode 100644
index 0000000..3567007b
--- /dev/null
+++ b/ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.h
@@ -0,0 +1,27 @@
+// 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 IOS_CHROME_BROWSER_UI_DIALOGS_JAVA_SCRIPT_DIALOG_METRICS_H_
+#define IOS_CHROME_BROWSER_UI_DIALOGS_JAVA_SCRIPT_DIALOG_METRICS_H_
+
+// Possible reasons for JavaScript dialog dismissals.  These values are
+// persisted to logs. Entries should not be renumbered and numeric values should
+// never be reused.
+enum class IOSJavaScriptDialogDismissalCause {
+  // The tab was closed while a JavaScript dialog was displayed.
+  kClosure = 0,
+  // The user taps the OK, Cancel, or Suppress Dialogs button.
+  kUser = 1,
+  // The dialog was blocked by the Suppress Dialog option.
+  kBlocked = 2,
+  // The dialog was closed due to navigation.
+  kNavigation = 3,
+
+  kMaxValue = kNavigation,
+};
+
+// Records a histogram for a dialog dismissal for |cause|.
+void RecordDialogDismissalCause(IOSJavaScriptDialogDismissalCause cause);
+
+#endif  // IOS_CHROME_BROWSER_UI_DIALOGS_JAVA_SCRIPT_DIALOG_METRICS_H_
diff --git a/ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.mm b/ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.mm
index 7a98cc6..d1e51248 100644
--- a/ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.mm
+++ b/ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.mm
@@ -7,6 +7,7 @@
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/dialogs/dialog_presenter.h"
 #import "ios/chrome/browser/ui/dialogs/java_script_dialog_blocking_state.h"
+#include "ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.h"
 #include "ui/gfx/text_elider.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -31,6 +32,7 @@
   JavaScriptDialogBlockingState::CreateForWebState(web_state);
   if (JavaScriptDialogBlockingState::FromWebState(web_state)->blocked()) {
     // Block the dialog if needed.
+    RecordDialogDismissalCause(IOSJavaScriptDialogDismissalCause::kBlocked);
     std::move(callback).Run(NO, nil);
     return;
   }
@@ -39,15 +41,17 @@
   switch (dialog_type) {
     case web::JAVASCRIPT_DIALOG_TYPE_ALERT: {
       __block web::DialogClosedCallback scoped_callback = std::move(callback);
-      [dialog_presenter_ runJavaScriptAlertPanelWithMessage:message_text
-                                                 requestURL:origin_url
-                                                   webState:web_state
-                                          completionHandler:^{
-                                            if (!scoped_callback.is_null()) {
-                                              std::move(scoped_callback)
-                                                  .Run(YES, nil);
-                                            }
-                                          }];
+      [dialog_presenter_
+          runJavaScriptAlertPanelWithMessage:message_text
+                                  requestURL:origin_url
+                                    webState:web_state
+                           completionHandler:^{
+                             RecordDialogDismissalCause(
+                                 IOSJavaScriptDialogDismissalCause::kUser);
+                             if (!scoped_callback.is_null()) {
+                               std::move(scoped_callback).Run(YES, nil);
+                             }
+                           }];
       break;
     }
     case web::JAVASCRIPT_DIALOG_TYPE_CONFIRM: {
@@ -57,6 +61,8 @@
                                     requestURL:origin_url
                                       webState:web_state
                              completionHandler:^(BOOL is_confirmed) {
+                               RecordDialogDismissalCause(
+                                   IOSJavaScriptDialogDismissalCause::kUser);
                                if (!scoped_callback.is_null()) {
                                  std::move(scoped_callback)
                                      .Run(is_confirmed, nil);
@@ -72,6 +78,8 @@
                                      requestURL:origin_url
                                        webState:web_state
                               completionHandler:^(NSString* text_input) {
+                                RecordDialogDismissalCause(
+                                    IOSJavaScriptDialogDismissalCause::kUser);
                                 if (!scoped_callback.is_null()) {
                                   std::move(scoped_callback)
                                       .Run(YES, text_input);
diff --git a/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h b/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h
index 7ea8ab4..b479d56f 100644
--- a/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h
+++ b/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h
@@ -19,6 +19,9 @@
   OverlayJavaScriptDialogPresenter();
   ~OverlayJavaScriptDialogPresenter() override;
 
+  // Notifies the presenter that the presenter that its tab is being closed.
+  void Close();
+
   // web::JavaScriptDialogPresenter:
   void RunJavaScriptDialog(web::WebState* web_state,
                            const GURL& origin_url,
@@ -37,6 +40,7 @@
 
   DISALLOW_COPY_AND_ASSIGN(OverlayJavaScriptDialogPresenter);
 
+  bool closing_ = false;
   base::WeakPtrFactory<OverlayJavaScriptDialogPresenter> weak_factory_;
 };
 
diff --git a/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.mm b/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.mm
index 8b8b087..1ab74be 100644
--- a/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.mm
+++ b/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h"
 
 #include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
 #import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/overlays/public/overlay_request.h"
 #import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
@@ -13,6 +14,7 @@
 #import "ios/chrome/browser/overlays/public/web_content_area/java_script_confirmation_overlay.h"
 #import "ios/chrome/browser/overlays/public/web_content_area/java_script_prompt_overlay.h"
 #import "ios/chrome/browser/ui/dialogs/java_script_dialog_blocking_state.h"
+#include "ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.h"
 #import "ios/web/public/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -24,6 +26,10 @@
 
 OverlayJavaScriptDialogPresenter::~OverlayJavaScriptDialogPresenter() = default;
 
+void OverlayJavaScriptDialogPresenter::Close() {
+  closing_ = true;
+}
+
 void OverlayJavaScriptDialogPresenter::RunJavaScriptDialog(
     web::WebState* web_state,
     const GURL& origin_url,
@@ -34,6 +40,7 @@
   JavaScriptDialogBlockingState::CreateForWebState(web_state);
   if (JavaScriptDialogBlockingState::FromWebState(web_state)->blocked()) {
     // Block the dialog if needed.
+    RecordDialogDismissalCause(IOSJavaScriptDialogDismissalCause::kBlocked);
     std::move(callback).Run(NO, nil);
     return;
   }
@@ -86,10 +93,17 @@
   }
 
   if (!response) {
+    // Cancelled dialogs have no response.  Cancellations that don't occur
+    // during tab closures are due to navigation.
+    IOSJavaScriptDialogDismissalCause cause =
+        closing_ ? IOSJavaScriptDialogDismissalCause::kClosure
+                 : IOSJavaScriptDialogDismissalCause::kNavigation;
+    RecordDialogDismissalCause(cause);
     std::move(callback).Run(false, nil);
     return;
   }
 
+  RecordDialogDismissalCause(IOSJavaScriptDialogDismissalCause::kUser);
   bool success = false;
   NSString* user_input = nil;
   switch (dialog_type) {
diff --git a/ios/chrome/browser/ui/location_bar/BUILD.gn b/ios/chrome/browser/ui/location_bar/BUILD.gn
index 9a23ed2..14d1ebfb 100644
--- a/ios/chrome/browser/ui/location_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/location_bar/BUILD.gn
@@ -40,7 +40,9 @@
     "//ios/chrome/browser/geolocation:geolocation_internal",
     "//ios/chrome/browser/infobars:badge",
     "//ios/chrome/browser/infobars:public",
+    "//ios/chrome/browser/main",
     "//ios/chrome/browser/ntp",
+    "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/ssl",
     "//ios/chrome/browser/ui:feature_flags",
@@ -143,6 +145,7 @@
   testonly = true
   sources = [
     "location_bar_coordinator_unittest.mm",
+    "location_bar_mediator_unittest.mm",
   ]
   deps = [
     ":location_bar",
@@ -154,8 +157,13 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/autocomplete",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/main:test_support",
+    "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/web_content_area",
+    "//ios/chrome/browser/overlays/test",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/location_bar/test",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/chrome/browser/ui/toolbar/test",
     "//ios/chrome/browser/url_loading",
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
index 56a52771..c9e5ccf 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
@@ -5,8 +5,6 @@
 #ifndef IOS_CHROME_BROWSER_UI_LOCATION_BAR_LOCATION_BAR_CONSUMER_H_
 #define IOS_CHROME_BROWSER_UI_LOCATION_BAR_LOCATION_BAR_CONSUMER_H_
 
-#import "ios/chrome/browser/infobars/infobar_type.h"
-
 // Consumer for the location bar mediator.
 @protocol LocationBarConsumer
 
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
index ee31261..ed2e84a 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.h
@@ -11,12 +11,9 @@
 #import "ios/chrome/browser/ui/omnibox/location_bar_delegate.h"
 #import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
 
-namespace ios {
-class ChromeBrowserState;
-}
-class WebStateList;
 @class CommandDispatcher;
 @protocol ApplicationCommands;
+class Browser;
 @protocol BrowserCommands;
 @protocol EditViewAnimatee;
 @protocol LocationBarAnimatee;
@@ -32,15 +29,13 @@
 // View controller containing the omnibox.
 @property(nonatomic, strong, readonly)
     UIViewController* locationBarViewController;
-// Weak reference to ChromeBrowserState;
-@property(nonatomic, assign) ios::ChromeBrowserState* browserState;
+// The location bar's Browser.
+@property(nonatomic, assign) Browser* browser;
 // The dispatcher for this view controller.
 @property(nonatomic, weak) CommandDispatcher* dispatcher;
 // Delegate for this coordinator.
 // TODO(crbug.com/799446): Change this.
 @property(nonatomic, weak) id<ToolbarCoordinatorDelegate> delegate;
-// The web state list this ToolbarCoordinator is handling.
-@property(nonatomic, assign) WebStateList* webStateList;
 
 @property(nonatomic, weak) id<OmniboxPopupPresenterDelegate>
     popupPresenterDelegate;
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index 5de11888..57ff373a 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -19,7 +19,9 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
 #include "ios/chrome/browser/infobars/infobar_metrics_recorder.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/ui/badges/badge_button_action_handler.h"
 #import "ios/chrome/browser/ui/badges/badge_button_factory.h"
@@ -93,6 +95,8 @@
 @property(nonatomic, strong) OmniboxCoordinator* omniboxCoordinator;
 @property(nonatomic, strong) LocationBarMediator* mediator;
 @property(nonatomic, strong) LocationBarViewController* viewController;
+@property(nonatomic, readonly) ios::ChromeBrowserState* browserState;
+@property(nonatomic, readonly) WebStateList* webStateList;
 
 // Tracks calls in progress to -cancelOmniboxEdit to avoid calling it from
 // itself when -resignFirstResponder causes -textFieldWillResignFirstResponder
@@ -102,16 +106,16 @@
 @end
 
 @implementation LocationBarCoordinator
-@synthesize commandDispatcher = _commandDispatcher;
-@synthesize viewController = _viewController;
-@synthesize started = _started;
-@synthesize mediator = _mediator;
-@synthesize browserState = _browserState;
-@synthesize dispatcher = _dispatcher;
-@synthesize delegate = _delegate;
-@synthesize webStateList = _webStateList;
-@synthesize omniboxPopupCoordinator = _omniboxPopupCoordinator;
-@synthesize omniboxCoordinator = _omniboxCoordinator;
+
+#pragma mark - Accessors
+
+- (ios::ChromeBrowserState*)browserState {
+  return self.browser ? self.browser->GetBrowserState() : nullptr;
+}
+
+- (WebStateList*)webStateList {
+  return self.browser ? self.browser->GetWebStateList() : nullptr;
+}
 
 #pragma mark - public
 
@@ -121,6 +125,7 @@
 
 - (void)start {
   DCHECK(self.commandDispatcher);
+  DCHECK(self.browser);
 
   if (self.started)
     return;
@@ -195,6 +200,8 @@
   self.mediator = [[LocationBarMediator alloc]
       initWithLocationBarModel:[self locationBarModel]];
   self.mediator.webStateList = self.webStateList;
+  self.mediator.webContentAreaOverlayPresenter = OverlayPresenter::FromBrowser(
+      self.browser, OverlayModality::kWebContentArea);
   self.mediator.templateURLService =
       ios::TemplateURLServiceFactory::GetForBrowserState(self.browserState);
   self.mediator.consumer = self;
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
index 6691846..8f805b35 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
@@ -12,6 +12,7 @@
 #include "components/variations/variations_http_header_provider.h"
 #include "ios/chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
@@ -75,9 +76,11 @@
     test_cbs_builder.AddTestingFactory(
         UrlLoadingServiceFactory::GetInstance(),
         UrlLoadingServiceFactory::GetDefaultFactory());
-
     browser_state_ = test_cbs_builder.Build();
 
+    browser_ =
+        std::make_unique<TestBrowser>(browser_state_.get(), &web_state_list_);
+
     auto web_state = std::make_unique<web::TestWebState>();
     web_state->SetBrowserState(browser_state_.get());
     web_state->SetCurrentURL(GURL("http://test/"));
@@ -88,8 +91,7 @@
     delegate_ = [[TestToolbarCoordinatorDelegate alloc] init];
 
     coordinator_ = [[LocationBarCoordinator alloc] init];
-    coordinator_.browserState = browser_state_.get();
-    coordinator_.webStateList = &web_state_list_;
+    coordinator_.browser = browser_.get();
     coordinator_.delegate = delegate_;
     coordinator_.commandDispatcher = [[CommandDispatcher alloc] init];
   }
@@ -108,6 +110,7 @@
   std::unique_ptr<TestChromeBrowserState> browser_state_;
   FakeWebStateListDelegate web_state_list_delegate_;
   WebStateList web_state_list_;
+  std::unique_ptr<Browser> browser_;
   TestToolbarCoordinatorDelegate* delegate_;
 };
 
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_mediator.h b/ios/chrome/browser/ui/location_bar/location_bar_mediator.h
index 8fb5c8b..caee007 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_mediator.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_mediator.h
@@ -10,6 +10,7 @@
 @protocol LocationBarConsumer;
 class TemplateURLService;
 class WebStateList;
+class OverlayPresenter;
 class LocationBarModel;
 
 // A mediator object that updates the mediator when the web state changes.
@@ -23,6 +24,11 @@
 // state.
 @property(nonatomic, assign) WebStateList* webStateList;
 
+// The overlay presenter for OverlayModality::kWebContentArea.  This mediator
+// listens for overlay presentation events to determine whether the share button
+// should be enabled.
+@property(nonatomic, assign) OverlayPresenter* webContentAreaOverlayPresenter;
+
 // The location bar model used by this mediator to extract the current URL and
 // the security state.
 @property(nonatomic, assign, readonly) LocationBarModel* locationBarModel;
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
index 86cbec1..ea71bb8 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
@@ -8,6 +8,8 @@
 #include "base/strings/sys_string_conversions.h"
 #include "components/omnibox/browser/location_bar_model.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h"
 #import "ios/chrome/browser/search_engines/search_engine_observer_bridge.h"
 #import "ios/chrome/browser/search_engines/search_engines_util.h"
 #include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
@@ -32,19 +34,26 @@
 
 @interface LocationBarMediator () <CRWWebStateObserver,
                                    SearchEngineObserving,
-                                   WebStateListObserving>
+                                   WebStateListObserving,
+                                   OverlayPresenterObserving>
 
 // The current web state associated with the toolbar.
 @property(nonatomic, assign) web::WebState* webState;
 
 // Whether the current default search engine supports search by image.
 @property(nonatomic, assign) BOOL searchEngineSupportsSearchByImage;
+
+// Whether an overlay is currently presented over the web content area.
+@property(nonatomic, assign, getter=isWebContentAreaShowingOverlay)
+    BOOL webContentAreaShowingOverlay;
+
 @end
 
 @implementation LocationBarMediator {
   std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
   std::unique_ptr<WebStateListObserverBridge> _webStateListObserver;
   std::unique_ptr<SearchEngineObserverBridge> _searchEngineObserver;
+  std::unique_ptr<OverlayPresenterObserverBridge> _overlayObserver;
 }
 
 - (instancetype)initWithLocationBarModel:(LocationBarModel*)locationBarModel {
@@ -54,6 +63,7 @@
     _locationBarModel = locationBarModel;
     _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
     _webStateListObserver = std::make_unique<WebStateListObserverBridge>(self);
+    _overlayObserver = std::make_unique<OverlayPresenterObserverBridge>(self);
     _searchEngineSupportsSearchByImage = NO;
   }
   return self;
@@ -66,17 +76,8 @@
 #pragma mark - Public
 
 - (void)disconnect {
-  if (_webStateList) {
-    _webStateList->RemoveObserver(_webStateListObserver.get());
-    _webStateListObserver.reset();
-    _webStateList = nullptr;
-  }
-
-  if (_webState) {
-    _webState->RemoveObserver(_webStateObserver.get());
-    _webStateObserver.reset();
-    _webState = nullptr;
-  }
+  self.webStateList = nullptr;
+  self.webContentAreaOverlayPresenter = nullptr;
 }
 
 #pragma mark - CRWWebStateObserver
@@ -135,6 +136,18 @@
   _webState = nullptr;
 }
 
+#pragma mark - OverlayPresesenterObserving
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    willShowOverlayForRequest:(OverlayRequest*)request {
+  self.webContentAreaShowingOverlay = YES;
+}
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    didHideOverlayForRequest:(OverlayRequest*)request {
+  self.webContentAreaShowingOverlay = NO;
+}
+
 #pragma mark - WebStateListObserver
 
 - (void)webStateList:(WebStateList*)webStateList
@@ -189,8 +202,24 @@
   }
 
   _webStateList = webStateList;
-  self.webState = self.webStateList->GetActiveWebState();
-  _webStateList->AddObserver(_webStateListObserver.get());
+
+  if (_webStateList) {
+    self.webState = self.webStateList->GetActiveWebState();
+    _webStateList->AddObserver(_webStateListObserver.get());
+  } else {
+    self.webState = nullptr;
+  }
+}
+
+- (void)setWebContentAreaOverlayPresenter:
+    (OverlayPresenter*)webContentAreaOverlayPresenter {
+  if (_webContentAreaOverlayPresenter)
+    _webContentAreaOverlayPresenter->RemoveObserver(_overlayObserver.get());
+
+  _webContentAreaOverlayPresenter = webContentAreaOverlayPresenter;
+
+  if (_webContentAreaOverlayPresenter)
+    _webContentAreaOverlayPresenter->AddObserver(_overlayObserver.get());
 }
 
 - (void)setTemplateURLService:(TemplateURLService*)templateURLService {
@@ -212,6 +241,13 @@
   }
 }
 
+- (void)setWebContentAreaShowingOverlay:(BOOL)webContentAreaShowingOverlay {
+  if (_webContentAreaShowingOverlay == webContentAreaShowingOverlay)
+    return;
+  _webContentAreaShowingOverlay = webContentAreaShowingOverlay;
+  [self.consumer updateLocationShareable:[self isSharingEnabled]];
+}
+
 #pragma mark - private
 
 - (void)notifyConsumerOfChangedLocation {
@@ -222,7 +258,7 @@
   if (isNTP) {
     [self.consumer updateAfterNavigatingToNTP];
   }
-  [self.consumer updateLocationShareable:[self isCurrentPageShareable]];
+  [self.consumer updateLocationShareable:[self isSharingEnabled]];
 }
 
 - (void)notifyConsumerOfChangedSecurityIcon {
@@ -269,7 +305,12 @@
 
 #pragma mark Shareability helpers
 
-- (BOOL)isCurrentPageShareable {
+- (BOOL)isSharingEnabled {
+  // Page sharing requires JavaScript execution, which is paused while overlays
+  // are displayed over the web content area.
+  if (self.webContentAreaShowingOverlay)
+    return NO;
+
   const GURL& URL = self.webState->GetLastCommittedURL();
   return URL.is_valid() && !web::GetWebClient()->IsAppSpecificURL(URL);
 }
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_mediator_unittest.mm b/ios/chrome/browser/ui/location_bar/location_bar_mediator_unittest.mm
new file mode 100644
index 0000000..21a3091
--- /dev/null
+++ b/ios/chrome/browser/ui/location_bar/location_bar_mediator_unittest.mm
@@ -0,0 +1,91 @@
+// 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.
+
+#import "ios/chrome/browser/ui/location_bar/location_bar_mediator.h"
+
+#include "components/omnibox/browser/test_location_bar_model.h"
+#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#import "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_alert_overlay.h"
+#include "ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h"
+#import "ios/chrome/browser/ui/location_bar/test/fake_location_bar_consumer.h"
+#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/web_state_list/web_state_opener.h"
+#include "ios/web/public/test/fakes/test_web_state.h"
+#include "ios/web/public/test/web_task_environment.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for LocationBarMediator.
+class LocationBarMediatorTest : public PlatformTest {
+ protected:
+  LocationBarMediatorTest()
+      : web_state_list_(&web_state_list_delegate_),
+        mediator_(
+            [[LocationBarMediator alloc] initWithLocationBarModel:&model_]),
+        consumer_([[FakeLocationBarConsumer alloc] init]) {
+    // Set up the TestBrowser.
+    TestChromeBrowserState::Builder browser_state_builder;
+    browser_state_ = browser_state_builder.Build();
+    browser_ =
+        std::make_unique<TestBrowser>(browser_state_.get(), &web_state_list_);
+    // Set up the OverlayPresenter.
+    OverlayPresenter* overlay_presenter = OverlayPresenter::FromBrowser(
+        browser_.get(), OverlayModality::kWebContentArea);
+    overlay_presenter->SetPresentationContext(&presentation_context_);
+    // Set up the mediator.
+    mediator_.webStateList = &web_state_list_;
+    mediator_.webContentAreaOverlayPresenter = overlay_presenter;
+    mediator_.consumer = consumer_;
+  }
+  ~LocationBarMediatorTest() override { [mediator_ disconnect]; }
+
+  FakeOverlayPresentationContext presentation_context_;
+  web::WebTaskEnvironment task_environment_;
+  FakeWebStateListDelegate web_state_list_delegate_;
+  WebStateList web_state_list_;
+  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<Browser> browser_;
+  TestLocationBarModel model_;
+  LocationBarMediator* mediator_;
+  FakeLocationBarConsumer* consumer_;
+};
+
+// Tests that the share button is disabled while overlays are presented
+// over the web content area.
+TEST_F(LocationBarMediatorTest, DisableShareForOverlays) {
+  const GURL kUrl("https://chromium.test");
+  std::unique_ptr<web::TestWebState> passed_web_state =
+      std::make_unique<web::TestWebState>();
+  web::TestWebState* web_state = passed_web_state.get();
+  web_state->SetCurrentURL(kUrl);
+  web_state_list_.InsertWebState(0, std::move(passed_web_state),
+                                 WebStateList::INSERT_ACTIVATE,
+                                 WebStateOpener(nullptr));
+  ASSERT_TRUE(consumer_.locationShareable);
+
+  // Present a JavaScript alert over the WebState and verify that the page is no
+  // longer shareable.
+  JavaScriptDialogSource source(web_state, kUrl, /* is_main_frame= */ true);
+  const std::string kMessage("message");
+  OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
+      web_state, OverlayModality::kWebContentArea);
+  queue->AddRequest(
+      OverlayRequest::CreateWithConfig<JavaScriptAlertOverlayRequestConfig>(
+          source, kMessage));
+  EXPECT_FALSE(consumer_.locationShareable);
+
+  // Cancel the request and verify that the location is shareable again.
+  queue->CancelAllRequests();
+  EXPECT_TRUE(consumer_.locationShareable);
+}
+
+// TODO(crbug.com/992578): Add more tests to this suite.
diff --git a/ios/chrome/browser/ui/location_bar/test/BUILD.gn b/ios/chrome/browser/ui/location_bar/test/BUILD.gn
new file mode 100644
index 0000000..02cb9cb
--- /dev/null
+++ b/ios/chrome/browser/ui/location_bar/test/BUILD.gn
@@ -0,0 +1,15 @@
+# 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.
+
+source_set("test") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "fake_location_bar_consumer.h",
+    "fake_location_bar_consumer.mm",
+  ]
+  deps = [
+    "//ios/chrome/browser/ui/location_bar",
+  ]
+}
diff --git a/ios/chrome/browser/ui/location_bar/test/fake_location_bar_consumer.h b/ios/chrome/browser/ui/location_bar/test/fake_location_bar_consumer.h
new file mode 100644
index 0000000..af3b12e
--- /dev/null
+++ b/ios/chrome/browser/ui/location_bar/test/fake_location_bar_consumer.h
@@ -0,0 +1,23 @@
+// 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 IOS_CHROME_BROWSER_UI_LOCATION_BAR_TEST_FAKE_LOCATION_BAR_CONSUMER_H_
+#define IOS_CHROME_BROWSER_UI_LOCATION_BAR_TEST_FAKE_LOCATION_BAR_CONSUMER_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/location_bar/location_bar_consumer.h"
+
+@interface FakeLocationBarConsumer : NSObject <LocationBarConsumer>
+@property(nonatomic, strong, readonly) NSString* locationText;
+@property(nonatomic, assign, readonly) BOOL clipTail;
+@property(nonatomic, strong, readonly) UIImage* icon;
+@property(nonatomic, strong, readonly) NSString* statusText;
+@property(nonatomic, assign, readonly, getter=isLocationShareable)
+    BOOL locationShareable;
+@property(nonatomic, assign, readonly, getter=isSearchByImageSupported)
+    BOOL searchByImageSupported;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_LOCATION_BAR_TEST_FAKE_LOCATION_BAR_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/location_bar/test/fake_location_bar_consumer.mm b/ios/chrome/browser/ui/location_bar/test/fake_location_bar_consumer.mm
new file mode 100644
index 0000000..f1b234c
--- /dev/null
+++ b/ios/chrome/browser/ui/location_bar/test/fake_location_bar_consumer.mm
@@ -0,0 +1,38 @@
+// 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.
+
+#import "ios/chrome/browser/ui/location_bar/test/fake_location_bar_consumer.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation FakeLocationBarConsumer
+
+- (void)updateLocationText:(NSString*)string clipTail:(BOOL)clipTail {
+  _locationText = string;
+  _clipTail = clipTail;
+}
+
+- (void)updateLocationIcon:(UIImage*)icon
+        securityStatusText:(NSString*)statusText {
+  _icon = icon;
+  _statusText = statusText;
+}
+
+- (void)updateLocationShareable:(BOOL)shareable {
+  _locationShareable = shareable;
+}
+
+- (void)defocusOmnibox {
+}
+
+- (void)updateAfterNavigatingToNTP {
+}
+
+- (void)updateSearchByImageSupported:(BOOL)searchByImageSupported {
+  _searchByImageSupported = searchByImageSupported;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/omnibox/BUILD.gn b/ios/chrome/browser/ui/omnibox/BUILD.gn
index f416ce8..7b2dd363 100644
--- a/ios/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/BUILD.gn
@@ -244,6 +244,7 @@
     "//components/omnibox/browser:test_support",
     "//ios/chrome/browser/autocomplete",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/tabs:tabs_internal",
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_perftest.mm b/ios/chrome/browser/ui/omnibox/omnibox_perftest.mm
index 417acbb8..5dbad03 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_perftest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_perftest.mm
@@ -12,6 +12,7 @@
 #include "components/omnibox/browser/test_location_bar_model.h"
 #include "ios/chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #include "ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.h"
@@ -94,6 +95,10 @@
                                     WebStateList::INSERT_FORCE_INDEX,
                                     WebStateOpener());
 
+    // Create the Browser for the toolbar coordinator.
+    browser_ = std::make_unique<TestBrowser>(chrome_browser_state_.get(),
+                                             web_state_list_.get());
+
     // Creates the Toolbar for testing and sizes it to the width of the screen.
     location_bar_model_delegate_.reset(
         new LocationBarModelDelegateIOS(web_state_list_.get()));
@@ -109,8 +114,8 @@
 
     CommandDispatcher* dispatcher = [[CommandDispatcher alloc] init];
 
-    coordinator_ = [[PrimaryToolbarCoordinator alloc]
-        initWithBrowserState:chrome_browser_state_.get()];
+    coordinator_ =
+        [[PrimaryToolbarCoordinator alloc] initWithBrowser:browser_.get()];
     coordinator_.delegate = toolbarDelegate;
     coordinator_.webStateList = web_state_list_.get();
     coordinator_.commandDispatcher = dispatcher;
@@ -227,6 +232,7 @@
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   FakeWebStateListDelegate web_state_list_delegate_;
   std::unique_ptr<WebStateList> web_state_list_;
+  std::unique_ptr<Browser> browser_;
   std::unique_ptr<LocationBarModelDelegateIOS> location_bar_model_delegate_;
   std::unique_ptr<LocationBarModel> location_bar_model_;
   PrimaryToolbarCoordinator* coordinator_;
diff --git a/ios/chrome/browser/ui/popup_menu/BUILD.gn b/ios/chrome/browser/ui/popup_menu/BUILD.gn
index 5e84da9..9500aeb 100644
--- a/ios/chrome/browser/ui/popup_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/BUILD.gn
@@ -51,6 +51,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/feature_engagement",
     "//ios/chrome/browser/find_in_page",
+    "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/translate",
@@ -103,6 +104,10 @@
     "//components/reading_list/core",
     "//components/translate/core/browser",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/main:test_support",
+    "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/web_content_area",
+    "//ios/chrome/browser/overlays/test",
     "//ios/chrome/browser/ui/popup_menu/cells",
     "//ios/chrome/browser/ui/popup_menu/public:popup_menu_ui",
     "//ios/chrome/browser/ui/toolbar/test",
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.h b/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.h
index ff6d4fd..a2d4df3 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.h
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.h
@@ -18,6 +18,12 @@
 // Coordinator for the popup menu, handling the commands.
 @interface PopupMenuCoordinator : ChromeCoordinator<PopupMenuLongPressDelegate>
 
+// PopupMenuCoordinator needs to be initialized with a Browser.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:
+                                  (ios::ChromeBrowserState*)browserState
+    NS_UNAVAILABLE;
+
 // Dispatcher used by this coordinator to receive the PopupMenuCommands.
 @property(nonatomic, weak) CommandDispatcher* dispatcher;
 // The WebStateList this coordinator is handling.
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
index 4baa2e1..ba047a1 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
@@ -11,6 +11,7 @@
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/feature_engagement/tracker_factory.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/ui/bubble/bubble_presenter.h"
@@ -242,6 +243,8 @@
   self.mediator.templateURLService =
       ios::TemplateURLServiceFactory::GetForBrowserState(self.browserState);
   self.mediator.popupMenu = tableViewController;
+  self.mediator.webContentAreaOverlayPresenter = OverlayPresenter::FromBrowser(
+      self.browser, OverlayModality::kWebContentArea);
 
   self.actionHandler = [[PopupMenuActionHandler alloc] init];
   self.actionHandler.baseViewController = self.baseViewController;
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.h b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.h
index f0ea9966..28d10821 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.h
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.h
@@ -17,6 +17,7 @@
 class Tracker;
 }
 @protocol BrowserCommands;
+class OverlayPresenter;
 @protocol PopupMenuConsumer;
 class ReadingListModel;
 class TemplateURLService;
@@ -39,6 +40,10 @@
 // The WebStateList that this mediator listens for any changes on the current
 // WebState.
 @property(nonatomic, assign) WebStateList* webStateList;
+// The overlay presenter for OverlayModality::kWebContentArea.  This mediator
+// listens for overlay presentation events to determine whether the "Read Later"
+// button should be enabled.
+@property(nonatomic, assign) OverlayPresenter* webContentAreaOverlayPresenter;
 // The consumer to be configured with this mediator.
 @property(nonatomic, strong) id<PopupMenuConsumer> popupMenu;
 // Dispatcher.
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
index 4e56a11..a700c78 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
@@ -20,6 +20,8 @@
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h"
 #import "ios/chrome/browser/search_engines/search_engines_util.h"
 #import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
 #import "ios/chrome/browser/ui/activity_services/canonical_url_retriever.h"
@@ -78,6 +80,7 @@
 @interface PopupMenuMediator () <BookmarkModelBridgeObserver,
                                  CRWWebStateObserver,
                                  IOSLanguageDetectionTabHelperObserving,
+                                 OverlayPresenterObserving,
                                  ReadingListMenuNotificationDelegate,
                                  WebStateListObserving> {
   std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
@@ -87,6 +90,7 @@
   // Bridge to get notified of the language detection event.
   std::unique_ptr<language::IOSLanguageDetectionTabHelperObserverBridge>
       _iOSLanguageDetectionTabHelperObserverBridge;
+  std::unique_ptr<OverlayPresenterObserver> _overlayPresenterObserver;
 }
 
 // Items to be displayed in the popup menu.
@@ -108,6 +112,10 @@
 // Whether the hint for the "New Incognito Tab" item should be triggered.
 @property(nonatomic, assign) BOOL triggerNewIncognitoTabTip;
 
+// Whether an overlay is currently presented over the web content area.
+@property(nonatomic, assign, getter=isWebContentAreaShowingOverlay)
+    BOOL webContentAreaShowingOverlay;
+
 #pragma mark*** Specific Items ***
 
 @property(nonatomic, strong) PopupMenuToolsItem* openNewIncognitoTabItem;
@@ -128,28 +136,6 @@
 
 @implementation PopupMenuMediator
 
-@synthesize items = _items;
-@synthesize isIncognito = _isIncognito;
-@synthesize popupMenu = _popupMenu;
-@synthesize bookmarkModel = _bookmarkModel;
-@synthesize dispatcher = _dispatcher;
-@synthesize engagementTracker = _engagementTracker;
-@synthesize readingListMenuNotifier = _readingListMenuNotifier;
-@synthesize triggerNewIncognitoTabTip = _triggerNewIncognitoTabTip;
-@synthesize type = _type;
-@synthesize webState = _webState;
-@synthesize webStateList = _webStateList;
-@synthesize openNewIncognitoTabItem = _openNewIncognitoTabItem;
-@synthesize reloadStopItem = _reloadStopItem;
-@synthesize readLaterItem = _readLaterItem;
-@synthesize bookmarkItem = _bookmarkItem;
-@synthesize findInPageItem = _findInPageItem;
-@synthesize siteInformationItem = _siteInformationItem;
-@synthesize requestDesktopSiteItem = _requestDesktopSiteItem;
-@synthesize requestMobileSiteItem = _requestMobileSiteItem;
-@synthesize readingListItem = _readingListItem;
-@synthesize specificItems = _specificItems;
-
 #pragma mark - Public
 
 - (instancetype)initWithType:(PopupMenuType)type
@@ -164,6 +150,8 @@
         [[ReadingListMenuNotifier alloc] initWithReadingList:readingListModel];
     _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
     _webStateListObserver = std::make_unique<WebStateListObserverBridge>(self);
+    _overlayPresenterObserver =
+        std::make_unique<OverlayPresenterObserverBridge>(self);
     _triggerNewIncognitoTabTip = triggerNewIncognitoTabTip;
   }
   return self;
@@ -196,6 +184,13 @@
     _engagementTracker = nullptr;
   }
 
+  if (_webContentAreaOverlayPresenter) {
+    _webContentAreaOverlayPresenter->RemoveObserver(
+        _overlayPresenterObserver.get());
+    self.webContentAreaShowingOverlay = NO;
+    _webContentAreaOverlayPresenter = nullptr;
+  }
+
   _readingListMenuNotifier = nil;
   _bookmarkModelBridge.reset();
   _iOSLanguageDetectionTabHelperObserverBridge.reset();
@@ -301,6 +296,18 @@
   [self updateBookmarkItem];
 }
 
+#pragma mark - OverlayPresenterObserving
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    willShowOverlayForRequest:(OverlayRequest*)request {
+  self.webContentAreaShowingOverlay = YES;
+}
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    didHideOverlayForRequest:(OverlayRequest*)request {
+  self.webContentAreaShowingOverlay = NO;
+}
+
 #pragma mark - Properties
 
 - (void)setWebState:(web::WebState*)webState {
@@ -341,6 +348,24 @@
   }
 }
 
+- (void)setWebContentAreaOverlayPresenter:
+    (OverlayPresenter*)webContentAreaOverlayPresenter {
+  if (_webContentAreaOverlayPresenter) {
+    _webContentAreaOverlayPresenter->RemoveObserver(
+        _overlayPresenterObserver.get());
+    self.webContentAreaShowingOverlay = NO;
+  }
+
+  _webContentAreaOverlayPresenter = webContentAreaOverlayPresenter;
+
+  if (_webContentAreaOverlayPresenter) {
+    _webContentAreaOverlayPresenter->AddObserver(
+        _overlayPresenterObserver.get());
+    self.webContentAreaShowingOverlay =
+        _webContentAreaOverlayPresenter->IsShowingOverlayUI();
+  }
+}
+
 - (void)setPopupMenu:(id<PopupMenuConsumer>)popupMenu {
   _popupMenu = popupMenu;
 
@@ -436,6 +461,13 @@
   return _items;
 }
 
+- (void)setWebContentAreaShowingOverlay:(BOOL)webContentAreaShowingOverlay {
+  if (_webContentAreaShowingOverlay == webContentAreaShowingOverlay)
+    return;
+  _webContentAreaShowingOverlay = webContentAreaShowingOverlay;
+  [self updatePopupMenu];
+}
+
 #pragma mark - PopupMenuActionHandlerCommands
 
 - (void)readPageLater {
@@ -498,7 +530,10 @@
 // status.
 - (void)updatePopupMenu {
   [self updateReloadStopItem];
-  self.readLaterItem.enabled = [self isCurrentURLWebURL];
+  // The "Read Later" functionality requires JavaScript execution, which is
+  // paused while overlays are displayed over the web content area.
+  self.readLaterItem.enabled =
+      !self.webContentAreaShowingOverlay && [self isCurrentURLWebURL];
   [self updateBookmarkItem];
   self.translateItem.enabled = [self isTranslateEnabled];
   self.findInPageItem.enabled = [self isFindInPageEnabled];
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
index 0ccd290..9ec214c 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
@@ -9,6 +9,13 @@
 #include "components/language/ios/browser/ios_language_detection_tab_helper.h"
 #include "components/reading_list/core/reading_list_model_impl.h"
 #include "components/translate/core/browser/translate_prefs.h"
+#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#import "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_alert_overlay.h"
+#include "ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h"
 #import "ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
 #import "ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.h"
@@ -74,6 +81,16 @@
     OCMExpect([popup_menu_strict_ setPopupMenuItems:[OCMArg any]]);
     OCMExpect([popup_menu_strict_ setDelegate:[OCMArg any]]);
     SetUpWebStateList();
+
+    // Set up the TestBrowser.
+    TestChromeBrowserState::Builder browser_state_builder;
+    browser_state_ = browser_state_builder.Build();
+    browser_ = std::make_unique<TestBrowser>(browser_state_.get(),
+                                             web_state_list_.get());
+    // Set up the OverlayPresenter.
+    OverlayPresenter::FromBrowser(browser_.get(),
+                                  OverlayModality::kWebContentArea)
+        ->SetPresentationContext(&presentation_context_);
   }
 
   // Explicitly disconnect the mediator so there won't be any WebStateList
@@ -170,13 +187,16 @@
     return NO;
   }
 
+  FakeOverlayPresentationContext presentation_context_;
+  std::unique_ptr<WebStateList> web_state_list_;
+  FakeWebStateListDelegate web_state_list_delegate_;
+  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<Browser> browser_;
   PopupMenuMediator* mediator_;
   std::unique_ptr<ReadingListModelImpl> reading_list_model_;
   ToolbarTestWebState* web_state_;
   ToolbarTestNavigationManager* navigation_manager_;
   std::unique_ptr<web::NavigationItem> navigation_item_;
-  std::unique_ptr<WebStateList> web_state_list_;
-  FakeWebStateListDelegate web_state_list_delegate_;
   id popup_menu_;
   // Mock refusing all calls except -setPopupMenuItems:.
   id popup_menu_strict_;
@@ -185,7 +205,8 @@
 // Tests that the feature engagement tracker get notified when the mediator is
 // disconnected and the tracker wants the notification badge displayed.
 TEST_F(PopupMenuMediatorTest, TestFeatureEngagementDisconnect) {
-  CreateMediator(PopupMenuTypeToolsMenu, NO, NO);
+  CreateMediator(PopupMenuTypeToolsMenu, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/NO);
   feature_engagement::test::MockTracker tracker;
   EXPECT_CALL(tracker, ShouldTriggerHelpUI(testing::_))
       .WillRepeatedly(testing::Return(true));
@@ -200,7 +221,8 @@
 // Tests that the mediator is returning the right number of items and sections
 // for the Tools Menu type.
 TEST_F(PopupMenuMediatorTest, TestToolsMenuItemsCount) {
-  CreateMediator(PopupMenuTypeToolsMenu, NO, NO);
+  CreateMediator(PopupMenuTypeToolsMenu, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/NO);
   NSUInteger number_of_action_items = 7;
   if (ios::GetChromeBrowserProvider()
           ->GetUserFeedbackProvider()
@@ -221,7 +243,8 @@
 // Tests that the mediator is returning the right number of items and sections
 // for the Tab Grid type, in non-incognito.
 TEST_F(PopupMenuMediatorTest, TestTabGridMenuNonIncognito) {
-  CreateMediator(PopupMenuTypeTabGrid, NO, NO);
+  CreateMediator(PopupMenuTypeTabGrid, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/NO);
   CheckMediatorSetItems(@[
     // New Tab, New Incognito Tab
     @(2),
@@ -233,7 +256,8 @@
 // Tests that the mediator is returning the right number of items and sections
 // for the Tab Grid type, in incognito.
 TEST_F(PopupMenuMediatorTest, TestTabGridMenuIncognito) {
-  CreateMediator(PopupMenuTypeTabGrid, YES, NO);
+  CreateMediator(PopupMenuTypeTabGrid, /*is_incognito=*/YES,
+                 /*trigger_incognito_hint=*/NO);
   CheckMediatorSetItems(@[
     // New Tab, New Incognito Tab
     @(2),
@@ -244,7 +268,8 @@
 
 // Tests that the mediator is asking for an item to be highlighted when asked.
 TEST_F(PopupMenuMediatorTest, TestNewIncognitoHint) {
-  CreateMediator(PopupMenuTypeToolsMenu, NO, YES);
+  CreateMediator(PopupMenuTypeToolsMenu, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/YES);
   mediator_.webStateList = web_state_list_.get();
   SetUpActiveWebState();
   OCMExpect([popup_menu_ setItemToHighlight:[OCMArg isNotNil]]);
@@ -254,7 +279,8 @@
 
 // Test that the mediator isn't asking for an highlighted item.
 TEST_F(PopupMenuMediatorTest, TestNewIncognitoNoHint) {
-  CreateMediator(PopupMenuTypeToolsMenu, NO, NO);
+  CreateMediator(PopupMenuTypeToolsMenu, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/NO);
   [[popup_menu_ reject] setItemToHighlight:[OCMArg any]];
   mediator_.webStateList = web_state_list_.get();
   SetUpActiveWebState();
@@ -263,7 +289,8 @@
 
 // Tests that the mediator is asking for an item to be highlighted when asked.
 TEST_F(PopupMenuMediatorTest, TestNewIncognitoHintTabGrid) {
-  CreateMediator(PopupMenuTypeTabGrid, NO, YES);
+  CreateMediator(PopupMenuTypeTabGrid, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/YES);
   OCMExpect([popup_menu_ setItemToHighlight:[OCMArg isNotNil]]);
   mediator_.webStateList = web_state_list_.get();
   SetUpActiveWebState();
@@ -274,7 +301,8 @@
 // Tests that the items returned by the mediator are correctly enabled on a
 // WebPage.
 TEST_F(PopupMenuMediatorTest, TestItemsStatusOnWebPage) {
-  CreateMediator(PopupMenuTypeToolsMenu, NO, NO);
+  CreateMediator(PopupMenuTypeToolsMenu, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/NO);
   mediator_.webStateList = web_state_list_.get();
   FakePopupMenuConsumer* consumer = [[FakePopupMenuConsumer alloc] init];
   mediator_.popupMenu = consumer;
@@ -290,7 +318,8 @@
 // Tests that the items returned by the mediator are correctly enabled on the
 // NTP.
 TEST_F(PopupMenuMediatorTest, TestItemsStatusOnNTP) {
-  CreateMediator(PopupMenuTypeToolsMenu, NO, NO);
+  CreateMediator(PopupMenuTypeToolsMenu, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/NO);
   mediator_.webStateList = web_state_list_.get();
   FakePopupMenuConsumer* consumer = [[FakePopupMenuConsumer alloc] init];
   mediator_.popupMenu = consumer;
@@ -303,3 +332,34 @@
   EXPECT_TRUE(HasItem(consumer, kToolsMenuNewTabId, /*enabled=*/YES));
   EXPECT_TRUE(HasItem(consumer, kToolsMenuSiteInformation, /*enabled=*/YES));
 }
+
+// Tests that the "Read Later" button is disabled while overlay UI is displayed
+// in OverlayModality::kWebContentArea.
+TEST_F(PopupMenuMediatorTest, TestReadLaterDisabled) {
+  const GURL kUrl("https://chromium.test");
+  web_state_->SetCurrentURL(kUrl);
+  CreateMediator(PopupMenuTypeToolsMenu, /*is_incognito=*/NO,
+                 /*trigger_incognito_hint=*/NO);
+  mediator_.webStateList = web_state_list_.get();
+  mediator_.webContentAreaOverlayPresenter = OverlayPresenter::FromBrowser(
+      browser_.get(), OverlayModality::kWebContentArea);
+  FakePopupMenuConsumer* consumer = [[FakePopupMenuConsumer alloc] init];
+  mediator_.popupMenu = consumer;
+  SetUpActiveWebState();
+  ASSERT_TRUE(HasItem(consumer, kToolsMenuReadLater, /*enabled=*/YES));
+
+  // Present a JavaScript alert over the WebState and verify that the page is no
+  // longer shareable.
+  JavaScriptDialogSource source(web_state_, kUrl, /*is_main_frame=*/true);
+  const std::string kMessage("message");
+  OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
+      web_state_, OverlayModality::kWebContentArea);
+  queue->AddRequest(
+      OverlayRequest::CreateWithConfig<JavaScriptAlertOverlayRequestConfig>(
+          source, kMessage));
+  EXPECT_TRUE(HasItem(consumer, kToolsMenuReadLater, /*enabled=*/NO));
+
+  // Cancel the request and verify that the "Read Later" button is enabled.
+  queue->CancelAllRequests();
+  EXPECT_TRUE(HasItem(consumer, kToolsMenuReadLater, /*enabled=*/YES));
+}
diff --git a/ios/chrome/browser/ui/qr_scanner/BUILD.gn b/ios/chrome/browser/ui/qr_scanner/BUILD.gn
index 7b79c5d8..e86642b 100644
--- a/ios/chrome/browser/ui/qr_scanner/BUILD.gn
+++ b/ios/chrome/browser/ui/qr_scanner/BUILD.gn
@@ -63,6 +63,7 @@
     "//ios/chrome/app:app_internal",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/main",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/icons",
     "//ios/chrome/browser/ui/location_bar",
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
index 62850fc..39707e0 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
@@ -12,6 +12,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/version_info/version_info.h"
 #import "ios/chrome/app/main_controller.h"
+#import "ios/chrome/browser/main/browser.h"
 #include "ios/chrome/browser/ui/icons/chrome_icon.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_coordinator.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h"
@@ -423,7 +424,8 @@
             ui::PageTransition transition) {
         web::NavigationManager::WebLoadParams params(replacementURL);
         params.transition_type = transition;
-        UrlLoadingServiceFactory::GetForBrowserState(self.browserState)
+        UrlLoadingServiceFactory::GetForBrowserState(
+            self.browser->GetBrowserState())
             ->Load(UrlLoadParams::InCurrentTab(params));
         [self cancelOmniboxEdit];
       };
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn
index 6d8d2a9..deaadfa 100644
--- a/ios/chrome/browser/ui/toolbar/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -31,6 +31,7 @@
     "//ios/chrome/browser/autocomplete",
     "//ios/chrome/browser/bookmarks",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/main",
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/ui/bookmarks",
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.h b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.h
index 87ccc58..74ca0ac6 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.h
+++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.h
@@ -23,9 +23,9 @@
 @interface AdaptiveToolbarCoordinator
     : ChromeCoordinator<SideSwipeToolbarSnapshotProviding, ToolbarCoordinatee>
 
-// Initializes this Coordinator with its |browserState|.
-- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
-    NS_DESIGNATED_INITIALIZER;
+// Initializes this Coordinator with its |browser| and a nil base view
+// controller.
+- (instancetype)initWithBrowser:(Browser*)browser NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
     NS_UNAVAILABLE;
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.mm
index 18ea6fa..6c4c45d0 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator.mm
@@ -38,8 +38,8 @@
 
 #pragma mark - ChromeCoordinator
 
-- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
-  return [super initWithBaseViewController:nil browserState:browserState];
+- (instancetype)initWithBrowser:(Browser*)browser {
+  return [super initWithBaseViewController:nil browser:browser];
 }
 
 - (void)start {
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
index bf25391..23a1c1c 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
@@ -11,6 +11,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller_factory.h"
@@ -221,12 +222,11 @@
 - (void)setUpLocationBar {
   self.locationBarCoordinator = [[LocationBarCoordinator alloc] init];
 
-  self.locationBarCoordinator.browserState = self.browserState;
+  self.locationBarCoordinator.browser = self.browser;
   self.locationBarCoordinator.dispatcher =
       base::mac::ObjCCastStrict<CommandDispatcher>(self.dispatcher);
   self.locationBarCoordinator.commandDispatcher = self.commandDispatcher;
   self.locationBarCoordinator.delegate = self.delegate;
-  self.locationBarCoordinator.webStateList = self.webStateList;
   self.locationBarCoordinator.popupPresenterDelegate =
       self.popupPresenterDelegate;
   [self.locationBarCoordinator start];
diff --git a/ios/chrome/browser/web/web_state_delegate_tab_helper.h b/ios/chrome/browser/web/web_state_delegate_tab_helper.h
index 528da58..016b086 100644
--- a/ios/chrome/browser/web/web_state_delegate_tab_helper.h
+++ b/ios/chrome/browser/web/web_state_delegate_tab_helper.h
@@ -8,6 +8,7 @@
 #include "base/memory/weak_ptr.h"
 #import "ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h"
 #import "ios/web/public/web_state/web_state_delegate.h"
+#include "ios/web/public/web_state/web_state_observer.h"
 #include "ios/web/public/web_state/web_state_user_data.h"
 
 class OverlayResponse;
@@ -15,6 +16,7 @@
 // Tab helper that handles the WebStateDelegate implementation.
 class WebStateDelegateTabHelper
     : public web::WebStateDelegate,
+      public web::WebStateObserver,
       public web::WebStateUserData<WebStateDelegateTabHelper> {
  public:
   ~WebStateDelegateTabHelper() override;
@@ -36,6 +38,9 @@
   friend class web::WebStateUserData<WebStateDelegateTabHelper>;
   WEB_STATE_USER_DATA_KEY_DECL();
 
+  // WebStateObserver:
+  void WebStateDestroyed(web::WebState* web_state) override;
+
   // Callback for HTTP authentication dialogs.
   void OnHTTPAuthOverlayFinished(web::WebStateDelegate::AuthCallback callback,
                                  OverlayResponse* response);
diff --git a/ios/chrome/browser/web/web_state_delegate_tab_helper.mm b/ios/chrome/browser/web/web_state_delegate_tab_helper.mm
index 6abae5e..7e142a3 100644
--- a/ios/chrome/browser/web/web_state_delegate_tab_helper.mm
+++ b/ios/chrome/browser/web/web_state_delegate_tab_helper.mm
@@ -19,7 +19,9 @@
 WEB_STATE_USER_DATA_KEY_IMPL(WebStateDelegateTabHelper)
 
 WebStateDelegateTabHelper::WebStateDelegateTabHelper(web::WebState* web_state)
-    : weak_factory_(this) {}
+    : weak_factory_(this) {
+  web_state->AddObserver(this);
+}
 
 WebStateDelegateTabHelper::~WebStateDelegateTabHelper() = default;
 
@@ -49,6 +51,13 @@
       ->AddRequest(std::move(request));
 }
 
+#pragma mark - WebStateObserver
+
+void WebStateDelegateTabHelper::WebStateDestroyed(web::WebState* web_state) {
+  java_script_dialog_presenter_.Close();
+  web_state->RemoveObserver(this);
+}
+
 #pragma mark - Overlay Callbacks
 
 void WebStateDelegateTabHelper::OnHTTPAuthOverlayFinished(
diff --git a/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm b/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
index 6153712..9aea0f8 100644
--- a/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
+++ b/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
@@ -179,7 +179,8 @@
 
   _unmaskingController->OnUnmaskPromptAccepted(
       base::SysNSStringToUTF16(CVC), base::SysNSStringToUTF16(expirationMonth),
-      base::SysNSStringToUTF16(expirationYear), storeLocally);
+      base::SysNSStringToUTF16(expirationYear), storeLocally,
+      /*enable_fido_auth=*/false);
 }
 
 - (BOOL)isCVCValid:(NSString*)CVC {
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 1ee53a0..e56ee20 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -614,9 +614,9 @@
     MaybeSendOverlayInfoToDecoder();
 }
 
-void WebMediaPlayerImpl::BecameDominantVisibleContent(bool isDominant) {
+void WebMediaPlayerImpl::BecameDominantVisibleContent(bool is_dominant) {
   if (observer_)
-    observer_->OnBecameDominantVisibleContent(isDominant);
+    observer_->OnBecameDominantVisibleContent(is_dominant);
 }
 
 void WebMediaPlayerImpl::SetIsEffectivelyFullscreen(
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 88d8d0b..830dbfb 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -220,11 +220,11 @@
   bool SupportsOverlayFullscreenVideo() override;
   void EnteredFullscreen() override;
   void ExitedFullscreen() override;
-  void BecameDominantVisibleContent(bool isDominant) override;
+  void BecameDominantVisibleContent(bool is_dominant) override;
   void SetIsEffectivelyFullscreen(
       blink::WebFullscreenVideoStatus fullscreen_video_status) override;
   void OnHasNativeControlsChanged(bool) override;
-  void OnDisplayTypeChanged(WebMediaPlayer::DisplayType) override;
+  void OnDisplayTypeChanged(WebMediaPlayer::DisplayType display_type) override;
 
   // blink::WebMediaPlayerDelegate::Observer implementation.
   void OnFrameHidden() override;
diff --git a/mojo/public/cpp/bindings/receiver.h b/mojo/public/cpp/bindings/receiver.h
index d3b4aa1..f6861eb 100644
--- a/mojo/public/cpp/bindings/receiver.h
+++ b/mojo/public/cpp/bindings/receiver.h
@@ -209,6 +209,9 @@
   // acknowledgement from the Remote is received.
   void FlushForTesting() { internal_state_.FlushForTesting(); }
 
+  // Exposed for testing, should not generally be used.
+  void EnableTestingMode() { internal_state_.EnableTestingMode(); }
+
   // Allows test code to swap the interface implementation.
   ImplPointerType SwapImplForTesting(ImplPointerType new_impl) {
     return internal_state_.SwapImplForTesting(new_impl);
diff --git a/net/cert/internal/trust_store_mac_unittest.cc b/net/cert/internal/trust_store_mac_unittest.cc
index f7dfcc5..9cbf290 100644
--- a/net/cert/internal/trust_store_mac_unittest.cc
+++ b/net/cert/internal/trust_store_mac_unittest.cc
@@ -218,6 +218,8 @@
   //
   // The output contains zero or more repetitions of:
   // "SHA-1 hash: <hash>\n<PEM encoded cert>\n"
+  // Starting with macOS 10.15, it includes both SHA-256 and SHA-1 hashes:
+  // "SHA-256 hash: <hash>\nSHA-1 hash: <hash>\n<PEM encoded cert>\n"
   std::string find_certificate_default_search_list_output;
   ASSERT_TRUE(
       base::GetAppOutput({"security", "find-certificate", "-a", "-p", "-Z"},
@@ -234,16 +236,26 @@
 
   base::ScopedCFTypeRef<SecPolicyRef> sec_policy(SecPolicyCreateBasicX509());
   ASSERT_TRUE(sec_policy);
-
-  for (const std::string& hash_and_pem : base::SplitStringUsingSubstr(
+  for (const std::string& hash_and_pem_partial : base::SplitStringUsingSubstr(
            find_certificate_system_roots_output +
                find_certificate_default_search_list_output,
-           "SHA-1 hash: ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+           "-----END CERTIFICATE-----", base::TRIM_WHITESPACE,
+           base::SPLIT_WANT_NONEMPTY)) {
+    // Re-add the PEM ending mark, since SplitStringUsingSubstr eats it.
+    const std::string hash_and_pem =
+        hash_and_pem_partial + "\n-----END CERTIFICATE-----\n";
+
+    // Use the first hash value found in the text. This might be SHA-256 or
+    // SHA-1, but it's only for debugging purposes so it doesn't matter as long
+    // as one exists.
+    std::string::size_type hash_pos = hash_and_pem.find("hash: ");
+    ASSERT_NE(std::string::npos, hash_pos);
+    hash_pos += 6;
     std::string::size_type eol_pos = hash_and_pem.find_first_of("\r\n");
     ASSERT_NE(std::string::npos, eol_pos);
-    // Extract the SHA-1 hash of the certificate. This isn't necessary for the
+    // Extract the hash of the certificate. This isn't necessary for the
     // test, but is a convenient identifier to use in any error messages.
-    std::string hash_text = hash_and_pem.substr(0, eol_pos);
+    std::string hash_text = hash_and_pem.substr(hash_pos, eol_pos - hash_pos);
 
     SCOPED_TRACE(hash_text);
     // TODO(mattm): The same cert might exist in both lists, could de-dupe
diff --git a/net/disk_cache/disk_cache_test_base.cc b/net/disk_cache/disk_cache_test_base.cc
index 65a36fc..35c36db 100644
--- a/net/disk_cache/disk_cache_test_base.cc
+++ b/net/disk_cache/disk_cache_test_base.cc
@@ -34,7 +34,10 @@
 
 DiskCacheTest::DiskCacheTest() {
   CHECK(temp_dir_.CreateUniqueTempDir());
-  cache_path_ = temp_dir_.GetPath();
+  // Put the cache into a subdir of |temp_dir_|, to permit tests to safely
+  // remove the cache directory without risking collisions with other tests.
+  cache_path_ = temp_dir_.GetPath().AppendASCII("cache");
+  CHECK(base::CreateDirectory(cache_path_));
 }
 
 DiskCacheTest::~DiskCacheTest() = default;
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index 08db028a..045fcb32 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -4935,8 +4935,7 @@
   EXPECT_NE(net::OK, OpenEntry(key, &entry));
 }
 
-// TODO(https://crbug.com/999584): Flaky on slower bots.
-TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheCreateRecoverFromRmdir) {
+TEST_F(DiskCacheEntryTest, SimpleCacheCreateRecoverFromRmdir) {
   // This test runs as APP_CACHE to make operations more synchronous.
   // (in particular we want to see if create succeeded or not, so we don't
   //  want an optimistic one).
diff --git a/net/dns/dns_client.cc b/net/dns/dns_client.cc
index b0d235d5..26bc7d0c 100644
--- a/net/dns/dns_client.cc
+++ b/net/dns/dns_client.cc
@@ -15,6 +15,7 @@
 #include "net/dns/dns_session.h"
 #include "net/dns/dns_socket_pool.h"
 #include "net/dns/dns_transaction.h"
+#include "net/dns/dns_util.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
 #include "net/socket/client_socket_factory.h"
@@ -41,6 +42,24 @@
   return c1.value() == *c2;
 }
 
+void UpdateConfigForDohUpgrade(DnsConfig* config) {
+  // TODO(crbug.com/878582): Reconsider whether the hardcoded mapping should
+  // also be applied in SECURE mode.
+  if (config->allow_dns_over_https_upgrade &&
+      config->dns_over_https_servers.empty() &&
+      config->secure_dns_mode == DnsConfig::SecureDnsMode::AUTOMATIC) {
+    // If we're in strict mode on Android, only attempt to upgrade the
+    // specified DoT hostname.
+    if (!config->dns_over_tls_hostname.empty()) {
+      config->dns_over_https_servers = GetDohUpgradeServersFromDotHostname(
+          config->dns_over_tls_hostname, config->disabled_upgrade_providers);
+    } else {
+      config->dns_over_https_servers = GetDohUpgradeServersFromNameservers(
+          config->nameservers, config->disabled_upgrade_providers);
+    }
+  }
+}
+
 constexpr base::TimeDelta kInitialDoHTimeout =
     base::TimeDelta::FromMilliseconds(5000);
 
@@ -168,6 +187,8 @@
       config = config_overrides_.ApplyOverrides(system_config_.value());
     }
 
+    UpdateConfigForDohUpgrade(&config);
+
     if (!config.IsValid() || config.unhandled_options)
       return base::nullopt;
 
diff --git a/net/dns/dns_config.cc b/net/dns/dns_config.cc
index 96b162d0..0a94c73 100644
--- a/net/dns/dns_config.cc
+++ b/net/dns/dns_config.cc
@@ -30,7 +30,8 @@
       attempts(2),
       rotate(false),
       use_local_ipv6(false),
-      secure_dns_mode(SecureDnsMode::OFF) {}
+      secure_dns_mode(SecureDnsMode::OFF),
+      allow_dns_over_https_upgrade(false) {}
 
 DnsConfig::~DnsConfig() = default;
 
@@ -60,7 +61,9 @@
          (attempts == d.attempts) && (rotate == d.rotate) &&
          (use_local_ipv6 == d.use_local_ipv6) &&
          (dns_over_https_servers == d.dns_over_https_servers) &&
-         (secure_dns_mode == d.secure_dns_mode);
+         (secure_dns_mode == d.secure_dns_mode) &&
+         (allow_dns_over_https_upgrade == d.allow_dns_over_https_upgrade) &&
+         (disabled_upgrade_providers == d.disabled_upgrade_providers);
 }
 
 void DnsConfig::CopyIgnoreHosts(const DnsConfig& d) {
@@ -77,6 +80,8 @@
   use_local_ipv6 = d.use_local_ipv6;
   dns_over_https_servers = d.dns_over_https_servers;
   secure_dns_mode = d.secure_dns_mode;
+  allow_dns_over_https_upgrade = d.allow_dns_over_https_upgrade;
+  disabled_upgrade_providers = d.disabled_upgrade_providers;
 }
 
 std::unique_ptr<base::Value> DnsConfig::ToValue() const {
@@ -114,6 +119,13 @@
   }
   dict->Set("doh_servers", std::move(list));
   dict->SetInteger("secure_dns_mode", static_cast<int>(secure_dns_mode));
+  dict->SetBoolean("allow_dns_over_https_upgrade",
+                   allow_dns_over_https_upgrade);
+
+  list = std::make_unique<base::ListValue>();
+  for (const auto& provider : disabled_upgrade_providers)
+    list->AppendString(provider);
+  dict->Set("disabled_upgrade_providers", std::move(list));
 
   return std::move(dict);
 }
diff --git a/net/dns/dns_config.h b/net/dns/dns_config.h
index 4d5a882..e1b01ae1 100644
--- a/net/dns/dns_config.h
+++ b/net/dns/dns_config.h
@@ -120,6 +120,15 @@
   // server hostname) using |HostResolver::ResolveHostParameters::
   // secure_dns_mode_override|.
   SecureDnsMode secure_dns_mode;
+
+  // If set to |true|, we will attempt to upgrade the user's DNS configuration
+  // to use DoH server(s) operated by the same provider(s) when the user is
+  // in AUTOMATIC mode and has not pre-specified DoH servers.
+  bool allow_dns_over_https_upgrade;
+
+  // List of providers to exclude from upgrade mapping. See the
+  // mapping in net/dns/dns_util.cc for provider ids.
+  std::vector<std::string> disabled_upgrade_providers;
 };
 
 }  // namespace net
diff --git a/net/dns/dns_config_overrides.cc b/net/dns/dns_config_overrides.cc
index 9f8f5e4..bf63483 100644
--- a/net/dns/dns_config_overrides.cc
+++ b/net/dns/dns_config_overrides.cc
@@ -29,7 +29,9 @@
          timeout == other.timeout && attempts == other.attempts &&
          rotate == other.rotate && use_local_ipv6 == other.use_local_ipv6 &&
          dns_over_https_servers == other.dns_over_https_servers &&
-         secure_dns_mode == other.secure_dns_mode;
+         secure_dns_mode == other.secure_dns_mode &&
+         allow_dns_over_https_upgrade == other.allow_dns_over_https_upgrade &&
+         disabled_upgrade_providers == other.disabled_upgrade_providers;
 }
 
 bool DnsConfigOverrides::operator!=(const DnsConfigOverrides& other) const {
@@ -54,6 +56,9 @@
   overrides.use_local_ipv6 = defaults.use_local_ipv6;
   overrides.dns_over_https_servers = defaults.dns_over_https_servers;
   overrides.secure_dns_mode = defaults.secure_dns_mode;
+  overrides.allow_dns_over_https_upgrade =
+      defaults.allow_dns_over_https_upgrade;
+  overrides.disabled_upgrade_providers = defaults.disabled_upgrade_providers;
 
   return overrides;
 }
@@ -61,7 +66,8 @@
 bool DnsConfigOverrides::OverridesEverything() const {
   return nameservers && search && hosts && append_to_multi_label_name &&
          randomize_ports && ndots && timeout && attempts && rotate &&
-         use_local_ipv6 && dns_over_https_servers && secure_dns_mode;
+         use_local_ipv6 && dns_over_https_servers && secure_dns_mode &&
+         allow_dns_over_https_upgrade && disabled_upgrade_providers;
 }
 
 DnsConfig DnsConfigOverrides::ApplyOverrides(const DnsConfig& config) const {
@@ -94,6 +100,12 @@
     overridden.dns_over_https_servers = dns_over_https_servers.value();
   if (secure_dns_mode)
     overridden.secure_dns_mode = secure_dns_mode.value();
+  if (allow_dns_over_https_upgrade) {
+    overridden.allow_dns_over_https_upgrade =
+        allow_dns_over_https_upgrade.value();
+  }
+  if (disabled_upgrade_providers)
+    overridden.disabled_upgrade_providers = disabled_upgrade_providers.value();
 
   return overridden;
 }
diff --git a/net/dns/dns_config_overrides.h b/net/dns/dns_config_overrides.h
index bd96265f..34dc9a5 100644
--- a/net/dns/dns_config_overrides.h
+++ b/net/dns/dns_config_overrides.h
@@ -58,6 +58,8 @@
   base::Optional<std::vector<DnsConfig::DnsOverHttpsServerConfig>>
       dns_over_https_servers;
   base::Optional<DnsConfig::SecureDnsMode> secure_dns_mode;
+  base::Optional<bool> allow_dns_over_https_upgrade;
+  base::Optional<std::vector<std::string>> disabled_upgrade_providers;
 
   // Note no overriding value for |unhandled_options|. It is meta-configuration,
   // and there should be no reason to override it.
diff --git a/net/dns/dns_session.cc b/net/dns/dns_session.cc
index e55427578..828d60e9 100644
--- a/net/dns/dns_session.cc
+++ b/net/dns/dns_session.cc
@@ -282,18 +282,8 @@
 
 void DnsSession::RecordRTT(unsigned server_index,
                            bool is_doh_server,
-                           bool is_probe,
                            base::TimeDelta rtt) {
   ServerStats* stats = GetServerStats(server_index, is_doh_server);
-  // If the histogram has not yet been populated beyond the initial seed values
-  // and this was a probe query, replace the seed values with a multiple of
-  // the probe's RTT.
-  if (is_probe && stats->rtt_histogram->TotalCount() == kNumSeeds) {
-    DCHECK(is_doh_server);
-    doh_server_stats_[server_index].first = std::make_unique<ServerStats>(
-        rtt * kDohProbeTimeMultiplier, rtt_buckets_.Pointer());
-    return;
-  }
 
   // Jacobson/Karels algorithm for TCP.
   // Using parameters: alpha = 1/8, delta = 1/4, beta = 4
diff --git a/net/dns/dns_session.h b/net/dns/dns_session.h
index 566f3ba..cbc269d 100644
--- a/net/dns/dns_session.h
+++ b/net/dns/dns_session.h
@@ -41,9 +41,6 @@
 // fallback queries is not taken into account.
 const int kAutomaticModeFailureLimit = 10;
 
-// Amount to scale a successful DoH probe RTT when setting a new seed timeout.
-const double kDohProbeTimeMultiplier = 1.5;
-
 // Session parameters and state shared between DNS transactions.
 // Ref-counted so that DnsClient::Request can keep working in absence of
 // DnsClient. A DnsSession must be recreated when DnsConfig changes.
@@ -115,13 +112,9 @@
   // Record the latest DoH probe state.
   void SetProbeSuccess(unsigned doh_server_index, bool success);
 
-  // Record how long it took to receive a response from the server. If
-  // |is_probe| and the underlying |ServerStats::rtt_histogram| has not been
-  // populated by real query times yet, update the seed value for the histogram
-  // to a scaled version of |rtt|.
+  // Record how long it took to receive a response from the server.
   void RecordRTT(unsigned server_index,
                  bool is_doh_server,
-                 bool is_probe,
                  base::TimeDelta rtt);
 
   // Return the timeout for the next query. |attempt| counts from 0 and is used
diff --git a/net/dns/dns_session_unittest.cc b/net/dns/dns_session_unittest.cc
index ebb461f..7b0552e 100644
--- a/net/dns/dns_session_unittest.cc
+++ b/net/dns/dns_session_unittest.cc
@@ -275,9 +275,9 @@
 // test for https://crbug.com/753568.
 TEST_F(DnsSessionTest, NegativeRtt) {
   Initialize(2 /* num_servers */, 2 /* num_doh_servers */);
-  session_->RecordRTT(0, false /* is_doh_server */, false /* is_probe */,
+  session_->RecordRTT(0, false /* is_doh_server */,
                       base::TimeDelta::FromMilliseconds(-1));
-  session_->RecordRTT(0, true /* is_doh_server */, false /* is_probe */,
+  session_->RecordRTT(0, true /* is_doh_server */,
                       base::TimeDelta::FromMilliseconds(-1));
 }
 
@@ -323,21 +323,6 @@
   EXPECT_FALSE(session_->HasAvailableDohServer());
 }
 
-TEST_F(DnsSessionTest, DohProbeRtt) {
-  Initialize(2 /* num_servers */, 2 /* num_doh_servers */);
-  base::TimeDelta probe_time = base::TimeDelta::FromMilliseconds(300);
-  session_->RecordRTT(1, true /* is_doh_server */, true /* is_probe */,
-                      probe_time);
-
-  // Only the DoH server that had the successful probe should have an updated
-  // histogram.
-  base::TimeDelta delta =
-      session_->NextDohTimeout(1) - probe_time * kDohProbeTimeMultiplier;
-  EXPECT_LE(delta.InMilliseconds(), 10);
-  delta = session_->NextDohTimeout(0) - config_.timeout;
-  EXPECT_GE(delta.InMilliseconds(), 10);
-}
-
 }  // namespace
 
 } // namespace net
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index d60632c..03d9aa5 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -936,7 +936,7 @@
     // the ServerStats have not been updated yet.
     session_->RecordServerSuccess(doh_server_index, true /* is_doh_server */);
     session_->RecordRTT(doh_server_index, true /* is_doh_server */,
-                        true /* is_probe */, base::TimeTicks::Now() - start);
+                        base::TimeTicks::Now() - start);
     session_->SetProbeSuccess(doh_server_index, true /* success */);
     probe_stats_[doh_server_index] = nullptr;
   }
@@ -1263,7 +1263,7 @@
     const DnsAttempt* attempt = attempts_[attempt_number].get();
     if (record_rtt && attempt->GetResponse()) {
       session_->RecordRTT(attempt->server_index(), secure_ /* is_doh_server */,
-                          false /* is_probe */, base::TimeTicks::Now() - start);
+                          base::TimeTicks::Now() - start);
     }
     if (callback_.is_null())
       return;
diff --git a/net/dns/dns_util.cc b/net/dns/dns_util.cc
index 8ae9bb66..3c8e070 100644
--- a/net/dns/dns_util.cc
+++ b/net/dns/dns_util.cc
@@ -14,6 +14,8 @@
 #include "base/big_endian.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "build/build_config.h"
@@ -110,6 +112,103 @@
   return true;
 }
 
+// Represents insecure DNS, DoT, and DoH services run by the same provider
+// and providing the same filtering behavior. These entries are used to
+// determine if insecure DNS or DoT services can be upgraded to associated
+// DoH services in automatic mode.
+struct DohUpgradeEntry {
+  DohUpgradeEntry(std::string provider,
+                  std::set<std::string> ip_strs,
+                  std::set<std::string> dns_over_tls_hostnames,
+                  DnsConfig::DnsOverHttpsServerConfig dns_over_https_config)
+      : provider(std::move(provider)),
+        dns_over_tls_hostnames(std::move(dns_over_tls_hostnames)),
+        dns_over_https_config(std::move(dns_over_https_config)) {
+    for (const std::string& ip_str : ip_strs) {
+      IPAddress ip_address;
+      bool success = ip_address.AssignFromIPLiteral(ip_str);
+      DCHECK(success);
+      ip_addresses.insert(ip_address);
+    }
+  }
+  DohUpgradeEntry(const DohUpgradeEntry& other) = default;
+  ~DohUpgradeEntry() = default;
+  const std::string provider;
+  std::set<IPAddress> ip_addresses;
+  const std::set<std::string> dns_over_tls_hostnames;
+  const DnsConfig::DnsOverHttpsServerConfig dns_over_https_config;
+};
+
+const std::vector<const DohUpgradeEntry>& GetDohUpgradeList() {
+  static const base::NoDestructor<std::vector<const DohUpgradeEntry>>
+      upgradable_servers({
+          DohUpgradeEntry("Cisco",
+                          {"208.67.222.222", "208.67.220.220",
+                           "2620:119:35::35", "2620:119:53::53"},
+                          {""} /* DoT hostname */,
+                          {"https://doh.opendns.com/dns-query{?dns}",
+                           false /* use_post */}),
+          DohUpgradeEntry(
+              "CleanBrowsingAdult",
+              {"185.228.168.10", "185.228.169.11", "2a0d:2a00:1::1",
+               "2a0d:2a00:2::1"},
+              {"adult-filter-dns.cleanbrowsing.org"} /* DoT hostname */,
+              {"https://doh.cleanbrowsing.org/doh/adult-filter{?dns}",
+               false /* use_post */}),
+          DohUpgradeEntry(
+              "CleanBrowsingFamily",
+              {"185.228.168.168", "185.228.169.168",
+               "2a0d:2a00:1::", "2a0d:2a00:2::"},
+              {"family-filter-dns.cleanbrowsing.org"} /* DoT hostname */,
+              {"https://doh.cleanbrowsing.org/doh/family-filter{?dns}",
+               false /* use_post */}),
+          DohUpgradeEntry(
+              "CleanBrowsingSecure",
+              {"185.228.168.9", "185.228.169.9", "2a0d:2a00:1::2",
+               "2a0d:2a00:2::2"},
+              {"security-filter-dns.cleanbrowsing.org"} /* DoT hostname */,
+              {"https://doh.cleanbrowsing.org/doh/security-filter{?dns}",
+               false /* use_post */}),
+          DohUpgradeEntry(
+              "Cloudflare",
+              {"1.1.1.1", "1.0.0.1", "2606:4700:4700::1111",
+               "2606:4700:4700::1001"},
+              {"one.one.one.one",
+               "1dot1dot1dot1.cloudflare-dns.com"} /* DoT hostname */,
+              {"https://chrome.cloudflare-dns.com/dns-query",
+               true /* use-post */}),
+          DohUpgradeEntry(
+              "Dnssb",
+              {"185.222.222.222", "185.184.222.222", "2a09::", "2a09::1"},
+              {"dns.sb"} /* DoT hostname */,
+              {"https://doh.dns.sb/dns-query?no_ecs=true{&dns}",
+               false /* use_post */}),
+          DohUpgradeEntry(
+              "Google",
+              {"8.8.8.8", "8.8.4.4", "2001:4860:4860::8888",
+               "2001:4860:4860::8844"},
+              {"dns.google", "dns.google.com",
+               "8888.google"} /* DoT hostname */,
+              {"https://dns.google/dns-query{?dns}", false /* use_post */}),
+          DohUpgradeEntry(
+              "Quad9Cdn",
+              {"9.9.9.11", "149.112.112.11", "2620:fe::11", "2620:fe::fe:11"},
+              {"dns11.quad9.net"} /* DoT hostname */,
+              {"https://dns11.quad9.net/dns-query", true /* use_post */}),
+          DohUpgradeEntry(
+              "Quad9Insecure",
+              {"9.9.9.10", "149.112.112.10", "2620:fe::10", "2620:fe::fe:10"},
+              {"dns10.quad9.net"} /* DoT hostname */,
+              {"https://dns10.quad9.net/dns-query", true /* use_post */}),
+          DohUpgradeEntry(
+              "Quad9Secure",
+              {"9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9"},
+              {"dns.quad9.net", "dns9.quad9.net"} /* DoT hostname */,
+              {"https://dns.quad9.net/dns-query", true /* use_post */}),
+      });
+  return *upgradable_servers;
+}
+
 }  // namespace
 
 bool DNSDomainFromDot(const base::StringPiece& dotted, std::string* out) {
@@ -282,4 +381,50 @@
   }
 }
 
+std::vector<DnsConfig::DnsOverHttpsServerConfig>
+GetDohUpgradeServersFromDotHostname(
+    const std::string& dot_server,
+    const std::vector<std::string>& excluded_providers) {
+  const std::vector<const DohUpgradeEntry>& upgradable_servers =
+      GetDohUpgradeList();
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> doh_servers;
+
+  if (dot_server.empty())
+    return doh_servers;
+
+  for (const auto& upgrade_entry : upgradable_servers) {
+    if (base::Contains(excluded_providers, upgrade_entry.provider))
+      continue;
+
+    if (base::Contains(upgrade_entry.dns_over_tls_hostnames, dot_server)) {
+      doh_servers.push_back(upgrade_entry.dns_over_https_config);
+      break;
+    }
+  }
+  return doh_servers;
+}
+
+std::vector<DnsConfig::DnsOverHttpsServerConfig>
+GetDohUpgradeServersFromNameservers(
+    const std::vector<IPEndPoint>& dns_servers,
+    const std::vector<std::string>& excluded_providers) {
+  const std::vector<const DohUpgradeEntry>& upgradable_servers =
+      GetDohUpgradeList();
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> doh_servers;
+
+  for (const auto& server : dns_servers) {
+    for (const auto& upgrade_entry : upgradable_servers) {
+      if (base::Contains(excluded_providers, upgrade_entry.provider))
+        continue;
+
+      // DoH servers should only be added once.
+      if (base::Contains(upgrade_entry.ip_addresses, server.address()) &&
+          !base::Contains(doh_servers, upgrade_entry.dns_over_https_config)) {
+        doh_servers.push_back(upgrade_entry.dns_over_https_config);
+      }
+    }
+  }
+  return doh_servers;
+}
+
 }  // namespace net
diff --git a/net/dns/dns_util.h b/net/dns/dns_util.h
index c12815f..038a68b 100644
--- a/net/dns/dns_util.h
+++ b/net/dns/dns_util.h
@@ -6,12 +6,15 @@
 #define NET_DNS_DNS_UTIL_H_
 
 #include <string>
+#include <vector>
 
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "net/base/address_family.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
 #include "net/base/network_change_notifier.h"
+#include "net/dns/dns_config.h"
 #include "net/dns/public/dns_query_type.h"
 
 namespace net {
@@ -108,6 +111,23 @@
 NET_EXPORT DnsQueryType
 AddressFamilyToDnsQueryType(AddressFamily address_family);
 
+// Uses the hardcoded upgrade mapping to discover DoH service(s) associated
+// with a DoT hostname. Providers listed in |excluded_providers| are not
+// eligible for upgrade.
+NET_EXPORT_PRIVATE std::vector<DnsConfig::DnsOverHttpsServerConfig>
+GetDohUpgradeServersFromDotHostname(
+    const std::string& dot_server,
+    const std::vector<std::string>& excluded_providers);
+
+// Uses the hardcoded upgrade mapping to discover DoH service(s) associated
+// with a list of insecure DNS servers. Server ordering is preserved across
+// the mapping. Providers listed in |excluded_providers| are not
+// eligible for upgrade.
+NET_EXPORT_PRIVATE std::vector<DnsConfig::DnsOverHttpsServerConfig>
+GetDohUpgradeServersFromNameservers(
+    const std::vector<IPEndPoint>& dns_servers,
+    const std::vector<std::string>& excluded_providers);
+
 }  // namespace net
 
 #endif  // NET_DNS_DNS_UTIL_H_
diff --git a/net/dns/dns_util_unittest.cc b/net/dns/dns_util_unittest.cc
index c88b6731..1309b38 100644
--- a/net/dns/dns_util_unittest.cc
+++ b/net/dns/dns_util_unittest.cc
@@ -5,6 +5,7 @@
 #include "net/dns/dns_util.h"
 
 #include "base/stl_util.h"
+#include "net/dns/public/dns_protocol.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -134,4 +135,68 @@
                 "https://dnsserver.example.net/dns-query{?dns}"));
 }
 
+TEST_F(DNSUtilTest, GetDohUpgradeServersFromDotHostname) {
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> doh_servers =
+      GetDohUpgradeServersFromDotHostname("", std::vector<std::string>());
+  EXPECT_EQ(0u, doh_servers.size());
+
+  doh_servers = GetDohUpgradeServersFromDotHostname("unrecognized",
+                                                    std::vector<std::string>());
+  EXPECT_EQ(0u, doh_servers.size());
+
+  doh_servers = GetDohUpgradeServersFromDotHostname(
+      "family-filter-dns.cleanbrowsing.org", std::vector<std::string>());
+  EXPECT_EQ(1u, doh_servers.size());
+  EXPECT_EQ("https://doh.cleanbrowsing.org/doh/family-filter{?dns}",
+            doh_servers[0].server_template);
+
+  doh_servers = GetDohUpgradeServersFromDotHostname(
+      "family-filter-dns.cleanbrowsing.org",
+      std::vector<std::string>({"CleanBrowsingFamily"}));
+  EXPECT_EQ(0u, doh_servers.size());
+}
+
+TEST_F(DNSUtilTest, GetDohUpgradeServersFromNameservers) {
+  std::vector<IPEndPoint> nameservers;
+  // Cloudflare upgradeable IPs
+  IPAddress dns_ip0(1, 0, 0, 1);
+  IPAddress dns_ip1;
+  EXPECT_TRUE(dns_ip1.AssignFromIPLiteral("2606:4700:4700::1111"));
+  // SafeBrowsing family filter upgradeable IP
+  IPAddress dns_ip2;
+  EXPECT_TRUE(dns_ip2.AssignFromIPLiteral("2a0d:2a00:2::"));
+  // SafeBrowsing security filter upgradeable IP
+  IPAddress dns_ip3(185, 228, 169, 9);
+  // None-upgradeable IP
+  IPAddress dns_ip4(1, 2, 3, 4);
+
+  nameservers.push_back(IPEndPoint(dns_ip0, dns_protocol::kDefaultPort));
+  nameservers.push_back(IPEndPoint(dns_ip1, dns_protocol::kDefaultPort));
+  nameservers.push_back(IPEndPoint(dns_ip2, 54));
+  nameservers.push_back(IPEndPoint(dns_ip3, dns_protocol::kDefaultPort));
+  nameservers.push_back(IPEndPoint(dns_ip4, dns_protocol::kDefaultPort));
+
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> doh_servers =
+      GetDohUpgradeServersFromNameservers(std::vector<IPEndPoint>(),
+                                          std::vector<std::string>());
+  EXPECT_EQ(0u, doh_servers.size());
+
+  doh_servers = GetDohUpgradeServersFromNameservers(nameservers,
+                                                    std::vector<std::string>());
+  EXPECT_EQ(3u, doh_servers.size());
+  EXPECT_EQ("https://chrome.cloudflare-dns.com/dns-query",
+            doh_servers[0].server_template);
+  EXPECT_EQ("https://doh.cleanbrowsing.org/doh/family-filter{?dns}",
+            doh_servers[1].server_template);
+  EXPECT_EQ("https://doh.cleanbrowsing.org/doh/security-filter{?dns}",
+            doh_servers[2].server_template);
+
+  doh_servers = GetDohUpgradeServersFromNameservers(
+      nameservers, std::vector<std::string>(
+                       {"CleanBrowsingSecure", "Cloudflare", "Unexpected"}));
+  EXPECT_EQ(1u, doh_servers.size());
+  EXPECT_EQ("https://doh.cleanbrowsing.org/doh/family-filter{?dns}",
+            doh_servers[0].server_template);
+}
+
 }  // namespace net
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index b8aaf44..b1cfeed 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -3359,6 +3359,31 @@
   return config;
 }
 
+DnsConfig CreateUpgradableDnsConfig() {
+  DnsConfig config;
+  config.secure_dns_mode = DnsConfig::SecureDnsMode::AUTOMATIC;
+  config.allow_dns_over_https_upgrade = true;
+  // Cloudflare upgradeable IPs
+  IPAddress dns_ip0(1, 0, 0, 1);
+  IPAddress dns_ip1;
+  EXPECT_TRUE(dns_ip1.AssignFromIPLiteral("2606:4700:4700::1111"));
+  // SafeBrowsing family filter upgradeable IP
+  IPAddress dns_ip2;
+  EXPECT_TRUE(dns_ip2.AssignFromIPLiteral("2a0d:2a00:2::"));
+  // SafeBrowsing security filter upgradeable IP
+  IPAddress dns_ip3(185, 228, 169, 9);
+  // Non-upgradeable IP
+  IPAddress dns_ip4(1, 2, 3, 4);
+
+  config.nameservers.push_back(IPEndPoint(dns_ip0, dns_protocol::kDefaultPort));
+  config.nameservers.push_back(IPEndPoint(dns_ip1, dns_protocol::kDefaultPort));
+  config.nameservers.push_back(IPEndPoint(dns_ip2, 54));
+  config.nameservers.push_back(IPEndPoint(dns_ip3, dns_protocol::kDefaultPort));
+  config.nameservers.push_back(IPEndPoint(dns_ip4, dns_protocol::kDefaultPort));
+  EXPECT_TRUE(config.IsValid());
+  return config;
+}
+
 // Specialized fixture for tests of DnsTask.
 class HostResolverManagerDnsTest : public HostResolverManagerTest {
  public:
@@ -6430,6 +6455,9 @@
   const DnsConfig::SecureDnsMode secure_dns_mode =
       DnsConfig::SecureDnsMode::SECURE;
   overrides.secure_dns_mode = secure_dns_mode;
+  overrides.allow_dns_over_https_upgrade = true;
+  const std::vector<std::string> disabled_upgrade_providers = {"provider_name"};
+  overrides.disabled_upgrade_providers = disabled_upgrade_providers;
 
   // This test is expected to test overriding all fields.
   EXPECT_TRUE(overrides.OverridesEverything());
@@ -6450,6 +6478,9 @@
   EXPECT_TRUE(overridden_config->use_local_ipv6);
   EXPECT_EQ(dns_over_https_servers, overridden_config->dns_over_https_servers);
   EXPECT_EQ(secure_dns_mode, overridden_config->secure_dns_mode);
+  EXPECT_TRUE(overridden_config->allow_dns_over_https_upgrade);
+  EXPECT_EQ(disabled_upgrade_providers,
+            overridden_config->disabled_upgrade_providers);
 }
 
 TEST_F(HostResolverManagerDnsTest,
@@ -6583,6 +6614,178 @@
   EXPECT_THAT(client_ptr->GetEffectiveConfig(),
               testing::Pointee(original_config));
 }
+
+TEST_F(HostResolverManagerDnsTest, DohMapping) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client = DnsClient::CreateClientForTesting(
+      nullptr /* net_log */, &socket_factory, base::Bind(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  ChangeDnsConfig(original_config);
+
+  const DnsConfig* fetched_config = client_ptr->GetEffectiveConfig();
+  EXPECT_EQ(original_config.nameservers, fetched_config->nameservers);
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> expected_doh_servers = {
+      {"https://chrome.cloudflare-dns.com/dns-query", true /* use-post */},
+      {"https://doh.cleanbrowsing.org/doh/family-filter{?dns}",
+       false /* use_post */},
+      {"https://doh.cleanbrowsing.org/doh/security-filter{?dns}",
+       false /* use_post */}};
+  EXPECT_EQ(expected_doh_servers, fetched_config->dns_over_https_servers);
+}
+
+TEST_F(HostResolverManagerDnsTest, DohMappingDisabled) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client = DnsClient::CreateClientForTesting(
+      nullptr /* net_log */, &socket_factory, base::Bind(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  original_config.allow_dns_over_https_upgrade = false;
+  ChangeDnsConfig(original_config);
+
+  const DnsConfig* fetched_config = client_ptr->GetEffectiveConfig();
+  EXPECT_EQ(original_config.nameservers, fetched_config->nameservers);
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> expected_doh_servers = {};
+  EXPECT_EQ(expected_doh_servers, fetched_config->dns_over_https_servers);
+}
+
+TEST_F(HostResolverManagerDnsTest, DohMappingModeIneligibleForUpgrade) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client = DnsClient::CreateClientForTesting(
+      nullptr /* net_log */, &socket_factory, base::Bind(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  original_config.secure_dns_mode = DnsConfig::SecureDnsMode::SECURE;
+  ChangeDnsConfig(original_config);
+
+  const DnsConfig* fetched_config = client_ptr->GetEffectiveConfig();
+  EXPECT_EQ(original_config.nameservers, fetched_config->nameservers);
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> expected_doh_servers = {};
+  EXPECT_EQ(expected_doh_servers, fetched_config->dns_over_https_servers);
+}
+
+TEST_F(HostResolverManagerDnsTest, DohMappingWithExclusion) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client = DnsClient::CreateClientForTesting(
+      nullptr /* net_log */, &socket_factory, base::Bind(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  original_config.disabled_upgrade_providers = {"CleanBrowsingSecure",
+                                                "Cloudflare", "Unexpected"};
+  ChangeDnsConfig(original_config);
+
+  // A DoH upgrade should be attempted on the DNS servers in the config, but
+  // only for permitted providers.
+  const DnsConfig* fetched_config = client_ptr->GetEffectiveConfig();
+  EXPECT_EQ(original_config.nameservers, fetched_config->nameservers);
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> expected_doh_servers = {
+      {"https://doh.cleanbrowsing.org/doh/family-filter{?dns}",
+       false /* use_post */}};
+  EXPECT_EQ(expected_doh_servers, fetched_config->dns_over_https_servers);
+}
+
+TEST_F(HostResolverManagerDnsTest, DohMappingIgnoredIfTemplateSpecified) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client = DnsClient::CreateClientForTesting(
+      nullptr /* net_log */, &socket_factory, base::Bind(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  ChangeDnsConfig(original_config);
+
+  // If the overrides contains DoH servers, no DoH upgrade should be attempted.
+  DnsConfigOverrides overrides;
+  const std::vector<DnsConfig::DnsOverHttpsServerConfig>
+      dns_over_https_servers_overrides = {
+          DnsConfig::DnsOverHttpsServerConfig("doh.server.override.com", true)};
+  overrides.dns_over_https_servers = dns_over_https_servers_overrides;
+  resolver_->SetDnsConfigOverrides(overrides);
+  const DnsConfig* fetched_config = client_ptr->GetEffectiveConfig();
+  EXPECT_EQ(original_config.nameservers, fetched_config->nameservers);
+  EXPECT_EQ(dns_over_https_servers_overrides,
+            fetched_config->dns_over_https_servers);
+}
+
+TEST_F(HostResolverManagerDnsTest, DohMappingWithAutomaticDot) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client = DnsClient::CreateClientForTesting(
+      nullptr /* net_log */, &socket_factory, base::Bind(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  original_config.dns_over_tls_active = true;
+  ChangeDnsConfig(original_config);
+
+  const DnsConfig* fetched_config = client_ptr->GetEffectiveConfig();
+  EXPECT_EQ(original_config.nameservers, fetched_config->nameservers);
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> expected_doh_servers = {
+      {"https://chrome.cloudflare-dns.com/dns-query", true /* use-post */},
+      {"https://doh.cleanbrowsing.org/doh/family-filter{?dns}",
+       false /* use_post */},
+      {"https://doh.cleanbrowsing.org/doh/security-filter{?dns}",
+       false /* use_post */}};
+  EXPECT_EQ(expected_doh_servers, fetched_config->dns_over_https_servers);
+}
+
+TEST_F(HostResolverManagerDnsTest, DohMappingWithStrictDot) {
+  // Use a real DnsClient to test config-handling behavior.
+  AlwaysFailSocketFactory socket_factory;
+  auto client = DnsClient::CreateClientForTesting(
+      nullptr /* net_log */, &socket_factory, base::Bind(&base::RandInt));
+  DnsClient* client_ptr = client.get();
+  resolver_->SetDnsClientForTesting(std::move(client));
+
+  // Create a DnsConfig containing IP addresses associated with Cloudflare,
+  // SafeBrowsing family filter, SafeBrowsing security filter, and other IPs
+  // not associated with hardcoded DoH services.
+  DnsConfig original_config = CreateUpgradableDnsConfig();
+  original_config.secure_dns_mode = DnsConfig::SecureDnsMode::AUTOMATIC;
+  original_config.dns_over_tls_active = true;
+
+  // Google DoT hostname
+  original_config.dns_over_tls_hostname = "dns.google";
+  ChangeDnsConfig(original_config);
+  const DnsConfig* fetched_config = client_ptr->GetEffectiveConfig();
+  EXPECT_EQ(original_config.nameservers, fetched_config->nameservers);
+  std::vector<DnsConfig::DnsOverHttpsServerConfig> expected_doh_servers = {
+      {"https://dns.google/dns-query{?dns}", false /* use_post */}};
+  EXPECT_EQ(expected_doh_servers, fetched_config->dns_over_https_servers);
+}
+
 #endif  // !defined(OS_IOS)
 
 TEST_F(HostResolverManagerDnsTest, FlushCacheOnDnsConfigOverridesChange) {
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 6a2f992..fffc6bc 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -1957,20 +1957,10 @@
                                                        /*is_success=*/true);
                     });
 
-  if (network != NetworkChangeNotifier::kInvalidNetworkHandle) {
-    OnProbeNetworkSucceeded(network, peer_address, self_address,
-                            std::move(socket), std::move(writer),
-                            std::move(reader));
-  }
-}
+  if (!allow_port_migration_ &&
+      network == NetworkChangeNotifier::kInvalidNetworkHandle)
+    return;
 
-void QuicChromiumClientSession::OnProbeNetworkSucceeded(
-    NetworkChangeNotifier::NetworkHandle network,
-    const quic::QuicSocketAddress& peer_address,
-    const quic::QuicSocketAddress& self_address,
-    std::unique_ptr<DatagramClientSocket> socket,
-    std::unique_ptr<QuicChromiumPacketWriter> writer,
-    std::unique_ptr<QuicChromiumPacketReader> reader) {
   LogProbeResultToHistogram(current_connection_migration_cause_, true);
 
   // Remove |this| as the old packet writer's delegate. Write error on old
@@ -2011,7 +2001,8 @@
       NetLogEventType::QUIC_CONNECTION_MIGRATION_SUCCESS_AFTER_PROBING,
       "migrate_to_network", network);
   if (network == default_network_) {
-    DVLOG(1) << "Client successfully migrated to default network.";
+    DVLOG(1) << "Client successfully migrated to default network: "
+             << default_network_;
     CancelMigrateBackToDefaultNetworkTimer();
     return;
   }
@@ -2037,21 +2028,17 @@
                                                        /*is_success=*/false);
                     });
 
-  if (network != NetworkChangeNotifier::kInvalidNetworkHandle)
-    OnProbeNetworkFailed(network, peer_address);
-}
-
-void QuicChromiumClientSession::OnProbeNetworkFailed(
-    NetworkChangeNotifier::NetworkHandle network,
-    const quic::QuicSocketAddress& peer_address) {
   LogProbeResultToHistogram(current_connection_migration_cause_, false);
-  // Probing failure can be ignored.
-  DVLOG(1) << "Connectivity probing failed on <network: " << network
-           << ", peer_address: " << peer_address.ToString() << ">.";
-  DVLOG_IF(1, network == default_network_ &&
-                  GetDefaultSocket()->GetBoundNetwork() != default_network_)
-      << "Client probing failed on the default network, still using "
-         "non-default network.";
+
+  if (network != NetworkChangeNotifier::kInvalidNetworkHandle) {
+    // Probing failure can be ignored.
+    DVLOG(1) << "Connectivity probing failed on <network: " << network
+             << ", peer_address: " << peer_address.ToString() << ">.";
+    DVLOG_IF(1, network == default_network_ &&
+                    GetDefaultSocket()->GetBoundNetwork() != default_network_)
+        << "Client probing failed on the default network, still using "
+           "non-default network.";
+  }
 }
 
 bool QuicChromiumClientSession::OnSendConnectivityProbingPacket(
@@ -2293,52 +2280,17 @@
 
   current_connection_migration_cause_ = ON_PATH_DEGRADING;
 
-  if (!migrate_session_early_v2_) {
-    HistogramAndLogMigrationFailure(
-        net_log_, MIGRATION_STATUS_PATH_DEGRADING_NOT_ENABLED, connection_id(),
-        "Migration on path degrading not enabled");
+  if (migrate_session_early_v2_) {
+    MaybeMigrateToAlternateNetworkOnPathDegrading();
     return;
   }
 
-  if (GetDefaultSocket()->GetBoundNetwork() == default_network_ &&
-      current_migrations_to_non_default_network_on_path_degrading_ >=
-          max_migrations_to_non_default_network_on_path_degrading_) {
-    HistogramAndLogMigrationFailure(
-        net_log_, MIGRATION_STATUS_ON_PATH_DEGRADING_DISABLED, connection_id(),
-        "Exceeds maximum number of migrations on path degrading");
-    return;
-  }
+  HistogramAndLogMigrationFailure(
+      net_log_, MIGRATION_STATUS_PATH_DEGRADING_NOT_ENABLED, connection_id(),
+      "Migration on path degrading not enabled");
 
-  NetworkChangeNotifier::NetworkHandle alternate_network =
-      stream_factory_->FindAlternateNetwork(
-          GetDefaultSocket()->GetBoundNetwork());
-  if (alternate_network == NetworkChangeNotifier::kInvalidNetworkHandle) {
-    HistogramAndLogMigrationFailure(
-        net_log_, MIGRATION_STATUS_NO_ALTERNATE_NETWORK, connection_id(),
-        "No alternative network on path degrading");
-    return;
-  }
-
-  LogHandshakeStatusOnConnectionMigrationSignal();
-
-  if (!IsCryptoHandshakeConfirmed()) {
-    HistogramAndLogMigrationFailure(
-        net_log_, MIGRATION_STATUS_PATH_DEGRADING_BEFORE_HANDSHAKE_CONFIRMED,
-        connection_id(), "Path degrading before handshake confirmed");
-    return;
-  }
-
-  const NetLogWithSource migration_net_log = NetLogWithSource::Make(
-      net_log_.net_log(), NetLogSourceType::QUIC_CONNECTION_MIGRATION);
-  migration_net_log.BeginEventWithStringParams(
-      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED, "trigger",
-      "PathDegrading");
-  // Probe the alternative network, session will migrate to the probed
-  // network and decide whether it wants to migrate back to the default
-  // network on success.
-  StartProbeNetwork(alternate_network, peer_address(), migration_net_log);
-  migration_net_log.EndEvent(
-      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED);
+  if (allow_port_migration_)
+    MaybeMigrateToDifferentPortOnPathDegrading();
 }
 
 bool QuicChromiumClientSession::ShouldKeepConnectionAlive() const {
@@ -2476,7 +2428,78 @@
   waiting_for_confirmation_callbacks_.clear();
 }
 
-ProbingResult QuicChromiumClientSession::StartProbeNetwork(
+void QuicChromiumClientSession::MaybeMigrateToDifferentPortOnPathDegrading() {
+  DCHECK(allow_port_migration_ && !migrate_session_early_v2_);
+
+  // Migration before handshake is not allowed.
+  if (!IsCryptoHandshakeConfirmed()) {
+    // TODO(zhongyi): add histogram and net log entries.
+    return;
+  }
+
+  // TODO(zhongyi): replace with port migration net log.
+  const NetLogWithSource migration_net_log = NetLogWithSource::Make(
+      net_log_.net_log(), NetLogSourceType::QUIC_CONNECTION_MIGRATION);
+  migration_net_log.BeginEventWithStringParams(
+      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED, "trigger",
+      "port migration on PathDegrading");
+
+  if (!stream_factory_)
+    return;
+
+  // Probe a different port, session will migrate to the probed port on success.
+  // TODO(zhongyi): maybe pass in the network handle current socket is on?
+  StartProbing(default_network_, peer_address(), migration_net_log);
+  migration_net_log.EndEvent(
+      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED);
+}
+
+void QuicChromiumClientSession::
+    MaybeMigrateToAlternateNetworkOnPathDegrading() {
+  DCHECK(migrate_session_early_v2_);
+
+  if (GetDefaultSocket()->GetBoundNetwork() == default_network_ &&
+      current_migrations_to_non_default_network_on_path_degrading_ >=
+          max_migrations_to_non_default_network_on_path_degrading_) {
+    HistogramAndLogMigrationFailure(
+        net_log_, MIGRATION_STATUS_ON_PATH_DEGRADING_DISABLED, connection_id(),
+        "Exceeds maximum number of migrations on path degrading");
+    return;
+  }
+
+  NetworkChangeNotifier::NetworkHandle alternate_network =
+      stream_factory_->FindAlternateNetwork(
+          GetDefaultSocket()->GetBoundNetwork());
+  if (alternate_network == NetworkChangeNotifier::kInvalidNetworkHandle) {
+    HistogramAndLogMigrationFailure(
+        net_log_, MIGRATION_STATUS_NO_ALTERNATE_NETWORK, connection_id(),
+        "No alternative network on path degrading");
+    return;
+  }
+
+  LogHandshakeStatusOnConnectionMigrationSignal();
+
+  if (!IsCryptoHandshakeConfirmed()) {
+    HistogramAndLogMigrationFailure(
+        net_log_, MIGRATION_STATUS_PATH_DEGRADING_BEFORE_HANDSHAKE_CONFIRMED,
+        connection_id(), "Path degrading before handshake confirmed");
+    return;
+  }
+
+  const NetLogWithSource migration_net_log = NetLogWithSource::Make(
+      net_log_.net_log(), NetLogSourceType::QUIC_CONNECTION_MIGRATION);
+  migration_net_log.BeginEventWithStringParams(
+      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED, "trigger",
+      "PathDegrading");
+  // Probe the alternative network, session will migrate to the probed
+  // network and decide whether it wants to migrate back to the default
+  // network on success.
+  MaybeStartProbing(alternate_network, peer_address(), migration_net_log);
+  migration_net_log.EndEvent(
+      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED);
+}
+
+ProbingResult QuicChromiumClientSession::MaybeStartProbing(
     NetworkChangeNotifier::NetworkHandle network,
     const quic::QuicSocketAddress& peer_address,
     const NetLogWithSource& migration_net_log) {
@@ -2510,6 +2533,13 @@
     return ProbingResult::DISABLED_BY_CONFIG;
   }
 
+  return StartProbing(network, peer_address, migration_net_log);
+}
+
+ProbingResult QuicChromiumClientSession::StartProbing(
+    NetworkChangeNotifier::NetworkHandle network,
+    const quic::QuicSocketAddress& peer_address,
+    const NetLogWithSource& migration_net_log) {
   // Check if probing manager is probing the same path.
   if (probing_manager_.IsUnderProbing(network, peer_address))
     return ProbingResult::PENDING;
@@ -2584,7 +2614,7 @@
   // will be cancelled and manager starts to probe |default_network_|
   // immediately.
   ProbingResult result =
-      StartProbeNetwork(default_network_, peer_address(), net_log_);
+      MaybeStartProbing(default_network_, peer_address(), net_log_);
 
   if (result == ProbingResult::DISABLED_WITH_IDLE_SESSION)
     return;
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index 4206c13..b264837e 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -694,10 +694,26 @@
   void CancelAllRequests(int net_error);
   void NotifyRequestsOfConfirmation(int net_error);
 
-  ProbingResult StartProbeNetwork(NetworkChangeNotifier::NetworkHandle network,
+  // Probe on <network, peer_address>.
+  // If <network, peer_addres> is identical to the current path, the probe
+  // is sent on a different port.
+  ProbingResult StartProbing(NetworkChangeNotifier::NetworkHandle network,
+                             const quic::QuicSocketAddress& peer_address,
+                             const NetLogWithSource& migration_net_log);
+
+  // Perform a few checks before StartProbing. If any of those checks fails,
+  // StartProbing will be skipped.
+  ProbingResult MaybeStartProbing(NetworkChangeNotifier::NetworkHandle network,
                                   const quic::QuicSocketAddress& peer_address,
                                   const NetLogWithSource& migration_net_log);
 
+  // Helper method to perform a few checks and initiate connection migration
+  // attempt when path degrading is detected.
+  void MaybeMigrateToAlternateNetworkOnPathDegrading();
+
+  // Helper method to initiate a port migration on path degrading is detected.
+  void MaybeMigrateToDifferentPortOnPathDegrading();
+
   // Called when there is only one possible working network: |network|, If any
   // error encountered, this session will be closed.
   // When the migration succeeds:
@@ -707,18 +723,6 @@
   //    network.
   void MigrateNetworkImmediately(NetworkChangeNotifier::NetworkHandle network);
 
-  // Called when probe |network| succeeded.
-  void OnProbeNetworkSucceeded(
-      NetworkChangeNotifier::NetworkHandle network,
-      const quic::QuicSocketAddress& peer_address,
-      const quic::QuicSocketAddress& self_address,
-      std::unique_ptr<DatagramClientSocket> socket,
-      std::unique_ptr<QuicChromiumPacketWriter> writer,
-      std::unique_ptr<QuicChromiumPacketReader> reader);
-  // Called when probe |network| failed.
-  void OnProbeNetworkFailed(NetworkChangeNotifier::NetworkHandle network,
-                            const quic::QuicSocketAddress& peer_address);
-
   void StartMigrateBackToDefaultNetworkTimer(base::TimeDelta delay);
   void CancelMigrateBackToDefaultNetworkTimer();
   void TryMigrateBackToDefaultNetwork(base::TimeDelta timeout);
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index d705213..f7551c79 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -1673,30 +1673,28 @@
   }
 }
 
-// TODO(renjietang): Find out why this test fails on trybot but passes locally,
-// with FLAGS_quic_reloadable_flag_quic_do_not_send_settings flipped.
-TEST_P(QuicChromiumClientSessionTest, DISABLED_MigrateToSocketReadError) {
+TEST_P(QuicChromiumClientSessionTest, MigrateToSocketReadError) {
   std::unique_ptr<quic::QuicEncryptedPacket> settings_packet;
   std::unique_ptr<quic::QuicEncryptedPacket> client_ping =
       client_maker_.MakeAckAndPingPacket(2, false, 1, 1, 1);
   std::unique_ptr<quic::QuicEncryptedPacket> initial_ping;
+  std::vector<MockWrite> old_writes;
+  std::vector<MockRead> old_reads;
   if (VersionUsesQpack(version_.transport_version)) {
     settings_packet = client_maker_.MakeInitialSettingsPacket(1);
-    MockWrite old_writes[] = {MockWrite(ASYNC, settings_packet->data(),
-                                        settings_packet->length(), 0)};
-    MockRead old_reads[] = {
-        MockRead(ASYNC, ERR_IO_PENDING, 1),  // causes reading to pause.
-        MockRead(ASYNC, ERR_NETWORK_CHANGED, 2)};
-    socket_data_.reset(new SequencedSocketData(old_reads, old_writes));
+    old_writes.push_back(MockWrite(ASYNC, settings_packet->data(),
+                                   settings_packet->length(), 0));
   } else {
     initial_ping = client_maker_.MakePingPacket(1, true);
-    MockWrite old_writes[] = {
-        MockWrite(ASYNC, initial_ping->data(), initial_ping->length(), 0)};
-    MockRead old_reads[] = {
-        MockRead(ASYNC, ERR_IO_PENDING, 1),  // causes reading to pause.
-        MockRead(ASYNC, ERR_NETWORK_CHANGED, 2)};
-    socket_data_.reset(new SequencedSocketData(old_reads, old_writes));
+    old_writes.push_back(
+        MockWrite(ASYNC, initial_ping->data(), initial_ping->length(), 0));
   }
+  old_reads.push_back(MockRead(ASYNC, ERR_IO_PENDING, 1));
+  old_reads.push_back(MockRead(ASYNC, ERR_NETWORK_CHANGED, 2));
+
+  socket_data_.reset(new SequencedSocketData(
+      base::span<const MockRead>(old_reads.data(), old_reads.size()),
+      base::span<const MockWrite>(old_writes.data(), old_writes.size())));
 
   std::unique_ptr<quic::QuicEncryptedPacket> server_ping(
       server_maker_.MakePingPacket(1, /*include_version=*/false));
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index cdae0879..b83bbde 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -789,7 +789,7 @@
     const quic::ParsedQuicVersion& version) {
   if (!net_log_is_capturing_)
     return;
-  string quic_version = QuicVersionToString(version.transport_version);
+  string quic_version = quic::ParsedQuicVersionToString(version);
   net_log_.AddEventWithStringParams(
       NetLogEventType::QUIC_SESSION_VERSION_NEGOTIATED, "version",
       quic_version);
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index ba27faf..70aa92e 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -187,12 +187,11 @@
   }
 };
 
-// TestConnectionMigrationSocketFactory will vend sockets with incremental port
-// number.
-class TestConnectionMigrationSocketFactory : public MockClientSocketFactory {
+// TestMigrationSocketFactory will vend sockets with incremental port number.
+class TestMigrationSocketFactory : public MockClientSocketFactory {
  public:
-  TestConnectionMigrationSocketFactory() : next_source_port_num_(1u) {}
-  ~TestConnectionMigrationSocketFactory() override {}
+  TestMigrationSocketFactory() : next_source_port_num_(1u) {}
+  ~TestMigrationSocketFactory() override {}
 
   std::unique_ptr<DatagramClientSocket> CreateDatagramClientSocket(
       DatagramSocket::BindType bind_type,
@@ -208,7 +207,7 @@
  private:
   uint16_t next_source_port_num_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestConnectionMigrationSocketFactory);
+  DISALLOW_COPY_AND_ASSIGN(TestMigrationSocketFactory);
 };
 
 class QuicStreamFactoryTestBase : public WithTaskEnvironment {
@@ -277,7 +276,8 @@
     mock_ncn->SetConnectedNetworksList(connected_networks);
     test_params_.quic_params.migrate_sessions_on_network_change_v2 = true;
     test_params_.quic_params.migrate_sessions_early_v2 = true;
-    socket_factory_.reset(new TestConnectionMigrationSocketFactory);
+    test_params_.quic_params.allow_port_migration = false;
+    socket_factory_.reset(new TestMigrationSocketFactory);
     Initialize();
   }
 
@@ -835,6 +835,9 @@
   void TestOnNetworkMadeDefaultNoOpenStreams(bool migrate_idle_sessions);
   void TestOnNetworkDisconnectedNonMigratableStream(bool migrate_idle_sessions);
 
+  // Port migrations.
+  void TestSimplePortMigrationOnPathDegrading();
+
   QuicFlagSaver flags_;  // Save/restore all QUIC flag values.
   std::unique_ptr<MockHostResolverBase> host_resolver_;
   std::unique_ptr<SSLConfigService> ssl_config_service_;
@@ -4065,6 +4068,195 @@
   EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
 }
 
+// Verifies that port migration can be attempted and succeed when path degrading
+// is detected, even if NetworkHandle is not supported.
+TEST_P(QuicStreamFactoryTest, MigratePortOnPathDegrading_WithoutNetworkHandle) {
+  test_params_.quic_params.allow_port_migration = true;
+  socket_factory_.reset(new TestMigrationSocketFactory);
+  Initialize();
+
+  TestSimplePortMigrationOnPathDegrading();
+}
+
+// Verifies that port migration can be attempted on the default network and
+// succeed when path degrading is detected. NetworkHandle is supported.
+TEST_P(QuicStreamFactoryTest, MigratePortOnPathDegrading_WithNetworkHandle) {
+  scoped_mock_network_change_notifier_.reset(
+      new ScopedMockNetworkChangeNotifier());
+  MockNetworkChangeNotifier* mock_ncn =
+      scoped_mock_network_change_notifier_->mock_network_change_notifier();
+  mock_ncn->ForceNetworkHandlesSupported();
+  mock_ncn->SetConnectedNetworksList({kDefaultNetworkForTests});
+  test_params_.quic_params.allow_port_migration = true;
+  socket_factory_.reset(new TestMigrationSocketFactory);
+  Initialize();
+
+  scoped_mock_network_change_notifier_->mock_network_change_notifier()
+      ->NotifyNetworkMadeDefault(kDefaultNetworkForTests);
+
+  TestSimplePortMigrationOnPathDegrading();
+}
+
+// Verifies that port migration can be attempted on the default network and
+// succeed when path degrading is detected.
+// NetworkHandle is supported. Migration on network change is also enabled.
+// No additional network migration is triggered post port migration.
+TEST_P(QuicStreamFactoryTest, MigratePortOnPathDegrading_WithMigration) {
+  scoped_mock_network_change_notifier_.reset(
+      new ScopedMockNetworkChangeNotifier());
+  MockNetworkChangeNotifier* mock_ncn =
+      scoped_mock_network_change_notifier_->mock_network_change_notifier();
+  mock_ncn->ForceNetworkHandlesSupported();
+  mock_ncn->SetConnectedNetworksList({kDefaultNetworkForTests});
+  // Enable migration on network change.
+  test_params_.quic_params.migrate_sessions_on_network_change_v2 = true;
+  test_params_.quic_params.allow_port_migration = true;
+  socket_factory_.reset(new TestMigrationSocketFactory);
+  Initialize();
+
+  scoped_mock_network_change_notifier_->mock_network_change_notifier()
+      ->NotifyNetworkMadeDefault(kDefaultNetworkForTests);
+
+  TestSimplePortMigrationOnPathDegrading();
+}
+
+void QuicStreamFactoryTestBase::TestSimplePortMigrationOnPathDegrading() {
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  // Using a testing task runner so that we can control time.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
+
+  int packet_number = 1;
+  MockQuicData quic_data1(version_);
+  quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging Read.
+  if (VersionUsesQpack(version_.transport_version)) {
+    quic_data1.AddWrite(SYNCHRONOUS,
+                        ConstructInitialSettingsPacket(packet_number++));
+  }
+  quic_data1.AddWrite(
+      SYNCHRONOUS,
+      ConstructGetRequestPacket(packet_number++,
+                                GetNthClientInitiatedBidirectionalStreamId(0),
+                                true, true));
+  quic_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  // Set up the second socket data provider that is used after migration.
+  // The response to the earlier request is read on the new socket.
+  MockQuicData quic_data2(version_);
+  // Connectivity probe to be sent on the new path.
+  quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket(
+                                       packet_number++, true));
+  quic_data2.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
+  // Connectivity probe to receive from the server.
+  quic_data2.AddRead(ASYNC,
+                     server_maker_.MakeConnectivityProbingPacket(1, false));
+  // Ping packet to send after migration is completed.
+  quic_data2.AddWrite(ASYNC, client_maker_.MakeAckAndPingPacket(
+                                 packet_number++, false, 1, 1, 1));
+  quic_data2.AddRead(
+      ASYNC,
+      ConstructOkResponsePacket(
+          2, GetNthClientInitiatedBidirectionalStreamId(0), false, false));
+  quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  quic_data2.AddWrite(
+      SYNCHRONOUS,
+      client_maker_.MakeAckAndRstPacket(
+          packet_number++, false, GetNthClientInitiatedBidirectionalStreamId(0),
+          quic::QUIC_STREAM_CANCELLED, 2, 2, 1, true));
+  quic_data2.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create request and QuicHttpStream.
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkIsolationKey(),
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+  std::unique_ptr<HttpStream> stream = CreateStream(&request);
+  EXPECT_TRUE(stream.get());
+
+  // Cause QUIC stream to be created.
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = url_;
+  request_info.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
+                                         net_log_, CompletionOnceCallback()));
+
+  // Ensure that session is alive and active.
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Send GET request on stream.
+  HttpResponseInfo response;
+  HttpRequestHeaders request_headers;
+  EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
+                                    callback_.callback()));
+
+  // Cause the connection to report path degrading to the session.
+  // Session will start to probe a different port.
+  session->connection()->OnPathDegradingTimeout();
+
+  // Next connectivity probe is scheduled to be sent in 2 *
+  // kDefaultRTTMilliSecs.
+  EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
+  base::TimeDelta next_task_delay = task_runner->NextPendingTaskDelay();
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs),
+            next_task_delay);
+
+  // The connection should still be alive, and not marked as going away.
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+  EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
+
+  // Resume quic data and a connectivity probe response will be read on the new
+  // socket.
+  quic_data2.Resume();
+
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  // There should be pending tasks, the nearest one will complete
+  // migration to the new port.
+  EXPECT_EQ(2u, task_runner->GetPendingTaskCount());
+  next_task_delay = task_runner->NextPendingTaskDelay();
+  EXPECT_EQ(base::TimeDelta(), next_task_delay);
+  task_runner->FastForwardBy(next_task_delay);
+
+  // Response headers are received over the new port.
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+  EXPECT_EQ(200, response.headers->response_code());
+
+  // Now there is one pending task to send connectivity probe and has been
+  // cancelled due to successful migration.
+  EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
+  next_task_delay = task_runner->NextPendingTaskDelay();
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs),
+            next_task_delay);
+  task_runner->FastForwardBy(next_task_delay);
+
+  EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
+
+  // Verify that the session is still alive.
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  stream.reset();
+  EXPECT_TRUE(quic_data1.AllReadDataConsumed());
+  EXPECT_TRUE(quic_data1.AllWriteDataConsumed());
+  EXPECT_TRUE(quic_data2.AllReadDataConsumed());
+  EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
+}
+
 // This test verifies that the session marks itself GOAWAY on path degrading
 // and it does not receive any new request
 TEST_P(QuicStreamFactoryTest, GoawayOnPathDegrading) {
diff --git a/net/socket/websocket_transport_connect_sub_job.cc b/net/socket/websocket_transport_connect_sub_job.cc
index f30e77e..aea2802 100644
--- a/net/socket/websocket_transport_connect_sub_job.cc
+++ b/net/socket/websocket_transport_connect_sub_job.cc
@@ -241,7 +241,7 @@
       one_address, nullptr, net_log().net_log(), net_log().source());
   // This use of base::Unretained() is safe because transport_socket_ is
   // destroyed in the destructor.
-  return transport_socket_->Connect(base::Bind(
+  return transport_socket_->Connect(base::BindOnce(
       &WebSocketTransportConnectSubJob::OnIOComplete, base::Unretained(this)));
 }
 
diff --git a/services/device/public/cpp/test/test_wake_lock_provider.cc b/services/device/public/cpp/test/test_wake_lock_provider.cc
index 543c4da..7771d3c 100644
--- a/services/device/public/cpp/test/test_wake_lock_provider.cc
+++ b/services/device/public/cpp/test/test_wake_lock_provider.cc
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
 #include "services/device/public/mojom/constants.mojom.h"
 
 namespace device {
@@ -118,7 +119,6 @@
 // would be 3.
 struct TestWakeLockProvider::WakeLockDataPerType {
   WakeLockDataPerType() = default;
-  WakeLockDataPerType(WakeLockDataPerType&&) = default;
   ~WakeLockDataPerType() = default;
 
   // Currently held count of this wake lock type.
@@ -129,7 +129,7 @@
   std::map<TestWakeLock*, std::unique_ptr<TestWakeLock>> wake_locks;
 
   // Observers for this wake lock type.
-  mojo::InterfacePtrSet<mojom::WakeLockObserver> observers;
+  mojo::RemoteSet<mojom::WakeLockObserver> observers;
 
   DISALLOW_COPY_AND_ASSIGN(WakeLockDataPerType);
 };
@@ -217,22 +217,21 @@
   // Notify observers of the last cancelation i.e. deactivation of wake lock
   // type |type|.
   if (new_count == 0) {
-    GetWakeLockDataPerType(type).observers.ForAllPtrs(
-        [type](mojom::WakeLockObserver* wake_lock_observer) {
-          wake_lock_observer->OnWakeLockDeactivated(type);
-        });
+    for (auto& observer : GetWakeLockDataPerType(type).observers)
+      observer->OnWakeLockDeactivated(type);
   }
 }
 
 void TestWakeLockProvider::NotifyOnWakeLockDeactivation(
     mojom::WakeLockType type,
-    mojom::WakeLockObserverPtr observer) {
+    mojo::PendingRemote<mojom::WakeLockObserver> pending_observer) {
+  mojo::Remote<mojom::WakeLockObserver> observer(std::move(pending_observer));
   // Notify observer immediately if wake lock is deactivated. Add it to the
   // observers list for future deactivation notifications.
   if (GetWakeLockDataPerType(type).count == 0) {
     observer->OnWakeLockDeactivated(type);
   }
-  GetWakeLockDataPerType(type).observers.AddPtr(std::move(observer));
+  GetWakeLockDataPerType(type).observers.Add(std::move(observer));
 }
 
 void TestWakeLockProvider::GetActiveWakeLocksForTests(
diff --git a/services/device/public/cpp/test/test_wake_lock_provider.h b/services/device/public/cpp/test/test_wake_lock_provider.h
index f34fb9a..691beea 100644
--- a/services/device/public/cpp/test/test_wake_lock_provider.h
+++ b/services/device/public/cpp/test/test_wake_lock_provider.h
@@ -12,9 +12,9 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/device/public/mojom/wake_lock.mojom.h"
 #include "services/device/public/mojom/wake_lock_provider.mojom.h"
 #include "services/service_manager/public/cpp/service.h"
@@ -46,7 +46,7 @@
                                  mojom::WakeLockRequest request) override;
   void NotifyOnWakeLockDeactivation(
       mojom::WakeLockType type,
-      mojom::WakeLockObserverPtr observer) override;
+      mojo::PendingRemote<mojom::WakeLockObserver> pending_observer) override;
   void GetActiveWakeLocksForTests(
       mojom::WakeLockType type,
       GetActiveWakeLocksForTestsCallback callback) override;
diff --git a/services/device/public/mojom/wake_lock_provider.mojom b/services/device/public/mojom/wake_lock_provider.mojom
index 2e89db96..00cf997 100644
--- a/services/device/public/mojom/wake_lock_provider.mojom
+++ b/services/device/public/mojom/wake_lock_provider.mojom
@@ -27,7 +27,8 @@
   // Notifies the caller if no wake lock of type |type| is currently activated.
   // If it is activated then the caller is notified on each deactivation event
   // after this call.
-  NotifyOnWakeLockDeactivation(WakeLockType type, WakeLockObserver observer);
+  NotifyOnWakeLockDeactivation(WakeLockType type,
+                               pending_remote<WakeLockObserver> observer);
 
   // Test-only method that returns the number of activated wake locks of type
   // |type|.
diff --git a/services/device/wake_lock/wake_lock_provider.cc b/services/device/wake_lock/wake_lock_provider.cc
index a50ef84..69b74d8 100644
--- a/services/device/wake_lock/wake_lock_provider.cc
+++ b/services/device/wake_lock/wake_lock_provider.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "mojo/public/cpp/bindings/remote_set.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/device/wake_lock/wake_lock.h"
 
@@ -18,7 +19,6 @@
 // would be 3.
 struct WakeLockProvider::WakeLockDataPerType {
   WakeLockDataPerType() = default;
-  WakeLockDataPerType(WakeLockDataPerType&&) = default;
   ~WakeLockDataPerType() = default;
 
   // Currently activated wake locks of this wake lock type.
@@ -29,7 +29,7 @@
   std::map<WakeLock*, std::unique_ptr<WakeLock>> wake_locks;
 
   // Observers for this wake lock type.
-  mojo::InterfacePtrSet<mojom::WakeLockObserver> observers;
+  mojo::RemoteSet<mojom::WakeLockObserver> observers;
 
   DISALLOW_COPY_AND_ASSIGN(WakeLockDataPerType);
 };
@@ -79,13 +79,14 @@
 
 void WakeLockProvider::NotifyOnWakeLockDeactivation(
     mojom::WakeLockType type,
-    mojom::WakeLockObserverPtr observer) {
+    mojo::PendingRemote<mojom::WakeLockObserver> pending_observer) {
+  mojo::Remote<mojom::WakeLockObserver> observer(std::move(pending_observer));
   // If |type| is not held then notify the observer immediately. Add it to the
   // observer list for future deactivation notifications.
   if (GetWakeLockDataPerType(type).count == 0) {
     observer->OnWakeLockDeactivated(type);
   }
-  GetWakeLockDataPerType(type).observers.AddPtr(std::move(observer));
+  GetWakeLockDataPerType(type).observers.Add(std::move(observer));
 }
 
 void WakeLockProvider::GetActiveWakeLocksForTests(
@@ -112,10 +113,8 @@
   // Notify observers of the last cancelation i.e. deactivation of wake lock
   // type |type|.
   if (new_count == 0) {
-    GetWakeLockDataPerType(type).observers.ForAllPtrs(
-        [type](mojom::WakeLockObserver* wake_lock_observer) {
-          wake_lock_observer->OnWakeLockDeactivated(type);
-        });
+    for (auto& observer : GetWakeLockDataPerType(type).observers)
+      observer->OnWakeLockDeactivated(type);
   }
 }
 
diff --git a/services/device/wake_lock/wake_lock_provider.h b/services/device/wake_lock/wake_lock_provider.h
index 8d38ef7..eece0f9 100644
--- a/services/device/wake_lock/wake_lock_provider.h
+++ b/services/device/wake_lock/wake_lock_provider.h
@@ -11,9 +11,9 @@
 
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
-#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/mojom/wake_lock_context.mojom.h"
 #include "services/device/public/mojom/wake_lock_provider.mojom.h"
 #include "services/device/wake_lock/wake_lock.h"
@@ -43,7 +43,7 @@
                                  mojom::WakeLockRequest request) override;
   void NotifyOnWakeLockDeactivation(
       mojom::WakeLockType type,
-      mojom::WakeLockObserverPtr observer) override;
+      mojo::PendingRemote<mojom::WakeLockObserver> pending_observer) override;
   void GetActiveWakeLocksForTests(
       mojom::WakeLockType type,
       GetActiveWakeLocksForTestsCallback callback) override;
diff --git a/services/device/wake_lock/wake_lock_unittest.cc b/services/device/wake_lock/wake_lock_unittest.cc
index 4bed8723c..47e0a264c 100644
--- a/services/device/wake_lock/wake_lock_unittest.cc
+++ b/services/device/wake_lock/wake_lock_unittest.cc
@@ -10,7 +10,9 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/device/device_service_test_base.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/device/public/mojom/wake_lock.mojom.h"
@@ -35,8 +37,8 @@
 
   ~TestWakeLockObserver() override = default;
 
-  void AddBinding(mojom::WakeLockObserverRequest request) {
-    bindings_.AddBinding(this, std::move(request));
+  void AddReceiver(mojo::PendingReceiver<mojom::WakeLockObserver> receiver) {
+    receivers_.Add(this, std::move(receiver));
   }
 
   // mojom::WakeLockObserver overrides.
@@ -56,7 +58,7 @@
     int64_t on_deactivation_count = 0;
   };
 
-  mojo::BindingSet<mojom::WakeLockObserver> bindings_;
+  mojo::ReceiverSet<mojom::WakeLockObserver> receivers_;
 
   std::map<mojom::WakeLockType, EventCount> wake_lock_events_;
 
@@ -416,9 +418,10 @@
 
 TEST_F(WakeLockTest, SameWakeLockTypeObserverTest) {
   // Set up observer for |kPreventAppSuspension| wake lock events.
-  mojom::WakeLockObserverPtr observer;
+  mojo::PendingRemote<mojom::WakeLockObserver> observer;
   TestWakeLockObserver test_wake_lock_observer;
-  test_wake_lock_observer.AddBinding(mojo::MakeRequest(&observer));
+  test_wake_lock_observer.AddReceiver(
+      observer.InitWithNewPipeAndPassReceiver());
   wake_lock_provider_->NotifyOnWakeLockDeactivation(
       mojom::WakeLockType::kPreventAppSuspension, std::move(observer));
   EXPECT_EQ(0, GetActiveWakeLocks(mojom::WakeLockType::kPreventAppSuspension));
@@ -482,13 +485,15 @@
   // Setup observer that will observe events for two different types of wake
   // locks. No wake locks should be active and deactivation counts should be
   // received for each type of observed wake lock.
-  mojom::WakeLockObserverPtr observer;
+  mojo::PendingRemote<mojom::WakeLockObserver> observer;
   TestWakeLockObserver test_wake_lock_observer;
-  test_wake_lock_observer.AddBinding(mojo::MakeRequest(&observer));
+  test_wake_lock_observer.AddReceiver(
+      observer.InitWithNewPipeAndPassReceiver());
   wake_lock_provider_->NotifyOnWakeLockDeactivation(
       mojom::WakeLockType::kPreventAppSuspension, std::move(observer));
   observer.reset();
-  test_wake_lock_observer.AddBinding(mojo::MakeRequest(&observer));
+  test_wake_lock_observer.AddReceiver(
+      observer.InitWithNewPipeAndPassReceiver());
   wake_lock_provider_->NotifyOnWakeLockDeactivation(
       mojom::WakeLockType::kPreventDisplaySleep, std::move(observer));
   EXPECT_EQ(0, GetActiveWakeLocks(mojom::WakeLockType::kPreventAppSuspension));
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index d6898e2..34a87ba 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -21,6 +21,7 @@
 #include "base/task/post_task.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
+#include "components/network_session_configurator/common/network_features.h"
 #include "components/os_crypt/os_crypt.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -442,12 +443,6 @@
   host_resolver_manager_->SetInsecureDnsClientEnabled(
       insecure_dns_client_enabled);
 
-  // Configure DNS over HTTPS.
-  if (!dns_over_https_servers || dns_over_https_servers.value().empty()) {
-    host_resolver_manager_->SetDnsConfigOverrides(net::DnsConfigOverrides());
-    return;
-  }
-
   for (auto* network_context : network_contexts_) {
     if (!network_context->IsPrimaryNetworkContext())
       continue;
@@ -456,13 +451,21 @@
         network_context->url_request_context());
   }
 
+  // Configure DNS over HTTPS.
   net::DnsConfigOverrides overrides;
-  overrides.dns_over_https_servers.emplace();
-  for (const auto& doh_server : *dns_over_https_servers) {
-    overrides.dns_over_https_servers.value().emplace_back(
-        doh_server->server_template, doh_server->use_post);
+  if (dns_over_https_servers && !dns_over_https_servers.value().empty()) {
+    overrides.dns_over_https_servers.emplace();
+    for (const auto& doh_server : *dns_over_https_servers) {
+      overrides.dns_over_https_servers.value().emplace_back(
+          doh_server->server_template, doh_server->use_post);
+    }
   }
   overrides.secure_dns_mode = secure_dns_mode;
+  overrides.allow_dns_over_https_upgrade =
+      base::FeatureList::IsEnabled(features::kDnsOverHttpsUpgrade);
+  overrides.disabled_upgrade_providers =
+      SplitString(features::kDnsOverHttpsUpgradeDisabledProvidersParam.Get(),
+                  ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   host_resolver_manager_->SetDnsConfigOverrides(overrides);
 }
 
@@ -712,16 +715,6 @@
 }
 
 void NetworkService::DestroyNetworkContexts() {
-  // If DNS over HTTPS is enabled, the HostResolver is currently using
-  // NetworkContexts to do DNS lookups, so need to tell the HostResolver
-  // to stop using DNS over HTTPS before destroying any NetworkContexts.
-  // The SetDnsConfigOverrides() call will will fail any in-progress DNS
-  // lookups, but only if there are current config overrides (which there will
-  // be if DNS over HTTPS is currently enabled).
-  if (host_resolver_manager_) {
-    host_resolver_manager_->SetDnsConfigOverrides(net::DnsConfigOverrides());
-  }
-
   // Delete NetworkContexts. If there's a primary NetworkContext, it must be
   // deleted after all other NetworkContexts, to avoid use-after-frees.
   for (auto it = owned_network_contexts_.begin();
diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc
index 2db0a317..56a5a53 100644
--- a/services/network/network_service_unittest.cc
+++ b/services/network/network_service_unittest.cc
@@ -17,10 +17,12 @@
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "net/base/escape.h"
+#include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/mock_network_change_notifier.h"
 #include "net/base/url_util.h"
@@ -30,6 +32,7 @@
 #include "net/dns/dns_test_util.h"
 #include "net/dns/host_resolver.h"
 #include "net/dns/host_resolver_manager.h"
+#include "net/dns/public/dns_protocol.h"
 #include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_auth_scheme.h"
 #include "net/net_buildflags.h"
@@ -43,6 +46,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_context.h"
 #include "services/network/network_context.h"
+#include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "services/network/public/mojom/net_log.mojom.h"
 #include "services/network/public/mojom/network_change_manager.mojom.h"
@@ -475,7 +479,7 @@
       net::DnsConfig::SecureDnsMode::AUTOMATIC,
       base::nullopt /* dns_over_https_servers */);
   EXPECT_FALSE(dns_client_ptr->CanUseInsecureDnsTransactions());
-  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF,
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::AUTOMATIC,
             dns_client_ptr->GetEffectiveConfig()->secure_dns_mode);
 
   std::vector<mojom::DnsOverHttpsServerPtr> dns_over_https_servers_ptr;
@@ -500,13 +504,6 @@
   const std::string kServer3 = "https://grapefruit/resolver/query{?dns}";
   const bool kServer3UsePost = false;
 
-  // Create the primary NetworkContext before enabling DNS over HTTPS.
-  mojom::NetworkContextPtr network_context;
-  mojom::NetworkContextParamsPtr context_params = CreateContextParams();
-  context_params->primary_network_context = true;
-  service()->CreateNetworkContext(mojo::MakeRequest(&network_context),
-                                  std::move(context_params));
-
   // Create valid DnsConfig.
   net::DnsConfig config;
   config.nameservers.push_back(net::IPEndPoint());
@@ -561,16 +558,54 @@
   EXPECT_EQ(kServer2UsePost, dns_over_https_servers[0].use_post);
   EXPECT_EQ(kServer3, dns_over_https_servers[1].server_template);
   EXPECT_EQ(kServer3UsePost, dns_over_https_servers[1].use_post);
+}
 
-  // Destroying the primary NetworkContext should disable DNS over HTTPS.
-  network_context.reset();
-  base::RunLoop().RunUntilIdle();
-  // DnsClient is still enabled.
-  EXPECT_TRUE(service()->host_resolver_manager()->GetDnsConfigAsValue());
-  // DNS over HTTPS is not.
-  dns_over_https_servers =
-      dns_client_ptr->GetEffectiveConfig()->dns_over_https_servers;
-  EXPECT_TRUE(dns_over_https_servers.empty());
+TEST_F(NetworkServiceTest, DisableDohUpgradeProviders) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitAndEnableFeatureWithParameters(
+      features::kDnsOverHttpsUpgrade,
+      {{"DisabledProviders", "CleanBrowsingSecure, , Cloudflare,Unexpected"}});
+  service()->ConfigureStubHostResolver(
+      true /* insecure_dns_client_enabled */,
+      net::DnsConfig::SecureDnsMode::AUTOMATIC,
+      base::nullopt /* dns_over_https_servers */);
+
+  // Set valid DnsConfig.
+  net::DnsConfig config;
+  // Cloudflare upgradeable IPs
+  net::IPAddress dns_ip0(1, 0, 0, 1);
+  net::IPAddress dns_ip1;
+  EXPECT_TRUE(dns_ip1.AssignFromIPLiteral("2606:4700:4700::1111"));
+  // CleanBrowsing family filter upgradeable IP
+  net::IPAddress dns_ip2;
+  EXPECT_TRUE(dns_ip2.AssignFromIPLiteral("2a0d:2a00:2::"));
+  // CleanBrowsing security filter upgradeable IP
+  net::IPAddress dns_ip3(185, 228, 169, 9);
+  // Non-upgradeable IP
+  net::IPAddress dns_ip4(1, 2, 3, 4);
+
+  config.nameservers.push_back(
+      net::IPEndPoint(dns_ip0, net::dns_protocol::kDefaultPort));
+  config.nameservers.push_back(
+      net::IPEndPoint(dns_ip1, net::dns_protocol::kDefaultPort));
+  config.nameservers.push_back(net::IPEndPoint(dns_ip2, 54));
+  config.nameservers.push_back(
+      net::IPEndPoint(dns_ip3, net::dns_protocol::kDefaultPort));
+  config.nameservers.push_back(
+      net::IPEndPoint(dns_ip4, net::dns_protocol::kDefaultPort));
+
+  auto dns_client = net::DnsClient::CreateClient(nullptr /* net_log */);
+  dns_client->SetSystemConfig(config);
+  net::DnsClient* dns_client_ptr = dns_client.get();
+  service()->host_resolver_manager()->SetDnsClientForTesting(
+      std::move(dns_client));
+
+  std::vector<net::DnsConfig::DnsOverHttpsServerConfig> expected_doh_servers = {
+      {"https://doh.cleanbrowsing.org/doh/family-filter{?dns}",
+       false /* use_post */}};
+  EXPECT_TRUE(dns_client_ptr->GetEffectiveConfig());
+  EXPECT_EQ(expected_doh_servers,
+            dns_client_ptr->GetEffectiveConfig()->dns_over_https_servers);
 }
 
 #endif  // !defined(OS_IOS)
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index 3f9668a..ea10768c 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -109,6 +109,24 @@
     "PrefetchMainResourceNetworkIsolationKey",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable usage of hardcoded DoH upgrade mapping for use in automatic mode.
+const base::Feature kDnsOverHttpsUpgrade {
+  "DnsOverHttpsUpgrade",
+#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_ANDROID) || \
+    defined(OS_WIN)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
+// Provides a mechanism to disable DoH upgrades for some subset of the hardcoded
+// upgrade mapping. Separate multiple provider ids with commas. See the
+// mapping in net/dns/dns_util.cc for provider ids.
+const base::FeatureParam<std::string>
+    kDnsOverHttpsUpgradeDisabledProvidersParam{&kDnsOverHttpsUpgrade,
+                                               "DisabledProviders", ""};
+
 bool ShouldEnableOutOfBlinkCors() {
   return base::FeatureList::IsEnabled(features::kOutOfBlinkCors);
 }
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index 7a13334..be538eb 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -7,6 +7,7 @@
 
 #include "base/component_export.h"
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 
 namespace network {
 namespace features {
@@ -43,6 +44,11 @@
 extern const base::Feature kBlockNonSecureExternalRequests;
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kPrefetchMainResourceNetworkIsolationKey;
+COMPONENT_EXPORT(NETWORK_CPP)
+extern const base::Feature kDnsOverHttpsUpgrade;
+COMPONENT_EXPORT(NETWORK_CPP)
+extern const base::FeatureParam<std::string>
+    kDnsOverHttpsUpgradeDisabledProvidersParam;
 
 COMPONENT_EXPORT(NETWORK_CPP) bool ShouldEnableOutOfBlinkCors();
 
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.cc b/services/network/public/cpp/host_resolver_mojom_traits.cc
index 3796681..6dafe5a6 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.cc
+++ b/services/network/public/cpp/host_resolver_mojom_traits.cc
@@ -207,6 +207,13 @@
 }
 
 // static
+DnsConfigOverrides::Tristate
+StructTraits<DnsConfigOverridesDataView, net::DnsConfigOverrides>::
+    allow_dns_over_https_upgrade(const net::DnsConfigOverrides& overrides) {
+  return ToTristate(overrides.allow_dns_over_https_upgrade);
+}
+
+// static
 bool StructTraits<DnsConfigOverridesDataView, net::DnsConfigOverrides>::Read(
     DnsConfigOverridesDataView data,
     net::DnsConfigOverrides* out) {
@@ -251,6 +258,11 @@
 
   out->secure_dns_mode = FromOptionalSecureDnsMode(data.secure_dns_mode());
 
+  out->allow_dns_over_https_upgrade =
+      FromTristate(data.allow_dns_over_https_upgrade());
+  if (!data.ReadDisabledUpgradeProviders(&out->disabled_upgrade_providers))
+    return false;
+
   return true;
 }
 
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.h b/services/network/public/cpp/host_resolver_mojom_traits.h
index b7e175b..1f1f1b1c 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.h
+++ b/services/network/public/cpp/host_resolver_mojom_traits.h
@@ -76,6 +76,14 @@
   static network::mojom::OptionalSecureDnsMode secure_dns_mode(
       const net::DnsConfigOverrides& overrides);
 
+  static network::mojom::DnsConfigOverrides::Tristate
+  allow_dns_over_https_upgrade(const net::DnsConfigOverrides& overrides);
+
+  static const base::Optional<std::vector<std::string>>&
+  disabled_upgrade_providers(const net::DnsConfigOverrides& overrides) {
+    return overrides.disabled_upgrade_providers;
+  }
+
   static bool Read(network::mojom::DnsConfigOverridesDataView data,
                    net::DnsConfigOverrides* out);
 };
diff --git a/services/network/public/cpp/host_resolver_mojom_traits_unittest.cc b/services/network/public/cpp/host_resolver_mojom_traits_unittest.cc
index 6a72b20..03f9f47 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/host_resolver_mojom_traits_unittest.cc
@@ -43,6 +43,8 @@
   original.dns_over_https_servers.emplace(
       {net::DnsConfig::DnsOverHttpsServerConfig("example.com", false)});
   original.secure_dns_mode = net::DnsConfig::SecureDnsMode::SECURE;
+  original.allow_dns_over_https_upgrade = true;
+  original.disabled_upgrade_providers.emplace({std::string("provider_name")});
 
   net::DnsConfigOverrides deserialized;
   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::DnsConfigOverrides>(
diff --git a/services/network/public/mojom/host_resolver.mojom b/services/network/public/mojom/host_resolver.mojom
index fce8cc7..c7e8519 100644
--- a/services/network/public/mojom/host_resolver.mojom
+++ b/services/network/public/mojom/host_resolver.mojom
@@ -102,6 +102,13 @@
   // The default SecureDnsMode to use when resolving queries. It can be
   // for individual requests such as requests to resolve a DoH server hostname.
   OptionalSecureDnsMode secure_dns_mode = OptionalSecureDnsMode.NO_OVERRIDE;
+
+  // Whether automatic upgrade to DNS over HTTPS servers is permitted.
+  Tristate allow_dns_over_https_upgrade = Tristate.NO_OVERRIDE;
+
+  // List of providers to exclude from upgrade mapping. See the
+  // mapping in net/dns/dns_util.cc for provider ids.
+  array<string>? disabled_upgrade_providers;
 };
 
 // Control handle used to control outstanding NetworkContext::ResolveHost
diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc
index b651165..62a8b56 100644
--- a/storage/browser/blob/blob_registry_impl_unittest.cc
+++ b/storage/browser/blob/blob_registry_impl_unittest.cc
@@ -20,6 +20,8 @@
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
@@ -227,9 +229,9 @@
 }
 
 TEST_F(BlobRegistryImplTest, Register_EmptyUUID) {
-  blink::mojom::BlobPtr blob;
+  mojo::Remote<blink::mojom::Blob> blob;
   EXPECT_FALSE(
-      registry_->Register(MakeRequest(&blob), "", "", "",
+      registry_->Register(blob.BindNewPipeAndPassReceiver(), "", "", "",
                           std::vector<blink::mojom::DataElementPtr>()));
 
   EXPECT_EQ(1u, bad_messages_.size());
@@ -238,7 +240,7 @@
   EXPECT_FALSE(registry_.is_connected());
 
   blob.FlushForTesting();
-  EXPECT_TRUE(blob.encountered_error());
+  EXPECT_FALSE(blob.is_connected());
   EXPECT_EQ(0u, BlobsUnderConstruction());
 }
 
@@ -247,9 +249,9 @@
   std::unique_ptr<BlobDataHandle> handle =
       CreateBlobFromString(kId, "hello world");
 
-  blink::mojom::BlobPtr blob;
+  mojo::Remote<blink::mojom::Blob> blob;
   EXPECT_FALSE(
-      registry_->Register(MakeRequest(&blob), kId, "", "",
+      registry_->Register(blob.BindNewPipeAndPassReceiver(), kId, "", "",
                           std::vector<blink::mojom::DataElementPtr>()));
 
   EXPECT_EQ(1u, bad_messages_.size());
@@ -258,7 +260,7 @@
   EXPECT_FALSE(registry_.is_connected());
 
   blob.FlushForTesting();
-  EXPECT_TRUE(blob.encountered_error());
+  EXPECT_FALSE(blob.is_connected());
   EXPECT_EQ(0u, BlobsUnderConstruction());
 }
 
@@ -267,9 +269,9 @@
   const std::string kContentType = "content/type";
   const std::string kContentDisposition = "disposition";
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, kContentType,
-                                  kContentDisposition,
+  mojo::Remote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.BindNewPipeAndPassReceiver(), kId,
+                                  kContentType, kContentDisposition,
                                   std::vector<blink::mojom::DataElementPtr>()));
 
   EXPECT_TRUE(bad_messages_.empty());
@@ -298,9 +300,9 @@
       blink::mojom::DataElement::NewBlob(blink::mojom::DataElementBlob::New(
           std::move(referenced_blob_info), 0, 16)));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
@@ -314,14 +316,15 @@
 TEST_F(BlobRegistryImplTest, Register_SelfReference) {
   const std::string kId = "id";
 
-  blink::mojom::BlobPtrInfo blob_info;
-  blink::mojom::BlobRequest blob_request = MakeRequest(&blob_info);
+  mojo::PendingRemote<blink::mojom::Blob> blob_remote;
+  mojo::PendingReceiver<blink::mojom::Blob> receiver =
+      blob_remote.InitWithNewPipeAndPassReceiver();
 
   std::vector<blink::mojom::DataElementPtr> elements;
   elements.push_back(blink::mojom::DataElement::NewBlob(
-      blink::mojom::DataElementBlob::New(std::move(blob_info), 0, 16)));
+      blink::mojom::DataElementBlob::New(std::move(blob_remote), 0, 16)));
 
-  EXPECT_TRUE(registry_->Register(std::move(blob_request), kId, "", "",
+  EXPECT_TRUE(registry_->Register(std::move(receiver), kId, "", "",
                                   std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
@@ -344,28 +347,32 @@
   const std::string kId2 = "id2";
   const std::string kId3 = "id3";
 
-  blink::mojom::BlobPtrInfo blob1_info, blob2_info, blob3_info;
-  blink::mojom::BlobRequest blob_request1 = MakeRequest(&blob1_info);
-  blink::mojom::BlobRequest blob_request2 = MakeRequest(&blob2_info);
-  blink::mojom::BlobRequest blob_request3 = MakeRequest(&blob3_info);
+  mojo::PendingRemote<blink::mojom::Blob> blob1_remote, blob2_remote,
+      blob3_remote;
+  mojo::PendingReceiver<blink::mojom::Blob> blob_receiver1 =
+      blob1_remote.InitWithNewPipeAndPassReceiver();
+  mojo::PendingReceiver<blink::mojom::Blob> blob_receiver2 =
+      blob2_remote.InitWithNewPipeAndPassReceiver();
+  mojo::PendingReceiver<blink::mojom::Blob> blob_receiver3 =
+      blob3_remote.InitWithNewPipeAndPassReceiver();
 
   std::vector<blink::mojom::DataElementPtr> elements1;
   elements1.push_back(blink::mojom::DataElement::NewBlob(
-      blink::mojom::DataElementBlob::New(std::move(blob1_info), 0, 16)));
+      blink::mojom::DataElementBlob::New(std::move(blob1_remote), 0, 16)));
 
   std::vector<blink::mojom::DataElementPtr> elements2;
   elements2.push_back(blink::mojom::DataElement::NewBlob(
-      blink::mojom::DataElementBlob::New(std::move(blob2_info), 0, 16)));
+      blink::mojom::DataElementBlob::New(std::move(blob2_remote), 0, 16)));
 
   std::vector<blink::mojom::DataElementPtr> elements3;
   elements3.push_back(blink::mojom::DataElement::NewBlob(
-      blink::mojom::DataElementBlob::New(std::move(blob3_info), 0, 16)));
+      blink::mojom::DataElementBlob::New(std::move(blob3_remote), 0, 16)));
 
-  EXPECT_TRUE(registry_->Register(std::move(blob_request1), kId1, "", "",
+  EXPECT_TRUE(registry_->Register(std::move(blob_receiver1), kId1, "", "",
                                   std::move(elements2)));
-  EXPECT_TRUE(registry_->Register(std::move(blob_request2), kId2, "", "",
+  EXPECT_TRUE(registry_->Register(std::move(blob_receiver2), kId2, "", "",
                                   std::move(elements3)));
-  EXPECT_TRUE(registry_->Register(std::move(blob_request3), kId3, "", "",
+  EXPECT_TRUE(registry_->Register(std::move(blob_receiver3), kId3, "", "",
                                   std::move(elements1)));
   EXPECT_TRUE(bad_messages_.empty());
 
@@ -415,9 +422,9 @@
       blink::mojom::DataElement::NewBlob(blink::mojom::DataElementBlob::New(
           std::move(referenced_blob_info), 0, 16)));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
@@ -443,8 +450,9 @@
                           MakeRequest(&blob1_info));
 
   const std::string kId2 = "id2";
-  blink::mojom::BlobPtrInfo blob2_info;
-  blink::mojom::BlobRequest blob_request2 = MakeRequest(&blob2_info);
+  mojo::PendingRemote<blink::mojom::Blob> blob2_remote;
+  mojo::PendingReceiver<blink::mojom::Blob> blob_receiver2 =
+      blob2_remote.InitWithNewPipeAndPassReceiver();
 
   std::vector<blink::mojom::DataElementPtr> elements1;
   elements1.push_back(blink::mojom::DataElement::NewBlob(
@@ -452,13 +460,13 @@
 
   std::vector<blink::mojom::DataElementPtr> elements2;
   elements2.push_back(blink::mojom::DataElement::NewBlob(
-      blink::mojom::DataElementBlob::New(std::move(blob2_info), 0, 8)));
+      blink::mojom::DataElementBlob::New(std::move(blob2_remote), 0, 8)));
 
-  blink::mojom::BlobPtr final_blob;
+  mojo::PendingRemote<blink::mojom::Blob> final_blob;
   const std::string kId3 = "id3";
-  EXPECT_TRUE(registry_->Register(MakeRequest(&final_blob), kId3, "", "",
-                                  std::move(elements2)));
-  EXPECT_TRUE(registry_->Register(std::move(blob_request2), kId2, "", "",
+  EXPECT_TRUE(registry_->Register(final_blob.InitWithNewPipeAndPassReceiver(),
+                                  kId3, "", "", std::move(elements2)));
+  EXPECT_TRUE(registry_->Register(std::move(blob_receiver2), kId2, "", "",
                                   std::move(elements1)));
 
   // kId3 references kId2, kId2 reference kId1, kId1 is a simple string.
@@ -491,9 +499,9 @@
       blink::mojom::DataElement::NewFile(blink::mojom::DataElementFile::New(
           base::FilePath(FILE_PATH_LITERAL("foobar")), 0, 16, base::nullopt)));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
@@ -515,9 +523,9 @@
   elements.push_back(blink::mojom::DataElement::NewFile(
       blink::mojom::DataElementFile::New(path, 0, 16, base::nullopt)));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
@@ -541,9 +549,9 @@
       blink::mojom::DataElementFilesystemURL::New(GURL("http://foobar.com/"), 0,
                                                   16, base::nullopt)));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
@@ -565,9 +573,9 @@
   elements.push_back(blink::mojom::DataElement::NewFileFilesystem(
       blink::mojom::DataElementFilesystemURL::New(url, 0, 16, base::nullopt)));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
@@ -589,9 +597,9 @@
   elements.push_back(blink::mojom::DataElement::NewFileFilesystem(
       blink::mojom::DataElementFilesystemURL::New(url, 0, 16, base::nullopt)));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
@@ -615,9 +623,9 @@
       blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New(
           10, std::vector<uint8_t>(5), CreateBytesProvider(""))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_FALSE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                   std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_FALSE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                   "", "", std::move(elements)));
   EXPECT_EQ(1u, bad_messages_.size());
 
   registry_.FlushForTesting();
@@ -649,9 +657,9 @@
           std::numeric_limits<uint64_t>::max() - 4, base::nullopt,
           CreateBytesProvider(""))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_FALSE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                   std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_FALSE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                   "", "", std::move(elements)));
   EXPECT_EQ(1u, bad_messages_.size());
 
   registry_.FlushForTesting();
@@ -684,9 +692,9 @@
           kTestBlobStorageMaxDiskSpace, base::nullopt,
           CreateBytesProvider(""))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
   WaitForBlobCompletion(handle.get());
@@ -710,9 +718,9 @@
           kData.size(), std::vector<uint8_t>(kData.begin(), kData.end()),
           CreateBytesProvider(kData))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
   WaitForBlobCompletion(handle.get());
@@ -740,9 +748,9 @@
       blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New(
           kData.size(), base::nullopt, CreateBytesProvider(kData))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
   WaitForBlobCompletion(handle.get());
@@ -771,9 +779,9 @@
       blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New(
           kData.size(), base::nullopt, CreateBytesProvider(kData))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
   WaitForBlobCompletion(handle.get());
@@ -807,9 +815,9 @@
       blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New(
           kData.size(), base::nullopt, CreateBytesProvider(kData))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
   WaitForBlobCompletion(handle.get());
@@ -853,9 +861,9 @@
       blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New(
           32, base::nullopt, std::move(bytes_provider_remote))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
@@ -878,9 +886,9 @@
       blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New(
           32, base::nullopt, std::move(bytes_provider_remote))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   EXPECT_TRUE(context_->registry().HasEntry(kId));
@@ -918,9 +926,9 @@
       blink::mojom::DataElement::NewBlob(blink::mojom::DataElementBlob::New(
           std::move(referenced_blob_info), 0, kData.size())));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
 
   EXPECT_TRUE(bad_messages_.empty());
 
@@ -956,9 +964,9 @@
       blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New(
           kData.size(), base::nullopt, std::move(bytes_provider_remote))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   EXPECT_TRUE(context_->registry().HasEntry(kId));
@@ -992,9 +1000,9 @@
       blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New(
           kData.size(), base::nullopt, std::move(bytes_provider_remote))));
 
-  blink::mojom::BlobPtr blob;
-  EXPECT_TRUE(registry_->Register(MakeRequest(&blob), kId, "", "",
-                                  std::move(elements)));
+  mojo::PendingRemote<blink::mojom::Blob> blob;
+  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
+                                  "", "", std::move(elements)));
   EXPECT_TRUE(bad_messages_.empty());
 
   EXPECT_TRUE(context_->registry().HasEntry(kId));
@@ -1044,8 +1052,8 @@
   EXPECT_EQ(kContentType, blob->content_type);
   EXPECT_EQ(kData.length(), blob->size);
   ASSERT_TRUE(blob->blob);
-  blink::mojom::BlobPtr blob_ptr(std::move(blob->blob));
-  EXPECT_EQ(blob->uuid, UUIDFromBlob(blob_ptr.get()));
+  mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob->blob));
+  EXPECT_EQ(blob->uuid, UUIDFromBlob(blob_remote.get()));
 
   EXPECT_EQ(kData.length(), progress_client.total_size);
   EXPECT_GE(progress_client.call_count, 1);
diff --git a/storage/browser/blob/blob_storage_context.cc b/storage/browser/blob/blob_storage_context.cc
index 253037f..18c9f9c6 100644
--- a/storage/browser/blob/blob_storage_context.cc
+++ b/storage/browser/blob/blob_storage_context.cc
@@ -25,6 +25,7 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_item.h"
 #include "storage/browser/blob/blob_data_snapshot.h"
@@ -74,14 +75,16 @@
   return CreateHandle(uuid, entry);
 }
 
-void BlobStorageContext::GetBlobDataFromBlobPtr(
-    blink::mojom::BlobPtr blob,
+void BlobStorageContext::GetBlobDataFromBlobRemote(
+    mojo::PendingRemote<blink::mojom::Blob> blob,
     base::OnceCallback<void(std::unique_ptr<BlobDataHandle>)> callback) {
   DCHECK(blob);
-  blink::mojom::Blob* raw_blob = blob.get();
+  mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob));
+  blink::mojom::Blob* raw_blob = blob_remote.get();
   raw_blob->GetInternalUUID(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
       base::BindOnce(
-          [](blink::mojom::BlobPtr, base::WeakPtr<BlobStorageContext> context,
+          [](mojo::Remote<blink::mojom::Blob>,
+             base::WeakPtr<BlobStorageContext> context,
              base::OnceCallback<void(std::unique_ptr<BlobDataHandle>)> callback,
              const std::string& uuid) {
             if (!context || uuid.empty()) {
@@ -90,7 +93,7 @@
             }
             std::move(callback).Run(context->GetBlobDataFromUUID(uuid));
           },
-          std::move(blob), AsWeakPtr(), std::move(callback)),
+          std::move(blob_remote), AsWeakPtr(), std::move(callback)),
       ""));
 }
 
diff --git a/storage/browser/blob/blob_storage_context.h b/storage/browser/blob/blob_storage_context.h
index 9730b0c..6f53380 100644
--- a/storage/browser/blob/blob_storage_context.h
+++ b/storage/browser/blob/blob_storage_context.h
@@ -20,6 +20,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/trace_event/memory_dump_provider.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_entry.h"
 #include "storage/browser/blob/blob_memory_controller.h"
@@ -63,8 +64,8 @@
   std::unique_ptr<BlobDataHandle> GetBlobDataFromPublicURL(const GURL& url);
   // If this BlobStorageContext is deleted before this method finishes, the
   // callback will still be called with null.
-  void GetBlobDataFromBlobPtr(
-      blink::mojom::BlobPtr blob,
+  void GetBlobDataFromBlobRemote(
+      mojo::PendingRemote<blink::mojom::Blob> blob,
       base::OnceCallback<void(std::unique_ptr<BlobDataHandle>)> callback);
 
   // Always returns a handle to a blob. Use BlobStatus::GetBlobStatus() and
diff --git a/storage/browser/blob/blob_url_store_impl.cc b/storage/browser/blob/blob_url_store_impl.cc
index cd3695e0..0d1b5b7 100644
--- a/storage/browser/blob/blob_url_store_impl.cc
+++ b/storage/browser/blob/blob_url_store_impl.cc
@@ -72,7 +72,7 @@
   }
 }
 
-void BlobURLStoreImpl::Register(blink::mojom::BlobPtr blob,
+void BlobURLStoreImpl::Register(mojo::PendingRemote<blink::mojom::Blob> blob,
                                 const GURL& url,
                                 RegisterCallback callback) {
   if (!url.SchemeIsBlob()) {
@@ -98,10 +98,11 @@
     return;
   }
 
-  blink::mojom::Blob* blob_ptr = blob.get();
+  mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob));
+  blink::mojom::Blob* blob_ptr = blob_remote.get();
   blob_ptr->GetInternalUUID(base::BindOnce(
       &BlobURLStoreImpl::RegisterWithUUID, weak_ptr_factory_.GetWeakPtr(),
-      std::move(blob), url, std::move(callback)));
+      std::move(blob_remote), url, std::move(callback)));
 }
 
 void BlobURLStoreImpl::Revoke(const GURL& url) {
@@ -162,7 +163,7 @@
   new BlobURLTokenImpl(context_, url, std::move(blob_handle), std::move(token));
 }
 
-void BlobURLStoreImpl::RegisterWithUUID(blink::mojom::BlobPtr blob,
+void BlobURLStoreImpl::RegisterWithUUID(mojo::Remote<blink::mojom::Blob> blob,
                                         const GURL& url,
                                         RegisterCallback callback,
                                         const std::string& uuid) {
diff --git a/storage/browser/blob/blob_url_store_impl.h b/storage/browser/blob/blob_url_store_impl.h
index 707d327..910b5a63 100644
--- a/storage/browser/blob/blob_url_store_impl.h
+++ b/storage/browser/blob/blob_url_store_impl.h
@@ -8,6 +8,8 @@
 #include <memory>
 
 #include "base/component_export.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "storage/browser/blob/blob_registry_impl.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h"
 
@@ -22,7 +24,7 @@
                    BlobRegistryImpl::Delegate* delegate);
   ~BlobURLStoreImpl() override;
 
-  void Register(blink::mojom::BlobPtr blob,
+  void Register(mojo::PendingRemote<blink::mojom::Blob> blob,
                 const GURL& url,
                 RegisterCallback callback) override;
   void Revoke(const GURL& url) override;
@@ -35,7 +37,7 @@
       mojo::PendingReceiver<blink::mojom::BlobURLToken> token) override;
 
  private:
-  void RegisterWithUUID(blink::mojom::BlobPtr blob,
+  void RegisterWithUUID(mojo::Remote<blink::mojom::Blob> blob,
                         const GURL& url,
                         RegisterCallback callback,
                         const std::string& uuid);
diff --git a/storage/browser/blob/blob_url_store_impl_unittest.cc b/storage/browser/blob/blob_url_store_impl_unittest.cc
index 332d77c..7965517 100644
--- a/storage/browser/blob/blob_url_store_impl_unittest.cc
+++ b/storage/browser/blob/blob_url_store_impl_unittest.cc
@@ -77,7 +77,7 @@
 
   void RegisterURL(BlobURLStore* store, BlobPtr blob, const GURL& url) {
     base::RunLoop loop;
-    store->Register(std::move(blob), url, loop.QuitClosure());
+    store->Register(blob.PassInterface(), url, loop.QuitClosure());
     loop.Run();
   }
 
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index dc6e57a..32ba13d 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -691,6 +691,1621 @@
       }
     ]
   },
+  "linux-chromeos-coverage-rel-dummy": {
+    "additional_compile_targets": [
+      "gn_all"
+    ],
+    "gtest_tests": [
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "accessibility_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "app_list_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "app_shell_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ash_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "aura_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "base_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "base_util_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_common_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_fuzzer_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_platform_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "webkit_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "boringssl_crypto_tests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "boringssl_ssl_tests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--disable-features=VizDisplayCompositor"
+        ],
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "non_viz_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter"
+        ],
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "webui_html_imports_polyfill_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=-*UsingRealWebcam*"
+        ],
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "chrome_app_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "chromedriver_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "chromeos_components_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "chromeos_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "components_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "compositor_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 6
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--disable-features=VizDisplayCompositor"
+        ],
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "non_viz_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--disable-perfetto",
+          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
+        ],
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "nonperfetto_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "args": [
+          "--disable-features=VizDisplayCompositor"
+        ],
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "non_viz_content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "dbus_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "device_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "display_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "events_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "exo_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "filesystem_service_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gin_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gl_unittests_ozone"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gwp_asan_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
+          "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_interactive_ui_tests.filter"
+        ],
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "webui_html_imports_polyfill_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "keyboard_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "latency_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "leveldb_service_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "libjingle_xmpp_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "media_service_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "media_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "message_center_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "mojo_core_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "nacl_helper_nonsfi_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "nacl_loader_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "net_unittests"
+      },
+      {
+        "args": [
+          "--ozone-platform=headless"
+        ],
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ozone_gl_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ozone_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ozone_x11_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "perfetto_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "service_manager_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "shell_dialogs_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "snapshot_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "sync_integration_tests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "traffic_annotation_auditor_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ui_chromeos_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "unit_tests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "url_unittests"
+      },
+      {
+        "experiment_percentage": 100,
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "usage_time_limit_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "views_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "viz_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "wayland_client_perftests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "wm_unittests"
+      },
+      {
+        "isolate_coverage_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "wtf_unittests"
+      }
+    ]
+  },
   "linux-chromeos-dbg": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 21c148c7..5845c9d4 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -1038,6 +1038,8 @@
       'chromeos-amd64-generic-google-rel',
       'chromeos-betty-google-rel',
       'chromeos-kevin-google-rel',
+      # code coverage, see https://crbug.com/1000367.
+      'linux-chromeos-coverage-rel-dummy',
     ]
 
   def check_input_file_consistency(self, verbose=False):
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 5041a8c..7c09e26 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -16,13 +16,6 @@
 # The goal is to drive the number of exceptions to zero, to make all
 # the bots behave similarly.
 {
-  'android_smoke_tests': {
-    'remove_from': [
-      # Enable on CQ when stable on CI.
-      'android-kitkat-arm-rel',
-      'android-marshmallow-arm64-rel',
-    ],
-  },
   'android_webview_unittests': {
     'remove_from': [
       # On chromium.android, these do not need to run prior to M.
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 965e54c..7859a749 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -722,6 +722,21 @@
         },
         'os_type': 'chromeos',
       },
+      # Replicate linux-chromeos-rel for code coverage experiment.
+      # TODO(crbug.com/1000367): Remove once linux-chromeos-coverage-rel is
+      # folded into linux-chromeos-rel.
+      'linux-chromeos-coverage-rel-dummy': {
+        'mixins': [
+          'linux-xenial',
+          'code-coverage',
+        ],
+        'additional_compile_targets': [
+          'gn_all',
+        ],
+        'test_suites': {
+          'gtest_tests': 'linux_chromeos_gtests',
+        },
+      },
       'linux-chromeos-dbg': {
         'mixins': [
           'linux-xenial',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b3454670..2ac37d7 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2447,8 +2447,7 @@
                 "android_webview",
                 "chromeos",
                 "linux",
-                "mac",
-                "windows"
+                "mac"
             ],
             "experiments": [
                 {
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 2bf2784..6632c171 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -179,6 +179,30 @@
 const base::Feature kRTCOfferExtmapAllowMixed{
     "RTCOfferExtmapAllowMixed", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables HW VP8 encoding on Android.
+const base::Feature kWebRtcHWVP8Encoding {
+  "WebRtcHWVP8Encoding",
+#if defined(OS_ANDROID)
+      base::FEATURE_DISABLED_BY_DEFAULT
+#else
+      base::FEATURE_ENABLED_BY_DEFAULT
+#endif
+};
+
+// Enables HW VP9 encoding on Android.
+const base::Feature kWebRtcHWVP9Encoding {
+  "WebRtcHWVP9Encoding",
+#if defined(OS_ANDROID)
+      base::FEATURE_DISABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
+// Enables HW H264 encoding on Android.
+const base::Feature kWebRtcHWH264Encoding{"WebRtcHWH264Encoding",
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
+
 #if BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
 // Run-time feature for the |rtc_use_h264| encoder/decoder.
 const base::Feature kWebRtcH264WithOpenH264FFmpeg{
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 9408195..3293392 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -160,6 +160,7 @@
     "platform/modules/peerconnection/rtc_event_log_output_sink.h",
     "platform/modules/peerconnection/rtc_event_log_output_sink_proxy_util.h",
     "platform/modules/peerconnection/rtc_video_decoder_factory.h",
+    "platform/modules/peerconnection/rtc_video_encoder_factory.h",
     "platform/modules/peerconnection/two_keys_adapter_map.h",
     "platform/modules/peerconnection/web_rtc_video_encoder_factory.h",
     "platform/modules/peerconnection/webrtc_audio_sink.h",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 52c8c080..3259d42 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -47,6 +47,9 @@
 BLINK_COMMON_EXPORT extern const base::Feature kRTCGetDisplayMedia;
 BLINK_COMMON_EXPORT extern const base::Feature kRTCUnifiedPlanByDefault;
 BLINK_COMMON_EXPORT extern const base::Feature kRTCOfferExtmapAllowMixed;
+BLINK_COMMON_EXPORT extern const base::Feature kWebRtcHWH264Encoding;
+BLINK_COMMON_EXPORT extern const base::Feature kWebRtcHWVP8Encoding;
+BLINK_COMMON_EXPORT extern const base::Feature kWebRtcHWVP9Encoding;
 
 #if BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
 BLINK_COMMON_EXPORT extern const base::Feature kWebRtcH264WithOpenH264FFmpeg;
diff --git a/third_party/blink/public/mojom/blob/blob_url_store.mojom b/third_party/blink/public/mojom/blob/blob_url_store.mojom
index 262143e3..33f4779 100644
--- a/third_party/blink/public/mojom/blob/blob_url_store.mojom
+++ b/third_party/blink/public/mojom/blob/blob_url_store.mojom
@@ -16,7 +16,7 @@
   // all URLs registered through it will be revoked.
   // TODO(kinuko,mek): This should probably create and return a new blob: URL rather
   // than letting the caller in the renderer provide one.
-  [Sync] Register(blink.mojom.Blob blob, url.mojom.Url url) => ();
+  [Sync] Register(pending_remote<blink.mojom.Blob> blob, url.mojom.Url url) => ();
 
   // Revokes a public Blob URL.
   Revoke(url.mojom.Url url);
diff --git a/third_party/blink/public/mojom/native_file_system/native_file_system_file_writer.mojom b/third_party/blink/public/mojom/native_file_system/native_file_system_file_writer.mojom
index 39edbcab..64d1623c 100644
--- a/third_party/blink/public/mojom/native_file_system/native_file_system_file_writer.mojom
+++ b/third_party/blink/public/mojom/native_file_system/native_file_system_file_writer.mojom
@@ -14,7 +14,7 @@
   // written.
   // TODO(mek): This might need some way of reporting progress events back to
   // the renderer.
-  Write(uint64 offset, Blob data) => (NativeFileSystemError result,
+  Write(uint64 offset, pending_remote<Blob> data) => (NativeFileSystemError result,
                                       uint64 bytes_written);
 
   // Write data from |stream| to the given |position| in the file being written
@@ -37,4 +37,4 @@
   // unsuccessful. Any temporary artifacts will be deleted.
   // Returns whether the operation succeeded.
   Close() => (NativeFileSystemError result);
-};
\ No newline at end of file
+};
diff --git a/content/renderer/media/webrtc/rtc_video_encoder_factory.h b/third_party/blink/public/platform/modules/peerconnection/rtc_video_encoder_factory.h
similarity index 72%
rename from content/renderer/media/webrtc/rtc_video_encoder_factory.h
rename to third_party/blink/public/platform/modules/peerconnection/rtc_video_encoder_factory.h
index a68c37a0..8dc853c8 100644
--- a/content/renderer/media/webrtc/rtc_video_encoder_factory.h
+++ b/third_party/blink/public/platform/modules/peerconnection/rtc_video_encoder_factory.h
@@ -2,27 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_RTC_VIDEO_ENCODER_FACTORY_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_RTC_VIDEO_ENCODER_FACTORY_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_RTC_VIDEO_ENCODER_FACTORY_H_
+#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_RTC_VIDEO_ENCODER_FACTORY_H_
 
 #include <vector>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "content/common/content_export.h"
 #include "media/base/video_codecs.h"
+#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/webrtc/api/video_codecs/video_encoder_factory.h"
 
 namespace media {
 class GpuVideoAcceleratorFactories;
 }  // namespace media
 
-namespace content {
+namespace blink {
 
 // This class creates RTCVideoEncoder instances (each wrapping a
 // media::VideoEncodeAccelerator) on behalf of the WebRTC stack.
-class CONTENT_EXPORT RTCVideoEncoderFactory
+//
+// TODO(crbug.com/787254): Move this class out of the Blink exposed API
+// when all users of it have been Onion souped.
+class BLINK_PLATFORM_EXPORT RTCVideoEncoderFactory
     : public webrtc::VideoEncoderFactory {
  public:
   explicit RTCVideoEncoderFactory(
@@ -48,6 +50,6 @@
   DISALLOW_COPY_AND_ASSIGN(RTCVideoEncoderFactory);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_RTC_VIDEO_ENCODER_FACTORY_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_RTC_VIDEO_ENCODER_FACTORY_H_
diff --git a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
index c7fe749..98de584 100644
--- a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
+++ b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
@@ -129,7 +129,10 @@
   // This means all setup is finally complete: the script has been loaded, the
   // worker thread has started, the script has been passed to the worker thread,
   // and CSP and ReferrerPolicy information has been set on the worker thread.
-  virtual void WillEvaluateScript() {}
+  //
+  // |v8_context| is the V8 context of the worker and is used to support
+  // service workers in Chrome extensions.
+  virtual void WillEvaluateScript(v8::Local<v8::Context> v8_context) {}
 
   // Called when initial script evaluation finished for the main script.
   // |success| is true if the evaluation completed with no uncaught exception.
@@ -139,23 +142,6 @@
   // initial method call after creating the worker scheduler.
   virtual void WillInitializeWorkerContext() {}
 
-  // Called when the worker context is initialized. This is probably called
-  // after WorkerContextStarted(). (WorkerThread::InitializeOnWorkerThread()
-  // calls WorkerContextStarted() via
-  // WorkerReportingProxy::DidCreateWorkerGlobalScope(),
-  // and then initializes the worker context if "needed" and calls
-  // DidInitializeWorkerContext(), but it's not clear when the context would
-  // already be initialized.)
-  //
-  // |context_proxy| is valid until WillDestroyWorkerContext() is called.
-  //
-  // This function is used to support service workers in Chrome extensions.
-  //
-  // TODO(nhiroki): Can you clarify this code and comment?
-  virtual void DidInitializeWorkerContext(
-      WebServiceWorkerContextProxy* context_proxy,
-      v8::Local<v8::Context> v8_context) {}
-
   // WorkerGlobalScope is about to be destroyed. The client should clear
   // the WebServiceWorkerGlobalScopeProxy when this is called.
   virtual void WillDestroyWorkerContext(v8::Local<v8::Context> context) {}
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 4e7954b..25c79ca 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -8227,10 +8227,14 @@
 
 void Document::CountPotentialFeaturePolicyViolation(
     mojom::FeaturePolicyFeature feature) const {
-  size_t index = static_cast<size_t>(feature);
-  if (potentially_violated_features_[index])
+  wtf_size_t index = static_cast<wtf_size_t>(feature);
+  if (potentially_violated_features_.size() == 0) {
+    potentially_violated_features_.resize(
+        static_cast<wtf_size_t>(mojom::FeaturePolicyFeature::kMaxValue) + 1);
+  } else if (potentially_violated_features_[index]) {
     return;
-  potentially_violated_features_.set(index);
+  }
+  potentially_violated_features_[index] = true;
   UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.FeaturePolicy.PotentialViolation",
                             feature);
 }
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 380ba3c9..1d3f649 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -2080,9 +2080,8 @@
 
   // Tracks which features have already been potentially violated in this
   // document. This helps to count them only once per page load.
-  mutable std::bitset<
-      static_cast<size_t>(mojom::FeaturePolicyFeature::kMaxValue) + 1>
-      potentially_violated_features_;
+  // We don't use std::bitset to avoid to include feature_policy.mojom-blink.h.
+  mutable Vector<bool> potentially_violated_features_;
 
   // Pending parsed headers to send to browser after DidCommitNavigation
   // IPC.
diff --git a/third_party/blink/renderer/core/execution_context/remote_security_context.h b/third_party/blink/renderer/core/execution_context/remote_security_context.h
index 9ef0272e5..c706510 100644
--- a/third_party/blink/renderer/core/execution_context/remote_security_context.h
+++ b/third_party/blink/renderer/core/execution_context/remote_security_context.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_REMOTE_SECURITY_CONTEXT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_REMOTE_SECURITY_CONTEXT_H_
 
+#include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/core/execution_context/security_context.h b/third_party/blink/renderer/core/execution_context/security_context.h
index c683c54..e3f4ac2 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.h
+++ b/third_party/blink/renderer/core/execution_context/security_context.h
@@ -27,29 +27,26 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_SECURITY_CONTEXT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_SECURITY_CONTEXT_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
-#include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "services/network/public/mojom/ip_address_space.mojom-blink-forward.h"
+#include "third_party/blink/public/common/frame/sandbox_flags.h"
+#include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/frame/sandbox_flags.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
-
-#include <memory>
-
-namespace network {
-namespace mojom {
-enum class IPAddressSpace : int32_t;
-}
-}  // namespace network
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
 
 namespace blink {
 
 class ContentSecurityPolicy;
 class FeaturePolicy;
+class PolicyValue;
 class SecurityOrigin;
 struct ParsedFeaturePolicyDeclaration;
 
@@ -60,11 +57,6 @@
 enum class ReportOptions { kReportOnFailure, kDoNotReport };
 enum class FeatureEnabledState { kDisabled, kReportOnly, kEnabled };
 
-namespace mojom {
-enum class FeaturePolicyDisposition : int32_t;
-enum class FeaturePolicyFeature : int32_t;
-}
-
 // Defines the security properties (such as the security origin, content
 // security policy, and other restrictions) of an environment in which
 // script execution or other activity may occur.
diff --git a/third_party/blink/renderer/core/fetch/blob_bytes_consumer_test.cc b/third_party/blink/renderer/core/fetch/blob_bytes_consumer_test.cc
index e7df5ba..21c35e3 100644
--- a/third_party/blink/renderer/core/fetch/blob_bytes_consumer_test.cc
+++ b/third_party/blink/renderer/core/fetch/blob_bytes_consumer_test.cc
@@ -146,10 +146,9 @@
 }
 
 TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_2) {
-  scoped_refptr<BlobDataHandle> blob_data_handle = BlobDataHandle::Create(
-      "uuid", "", std::numeric_limits<uint64_t>::max(),
-      CreateBlob("foo bar")->CloneBlobPtr().PassInterface());
-  ;
+  scoped_refptr<BlobDataHandle> blob_data_handle =
+      BlobDataHandle::Create("uuid", "", std::numeric_limits<uint64_t>::max(),
+                             CreateBlob("foo bar")->CloneBlobRemote());
   BlobBytesConsumer* consumer =
       MakeGarbageCollected<BlobBytesConsumer>(&GetDocument(), blob_data_handle);
 
@@ -167,10 +166,9 @@
 }
 
 TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_3) {
-  scoped_refptr<BlobDataHandle> blob_data_handle = BlobDataHandle::Create(
-      "uuid", "", std::numeric_limits<uint64_t>::max(),
-      CreateBlob("foo bar")->CloneBlobPtr().PassInterface());
-  ;
+  scoped_refptr<BlobDataHandle> blob_data_handle =
+      BlobDataHandle::Create("uuid", "", std::numeric_limits<uint64_t>::max(),
+                             CreateBlob("foo bar")->CloneBlobRemote());
   BlobBytesConsumer* consumer =
       MakeGarbageCollected<BlobBytesConsumer>(&GetDocument(), blob_data_handle);
 
diff --git a/third_party/blink/renderer/core/fileapi/blob.cc b/third_party/blink/renderer/core/fileapi/blob.cc
index a0b0c23..f53d7c4 100644
--- a/third_party/blink/renderer/core/fileapi/blob.cc
+++ b/third_party/blink/renderer/core/fileapi/blob.cc
@@ -258,8 +258,8 @@
   return instance;
 }
 
-mojom::blink::BlobPtr Blob::AsMojoBlob() {
-  return blob_data_handle_->CloneBlobPtr();
+mojo::PendingRemote<mojom::blink::Blob> Blob::AsMojoBlob() {
+  return blob_data_handle_->CloneBlobRemote();
 }
 
 // static
diff --git a/third_party/blink/renderer/core/fileapi/blob.h b/third_party/blink/renderer/core/fileapi/blob.h
index 5a711e2..f481ceb 100644
--- a/third_party/blink/renderer/core/fileapi/blob.h
+++ b/third_party/blink/renderer/core/fileapi/blob.h
@@ -32,6 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_BLOB_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/fileapi/file_reader_loader.h"
@@ -117,7 +118,7 @@
 
   // URLRegistrable to support PublicURLs.
   URLRegistry& Registry() const final;
-  mojom::blink::BlobPtr AsMojoBlob() final;
+  mojo::PendingRemote<mojom::blink::Blob> AsMojoBlob() final;
 
   // ImageBitmapSource implementation
   bool IsBlob() const override { return true; }
diff --git a/third_party/blink/renderer/core/fileapi/public_url_manager.cc b/third_party/blink/renderer/core/fileapi/public_url_manager.cc
index 31af057c..13b2150 100644
--- a/third_party/blink/renderer/core/fileapi/public_url_manager.cc
+++ b/third_party/blink/renderer/core/fileapi/public_url_manager.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/core/fileapi/public_url_manager.h"
 
 #include "base/metrics/histogram_macros.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/common/blob/blob_utils.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
 #include "third_party/blink/renderer/core/fileapi/url_registry.h"
@@ -111,7 +112,7 @@
   DCHECK(!url.IsEmpty());
   const String& url_string = url.GetString();
 
-  mojom::blink::BlobPtr blob = registrable->AsMojoBlob();
+  mojo::PendingRemote<mojom::blink::Blob> blob = registrable->AsMojoBlob();
   if (blob) {
     // Measure how much jank the following synchronous IPC introduces.
     SCOPED_UMA_HISTOGRAM_TIMER("Storage.Blob.RegisterPublicURLTime");
diff --git a/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc b/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc
index 49ff19ff..fcc95bb 100644
--- a/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc
+++ b/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc
@@ -6,7 +6,8 @@
 
 #include "base/test/scoped_feature_list.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/core/fileapi/url_registry.h"
@@ -20,28 +21,28 @@
 namespace blink {
 namespace {
 
-using mojom::blink::Blob;
-using mojom::blink::BlobPtr;
 using mojom::blink::BlobURLStore;
 
 class TestURLRegistrable : public URLRegistrable {
  public:
-  TestURLRegistrable(URLRegistry* registry, BlobPtr blob = nullptr)
+  TestURLRegistrable(
+      URLRegistry* registry,
+      mojo::PendingRemote<mojom::blink::Blob> blob = mojo::NullRemote())
       : registry_(registry), blob_(std::move(blob)) {}
 
   URLRegistry& Registry() const override { return *registry_; }
 
-  BlobPtr AsMojoBlob() override {
+  mojo::PendingRemote<mojom::blink::Blob> AsMojoBlob() override {
     if (!blob_)
-      return nullptr;
-    BlobPtr result;
-    blob_->Clone(MakeRequest(&result));
+      return mojo::NullRemote();
+    mojo::PendingRemote<mojom::blink::Blob> result;
+    blob_->Clone(result.InitWithNewPipeAndPassReceiver());
     return result;
   }
 
  private:
   URLRegistry* const registry_;
-  BlobPtr blob_;
+  mojo::Remote<mojom::blink::Blob> blob_;
 };
 
 class FakeURLRegistry : public URLRegistry {
@@ -83,10 +84,10 @@
     return execution_context_->GetPublicURLManager();
   }
 
-  BlobPtr CreateMojoBlob(const String& uuid) {
-    BlobPtr result;
-    mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid),
-                            MakeRequest(&result));
+  mojo::PendingRemote<mojom::blink::Blob> CreateMojoBlob(const String& uuid) {
+    mojo::PendingRemote<mojom::blink::Blob> result;
+    mojo::MakeSelfOwnedReceiver(std::make_unique<FakeBlob>(uuid),
+                                result.InitWithNewPipeAndPassReceiver());
     return result;
   }
 
diff --git a/third_party/blink/renderer/core/fileapi/url_registry.h b/third_party/blink/renderer/core/fileapi/url_registry.h
index e68d8ae6..e89d005 100644
--- a/third_party/blink/renderer/core/fileapi/url_registry.h
+++ b/third_party/blink/renderer/core/fileapi/url_registry.h
@@ -31,6 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_URL_REGISTRY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_URL_REGISTRY_H_
 
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -45,7 +46,9 @@
  public:
   virtual ~URLRegistrable() = default;
   virtual URLRegistry& Registry() const = 0;
-  virtual mojom::blink::BlobPtr AsMojoBlob() { return nullptr; }
+  virtual mojo::PendingRemote<mojom::blink::Blob> AsMojoBlob() {
+    return mojo::NullRemote();
+  }
 };
 
 class CORE_EXPORT URLRegistry {
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
index a8ce21a..004317075 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
@@ -130,9 +130,8 @@
     result.blobs.push_back(mojom::SerializedBlob::New(
         blob.value->Uuid().Utf8(), blob.value->GetType().Utf8(),
         blob.value->size(),
-        mojom::BlobPtrInfo(
-            blob.value->CloneBlobPtr().PassInterface().PassHandle(),
-            mojom::Blob::Version_)));
+        mojom::BlobPtrInfo(blob.value->CloneBlobRemote().PassPipe(),
+                           mojom::Blob::Version_)));
   }
   result.stack_trace_id = message.sender_stack_trace_id.id;
   result.stack_trace_debugger_id_first =
diff --git a/third_party/blink/renderer/core/mojo/BUILD.gn b/third_party/blink/renderer/core/mojo/BUILD.gn
index ac37110..eb1f279 100644
--- a/third_party/blink/renderer/core/mojo/BUILD.gn
+++ b/third_party/blink/renderer/core/mojo/BUILD.gn
@@ -58,6 +58,6 @@
 
 mojom("test_bindings") {
   sources = [
-    "tests/JsToCpp.mojom",
+    "tests/js_to_cpp.mojom",
   ]
 }
diff --git a/third_party/blink/renderer/core/mojo/tests/JsToCpp.mojom b/third_party/blink/renderer/core/mojo/tests/js_to_cpp.mojom
similarity index 96%
rename from third_party/blink/renderer/core/mojo/tests/JsToCpp.mojom
rename to third_party/blink/renderer/core/mojo/tests/js_to_cpp.mojom
index 3e4bd1a2..ede432b 100644
--- a/third_party/blink/renderer/core/mojo/tests/JsToCpp.mojom
+++ b/third_party/blink/renderer/core/mojo/tests/js_to_cpp.mojom
@@ -51,7 +51,7 @@
 };
 
 interface JsSide {
-  SetCppSide(CppSide cpp);
+  SetCppSide(pending_remote<CppSide> cpp);
 
   Ping();
   Echo(int32 numIterations, EchoArgs arg);
diff --git a/third_party/blink/renderer/core/mojo/tests/js_to_cpp_test.cc b/third_party/blink/renderer/core/mojo/tests/js_to_cpp_test.cc
index b924f4ae..827599e2f 100644
--- a/third_party/blink/renderer/core/mojo/tests/js_to_cpp_test.cc
+++ b/third_party/blink/renderer/core/mojo/tests/js_to_cpp_test.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/stl_util.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
@@ -14,7 +14,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/mojo/mojo_handle.h"
-#include "third_party/blink/renderer/core/mojo/tests/JsToCpp.mojom-blink.h"
+#include "third_party/blink/renderer/core/mojo/tests/js_to_cpp.mojom-blink.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
@@ -57,7 +57,7 @@
 
 String TestBindingsScriptPath() {
   return test::ExecutableDir() +
-         "/gen/third_party/blink/renderer/core/mojo/tests/JsToCpp.mojom.js";
+         "/gen/third_party/blink/renderer/core/mojo/tests/js_to_cpp.mojom.js";
 }
 
 String TestScriptPath() {
@@ -216,18 +216,17 @@
 // run_loop().
 class CppSideConnection : public js_to_cpp::blink::CppSide {
  public:
-  CppSideConnection() : mishandled_messages_(0), binding_(this) {}
+  CppSideConnection() : mishandled_messages_(0) {}
   ~CppSideConnection() override = default;
 
-  void set_js_side(js_to_cpp::blink::JsSidePtr js_side) {
+  void set_js_side(mojo::Remote<js_to_cpp::blink::JsSide> js_side) {
     js_side_ = std::move(js_side);
   }
-  js_to_cpp::blink::JsSide* js_side() { return js_side_.get(); }
 
-  void Bind(mojo::InterfaceRequest<js_to_cpp::blink::CppSide> request) {
-    binding_.Bind(std::move(request));
+  void Bind(mojo::PendingReceiver<js_to_cpp::blink::CppSide> receiver) {
+    receiver_.Bind(std::move(receiver));
     // Keep the pipe open even after validation errors.
-    binding_.EnableTestingMode();
+    receiver_.EnableTestingMode();
   }
 
   // js_to_cpp::CppSide:
@@ -252,9 +251,9 @@
   }
 
  protected:
-  js_to_cpp::blink::JsSidePtr js_side_;
+  mojo::Remote<js_to_cpp::blink::JsSide> js_side_;
   int mishandled_messages_;
-  mojo::Binding<js_to_cpp::blink::CppSide> binding_;
+  mojo::Receiver<js_to_cpp::blink::CppSide> receiver_{this};
 };
 
 // Trivial test to verify a message sent from JS is received.
@@ -372,13 +371,13 @@
 class JsToCppTest : public testing::Test {
  public:
   void RunTest(CppSideConnection* cpp_side) {
-    js_to_cpp::blink::CppSidePtr cpp_side_ptr;
-    cpp_side->Bind(MakeRequest(&cpp_side_ptr));
+    mojo::PendingRemote<js_to_cpp::blink::CppSide> cpp_side_remote;
+    cpp_side->Bind(cpp_side_remote.InitWithNewPipeAndPassReceiver());
 
-    js_to_cpp::blink::JsSidePtr js_side_ptr;
-    auto js_side_request = MakeRequest(&js_side_ptr);
-    js_side_ptr->SetCppSide(std::move(cpp_side_ptr));
-    cpp_side->set_js_side(std::move(js_side_ptr));
+    mojo::Remote<js_to_cpp::blink::JsSide> js_side_remote;
+    auto js_side_receiver = js_side_remote.BindNewPipeAndPassReceiver();
+    js_side_remote->SetCppSide(std::move(cpp_side_remote));
+    cpp_side->set_js_side(std::move(js_side_remote));
 
     V8TestingScope scope;
     scope.GetPage().GetSettings().SetScriptEnabled(true);
@@ -392,7 +391,7 @@
     v8::Local<v8::Object> global_proxy = scope.GetContext()->Global();
     v8::Local<v8::Value> args[1] = {
         ToV8(MakeGarbageCollected<MojoHandle>(
-                 mojo::ScopedHandle::From(js_side_request.PassMessagePipe())),
+                 mojo::ScopedHandle::From(js_side_receiver.PassPipe())),
              global_proxy, scope.GetIsolate())};
     V8ScriptRunner::CallFunction(start_fn.As<v8::Function>(),
                                  scope.GetExecutionContext(), global_proxy,
diff --git a/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h b/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h
index 23c5266..6e0d088 100644
--- a/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h
+++ b/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h
@@ -44,9 +44,9 @@
  public:
   FrameHostTestDocumentInterfaceBroker(
       mojom::blink::DocumentInterfaceBroker* document_interface_broker,
-      mojom::blink::DocumentInterfaceBrokerRequest request)
+      mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker> receiver)
       : TestDocumentInterfaceBroker(document_interface_broker,
-                                    std::move(request)) {}
+                                    std::move(receiver)) {}
 
   void GetFrameHostTestInterface(
       mojo::PendingReceiver<mojom::blink::FrameHostTestInterface> receiver)
diff --git a/third_party/blink/renderer/core/testing/test_document_interface_broker.cc b/third_party/blink/renderer/core/testing/test_document_interface_broker.cc
index 7d711d41..9ad8a94 100644
--- a/third_party/blink/renderer/core/testing/test_document_interface_broker.cc
+++ b/third_party/blink/renderer/core/testing/test_document_interface_broker.cc
@@ -10,9 +10,9 @@
 
 TestDocumentInterfaceBroker::TestDocumentInterfaceBroker(
     mojom::blink::DocumentInterfaceBroker* document_interface_broker,
-    mojom::blink::DocumentInterfaceBrokerRequest request)
+    mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker> receiver)
     : real_broker_(document_interface_broker),
-      binding_(this, std::move(request)) {}
+      receiver_(this, std::move(receiver)) {}
 
 TestDocumentInterfaceBroker::~TestDocumentInterfaceBroker() {}
 
@@ -22,7 +22,7 @@
 }
 
 void TestDocumentInterfaceBroker::Flush() {
-  binding_.FlushForTesting();
+  receiver_.FlushForTesting();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/test_document_interface_broker.h b/third_party/blink/renderer/core/testing/test_document_interface_broker.h
index 9e102722..7ab058e 100644
--- a/third_party/blink/renderer/core/testing/test_document_interface_broker.h
+++ b/third_party/blink/renderer/core/testing/test_document_interface_broker.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_TEST_DOCUMENT_INTERFACE_BROKER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_TEST_DOCUMENT_INTERFACE_BROKER_H_
 
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink-test-utils.h"
 
 namespace blink {
@@ -19,14 +19,14 @@
  public:
   TestDocumentInterfaceBroker(
       mojom::blink::DocumentInterfaceBroker* document_interface_broker,
-      mojom::blink::DocumentInterfaceBrokerRequest request);
+      mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker> receiver);
   ~TestDocumentInterfaceBroker() override;
   mojom::blink::DocumentInterfaceBroker* GetForwardingInterface() override;
   void Flush();
 
  private:
   mojom::blink::DocumentInterfaceBroker* real_broker_;
-  mojo::Binding<mojom::blink::DocumentInterfaceBroker> binding_;
+  mojo::Receiver<mojom::blink::DocumentInterfaceBroker> receiver_;
 };
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/devtools/front_end/Images/whatsnew.png b/third_party/blink/renderer/devtools/front_end/Images/whatsnew.png
index 72cc1676..397d4aa9 100644
--- a/third_party/blink/renderer/devtools/front_end/Images/whatsnew.png
+++ b/third_party/blink/renderer/devtools/front_end/Images/whatsnew.png
Binary files differ
diff --git a/third_party/blink/renderer/devtools/front_end/help/ReleaseNoteText.js b/third_party/blink/renderer/devtools/front_end/help/ReleaseNoteText.js
index 1bba5a4..02b11fe9 100644
--- a/third_party/blink/renderer/devtools/front_end/help/ReleaseNoteText.js
+++ b/third_party/blink/renderer/devtools/front_end/help/ReleaseNoteText.js
@@ -13,6 +13,30 @@
 /** @type {!Array<!Help.ReleaseNote>} */
 Help.releaseNoteText = [
   {
+    version: 20,
+    header: 'Highlights from the Chrome 78 update',
+    highlights: [
+      {
+        title: 'Lighthouse 5.2 in the Audits panel',
+        subtitle:
+            'Measure the impact of third-party code on your load performance with the new Third-Party Usage audit.',
+        link: 'https://developers.google.com/web/updates/2019/09/devtools#audits',
+      },
+      {
+        title: 'Largest Contentful Paint (LCP) in the Performance panel',
+        subtitle: 'Click the new LCP marker in the Timing section to see the DOM node associated with your LCP.',
+        link: 'https://developers.google.com/web/updates/2019/09/devtools#LCP',
+      },
+      {
+        title: 'File issues and feature requests from the Main Menu',
+        subtitle:
+            'Found a bug? Got an idea on how to improve DevTools? Go to Main Menu > Help > Report a DevTools issue.',
+        link: 'https://developers.google.com/web/updates/2019/09/devtools#issues',
+      },
+    ],
+    link: 'https://developers.google.com/web/updates/2019/09/devtools',
+  },
+  {
     version: 19,
     header: 'Highlights from the Chrome 77 update',
     highlights: [
diff --git a/third_party/blink/renderer/devtools/front_end/quick_open/HelpQuickOpen.js b/third_party/blink/renderer/devtools/front_end/quick_open/HelpQuickOpen.js
index 4399d74..a8757e18b 100644
--- a/third_party/blink/renderer/devtools/front_end/quick_open/HelpQuickOpen.js
+++ b/third_party/blink/renderer/devtools/front_end/quick_open/HelpQuickOpen.js
@@ -13,8 +13,8 @@
    * @param {!Runtime.Extension} extension
    */
   _addProvider(extension) {
-    if (extension.descriptor()['title'])
-      this._providers.push({prefix: extension.descriptor()['prefix'], title: extension.descriptor()['title']});
+    if (extension.title())
+      this._providers.push({prefix: extension.descriptor()['prefix'], title: extension.title()});
   }
 
   /**
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
index e5995f6..5cdce3a 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
@@ -40,6 +40,7 @@
 const size_t kMaxDeviceNameLength = 248;
 const char kDeviceNameTooLong[] =
     "A device name can't be longer than 248 bytes.";
+const char kInactiveDocumentError[] = "Document not active";
 }  // namespace
 
 static void CanonicalizeFilter(
@@ -141,6 +142,12 @@
 
 ScriptPromise Bluetooth::getAvailability(ScriptState* script_state) {
   ExecutionContext* context = GetExecutionContext();
+  if (!context || context->IsContextDestroyed()) {
+    return ScriptPromise::Reject(
+        script_state, V8ThrowException::CreateTypeError(
+                          script_state->GetIsolate(), kInactiveDocumentError));
+  }
+
   CHECK(context->IsSecureContext());
   EnsureServiceConnection();
 
@@ -198,7 +205,7 @@
   if (!frame) {
     return ScriptPromise::Reject(
         script_state, V8ThrowException::CreateTypeError(
-                          script_state->GetIsolate(), "Document not active"));
+                          script_state->GetIsolate(), kInactiveDocumentError));
   }
 
   if (!LocalFrame::HasTransientUserActivation(frame)) {
@@ -314,7 +321,7 @@
   if (!frame) {
     return ScriptPromise::Reject(
         script_state, V8ThrowException::CreateTypeError(
-                          script_state->GetIsolate(), "Document not active"));
+                          script_state->GetIsolate(), kInactiveDocumentError));
   }
 
   if (!LocalFrame::HasTransientUserActivation(frame)) {
@@ -448,6 +455,8 @@
   if (!service_) {
     // See https://bit.ly/2S0zRAS for task types.
     auto* context = GetExecutionContext();
+    DCHECK(context);
+
     auto task_runner = context->GetTaskRunner(TaskType::kMiscPlatformAPI);
     context->GetInterfaceProvider()->GetInterface(
         mojo::MakeRequest(&service_, task_runner));
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container_test.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container_test.cc
index ead9465..18ba4aa 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container_test.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container_test.cc
@@ -103,10 +103,10 @@
  public:
   MockCredentialManagerDocumentInterfaceBroker(
       mojom::blink::DocumentInterfaceBroker* document_interface_broker,
-      mojom::blink::DocumentInterfaceBrokerRequest request,
+      mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker> receiver,
       MockCredentialManager* mock_credential_manager)
       : TestDocumentInterfaceBroker(document_interface_broker,
-                                    std::move(request)),
+                                    std::move(receiver)),
         mock_credential_manager_(mock_credential_manager) {}
 
   void GetCredentialManager(
diff --git a/third_party/blink/renderer/modules/idle/idle_detector.cc b/third_party/blink/renderer/modules/idle/idle_detector.cc
index b4f72c27..282556a 100644
--- a/third_party/blink/renderer/modules/idle/idle_detector.cc
+++ b/third_party/blink/renderer/modules/idle/idle_detector.cc
@@ -8,6 +8,7 @@
 
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/idle/idle_manager.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
index c626129..64b3d20 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
@@ -206,17 +206,19 @@
   }
 }
 
-const mojom::blink::MediaStreamDispatcherHostPtr&
+const mojo::Remote<mojom::blink::MediaStreamDispatcherHost>&
 MediaStreamVideoCapturerSource::GetMediaStreamDispatcherHost() {
   DCHECK(frame_);
-  if (!host_)
-    frame_->GetInterfaceProvider().GetInterface(mojo::MakeRequest(&host_));
+  if (!host_) {
+    frame_->GetInterfaceProvider().GetInterface(
+        host_.BindNewPipeAndPassReceiver());
+  }
   return host_;
 }
 
 void MediaStreamVideoCapturerSource::SetMediaStreamDispatcherHostForTesting(
-    mojom::blink::MediaStreamDispatcherHostPtr host) {
-  host_ = std::move(host);
+    mojo::PendingRemote<mojom::blink::MediaStreamDispatcherHost> host) {
+  host_.Bind(std::move(host));
 }
 
 media::VideoCapturerSource*
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h
index 7d555cc2..94a08a98 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h
@@ -13,6 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "media/capture/video_capture_types.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/media/video_capture.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
@@ -52,7 +53,8 @@
       DeviceCapturerFactoryCallback testing_factory_callback);
 
   void SetMediaStreamDispatcherHostForTesting(
-      mojom::blink::MediaStreamDispatcherHostPtr dispatcher_host);
+      mojo::PendingRemote<mojom::blink::MediaStreamDispatcherHost>
+          dispatcher_host);
 
   media::VideoCapturerSource* GetSourceForTesting();
 
@@ -83,11 +85,11 @@
   void OnRunStateChanged(const media::VideoCaptureParams& new_capture_params,
                          bool is_running);
 
-  const mojom::blink::MediaStreamDispatcherHostPtr&
+  const mojo::Remote<mojom::blink::MediaStreamDispatcherHost>&
   GetMediaStreamDispatcherHost();
 
   WeakPersistent<LocalFrame> frame_;
-  mojom::blink::MediaStreamDispatcherHostPtr host_;
+  mojo::Remote<mojom::blink::MediaStreamDispatcherHost> host_;
 
   // The source that provides video frames.
   std::unique_ptr<media::VideoCapturerSource> source_;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
index 9ba6db13..9824f40 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "media/base/bind_to_current_loop.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
@@ -112,9 +113,8 @@
         WTF::BindRepeating(&MediaStreamVideoCapturerSourceTest::OnSourceStopped,
                            WTF::Unretained(this)),
         std::move(delegate));
-    mojom::blink::MediaStreamDispatcherHostPtr dispatcher_host =
-        mock_dispatcher_host_.CreateInterfacePtrAndBind();
-    source_->SetMediaStreamDispatcherHostForTesting(std::move(dispatcher_host));
+    source_->SetMediaStreamDispatcherHostForTesting(
+        mock_dispatcher_host_.CreatePendingRemoteAndBind());
     webkit_source_.Initialize(WebString::FromASCII("dummy_source_id"),
                               WebMediaStreamSource::kTypeVideo,
                               WebString::FromASCII("dummy_source_name"),
diff --git a/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.cc b/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.cc
index f2c005e..eb24eafb 100644
--- a/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.cc
+++ b/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.cc
@@ -10,16 +10,13 @@
 
 namespace blink {
 
-MockMojoMediaStreamDispatcherHost::MockMojoMediaStreamDispatcherHost()
-    : binding_(this) {}
+MockMojoMediaStreamDispatcherHost::MockMojoMediaStreamDispatcherHost() {}
 
 MockMojoMediaStreamDispatcherHost::~MockMojoMediaStreamDispatcherHost() {}
 
-mojom::blink::MediaStreamDispatcherHostPtr
-MockMojoMediaStreamDispatcherHost::CreateInterfacePtrAndBind() {
-  mojom::blink::MediaStreamDispatcherHostPtr dispatcher_host;
-  binding_.Bind(mojo::MakeRequest(&dispatcher_host));
-  return dispatcher_host;
+mojo::PendingRemote<mojom::blink::MediaStreamDispatcherHost>
+MockMojoMediaStreamDispatcherHost::CreatePendingRemoteAndBind() {
+  return receiver_.BindNewPipeAndPassRemote();
 }
 
 void MockMojoMediaStreamDispatcherHost::GenerateStream(
diff --git a/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.h b/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.h
index cab033b8..dc05c48 100644
--- a/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.h
+++ b/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.h
@@ -8,7 +8,8 @@
 #include <string>
 
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/common/mediastream/media_stream_controls.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
@@ -22,7 +23,8 @@
   MockMojoMediaStreamDispatcherHost();
   ~MockMojoMediaStreamDispatcherHost() override;
 
-  mojom::blink::MediaStreamDispatcherHostPtr CreateInterfacePtrAndBind();
+  mojo::PendingRemote<mojom::blink::MediaStreamDispatcherHost>
+  CreatePendingRemoteAndBind();
 
   void GenerateStream(
       int32_t request_id,
@@ -71,7 +73,7 @@
   WTF::Vector<MediaStreamDevice> audio_devices_;
   WTF::Vector<MediaStreamDevice> video_devices_;
   GenerateStreamCallback generate_stream_cb_;
-  mojo::Binding<mojom::blink::MediaStreamDispatcherHost> binding_;
+  mojo::Receiver<mojom::blink::MediaStreamDispatcherHost> receiver_{this};
 
   DISALLOW_COPY_AND_ASSIGN(MockMojoMediaStreamDispatcherHost);
 };
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc b/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
index f440e113..62b0c3d 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
@@ -491,10 +491,8 @@
     user_media_processor_ = MakeGarbageCollected<UserMediaProcessorUnderTest>(
         base::WrapUnique(msd_observer),
         std::move(user_media_processor_host_proxy), &state_);
-    blink::mojom::blink::MediaStreamDispatcherHostPtr dispatcher_host =
-        mock_dispatcher_host_.CreateInterfacePtrAndBind();
     user_media_processor_->set_media_stream_dispatcher_host_for_testing(
-        std::move(dispatcher_host));
+        mock_dispatcher_host_.CreatePendingRemoteAndBind());
 
     user_media_client_impl_ = MakeGarbageCollected<UserMediaClientUnderTest>(
         user_media_processor_, &state_);
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
index 5f7762c..061230f4 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -1641,11 +1641,11 @@
   return !local_sources_.IsEmpty();
 }
 
-const blink::mojom::blink::MediaStreamDispatcherHostPtr&
+const mojo::Remote<blink::mojom::blink::MediaStreamDispatcherHost>&
 UserMediaProcessor::GetMediaStreamDispatcherHost() {
   if (!dispatcher_host_) {
     frame_->GetInterfaceProvider().GetInterface(
-        mojo::MakeRequest(&dispatcher_host_));
+        dispatcher_host_.BindNewPipeAndPassReceiver());
   }
   return dispatcher_host_;
 }
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.h b/third_party/blink/renderer/modules/mediastream/user_media_processor.h
index 92cce59..7d1036ec 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.h
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
@@ -99,8 +100,9 @@
                        const blink::MediaStreamDevice& new_device);
 
   void set_media_stream_dispatcher_host_for_testing(
-      blink::mojom::blink::MediaStreamDispatcherHostPtr dispatcher_host) {
-    dispatcher_host_ = std::move(dispatcher_host);
+      mojo::PendingRemote<blink::mojom::blink::MediaStreamDispatcherHost>
+          dispatcher_host) {
+    dispatcher_host_.Bind(std::move(dispatcher_host));
   }
 
   void Trace(Visitor*);
@@ -244,7 +246,7 @@
   void StopLocalSource(const blink::WebMediaStreamSource& source,
                        bool notify_dispatcher);
 
-  const blink::mojom::blink::MediaStreamDispatcherHostPtr&
+  const mojo::Remote<blink::mojom::blink::MediaStreamDispatcherHost>&
   GetMediaStreamDispatcherHost();
   const blink::mojom::blink::MediaDevicesDispatcherHostPtr&
   GetMediaDevicesDispatcher();
@@ -285,7 +287,7 @@
   LocalStreamSources local_sources_;
   LocalStreamSources pending_local_sources_;
 
-  blink::mojom::blink::MediaStreamDispatcherHostPtr dispatcher_host_;
+  mojo::Remote<blink::mojom::blink::MediaStreamDispatcherHost> dispatcher_host_;
 
   // UserMedia requests are processed sequentially. |current_request_info_|
   // contains the request currently being processed, if any, and
diff --git a/third_party/blink/renderer/modules/payments/payment_request_event.cc b/third_party/blink/renderer/modules/payments/payment_request_event.cc
index b044251..df182783 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_event.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_event.cc
@@ -27,25 +27,24 @@
     const AtomicString& type,
     const PaymentRequestEventInit* initializer) {
   return MakeGarbageCollected<PaymentRequestEvent>(
-      type, initializer, payments::mojom::blink::PaymentHandlerHostPtrInfo(),
-      nullptr, nullptr);
+      type, initializer, mojo::NullRemote(), nullptr, nullptr);
 }
 
 PaymentRequestEvent* PaymentRequestEvent::Create(
     const AtomicString& type,
     const PaymentRequestEventInit* initializer,
-    payments::mojom::blink::PaymentHandlerHostPtrInfo host_info,
+    mojo::PendingRemote<payments::mojom::blink::PaymentHandlerHost> host,
     RespondWithObserver* respond_with_observer,
     WaitUntilObserver* wait_until_observer) {
   return MakeGarbageCollected<PaymentRequestEvent>(
-      type, initializer, std::move(host_info), respond_with_observer,
+      type, initializer, std::move(host), respond_with_observer,
       wait_until_observer);
 }
 
 PaymentRequestEvent::PaymentRequestEvent(
     const AtomicString& type,
     const PaymentRequestEventInit* initializer,
-    payments::mojom::blink::PaymentHandlerHostPtrInfo host_info,
+    mojo::PendingRemote<payments::mojom::blink::PaymentHandlerHost> host,
     RespondWithObserver* respond_with_observer,
     WaitUntilObserver* wait_until_observer)
     : ExtendableEvent(type, initializer, wait_until_observer),
@@ -62,11 +61,11 @@
                      : HeapVector<Member<PaymentDetailsModifier>>()),
       instrument_key_(initializer->instrumentKey()),
       observer_(respond_with_observer) {
-  if (!host_info.is_valid())
+  if (!host.is_valid())
     return;
 
-  payment_handler_host_.Bind(std::move(host_info));
-  payment_handler_host_.set_connection_error_handler(WTF::Bind(
+  payment_handler_host_.Bind(std::move(host));
+  payment_handler_host_.set_disconnect_handler(WTF::Bind(
       &PaymentRequestEvent::OnHostConnectionError, WrapWeakPersistent(this)));
 }
 
diff --git a/third_party/blink/renderer/modules/payments/payment_request_event.h b/third_party/blink/renderer/modules/payments/payment_request_event.h
index b82cf71..ca798d6 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_event.h
+++ b/third_party/blink/renderer/modules/payments/payment_request_event.h
@@ -6,6 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_PAYMENT_REQUEST_EVENT_H_
 
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/payments/payment_handler_host.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/modules/event_modules.h"
@@ -33,14 +35,14 @@
   static PaymentRequestEvent* Create(
       const AtomicString& type,
       const PaymentRequestEventInit*,
-      payments::mojom::blink::PaymentHandlerHostPtrInfo host_info,
+      mojo::PendingRemote<payments::mojom::blink::PaymentHandlerHost> host,
       RespondWithObserver*,
       WaitUntilObserver*);
 
   PaymentRequestEvent(
       const AtomicString& type,
       const PaymentRequestEventInit*,
-      payments::mojom::blink::PaymentHandlerHostPtrInfo host_info,
+      mojo::PendingRemote<payments::mojom::blink::PaymentHandlerHost> host,
       RespondWithObserver*,
       WaitUntilObserver*);
   ~PaymentRequestEvent() override;
@@ -82,7 +84,8 @@
 
   Member<ScriptPromiseResolver> change_payment_method_resolver_;
   Member<RespondWithObserver> observer_;
-  payments::mojom::blink::PaymentHandlerHostPtr payment_handler_host_;
+  mojo::Remote<payments::mojom::blink::PaymentHandlerHost>
+      payment_handler_host_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestEvent);
 };
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index dcc2d61..4a4ae32e 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -36,6 +36,7 @@
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
@@ -1879,8 +1880,8 @@
   PaymentRequestRespondWithObserver* respond_with_observer =
       PaymentRequestRespondWithObserver::Create(this, event_id,
                                                 wait_until_observer);
-  payments::mojom::blink::PaymentHandlerHostPtrInfo payment_handler_host =
-      std::move(event_data->payment_handler_host);
+  mojo::PendingRemote<payments::mojom::blink::PaymentHandlerHost>
+      payment_handler_host = std::move(event_data->payment_handler_host);
   Event* event = PaymentRequestEvent::Create(
       event_type_names::kPaymentrequest,
       PaymentEventDataConversion::ToPaymentRequestEventInit(
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
index 4368131..87bb62d 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
@@ -164,10 +164,6 @@
 
 void ServiceWorkerGlobalScopeProxy::DidInitializeWorkerContext() {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
-  ScriptState::Scope scope(
-      WorkerGlobalScope()->ScriptController()->GetScriptState());
-  Client().DidInitializeWorkerContext(
-      this, WorkerGlobalScope()->ScriptController()->GetContext());
   TRACE_EVENT_END0("ServiceWorker",
                    "ServiceWorkerGlobalScopeProxy::InitializeWorkerContext");
 }
@@ -211,7 +207,11 @@
   // metrics if the metrics are no longer referenced, and then merge
   // WillEvaluateClassicScript and WillEvaluateModuleScript for cleanup.
   worker_global_scope_->CountWorkerScript(script_size, cached_metadata_size);
-  Client().WillEvaluateScript();
+
+  ScriptState::Scope scope(
+      WorkerGlobalScope()->ScriptController()->GetScriptState());
+  Client().WillEvaluateScript(
+      WorkerGlobalScope()->ScriptController()->GetContext());
 }
 
 void ServiceWorkerGlobalScopeProxy::WillEvaluateImportedClassicScript(
@@ -223,7 +223,10 @@
 
 void ServiceWorkerGlobalScopeProxy::WillEvaluateModuleScript() {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
-  Client().WillEvaluateScript();
+  ScriptState::Scope scope(
+      WorkerGlobalScope()->ScriptController()->GetScriptState());
+  Client().WillEvaluateScript(
+      WorkerGlobalScope()->ScriptController()->GetContext());
 }
 
 void ServiceWorkerGlobalScopeProxy::DidEvaluateClassicScript(bool success) {
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 0753f7f..d2fcb71 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1241,6 +1241,7 @@
     "peerconnection/rtc_video_decoder_factory.cc",
     "peerconnection/rtc_video_encoder.cc",
     "peerconnection/rtc_video_encoder.h",
+    "peerconnection/rtc_video_encoder_factory.cc",
     "peerconnection/rtc_void_request.h",
     "peerconnection/webrtc_audio_sink.cc",
     "peerconnection/webrtc_video_track_source.cc",
diff --git a/third_party/blink/renderer/platform/blob/blob_data.cc b/third_party/blink/renderer/platform/blob/blob_data.cc
index 5c28f0e..3047792 100644
--- a/third_party/blink/renderer/platform/blob/blob_data.cc
+++ b/third_party/blink/renderer/platform/blob/blob_data.cc
@@ -55,8 +55,6 @@
 
 namespace blink {
 
-using mojom::blink::BlobPtr;
-using mojom::blink::BlobPtrInfo;
 using mojom::blink::BytesProvider;
 using mojom::blink::DataElement;
 using mojom::blink::DataElementBlob;
@@ -193,8 +191,8 @@
   // Skip zero-byte items, as they don't matter for the contents of the blob.
   if (length == 0)
     return;
-  elements_.push_back(DataElement::NewBlob(DataElementBlob::New(
-      data_handle->CloneBlobPtr().PassInterface(), offset, length)));
+  elements_.push_back(DataElement::NewBlob(
+      DataElementBlob::New(data_handle->CloneBlobRemote(), offset, length)));
 }
 
 void BlobData::AppendFileSystemURL(const KURL& url,
@@ -308,10 +306,10 @@
     const String& uuid,
     const String& type,
     uint64_t size,
-    mojom::blink::BlobPtrInfo blob_info) {
-  if (blob_info.is_valid()) {
+    mojo::PendingRemote<mojom::blink::Blob> blob_remote) {
+  if (blob_remote.is_valid()) {
     return base::AdoptRef(
-        new BlobDataHandle(uuid, type, size, std::move(blob_info)));
+        new BlobDataHandle(uuid, type, size, std::move(blob_remote)));
   }
   return base::AdoptRef(new BlobDataHandle(uuid, type, size));
 }
@@ -320,8 +318,8 @@
     : uuid_(WTF::CreateCanonicalUUIDString()),
       size_(0),
       is_single_unknown_size_file_(false) {
-  GetThreadSpecificRegistry()->Register(MakeRequest(&blob_info_), uuid_, "", "",
-                                        {});
+  GetThreadSpecificRegistry()->Register(
+      blob_remote_.InitWithNewPipeAndPassReceiver(), uuid_, "", "", {});
 }
 
 BlobDataHandle::BlobDataHandle(std::unique_ptr<BlobData> data, uint64_t size)
@@ -331,9 +329,9 @@
       is_single_unknown_size_file_(data->IsSingleUnknownSizeFile()) {
   TRACE_EVENT0("Blob", "Registry::RegisterBlob");
   SCOPED_BLINK_UMA_HISTOGRAM_TIMER_THREAD_SAFE("Storage.Blob.RegisterBlobTime");
-  GetThreadSpecificRegistry()->Register(MakeRequest(&blob_info_), uuid_,
-                                        type_.IsNull() ? "" : type_, "",
-                                        data->ReleaseElements());
+  GetThreadSpecificRegistry()->Register(
+      blob_remote_.InitWithNewPipeAndPassReceiver(), uuid_,
+      type_.IsNull() ? "" : type_, "", data->ReleaseElements());
 }
 
 BlobDataHandle::BlobDataHandle(const String& uuid,
@@ -345,55 +343,55 @@
       is_single_unknown_size_file_(false) {
   SCOPED_BLINK_UMA_HISTOGRAM_TIMER_THREAD_SAFE(
       "Storage.Blob.GetBlobFromUUIDTime");
-  GetThreadSpecificRegistry()->GetBlobFromUUID(MakeRequest(&blob_info_), uuid_);
+  GetThreadSpecificRegistry()->GetBlobFromUUID(
+      blob_remote_.InitWithNewPipeAndPassReceiver(), uuid_);
 }
 
-BlobDataHandle::BlobDataHandle(const String& uuid,
-                               const String& type,
-                               uint64_t size,
-                               BlobPtrInfo blob_info)
+BlobDataHandle::BlobDataHandle(
+    const String& uuid,
+    const String& type,
+    uint64_t size,
+    mojo::PendingRemote<mojom::blink::Blob> blob_remote)
     : uuid_(uuid.IsolatedCopy()),
       type_(IsValidBlobType(type) ? type.IsolatedCopy() : ""),
       size_(size),
       is_single_unknown_size_file_(false),
-      blob_info_(std::move(blob_info)) {
-  DCHECK(blob_info_.is_valid());
+      blob_remote_(std::move(blob_remote)) {
+  DCHECK(blob_remote_.is_valid());
 }
 
 BlobDataHandle::~BlobDataHandle() {
 }
 
-BlobPtr BlobDataHandle::CloneBlobPtr() {
-  MutexLocker locker(blob_info_mutex_);
-  if (!blob_info_.is_valid())
-    return nullptr;
-  BlobPtr blob, blob_clone;
-  blob.Bind(std::move(blob_info_));
-  blob->Clone(MakeRequest(&blob_clone));
-  blob_info_ = blob.PassInterface();
+mojo::PendingRemote<mojom::blink::Blob> BlobDataHandle::CloneBlobRemote() {
+  MutexLocker locker(blob_remote_mutex_);
+  if (!blob_remote_.is_valid())
+    return mojo::NullRemote();
+  mojo::Remote<mojom::blink::Blob> blob(std::move(blob_remote_));
+  mojo::PendingRemote<mojom::blink::Blob> blob_clone;
+  blob->Clone(blob_clone.InitWithNewPipeAndPassReceiver());
+  blob_remote_ = blob.Unbind();
   return blob_clone;
 }
 
 network::mojom::blink::DataPipeGetterPtr BlobDataHandle::AsDataPipeGetter() {
-  MutexLocker locker(blob_info_mutex_);
-  if (!blob_info_.is_valid())
+  MutexLocker locker(blob_remote_mutex_);
+  if (!blob_remote_.is_valid())
     return nullptr;
   network::mojom::blink::DataPipeGetterPtr result;
-  BlobPtr blob;
-  blob.Bind(std::move(blob_info_));
+  mojo::Remote<mojom::blink::Blob> blob(std::move(blob_remote_));
   blob->AsDataPipeGetter(MakeRequest(&result));
-  blob_info_ = blob.PassInterface();
+  blob_remote_ = blob.Unbind();
   return result;
 }
 
 void BlobDataHandle::ReadAll(
     mojo::ScopedDataPipeProducerHandle pipe,
     mojo::PendingRemote<mojom::blink::BlobReaderClient> client) {
-  MutexLocker locker(blob_info_mutex_);
-  BlobPtr blob;
-  blob.Bind(std::move(blob_info_));
+  MutexLocker locker(blob_remote_mutex_);
+  mojo::Remote<mojom::blink::Blob> blob(std::move(blob_remote_));
   blob->ReadAll(std::move(pipe), std::move(client));
-  blob_info_ = blob.PassInterface();
+  blob_remote_ = blob.Unbind();
 }
 
 void BlobDataHandle::ReadRange(
@@ -401,11 +399,10 @@
     uint64_t length,
     mojo::ScopedDataPipeProducerHandle pipe,
     mojo::PendingRemote<mojom::blink::BlobReaderClient> client) {
-  MutexLocker locker(blob_info_mutex_);
-  BlobPtr blob;
-  blob.Bind(std::move(blob_info_));
+  MutexLocker locker(blob_remote_mutex_);
+  mojo::Remote<mojom::blink::Blob> blob(std::move(blob_remote_));
   blob->ReadRange(offset, length, std::move(pipe), std::move(client));
-  blob_info_ = blob.PassInterface();
+  blob_remote_ = blob.Unbind();
 }
 
 // static
diff --git a/third_party/blink/renderer/platform/blob/blob_data.h b/third_party/blink/renderer/platform/blob/blob_data.h
index a52bfe7..212126e4 100644
--- a/third_party/blink/renderer/platform/blob/blob_data.h
+++ b/third_party/blink/renderer/platform/blob/blob_data.h
@@ -64,8 +64,6 @@
 namespace mojom {
 namespace blink {
 class Blob;
-using BlobPtr = mojo::InterfacePtr<Blob>;
-using BlobPtrInfo = mojo::InterfacePtrInfo<Blob>;
 
 class BlobReaderClient;
 
@@ -196,10 +194,11 @@
     return base::AdoptRef(new BlobDataHandle(uuid, type, size));
   }
 
-  static scoped_refptr<BlobDataHandle> Create(const String& uuid,
-                                              const String& type,
-                                              uint64_t size,
-                                              mojom::blink::BlobPtrInfo);
+  static scoped_refptr<BlobDataHandle> Create(
+      const String& uuid,
+      const String& type,
+      uint64_t size,
+      mojo::PendingRemote<mojom::blink::Blob>);
 
   String Uuid() const { return uuid_.IsolatedCopy(); }
   String GetType() const { return type_.IsolatedCopy(); }
@@ -209,7 +208,7 @@
 
   ~BlobDataHandle();
 
-  mojom::blink::BlobPtr CloneBlobPtr();
+  mojo::PendingRemote<mojom::blink::Blob> CloneBlobRemote();
   network::mojom::blink::DataPipeGetterPtr AsDataPipeGetter();
 
   void ReadAll(mojo::ScopedDataPipeProducerHandle,
@@ -229,18 +228,19 @@
   BlobDataHandle(const String& uuid,
                  const String& type,
                  uint64_t size,
-                 mojom::blink::BlobPtrInfo);
+                 mojo::PendingRemote<mojom::blink::Blob>);
 
   const String uuid_;
   const String type_;
   const uint64_t size_;
   const bool is_single_unknown_size_file_;
   // This class is supposed to be thread safe. So to be able to use the mojo
-  // Blob interface from multiple threads store a InterfacePtrInfo combined with
+  // Blob interface from multiple threads store a PendingRemote combined with
   // a mutex, and make sure any access to the mojo interface is done protected
   // by the mutex.
-  mojom::blink::BlobPtrInfo blob_info_ GUARDED_BY(blob_info_mutex_);
-  Mutex blob_info_mutex_;
+  mojo::PendingRemote<mojom::blink::Blob> blob_remote_
+      GUARDED_BY(blob_remote_mutex_);
+  Mutex blob_remote_mutex_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/blob/serialized_blob_mojom_traits.h b/third_party/blink/renderer/platform/blob/serialized_blob_mojom_traits.h
index e47e905..4653ec4 100644
--- a/third_party/blink/renderer/platform/blob/serialized_blob_mojom_traits.h
+++ b/third_party/blink/renderer/platform/blob/serialized_blob_mojom_traits.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_SERIALIZED_BLOB_MOJOM_TRAITS_H_
 
 #include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/string_traits_wtf.h"
 #include "third_party/blink/public/mojom/blob/serialized_blob.mojom-blink.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
@@ -38,9 +39,9 @@
     return input->size();
   }
 
-  static blink::mojom::blink::BlobPtr blob(
+  static mojo::PendingRemote<blink::mojom::blink::Blob> blob(
       const scoped_refptr<blink::BlobDataHandle>& input) {
-    return input->CloneBlobPtr();
+    return input->CloneBlobRemote();
   }
 
   static bool Read(blink::mojom::blink::SerializedBlob::DataView,
diff --git a/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.cc b/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.cc
index 677777e..12e2eb64 100644
--- a/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.cc
+++ b/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.cc
@@ -8,10 +8,10 @@
 
 namespace blink {
 
-void FakeBlobURLStore::Register(mojom::blink::BlobPtr blob,
+void FakeBlobURLStore::Register(mojo::PendingRemote<mojom::blink::Blob> blob,
                                 const KURL& url,
                                 RegisterCallback callback) {
-  registrations.insert(url, std::move(blob));
+  registrations.insert(url, mojo::Remote<mojom::blink::Blob>(std::move(blob)));
   std::move(callback).Run();
 }
 
diff --git a/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h b/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h
index 5f2a0f1..f26a0a5c 100644
--- a/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h
+++ b/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h
@@ -7,6 +7,8 @@
 
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink.h"
 
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -16,7 +18,9 @@
 // Mocked BlobURLStore implementation for testing.
 class FakeBlobURLStore : public mojom::blink::BlobURLStore {
  public:
-  void Register(mojom::blink::BlobPtr, const KURL&, RegisterCallback) override;
+  void Register(mojo::PendingRemote<mojom::blink::Blob>,
+                const KURL&,
+                RegisterCallback) override;
   void Revoke(const KURL&) override;
   void Resolve(const KURL&, ResolveCallback) override;
   void ResolveAsURLLoaderFactory(
@@ -26,7 +30,7 @@
       const KURL&,
       mojo::PendingReceiver<mojom::blink::BlobURLToken>) override;
 
-  HashMap<KURL, mojom::blink::BlobPtr> registrations;
+  HashMap<KURL, mojo::Remote<mojom::blink::Blob>> registrations;
   Vector<KURL> revocations;
 };
 
diff --git a/third_party/blink/renderer/platform/exported/web_blob_info.cc b/third_party/blink/renderer/platform/exported/web_blob_info.cc
index 7c4a712..79fee2d6 100644
--- a/third_party/blink/renderer/platform/exported/web_blob_info.cc
+++ b/third_party/blink/renderer/platform/exported/web_blob_info.cc
@@ -67,7 +67,7 @@
 mojo::ScopedMessagePipeHandle WebBlobInfo::CloneBlobHandle() const {
   if (!blob_handle_)
     return mojo::ScopedMessagePipeHandle();
-  return blob_handle_->CloneBlobPtr().PassInterface().PassHandle();
+  return blob_handle_->CloneBlobRemote().PassPipe();
 }
 
 WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle)
diff --git a/third_party/blink/renderer/platform/exported/web_http_body.cc b/third_party/blink/renderer/platform/exported/web_http_body.cc
index e0fec84..a0a12cf 100644
--- a/third_party/blink/renderer/platform/exported/web_http_body.cc
+++ b/third_party/blink/renderer/platform/exported/web_http_body.cc
@@ -91,9 +91,7 @@
       result.blob_length = std::numeric_limits<uint64_t>::max();
       if (element.optional_blob_data_handle_) {
         result.optional_blob_handle =
-            element.optional_blob_data_handle_->CloneBlobPtr()
-                .PassInterface()
-                .PassHandle();
+            element.optional_blob_data_handle_->CloneBlobRemote().PassPipe();
         result.blob_length = element.optional_blob_data_handle_->size();
       }
       break;
diff --git a/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc b/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc
index 81c234a1..c9a48d5 100644
--- a/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc
+++ b/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc
@@ -87,7 +87,7 @@
     return;
 
   Platform::Current()->GetInterfaceProvider()->GetInterface(
-      mojo::MakeRequest(&service_));
+      service_.BindNewPipeAndPassReceiver());
 }
 
 void FontUniqueNameLookupAndroid::ReceiveReadOnlySharedMemoryRegion(
diff --git a/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h b/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h
index fb4a35a..02d40e50 100644
--- a/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h
+++ b/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ANDROID_FONT_UNIQUE_NAME_LOOKUP_ANDROID_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ANDROID_FONT_UNIQUE_NAME_LOOKUP_ANDROID_H_
 
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/font_unique_name_lookup/font_table_matcher.h"
 #include "third_party/blink/public/mojom/font_unique_name_lookup/font_unique_name_lookup.mojom-blink.h"
 #include "third_party/blink/renderer/platform/fonts/font_unique_name_lookup.h"
@@ -32,7 +33,7 @@
   void ReceiveReadOnlySharedMemoryRegion(
       base::ReadOnlySharedMemoryRegion shared_memory_region);
 
-  mojom::blink::FontUniqueNameLookupPtr service_;
+  mojo::Remote<mojom::blink::FontUniqueNameLookup> service_;
   WTF::Deque<NotifyFontUniqueNameLookupReady> pending_callbacks_;
   base::Optional<bool> sync_available_;
 
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc b/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc
index a639681..4e64779 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc
@@ -81,9 +81,7 @@
   if (data.type_ == blink::FormDataElement::kEncodedBlob) {
     if (data.optional_blob_data_handle_) {
       blink::mojom::blink::BlobPtr blob_ptr(blink::mojom::blink::BlobPtrInfo(
-          data.optional_blob_data_handle_->CloneBlobPtr()
-              .PassInterface()
-              .PassHandle(),
+          data.optional_blob_data_handle_->CloneBlobRemote().PassPipe(),
           blink::mojom::blink::Blob::Version_));
       network::mojom::blink::DataPipeGetterPtr data_pipe_getter_ptr;
       blob_ptr->AsDataPipeGetter(MakeRequest(&data_pipe_getter_ptr));
diff --git a/third_party/blink/renderer/platform/peerconnection/DEPS b/third_party/blink/renderer/platform/peerconnection/DEPS
index 1f616e0..909e5b662 100644
--- a/third_party/blink/renderer/platform/peerconnection/DEPS
+++ b/third_party/blink/renderer/platform/peerconnection/DEPS
@@ -9,6 +9,7 @@
     # Dependencies.
     "+base/threading/thread_restrictions.h",
     "+media/base",
+    "+media/media_buildflags.h",
     "+media/video/gpu_video_accelerator_factories.h",
     "+media/video/video_decode_accelerator.h",
     "+media/video/h264_parser.h",
diff --git a/content/renderer/media/webrtc/rtc_video_encoder_factory.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc
similarity index 91%
rename from content/renderer/media/webrtc/rtc_video_encoder_factory.cc
rename to third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc
index 5754649..05253dc 100644
--- a/content/renderer/media/webrtc/rtc_video_encoder_factory.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc
@@ -2,15 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/rtc_video_encoder_factory.h"
+#include "third_party/blink/public/platform/modules/peerconnection/rtc_video_encoder_factory.h"
 
 #include <memory>
 
-#include "base/command_line.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
-#include "content/public/common/content_features.h"
-#include "content/public/common/content_switches.h"
 #include "media/media_buildflags.h"
 #include "media/video/gpu_video_accelerator_factories.h"
 #include "third_party/blink/public/common/features.h"
@@ -20,7 +17,7 @@
 #include "third_party/webrtc/common_video/h264/profile_level_id.h"
 #include "third_party/webrtc/media/base/codec.h"
 
-namespace content {
+namespace blink {
 
 namespace {
 
@@ -32,7 +29,7 @@
 
   if (profile.profile >= media::VP8PROFILE_MIN &&
       profile.profile <= media::VP8PROFILE_MAX) {
-    if (base::FeatureList::IsEnabled(features::kWebRtcHWVP8Encoding)) {
+    if (base::FeatureList::IsEnabled(blink::features::kWebRtcHWVP8Encoding)) {
       return webrtc::SdpVideoFormat("VP8");
     }
   } else if (profile.profile >= media::H264PROFILE_MIN &&
@@ -46,7 +43,7 @@
         blink::features::kWebRtcH264WithOpenH264FFmpeg);
 #endif  // BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
     if (webrtc_h264_sw_enabled ||
-        base::FeatureList::IsEnabled(features::kWebRtcHWH264Encoding)) {
+        base::FeatureList::IsEnabled(blink::features::kWebRtcHWH264Encoding)) {
       webrtc::H264::Profile h264_profile;
       switch (profile.profile) {
         case media::H264PROFILE_BASELINE:
@@ -91,7 +88,7 @@
     }
   } else if (profile.profile >= media::VP9PROFILE_MIN &&
              profile.profile <= media::VP9PROFILE_MAX) {
-    if (base::FeatureList::IsEnabled(features::kWebRtcHWVP9Encoding)) {
+    if (base::FeatureList::IsEnabled(blink::features::kWebRtcHWVP9Encoding)) {
       return webrtc::SdpVideoFormat("VP9");
     }
   }
@@ -147,4 +144,4 @@
   return info;
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 22a23a61..d7d76ba1 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -91,6 +91,10 @@
 crbug.com/996235 [ Linux ] media/controls/doubletap-to-jump-backwards-at-start.html [ Pass Leak ]
 crbug.com/996235 [ Linux ] virtual/audio-service/media/controls/doubletap-to-jump-backwards-at-start.html [ Pass Leak ]
 
+crbug.com/1000512 [ Linux ] http/tests/devtools/a11y-axe-core/elements/event-listeners-a11y-test.js [ Leak ]
+crbug.com/1000512 [ Linux ] http/tests/devtools/a11y-axe-core/security/security-origin-a11y-test.js  [ Leak ]
+crbug.com/1000512 [ Linux ] http/tests/devtools/a11y-axe-core/sources/source-navigator-filesystem-a11y-test.js [ Leak ]
+
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
 # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/README.md b/third_party/blink/web_tests/external/wpt/common/security-features/README.md
index a2ae9aa..deac1cc 100644
--- a/third_party/blink/web_tests/external/wpt/common/security-features/README.md
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/README.md
@@ -11,3 +11,96 @@
 - `scope`:
     Serves nested contexts, such as iframe documents or workers.
     Used from `invokeFrom*()` functions in `resources/common.js`.
+
+# spec.src.json format
+
+## Source Contexts
+
+In **`source_context_list_schema`**, we can specify
+
+- source contexts from where subresource requests are sent, and
+- how policies are delivered, by source contexts and/or subresource requests.
+
+- `sourceContextList`: an array of `SourceContext` objects, and
+- `subresourcePolicyDeliveries`: an array of `PolicyDelivery` objects.
+
+They have the same object format as described in
+`common/security-features/resources/common.js` comments, and are directly
+serialized to generated HTML files and passed to JavaScript test code,
+except that:
+
+- The first entry of `sourceContextList`'s `sourceContextType` should be
+  always `top`, which represents the top-level generated test HTML.
+  (This entry is omitted in the JSON passed to JavaScript, but
+  the policy deliveries specified here are written as e.g.
+  <meta> elements in the generated test HTML or HTTP headers)
+- Instead of `PolicyDelivery` object (in `sourceContextList` or
+  `subresourcePolicyDeliveries`), following placeholder strings can be used.
+
+The keys of `source_context_list_schema` can be used as the values of
+`source_context_list` fields, to indicate which source context configuration
+to be used.
+
+## PolicyDelivery placeholders
+
+Each test contains
+
+- `delivery_key` (derived from the top-level `delivery_key`) and
+- `delivery_value`, `delivery_type` (derived from `test_expansion`),
+
+which represents the **target policy delivery**, the policy delivery to be
+tested.
+
+The following placeholder strings in `source_context_list_schema` can be used:
+
+- `"policy"`:
+    - Replaced with the target policy delivery.
+    - Can be used to specify where the target policy delivery should be
+      delivered.
+- `"policyIfNonNull"`:
+    - Replaced with the target policy delivery, only if it has non-null value.
+      If the value is null, then the test file is not generated.
+- `"anotherPolicy"`:
+    - Replaced with a `PolicyDelivery` object that has a different value from
+      the target policy delivery.
+    - Can be used to specify e.g. a policy that should be overridden by
+      the target policy delivery.
+
+For example, when the target policy delivery is
+{deliveryType: "http-rp", key: "referrerPolicy", value: "no-referrer"},
+
+    "sourceContextList": [
+      {"sourceContextType": "top", "policyDeliveries": ["anotherPolicy"]},
+      {"sourceContextType": "classic-worker", "policyDeliveries": ["policy"]}
+    ]
+
+is replaced with
+
+    "sourceContextList": [
+      {"sourceContextType": "top", "policyDeliveries": [
+          {"deliveryType": "meta",
+           "key": "referrerPolicy",
+           "value": "unsafe-url"}]
+      },
+      {"sourceContextType": "classic-worker", "policyDeliveries": [
+          {"deliveryType": "http-rp",
+           "key": "referrerPolicy",
+           "value": "no-referrer"}]
+      }
+    ]
+
+which indicates
+
+- The top-level Document has `<meta name="referrer" content="unsafe-url">`.
+- The classic worker is created with
+  `Referrer-Policy: no-referrer` HTTP response headers.
+
+## `source_context_schema` and `subresource_schema`
+
+These represent supported delivery types and subresources
+for each source context or subresource type. These are used
+
+- To filter out test files for unsupported combinations of delivery types,
+  source contexts and subresources.
+- To determine what delivery types should be used for `anotherPolicy`
+  placeholder.
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/tools/generate.py b/third_party/blink/web_tests/external/wpt/common/security-features/tools/generate.py
index 0b18a39..0cf7fab 100644
--- a/third_party/blink/web_tests/external/wpt/common/security-features/tools/generate.py
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/tools/generate.py
@@ -1,9 +1,12 @@
 from __future__ import print_function
 
-import copy
-import os, sys, json
-import spec_validator
 import argparse
+import copy
+import json
+import os
+import sys
+
+import spec_validator
 import util
 
 
@@ -54,7 +57,11 @@
     del selection['name']
 
     return json.dumps(
-        selection, indent=2, separators=(',', ': '), sort_keys=True)
+        selection,
+        indent=2,
+        separators=(',', ': '),
+        sort_keys=True,
+        cls=util.CustomEncoder)
 
 
 def get_test_filename(config, selection):
@@ -108,7 +115,48 @@
     return {"meta": meta, "headers": headers}
 
 
-def generate_selection(config, selection, spec, test_html_template_basename):
+def generate_selection(spec_json, config, selection, spec,
+                       test_html_template_basename):
+    test_filename = get_test_filename(config, selection)
+
+    target_policy_delivery = util.PolicyDelivery(selection['delivery_type'],
+                                                 selection['delivery_key'],
+                                                 selection['delivery_value'])
+    del selection['delivery_type']
+    del selection['delivery_key']
+    del selection['delivery_value']
+
+    # Parse source context list and policy deliveries of source contexts.
+    # `util.ShouldSkip()` exceptions are raised if e.g. unsuppported
+    # combinations of source contexts and policy deliveries are used.
+    source_context_list_scheme = spec_json['source_context_list_schema'][
+        selection['source_context_list']]
+    selection['source_context_list'] = [
+        util.SourceContext.from_json(source_context, target_policy_delivery,
+                                     spec_json['source_context_schema'])
+        for source_context in source_context_list_scheme['sourceContextList']
+    ]
+
+    # Check if the subresource is supported by the innermost source context.
+    innermost_source_context = selection['source_context_list'][-1]
+    supported_subresource = spec_json['source_context_schema'][
+        'supported_subresource'][innermost_source_context.source_context_type]
+    if supported_subresource != '*':
+        if selection['subresource'] not in supported_subresource:
+            raise util.ShouldSkip()
+
+    # Parse subresource policy deliveries.
+    selection[
+        'subresource_policy_deliveries'] = util.PolicyDelivery.list_from_json(
+            source_context_list_scheme['subresourcePolicyDeliveries'],
+            target_policy_delivery, spec_json['subresource_schema']
+            ['supported_delivery_type'][selection['subresource']])
+
+    # We process the top source context below, and do not include it in
+    # `test_parameters` in JavaScript.
+    top_source_context = selection['source_context_list'].pop(0)
+    assert (top_source_context.source_context_type == 'top')
+
     test_parameters = dump_test_parameters(selection)
     # Adjust the template for the test invoking JS. Indent it to look nice.
     indent = "\n" + " " * 8
@@ -131,7 +179,6 @@
     selection['sanity_checker_js'] = config.sanity_checker_js
     selection['spec_json_js'] = config.spec_json_js
 
-    test_filename = get_test_filename(config, selection)
     test_headers_filename = test_filename + ".headers"
     test_directory = os.path.dirname(test_filename)
 
@@ -159,11 +206,7 @@
     except:
         pass
 
-    delivery = handle_deliveries([
-        util.PolicyDelivery(selection['delivery_type'],
-                            selection['delivery_key'],
-                            selection['delivery_value'])
-    ])
+    delivery = handle_deliveries(top_source_context.policy_deliveries)
 
     if len(delivery['headers']) > 0:
         with open(test_headers_filename, "w") as f:
@@ -232,7 +275,11 @@
 
         for selection_path in output_dict:
             selection = output_dict[selection_path]
-            generate_selection(config, selection, spec, html_template)
+            try:
+                generate_selection(spec_json, config, selection, spec,
+                                   html_template)
+            except util.ShouldSkip:
+                continue
 
 
 def main(config):
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/tools/spec_validator.py b/third_party/blink/web_tests/external/wpt/common/security-features/tools/spec_validator.py
index 2c966478..fff4c5dd 100755
--- a/third_party/blink/web_tests/external/wpt/common/security-features/tools/spec_validator.py
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/tools/spec_validator.py
@@ -57,6 +57,15 @@
                 'Unexpected field "%s".' % actual_field
 
 
+def leaf_values(schema):
+    if isinstance(schema, list):
+        return schema
+    ret = []
+    for _, sub_schema in schema.iteritems():
+        ret += leaf_values(sub_schema)
+    return ret
+
+
 def assert_value_unique_in(value, used_values):
     assert value not in used_values, 'Duplicate value "%s"!' % str(value)
     used_values[value] = True
@@ -78,8 +87,9 @@
 
     details['object'] = spec_json
     assert_contains_only_fields(spec_json, [
-        "specification", "delivery_key", "test_expansion_schema",
-        "excluded_tests"
+        "specification", "delivery_key", "subresource_schema",
+        "source_context_schema", "source_context_list_schema",
+        "test_expansion_schema", "excluded_tests"
     ])
     assert_non_empty_list(spec_json, "specification")
     assert_non_empty_dict(spec_json, "test_expansion_schema")
@@ -91,6 +101,29 @@
 
     valid_test_expansion_fields = ['name'] + test_expansion_schema.keys()
 
+    valid_source_context_names = [
+        "top", "iframe", "srcdoc", "worker-classic", "worker-module",
+        "worker-classic-data", "worker-module-data"
+    ]
+
+    valid_subresource_names = [
+        "a-tag", "area-tag", "audio-tag", "form-tag", "iframe-tag", "img-tag",
+        "link-css-tag", "link-prefetch-tag", "object-tag", "picture-tag",
+        "script-tag", "video-tag"
+    ] + ["beacon", "fetch", "xhr", "websocket"] + [
+        "worker-classic", "worker-module", "worker-import",
+        "worker-import-data", "sharedworker-classic", "sharedworker-module",
+        "sharedworker-import", "sharedworker-import-data",
+        "serviceworker-classic", "serviceworker-module",
+        "serviceworker-import", "serviceworker-import-data"
+    ] + [
+        "worklet-animation", "worklet-audio", "worklet-layout",
+        "worklet-paint", "worklet-animation-import", "worklet-audio-import",
+        "worklet-layout-import", "worklet-paint-import",
+        "worklet-animation-import-data", "worklet-audio-import-data",
+        "worklet-layout-import-data", "worklet-paint-import-data"
+    ]
+
     # Validate each single spec.
     for spec in specification:
         details['object'] = spec
@@ -123,12 +156,66 @@
                                       test_expansion_schema[artifact])
                 del details['test_expansion_field']
 
+    # Validate source_context_schema.
+    details['object'] = spec_json['source_context_schema']
+    assert_contains_only_fields(
+        spec_json['source_context_schema'],
+        ['supported_delivery_type', 'supported_subresource'])
+    assert_contains_only_fields(
+        spec_json['source_context_schema']['supported_delivery_type'],
+        valid_source_context_names)
+    for source_context in spec_json['source_context_schema'][
+            'supported_delivery_type']:
+        assert_valid_artifact(
+            spec_json['source_context_schema']['supported_delivery_type'],
+            source_context, test_expansion_schema['delivery_type'])
+    assert_contains_only_fields(
+        spec_json['source_context_schema']['supported_subresource'],
+        valid_source_context_names)
+    for source_context in spec_json['source_context_schema'][
+            'supported_subresource']:
+        assert_valid_artifact(
+            spec_json['source_context_schema']['supported_subresource'],
+            source_context, leaf_values(test_expansion_schema['subresource']))
+
+    # Validate subresource_schema.
+    details['object'] = spec_json['subresource_schema']
+    assert_contains_only_fields(spec_json['subresource_schema'],
+                                ['supported_delivery_type'])
+    assert_contains_only_fields(
+        spec_json['subresource_schema']['supported_delivery_type'],
+        leaf_values(test_expansion_schema['subresource']))
+    for subresource in spec_json['subresource_schema'][
+            'supported_delivery_type']:
+        assert_valid_artifact(
+            spec_json['subresource_schema']['supported_delivery_type'],
+            subresource, test_expansion_schema['delivery_type'])
+
     # Validate the test_expansion schema members.
     details['object'] = test_expansion_schema
     assert_contains_only_fields(test_expansion_schema, [
-        'expansion', 'source_scheme', 'delivery_type', 'delivery_value',
-        'redirection', 'subresource', 'origin', 'expectation'
+        'expansion', 'source_scheme', 'source_context_list', 'delivery_type',
+        'delivery_value', 'redirection', 'subresource', 'origin', 'expectation'
     ])
+    assert_atom_or_list_items_from(test_expansion_schema, 'expansion',
+                                   ['default', 'override'])
+    assert_atom_or_list_items_from(test_expansion_schema, 'source_scheme',
+                                   ['http', 'https'])
+    assert_atom_or_list_items_from(
+        test_expansion_schema, 'source_context_list',
+        spec_json['source_context_list_schema'].keys())
+
+    assert_atom_or_list_items_from(test_expansion_schema, 'redirection', [
+        'no-redirect', 'keep-origin', 'swap-origin', 'keep-scheme',
+        'swap-scheme'
+    ])
+    for subresource in leaf_values(test_expansion_schema['subresource']):
+        assert subresource in valid_subresource_names, "Invalid subresource %s" % subresource
+    assert_atom_or_list_items_from(test_expansion_schema, 'origin', [
+        'same-http', 'same-https', 'same-ws', 'same-wss', 'cross-http',
+        'cross-https', 'cross-ws', 'cross-wss'
+    ])
+
     # Validate excluded tests.
     details['object'] = excluded_tests
     for excluded_test_expansion in excluded_tests:
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/tools/util.py b/third_party/blink/web_tests/external/wpt/common/security-features/tools/util.py
index fc8d3b3..57e95fe 100644
--- a/third_party/blink/web_tests/external/wpt/common/security-features/tools/util.py
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/tools/util.py
@@ -41,6 +41,21 @@
             sys.exit(1)
 
 
+class ShouldSkip(Exception):
+    '''
+    Raised when the given combination of subresource type, source context type,
+    delivery type etc. are not supported and we should skip that configuration.
+    ShouldSkip is expected in normal generator execution (and thus subsequent
+    generation continues), as we first enumerate a broad range of configurations
+    first, and later raise ShouldSkip to filter out unsupported combinations.
+
+    ShouldSkip is distinguished from other general errors that cause immediate
+    termination of the generator and require fix.
+    '''
+    def __init__(self):
+        pass
+
+
 class PolicyDelivery(object):
     '''
     See `@typedef PolicyDelivery` comments in `resources/common.js`.
@@ -50,3 +65,129 @@
         self.delivery_type = delivery_type
         self.key = key
         self.value = value
+
+    @classmethod
+    def list_from_json(cls, list, target_policy_delivery,
+                       supported_delivery_types):
+        # type: (dict, PolicyDelivery, typing.List[str]) -> typing.List[PolicyDelivery]
+        '''
+        Parses a JSON object `list` that represents a list of `PolicyDelivery`
+        and returns a list of `PolicyDelivery`, plus supporting placeholders
+        (see `from_json()` comments below or
+        `common/security-features/README.md`).
+
+        Can raise `ShouldSkip`.
+        '''
+        if list is None:
+            return []
+
+        out = []
+        for obj in list:
+            policy_delivery = PolicyDelivery.from_json(
+                obj, target_policy_delivery, supported_delivery_types)
+            # Drop entries with null values.
+            if policy_delivery.value is None:
+                continue
+            out.append(policy_delivery)
+        return out
+
+    @classmethod
+    def from_json(cls, obj, target_policy_delivery, supported_delivery_types):
+        # type: (dict, PolicyDelivery, typing.List[str]) -> PolicyDelivery
+        '''
+           Parses a JSON object `obj` and returns a `PolicyDelivery` object.
+           In addition to dicts (in the same format as to_json() outputs),
+           this method accepts the following placeholders:
+             "policy":
+               `target_policy_delivery`
+             "policyIfNonNull":
+               `target_policy_delivery` if its value is not None.
+             "anotherPolicy":
+               A PolicyDelivery that has the same key as
+               `target_policy_delivery` but a different value.
+               The delivery type is selected from `supported_delivery_types`.
+
+        Can raise `ShouldSkip`.
+        '''
+
+        if obj == "policy":
+            policy_delivery = target_policy_delivery
+        elif obj == "nonNullPolicy":
+            if target_policy_delivery.value is None:
+                raise ShouldSkip()
+            policy_delivery = target_policy_delivery
+        elif obj == "anotherPolicy":
+            policy_delivery = target_policy_delivery.get_another_policy(
+                supported_delivery_types[0])
+        elif type(obj) == dict:
+            policy_delivery = PolicyDelivery(obj['deliveryType'], obj['key'],
+                                             obj['value'])
+        else:
+            raise Exception('policy delivery is invalid: ' + obj)
+
+        # Omit unsupported combinations of source contexts and delivery type.
+        if policy_delivery.delivery_type not in supported_delivery_types:
+            raise ShouldSkip()
+
+        return policy_delivery
+
+    def to_json(self):
+        # type: () -> dict
+        return {
+            "deliveryType": self.delivery_type,
+            "key": self.key,
+            "value": self.value
+        }
+
+    def get_another_policy(self, delivery_type):
+        # type: (str) -> PolicyDelivery
+        if self.key == 'referrerPolicy':
+            if self.value == 'no-referrer':
+                return PolicyDelivery(delivery_type, self.key, 'unsafe-url')
+            else:
+                return PolicyDelivery(delivery_type, self.key, 'no-referrer')
+        else:
+            raise Exception('delivery key is invalid: ' + self.key)
+
+
+class SourceContext(object):
+    def __init__(self, source_context_type, policy_deliveries):
+        # type: (unicode, typing.List[PolicyDelivery]) -> None
+        self.source_context_type = source_context_type
+        self.policy_deliveries = policy_deliveries
+
+    @classmethod
+    def from_json(cls, obj, target_policy_delivery, source_context_schema):
+        '''
+        Parses a JSON object `obj` and returns a `SourceContext` object.
+
+        `target_policy_delivery` and `source_context_schema` are used for
+        policy delivery placeholders and filtering out unsupported
+        delivery types.
+
+        Can raise `ShouldSkip`.
+        '''
+        source_context_type = obj.get('sourceContextType')
+        policy_deliveries = PolicyDelivery.list_from_json(
+            obj.get('policyDeliveries'), target_policy_delivery,
+            source_context_schema['supported_delivery_type']
+            [source_context_type])
+        return SourceContext(source_context_type, policy_deliveries)
+
+    def to_json(self):
+        return {
+            "sourceContextType": self.source_context_type,
+            "policyDeliveries": [x.to_json() for x in self.policy_deliveries]
+        }
+
+
+class CustomEncoder(json.JSONEncoder):
+    '''
+    Used to dump dicts containing `SourceContext`/`PolicyDelivery` into JSON.
+    '''
+    def default(self, obj):
+        if isinstance(obj, SourceContext):
+            return obj.to_json()
+        if isinstance(obj, PolicyDelivery):
+            return obj.to_json()
+        return json.JSONEncoder.default(self, obj)
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/generic/test-case.sub.js b/third_party/blink/web_tests/external/wpt/mixed-content/generic/test-case.sub.js
new file mode 100644
index 0000000..d25986e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/generic/test-case.sub.js
@@ -0,0 +1,127 @@
+/**
+ * @fileoverview Test case for mixed-content in Web Platform Tests.
+ * @author burnik@google.com (Kristijan Burnik)
+ */
+
+// TODO: This function is currently placed and duplicated at:
+// - mixed-content/generic/mixed-content-test-case.js
+// - referrer-policy/generic/referrer-policy-test-case.js
+// but should be moved to /common/security-features/resources/common.js.
+function getSubresourceOrigin(originType) {
+  const httpProtocol = "http";
+  const httpsProtocol = "https";
+  const wsProtocol = "ws";
+  const wssProtocol = "wss";
+
+  const sameOriginHost = "{{host}}";
+  const crossOriginHost = "{{domains[www1]}}";
+
+  // These values can evaluate to either empty strings or a ":port" string.
+  const httpPort = getNormalizedPort(parseInt("{{ports[http][0]}}", 10));
+  const httpsPort = getNormalizedPort(parseInt("{{ports[https][0]}}", 10));
+  const wsPort = getNormalizedPort(parseInt("{{ports[ws][0]}}", 10));
+  const wssPort = getNormalizedPort(parseInt("{{ports[wss][0]}}", 10));
+
+  /**
+    @typedef OriginType
+    @type {string}
+
+    Represents the origin of the subresource request URL.
+    The keys of `originMap` below are the valid values.
+
+    Note that there can be redirects from the specified origin
+    (see RedirectionType), and thus the origin of the subresource
+    response URL might be different from what is specified by OriginType.
+  */
+  const originMap = {
+    "same-https": httpsProtocol + "://" + sameOriginHost + httpsPort,
+    "same-http": httpProtocol + "://" + sameOriginHost + httpPort,
+    "cross-https": httpsProtocol + "://" + crossOriginHost + httpsPort,
+    "cross-http": httpProtocol + "://" + crossOriginHost + httpPort,
+    "same-wss": wssProtocol + "://" + sameOriginHost + wssPort,
+    "same-ws": wsProtocol + "://" + sameOriginHost + wsPort,
+    "cross-wss": wssProtocol + "://" + crossOriginHost + wssPort,
+    "cross-ws": wsProtocol + "://" + crossOriginHost + wsPort,
+  };
+
+  return originMap[originType];
+}
+
+/**
+ * MixedContentTestCase exercises all the tests for checking browser behavior
+ * when resources regarded as mixed-content are requested. A single run covers
+ * only a single scenario.
+ * @param {object} scenario A JSON describing the test arrangement and
+ *     expectation(s). Refer to /mixed-content/spec.src.json for details.
+ * @param {string} description The test scenario verbose description.
+ * @param {SanityChecker} sanityChecker Instance of an object used to check the
+ *     running scenario. Useful in debug mode. See ./sanity-checker.js.
+ *     Run {@code ./tools/generate.py -h} for info on test generating modes.
+ * @return {object} Object wrapping the start method used to run the test.
+ */
+function TestCase(scenario, description, sanityChecker) {
+  sanityChecker.checkScenario(scenario, subresourceMap);
+
+  const redirectionTypeConversion = {
+    "no-redirect": "no-redirect",
+    "keep-scheme": "keep-scheme-redirect",
+    "swap-scheme": "swap-scheme-redirect",
+    "keep-origin": "keep-origin-redirect",
+    "swap-origin": "swap-origin-redirect"
+  };
+  const subresourceTypeConversion = {
+    "beacon": "beacon-request",
+    "fetch": "fetch-request",
+    "xhr": "xhr-request",
+    "websocket": "websocket-request",
+    "worker-classic": "worker-request",
+    "worker-module": "module-worker",
+    "worker-import-data": "module-data-worker-import",
+    "sharedworker-classic": "shared-worker",
+    "worklet-animation": "worklet-animation-top-level",
+    "worklet-audio": "worklet-audio-top-level",
+    "worklet-layout": "worklet-layout-top-level",
+    "worklet-paint": "worklet-paint-top-level",
+    "worklet-animation-import-data": "worklet-animation-data-import",
+    "worklet-audio-import-data": "worklet-audio-data-import",
+    "worklet-layout-import-data": "worklet-layout-data-import",
+    "worklet-paint-import-data": "worklet-paint-data-import"
+  };
+  const subresourceType =
+      subresourceTypeConversion[scenario.subresource] || scenario.subresource;
+
+  const urls = getRequestURLs(subresourceType,
+                              scenario.origin,
+                              redirectionTypeConversion[scenario.redirection]);
+  const checkResult = _ => {
+    // Send request to check if the key has been torn down.
+    return xhrRequest(urls.assertUrl)
+      .then(assertResult => {
+          // Now check if the value has been torn down. If it's still there,
+          // we have blocked the request to mixed-content.
+          assert_equals(assertResult.status, scenario.expectation,
+            "The resource request should be '" + scenario.expectation + "'.");
+        });
+  };
+
+  function runTest() {
+    /** @type {Subresource} */
+    const subresource = {
+      subresourceType: subresourceType,
+      url: urls.testUrl,
+      policyDeliveries: scenario.subresource_policy_deliveries,
+    };
+
+    promise_test(() => {
+      return xhrRequest(urls.announceUrl)
+        // Send out the real resource request.
+        // This should tear down the key if it's not blocked.
+        .then(_ => invokeRequest(subresource, scenario.source_context_list))
+        // We check the key state, regardless of whether the main request
+        // succeeded or failed.
+        .then(checkResult, checkResult);
+      }, description);
+  }  // runTest
+
+  return {start: runTest};
+}
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/generate.py b/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/generate.py
index 9c911aa6..b80bc8d 100755
--- a/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/generate.py
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/generate.py
@@ -13,36 +13,25 @@
 
 class MixedContentConfig(object):
     def __init__(self):
-        self.selection_pattern = '%(subresource)s/' + \
-                                 '%(delivery_type)s/' + \
-                                 '%(delivery_value)s/' + \
-                                 '%(origin)s/' + \
-                                 'top-level/' + \
-                                 '%(redirection)s/'
+        self.selection_pattern = \
+              '%(source_context_list)s.%(delivery_type)s/' + \
+              '%(delivery_value)s/' + \
+              '%(subresource)s/' + \
+              '%(origin)s.%(redirection)s.%(source_scheme)s'
 
-        self.test_file_path_pattern = self.selection_pattern + \
-                                      '%(spec_name)s/' + \
-                                      '%(name)s.%(source_scheme)s.html'
+        self.test_file_path_pattern = 'gen/' + self.selection_pattern + '.html'
 
-        self.test_description_template = '''delivery_type: %(delivery_type)s
-delivery_value: %(delivery_value)s
-origin: %(origin)s
-source_scheme: %(source_scheme)s
-context_nesting: top-level
-redirection: %(redirection)s
-subresource: %(subresource)s
-expectation: %(expectation)s
-'''
+        self.test_description_template = 'Mixed-Content: Expects %(expectation)s for %(subresource)s to %(origin)s origin and %(redirection)s redirection from %(source_scheme)s context.'
 
         self.test_page_title_template = 'Mixed-Content: %s'
 
-        self.helper_js = '/mixed-content/generic/mixed-content-test-case.js?pipe=sub'
+        self.helper_js = '/mixed-content/generic/test-case.sub.js'
 
         # For debug target only.
         self.sanity_checker_js = '/mixed-content/generic/sanity-checker.js'
         self.spec_json_js = '/mixed-content/spec_json.js'
 
-        self.test_case_name = 'MixedContentTestCase'
+        self.test_case_name = 'TestCase'
 
         script_directory = os.path.dirname(os.path.abspath(__file__))
         self.spec_directory = os.path.abspath(
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/spec.src.json b/third_party/blink/web_tests/external/wpt/mixed-content/spec.src.json
index 95dc2d3..e4844a8 100644
--- a/third_party/blink/web_tests/external/wpt/mixed-content/spec.src.json
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/spec.src.json
@@ -10,6 +10,7 @@
           "name": "opt-in-blocks",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "opt-in",
           "redirection": "*",
@@ -27,6 +28,7 @@
           "name": "no-opt-in-allows",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": null,
           "redirection": "*",
@@ -52,6 +54,7 @@
           "name": "opt-in-blocks",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "opt-in",
           "redirection": "*",
@@ -69,6 +72,7 @@
           "name": "no-opt-in-blocks",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": null,
           "redirection": "*",
@@ -86,11 +90,12 @@
           "name": "ws-downgrade-blocks",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "*",
           "redirection": "*",
           "subresource": {
-            "blockable": "websocket-request",
+            "blockable": "websocket",
             "optionally-blockable": []
           },
           "origin": [
@@ -111,6 +116,7 @@
           "name": "allowed",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "*",
           "redirection": [
@@ -130,6 +136,7 @@
           "name": "websocket-allowed",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "*",
           "redirection": [
@@ -137,7 +144,7 @@
             "keep-scheme"
           ],
           "subresource": {
-            "blockable": "websocket-request",
+            "blockable": "websocket",
             "optionally-blockable": []
           },
           "origin": [
@@ -154,6 +161,7 @@
       "name": "Skip-redundant-no-opt-in",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "delivery_type": "http-rp",
       "delivery_value": null,
       "redirection": "*",
@@ -168,6 +176,7 @@
       "name": "Redundant-subresources",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "delivery_type": "*",
       "delivery_value": "*",
       "redirection": "*",
@@ -184,12 +193,13 @@
       "name": "Skip-origins-not-applicable-to-websockets",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "delivery_type": "*",
       "delivery_value": "*",
       "redirection": "*",
       "subresource": {
         "blockable": [
-          "websocket-request"
+          "websocket"
         ],
         "optionally-blockable": []
       },
@@ -205,6 +215,7 @@
       "name": "Skip-redundant-for-opt-in-method",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "delivery_type": "meta",
       "delivery_value": "opt-in",
       "redirection": [
@@ -219,6 +230,126 @@
       "expectation": "*"
     }
   ],
+  "source_context_schema": {
+    "supported_delivery_type": {
+      "top": [
+        "http-rp",
+        "meta"
+      ],
+      "iframe": [
+        "http-rp",
+        "meta"
+      ],
+      "srcdoc": [
+        "meta"
+      ],
+      "worker-classic": [
+        "http-rp"
+      ],
+      "worker-module": [
+        "http-rp"
+      ],
+      "worker-classic-data": [],
+      "worker-module-data": []
+    },
+    "supported_subresource": {
+      "top": "*",
+      "iframe": "*",
+      "srcdoc": "*",
+      "worker-classic": [
+        "xhr",
+        "fetch",
+        "websocket"
+      ],
+      "worker-module": [
+        "xhr",
+        "fetch",
+        "websocket"
+      ],
+      "worker-classic-data": [
+        "xhr",
+        "fetch",
+        "websocket"
+      ],
+      "worker-module-data": [
+        "xhr",
+        "fetch",
+        "websocket"
+      ]
+    }
+  },
+  "subresource_schema": {
+    "supported_delivery_type": {
+      "script-tag": [],
+      "link-css-tag": [],
+      "xhr": [],
+      "worker-classic": [],
+      "worker-module": [],
+      "worker-import-data": [],
+      "worklet-animation": [],
+      "worklet-audio": [],
+      "worklet-layout": [],
+      "worklet-paint": [],
+      "worklet-animation-import-data": [],
+      "worklet-audio-import-data": [],
+      "worklet-layout-import-data": [],
+      "worklet-paint-import-data": [],
+      "fetch": [],
+      "a-tag": [],
+      "object-tag": [],
+      "picture-tag": [],
+      "websocket": [],
+      "link-prefetch-tag": [],
+      "beacon": [],
+      "img-tag": [],
+      "audio-tag": [],
+      "video-tag": []
+    }
+  },
+  "source_context_list_schema": {
+    "top": {
+      "description": "Policy set by the top-level Document",
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "policy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "worker-classic-data": {
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "policy"
+          ]
+        },
+        {
+          "sourceContextType": "worker-classic-data",
+          "policyDeliveries": []
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "worker-module-data": {
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "policy"
+          ]
+        },
+        {
+          "sourceContextType": "worker-module-data",
+          "policyDeliveries": []
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    }
+  },
   "test_expansion_schema": {
     "expansion": [
       "default",
@@ -236,6 +367,11 @@
       null,
       "opt-in"
     ],
+    "source_context_list": [
+      "top",
+      "worker-classic-data",
+      "worker-module-data"
+    ],
     "redirection": [
       "no-redirect",
       "keep-scheme",
@@ -255,26 +391,25 @@
       "blockable": [
         "script-tag",
         "link-css-tag",
-        "xhr-request",
-        "worker-request",
-        "module-worker-top-level",
-        "module-data-worker-import",
-        "classic-data-worker-fetch",
-        "worklet-animation-top-level",
-        "worklet-audio-top-level",
-        "worklet-layout-top-level",
-        "worklet-paint-top-level",
-        "worklet-animation-data-import",
-        "worklet-audio-data-import",
-        "worklet-layout-data-import",
-        "worklet-paint-data-import",
-        "fetch-request",
+        "xhr",
+        "worker-classic",
+        "worker-module",
+        "worker-import-data",
+        "worklet-animation",
+        "worklet-audio",
+        "worklet-layout",
+        "worklet-paint",
+        "worklet-animation-import-data",
+        "worklet-audio-import-data",
+        "worklet-layout-import-data",
+        "worklet-paint-import-data",
+        "fetch",
         "a-tag",
         "object-tag",
         "picture-tag",
-        "websocket-request",
+        "websocket",
         "link-prefetch-tag",
-        "beacon-request"
+        "beacon"
       ],
       "optionally-blockable": [
         "img-tag",
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/spec_json.js b/third_party/blink/web_tests/external/wpt/mixed-content/spec_json.js
index 0868df2..009a8530 100644
--- a/third_party/blink/web_tests/external/wpt/mixed-content/spec_json.js
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/spec_json.js
@@ -1 +1 @@
-var SPEC_JSON = {"test_expansion_schema": {"origin": ["same-host-https", "same-host-http", "cross-origin-https", "cross-origin-http", "same-host-wss", "same-host-ws", "cross-origin-wss", "cross-origin-ws"], "subresource": {"blockable": ["script-tag", "link-css-tag", "xhr-request", "worker-request", "module-worker-top-level", "module-data-worker-import", "classic-data-worker-fetch", "worklet-animation-top-level", "worklet-audio-top-level", "worklet-layout-top-level", "worklet-paint-top-level", "worklet-animation-data-import", "worklet-audio-data-import", "worklet-layout-data-import", "worklet-paint-data-import", "fetch-request", "a-tag", "object-tag", "picture-tag", "websocket-request", "link-prefetch-tag", "beacon-request"], "optionally-blockable": ["img-tag", "audio-tag", "video-tag"]}, "context_nesting": ["top-level", "sub-level"], "expectation": ["allowed", "blocked"], "expansion": ["default", "override"], "redirection": ["no-redirect", "keep-scheme-redirect", "swap-scheme-redirect"], "opt_in_method": ["no-opt-in", "http-csp", "meta-csp", "img-crossorigin"], "source_scheme": ["http", "https"]}, "specification": [{"test_expansion": [{"origin": ["cross-origin-http", "same-host-http"], "name": "opt-in-blocks", "redirection": "*", "expectation": "blocked", "expansion": "default", "context_nesting": "top-level", "opt_in_method": ["http-csp", "meta-csp"], "source_scheme": "https", "subresource": {"blockable": [], "optionally-blockable": "*"}}, {"origin": ["cross-origin-http", "same-host-http"], "name": "no-opt-in-allows", "redirection": "*", "expectation": "allowed", "expansion": "default", "context_nesting": "top-level", "opt_in_method": "no-opt-in", "source_scheme": "https", "subresource": {"blockable": [], "optionally-blockable": "*"}}], "description": "Test behavior of optionally-blockable content", "specification_url": "http://www.w3.org/TR/mixed-content/#category-optionally-blockable", "name": "optionally-blockable", "title": "Optionally-blockable content"}, {"test_expansion": [{"origin": ["cross-origin-http", "same-host-http"], "name": "opt-in-blocks", "redirection": "*", "expectation": "blocked", "expansion": "default", "context_nesting": "top-level", "opt_in_method": ["http-csp", "meta-csp"], "source_scheme": "https", "subresource": {"blockable": "*", "optionally-blockable": []}}, {"origin": ["cross-origin-http", "same-host-http"], "name": "no-opt-in-blocks", "redirection": "*", "expectation": "blocked", "expansion": "default", "context_nesting": "top-level", "opt_in_method": "no-opt-in", "source_scheme": "https", "subresource": {"blockable": "*", "optionally-blockable": []}}, {"origin": ["cross-origin-ws", "same-host-ws"], "name": "ws-downgrade-blocks", "redirection": "*", "expectation": "blocked", "expansion": "default", "context_nesting": "top-level", "opt_in_method": ["no-opt-in", "http-csp", "meta-csp"], "source_scheme": "https", "subresource": {"blockable": "websocket-request", "optionally-blockable": []}}], "description": "Test behavior of blockable content.", "specification_url": "http://www.w3.org/TR/mixed-content/#category-blockable", "name": "blockable", "title": "Blockable content"}, {"test_expansion": [{"origin": ["same-host-https"], "name": "allowed", "redirection": ["no-redirect", "keep-scheme-redirect"], "expectation": "allowed", "expansion": "default", "context_nesting": "top-level", "opt_in_method": "*", "source_scheme": "https", "subresource": {"blockable": "*", "optionally-blockable": "*"}}, {"origin": ["same-host-wss"], "name": "websocket-allowed", "redirection": ["no-redirect", "keep-scheme-redirect"], "expectation": "allowed", "expansion": "default", "context_nesting": "top-level", "opt_in_method": "*", "source_scheme": "https", "subresource": {"blockable": "websocket-request", "optionally-blockable": []}}], "description": "Test behavior of allowed content.", "specification_url": "http://www.w3.org/TR/mixed-content/", "name": "allowed", "title": "Allowed content"}], "excluded_tests": [{"origin": "*", "name": "Redundant-subresources", "redirection": "*", "expectation": "*", "expansion": "*", "context_nesting": "*", "opt_in_method": "*", "source_scheme": "*", "subresource": {"blockable": ["a-tag"], "optionally-blockable": []}}, {"origin": ["same-host-https", "same-host-http", "cross-origin-https", "cross-origin-http"], "name": "Skip-origins-not-applicable-to-websockets", "redirection": "*", "expectation": "*", "expansion": "*", "context_nesting": "*", "opt_in_method": "*", "source_scheme": "*", "subresource": {"blockable": ["websocket-request"], "optionally-blockable": []}}, {"origin": "*", "name": "TODO-opt-in-method-img-cross-origin", "redirection": "*", "expectation": "*", "expansion": "*", "context_nesting": "*", "opt_in_method": "img-crossorigin", "source_scheme": "*", "subresource": {"blockable": "*", "optionally-blockable": "*"}}, {"origin": "*", "name": "Skip-redundant-for-opt-in-method", "redirection": ["keep-scheme-redirect", "swap-scheme-redirect"], "expectation": "*", "expansion": "*", "context_nesting": "*", "opt_in_method": ["meta-csp", "img-crossorigin"], "source_scheme": "*", "subresource": {"blockable": "*", "optionally-blockable": "*"}}]};
+var SPEC_JSON = {"subresource_schema": {"supported_delivery_type": {"picture-tag": [], "worklet-layout": [], "worklet-paint": [], "img-tag": [], "a-tag": [], "worklet-layout-import-data": [], "worklet-audio-import-data": [], "worklet-animation": [], "websocket": [], "worklet-paint-import-data": [], "video-tag": [], "object-tag": [], "worklet-audio": [], "beacon": [], "worker-module": [], "worker-import-data": [], "script-tag": [], "worklet-animation-import-data": [], "link-css-tag": [], "xhr": [], "worker-classic": [], "link-prefetch-tag": [], "audio-tag": [], "fetch": []}}, "excluded_tests": [{"delivery_value": null, "origin": "*", "delivery_type": "http-rp", "name": "Skip-redundant-no-opt-in", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": {"blockable": "*", "optionally-blockable": "*"}}, {"delivery_value": "*", "origin": "*", "delivery_type": "*", "name": "Redundant-subresources", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": {"blockable": ["a-tag"], "optionally-blockable": []}}, {"delivery_value": "*", "origin": ["same-https", "same-http", "cross-https", "cross-http"], "delivery_type": "*", "name": "Skip-origins-not-applicable-to-websockets", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": {"blockable": ["websocket"], "optionally-blockable": []}}, {"delivery_value": "opt-in", "origin": "*", "delivery_type": "meta", "name": "Skip-redundant-for-opt-in-method", "expectation": "*", "expansion": "*", "redirection": ["keep-scheme", "swap-scheme"], "source_context_list": "*", "source_scheme": "*", "subresource": {"blockable": "*", "optionally-blockable": "*"}}], "specification": [{"test_expansion": [{"delivery_value": "opt-in", "origin": ["cross-http", "same-http"], "delivery_type": "*", "name": "opt-in-blocks", "expectation": "blocked", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": {"blockable": [], "optionally-blockable": "*"}}, {"delivery_value": null, "origin": ["cross-http", "same-http"], "delivery_type": "*", "name": "no-opt-in-allows", "expectation": "allowed", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": {"blockable": [], "optionally-blockable": "*"}}], "description": "Test behavior of optionally-blockable content", "specification_url": "http://www.w3.org/TR/mixed-content/#category-optionally-blockable", "name": "optionally-blockable", "title": "Optionally-blockable content"}, {"test_expansion": [{"delivery_value": "opt-in", "origin": ["cross-http", "same-http"], "delivery_type": "*", "name": "opt-in-blocks", "expectation": "blocked", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": {"blockable": "*", "optionally-blockable": []}}, {"delivery_value": null, "origin": ["cross-http", "same-http"], "delivery_type": "*", "name": "no-opt-in-blocks", "expectation": "blocked", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": {"blockable": "*", "optionally-blockable": []}}, {"delivery_value": "*", "origin": ["cross-ws", "same-ws"], "delivery_type": "*", "name": "ws-downgrade-blocks", "expectation": "blocked", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": {"blockable": "websocket", "optionally-blockable": []}}], "description": "Test behavior of blockable content.", "specification_url": "http://www.w3.org/TR/mixed-content/#category-blockable", "name": "blockable", "title": "Blockable content"}, {"test_expansion": [{"delivery_value": "*", "origin": ["same-https"], "delivery_type": "*", "name": "allowed", "expectation": "allowed", "expansion": "default", "redirection": ["no-redirect", "keep-scheme"], "source_context_list": "*", "source_scheme": "https", "subresource": {"blockable": "*", "optionally-blockable": "*"}}, {"delivery_value": "*", "origin": ["same-wss"], "delivery_type": "*", "name": "websocket-allowed", "expectation": "allowed", "expansion": "default", "redirection": ["no-redirect", "keep-scheme"], "source_context_list": "*", "source_scheme": "https", "subresource": {"blockable": "websocket", "optionally-blockable": []}}], "description": "Test behavior of allowed content.", "specification_url": "http://www.w3.org/TR/mixed-content/", "name": "allowed", "title": "Allowed content"}], "test_expansion_schema": {"delivery_value": [null, "opt-in"], "origin": ["same-https", "same-http", "cross-https", "cross-http", "same-wss", "same-ws", "cross-wss", "cross-ws"], "delivery_type": ["http-rp", "meta"], "subresource": {"blockable": ["script-tag", "link-css-tag", "xhr", "worker-classic", "worker-module", "worker-import-data", "worklet-animation", "worklet-audio", "worklet-layout", "worklet-paint", "worklet-animation-import-data", "worklet-audio-import-data", "worklet-layout-import-data", "worklet-paint-import-data", "fetch", "a-tag", "object-tag", "picture-tag", "websocket", "link-prefetch-tag", "beacon"], "optionally-blockable": ["img-tag", "audio-tag", "video-tag"]}, "expectation": ["allowed", "blocked"], "expansion": ["default", "override"], "redirection": ["no-redirect", "keep-scheme", "swap-scheme"], "source_context_list": ["top", "worker-classic-data", "worker-module-data"], "source_scheme": ["http", "https"]}, "source_context_list_schema": {"worker-classic-data": {"subresourcePolicyDeliveries": [], "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["policy"]}, {"sourceContextType": "worker-classic-data", "policyDeliveries": []}]}, "top": {"subresourcePolicyDeliveries": [], "description": "Policy set by the top-level Document", "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["policy"]}]}, "worker-module-data": {"subresourcePolicyDeliveries": [], "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["policy"]}, {"sourceContextType": "worker-module-data", "policyDeliveries": []}]}}, "delivery_key": "mixedContent", "source_context_schema": {"supported_delivery_type": {"iframe": ["http-rp", "meta"], "worker-module-data": [], "worker-classic-data": [], "top": ["http-rp", "meta"], "worker-classic": ["http-rp"], "worker-module": ["http-rp"], "srcdoc": ["meta"]}, "supported_subresource": {"iframe": "*", "worker-module-data": ["xhr", "fetch", "websocket"], "worker-classic-data": ["xhr", "fetch", "websocket"], "top": "*", "worker-classic": ["xhr", "fetch", "websocket"], "worker-module": ["xhr", "fetch", "websocket"], "srcdoc": "*"}}};
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/test-case.sub.js b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/test-case.sub.js
new file mode 100644
index 0000000..356f805
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/test-case.sub.js
@@ -0,0 +1,203 @@
+// TODO: This function is currently placed and duplicated at:
+// - mixed-content/generic/mixed-content-test-case.js
+// - referrer-policy/generic/referrer-policy-test-case.sub.js
+// but should be moved to /common/security-features/resources/common.js.
+function getSubresourceOrigin(originType) {
+  const httpProtocol = "http";
+  const httpsProtocol = "https";
+  const wsProtocol = "ws";
+  const wssProtocol = "wss";
+
+  const sameOriginHost = "{{host}}";
+  const crossOriginHost = "{{domains[www1]}}";
+
+  // These values can evaluate to either empty strings or a ":port" string.
+  const httpPort = getNormalizedPort(parseInt("{{ports[http][0]}}", 10));
+  const httpsPort = getNormalizedPort(parseInt("{{ports[https][0]}}", 10));
+  const wsPort = getNormalizedPort(parseInt("{{ports[ws][0]}}", 10));
+  const wssPort = getNormalizedPort(parseInt("{{ports[wss][0]}}", 10));
+
+  /**
+    @typedef OriginType
+    @type {string}
+
+    Represents the origin of the subresource request URL.
+    The keys of `originMap` below are the valid values.
+
+    Note that there can be redirects from the specified origin
+    (see RedirectionType), and thus the origin of the subresource
+    response URL might be different from what is specified by OriginType.
+  */
+  const originMap = {
+    "same-https": httpsProtocol + "://" + sameOriginHost + httpsPort,
+    "same-http": httpProtocol + "://" + sameOriginHost + httpPort,
+    "cross-https": httpsProtocol + "://" + crossOriginHost + httpsPort,
+    "cross-http": httpProtocol + "://" + crossOriginHost + httpPort,
+    "same-wss": wssProtocol + "://" + sameOriginHost + wssPort,
+    "same-ws": wsProtocol + "://" + sameOriginHost + wsPort,
+    "cross-wss": wssProtocol + "://" + crossOriginHost + wssPort,
+    "cross-ws": wsProtocol + "://" + crossOriginHost + wsPort,
+  };
+
+  return originMap[originType];
+}
+
+// NOTE: This method only strips the fragment and is not in accordance to the
+// recommended draft specification:
+// https://w3c.github.io/webappsec/specs/referrer-policy/#null
+// TODO(kristijanburnik): Implement this helper as defined by spec once added
+// scenarios for URLs containing username/password/etc.
+function stripUrlForUseAsReferrer(url) {
+  return url.replace(/#.*$/, "");
+}
+
+function invokeScenario(scenario) {
+  const redirectionTypeConversion = {
+    "no-redirect": "no-redirect",
+    "keep-scheme": "keep-scheme-redirect",
+    "swap-scheme": "swap-scheme-redirect",
+    "keep-origin": "keep-origin-redirect",
+    "swap-origin": "swap-origin-redirect"
+  };
+  const subresourceTypeConversion = {
+    "beacon": "beacon-request",
+    "fetch": "fetch-request",
+    "xhr": "xhr-request",
+    "websocket": "websocket-request",
+    "worker-classic": "worker-request",
+    "worker-module": "module-worker",
+    "worker-import-data": "module-data-worker-import",
+    "sharedworker-classic": "shared-worker",
+    "worklet-animation": "worklet-animation-top-level",
+    "worklet-audio": "worklet-audio-top-level",
+    "worklet-layout": "worklet-layout-top-level",
+    "worklet-paint": "worklet-paint-top-level",
+    "worklet-animation-import-data": "worklet-animation-data-import",
+    "worklet-audio-import-data": "worklet-audio-data-import",
+    "worklet-layout-import-data": "worklet-layout-data-import",
+    "worklet-paint-import-data": "worklet-paint-data-import"
+  };
+  const subresourceType =
+      subresourceTypeConversion[scenario.subresource] || scenario.subresource;
+  const urls = getRequestURLs(
+    subresourceType,
+    scenario.origin,
+    redirectionTypeConversion[scenario.redirection]);
+  /** @type {Subresource} */
+  const subresource = {
+    subresourceType: subresourceType,
+    url: urls.testUrl,
+    policyDeliveries: scenario.subresource_policy_deliveries,
+  };
+
+  return invokeRequest(subresource, scenario.source_context_list);
+}
+
+function TestCase(scenario, testDescription, sanityChecker) {
+  // This check is A NOOP in release.
+  sanityChecker.checkScenario(scenario);
+
+  const referrerUrlResolver = {
+    "omitted": function(sourceUrl) {
+      return undefined;
+    },
+    "origin": function(sourceUrl) {
+      return new URL(sourceUrl).origin + "/";
+    },
+    "stripped-referrer": function(sourceUrl) {
+      return stripUrlForUseAsReferrer(sourceUrl);
+    }
+  };
+
+  const checkResult = (expectation, result) => {
+    // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
+    let referrerSource = result.sourceContextUrl;
+    const sentFromSrcdoc = scenario.source_context_list.length > 0 &&
+        scenario.source_context_list[scenario.source_context_list.length - 1]
+        .sourceContextType === 'srcdoc';
+    if (sentFromSrcdoc) {
+      // Step 3. While document is an iframe srcdoc document, let document be
+      // document's browsing context's browsing context container's node
+      // document. [spec text]
+
+      // Workaround for srcdoc cases. Currently we only test <iframe srcdoc>
+      // inside the top-level Document, so |document| in the spec here is
+      // the top-level Document.
+      // This doesn't work if e.g. we test <iframe srcdoc> inside another
+      // external <iframe>.
+      referrerSource = location.toString();
+    }
+    const expectedReferrerUrl =
+      referrerUrlResolver[expectation](referrerSource);
+
+    // Check the reported URL.
+    assert_equals(result.referrer,
+                  expectedReferrerUrl,
+                  "Reported Referrer URL is '" +
+                  expectation + "'.");
+    assert_equals(result.headers.referer,
+                  expectedReferrerUrl,
+                  "Reported Referrer URL from HTTP header is '" +
+                  expectedReferrerUrl + "'");
+  };
+
+  function runTest() {
+    function historyBackPromise(t, scenario) {
+      history.back();
+      return new Promise(resolve => {
+          // Wait for completion of `history.back()` by listening the
+          // popstate events that are fired near the end of
+          // `history.back()` processing.
+          window.addEventListener('popstate', resolve, {once: true});
+
+          // Workaround for Safari: Waiting for popstate events causes
+          // timeout in a-tag tests. To avoid timeout, we anyway resolve
+          // the promise.
+          if (scenario.subresource === 'a-tag') {
+            t.step_timeout(resolve, 1000);
+          }
+        });
+    }
+
+    promise_test(_ => {
+      return invokeScenario(scenario)
+        .then(result => checkResult(scenario.expectation, result));
+    }, testDescription);
+
+    // `Referer` headers with length over 4k are culled down to an origin, so,
+    // let's test around that boundary for tests that would otherwise return
+    // the complete URL.
+    // The following tests run only on top-level Documents, because they rely
+    // on navigations using `history`.
+    // Different subresource URLs are used because getRequestURLs() is called
+    // for each sub test which returns a unique URL.
+    if (scenario.expectation == "stripped-referrer" &&
+        scenario.source_context_list.length == 0) {
+      promise_test(t => {
+        history.pushState(null, null, "/");
+        history.replaceState(null, null, "A".repeat(4096 - location.href.length - 1));
+        return invokeScenario(scenario)
+          .then(result => checkResult(scenario.expectation, result))
+          .finally(_ => historyBackPromise(t, scenario));
+      }, "`Referer` header with length < 4k is not stripped to an origin.");
+
+      promise_test(t => {
+        history.pushState(null, null, "/");
+        history.replaceState(null, null, "A".repeat(4096 - location.href.length));
+        return invokeScenario(scenario)
+          .then(result => checkResult(scenario.expectation, result))
+          .finally(_ => historyBackPromise(t, scenario));
+      }, "`Referer` header with length == 4k is not stripped to an origin.");
+
+      promise_test(t => {
+        history.pushState(null, null, "/");
+        history.replaceState(null, null, "A".repeat(4096 - location.href.length + 1));
+        return invokeScenario(scenario)
+          .then(result => checkResult("origin", result))
+          .finally(_ => historyBackPromise(t, scenario));
+      }, "`Referer` header with length > 4k is stripped to an origin.");
+    }
+  }
+
+  return {start: runTest};
+}
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/generate.py b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/generate.py
index ba1e963..480603b 100755
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/generate.py
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/generate.py
@@ -13,30 +13,25 @@
 
 class ReferrerPolicyConfig(object):
     def __init__(self):
-        self.selection_pattern = '%(delivery_type)s/' + \
-                                 '%(origin)s/' + \
-                                 '%(source_scheme)s/' + \
-                                 '%(subresource)s/' + \
-                                 '%(redirection)s/'
+        self.selection_pattern = \
+              '%(source_context_list)s.%(delivery_type)s/' + \
+              '%(delivery_value)s/' + \
+              '%(subresource)s/' + \
+              '%(origin)s.%(redirection)s.%(source_scheme)s'
 
-        self.test_file_path_pattern = '%(spec_name)s/' + self.selection_pattern + \
-                                      '%(name)s.%(source_scheme)s.html'
+        self.test_file_path_pattern = 'gen/' + self.selection_pattern + '.html'
 
-        self.test_description_template = '''The referrer URL is %(expectation)s when a
-document served over %(source_scheme)s requires a
-sub-resource via %(subresource)s using the %(delivery_type)s
-delivery method with %(redirection)s and when
-the target request is %(origin)s.'''
+        self.test_description_template = 'Referrer Policy: Expects %(expectation)s for %(subresource)s to %(origin)s origin and %(redirection)s redirection from %(source_scheme)s context.'
 
         self.test_page_title_template = 'Referrer-Policy: %s'
 
-        self.helper_js = '/referrer-policy/generic/referrer-policy-test-case.sub.js'
+        self.helper_js = '/referrer-policy/generic/test-case.sub.js'
 
         # For debug target only.
         self.sanity_checker_js = '/referrer-policy/generic/sanity-checker.js'
         self.spec_json_js = '/referrer-policy/spec_json.js'
 
-        self.test_case_name = 'ReferrerPolicyTestCase'
+        self.test_case_name = 'TestCase'
 
         script_directory = os.path.dirname(os.path.abspath(__file__))
         self.spec_directory = os.path.abspath(
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/spec.src.json b/third_party/blink/web_tests/external/wpt/referrer-policy/spec.src.json
index f69c289..ab02dcc 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/spec.src.json
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/spec.src.json
@@ -10,6 +10,7 @@
           "name": "insecure-protocol",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": null,
           "redirection": "*",
@@ -24,6 +25,7 @@
           "name": "upgrade-protocol",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": null,
           "redirection": "*",
@@ -38,6 +40,7 @@
           "name": "downgrade-protocol",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": null,
           "redirection": "*",
@@ -52,6 +55,7 @@
           "name": "secure-protocol",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": null,
           "redirection": "*",
@@ -74,6 +78,7 @@
           "name": "generic",
           "expansion": "default",
           "source_scheme": "*",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "no-referrer",
           "redirection": "*",
@@ -93,6 +98,7 @@
           "name": "insecure-protocol",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "no-referrer-when-downgrade",
           "redirection": "*",
@@ -107,6 +113,7 @@
           "name": "upgrade-protocol",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "no-referrer-when-downgrade",
           "redirection": "*",
@@ -121,6 +128,7 @@
           "name": "downgrade-protocol",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "no-referrer-when-downgrade",
           "redirection": "*",
@@ -135,6 +143,7 @@
           "name": "secure-protocol",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "no-referrer-when-downgrade",
           "redirection": "*",
@@ -157,6 +166,7 @@
           "name": "generic",
           "expansion": "default",
           "source_scheme": "*",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "origin",
           "redirection": "*",
@@ -176,6 +186,7 @@
           "name": "same-origin-insecure",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "same-origin",
           "redirection": "*",
@@ -187,6 +198,7 @@
           "name": "same-origin-secure-default",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "same-origin",
           "redirection": "*",
@@ -198,6 +210,7 @@
           "name": "same-origin-insecure",
           "expansion": "override",
           "source_scheme": "*",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "same-origin",
           "redirection": "swap-origin",
@@ -212,6 +225,7 @@
           "name": "cross-origin",
           "expansion": "default",
           "source_scheme": "*",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "same-origin",
           "redirection": "*",
@@ -234,6 +248,7 @@
           "name": "same-origin-insecure",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
@@ -245,6 +260,7 @@
           "name": "same-origin-secure-default",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
@@ -256,6 +272,7 @@
           "name": "same-origin-upgrade",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
@@ -267,6 +284,7 @@
           "name": "same-origin-downgrade",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
@@ -278,6 +296,7 @@
           "name": "same-origin-insecure",
           "expansion": "override",
           "source_scheme": "*",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "origin-when-cross-origin",
           "redirection": "swap-origin",
@@ -292,6 +311,7 @@
           "name": "cross-origin",
           "expansion": "default",
           "source_scheme": "*",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
@@ -314,6 +334,7 @@
           "name": "insecure-protocol",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin",
           "redirection": "*",
@@ -328,6 +349,7 @@
           "name": "upgrade-protocol",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin",
           "redirection": "*",
@@ -342,6 +364,7 @@
           "name": "downgrade-protocol",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin",
           "redirection": "*",
@@ -356,6 +379,7 @@
           "name": "secure-protocol",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin",
           "redirection": "*",
@@ -378,6 +402,7 @@
           "name": "same-insecure",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
@@ -389,6 +414,7 @@
           "name": "same-insecure",
           "expansion": "override",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "swap-origin",
@@ -400,6 +426,7 @@
           "name": "cross-insecure",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
@@ -411,6 +438,7 @@
           "name": "upgrade-protocol",
           "expansion": "default",
           "source_scheme": "http",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
@@ -425,6 +453,7 @@
           "name": "downgrade-protocol",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
@@ -439,6 +468,7 @@
           "name": "same-secure",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
@@ -450,6 +480,7 @@
           "name": "same-secure",
           "expansion": "override",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "swap-origin",
@@ -461,6 +492,7 @@
           "name": "cross-secure",
           "expansion": "default",
           "source_scheme": "https",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
@@ -480,6 +512,7 @@
           "name": "generic",
           "expansion": "default",
           "source_scheme": "*",
+          "source_context_list": "*",
           "delivery_type": "*",
           "delivery_value": "unsafe-url",
           "redirection": "*",
@@ -496,6 +529,7 @@
       "name": "cross-origin-workers",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "redirection": "*",
       "delivery_type": "*",
       "delivery_value": "*",
@@ -504,9 +538,9 @@
         "cross-https"
       ],
       "subresource": [
-        "worker-request",
-        "module-worker",
-        "shared-worker"
+        "worker-classic",
+        "worker-module",
+        "sharedworker-classic"
       ],
       "expectation": "*"
     },
@@ -514,6 +548,7 @@
       "name": "upgraded-protocol-workers",
       "expansion": "*",
       "source_scheme": "http",
+      "source_context_list": "*",
       "delivery_type": "*",
       "delivery_value": "*",
       "redirection": "*",
@@ -522,9 +557,9 @@
         "cross-https"
       ],
       "subresource": [
-        "worker-request",
-        "module-worker",
-        "shared-worker"
+        "worker-classic",
+        "worker-module",
+        "sharedworker-classic"
       ],
       "expectation": "*"
     },
@@ -532,6 +567,7 @@
       "name": "mixed-content-insecure-subresources",
       "expansion": "*",
       "source_scheme": "https",
+      "source_context_list": "*",
       "delivery_type": "*",
       "delivery_value": "*",
       "redirection": "*",
@@ -546,6 +582,7 @@
       "name": "area-tag",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "delivery_type": "*",
       "delivery_value": "*",
       "redirection": "*",
@@ -557,14 +594,15 @@
       "name": "worker-requests-with-swap-origin-redirect",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "delivery_type": "*",
       "delivery_value": "*",
       "redirection": "swap-origin",
       "origin": "*",
       "subresource": [
-        "worker-request",
-        "module-worker",
-        "shared-worker"
+        "worker-classic",
+        "worker-module",
+        "sharedworker-classic"
       ],
       "expectation": "*"
     },
@@ -572,6 +610,7 @@
       "name": "overhead-for-redirection",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "delivery_type": "*",
       "delivery_value": "*",
       "redirection": [
@@ -589,6 +628,7 @@
       "name": "source-https-unsupported-by-web-platform-tests-runners",
       "expansion": "*",
       "source_scheme": "https",
+      "source_context_list": "*",
       "delivery_type": "*",
       "delivery_value": "*",
       "redirection": "*",
@@ -600,6 +640,7 @@
       "name": "<link rel=noreferrer>'s delivery_value should be no-referrer",
       "expansion": "*",
       "source_scheme": "*",
+      "source_context_list": "*",
       "delivery_type": "rel-noref",
       "delivery_value": [
         null,
@@ -617,6 +658,226 @@
       "expectation": "*"
     }
   ],
+  "source_context_schema": {
+    "supported_delivery_type": {
+      "top": [
+        "meta",
+        "http-rp"
+      ],
+      "iframe": [
+        "meta",
+        "http-rp"
+      ],
+      "srcdoc": [
+        "meta"
+      ],
+      "worker-classic": [
+        "http-rp"
+      ],
+      "worker-module": [
+        "http-rp"
+      ],
+      "worker-classic-data": [],
+      "worker-module-data": []
+    },
+    "supported_subresource": {
+      "top": "*",
+      "iframe": "*",
+      "srcdoc": "*",
+      "worker-classic": [
+        "xhr",
+        "fetch",
+        "worker-classic",
+        "worker-module"
+      ],
+      "worker-module": [
+        "xhr",
+        "fetch",
+        "worker-classic",
+        "worker-module"
+      ],
+      "worker-classic-data": [
+        "xhr",
+        "fetch"
+      ],
+      "worker-module-data": [
+        "xhr",
+        "fetch"
+      ]
+    }
+  },
+  "subresource_schema": {
+    "supported_delivery_type": {
+      "iframe-tag": [
+        "attr"
+      ],
+      "img-tag": [
+        "attr"
+      ],
+      "script-tag": [
+        "attr"
+      ],
+      "a-tag": [
+        "attr",
+        "rel-noref"
+      ],
+      "area-tag": [
+        "attr"
+      ],
+      "xhr": [],
+      "fetch": [],
+      "worker-module": [],
+      "sharedworker-classic": [],
+      "worker-classic": []
+    }
+  },
+  "source_context_list_schema": {
+    "top": {
+      "description": "Policy set by the top-level Document",
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "policy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "req": {
+      "description": "Subresource request's policy should override Document's policy",
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "anotherPolicy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": [
+        "nonNullPolicy"
+      ]
+    },
+    "srcdoc-inherit": {
+      "description": "srcdoc iframe should inherit parent Document's policy",
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "policy"
+          ]
+        },
+        {
+          "sourceContextType": "srcdoc"
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "srcdoc": {
+      "description": "srcdoc iframe's policy should override parent Document's policy",
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "anotherPolicy"
+          ]
+        },
+        {
+          "sourceContextType": "srcdoc",
+          "policyDeliveries": [
+            "nonNullPolicy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "iframe": {
+      "description": "external iframe's policy should override parent Document's policy",
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "anotherPolicy"
+          ]
+        },
+        {
+          "sourceContextType": "iframe",
+          "policyDeliveries": [
+            "policy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "worker-classic": {
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "anotherPolicy"
+          ]
+        },
+        {
+          "sourceContextType": "worker-classic",
+          "policyDeliveries": [
+            "policy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "worker-classic-data": {
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "anotherPolicy"
+          ]
+        },
+        {
+          "sourceContextType": "worker-classic-data",
+          "policyDeliveries": [
+            "policy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "worker-module": {
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "anotherPolicy"
+          ]
+        },
+        {
+          "sourceContextType": "worker-module",
+          "policyDeliveries": [
+            "policy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    },
+    "worker-module-data": {
+      "sourceContextList": [
+        {
+          "sourceContextType": "top",
+          "policyDeliveries": [
+            "anotherPolicy"
+          ]
+        },
+        {
+          "sourceContextType": "worker-module-data",
+          "policyDeliveries": [
+            "policy"
+          ]
+        }
+      ],
+      "subresourcePolicyDeliveries": []
+    }
+  },
   "test_expansion_schema": {
     "expansion": [
       "default",
@@ -645,6 +906,17 @@
       "cross-http",
       "cross-https"
     ],
+    "source_context_list": [
+      "top",
+      "req",
+      "srcdoc-inherit",
+      "srcdoc",
+      "iframe",
+      "worker-classic",
+      "worker-classic-data",
+      "worker-module",
+      "worker-module-data"
+    ],
     "source_scheme": [
       "http",
       "https"
@@ -660,11 +932,11 @@
       "script-tag",
       "a-tag",
       "area-tag",
-      "xhr-request",
-      "worker-request",
-      "module-worker",
-      "shared-worker",
-      "fetch-request"
+      "xhr",
+      "worker-classic",
+      "worker-module",
+      "sharedworker-classic",
+      "fetch"
     ],
     "expectation": [
       "omitted",
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/spec_json.js b/third_party/blink/web_tests/external/wpt/referrer-policy/spec_json.js
index 346a621..1b507db 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/spec_json.js
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/spec_json.js
@@ -1 +1 @@
-var SPEC_JSON = {"test_expansion_schema": {"origin": ["same-origin", "cross-origin"], "subresource": ["iframe-tag", "img-tag", "script-tag", "a-tag", "area-tag", "xhr-request", "worker-request", "module-worker", "shared-worker", "fetch-request"], "target_protocol": ["http", "https"], "expansion": ["default", "override"], "delivery_method": ["http-rp", "meta-referrer", "attr-referrer", "rel-noreferrer"], "redirection": ["no-redirect", "keep-origin-redirect", "swap-origin-redirect"], "referrer_url": ["omitted", "origin", "stripped-referrer"], "source_protocol": ["http", "https"]}, "specification": [{"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policies", "referrer_policy": null, "title": "Referrer Policy is not explicitly defined", "test_expansion": [{"origin": "*", "name": "insecure-protocol", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "http", "subresource": "*"}, {"origin": "*", "name": "upgrade-protocol", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "http", "subresource": "*"}, {"origin": "*", "name": "downgrade-protocol", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "omitted", "source_protocol": "https", "subresource": "*"}, {"origin": "*", "name": "secure-protocol", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "https", "subresource": "*"}], "name": "unset-referrer-policy", "description": "Check that referrer URL follows no-referrer-when-downgrade policy when no explicit Referrer Policy is set."}, {"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-no-referrer", "referrer_policy": "no-referrer", "title": "Referrer Policy is set to 'no-referrer'", "test_expansion": [{"origin": "*", "name": "generic", "target_protocol": "*", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "omitted", "source_protocol": "*", "subresource": "*"}], "name": "no-referrer", "description": "Check that sub-resource never gets the referrer URL."}, {"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-no-referrer-when-downgrade", "referrer_policy": "no-referrer-when-downgrade", "title": "Referrer Policy is set to 'no-referrer-when-downgrade'", "test_expansion": [{"origin": "*", "name": "insecure-protocol", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "http", "subresource": "*"}, {"origin": "*", "name": "upgrade-protocol", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "http", "subresource": "*"}, {"origin": "*", "name": "downgrade-protocol", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "omitted", "source_protocol": "https", "subresource": "*"}, {"origin": "*", "name": "secure-protocol", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "https", "subresource": "*"}], "name": "no-referrer-when-downgrade", "description": "Check that non a priori insecure subresource gets the full Referrer URL. A priori insecure subresource gets no referrer information."}, {"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-origin", "referrer_policy": "origin", "title": "Referrer Policy is set to 'origin'", "test_expansion": [{"origin": "*", "name": "generic", "target_protocol": "*", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "*", "subresource": "*"}], "name": "origin", "description": "Check that all subresources in all casses get only the origin portion of the referrer URL."}, {"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-same-origin", "referrer_policy": "same-origin", "title": "Referrer Policy is set to 'same-origin'", "test_expansion": [{"origin": "same-origin", "name": "same-origin-insecure", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "http", "subresource": "*"}, {"origin": "same-origin", "name": "same-origin-secure-default", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "https", "subresource": "*"}, {"origin": "same-origin", "name": "same-origin-insecure", "target_protocol": "*", "expansion": "override", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "swap-origin-redirect", "referrer_url": "omitted", "source_protocol": "*", "subresource": "*"}, {"origin": "cross-origin", "name": "cross-origin", "target_protocol": "*", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "omitted", "source_protocol": "*", "subresource": "*"}], "name": "same-origin", "description": "Check that cross-origin subresources get no referrer information and same-origin get the stripped referrer URL."}, {"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-origin-when-cross-origin", "referrer_policy": "origin-when-cross-origin", "title": "Referrer Policy is set to 'origin-when-cross-origin'", "test_expansion": [{"origin": "same-origin", "name": "same-origin-insecure", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "http", "subresource": "*"}, {"origin": "same-origin", "name": "same-origin-secure-default", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "https", "subresource": "*"}, {"origin": "same-origin", "name": "same-origin-upgrade", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "http", "subresource": "*"}, {"origin": "same-origin", "name": "same-origin-downgrade", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "https", "subresource": "*"}, {"origin": "same-origin", "name": "same-origin-insecure", "target_protocol": "*", "expansion": "override", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "swap-origin-redirect", "referrer_url": "origin", "source_protocol": "*", "subresource": "*"}, {"origin": "cross-origin", "name": "cross-origin", "target_protocol": "*", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "*", "subresource": "*"}], "name": "origin-when-cross-origin", "description": "Check that cross-origin subresources get the origin portion of the referrer URL and same-origin get the stripped referrer URL."}, {"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin", "referrer_policy": "strict-origin", "title": "Referrer Policy is set to 'strict-origin'", "test_expansion": [{"origin": "*", "name": "insecure-protocol", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "http", "subresource": "*"}, {"origin": "*", "name": "upgrade-protocol", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "http", "subresource": "*"}, {"origin": "*", "name": "downgrade-protocol", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "omitted", "source_protocol": "https", "subresource": "*"}, {"origin": "*", "name": "secure-protocol", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "https", "subresource": "*"}], "name": "strict-origin", "description": "Check that non a priori insecure subresource gets only the origin portion of the referrer URL. A priori insecure subresource gets no referrer information."}, {"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin-when-cross-origin", "referrer_policy": "strict-origin-when-cross-origin", "title": "Referrer Policy is set to 'strict-origin-when-cross-origin'", "test_expansion": [{"origin": "same-origin", "name": "same-insecure", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "http", "subresource": "*"}, {"origin": "same-origin", "name": "same-insecure", "target_protocol": "http", "expansion": "override", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "swap-origin-redirect", "referrer_url": "origin", "source_protocol": "http", "subresource": "*"}, {"origin": "cross-origin", "name": "cross-insecure", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "http", "subresource": "*"}, {"origin": "*", "name": "upgrade-protocol", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "http", "subresource": "*"}, {"origin": "*", "name": "downgrade-protocol", "target_protocol": "http", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "omitted", "source_protocol": "https", "subresource": "*"}, {"origin": "same-origin", "name": "same-secure", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "https", "subresource": "*"}, {"origin": "same-origin", "name": "same-secure", "target_protocol": "https", "expansion": "override", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "swap-origin-redirect", "referrer_url": "origin", "source_protocol": "https", "subresource": "*"}, {"origin": "cross-origin", "name": "cross-secure", "target_protocol": "https", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "origin", "source_protocol": "https", "subresource": "*"}], "name": "strict-origin-when-cross-origin", "description": "Check that a priori insecure subresource gets no referrer information. Otherwise, cross-origin subresources get the origin portion of the referrer URL and same-origin get the stripped referrer URL."}, {"specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-unsafe-url", "referrer_policy": "unsafe-url", "title": "Referrer Policy is set to 'unsafe-url'", "test_expansion": [{"origin": "*", "name": "generic", "target_protocol": "*", "expansion": "default", "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"], "redirection": "*", "referrer_url": "stripped-referrer", "source_protocol": "*", "subresource": "*"}], "name": "unsafe-url", "description": "Check that all sub-resources get the stripped referrer URL."}], "referrer_policy_schema": [null, "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "origin-when-cross-origin", "strict-origin", "strict-origin-when-cross-origin", "unsafe-url"], "excluded_tests": [{"origin": "cross-origin", "name": "cross-origin-workers", "target_protocol": "*", "expansion": "*", "delivery_method": "*", "redirection": "*", "referrer_url": "*", "source_protocol": "*", "subresource": ["worker-request", "module-worker", "shared-worker"]}, {"origin": "*", "name": "upgraded-protocol-workers", "target_protocol": "https", "expansion": "*", "delivery_method": "*", "redirection": "*", "referrer_url": "*", "source_protocol": "http", "subresource": ["worker-request", "module-worker", "shared-worker"]}, {"origin": "*", "name": "mixed-content-insecure-subresources", "target_protocol": "http", "expansion": "*", "delivery_method": "*", "redirection": "*", "referrer_url": "*", "source_protocol": "https", "subresource": "*"}, {"origin": "*", "name": "elements-not-supporting-attr-referrer", "target_protocol": "*", "expansion": "*", "delivery_method": ["attr-referrer"], "redirection": "*", "referrer_url": "*", "source_protocol": "*", "subresource": ["xhr-request", "worker-request", "module-worker", "shared-worker", "fetch-request"]}, {"origin": "*", "name": "elements-not-supporting-rel-noreferrer", "target_protocol": "*", "expansion": "*", "delivery_method": ["rel-noreferrer"], "redirection": "*", "referrer_url": "*", "source_protocol": "*", "subresource": ["iframe-tag", "img-tag", "script-tag", "xhr-request", "worker-request", "module-worker", "shared-worker", "fetch-request", "area-tag"]}, {"origin": "*", "name": "area-tag", "target_protocol": "*", "expansion": "*", "delivery_method": "*", "redirection": "*", "referrer_url": "*", "source_protocol": "*", "subresource": "area-tag"}, {"origin": "*", "name": "worker-requests-with-swap-origin-redirect", "target_protocol": "*", "expansion": "*", "delivery_method": "*", "redirection": "swap-origin-redirect", "referrer_url": "*", "source_protocol": "*", "subresource": ["worker-request", "module-worker", "shared-worker"]}, {"origin": "*", "name": "overhead-for-redirection", "target_protocol": "*", "expansion": "*", "delivery_method": "*", "redirection": ["keep-origin-redirect", "swap-origin-redirect"], "referrer_url": "*", "source_protocol": "*", "subresource": ["a-tag", "area-tag"]}, {"origin": "*", "name": "source-https-unsupported-by-web-platform-tests-runners", "target_protocol": "*", "expansion": "*", "delivery_method": "*", "redirection": "*", "referrer_url": "*", "source_protocol": "https", "subresource": "*"}]};
+var SPEC_JSON = {"subresource_schema": {"supported_delivery_type": {"img-tag": ["attr"], "sharedworker-classic": [], "xhr": [], "a-tag": ["attr", "rel-noref"], "area-tag": ["attr"], "iframe-tag": ["attr"], "worker-module": [], "script-tag": ["attr"], "fetch": [], "worker-classic": []}}, "excluded_tests": [{"delivery_value": "*", "origin": ["cross-http", "cross-https"], "delivery_type": "*", "name": "cross-origin-workers", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": ["worker-classic", "worker-module", "sharedworker-classic"]}, {"delivery_value": "*", "origin": ["same-https", "cross-https"], "delivery_type": "*", "name": "upgraded-protocol-workers", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": ["worker-classic", "worker-module", "sharedworker-classic"]}, {"delivery_value": "*", "origin": ["same-http", "cross-http"], "delivery_type": "*", "name": "mixed-content-insecure-subresources", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "*", "origin": "*", "delivery_type": "*", "name": "area-tag", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": "area-tag"}, {"delivery_value": "*", "origin": "*", "delivery_type": "*", "name": "worker-requests-with-swap-origin-redirect", "expectation": "*", "expansion": "*", "redirection": "swap-origin", "source_context_list": "*", "source_scheme": "*", "subresource": ["worker-classic", "worker-module", "sharedworker-classic"]}, {"delivery_value": "*", "origin": "*", "delivery_type": "*", "name": "overhead-for-redirection", "expectation": "*", "expansion": "*", "redirection": ["keep-origin", "swap-origin"], "source_context_list": "*", "source_scheme": "*", "subresource": ["a-tag", "area-tag"]}, {"delivery_value": "*", "origin": "*", "delivery_type": "*", "name": "source-https-unsupported-by-web-platform-tests-runners", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": [null, "no-referrer-when-downgrade", "same-origin", "origin", "origin-when-cross-origin", "strict-origin", "strict-origin-when-cross-origin", "unsafe-url"], "origin": "*", "delivery_type": "rel-noref", "name": "<link rel=noreferrer>'s delivery_value should be no-referrer", "expectation": "*", "expansion": "*", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": "*"}], "specification": [{"test_expansion": [{"delivery_value": null, "origin": ["same-http", "cross-http"], "delivery_type": "*", "name": "insecure-protocol", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": null, "origin": ["same-https", "cross-https"], "delivery_type": "*", "name": "upgrade-protocol", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": null, "origin": ["same-http", "cross-http"], "delivery_type": "*", "name": "downgrade-protocol", "expectation": "omitted", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": null, "origin": ["same-https", "cross-https"], "delivery_type": "*", "name": "secure-protocol", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}], "description": "Check that referrer URL follows no-referrer-when-downgrade policy when no explicit Referrer Policy is set.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policies", "name": "unset-referrer-policy", "title": "Referrer Policy is not explicitly defined"}, {"test_expansion": [{"delivery_value": "no-referrer", "origin": "*", "delivery_type": "*", "name": "generic", "expectation": "omitted", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": "*"}], "description": "Check that sub-resource never gets the referrer URL.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-no-referrer", "name": "no-referrer", "title": "Referrer Policy is set to 'no-referrer'"}, {"test_expansion": [{"delivery_value": "no-referrer-when-downgrade", "origin": ["same-http", "cross-http"], "delivery_type": "*", "name": "insecure-protocol", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "no-referrer-when-downgrade", "origin": ["same-https", "cross-https"], "delivery_type": "*", "name": "upgrade-protocol", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "no-referrer-when-downgrade", "origin": ["same-http", "cross-http"], "delivery_type": "*", "name": "downgrade-protocol", "expectation": "omitted", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "no-referrer-when-downgrade", "origin": ["same-https", "cross-https"], "delivery_type": "*", "name": "secure-protocol", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}], "description": "Check that non a priori insecure subresource gets the full Referrer URL. A priori insecure subresource gets no referrer information.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-no-referrer-when-downgrade", "name": "no-referrer-when-downgrade", "title": "Referrer Policy is set to 'no-referrer-when-downgrade'"}, {"test_expansion": [{"delivery_value": "origin", "origin": "*", "delivery_type": "*", "name": "generic", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": "*"}], "description": "Check that all subresources in all casses get only the origin portion of the referrer URL.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-origin", "name": "origin", "title": "Referrer Policy is set to 'origin'"}, {"test_expansion": [{"delivery_value": "same-origin", "origin": "same-http", "delivery_type": "*", "name": "same-origin-insecure", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "same-origin", "origin": "same-https", "delivery_type": "*", "name": "same-origin-secure-default", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "same-origin", "origin": ["same-http", "same-https"], "delivery_type": "*", "name": "same-origin-insecure", "expectation": "omitted", "expansion": "override", "redirection": "swap-origin", "source_context_list": "*", "source_scheme": "*", "subresource": "*"}, {"delivery_value": "same-origin", "origin": ["cross-http", "cross-https"], "delivery_type": "*", "name": "cross-origin", "expectation": "omitted", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": "*"}], "description": "Check that cross-origin subresources get no referrer information and same-origin get the stripped referrer URL.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-same-origin", "name": "same-origin", "title": "Referrer Policy is set to 'same-origin'"}, {"test_expansion": [{"delivery_value": "origin-when-cross-origin", "origin": "same-http", "delivery_type": "*", "name": "same-origin-insecure", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "origin-when-cross-origin", "origin": "same-https", "delivery_type": "*", "name": "same-origin-secure-default", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "origin-when-cross-origin", "origin": "same-https", "delivery_type": "*", "name": "same-origin-upgrade", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "origin-when-cross-origin", "origin": "same-http", "delivery_type": "*", "name": "same-origin-downgrade", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "origin-when-cross-origin", "origin": ["same-http", "same-https"], "delivery_type": "*", "name": "same-origin-insecure", "expectation": "origin", "expansion": "override", "redirection": "swap-origin", "source_context_list": "*", "source_scheme": "*", "subresource": "*"}, {"delivery_value": "origin-when-cross-origin", "origin": ["cross-http", "cross-https"], "delivery_type": "*", "name": "cross-origin", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": "*"}], "description": "Check that cross-origin subresources get the origin portion of the referrer URL and same-origin get the stripped referrer URL.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-origin-when-cross-origin", "name": "origin-when-cross-origin", "title": "Referrer Policy is set to 'origin-when-cross-origin'"}, {"test_expansion": [{"delivery_value": "strict-origin", "origin": ["same-http", "cross-http"], "delivery_type": "*", "name": "insecure-protocol", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "strict-origin", "origin": ["same-https", "cross-https"], "delivery_type": "*", "name": "upgrade-protocol", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "strict-origin", "origin": ["same-http", "cross-http"], "delivery_type": "*", "name": "downgrade-protocol", "expectation": "omitted", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "strict-origin", "origin": ["same-https", "cross-https"], "delivery_type": "*", "name": "secure-protocol", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}], "description": "Check that non a priori insecure subresource gets only the origin portion of the referrer URL. A priori insecure subresource gets no referrer information.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin", "name": "strict-origin", "title": "Referrer Policy is set to 'strict-origin'"}, {"test_expansion": [{"delivery_value": "strict-origin-when-cross-origin", "origin": "same-http", "delivery_type": "*", "name": "same-insecure", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "strict-origin-when-cross-origin", "origin": "same-http", "delivery_type": "*", "name": "same-insecure", "expectation": "origin", "expansion": "override", "redirection": "swap-origin", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "strict-origin-when-cross-origin", "origin": "cross-http", "delivery_type": "*", "name": "cross-insecure", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "strict-origin-when-cross-origin", "origin": ["same-https", "cross-https"], "delivery_type": "*", "name": "upgrade-protocol", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "http", "subresource": "*"}, {"delivery_value": "strict-origin-when-cross-origin", "origin": ["same-http", "cross-http"], "delivery_type": "*", "name": "downgrade-protocol", "expectation": "omitted", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "strict-origin-when-cross-origin", "origin": "same-https", "delivery_type": "*", "name": "same-secure", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "strict-origin-when-cross-origin", "origin": "same-https", "delivery_type": "*", "name": "same-secure", "expectation": "origin", "expansion": "override", "redirection": "swap-origin", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}, {"delivery_value": "strict-origin-when-cross-origin", "origin": "cross-https", "delivery_type": "*", "name": "cross-secure", "expectation": "origin", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "https", "subresource": "*"}], "description": "Check that a priori insecure subresource gets no referrer information. Otherwise, cross-origin subresources get the origin portion of the referrer URL and same-origin get the stripped referrer URL.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin-when-cross-origin", "name": "strict-origin-when-cross-origin", "title": "Referrer Policy is set to 'strict-origin-when-cross-origin'"}, {"test_expansion": [{"delivery_value": "unsafe-url", "origin": "*", "delivery_type": "*", "name": "generic", "expectation": "stripped-referrer", "expansion": "default", "redirection": "*", "source_context_list": "*", "source_scheme": "*", "subresource": "*"}], "description": "Check that all sub-resources get the stripped referrer URL.", "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-unsafe-url", "name": "unsafe-url", "title": "Referrer Policy is set to 'unsafe-url'"}], "test_expansion_schema": {"delivery_value": [null, "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "origin-when-cross-origin", "strict-origin", "strict-origin-when-cross-origin", "unsafe-url"], "origin": ["same-http", "same-https", "cross-http", "cross-https"], "delivery_type": ["attr", "rel-noref", "http-rp", "meta"], "subresource": ["iframe-tag", "img-tag", "script-tag", "a-tag", "area-tag", "xhr", "worker-classic", "worker-module", "sharedworker-classic", "fetch"], "expectation": ["omitted", "origin", "stripped-referrer"], "expansion": ["default", "override"], "redirection": ["no-redirect", "keep-origin", "swap-origin"], "source_context_list": ["top", "req", "srcdoc-inherit", "srcdoc", "iframe", "worker-classic", "worker-classic-data", "worker-module", "worker-module-data"], "source_scheme": ["http", "https"]}, "source_context_list_schema": {"srcdoc-inherit": {"subresourcePolicyDeliveries": [], "description": "srcdoc iframe should inherit parent Document's policy", "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["policy"]}, {"sourceContextType": "srcdoc"}]}, "worker-module": {"subresourcePolicyDeliveries": [], "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["anotherPolicy"]}, {"sourceContextType": "worker-module", "policyDeliveries": ["policy"]}]}, "worker-module-data": {"subresourcePolicyDeliveries": [], "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["anotherPolicy"]}, {"sourceContextType": "worker-module-data", "policyDeliveries": ["policy"]}]}, "worker-classic-data": {"subresourcePolicyDeliveries": [], "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["anotherPolicy"]}, {"sourceContextType": "worker-classic-data", "policyDeliveries": ["policy"]}]}, "top": {"subresourcePolicyDeliveries": [], "description": "Policy set by the top-level Document", "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["policy"]}]}, "req": {"subresourcePolicyDeliveries": ["nonNullPolicy"], "description": "Subresource request's policy should override Document's policy", "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["anotherPolicy"]}]}, "worker-classic": {"subresourcePolicyDeliveries": [], "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["anotherPolicy"]}, {"sourceContextType": "worker-classic", "policyDeliveries": ["policy"]}]}, "iframe": {"subresourcePolicyDeliveries": [], "description": "external iframe's policy should override parent Document's policy", "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["anotherPolicy"]}, {"sourceContextType": "iframe", "policyDeliveries": ["policy"]}]}, "srcdoc": {"subresourcePolicyDeliveries": [], "description": "srcdoc iframe's policy should override parent Document's policy", "sourceContextList": [{"sourceContextType": "top", "policyDeliveries": ["anotherPolicy"]}, {"sourceContextType": "srcdoc", "policyDeliveries": ["nonNullPolicy"]}]}}, "delivery_key": "referrerPolicy", "source_context_schema": {"supported_delivery_type": {"iframe": ["meta", "http-rp"], "worker-module-data": [], "worker-classic-data": [], "top": ["meta", "http-rp"], "worker-classic": ["http-rp"], "worker-module": ["http-rp"], "srcdoc": ["meta"]}, "supported_subresource": {"iframe": "*", "worker-module-data": ["xhr", "fetch"], "worker-classic-data": ["xhr", "fetch"], "top": "*", "worker-classic": ["xhr", "fetch", "worker-classic", "worker-module"], "worker-module": ["xhr", "fetch", "worker-classic", "worker-module"], "srcdoc": "*"}}};
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index cf423d6..d38a393 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 3a6c6012ba2b9ed662872ccaf7d276d56240943b
+Revision: e1e55e224627a5e74d524f6395a3d1617368a564
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index acb4a39..46c3424 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -33,7 +33,7 @@
       '8048ece6c16c91acfe0d36d1d3cc0890ab6e945c',
   'crashpad/third_party/mini_chromium/mini_chromium':
       Var('chromium_git') + '/chromium/mini_chromium@' +
-      '6ad086b2b6ed3b3169226ee9f311eb2332f332c2',
+      '5889767521eed1483b5858b12c54893f21d72fd0',
   'crashpad/third_party/libfuzzer/src':
       Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
       'fda403cf93ecb8792cb1d061564d89a6553ca020',
diff --git a/third_party/crashpad/crashpad/build/BUILDCONFIG.gn b/third_party/crashpad/crashpad/build/BUILDCONFIG.gn
index 95dc277..bc53108 100644
--- a/third_party/crashpad/crashpad/build/BUILDCONFIG.gn
+++ b/third_party/crashpad/crashpad/build/BUILDCONFIG.gn
@@ -55,6 +55,7 @@
 _default_configs = [
   "//third_party/mini_chromium/mini_chromium/build:default",
   "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors",
+  "//third_party/mini_chromium/mini_chromium/build:Wimplicit_fallthrough",
 ]
 
 if (crashpad_use_libfuzzer) {
diff --git a/third_party/crashpad/crashpad/client/crashpad_client.h b/third_party/crashpad/crashpad/client/crashpad_client.h
index 56d738b..36c9d1f8 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client.h
+++ b/third_party/crashpad/crashpad/client/crashpad_client.h
@@ -588,6 +588,16 @@
   static void UseSystemDefaultHandler();
 #endif
 
+#if defined(OS_CHROMEOS)
+  //! \brief Sets a timestamp on the signal handler to be passed on to
+  //!     crashpad_handler and then eventually Chrome OS's crash_reporter.
+  //!
+  //! \note This method is used by clients that use `StartHandler()` to start
+  //!     a handler and not by clients that use any other handler starting
+  //!     methods.
+  static void SetCrashLoopBefore(uint64_t crash_loop_before_time);
+#endif
+
  private:
 #if defined(OS_MACOSX)
   base::mac::ScopedMachSendRight exception_port_;
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
index 2e2ec30..eb210a2 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
@@ -301,11 +301,20 @@
     ExceptionHandlerProtocol::ClientInformation info = {};
     info.exception_information_address =
         FromPointerCast<VMAddress>(&GetExceptionInfo());
+#if defined(OS_CHROMEOS)
+    info.crash_loop_before_time = crash_loop_before_time_;
+#endif
 
     ExceptionHandlerClient client(sock_to_handler_.get(), true);
     client.RequestCrashDump(info);
   }
 
+#if defined(OS_CHROMEOS)
+  void SetCrashLoopBefore(uint64_t crash_loop_before_time) {
+    crash_loop_before_time_ = crash_loop_before_time;
+  }
+#endif
+
  private:
   RequestCrashDumpHandler() = default;
 
@@ -314,6 +323,14 @@
   ScopedFileHandle sock_to_handler_;
   pid_t handler_pid_ = -1;
 
+#if defined(OS_CHROMEOS)
+  // An optional UNIX timestamp passed to us from Chrome.
+  // This will pass to crashpad_handler and then to Chrome OS crash_reporter.
+  // This should really be a time_t, but it's basically an opaque value (we
+  // don't anything with it except pass it along).
+  uint64_t crash_loop_before_time_ = 0;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(RequestCrashDumpHandler);
 };
 
@@ -526,4 +543,12 @@
   SignalHandler::Get()->SetFirstChanceHandler(handler);
 }
 
+#if defined(OS_CHROMEOS)
+// static
+void CrashpadClient::SetCrashLoopBefore(uint64_t crash_loop_before_time) {
+  auto request_crash_dump_handler = RequestCrashDumpHandler::Get();
+  request_crash_dump_handler->SetCrashLoopBefore(crash_loop_before_time);
+}
+#endif
+
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/compat/BUILD.gn b/third_party/crashpad/crashpad/compat/BUILD.gn
index 7a33d7ca..62ee528 100644
--- a/third_party/crashpad/crashpad/compat/BUILD.gn
+++ b/third_party/crashpad/crashpad/compat/BUILD.gn
@@ -79,6 +79,8 @@
   if (crashpad_is_linux || crashpad_is_android) {
     sources += [
       "linux/signal.h",
+      "linux/sys/mman.cc",
+      "linux/sys/mman.h",
       "linux/sys/ptrace.h",
       "linux/sys/user.h",
     ]
diff --git a/third_party/crashpad/crashpad/compat/linux/sys/mman.cc b/third_party/crashpad/crashpad/compat/linux/sys/mman.cc
new file mode 100644
index 0000000..12aaa2c7
--- /dev/null
+++ b/third_party/crashpad/crashpad/compat/linux/sys/mman.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sys/mman.h>
+
+#include <dlfcn.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#if defined(__GLIBC__)
+
+extern "C" {
+
+int memfd_create(const char* name, unsigned int flags) {
+  using MemfdCreateType = int (*)(const char*, int);
+  static const MemfdCreateType next_memfd_create =
+      reinterpret_cast<MemfdCreateType>(dlsym(RTLD_NEXT, "memfd_create"));
+  return next_memfd_create ? next_memfd_create(name, flags)
+                           : syscall(SYS_memfd_create, name, flags);
+}
+
+}  // extern "C"
+
+#endif  // __GLIBC__
diff --git a/third_party/crashpad/crashpad/compat/linux/sys/mman.h b/third_party/crashpad/crashpad/compat/linux/sys/mman.h
new file mode 100644
index 0000000..61c55d7
--- /dev/null
+++ b/third_party/crashpad/crashpad/compat/linux/sys/mman.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
+#define CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
+
+#include_next <sys/mman.h>
+
+#include <features.h>
+
+// There's no memfd_create() wrapper before glibc 2.27.
+// This can't select for glibc < 2.27 because linux-chromeos-rel bots build this
+// code using a sysroot which has glibc 2.27, but then run it on Ubuntu 16.04,
+// which doesn't.
+#if defined(__GLIBC__)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int memfd_create(const char* name, unsigned int flags);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // __GLIBC__
+
+#endif  // CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
diff --git a/third_party/crashpad/crashpad/handler/BUILD.gn b/third_party/crashpad/crashpad/handler/BUILD.gn
index b841b0c..550f686a 100644
--- a/third_party/crashpad/crashpad/handler/BUILD.gn
+++ b/third_party/crashpad/crashpad/handler/BUILD.gn
@@ -42,6 +42,8 @@
   if (crashpad_is_linux || crashpad_is_android) {
     set_sources_assignment_filter([])
     sources += [
+      "linux/capture_snapshot.cc",
+      "linux/capture_snapshot.h",
       "linux/crash_report_exception_handler.cc",
       "linux/crash_report_exception_handler.h",
       "linux/exception_handler_server.cc",
@@ -49,6 +51,13 @@
     ]
   }
 
+  if (crashpad_is_linux) {
+    sources += [
+      "linux/cros_crash_report_exception_handler.cc",
+      "linux/cros_crash_report_exception_handler.h",
+    ]
+  }
+
   if (crashpad_is_win) {
     sources += [
       "win/crash_report_exception_handler.cc",
diff --git a/third_party/crashpad/crashpad/handler/handler_main.cc b/third_party/crashpad/crashpad/handler/handler_main.cc
index 05a2e7b..7b69e84 100644
--- a/third_party/crashpad/crashpad/handler/handler_main.cc
+++ b/third_party/crashpad/crashpad/handler/handler_main.cc
@@ -56,6 +56,10 @@
 #include "util/string/split_string.h"
 #include "util/synchronization/semaphore.h"
 
+#if defined(OS_CHROMEOS)
+#include "handler/linux/cros_crash_report_exception_handler.h"
+#endif
+
 #if defined(OS_LINUX) || defined(OS_ANDROID)
 #include <unistd.h>
 
@@ -157,6 +161,9 @@
 #endif  // OS_LINUX || OS_ANDROID
 "      --url=URL               send crash reports to this Breakpad server URL,\n"
 "                              only if uploads are enabled for the database\n"
+#if defined(OS_CHROMEOS)
+"      --use-cros-crash-reporter\n"
+#endif  // OS_CHROMEOS
 "      --help                  display this help and exit\n"
 "      --version               output version information and exit\n",
           me.value().c_str());
@@ -188,6 +195,9 @@
   bool periodic_tasks;
   bool rate_limit;
   bool upload_gzip;
+#if defined(OS_CHROMEOS)
+  bool use_cros_crash_reporter;
+#endif  // OS_CHROMEOS
 };
 
 // Splits |key_value| on '=' and inserts the resulting key and value into |map|.
@@ -509,6 +519,12 @@
 int HandlerMain(int argc,
                 char* argv[],
                 const UserStreamDataSources* user_stream_sources) {
+#if defined(OS_CHROMEOS)
+  if (freopen("/var/log/chrome/chrome", "a", stderr) == nullptr) {
+    PLOG(ERROR) << "Failed to redirect stderr to /var/log/chrome/chrome";
+  }
+#endif
+
   InstallCrashHandler();
   CallMetricsRecordNormalExit metrics_record_normal_exit;
 
@@ -553,6 +569,9 @@
     kOptionTraceParentWithException,
 #endif
     kOptionURL,
+#if defined(OS_CHROMEOS)
+    kOptionUseCrosCrashReporter,
+#endif  // OS_CHROMEOS
 
     // Standard options.
     kOptionHelp = -2,
@@ -618,6 +637,12 @@
      kOptionTraceParentWithException},
 #endif  // OS_LINUX || OS_ANDROID
     {"url", required_argument, nullptr, kOptionURL},
+#if defined(OS_CHROEMOS)
+    {"use-cros-crash-reporter",
+      no_argument,
+      nullptr,
+      kOptionUseCrosCrashReporter},
+#endif  // OS_CHROMEOS
     {"help", no_argument, nullptr, kOptionHelp},
     {"version", no_argument, nullptr, kOptionVersion},
     {nullptr, 0, nullptr, 0},
@@ -759,6 +784,12 @@
         options.url = optarg;
         break;
       }
+#if defined(OS_CHROMEOS)
+      case kOptionUseCrosCrashReporter: {
+        options.use_cros_crash_reporter = true;
+        break;
+      }
+#endif  // OS_CHROMEOS
       case kOptionHelp: {
         Usage(me);
         MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
@@ -884,7 +915,27 @@
     upload_thread.Get()->Start();
   }
 
-  CrashReportExceptionHandler exception_handler(
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  std::unique_ptr<ExceptionHandlerServer::Delegate> exception_handler;
+#else
+  std::unique_ptr<CrashReportExceptionHandler> exception_handler;
+#endif
+
+#if defined(OS_CHROMEOS)
+  if (options.use_cros_crash_reporter) {
+    exception_handler = std::make_unique<CrosCrashReportExceptionHandler>(
+        database.get(),
+        &options.annotations,
+        user_stream_sources);
+  } else {
+    exception_handler = std::make_unique<CrashReportExceptionHandler>(
+        database.get(),
+        static_cast<CrashReportUploadThread*>(upload_thread.Get()),
+        &options.annotations,
+        user_stream_sources);
+  }
+#else
+  exception_handler = std::make_unique<CrashReportExceptionHandler>(
       database.get(),
       static_cast<CrashReportUploadThread*>(upload_thread.Get()),
       &options.annotations,
@@ -893,15 +944,17 @@
       nullptr,
 #endif
       user_stream_sources);
+#endif  // OS_CHROMEOS
 
- #if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_LINUX) || defined(OS_ANDROID)
   if (options.exception_information_address) {
     ExceptionHandlerProtocol::ClientInformation info;
     info.exception_information_address = options.exception_information_address;
     info.sanitization_information_address =
         options.sanitization_information_address;
-    return exception_handler.HandleException(getppid(), info) ? EXIT_SUCCESS
-                                                              : ExitFailure();
+    return exception_handler->HandleException(getppid(), geteuid(), info)
+               ? EXIT_SUCCESS
+               : ExitFailure();
   }
 #endif  // OS_LINUX || OS_ANDROID
 
@@ -1005,7 +1058,7 @@
 #if defined(OS_WIN)
   if (options.initial_client_data.IsValid()) {
     exception_handler_server.InitializeWithInheritedDataForInitialClient(
-        options.initial_client_data, &exception_handler);
+        options.initial_client_data, exception_handler.get());
   }
 #elif defined(OS_LINUX) || defined(OS_ANDROID)
   if (options.initial_client_fd == kInvalidFileHandle ||
@@ -1016,7 +1069,7 @@
   }
 #endif  // OS_WIN
 
-  exception_handler_server.Run(&exception_handler);
+  exception_handler_server.Run(exception_handler.get());
 
   return EXIT_SUCCESS;
 }
diff --git a/third_party/crashpad/crashpad/handler/linux/capture_snapshot.cc b/third_party/crashpad/crashpad/handler/linux/capture_snapshot.cc
new file mode 100644
index 0000000..005402956
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/linux/capture_snapshot.cc
@@ -0,0 +1,119 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "handler/linux/capture_snapshot.h"
+
+#include <utility>
+
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/sanitized/sanitization_information.h"
+#include "util/misc/metrics.h"
+#include "util/misc/tri_state.h"
+
+namespace crashpad {
+
+bool CaptureSnapshot(
+    PtraceConnection* connection,
+    const ExceptionHandlerProtocol::ClientInformation& info,
+    const std::map<std::string, std::string>& process_annotations,
+    uid_t client_uid,
+    VMAddress requesting_thread_stack_address,
+    pid_t* requesting_thread_id,
+    std::unique_ptr<ProcessSnapshotLinux>* snapshot,
+    std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot) {
+  std::unique_ptr<ProcessSnapshotLinux> process_snapshot(
+      new ProcessSnapshotLinux());
+  if (!process_snapshot->Initialize(connection)) {
+    Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);
+    return false;
+  }
+
+  pid_t local_requesting_thread_id = -1;
+  if (requesting_thread_stack_address) {
+    local_requesting_thread_id = process_snapshot->FindThreadWithStackAddress(
+        requesting_thread_stack_address);
+  }
+
+  if (requesting_thread_id) {
+    *requesting_thread_id = local_requesting_thread_id;
+  }
+
+  if (!process_snapshot->InitializeException(info.exception_information_address,
+                                             local_requesting_thread_id)) {
+    Metrics::ExceptionCaptureResult(
+        Metrics::CaptureResult::kExceptionInitializationFailed);
+    return false;
+  }
+
+  Metrics::ExceptionCode(process_snapshot->Exception()->Exception());
+
+  CrashpadInfoClientOptions client_options;
+  process_snapshot->GetCrashpadOptions(&client_options);
+  if (client_options.crashpad_handler_behavior == TriState::kDisabled) {
+    return false;
+  }
+
+  for (auto& p : process_annotations) {
+    process_snapshot->AddAnnotation(p.first, p.second);
+  }
+
+  if (info.sanitization_information_address) {
+    SanitizationInformation sanitization_info;
+    ProcessMemoryRange range;
+    if (!range.Initialize(connection->Memory(), connection->Is64Bit()) ||
+        !range.Read(info.sanitization_information_address,
+                    sizeof(sanitization_info),
+                    &sanitization_info)) {
+      Metrics::ExceptionCaptureResult(
+          Metrics::CaptureResult::kSanitizationInitializationFailed);
+      return false;
+    }
+
+    auto annotations_whitelist = std::make_unique<std::vector<std::string>>();
+    auto memory_range_whitelist =
+        std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();
+    if (!ReadAnnotationsWhitelist(
+            range,
+            sanitization_info.annotations_whitelist_address,
+            annotations_whitelist.get()) ||
+        !ReadMemoryRangeWhitelist(
+            range,
+            sanitization_info.memory_range_whitelist_address,
+            memory_range_whitelist.get())) {
+      Metrics::ExceptionCaptureResult(
+          Metrics::CaptureResult::kSanitizationInitializationFailed);
+      return false;
+    }
+
+    std::unique_ptr<ProcessSnapshotSanitized> sanitized(
+        new ProcessSnapshotSanitized());
+    if (!sanitized->Initialize(process_snapshot.get(),
+                               sanitization_info.annotations_whitelist_address
+                                   ? std::move(annotations_whitelist)
+                                   : nullptr,
+                               std::move(memory_range_whitelist),
+                               sanitization_info.target_module_address,
+                               sanitization_info.sanitize_stacks)) {
+      Metrics::ExceptionCaptureResult(
+          Metrics::CaptureResult::kSkippedDueToSanitization);
+      return false;
+    }
+    *sanitized_snapshot = std::move(sanitized);
+  }
+
+  *snapshot = std::move(process_snapshot);
+  return true;
+}
+
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/linux/capture_snapshot.h b/third_party/crashpad/crashpad/handler/linux/capture_snapshot.h
new file mode 100644
index 0000000..78886dc5
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/linux/capture_snapshot.h
@@ -0,0 +1,67 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
+#define CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
+
+#include <sys/types.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "snapshot/linux/process_snapshot_linux.h"
+#include "snapshot/sanitized/process_snapshot_sanitized.h"
+#include "util/linux/exception_handler_protocol.h"
+#include "util/linux/ptrace_connection.h"
+#include "util/misc/address_types.h"
+
+namespace crashpad {
+
+//! \brief Captures a snapshot of a client over \a connection.
+//!
+//! \param[in] connection A PtraceConnection to the client to snapshot.
+//! \param[in] info Information about the client configuring the snapshot.
+//! \param[in] process_annotations A map of annotations to insert as
+//!     process-level annotations into the snapshot.
+//! \param[in] client_uid The client's user ID.
+//! \param[in] requesting_thread_stack_address An address on the stack of the
+//!     thread requesting the snapshot. If \a info includes an exception
+//!     address, the exception will be assigned to the thread whose stack
+//!     address range contains this address. If 0, \a requesting_thread_id will
+//!     be -1.
+//! \param[out] requesting_thread_id The thread ID of the thread corresponding
+//!     to \a requesting_thread_stack_address. Set to -1 if the thread ID could
+//!     not be determined. Optional.
+//! \param[out] process_snapshot A snapshot of the client process, valid if this
+//!     function returns `true`.
+//! \param[out] sanitized_snapshot A sanitized snapshot of the client process,
+//!     valid if this function returns `true` and sanitization was requested in
+//!     \a info.
+//! \return `true` if \a process_snapshot was successfully created. A message
+//!     will be logged on failure, but not if the snapshot was skipped because
+//!     handling was disabled by CrashpadInfoClientOptions.
+bool CaptureSnapshot(
+    PtraceConnection* connection,
+    const ExceptionHandlerProtocol::ClientInformation& info,
+    const std::map<std::string, std::string>& process_annotations,
+    uid_t client_uid,
+    VMAddress requesting_thread_stack_address,
+    pid_t* requesting_thread_id,
+    std::unique_ptr<ProcessSnapshotLinux>* process_snapshot,
+    std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/handler/linux/crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/linux/crash_report_exception_handler.cc
index 875afec..9b4f201 100644
--- a/third_party/crashpad/crashpad/handler/linux/crash_report_exception_handler.cc
+++ b/third_party/crashpad/crashpad/handler/linux/crash_report_exception_handler.cc
@@ -14,19 +14,19 @@
 
 #include "handler/linux/crash_report_exception_handler.h"
 
-#include <vector>
+#include <memory>
+#include <utility>
 
 #include "base/logging.h"
 #include "client/settings.h"
+#include "handler/linux/capture_snapshot.h"
 #include "minidump/minidump_file_writer.h"
-#include "snapshot/crashpad_info_client_options.h"
 #include "snapshot/linux/process_snapshot_linux.h"
 #include "snapshot/sanitized/process_snapshot_sanitized.h"
-#include "snapshot/sanitized/sanitization_information.h"
 #include "util/linux/direct_ptrace_connection.h"
 #include "util/linux/ptrace_client.h"
+#include "util/misc/implicit_cast.h"
 #include "util/misc/metrics.h"
-#include "util/misc/tri_state.h"
 #include "util/misc/uuid.h"
 
 namespace crashpad {
@@ -45,6 +45,7 @@
 
 bool CrashReportExceptionHandler::HandleException(
     pid_t client_process_id,
+    uid_t client_uid,
     const ExceptionHandlerProtocol::ClientInformation& info,
     VMAddress requesting_thread_stack_address,
     pid_t* requesting_thread_id,
@@ -60,6 +61,7 @@
 
   return HandleExceptionWithConnection(&connection,
                                        info,
+                                       client_uid,
                                        requesting_thread_stack_address,
                                        requesting_thread_id,
                                        local_report_id);
@@ -67,6 +69,7 @@
 
 bool CrashReportExceptionHandler::HandleExceptionWithBroker(
     pid_t client_process_id,
+    uid_t client_uid,
     const ExceptionHandlerProtocol::ClientInformation& info,
     int broker_sock,
     UUID* local_report_id) {
@@ -80,142 +83,83 @@
   }
 
   return HandleExceptionWithConnection(
-      &client, info, 0, nullptr, local_report_id);
+      &client, info, client_uid, 0, nullptr, local_report_id);
 }
 
 bool CrashReportExceptionHandler::HandleExceptionWithConnection(
     PtraceConnection* connection,
     const ExceptionHandlerProtocol::ClientInformation& info,
+    uid_t client_uid,
     VMAddress requesting_thread_stack_address,
     pid_t* requesting_thread_id,
     UUID* local_report_id) {
-  ProcessSnapshotLinux process_snapshot;
-  if (!process_snapshot.Initialize(connection)) {
-    Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);
+  std::unique_ptr<ProcessSnapshotLinux> process_snapshot;
+  std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;
+  if (!CaptureSnapshot(connection,
+                       info,
+                       *process_annotations_,
+                       client_uid,
+                       requesting_thread_stack_address,
+                       requesting_thread_id,
+                       &process_snapshot,
+                       &sanitized_snapshot)) {
     return false;
   }
 
-  pid_t local_requesting_thread_id = -1;
-  if (requesting_thread_stack_address) {
-    local_requesting_thread_id = process_snapshot.FindThreadWithStackAddress(
-        requesting_thread_stack_address);
+  UUID client_id;
+  Settings* const settings = database_->GetSettings();
+  if (settings) {
+    // If GetSettings() or GetClientID() fails, something else will log a
+    // message and client_id will be left at its default value, all zeroes,
+    // which is appropriate.
+    settings->GetClientID(&client_id);
   }
+  process_snapshot->SetClientID(client_id);
 
-  if (requesting_thread_id) {
-    *requesting_thread_id = local_requesting_thread_id;
-  }
-
-  if (!process_snapshot.InitializeException(info.exception_information_address,
-                                            local_requesting_thread_id)) {
+  std::unique_ptr<CrashReportDatabase::NewReport> new_report;
+  CrashReportDatabase::OperationStatus database_status =
+      database_->PrepareNewCrashReport(&new_report);
+  if (database_status != CrashReportDatabase::kNoError) {
+    LOG(ERROR) << "PrepareNewCrashReport failed";
     Metrics::ExceptionCaptureResult(
-        Metrics::CaptureResult::kExceptionInitializationFailed);
+        Metrics::CaptureResult::kPrepareNewCrashReportFailed);
     return false;
   }
 
-  Metrics::ExceptionCode(process_snapshot.Exception()->Exception());
+  process_snapshot->SetReportID(new_report->ReportID());
 
-  CrashpadInfoClientOptions client_options;
-  process_snapshot.GetCrashpadOptions(&client_options);
-  if (client_options.crashpad_handler_behavior != TriState::kDisabled) {
-    UUID client_id;
-    Settings* const settings = database_->GetSettings();
-    if (settings) {
-      // If GetSettings() or GetClientID() fails, something else will log a
-      // message and client_id will be left at its default value, all zeroes,
-      // which is appropriate.
-      settings->GetClientID(&client_id);
-    }
+  ProcessSnapshot* snapshot =
+      sanitized_snapshot
+          ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot.get())
+          : implicit_cast<ProcessSnapshot*>(process_snapshot.get());
 
-    process_snapshot.SetClientID(client_id);
-    for (auto& p : *process_annotations_) {
-      process_snapshot.AddAnnotation(p.first, p.second);
-    }
+  MinidumpFileWriter minidump;
+  minidump.InitializeFromSnapshot(snapshot);
+  AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
 
-    std::unique_ptr<CrashReportDatabase::NewReport> new_report;
-    CrashReportDatabase::OperationStatus database_status =
-        database_->PrepareNewCrashReport(&new_report);
-    if (database_status != CrashReportDatabase::kNoError) {
-      LOG(ERROR) << "PrepareNewCrashReport failed";
-      Metrics::ExceptionCaptureResult(
-          Metrics::CaptureResult::kPrepareNewCrashReportFailed);
-      return false;
-    }
+  if (!minidump.WriteEverything(new_report->Writer())) {
+    LOG(ERROR) << "WriteEverything failed";
+    Metrics::ExceptionCaptureResult(
+        Metrics::CaptureResult::kMinidumpWriteFailed);
+    return false;
+  }
 
-    process_snapshot.SetReportID(new_report->ReportID());
+  UUID uuid;
+  database_status =
+      database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
+  if (database_status != CrashReportDatabase::kNoError) {
+    LOG(ERROR) << "FinishedWritingCrashReport failed";
+    Metrics::ExceptionCaptureResult(
+        Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
+    return false;
+  }
 
-    ProcessSnapshot* snapshot = nullptr;
-    ProcessSnapshotSanitized sanitized;
-    std::vector<std::string> annotations_whitelist;
-    std::vector<std::pair<VMAddress, VMAddress>> memory_range_whitelist;
-    if (info.sanitization_information_address) {
-      SanitizationInformation sanitization_info;
-      ProcessMemoryRange range;
-      if (!range.Initialize(connection->Memory(), connection->Is64Bit()) ||
-          !range.Read(info.sanitization_information_address,
-                      sizeof(sanitization_info),
-                      &sanitization_info)) {
-        Metrics::ExceptionCaptureResult(
-            Metrics::CaptureResult::kSanitizationInitializationFailed);
-        return false;
-      }
+  if (upload_thread_) {
+    upload_thread_->ReportPending(uuid);
+  }
 
-      if (!ReadAnnotationsWhitelist(
-              range,
-              sanitization_info.annotations_whitelist_address,
-              &annotations_whitelist) ||
-          !ReadMemoryRangeWhitelist(
-              range,
-              sanitization_info.memory_range_whitelist_address,
-              &memory_range_whitelist)) {
-        Metrics::ExceptionCaptureResult(
-            Metrics::CaptureResult::kSanitizationInitializationFailed);
-        return false;
-      }
-
-      if (!sanitized.Initialize(&process_snapshot,
-                                sanitization_info.annotations_whitelist_address
-                                    ? &annotations_whitelist
-                                    : nullptr,
-                                &memory_range_whitelist,
-                                sanitization_info.target_module_address,
-                                sanitization_info.sanitize_stacks)) {
-        Metrics::ExceptionCaptureResult(
-            Metrics::CaptureResult::kSkippedDueToSanitization);
-        return true;
-      }
-
-      snapshot = &sanitized;
-    } else {
-      snapshot = &process_snapshot;
-    }
-
-    MinidumpFileWriter minidump;
-    minidump.InitializeFromSnapshot(snapshot);
-    AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
-
-    if (!minidump.WriteEverything(new_report->Writer())) {
-      LOG(ERROR) << "WriteEverything failed";
-      Metrics::ExceptionCaptureResult(
-          Metrics::CaptureResult::kMinidumpWriteFailed);
-      return false;
-    }
-
-    UUID uuid;
-    database_status =
-        database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
-    if (database_status != CrashReportDatabase::kNoError) {
-      LOG(ERROR) << "FinishedWritingCrashReport failed";
-      Metrics::ExceptionCaptureResult(
-          Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
-      return false;
-    }
-    if (local_report_id != nullptr) {
-      *local_report_id = uuid;
-    }
-
-    if (upload_thread_) {
-      upload_thread_->ReportPending(uuid);
-    }
+  if (local_report_id != nullptr) {
+    *local_report_id = uuid;
   }
 
   Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
diff --git a/third_party/crashpad/crashpad/handler/linux/crash_report_exception_handler.h b/third_party/crashpad/crashpad/handler/linux/crash_report_exception_handler.h
index dba8b63..522a77d 100644
--- a/third_party/crashpad/crashpad/handler/linux/crash_report_exception_handler.h
+++ b/third_party/crashpad/crashpad/handler/linux/crash_report_exception_handler.h
@@ -60,11 +60,12 @@
       const std::map<std::string, std::string>* process_annotations,
       const UserStreamDataSources* user_stream_data_sources);
 
-  ~CrashReportExceptionHandler();
+  ~CrashReportExceptionHandler() override;
 
   // ExceptionHandlerServer::Delegate:
 
   bool HandleException(pid_t client_process_id,
+                       uid_t client_uid,
                        const ExceptionHandlerProtocol::ClientInformation& info,
                        VMAddress requesting_thread_stack_address = 0,
                        pid_t* requesting_thread_id = nullptr,
@@ -72,6 +73,7 @@
 
   bool HandleExceptionWithBroker(
       pid_t client_process_id,
+      uid_t client_uid,
       const ExceptionHandlerProtocol::ClientInformation& info,
       int broker_sock,
       UUID* local_report_id = nullptr) override;
@@ -80,6 +82,7 @@
   bool HandleExceptionWithConnection(
       PtraceConnection* connection,
       const ExceptionHandlerProtocol::ClientInformation& info,
+      uid_t client_uid,
       VMAddress requesting_thread_stack_address,
       pid_t* requesting_thread_id,
       UUID* local_report_id = nullptr);
diff --git a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
new file mode 100644
index 0000000..a24b17f6
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
@@ -0,0 +1,278 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "handler/linux/cros_crash_report_exception_handler.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "client/settings.h"
+#include "handler/linux/capture_snapshot.h"
+#include "handler/minidump_to_upload_parameters.h"
+#include "minidump/minidump_file_writer.h"
+#include "snapshot/linux/process_snapshot_linux.h"
+#include "snapshot/minidump/process_snapshot_minidump.h"
+#include "snapshot/sanitized/process_snapshot_sanitized.h"
+#include "util/file/file_writer.h"
+#include "util/linux/direct_ptrace_connection.h"
+#include "util/linux/ptrace_client.h"
+#include "util/misc/metrics.h"
+#include "util/misc/uuid.h"
+#include "util/posix/double_fork_and_exec.h"
+
+namespace crashpad {
+
+namespace {
+
+// Returns the process name for a pid.
+const std::string GetProcessNameFromPid(pid_t pid) {
+  // Symlink to process binary is at /proc/###/exe.
+  std::string link_path = "/proc/" + std::to_string(pid) + "/exe";
+
+  constexpr int kMaxSize = 4096;
+  std::unique_ptr<char[]> buf(new char[kMaxSize]);
+  ssize_t size = readlink(link_path.c_str(), buf.get(), kMaxSize);
+  std::string result;
+  if (size < 0) {
+    PLOG(ERROR) << "Failed to readlink " << link_path;
+  } else {
+    result.assign(buf.get(), size);
+    size_t last_slash_pos = result.rfind('/');
+    if (last_slash_pos != std::string::npos) {
+      result = result.substr(last_slash_pos + 1);
+    }
+  }
+  return result;
+}
+
+bool WriteAnnotationsAndMinidump(
+    const std::map<std::string, std::string>& parameters,
+    MinidumpFileWriter& minidump,
+    FileWriter& file_writer) {
+  for (const auto& kv : parameters) {
+    if (kv.first.find(':') != std::string::npos) {
+      LOG(ERROR) << "Annotation key cannot have ':' in it " << kv.first;
+      return false;
+    }
+    if (!file_writer.Write(kv.first.c_str(), strlen(kv.first.c_str()))) {
+      return false;
+    }
+    if (!file_writer.Write(":", 1)) {
+      return false;
+    }
+    size_t value_size = strlen(kv.second.c_str());
+    std::string value_size_str = std::to_string(value_size);
+    if (!file_writer.Write(value_size_str.c_str(), value_size_str.size())) {
+      return false;
+    }
+    if (!file_writer.Write(":", 1)) {
+      return false;
+    }
+    if (!file_writer.Write(kv.second.c_str(), strlen(kv.second.c_str()))) {
+      return false;
+    }
+  }
+
+  static constexpr char kMinidumpName[] =
+      "upload_file_minidump\"; filename=\"dump\":";
+  if (!file_writer.Write(kMinidumpName, sizeof(kMinidumpName) - 1)) {
+    return false;
+  }
+  crashpad::FileOffset dump_size_start_offset = file_writer.Seek(0, SEEK_CUR);
+  if (dump_size_start_offset < 0) {
+    LOG(ERROR) << "Failed to get minidump size start offset";
+    return false;
+  }
+  static constexpr char kMinidumpLengthFilling[] = "00000000000000000000:";
+  if (!file_writer.Write(kMinidumpLengthFilling,
+                         sizeof(kMinidumpLengthFilling) - 1)) {
+    return false;
+  }
+  crashpad::FileOffset dump_start_offset = file_writer.Seek(0, SEEK_CUR);
+  if (dump_start_offset < 0) {
+    LOG(ERROR) << "Failed to get minidump start offset";
+    return false;
+  }
+  if (!minidump.WriteEverything(&file_writer)) {
+    return false;
+  }
+  crashpad::FileOffset dump_end_offset = file_writer.Seek(0, SEEK_CUR);
+  if (dump_end_offset < 0) {
+    LOG(ERROR) << "Failed to get minidump end offset";
+    return false;
+  }
+
+  size_t dump_data_size = dump_end_offset - dump_start_offset;
+  std::string dump_data_size_str = std::to_string(dump_data_size);
+  file_writer.Seek(dump_size_start_offset + strlen(kMinidumpLengthFilling) - 1 -
+                       dump_data_size_str.size(),
+                   SEEK_SET);
+  if (!file_writer.Write(dump_data_size_str.c_str(),
+                         dump_data_size_str.size())) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+CrosCrashReportExceptionHandler::CrosCrashReportExceptionHandler(
+    CrashReportDatabase* database,
+    const std::map<std::string, std::string>* process_annotations,
+    const UserStreamDataSources* user_stream_data_sources)
+    : database_(database),
+      process_annotations_(process_annotations),
+      user_stream_data_sources_(user_stream_data_sources) {}
+
+CrosCrashReportExceptionHandler::~CrosCrashReportExceptionHandler() = default;
+
+bool CrosCrashReportExceptionHandler::HandleException(
+    pid_t client_process_id,
+    uid_t client_uid,
+    const ExceptionHandlerProtocol::ClientInformation& info,
+    VMAddress requesting_thread_stack_address,
+    pid_t* requesting_thread_id,
+    UUID* local_report_id) {
+  Metrics::ExceptionEncountered();
+
+  DirectPtraceConnection connection;
+  if (!connection.Initialize(client_process_id)) {
+    Metrics::ExceptionCaptureResult(
+        Metrics::CaptureResult::kDirectPtraceFailed);
+    return false;
+  }
+
+  return HandleExceptionWithConnection(&connection,
+                                       info,
+                                       client_uid,
+                                       requesting_thread_stack_address,
+                                       requesting_thread_id,
+                                       local_report_id);
+}
+
+bool CrosCrashReportExceptionHandler::HandleExceptionWithBroker(
+    pid_t client_process_id,
+    uid_t client_uid,
+    const ExceptionHandlerProtocol::ClientInformation& info,
+    int broker_sock,
+    UUID* local_report_id) {
+  Metrics::ExceptionEncountered();
+
+  PtraceClient client;
+  if (!client.Initialize(broker_sock, client_process_id)) {
+    Metrics::ExceptionCaptureResult(
+        Metrics::CaptureResult::kBrokeredPtraceFailed);
+    return false;
+  }
+
+  return HandleExceptionWithConnection(
+      &client, info, client_uid, 0, nullptr, local_report_id);
+}
+
+bool CrosCrashReportExceptionHandler::HandleExceptionWithConnection(
+    PtraceConnection* connection,
+    const ExceptionHandlerProtocol::ClientInformation& info,
+    uid_t client_uid,
+    VMAddress requesting_thread_stack_address,
+    pid_t* requesting_thread_id,
+    UUID* local_report_id) {
+  std::unique_ptr<ProcessSnapshotLinux> process_snapshot;
+  std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;
+  if (!CaptureSnapshot(connection,
+                       info,
+                       *process_annotations_,
+                       client_uid,
+                       requesting_thread_stack_address,
+                       requesting_thread_id,
+                       &process_snapshot,
+                       &sanitized_snapshot)) {
+    return false;
+  }
+
+  UUID client_id;
+  Settings* const settings = database_->GetSettings();
+  if (settings) {
+    // If GetSettings() or GetClientID() fails, something else will log a
+    // message and client_id will be left at its default value, all zeroes,
+    // which is appropriate.
+    settings->GetClientID(&client_id);
+  }
+  process_snapshot->SetClientID(client_id);
+
+  UUID uuid;
+  uuid.InitializeWithNew();
+  process_snapshot->SetReportID(uuid);
+
+  ProcessSnapshot* snapshot =
+      sanitized_snapshot
+          ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot.get())
+          : implicit_cast<ProcessSnapshot*>(process_snapshot.get());
+
+  MinidumpFileWriter minidump;
+  minidump.InitializeFromSnapshot(snapshot);
+  AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
+
+  FileWriter file_writer;
+  if (!file_writer.OpenMemfd(base::FilePath("/tmp/minidump"))) {
+    Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kOpenMemfdFailed);
+    return false;
+  }
+
+  std::map<std::string, std::string> parameters =
+      BreakpadHTTPFormParametersFromMinidump(snapshot);
+  // Used to differentiate between breakpad and crashpad while the switch is
+  // ramping up.
+  parameters.emplace("crash_library", "crashpad");
+
+  if (!WriteAnnotationsAndMinidump(parameters, minidump, file_writer)) {
+    Metrics::ExceptionCaptureResult(
+        Metrics::CaptureResult::kMinidumpWriteFailed);
+    return false;
+  }
+
+  // CrOS uses crash_reporter instead of Crashpad to report crashes.
+  // crash_reporter needs to know the pid and uid of the crashing process.
+  std::vector<std::string> argv({"/sbin/crash_reporter"});
+
+  argv.push_back("--chrome_memfd=" + std::to_string(file_writer.fd()));
+  argv.push_back("--pid=" + std::to_string(*requesting_thread_id));
+  argv.push_back("--uid=" + std::to_string(client_uid));
+  std::string process_name = GetProcessNameFromPid(*requesting_thread_id);
+  argv.push_back("--exe=" + (process_name.empty() ? "chrome" : process_name));
+
+  if (info.crash_loop_before_time != 0) {
+    argv.push_back("--crash_loop_before=" +
+                   std::to_string(info.crash_loop_before_time));
+  }
+
+  if (!DoubleForkAndExec(argv,
+                         nullptr /* envp */,
+                         file_writer.fd() /* preserve_fd */,
+                         false /* use_path */,
+                         nullptr /* child_function */)) {
+    LOG(ERROR) << "DoubleForkAndExec failed";
+    Metrics::ExceptionCaptureResult(
+        Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
+    return false;
+  }
+
+  if (local_report_id != nullptr) {
+    *local_report_id = uuid;
+  }
+
+  Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
+  return true;
+}
+
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.h b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.h
new file mode 100644
index 0000000..f6ca445b
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.h
@@ -0,0 +1,97 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
+#define CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "client/crash_report_database.h"
+#include "handler/linux/exception_handler_server.h"
+#include "handler/user_stream_data_source.h"
+#include "util/linux/exception_handler_protocol.h"
+#include "util/linux/ptrace_connection.h"
+#include "util/misc/address_types.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+
+//! \brief An exception handler that writes crash reports to the ChromeOS
+//!     crash_reporter.
+class CrosCrashReportExceptionHandler
+    : public ExceptionHandlerServer::Delegate {
+ public:
+  //! \brief Creates a new object that will pass reports to
+  //!     `/sbin/crash_reporter`.
+  //!
+  //! \param[in] database The database that supplies settings for this client.
+  //!     This object does not write its reports to this database.
+  //! \param[in] process_annotations A map of annotations to insert as
+  //!     process-level annotations into each crash report that is written. Do
+  //!     not confuse this with module-level annotations, which are under the
+  //!     control of the crashing process, and are used to implement Chrome’s
+  //!     “crash keys.” Process-level annotations are those that are beyond the
+  //!     control of the crashing process, which must reliably be set even if
+  //!     the process crashes before it’s able to establish its own annotations.
+  //!     To interoperate with Breakpad servers, the recommended practice is to
+  //!     specify values for the `"prod"` and `"ver"` keys as process
+  //!     annotations.
+  //! \param[in] user_stream_data_sources Data sources to be used to extend
+  //!     crash reports. For each crash report that is written, the data sources
+  //!     are called in turn. These data sources may contribute additional
+  //!     minidump streams. `nullptr` if not required.
+  CrosCrashReportExceptionHandler(
+      CrashReportDatabase* database,
+      const std::map<std::string, std::string>* process_annotations,
+      const UserStreamDataSources* user_stream_data_sources);
+
+  ~CrosCrashReportExceptionHandler() override;
+
+  // ExceptionHandlerServer::Delegate:
+
+  bool HandleException(pid_t client_process_id,
+                       uid_t client_uid,
+                       const ExceptionHandlerProtocol::ClientInformation& info,
+                       VMAddress requesting_thread_stack_address = 0,
+                       pid_t* requesting_thread_id = nullptr,
+                       UUID* local_report_id = nullptr) override;
+
+  bool HandleExceptionWithBroker(
+      pid_t client_process_id,
+      uid_t client_uid,
+      const ExceptionHandlerProtocol::ClientInformation& info,
+      int broker_sock,
+      UUID* local_report_id = nullptr) override;
+
+ private:
+  bool HandleExceptionWithConnection(
+      PtraceConnection* connection,
+      const ExceptionHandlerProtocol::ClientInformation& info,
+      uid_t client_uid,
+      VMAddress requesting_thread_stack_address,
+      pid_t* requesting_thread_id,
+      UUID* local_report_id = nullptr);
+
+  CrashReportDatabase* database_;  // weak
+  const std::map<std::string, std::string>* process_annotations_;  // weak
+  const UserStreamDataSources* user_stream_data_sources_;  // weak
+
+  DISALLOW_COPY_AND_ASSIGN(CrosCrashReportExceptionHandler);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
diff --git a/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc b/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc
index ef03696a..7bb14f92 100644
--- a/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc
+++ b/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc
@@ -448,6 +448,7 @@
     bool multiple_clients) {
   pid_t client_process_id = creds.pid;
   pid_t requesting_thread_id = -1;
+  uid_t client_uid = creds.uid;
 
   switch (
       strategy_decider_->ChooseStrategy(client_sock, multiple_clients, creds)) {
@@ -469,6 +470,7 @@
 
     case PtraceStrategyDecider::Strategy::kDirectPtrace: {
       delegate_->HandleException(client_process_id,
+                                 client_uid,
                                  client_info,
                                  requesting_thread_stack_address,
                                  &requesting_thread_id);
@@ -482,7 +484,7 @@
     case PtraceStrategyDecider::Strategy::kUseBroker:
       DCHECK(!multiple_clients);
       delegate_->HandleExceptionWithBroker(
-          client_process_id, client_info, client_sock);
+          client_process_id, client_uid, client_info, client_sock);
       break;
   }
 
diff --git a/third_party/crashpad/crashpad/handler/linux/exception_handler_server.h b/third_party/crashpad/crashpad/handler/linux/exception_handler_server.h
index b6251e1..ac430a4 100644
--- a/third_party/crashpad/crashpad/handler/linux/exception_handler_server.h
+++ b/third_party/crashpad/crashpad/handler/linux/exception_handler_server.h
@@ -76,6 +76,7 @@
     //! \brief Called on receipt of a crash dump request from a client.
     //!
     //! \param[in] client_process_id The process ID of the crashing client.
+    //! \param[in] client_uid The user ID of the crashing client.
     //! \param[in] info Information on the client.
     //! \param[in] requesting_thread_stack_address Any address within the stack
     //!     range for the the thread that sent the crash dump request. Optional.
@@ -88,6 +89,7 @@
     //! \return `true` on success. `false` on failure with a message logged.
     virtual bool HandleException(
         pid_t client_process_id,
+        uid_t client_uid,
         const ExceptionHandlerProtocol::ClientInformation& info,
         VMAddress requesting_thread_stack_address = 0,
         pid_t* requesting_thread_id = nullptr,
@@ -97,6 +99,7 @@
     //!     crash that should be mediated by a PtraceBroker.
     //!
     //! \param[in] client_process_id The process ID of the crashing client.
+    //! \param[in] client_uid The uid of the crashing client.
     //! \param[in] info Information on the client.
     //! \param[in] broker_sock A socket connected to the PtraceBroker.
     //! \param[out] local_report_id The unique identifier for the report created
@@ -104,12 +107,12 @@
     //! \return `true` on success. `false` on failure with a message logged.
     virtual bool HandleExceptionWithBroker(
         pid_t client_process_id,
+        uid_t client_uid,
         const ExceptionHandlerProtocol::ClientInformation& info,
         int broker_sock,
         UUID* local_report_id = nullptr) = 0;
 
-   protected:
-    ~Delegate() {}
+    virtual ~Delegate() {}
   };
 
   ExceptionHandlerServer();
diff --git a/third_party/crashpad/crashpad/handler/linux/exception_handler_server_test.cc b/third_party/crashpad/crashpad/handler/linux/exception_handler_server_test.cc
index ea10db35..45f19965 100644
--- a/third_party/crashpad/crashpad/handler/linux/exception_handler_server_test.cc
+++ b/third_party/crashpad/crashpad/handler/linux/exception_handler_server_test.cc
@@ -108,6 +108,7 @@
   }
 
   bool HandleException(pid_t client_process_id,
+                       uid_t client_uid,
                        const ExceptionHandlerProtocol::ClientInformation& info,
                        VMAddress requesting_thread_stack_address,
                        pid_t* requesting_thread_id = nullptr,
@@ -141,6 +142,7 @@
 
   bool HandleExceptionWithBroker(
       pid_t client_process_id,
+      uid_t client_uid,
       const ExceptionHandlerProtocol::ClientInformation& info,
       int broker_sock,
       UUID* local_report_id = nullptr) override {
diff --git a/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
index 0b44de6..b5a59e4 100644
--- a/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
+++ b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
@@ -30,7 +30,8 @@
 
 //! \brief An exception handler that writes crash reports for exception messages
 //!     to a CrashReportDatabase.
-class CrashReportExceptionHandler : public UniversalMachExcServer::Interface {
+class CrashReportExceptionHandler final
+    : public UniversalMachExcServer::Interface {
  public:
   //! \brief Creates a new object that will store crash reports in \a database.
   //!
diff --git a/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h
index c2781de3..566f0472d 100644
--- a/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h
+++ b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h
@@ -31,7 +31,8 @@
 
 //! \brief An exception handler that writes crash reports for exception messages
 //!     to a CrashReportDatabase.
-class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
+class CrashReportExceptionHandler final
+    : public ExceptionHandlerServer::Delegate {
  public:
   //! \brief Creates a new object that will store crash reports in \a database.
   //!
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
index 7226545..f29a6fa 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
@@ -232,7 +232,7 @@
   // it as a valid minidump file.
   header_.Signature = MINIDUMP_SIGNATURE;
 
-  if (file_writer->Seek(start_offset, SEEK_SET) != 0) {
+  if (file_writer->Seek(start_offset, SEEK_SET) < 0) {
     return false;
   }
 
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc
index ecad29a..c76a1f57 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc
@@ -105,8 +105,6 @@
   process_memory_.reset(new ProcessMemoryFuchsia());
   process_memory_->Initialize(*process_);
 
-  memory_map_.Initialize(*process_);
-
   INITIALIZATION_STATE_SET_VALID(initialized_);
   return true;
 }
@@ -133,6 +131,16 @@
   return threads_;
 }
 
+const MemoryMapFuchsia* ProcessReaderFuchsia::MemoryMap() {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+  if (!initialized_memory_map_) {
+    InitializeMemoryMap();
+  }
+
+  return memory_map_.get();
+}
+
 void ProcessReaderFuchsia::InitializeModules() {
   DCHECK(!initialized_modules_);
   DCHECK(modules_.empty());
@@ -255,13 +263,15 @@
     std::unique_ptr<ProcessMemoryRange> process_memory_range(
         new ProcessMemoryRange());
     // TODO(scottmg): Could this be limited range?
-    process_memory_range->Initialize(process_memory_.get(), true);
-    process_memory_ranges_.push_back(std::move(process_memory_range));
+    if (process_memory_range->Initialize(process_memory_.get(), true)) {
+      process_memory_ranges_.push_back(std::move(process_memory_range));
 
-    reader->Initialize(*process_memory_ranges_.back(), base);
-    module.reader = reader.get();
-    module_readers_.push_back(std::move(reader));
-    modules_.push_back(module);
+      if (reader->Initialize(*process_memory_ranges_.back(), base)) {
+        module.reader = reader.get();
+        module_readers_.push_back(std::move(reader));
+        modules_.push_back(module);
+      }
+    }
 
     map = next;
   }
@@ -311,7 +321,13 @@
       } else {
         thread.general_registers = general_regs;
 
-        GetStackRegions(general_regs, memory_map_, &thread.stack_regions);
+        const MemoryMapFuchsia* memory_map = MemoryMap();
+        if (memory_map) {
+          // Attempt to retrive stack regions if a memory map was retrieved. In
+          // particular, this may be null when operating on the current process
+          // where the memory map will not be able to be retrieved.
+          GetStackRegions(general_regs, *memory_map, &thread.stack_regions);
+        }
       }
 
       zx_thread_state_vector_regs_t vector_regs;
@@ -329,4 +345,15 @@
   }
 }
 
+void ProcessReaderFuchsia::InitializeMemoryMap() {
+  DCHECK(!initialized_memory_map_);
+
+  initialized_memory_map_ = true;
+
+  memory_map_.reset(new MemoryMapFuchsia);
+  if (!memory_map_->Initialize(*process_)) {
+    memory_map_.reset();
+  }
+}
+
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia.h b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia.h
index 1392004e..91c6331c7 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia.h
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia.h
@@ -112,7 +112,7 @@
   const ProcessMemory* Memory() const { return process_memory_.get(); }
 
   //! \brief Return a memory map for the target process.
-  const MemoryMapFuchsia* MemoryMap() const { return &memory_map_; }
+  const MemoryMapFuchsia* MemoryMap();
 
  private:
   //! Performs lazy initialization of the \a modules_ vector on behalf of
@@ -123,15 +123,20 @@
   //! Threads().
   void InitializeThreads();
 
+  //! Performs lazy initialization of the \a memory_map_ on behalf of
+  //! MemoryMap().
+  void InitializeMemoryMap();
+
   std::vector<Module> modules_;
   std::vector<Thread> threads_;
   std::vector<std::unique_ptr<ElfImageReader>> module_readers_;
   std::vector<std::unique_ptr<ProcessMemoryRange>> process_memory_ranges_;
   std::unique_ptr<ProcessMemoryFuchsia> process_memory_;
-  MemoryMapFuchsia memory_map_;
+  std::unique_ptr<MemoryMapFuchsia> memory_map_;
   zx::unowned_process process_;
   bool initialized_modules_ = false;
   bool initialized_threads_ = false;
+  bool initialized_memory_map_ = false;
   InitializationStateDcheck initialized_;
 
   DISALLOW_COPY_AND_ASSIGN(ProcessReaderFuchsia);
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc
index 1ff758d1..294c9f9 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc
@@ -41,10 +41,13 @@
   InitializeThreads();
   InitializeModules();
 
-  for (const auto& entry : process_reader_.MemoryMap()->Entries()) {
-    if (entry.type == ZX_INFO_MAPS_TYPE_MAPPING) {
-      memory_map_.push_back(
-          std::make_unique<internal::MemoryMapRegionSnapshotFuchsia>(entry));
+  const MemoryMapFuchsia* memory_map = process_reader_.MemoryMap();
+  if (memory_map) {
+    for (const auto& entry : memory_map->Entries()) {
+      if (entry.type == ZX_INFO_MAPS_TYPE_MAPPING) {
+        memory_map_.push_back(
+            std::make_unique<internal::MemoryMapRegionSnapshotFuchsia>(entry));
+      }
     }
   }
 
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized.cc b/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized.cc
index ae689e5..722abac 100644
--- a/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized.cc
+++ b/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized.cc
@@ -84,13 +84,14 @@
 
 bool ProcessSnapshotSanitized::Initialize(
     const ProcessSnapshot* snapshot,
-    const std::vector<std::string>* annotations_whitelist,
-    const std::vector<std::pair<VMAddress, VMAddress>>* memory_range_whitelist,
+    std::unique_ptr<const std::vector<std::string>> annotations_whitelist,
+    std::unique_ptr<const std::vector<std::pair<VMAddress, VMAddress>>>
+        memory_range_whitelist,
     VMAddress target_module_address,
     bool sanitize_stacks) {
   INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
   snapshot_ = snapshot;
-  annotations_whitelist_ = annotations_whitelist;
+  annotations_whitelist_ = std::move(annotations_whitelist);
   sanitize_stacks_ = sanitize_stacks;
 
   if (target_module_address) {
@@ -141,7 +142,7 @@
   if (annotations_whitelist_) {
     for (const auto module : snapshot_->Modules()) {
       modules_.emplace_back(std::make_unique<internal::ModuleSnapshotSanitized>(
-          module, annotations_whitelist_));
+          module, annotations_whitelist_.get()));
     }
   }
 
@@ -158,7 +159,7 @@
     }
   }
 
-  process_memory_.Initialize(snapshot_->Memory(), memory_range_whitelist);
+  process_memory_.Initialize(snapshot_->Memory(), memory_range_whitelist.get());
 
   INITIALIZATION_STATE_SET_VALID(initialized_);
   return true;
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized.h b/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized.h
index 9ed2350..4e7e6d6 100644
--- a/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized.h
+++ b/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized.h
@@ -61,12 +61,13 @@
   //!     internal::StackSnapshotSanitized.
   //! \return `false` if \a snapshot does not meet sanitization requirements and
   //!     should be filtered entirely. Otherwise `true`.
-  bool Initialize(const ProcessSnapshot* snapshot,
-                  const std::vector<std::string>* annotations_whitelist,
-                  const std::vector<std::pair<VMAddress, VMAddress>>*
-                      memory_range_whitelist,
-                  VMAddress target_module_address,
-                  bool sanitize_stacks);
+  bool Initialize(
+      const ProcessSnapshot* snapshot,
+      std::unique_ptr<const std::vector<std::string>> annotations_whitelist,
+      std::unique_ptr<const std::vector<std::pair<VMAddress, VMAddress>>>
+          memory_range_whitelist,
+      VMAddress target_module_address,
+      bool sanitize_stacks);
 
   // ProcessSnapshot:
 
@@ -99,7 +100,7 @@
   RangeSet address_ranges_;
   const ProcessSnapshot* snapshot_;
   ProcessMemorySanitized process_memory_;
-  const std::vector<std::string>* annotations_whitelist_;
+  std::unique_ptr<const std::vector<std::string>> annotations_whitelist_;
   bool sanitize_stacks_;
   InitializationStateDcheck initialized_;
 
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc b/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc
index 9bad86c..5c5ff1a 100644
--- a/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc
@@ -271,17 +271,18 @@
                         addrs.string_address,
                         /* sanitized= */ false);
 
-    std::vector<std::string> annotations_whitelist;
-    annotations_whitelist.push_back(kWhitelistedAnnotationName);
+    auto annotations_whitelist = std::make_unique<std::vector<std::string>>();
+    annotations_whitelist->push_back(kWhitelistedAnnotationName);
 
-    std::vector<std::pair<VMAddress, VMAddress>> memory_ranges_whitelist;
-    memory_ranges_whitelist.push_back(
+    auto memory_ranges_whitelist =
+        std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();
+    memory_ranges_whitelist->push_back(
         std::make_pair(addrs.string_address, addrs.string_address + 1));
 
     ProcessSnapshotSanitized sanitized;
     ASSERT_TRUE(sanitized.Initialize(&snapshot,
-                                     &annotations_whitelist,
-                                     &memory_ranges_whitelist,
+                                     std::move(annotations_whitelist),
+                                     std::move(memory_ranges_whitelist),
                                      addrs.module_address,
                                      true));
 
diff --git a/third_party/crashpad/crashpad/third_party/fuchsia/BUILD.gn b/third_party/crashpad/crashpad/third_party/fuchsia/BUILD.gn
index b7f69e6..9c25b58 100644
--- a/third_party/crashpad/crashpad/third_party/fuchsia/BUILD.gn
+++ b/third_party/crashpad/crashpad/third_party/fuchsia/BUILD.gn
@@ -17,8 +17,8 @@
 if (crashpad_is_in_fuchsia) {
   group("fuchsia") {
     public_deps = [
-      "//zircon/public/fidl/fuchsia.mem",
-      "//zircon/public/fidl/fuchsia.sysinfo:fuchsia.sysinfo_c",
+      "//zircon/system/fidl/fuchsia-mem",
+      "//zircon/system/fidl/fuchsia-sysinfo:fuchsia-sysinfo_c",
       "//zircon/public/lib/fdio",
       "//zircon/public/lib/zx",
     ]
diff --git a/third_party/crashpad/crashpad/third_party/zlib/BUILD.gn b/third_party/crashpad/crashpad/third_party/zlib/BUILD.gn
index f84b8baf..3267b981 100644
--- a/third_party/crashpad/crashpad/third_party/zlib/BUILD.gn
+++ b/third_party/crashpad/crashpad/third_party/zlib/BUILD.gn
@@ -104,6 +104,10 @@
       ]
     }
 
+    configs -= [
+      "//third_party/mini_chromium/mini_chromium/build:Wimplicit_fallthrough",
+    ]
+
     if (current_cpu == "x86" || current_cpu == "x64") {
       sources += [
         "zlib/crc_folding.c",
diff --git a/third_party/crashpad/crashpad/util/file/file_io.h b/third_party/crashpad/crashpad/util/file/file_io.h
index 797db682..3c956cc 100644
--- a/third_party/crashpad/crashpad/util/file/file_io.h
+++ b/third_party/crashpad/crashpad/util/file/file_io.h
@@ -398,6 +398,19 @@
                                    FileWriteMode mode,
                                    FilePermissions permissions);
 
+#if defined(OS_LINUX)
+//! \brief Wraps memfd_create(), logging an error if the operation fails.
+//!     Unlike other file open operations, this doesn't set `O_CLOEXEC`.
+//!
+//! \return The newly opened FileHandle, or an invalid FileHandle on failure.
+//!
+//! \sa ScopedFileHandle
+//! \sa LoggingOpenFileForRead
+//! \sa LoggingOpenFileForWrite
+//! \sa LoggingOpenFileForReadAndWrite
+FileHandle LoggingOpenMemFileForWrite(const base::FilePath& path);
+#endif  // OS_LINUX
+
 //! \brief Wraps OpenFileForReadAndWrite(), logging an error if the operation
 //!     fails.
 //!
diff --git a/third_party/crashpad/crashpad/util/file/file_io_posix.cc b/third_party/crashpad/crashpad/util/file/file_io_posix.cc
index f311616..b72a48eb 100644
--- a/third_party/crashpad/crashpad/util/file/file_io_posix.cc
+++ b/third_party/crashpad/crashpad/util/file/file_io_posix.cc
@@ -16,6 +16,7 @@
 
 #include <fcntl.h>
 #include <sys/file.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -98,6 +99,12 @@
            permissions == FilePermissions::kWorldReadable ? 0644 : 0600));
 }
 
+#if defined(OS_LINUX)
+FileHandle OpenMemFileForOutput(const base::FilePath& path) {
+  return HANDLE_EINTR(memfd_create(path.value().c_str(), 0));
+}
+#endif
+
 }  // namespace
 
 namespace internal {
@@ -149,6 +156,14 @@
   return fd;
 }
 
+#if defined(OS_LINUX)
+FileHandle LoggingOpenMemFileForWrite(const base::FilePath& path) {
+  FileHandle fd = OpenMemFileForOutput(path);
+  PLOG_IF(ERROR, fd < 0) << "memfd_create " << path.value();
+  return fd;
+}
+#endif
+
 FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path,
                                           FileWriteMode mode,
                                           FilePermissions permissions) {
diff --git a/third_party/crashpad/crashpad/util/file/file_writer.cc b/third_party/crashpad/crashpad/util/file/file_writer.cc
index 13ac6cf..de28575d 100644
--- a/third_party/crashpad/crashpad/util/file/file_writer.cc
+++ b/third_party/crashpad/crashpad/util/file/file_writer.cc
@@ -171,6 +171,23 @@
   return true;
 }
 
+#if defined(OS_LINUX)
+bool FileWriter::OpenMemfd(const base::FilePath& path) {
+  CHECK(!file_.is_valid());
+  file_.reset(LoggingOpenMemFileForWrite(path));
+  if (!file_.is_valid()) {
+    return false;
+  }
+
+  weak_file_handle_file_writer_.set_file_handle(file_.get());
+  return true;
+}
+
+int FileWriter::fd() {
+  return file_.get();
+}
+#endif
+
 void FileWriter::Close() {
   CHECK(file_.is_valid());
 
diff --git a/third_party/crashpad/crashpad/util/file/file_writer.h b/third_party/crashpad/crashpad/util/file/file_writer.h
index ed261ec..663adff 100644
--- a/third_party/crashpad/crashpad/util/file/file_writer.h
+++ b/third_party/crashpad/crashpad/util/file/file_writer.h
@@ -131,6 +131,22 @@
             FileWriteMode write_mode,
             FilePermissions permissions);
 
+#if defined(OS_LINUX)
+  //! \brief Wraps LoggingOpenMemFileForWrite().
+  //!
+  //! \return `true` if the operation succeeded, `false` if it failed, with an
+  //!     error message logged.
+  //!
+  //! \note After a successful call, this method or Open() cannot be called
+  //      again until after Close().
+  bool OpenMemfd(const base::FilePath& path);
+
+  //! \brief Returns the underlying file descriptor.
+  //!
+  //! \note This is used when this writes to a Memfd.
+  int fd();
+#endif
+
   //! \brief Wraps CheckedCloseHandle().
   //!
   //! \note It is only valid to call this method on an object that has had a
diff --git a/third_party/crashpad/crashpad/util/linux/exception_handler_protocol.cc b/third_party/crashpad/crashpad/util/linux/exception_handler_protocol.cc
index 628a4e12..45590c8 100644
--- a/third_party/crashpad/crashpad/util/linux/exception_handler_protocol.cc
+++ b/third_party/crashpad/crashpad/util/linux/exception_handler_protocol.cc
@@ -17,7 +17,12 @@
 namespace crashpad {
 
 ExceptionHandlerProtocol::ClientInformation::ClientInformation()
-    : exception_information_address(0), sanitization_information_address(0) {}
+    : exception_information_address(0),
+      sanitization_information_address(0)
+#if defined(OS_LINUX)
+      , crash_loop_before_time(0)
+#endif  // OS_LINUX
+{}
 
 ExceptionHandlerProtocol::ClientToServerMessage::ClientToServerMessage()
     : version(kVersion),
diff --git a/third_party/crashpad/crashpad/util/linux/exception_handler_protocol.h b/third_party/crashpad/crashpad/util/linux/exception_handler_protocol.h
index 7f4da26..7312b9d 100644
--- a/third_party/crashpad/crashpad/util/linux/exception_handler_protocol.h
+++ b/third_party/crashpad/crashpad/util/linux/exception_handler_protocol.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "util/file/file_io.h"
 #include "util/misc/address_types.h"
 
@@ -50,6 +51,13 @@
     //! \brief The address in the client's address space of a
     //!     SanitizationInformation struct, or 0 if there is no such struct.
     VMAddress sanitization_information_address;
+
+#if defined(OS_LINUX)
+    //! \brief Indicates that the client is likely in a crash loop if a crash
+    //!     occurs before this timestamp. This value is only used by ChromeOS's
+    //!     `/sbin/crash_reporter`.
+    uint64_t crash_loop_before_time;
+#endif
   };
 
   //! \brief The signal used to indicate a crash dump is complete.
diff --git a/third_party/crashpad/crashpad/util/misc/metrics.h b/third_party/crashpad/crashpad/util/misc/metrics.h
index 8046497..d976381 100644
--- a/third_party/crashpad/crashpad/util/misc/metrics.h
+++ b/third_party/crashpad/crashpad/util/misc/metrics.h
@@ -139,6 +139,9 @@
     //! \brief Sanitization caused this crash dump to be skipped.
     kSkippedDueToSanitization = 11,
 
+    //! \brief Failure to open a memfd caused this crash dump to be skipped.
+    kOpenMemfdFailed = 12,
+
     //! \brief The number of values in this enumeration; not a valid value.
     kMaxValue
   };
diff --git a/third_party/sqlite/fuzz/README.md b/third_party/sqlite/fuzz/README.md
index 3be57b3..5e5a12c 100644
--- a/third_party/sqlite/fuzz/README.md
+++ b/third_party/sqlite/fuzz/README.md
@@ -44,8 +44,10 @@
 3. `gn args out/Fuzzer  # Set arguments to matches those in the clusterfuzz "Detailed report"'s "GN CONFIG (ARGS.GN)" section`
 4. `autoninja -C out/Fuzzer/ ${FUZZER_NAME}  # Build the fuzzer target`
 5. `./out/Fuzzer/${FUZZER_NAME} ${CLUSTERFUZZ_TESTCASE}  # Verify repro by running fuzzer (for memory leaks, try setting "ASAN_OPTIONS=detect_leaks=1")`
-6. `LPM_DUMP_NATIVE_INPUT=1 SQL_SKIP_QUERIES=AlterTable ./out/Fuzzer/${FUZZER_NAME} ${CLUSTERFUZZ_TESTCASE}  # Try using different args to get SQL statements that will repro the bug`
-7. Optionally, take output from (7) into a repro.sql file for further testing.
+6. `LPM_DUMP_NATIVE_INPUT=1 SQL_SKIP_QUERIES=AlterTable ./out/Fuzzer/${FUZZER_NAME} ${CLUSTERFUZZ_TESTCASE}  # Try using different args to get SQL statements that will repro the bug. SQL_SKIP_QUERIES can help minimize the repro`
+7. Optionally, minimize the testcase further using the `-minimize_crash`
+[flag](https://chromium.googlesource.com/chromium/src/+/master/testing/libfuzzer/reproducing.md#minimizing-a-crash-input-optional).
+8. Optionally, take output from (7) into a repro.sql file for further testing.
 To do so, either copy the SQL query in the output from (6) into a .sql file, or
 run the final command in (7) with a `> repro.sql` at the end, and filter out
 non-sql content afterwards. Either way, ensure that the case continues to repro
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index fdba901..a8af000 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -722,6 +722,10 @@
       'chromeos-kevin-experimental-rel': 'cros_chrome_sdk_headless_ozone',
       'chromeos-kevin-rel': 'cros_chrome_sdk_headless_ozone',
       'linux-chromeos-rel': 'chromeos_with_codecs_release_trybot',
+      # Replicate linux-chromeos-rel for code coverage experiment.
+      # TODO(crbug.com/1000367): Remove once linux-chromeos-coverage-rel is
+      # folded into linux-chromeos-rel.
+      'linux-chromeos-coverage-rel': 'chromeos_with_codecs_release_trybot_code_coverage',
       'linux-chromeos-compile-dbg': 'chromeos_with_codecs_debug_bot',
       'linux-chromeos-dbg': 'chromeos_with_codecs_debug_bot',
     },
@@ -1266,6 +1270,11 @@
       'chromeos_with_codecs', 'release_trybot', 'use_vaapi', 'no_symbols',
     ],
 
+    'chromeos_with_codecs_release_trybot_code_coverage': [
+      'chromeos_with_codecs', 'release_trybot', 'use_vaapi', 'no_symbols',
+      'use_clang_coverage', 'partial_clang_instrumentation',
+    ],
+
     'clang_code_coverage': [
       'release_bot', 'clang', 'use_clang_coverage', 'no_symbols', 'libfuzzer',
     ],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index cf85bde..10570af 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -11655,6 +11655,38 @@
   <int value="4" label="Failed Architecture mismatch"/>
 </enum>
 
+<enum name="CrostiniResult">
+  <int value="0" label="SUCCESS"/>
+  <int value="4" label="CREATE_DISK_IMAGE_FAILED"/>
+  <int value="5" label="VM_START_FAILED"/>
+  <int value="6" label="VM_STOP_FAILED"/>
+  <int value="7" label="DESTROY_DISK_IMAGE_FAILED"/>
+  <int value="8" label="LIST_VM_DISKS_FAILED"/>
+  <int value="9" label="CLIENT_ERROR"/>
+  <int value="11" label="CONTAINER_DOWNLOAD_TIMED_OUT"/>
+  <int value="12" label="CONTAINER_CREATE_CANCELLED"/>
+  <int value="13" label="CONTAINER_CREATE_FAILED"/>
+  <int value="14" label="CONTAINER_START_CANCELLED"/>
+  <int value="15" label="CONTAINER_START_FAILED"/>
+  <int value="17" label="INSTALL_LINUX_PACKAGE_FAILED"/>
+  <int value="18" label="BLOCKING_OPERATION_ALREADY_ACTIVE"/>
+  <int value="19" label="UNINSTALL_PACKAGE_FAILED"/>
+  <int value="20" label="SSHFS_MOUNT_ERROR"/>
+  <int value="21" label="OFFLINE_WHEN_UPGRADE_REQUIRED"/>
+  <int value="22" label="LOAD_COMPONENT_FAILED"/>
+  <int value="27" label="CROSTINI_UNINSTALLER_RUNNING"/>
+  <int value="29" label="UNKNOWN_ERROR"/>
+  <int value="30" label="CONTAINER_EXPORT_IMPORT_FAILED"/>
+  <int value="31" label="CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED"/>
+  <int value="32" label="CONTAINER_EXPORT_IMPORT_FAILED_VM_STARTED"/>
+  <int value="33" label="CONTAINER_EXPORT_IMPORT_FAILED_ARCHITECTURE"/>
+  <int value="34" label="NOT_ALLOWED"/>
+  <int value="35" label="CONTAINER_EXPORT_IMPORT_FAILED_SPACE"/>
+  <int value="36" label="GET_CONTAINER_SSH_KEYS_FAILED"/>
+  <int value="37" label="CONTAINER_EXPORT_IMPORT_CANCELLED"/>
+  <int value="38" label="RESTART_ABORTED"/>
+</enum>
+
 <enum name="CrostiniSetupResult">
   <int value="0" label="Not Started"/>
   <int value="1" label="User Cancelled"/>
@@ -32179,6 +32211,20 @@
   <int value="3" label="App Bundle URL, not handled"/>
 </enum>
 
+<enum name="IOSJavaScriptDialogDismissalCause">
+  <int value="0" label="Tab closed">The tab owning the dialog was closed</int>
+  <int value="1" label="Closed by user">
+    The user tapped on the OK or Cancel button.
+  </int>
+  <int value="2" label="Blocked by user">
+    Dialog was blocked because the user tapped the &quot;Suppress Dialogs&quot;
+    option.
+  </int>
+  <int value="3" label="Closed for navigation">
+    The user navigated the tab (e.g. back/forward, reload)
+  </int>
+</enum>
+
 <enum name="IosLocationAuthorizationStatus">
   <int value="0" label="User has not made a choice"/>
   <int value="1" label="Restricted"/>
@@ -35557,6 +35603,7 @@
       label="OmniboxUIExperimentBlueTitlesAndGrayUrlsOnPageSuggestions:disabled"/>
   <int value="-666508951" label="CrOSContainer:enabled"/>
   <int value="-663476391" label="enable-pixel-canvas-recording:enabled"/>
+  <int value="-662720891" label="PreviewsCoinFlipHoldback_UKMOnly:enabled"/>
   <int value="-662064703" label="MediaSessionService:enabled"/>
   <int value="-661978438" label="enable-data-reduction-proxy-lo-fi"/>
   <int value="-660160292" label="enable-apps-show-on-first-paint"/>
@@ -35626,6 +35673,7 @@
   <int value="-561194974" label="AutofillExpandedPopupViews:enabled"/>
   <int value="-560551550" label="use-memory-pressure-chromeos"/>
   <int value="-560114351" label="OfflinePagesRenovations:disabled"/>
+  <int value="-558471324" label="PreviewsCoinFlipHoldback_UKMOnly:disabled"/>
   <int value="-557742250" label="ContentSuggestionsCategories:disabled"/>
   <int value="-548082154" label="protect-sync-credential:disabled"/>
   <int value="-547301855" label="SyncPseudoUSSSupervisedUsers:enabled"/>
@@ -43593,6 +43641,14 @@
   <int value="2" label="Suppression expired"/>
 </enum>
 
+<enum name="NotificationSchedulerNotificationLifeCycleEvent">
+  <int value="0" label="Schedule request"/>
+  <int value="1" label="Scheduled"/>
+  <int value="2" label="Invalid input parameters"/>
+  <int value="3" label="Shown"/>
+  <int value="4" label="Client Cancel"/>
+</enum>
+
 <enum name="NotificationSchedulerUserActionType">
   <int value="0" label="Click"/>
   <int value="1" label="Button Click"/>
@@ -54444,6 +54500,18 @@
   </int>
 </enum>
 
+<enum name="SharingClickToCallEntryPoint">
+  <int value="0" label="Left click on a phone link"/>
+  <int value="1" label="Right click on a phone link"/>
+  <int value="2" label="Right click on a phone number selection"/>
+</enum>
+
+<enum name="SharingClickToCallSelection">
+  <int value="0" label="None"/>
+  <int value="1" label="Device"/>
+  <int value="2" label="App"/>
+</enum>
+
 <enum name="SharingDeviceRegistrationResult">
   <int value="0" label="Operation is successful"/>
   <int value="1" label="Failed with Sync not ready"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index a2c048d1..a0d0143d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4691,6 +4691,22 @@
   <summary>Interactions with the App Launcher promo dialog.</summary>
 </histogram>
 
+<histogram name="Apps.AppList.AggregatedMlAppRankFail" expires_after="M92">
+  <owner>pdyson@chromium.org</owner>
+  <summary>
+    The number of apps the were not able to be ranked by the Aggregated ML app
+    ranker (Top Cat).
+  </summary>
+</histogram>
+
+<histogram name="Apps.AppList.AggregatedMlAppRankSuccess" expires_after="M92">
+  <owner>pdyson@chromium.org</owner>
+  <summary>
+    The number of apps successfully ranked by the Aggregated ML app ranker (Top
+    Cat).
+  </summary>
+</histogram>
+
 <histogram name="Apps.AppList.AnswerCardSearchProvider.SearchAnswerLoadingTime"
     units="ms" expires_after="2020-03-01">
   <owner>jennyz@chriomium.org</owner>
@@ -25267,6 +25283,17 @@
   </summary>
 </histogram>
 
+<histogram name="Crostini.RestarterResult" enum="CrostiniResult"
+    expires_after="2020-09-01">
+  <owner>sidereal@google.com</owner>
+  <owner>hollingum@google.com</owner>
+  <owner>nverne@chromium.org</owner>
+  <summary>
+    The result of a single run of CrostiniRestarter. This is recorded any time
+    the crostini restart flow is triggered, except during the initial install.
+  </summary>
+</histogram>
+
 <histogram name="Crostini.Restore" enum="CrostiniImportContainerResult"
     expires_after="2020-01-01">
   <owner>joelhockey@chromium.org</owner>
@@ -54914,6 +54941,13 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.Dialogs.JavaScriptDialogClosed"
+    enum="IOSJavaScriptDialogDismissalCause" expires_after="M80">
+  <owner>kkhorimoto@chromium.org</owner>
+  <owner>michaeldo@chromium.org</owner>
+  <summary>Tracks the way JavaScript dialogs are closed on iOS.</summary>
+</histogram>
+
 <histogram name="IOS.DragAndDrop.DragContent" enum="DragContent"
     expires_after="2020-03-01">
   <owner>jif@chromium.org</owner>
@@ -87461,6 +87495,19 @@
   </summary>
 </histogram>
 
+<histogram name="Notifications.Scheduler.NotificationLifeCycleEvent"
+    enum="NotificationSchedulerNotificationLifeCycleEvent"
+    expires_after="2020-08-01">
+<!-- Name completed by histogram_suffixes name="NotificationSchedulerClientType" -->
+
+  <owner>xingliu@chromium.org</owner>
+  <owner>hesen@chromium.org</owner>
+  <summary>
+    Records life cycle events for a scheduled notification, when notification is
+    scheduled, shown or encountering any error.
+  </summary>
+</histogram>
+
 <histogram name="Notifications.Scheduler.PngIconConverter.DecodeResult"
     enum="BooleanSuccess" expires_after="2020-08-01">
   <owner>xingliu@chromium.org</owner>
@@ -140725,8 +140772,9 @@
 </histogram>
 
 <histogram name="Tab.AgeUponRestoreFromColdStart" units="minutes"
-    expires_after="M77">
+    expires_after="M82">
   <owner>dtrainor@chromium.org</owner>
+  <owner>yfriedman@chromium.org</owner>
   <summary>
     [Android] Age (time since the last display in previous sessions) of a tab
     being restored due to the first tab switch after the browser cold start,
@@ -141023,8 +141071,9 @@
   </summary>
 </histogram>
 
-<histogram name="Tab.PerceivedRestoreTime" units="ms" expires_after="M77">
+<histogram name="Tab.PerceivedRestoreTime" units="ms" expires_after="M82">
   <owner>dtrainor@chromium.org</owner>
+  <owner>yfriedman@chromium.org</owner>
   <summary>
     [Android] User-perceived load time for a successful tab restore, measured
     from the first time the user sees the tab being restored until the load
@@ -167747,6 +167796,8 @@
   <suffix name="WebUI"/>
   <affected-histogram name="Notifications.Scheduler.IhnrActionButtonEvent"/>
   <affected-histogram name="Notifications.Scheduler.Impression.Count"/>
+  <affected-histogram
+      name="Notifications.Scheduler.NotificationLifeCycleEvent"/>
   <affected-histogram name="Notifications.Scheduler.UserAction"/>
 </histogram_suffixes>
 
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 1160d08..806d1b2 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -7165,6 +7165,39 @@
   </metric>
 </event>
 
+<event name="Sharing.ClickToCall">
+  <owner>knollr@chromium.org</owner>
+  <summary>
+    Collected when a user finishes a Click to Call journey.
+  </summary>
+  <metric name="EntryPoint" enum="SharingClickToCallEntryPoint">
+    <summary>
+      Specifies the entry point of the user journey (i.e. left click, right
+      click, text selection).
+    </summary>
+  </metric>
+  <metric name="HasApps" enum="Boolean">
+    <summary>
+      Boolean value indicating whether any (&gt;0) apps are available that the
+      call could be sent to.
+    </summary>
+  </metric>
+  <metric name="HasDevices" enum="Boolean">
+    <summary>
+      Boolean value indicating whether any (&gt;0) devices are available that
+      the call could be sent to. These belong to the same account and are
+      running Android.
+    </summary>
+  </metric>
+  <metric name="Selection" enum="SharingClickToCallSelection">
+    <summary>
+      Whether the user selected a device, an app or nothing (i.e. dismissal) in
+      the user interface. For some entry points this might always be the same
+      value (e.g. right clicks always select devices).
+    </summary>
+  </metric>
+</event>
+
 <event name="SiteIsolation.XSD.Browser.Blocked">
   <obsolete>
     Removed in June 2019 / M77. Some old data has been saved in a
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 4a7a8bbd..b80ef9c1 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -90,6 +90,7 @@
 crbug.com/901493 [ android-nexus-6 android-webview ] blink_perf.paint/* [ Skip ]
 crbug.com/963967 [ android ] blink_perf.paint/select-all-words.html [ Skip ]
 crbug.com/966636 [ android-pixel-2 android-webview ] blink_perf.paint/select-all-words.html [ Skip ]
+crbug.com/1000460 [ android-pixel-2 ] blink_perf.paint/color-changes.html [ Skip ]
 
 # Benchmark: blink_perf.parser
 crbug.com/966913 [ android-nexus-5x android-webview ] blink_perf.parser/query-selector-all-class-deep.html [ Skip ]
diff --git a/ui/gl/gl_switches.h b/ui/gl/gl_switches.h
index e461dd5..42212a7 100644
--- a/ui/gl/gl_switches.h
+++ b/ui/gl/gl_switches.h
@@ -19,8 +19,8 @@
 GL_EXPORT extern const char kGLImplementationANGLEName[];
 GL_EXPORT extern const char kGLImplementationSwiftShaderName[];
 GL_EXPORT extern const char kGLImplementationSwiftShaderForWebGLName[];
-extern const char kGLImplementationMockName[];
-extern const char kGLImplementationStubName[];
+GL_EXPORT extern const char kGLImplementationMockName[];
+GL_EXPORT extern const char kGLImplementationStubName[];
 GL_EXPORT extern const char kGLImplementationDisabledName[];
 
 GL_EXPORT extern const char kANGLEImplementationDefaultName[];
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
index 00f2776..8926f18 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
@@ -12,6 +12,7 @@
 import android.widget.LinearLayout;
 
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.components.embedder_support.view.ContentViewRenderView;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.ViewEventSink;
@@ -28,10 +29,12 @@
     private long mNativeBrowserController;
 
     private ActivityWindowAndroid mWindowAndroid;
-    // This is set as the content view of the activity. It contains mContentView.
+    // This is set as the content view of the activity. It contains mContentViewRenderView.
     private LinearLayout mLinearLayout;
     // This is parented to mLinearLayout.
-    private ContentViewRenderView mContentView;
+    private ContentViewRenderView mContentViewRenderView;
+    // One of these is needed per WebContents.
+    private ContentView mContentView;
     private ProfileImpl mProfile;
     private WebContents mWebContents;
     private BrowserObserverProxy mBrowserObserverProxy;
@@ -65,21 +68,27 @@
         mLinearLayout.setOrientation(LinearLayout.VERTICAL);
 
         mWindowAndroid = new ActivityWindowAndroid(context);
-        mContentView = new ContentViewRenderView(context);
-        mWindowAndroid.setAnimationPlaceholderView(mContentView.getSurfaceView());
+        mContentViewRenderView = new ContentViewRenderView(context);
+        mWindowAndroid.setAnimationPlaceholderView(mContentViewRenderView.getSurfaceView());
 
-        mContentView.onNativeLibraryLoaded(mWindowAndroid);
+        mContentViewRenderView.onNativeLibraryLoaded(mWindowAndroid);
 
         mNativeBrowserController = nativeCreateBrowserController(profile.getNativeProfile());
         mWebContents = nativeGetWebContents(mNativeBrowserController);
-        mWebContents.initialize("", ViewAndroidDelegate.createBasicDelegate(mContentView),
+        mWebContents.initialize("", ViewAndroidDelegate.createBasicDelegate(mContentViewRenderView),
                 new InternalAccessDelegateImpl(), mWindowAndroid,
                 WebContents.createDefaultInternalsHolder());
 
-        mContentView.setCurrentWebContents(mWebContents);
-        mLinearLayout.addView(mContentView,
+        mContentViewRenderView.setCurrentWebContents(mWebContents);
+        mLinearLayout.addView(mContentViewRenderView,
                 new LinearLayout.LayoutParams(
                         LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 1f));
+
+        mContentView = ContentView.createContentView(context, mWebContents);
+        mContentViewRenderView.addView(mContentView,
+                new LinearLayout.LayoutParams(
+                        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1f));
+
         mWebContents.onShow();
     }