diff --git a/BUILD.gn b/BUILD.gn
index bf2ddc9..0e7a624 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -389,10 +389,6 @@
       ]
     }
 
-    if (target_cpu != "x64") {
-      deps += [ "//content/shell/android:chromium_linker_test_apk" ]
-    }
-
     if (enable_chrome_android_internal) {
       deps += [ "//clank" ]
     }
diff --git a/DEPS b/DEPS
index 02f518b3..7461b416f 100644
--- a/DEPS
+++ b/DEPS
@@ -175,11 +175,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': '0340292972b9719ac8eec5ddccff2805b9045d42',
+  'skia_revision': '6dc0f63a509cc111cbda2a419cc835bd65272bb0',
   # 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': '576a8e97c96ee49267829f4c6825b257cf533f77',
+  'v8_revision': '83b2817d8edb5b18f82a57b2500f76e4b6e43849',
   # 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.
@@ -187,11 +187,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '45c5398037c4daefd01ef466292497790ba28ef0',
+  'angle_revision': 'd0748eb038be66dfd05c05773d1edbfd3bdacbc8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '16ae92a4ee5212883f1dbeefd1a91f924b2c2548',
+  'swiftshader_revision': 'ce25c2d434cfba5c7edee5b82078e033bc0057f2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -238,7 +238,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': 'e4aabc8bab65e599245a36962a229c9dec2c4b5d',
+  'catapult_revision': '7004f998c7ba0e88c27d6f294666dd67b0916ec8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -246,7 +246,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'af976e22c803c07f578a59826583ab72d5faa246',
+  'devtools_frontend_revision': '6a14c8da7c4d00e6631b01aef693561d591b24d7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -532,7 +532,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '8a6d4f9ab70a581253c8c2fb89ba6e076b71431a',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'ecb6de8a5a229721f761f13056c584c2b9208b6c',
       'condition': 'checkout_ios',
   },
 
@@ -874,7 +874,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8cf4a76817cbc62ad4e47a230978355a79840009',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c7796cf40d5ee7694c8d8abe91d4ee1023444bd8',
       'condition': 'checkout_linux',
   },
 
@@ -1250,7 +1250,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'ccdbaf215f0c8ff228c7ddf615fb50efe9ed6f04',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'c095a02a0ee049448900116fea85bd35d4cf30f6',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1454,7 +1454,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'ec18cc3262922e7dcdbe70243c6f40606f979144',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '59f3b71c04deb301c989d8f316424d01dd91d2c5',
+    Var('webrtc_git') + '/src.git' + '@' + '4d3f93f348136b6cbad827124be4cedf5794aab3',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1529,7 +1529,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1422f8e60e7b0e0b4618f4b48c7919bcdf11d456',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6ad2153566cc1e9270d681c2a621efd9537ba178',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/home_screen/home_launcher_gesture_handler.h b/ash/home_screen/home_launcher_gesture_handler.h
index 9bbae69..bcc0da1 100644
--- a/ash/home_screen/home_launcher_gesture_handler.h
+++ b/ash/home_screen/home_launcher_gesture_handler.h
@@ -95,6 +95,11 @@
 
   Mode mode() const { return mode_; }
 
+  SwipeHomeToOverviewController*
+  swipe_home_to_overview_controller_for_testing() {
+    return swipe_home_to_overview_controller_.get();
+  }
+
  private:
   class ScopedWindowModifier;
 
diff --git a/ash/shelf/contextual_nudge.cc b/ash/shelf/contextual_nudge.cc
index 6bbd8f6..a989145 100644
--- a/ash/shelf/contextual_nudge.cc
+++ b/ash/shelf/contextual_nudge.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shelf/shelf.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/wm/collision_detection/collision_detection_utils.h"
 #include "ui/aura/window.h"
@@ -41,15 +42,17 @@
                                  Position position,
                                  const gfx::Insets& margins,
                                  const base::string16& text,
-                                 SkColor text_color)
+                                 SkColor text_color,
+                                 const base::RepeatingClosure& tap_callback)
     : views::BubbleDialogDelegateView(anchor,
                                       GetArrowForPosition(position),
-                                      views::BubbleBorder::NO_ASSETS) {
+                                      views::BubbleBorder::NO_ASSETS),
+      tap_callback_(tap_callback) {
   set_color(SK_ColorTRANSPARENT);
   set_margins(margins);
   set_close_on_deactivate(false);
+  set_accept_events(!tap_callback.is_null());
   SetCanActivate(false);
-  set_accept_events(false);
   set_adjust_if_offscreen(false);
   set_shadow(views::BubbleBorder::NO_ASSETS);
   DialogDelegate::set_buttons(ui::DIALOG_BUTTON_NONE);
@@ -94,4 +97,26 @@
   return ui::LAYER_NOT_DRAWN;
 }
 
+void ContextualNudge::OnGestureEvent(ui::GestureEvent* event) {
+  if (event->type() == ui::ET_GESTURE_TAP && tap_callback_) {
+    event->StopPropagation();
+    tap_callback_.Run();
+    return;
+  }
+
+  // Pass on non tap events to the shelf (so it can handle swipe gestures that
+  // start on top of the nudge). Convert event to screen coordinates, as this is
+  // what Shelf::ProcessGestureEvent() expects.
+  ui::GestureEvent event_in_screen(*event);
+  gfx::Point location_in_screen(event->location());
+  View::ConvertPointToScreen(this, &location_in_screen);
+  event_in_screen.set_location(location_in_screen);
+
+  Shelf* shelf = Shelf::ForWindow(GetWidget()->GetNativeWindow());
+  if (shelf->ProcessGestureEvent(event_in_screen))
+    event->StopPropagation();
+  else
+    views::BubbleDialogDelegateView::OnGestureEvent(event);
+}
+
 }  // namespace ash
diff --git a/ash/shelf/contextual_nudge.h b/ash/shelf/contextual_nudge.h
index ce7fbe98..595a32a 100644
--- a/ash/shelf/contextual_nudge.h
+++ b/ash/shelf/contextual_nudge.h
@@ -6,6 +6,7 @@
 #define ASH_SHELF_CONTEXTUAL_NUDGE_H_
 
 #include "ash/ash_export.h"
+#include "base/callback.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/label.h"
 
@@ -31,12 +32,14 @@
   // |margins| - The margins added to the nudge bubble.
   // |text| - The nudge text.
   // |text_color| - The nudge text label foreground color.
+  // |tap_callback| - If set, the callback called when the user taps the nuge.
   ContextualNudge(views::View* anchor,
                   aura::Window* parent_window,
                   Position position,
                   const gfx::Insets& margins,
                   const base::string16& text,
-                  SkColor text_color);
+                  SkColor text_color,
+                  const base::RepeatingClosure& tap_callback);
   ~ContextualNudge() override;
 
   ContextualNudge(const ContextualNudge&) = delete;
@@ -51,8 +54,11 @@
   // BubbleDialogDelegateView:
   gfx::Size CalculatePreferredSize() const override;
   ui::LayerType GetLayerType() const override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
 
  private:
+  base::RepeatingClosure tap_callback_;
+
   views::Label* label_;
 };
 
diff --git a/ash/shelf/contextual_tooltip.cc b/ash/shelf/contextual_tooltip.cc
index 22f0c47..89a465d 100644
--- a/ash/shelf/contextual_tooltip.cc
+++ b/ash/shelf/contextual_tooltip.cc
@@ -90,8 +90,12 @@
   if (!features::AreContextualNudgesEnabled())
     return false;
 
-  if (GetSuccessCount(prefs, type) >= kSuccessLimit)
+  const int success_count = GetSuccessCount(prefs, type);
+  if (success_count >= kSuccessLimit ||
+      (type == TooltipType::kHomeToOverview &&
+       success_count >= kSuccessLimitHomeToOverview)) {
     return false;
+  }
 
   const int shown_count = GetShownCount(prefs, type);
   if (shown_count >= kNotificationLimit)
diff --git a/ash/shelf/contextual_tooltip.h b/ash/shelf/contextual_tooltip.h
index 4de0c6e..7066f534 100644
--- a/ash/shelf/contextual_tooltip.h
+++ b/ash/shelf/contextual_tooltip.h
@@ -25,6 +25,7 @@
 // hasn't performed the gesture |kSuccessLimit| times successfully.
 constexpr int kNotificationLimit = 3;
 constexpr int kSuccessLimit = 7;
+constexpr int kSuccessLimitHomeToOverview = 3;
 
 // Minimum time between showing contextual nudges to the user.
 constexpr base::TimeDelta kMinInterval = base::TimeDelta::FromDays(1);
diff --git a/ash/shelf/drag_handle.cc b/ash/shelf/drag_handle.cc
index 4bfa304..4ff6a9c9 100644
--- a/ash/shelf/drag_handle.cc
+++ b/ash/shelf/drag_handle.cc
@@ -177,7 +177,8 @@
       gfx::Insets(), l10n_util::GetStringUTF16(IDS_ASH_DRAG_HANDLE_NUDGE),
       AshColorProvider::Get()->GetContentLayerColor(
           AshColorProvider::ContentLayerType::kTextPrimary,
-          AshColorProvider::AshColorMode::kDark));
+          AshColorProvider::AshColorMode::kDark),
+      base::RepeatingClosure());
   drag_handle_nudge_->GetWidget()->Show();
   drag_handle_nudge_->label()->layer()->SetOpacity(0.0f);
 
diff --git a/ash/shelf/home_to_overview_nudge_controller.cc b/ash/shelf/home_to_overview_nudge_controller.cc
index d0690d9..e77d1d0 100644
--- a/ash/shelf/home_to_overview_nudge_controller.cc
+++ b/ash/shelf/home_to_overview_nudge_controller.cc
@@ -48,7 +48,7 @@
 // The baseline vertical offset from default kShown state bounds added to
 // hotseat position when the nudge is shown - this is the offset that the
 // hotseat will have once show throb animation completes.
-constexpr int kHotseatBaselineNudgeOffset = -20;
+constexpr int kHotseatBaselineNudgeOffset = -22;
 
 // The number of times the nudge should be moved up and down when the nudge is
 // shown.
@@ -167,7 +167,9 @@
       l10n_util::GetStringUTF16(IDS_ASH_HOME_TO_OVERVIEW_CONTEXTUAL_NUDGE),
       AshColorProvider::Get()->GetContentLayerColor(
           AshColorProvider::ContentLayerType::kTextPrimary,
-          AshColorProvider::AshColorMode::kDark));
+          AshColorProvider::AshColorMode::kDark),
+      base::BindRepeating(&HomeToOverviewNudgeController::HandleNudgeTap,
+                          weak_factory_.GetWeakPtr()));
 
   UpdateNudgeAnchorBounds();
 
@@ -291,6 +293,9 @@
 
   widget_observer_.RemoveAll();
   nudge_ = nullptr;
+
+  // Invalidated nudge tap handler callbacks.
+  weak_factory_.InvalidateWeakPtrs();
 }
 
 void HomeToOverviewNudgeController::UpdateNudgeAnchorBounds() {
@@ -307,4 +312,8 @@
                 gfx::Size(shelf_bounds.width(), hotseat_bounds.height())));
 }
 
+void HomeToOverviewNudgeController::HandleNudgeTap() {
+  HideNudge();
+}
+
 }  // namespace ash
diff --git a/ash/shelf/home_to_overview_nudge_controller.h b/ash/shelf/home_to_overview_nudge_controller.h
index af7367a24..a1ce4e91 100644
--- a/ash/shelf/home_to_overview_nudge_controller.h
+++ b/ash/shelf/home_to_overview_nudge_controller.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ash/ash_export.h"
+#include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "base/timer/timer.h"
 #include "ui/views/widget/widget.h"
@@ -58,6 +59,9 @@
   // Updates the nudge anchor bounds for the current hotseat and shelf bounds.
   void UpdateNudgeAnchorBounds();
 
+  // Passed to |nudge_| as its tap gesture handler.
+  void HandleNudgeTap();
+
   bool nudge_allowed_for_shelf_state_ = false;
 
   HotseatWidget* const hotseat_widget_;
@@ -69,6 +73,8 @@
   // Observes hotseat widget to detect the hotseat bounds changes, and the
   // nudge widget to detect that the widget is being destroyed.
   ScopedObserver<views::Widget, views::WidgetObserver> widget_observer_{this};
+
+  base::WeakPtrFactory<HomeToOverviewNudgeController> weak_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/shelf/home_to_overview_nudge_controller_unittest.cc b/ash/shelf/home_to_overview_nudge_controller_unittest.cc
index 43b98f4..84de223 100644
--- a/ash/shelf/home_to_overview_nudge_controller_unittest.cc
+++ b/ash/shelf/home_to_overview_nudge_controller_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "ash/shelf/home_to_overview_nudge_controller.h"
 
+#include "ash/home_screen/home_launcher_gesture_handler.h"
+#include "ash/home_screen/home_screen_controller.h"
+#include "ash/home_screen/swipe_home_to_overview_controller.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/shelf/contextual_nudge.h"
 #include "ash/shelf/contextual_tooltip.h"
@@ -489,6 +492,138 @@
   EXPECT_FALSE(GetNudgeController()->nudge_for_testing());
 }
 
+// Tests that tapping on the nudge hides the nudge.
+TEST_F(HomeToOverviewNudgeControllerTest, TapOnTheNudgeClosedTheNudge) {
+  TabletModeControllerTestApi().EnterTabletMode();
+  CreateUserSessions(1);
+  ScopedWindowList windows = CreateAndMinimizeWindows(2);
+
+  ASSERT_TRUE(GetNudgeController());
+  ASSERT_TRUE(GetNudgeController()->HasShowTimerForTesting());
+
+  GetNudgeController()->FireShowTimerForTesting();
+
+  ASSERT_TRUE(GetNudgeController()->nudge_for_testing());
+  views::Widget* nudge_widget = GetNudgeWidget();
+  WidgetCloseObserver widget_close_observer(nudge_widget);
+
+  GetEventGenerator()->GestureTapAt(
+      nudge_widget->GetWindowBoundsInScreen().CenterPoint());
+
+  EXPECT_FALSE(GetNudgeController()->nudge_for_testing());
+  EXPECT_TRUE(widget_close_observer.WidgetClosed());
+
+  EXPECT_EQ(gfx::Transform(),
+            GetHotseatWidget()->GetLayer()->GetTargetTransform());
+}
+
+// Tests that the nudge stops showing up if the user performs the gesture few
+// times.
+TEST_F(HomeToOverviewNudgeControllerTest, NoNudgeAfterSuccessfulGestures) {
+  TabletModeControllerTestApi().EnterTabletMode();
+  CreateUserSessions(1);
+  ScopedWindowList windows = CreateAndMinimizeWindows(2);
+
+  EXPECT_FALSE(GetNudgeController()->nudge_for_testing());
+
+  ASSERT_TRUE(GetNudgeController()->HasShowTimerForTesting());
+  GetNudgeController()->FireShowTimerForTesting();
+
+  // Perform home to overview gesture kSuccessLimitHomeToOverview times.
+  for (int i = 0; i < contextual_tooltip::kSuccessLimitHomeToOverview; ++i) {
+    SCOPED_TRACE(testing::Message() << "Attempt " << i);
+
+    // Perform home to overview gesture.
+    wm::ActivateWindow(windows[0].get());
+    WindowState::Get(windows[0].get())->Minimize();
+
+    // Simluate swipe up and hold gesture on the home screen (which should
+    // transition to overview).
+    const gfx::Point start = GetPrimaryShelf()
+                                 ->hotseat_widget()
+                                 ->GetWindowBoundsInScreen()
+                                 .CenterPoint();
+    GetEventGenerator()->GestureScrollSequenceWithCallback(
+        start, start + gfx::Vector2d(0, -100),
+        base::TimeDelta::FromMilliseconds(50),
+        /*num_steps = */ 12,
+        base::BindRepeating(
+            [](ui::EventType type, const gfx::Vector2dF& offset) {
+              if (type != ui::ET_GESTURE_SCROLL_UPDATE)
+                return;
+
+              // If the swipe home to overview controller started the timer to
+              // transition to overview (which happens after swipe moves far
+              // enough), run it to trigger transition to overview.
+              SwipeHomeToOverviewController* swipe_controller =
+                  Shell::Get()
+                      ->home_screen_controller()
+                      ->home_launcher_gesture_handler()
+                      ->swipe_home_to_overview_controller_for_testing();
+              ASSERT_TRUE(swipe_controller);
+
+              base::OneShotTimer* transition_timer =
+                  swipe_controller->overview_transition_timer_for_testing();
+              if (transition_timer->IsRunning())
+                transition_timer->FireNow();
+            }));
+
+    // No point oof continuing the test if transition to overview failed.
+    ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
+  }
+
+  // The nudge should not be shown next time the user transitions to home.
+  test_clock_.Advance(base::TimeDelta::FromHours(25));
+  ScopedWindowList extra_window = CreateAndMinimizeWindows(1);
+
+  EXPECT_FALSE(GetNudgeController()->nudge_for_testing());
+  EXPECT_FALSE(GetNudgeController()->HasShowTimerForTesting());
+  EXPECT_EQ(gfx::Transform(),
+            GetHotseatWidget()->GetLayer()->GetTargetTransform());
+}
+
+// Tests that swipe up and hold gesture that starts on top of contextual nudge
+// widget works - i.e. that home still transitions to overview.
+TEST_F(HomeToOverviewNudgeControllerTest, HomeToOverviewGestureFromNudge) {
+  TabletModeControllerTestApi().EnterTabletMode();
+  CreateUserSessions(1);
+  ScopedWindowList windows = CreateAndMinimizeWindows(2);
+
+  EXPECT_FALSE(GetNudgeController()->nudge_for_testing());
+
+  ASSERT_TRUE(GetNudgeController()->HasShowTimerForTesting());
+  GetNudgeController()->FireShowTimerForTesting();
+
+  // Simluate swipe up and hold gesture on home screen from the nudge widget.
+  const gfx::Point start =
+      GetNudgeWidget()->GetWindowBoundsInScreen().CenterPoint();
+  GetEventGenerator()->GestureScrollSequenceWithCallback(
+      start, start + gfx::Vector2d(0, -100),
+      base::TimeDelta::FromMilliseconds(50),
+      /*num_steps = */ 12,
+      base::BindRepeating([](ui::EventType type, const gfx::Vector2dF& offset) {
+        if (type != ui::ET_GESTURE_SCROLL_UPDATE)
+          return;
+
+        // If the swipe home to overview controller started the timer to
+        // transition to overview (which happens after swipe moves far
+        // enough), run it to trigger transition to overview.
+        SwipeHomeToOverviewController* swipe_controller =
+            Shell::Get()
+                ->home_screen_controller()
+                ->home_launcher_gesture_handler()
+                ->swipe_home_to_overview_controller_for_testing();
+        ASSERT_TRUE(swipe_controller);
+
+        base::OneShotTimer* transition_timer =
+            swipe_controller->overview_transition_timer_for_testing();
+        if (transition_timer->IsRunning())
+          transition_timer->FireNow();
+      }));
+
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
+}
+
 // Tests that nudge and hotseat get repositioned appropriatelly if the display
 // bounds change.
 TEST_F(HomeToOverviewNudgeControllerTest,
diff --git a/ash/shelf/hotseat_widget.cc b/ash/shelf/hotseat_widget.cc
index b8d7d6d..3f66cb319 100644
--- a/ash/shelf/hotseat_widget.cc
+++ b/ash/shelf/hotseat_widget.cc
@@ -84,53 +84,6 @@
 
 }  // namespace
 
-// Records smoothness of animations for background of the hotseat widget.
-class HotseatWidgetBackgroundAnimationMetricsReporter
-    : public HotseatTransitionAnimator::Observer,
-      public ui::AnimationMetricsReporter {
- public:
-  explicit HotseatWidgetBackgroundAnimationMetricsReporter(HotseatState state)
-      : target_state_(state) {}
-
-  ~HotseatWidgetBackgroundAnimationMetricsReporter() override = default;
-
-  void OnHotseatTransitionAnimationWillStart(HotseatState from_state,
-                                             HotseatState to_state) override {
-    target_state_ = to_state;
-  }
-
-  // ui::AnimationMetricsReporter:
-  void Report(int value) override {
-    switch (target_state_) {
-      case HotseatState::kShownClamshell:
-      case HotseatState::kShownHomeLauncher:
-        UMA_HISTOGRAM_PERCENTAGE(
-            "Ash.HotseatWidgetAnimation.TranslucentBackground."
-            "AnimationSmoothness.TransitionToShownHotseat",
-            value);
-        break;
-      case HotseatState::kExtended:
-        UMA_HISTOGRAM_PERCENTAGE(
-            "Ash.HotseatWidgetAnimation.TranslucentBackground."
-            "AnimationSmoothness.TransitionToExtendedHotseat",
-            value);
-        break;
-      case HotseatState::kHidden:
-        UMA_HISTOGRAM_PERCENTAGE(
-            "Ash.HotseatWidgetAnimation.TranslucentBackground."
-            "AnimationSmoothness.TransitionToHiddenHotseat",
-            value);
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-
- private:
-  // The state to which the animation is transitioning.
-  HotseatState target_state_;
-};
-
 class HotseatWidget::DelegateView : public HotseatTransitionAnimator::Observer,
                                     public views::WidgetDelegateView,
                                     public OverviewObserver,
@@ -143,8 +96,7 @@
 
   // Initializes the view.
   void Init(ScrollableShelfView* scrollable_shelf_view,
-            ui::Layer* parent_layer,
-            ui::AnimationMetricsReporter* background_metrics_reporter);
+            ui::Layer* parent_layer);
 
   // Updates the hotseat background.
   void UpdateTranslucentBackground();
@@ -189,8 +141,6 @@
   ScrollableShelfView* scrollable_shelf_view_ = nullptr;  // unowned.
   // Blur is disabled during animations to improve performance.
   bool blur_lock_ = false;
-  // Owned by the Hotseat Widget.
-  ui::AnimationMetricsReporter* background_metrics_reporter_;
 
   // The most recent color that the |translucent_background_| has been animated
   // to.
@@ -202,7 +152,8 @@
 HotseatWidget::DelegateView::~DelegateView() {
   WallpaperControllerImpl* wallpaper_controller =
       Shell::Get()->wallpaper_controller();
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  OverviewController* overview_controller =
+      Shell::Get()->overview_controller();
   if (wallpaper_controller)
     wallpaper_controller->RemoveObserver(this);
   if (overview_controller)
@@ -211,13 +162,13 @@
 
 void HotseatWidget::DelegateView::Init(
     ScrollableShelfView* scrollable_shelf_view,
-    ui::Layer* parent_layer,
-    ui::AnimationMetricsReporter* background_metrics_reporter) {
+    ui::Layer* parent_layer) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
   WallpaperControllerImpl* wallpaper_controller =
       Shell::Get()->wallpaper_controller();
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  OverviewController* overview_controller =
+      Shell::Get()->overview_controller();
   if (wallpaper_controller)
     wallpaper_controller->AddObserver(this);
   if (overview_controller)
@@ -227,7 +178,6 @@
   DCHECK(scrollable_shelf_view);
   scrollable_shelf_view_ = scrollable_shelf_view;
   UpdateTranslucentBackground();
-  background_metrics_reporter_ = background_metrics_reporter;
 }
 
 void HotseatWidget::DelegateView::UpdateTranslucentBackground() {
@@ -262,8 +212,6 @@
   animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
   animation_setter.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-  if (animate)
-    animation_setter.SetAnimationMetricsReporter(background_metrics_reporter_);
 
   if (ShelfConfig::Get()->GetDefaultShelfColor() != target_color_) {
     target_color_ = ShelfConfig::Get()->GetDefaultShelfColor();
@@ -368,11 +316,7 @@
   scrollable_shelf_view_ = GetContentsView()->AddChildView(
       std::make_unique<ScrollableShelfView>(ShelfModel::Get(), shelf));
   scrollable_shelf_view_->Init();
-  traslucent_background_metrics_reporter_ =
-      std::make_unique<HotseatWidgetBackgroundAnimationMetricsReporter>(
-          state());
-  delegate_view_->Init(scrollable_shelf_view(), GetLayer(),
-                       traslucent_background_metrics_reporter_.get());
+  delegate_view_->Init(scrollable_shelf_view(), GetLayer());
 }
 
 void HotseatWidget::OnHotseatTransitionAnimatorCreated(
diff --git a/ash/shelf/hotseat_widget.h b/ash/shelf/hotseat_widget.h
index 940cf26..2acc36d67 100644
--- a/ash/shelf/hotseat_widget.h
+++ b/ash/shelf/hotseat_widget.h
@@ -23,7 +23,6 @@
 class Shelf;
 class ShelfView;
 class HotseatTransitionAnimator;
-class HotseatWidgetBackgroundAnimationMetricsReporter;
 
 // The hotseat widget is part of the shelf and hosts app shortcuts.
 class ASH_EXPORT HotseatWidget : public ShelfComponent,
@@ -159,11 +158,6 @@
   // during an animation.
   std::unique_ptr<aura::ScopedWindowTargeter> hotseat_window_targeter_;
 
-  // Metrics reporter for animations of the traslucent background in the
-  // hotseat.
-  std::unique_ptr<HotseatWidgetBackgroundAnimationMetricsReporter>
-      traslucent_background_metrics_reporter_;
-
   DISALLOW_COPY_AND_ASSIGN(HotseatWidget);
 };
 
diff --git a/ash/shelf/login_shelf_gesture_controller.cc b/ash/shelf/login_shelf_gesture_controller.cc
index 2b75692..3fa8bd7 100644
--- a/ash/shelf/login_shelf_gesture_controller.cc
+++ b/ash/shelf/login_shelf_gesture_controller.cc
@@ -43,7 +43,8 @@
       is_oobe ? gfx::kGoogleGrey700 : gfx::kGoogleGrey100;
   nudge_ = new ContextualNudge(drag_handle, nullptr /*parent_window*/,
                                ContextualNudge::Position::kTop, gfx::Insets(4),
-                               gesture_nudge, nudge_text_color);
+                               gesture_nudge, nudge_text_color,
+                               base::RepeatingClosure());
   nudge_->GetWidget()->Show();
   nudge_->GetWidget()->AddObserver(this);
 }
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index 45eeb8d0..a88e0d7 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -79,19 +79,19 @@
       case HotseatState::kShownClamshell:
       case HotseatState::kShownHomeLauncher:
         UMA_HISTOGRAM_PERCENTAGE(
-            "Ash.HotseatWidgetAnimation.Widget.AnimationSmoothness."
+            "Ash.HotseatWidgetAnimation.AnimationSmoothness."
             "TransitionToShownHotseat",
             value);
         break;
       case HotseatState::kExtended:
         UMA_HISTOGRAM_PERCENTAGE(
-            "Ash.HotseatWidgetAnimation.Widget.AnimationSmoothness."
+            "Ash.HotseatWidgetAnimation.AnimationSmoothness."
             "TransitionToExtendedHotseat",
             value);
         break;
       case HotseatState::kHidden:
         UMA_HISTOGRAM_PERCENTAGE(
-            "Ash.HotseatWidgetAnimation.Widget.AnimationSmoothness."
+            "Ash.HotseatWidgetAnimation.AnimationSmoothness."
             "TransitionToHiddenHotseat",
             value);
         break;
diff --git a/ash/system/palette/palette_tray_unittest.cc b/ash/system/palette/palette_tray_unittest.cc
index 20c33eb1..30ddfa5 100644
--- a/ash/system/palette/palette_tray_unittest.cc
+++ b/ash/system/palette/palette_tray_unittest.cc
@@ -311,6 +311,9 @@
     EXPECT_EQ(expected, highlighter_showing());
     EXPECT_EQ(expected, metalayer_enabled());
     generator->ReleaseTouch();
+    // If the tool is not enabled, the gesture may open a context menu instead.
+    // Press escape to close the menu.
+    generator->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
   }
 
   void WaitDragAndAssertMetalayer(const std::string& context,
diff --git a/ash/system/palette/tools/metalayer_mode.cc b/ash/system/palette/tools/metalayer_mode.cc
index b02fdaa..22b8d6c 100644
--- a/ash/system/palette/tools/metalayer_mode.cc
+++ b/ash/system/palette/tools/metalayer_mode.cc
@@ -150,6 +150,19 @@
   event->StopPropagation();
 }
 
+void MetalayerMode::OnGestureEvent(ui::GestureEvent* event) {
+  if (!feature_enabled())
+    return;
+
+  // When the stylus button is pressed, a ET_GESTURE_LONG_PRESS event with
+  // EF_LEFT_MOUSE_BUTTON will be generated by the GestureDetector. If the
+  // metalayer feature is enabled, these should be consumed.
+  if (event->type() == ui::ET_GESTURE_LONG_PRESS &&
+      (event->flags() & ui::EF_LEFT_MOUSE_BUTTON)) {
+    event->StopPropagation();
+  }
+}
+
 void MetalayerMode::OnAssistantStatusChanged(mojom::AssistantState state) {
   assistant_state_ = state;
   UpdateState();
diff --git a/ash/system/palette/tools/metalayer_mode.h b/ash/system/palette/tools/metalayer_mode.h
index fe24116..b802fea 100644
--- a/ash/system/palette/tools/metalayer_mode.h
+++ b/ash/system/palette/tools/metalayer_mode.h
@@ -62,6 +62,7 @@
 
   // ui::EventHandler:
   void OnTouchEvent(ui::TouchEvent* event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
 
   // AssistantStateObserver:
   void OnAssistantStatusChanged(mojom::AssistantState state) override;
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index f73b32c..c642731 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -5957,7 +5957,7 @@
   EXPECT_FALSE(overview_controller()->InOverviewSession());
   EXPECT_FALSE(split_view_controller()->InSplitViewMode());
 
-  // 8. Test if splitview is active, open the app list will end overview if
+  // 8. Test if splitview is not active, open the app list will end overview if
   // overview is active.
   ToggleOverview();
   // Open app list.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index e0833b5..3b5f788 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -28,6 +28,7 @@
 import("//build/config/jumbo.gni")
 import("//build/config/logging.gni")
 import("//build/config/nacl/config.gni")
+import("//build/config/profiling/profiling.gni")
 import("//build/config/sysroot.gni")
 import("//build/config/ui.gni")
 import("//build/nocompile.gni")
@@ -1303,7 +1304,7 @@
     ":base_static",
     ":build_date",
     ":cfi_buildflags",
-    ":clang_coverage_buildflags",
+    ":clang_profiling_buildflags",
     ":debugging_buildflags",
     ":logging_buildflags",
     ":orderfile_buildflags",
@@ -1359,11 +1360,11 @@
     }
   }
 
-  if (use_clang_coverage) {
-    # Call-sites use this conditional on the CLANG_COVERAGE macro, for clarity.
+  if (use_clang_profiling) {
+    # Call-sites use this conditional on the CLANG_PROFILING macro, for clarity.
     sources += [
-      "test/clang_coverage.cc",
-      "test/clang_coverage.h",
+      "test/clang_profiling.cc",
+      "test/clang_profiling.h",
     ]
   }
 
@@ -2166,13 +2167,13 @@
   flags = [ "USE_PARTITION_ALLOC=$use_partition_alloc" ]
 }
 
-buildflag_header("clang_coverage_buildflags") {
-  header = "clang_coverage_buildflags.h"
+buildflag_header("clang_profiling_buildflags") {
+  header = "clang_profiling_buildflags.h"
   header_dir = "base"
 
   flags = [
-    "CLANG_COVERAGE=$use_clang_coverage",
-    "CLANG_COVERAGE_INSIDE_SANDBOX=$use_clang_coverage_inside_sandbox",
+    "CLANG_PROFILING=$use_clang_profiling",
+    "CLANG_PROFILING_INSIDE_SANDBOX=$use_clang_profiling_inside_sandbox",
   ]
 }
 
diff --git a/base/base_paths_win.cc b/base/base_paths_win.cc
index 341b6cfa..eac4b5f 100644
--- a/base/base_paths_win.cc
+++ b/base/base_paths_win.cc
@@ -9,9 +9,12 @@
 #include "base/base_paths.h"
 #include "base/environment.h"
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/win/current_module.h"
 #include "base/win/scoped_co_mem.h"
 #include "base/win/windows_version.h"
@@ -172,12 +175,19 @@
                 .Append(FILE_PATH_LITERAL("Internet Explorer"))
                 .Append(FILE_PATH_LITERAL("Quick Launch"));
       break;
-    case base::DIR_TASKBAR_PINS:
+    case base::DIR_TASKBAR_PINS: {
       if (!PathService::Get(base::DIR_USER_QUICK_LAUNCH, &cur))
         return false;
       cur = cur.Append(FILE_PATH_LITERAL("User Pinned"))
                 .Append(FILE_PATH_LITERAL("TaskBar"));
+      // Allow a blocking call here to check for existence of the directory. In
+      // practice, all uses of SHGetFolderPath in this function make a similar
+      // check, so this does not add new I/O that wasn't already happening.
+      ScopedAllowBlocking allow_blocking(FROM_HERE);
+      if (!DirectoryExists(cur))
+        return false;
       break;
+    }
     case base::DIR_IMPLICIT_APP_SHORTCUTS:
       if (!PathService::Get(base::DIR_USER_QUICK_LAUNCH, &cur))
         return false;
diff --git a/base/debug/debugger_posix.cc b/base/debug/debugger_posix.cc
index 8a7858d9..1982173 100644
--- a/base/debug/debugger_posix.cc
+++ b/base/debug/debugger_posix.cc
@@ -17,7 +17,7 @@
 #include <memory>
 #include <vector>
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/stl_util.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
@@ -56,8 +56,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 
-#if BUILDFLAG(CLANG_COVERAGE)
-#include "base/test/clang_coverage.h"
+#if BUILDFLAG(CLANG_PROFILING)
+#include "base/test/clang_profiling.h"
 #endif
 
 #if defined(USE_SYMBOLIZE)
@@ -324,8 +324,8 @@
 #endif
 
 void BreakDebugger() {
-#if BUILDFLAG(CLANG_COVERAGE)
-  WriteClangCoverageProfile();
+#if BUILDFLAG(CLANG_PROFILING)
+  WriteClangProfilingProfile();
 #endif
 
   // NOTE: This code MUST be async-signal safe (it's used by in-process
diff --git a/base/debug/debugger_win.cc b/base/debug/debugger_win.cc
index fd10b4b..2e3875b 100644
--- a/base/debug/debugger_win.cc
+++ b/base/debug/debugger_win.cc
@@ -7,10 +7,10 @@
 #include <stdlib.h>
 #include <windows.h>
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 
-#if BUILDFLAG(CLANG_COVERAGE)
-#include "base/test/clang_coverage.h"
+#if BUILDFLAG(CLANG_PROFILING)
+#include "base/test/clang_profiling.h"
 #endif
 
 namespace base {
@@ -21,8 +21,8 @@
 }
 
 void BreakDebugger() {
-#if BUILDFLAG(CLANG_COVERAGE)
-  WriteClangCoverageProfile();
+#if BUILDFLAG(CLANG_PROFILING)
+  WriteClangProfilingProfile();
 #endif
 
   if (IsDebugUISuppressed())
diff --git a/base/process/process_fuchsia.cc b/base/process/process_fuchsia.cc
index f260575..516a067 100644
--- a/base/process/process_fuchsia.cc
+++ b/base/process/process_fuchsia.cc
@@ -8,14 +8,14 @@
 #include <zircon/process.h>
 #include <zircon/syscalls.h>
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/debug/activity_tracker.h"
 #include "base/fuchsia/default_job.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/strings/stringprintf.h"
 
-#if BUILDFLAG(CLANG_COVERAGE)
-#include "base/test/clang_coverage.h"
+#if BUILDFLAG(CLANG_PROFILING)
+#include "base/test/clang_profiling.h"
 #endif
 
 namespace base {
@@ -93,8 +93,8 @@
 
 // static
 void Process::TerminateCurrentProcessImmediately(int exit_code) {
-#if BUILDFLAG(CLANG_COVERAGE)
-  WriteClangCoverageProfile();
+#if BUILDFLAG(CLANG_PROFILING)
+  WriteClangProfilingProfile();
 #endif
   _exit(exit_code);
 }
diff --git a/base/process/process_posix.cc b/base/process/process_posix.cc
index 9636d44..17dae528 100644
--- a/base/process/process_posix.cc
+++ b/base/process/process_posix.cc
@@ -10,7 +10,7 @@
 #include <sys/resource.h>
 #include <sys/wait.h>
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/debug/activity_tracker.h"
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
@@ -23,8 +23,8 @@
 #include <sys/event.h>
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE)
-#include "base/test/clang_coverage.h"
+#if BUILDFLAG(CLANG_PROFILING)
+#include "base/test/clang_profiling.h"
 #endif
 
 namespace {
@@ -276,8 +276,8 @@
 
 // static
 void Process::TerminateCurrentProcessImmediately(int exit_code) {
-#if BUILDFLAG(CLANG_COVERAGE)
-  WriteClangCoverageProfile();
+#if BUILDFLAG(CLANG_PROFILING)
+  WriteClangProfilingProfile();
 #endif
   _exit(exit_code);
 }
diff --git a/base/process/process_win.cc b/base/process/process_win.cc
index 4f2ccf96..85688b7 100644
--- a/base/process/process_win.cc
+++ b/base/process/process_win.cc
@@ -4,7 +4,7 @@
 
 #include "base/process/process.h"
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/debug/activity_tracker.h"
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
@@ -13,8 +13,8 @@
 
 #include <windows.h>
 
-#if BUILDFLAG(CLANG_COVERAGE)
-#include "base/test/clang_coverage.h"
+#if BUILDFLAG(CLANG_PROFILING)
+#include "base/test/clang_profiling.h"
 #endif
 
 namespace {
@@ -90,8 +90,8 @@
 
 // static
 void Process::TerminateCurrentProcessImmediately(int exit_code) {
-#if BUILDFLAG(CLANG_COVERAGE)
-  WriteClangCoverageProfile();
+#if BUILDFLAG(CLANG_PROFILING)
+  WriteClangProfilingProfile();
 #endif
   ::TerminateProcess(GetCurrentProcess(), exit_code);
   // There is some ambiguity over whether the call above can return. Rather than
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 712c70899..7a88a66a 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -26,7 +26,7 @@
   ]
   deps = [
     "//base",
-    "//base:clang_coverage_buildflags",
+    "//base:clang_profiling_buildflags",
   ]
 }
 
diff --git a/base/test/clang_coverage.cc b/base/test/clang_coverage.cc
deleted file mode 100644
index 33f823e..0000000
--- a/base/test/clang_coverage.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/clang_coverage.h"
-
-#include "base/no_destructor.h"
-#include "base/synchronization/lock.h"
-
-extern "C" int __llvm_profile_dump(void);
-
-namespace base {
-
-void WriteClangCoverageProfile() {
-  // __llvm_profile_dump() guarantees that it will not dump coverage information
-  // if it is being called twice or more. However, it is not thread safe, as it
-  // is supposed to be called from atexit() handler rather than being called
-  // directly from random places. Since we have to call it ourselves, we must
-  // ensure thread safety in order to prevent duplication of coverage counters.
-  static base::NoDestructor<base::Lock> lock;
-  base::AutoLock auto_lock(*lock);
-  __llvm_profile_dump();
-}
-
-}  // namespace base
diff --git a/base/test/clang_coverage.h b/base/test/clang_coverage.h
deleted file mode 100644
index ed2e3d7..0000000
--- a/base/test/clang_coverage.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TEST_CLANG_COVERAGE_H_
-#define BASE_TEST_CLANG_COVERAGE_H_
-
-#include "base/clang_coverage_buildflags.h"
-
-#if !BUILDFLAG(CLANG_COVERAGE)
-#error "Clang coverage can only be used if CLANG_COVERAGE macro is defined"
-#endif
-
-namespace base {
-
-// Write out the accumulated code coverage profile to the configured file.
-// This is used internally by e.g. base::Process and FATAL logging, to cause
-// coverage information to be stored even when performing an "immediate" exit
-// (or triggering a debug crash), where the automatic at-exit writer will not
-// be invoked.
-// This call is thread-safe, and will write profiling data at-most-once.
-void WriteClangCoverageProfile();
-
-}  // namespace base
-
-#endif  // BASE_TEST_CLANG_COVERAGE_H_
diff --git a/base/test/clang_profiling.cc b/base/test/clang_profiling.cc
new file mode 100644
index 0000000..5681a10
--- /dev/null
+++ b/base/test/clang_profiling.cc
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/clang_profiling.h"
+
+#include "base/no_destructor.h"
+#include "base/synchronization/lock.h"
+
+extern "C" int __llvm_profile_dump(void);
+
+namespace base {
+
+void WriteClangProfilingProfile() {
+  // __llvm_profile_dump() guarantees that it will not dump profiling
+  // information if it is being called twice or more. However, it is not thread
+  // safe, as it is supposed to be called from atexit() handler rather than
+  // being called directly from random places. Since we have to call it
+  // ourselves, we must ensure thread safety in order to prevent duplication of
+  // profiling counters.
+  static base::NoDestructor<base::Lock> lock;
+  base::AutoLock auto_lock(*lock);
+  __llvm_profile_dump();
+}
+
+}  // namespace base
diff --git a/base/test/clang_profiling.h b/base/test/clang_profiling.h
new file mode 100644
index 0000000..1ecc02d
--- /dev/null
+++ b/base/test/clang_profiling.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_CLANG_PROFILING_H_
+#define BASE_TEST_CLANG_PROFILING_H_
+
+#include "base/clang_profiling_buildflags.h"
+
+#if !BUILDFLAG(CLANG_PROFILING)
+#error "Clang profiling can only be used if CLANG_PROFILING macro is defined"
+#endif
+
+namespace base {
+
+// Write out the accumulated code profiling profile to the configured file.
+// This is used internally by e.g. base::Process and FATAL logging, to cause
+// profiling information to be stored even when performing an "immediate" exit
+// (or triggering a debug crash), where the automatic at-exit writer will not
+// be invoked.
+// This call is thread-safe, and will write profiling data at-most-once.
+void WriteClangProfilingProfile();
+
+}  // namespace base
+
+#endif  // BASE_TEST_CLANG_PROFILING_H_
diff --git a/base/test/test_timeouts.cc b/base/test/test_timeouts.cc
index ef612bb..e77f175 100644
--- a/base/test/test_timeouts.cc
+++ b/base/test/test_timeouts.cc
@@ -7,7 +7,7 @@
 #include <algorithm>
 #include <string>
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/command_line.h"
 #include "base/debug/debugger.h"
 #include "base/logging.h"
@@ -57,7 +57,7 @@
   constexpr int kTimeoutMultiplier = 3;
 #elif defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
   constexpr int kTimeoutMultiplier = 2;
-#elif BUILDFLAG(CLANG_COVERAGE)
+#elif BUILDFLAG(CLANG_PROFILING)
   // On coverage build, tests run 3x slower.
   constexpr int kTimeoutMultiplier = 3;
 #elif !defined(NDEBUG) && defined(OS_CHROMEOS)
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 6833031..25e1c98 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -303,12 +303,15 @@
 
 class AdjustOOMScoreHelper;
 class FileDescriptorWatcher;
+class FilePath;
 class GetAppOutputScopedAllowBaseSyncPrimitives;
 class ScopedAllowThreadRecallForStackSamplingProfiler;
 class SimpleThread;
 class StackSamplingProfiler;
 class Thread;
 
+bool PathProviderWin(int, FilePath*);
+
 #if DCHECK_IS_ON()
 #define INLINE_IF_DCHECK_IS_OFF BASE_EXPORT
 #define EMPTY_BODY_IF_DCHECK_IS_OFF
@@ -376,6 +379,8 @@
   friend class content::RenderProcessHostImpl;
   friend class weblayer::WebLayerPathProvider;
 
+  friend bool PathProviderWin(int, FilePath*);
+
   ScopedAllowBlocking(const Location& from_here = Location::Current());
   ~ScopedAllowBlocking();
 
diff --git a/build/android/pylib/base/test_instance_factory.py b/build/android/pylib/base/test_instance_factory.py
index 60fea1ba..cb34956d 100644
--- a/build/android/pylib/base/test_instance_factory.py
+++ b/build/android/pylib/base/test_instance_factory.py
@@ -5,7 +5,6 @@
 from pylib.gtest import gtest_test_instance
 from pylib.instrumentation import instrumentation_test_instance
 from pylib.junit import junit_test_instance
-from pylib.linker import linker_test_instance
 from pylib.monkey import monkey_test_instance
 from pylib.utils import device_dependencies
 
@@ -20,8 +19,6 @@
         args, device_dependencies.GetDataDependencies, error_func)
   elif args.command == 'junit':
     return junit_test_instance.JunitTestInstance(args, error_func)
-  elif args.command == 'linker':
-    return linker_test_instance.LinkerTestInstance(args)
   elif args.command == 'monkey':
     return monkey_test_instance.MonkeyTestInstance(args, error_func)
 
diff --git a/build/android/pylib/base/test_run_factory.py b/build/android/pylib/base/test_run_factory.py
index dc74644..df6eeb8f 100644
--- a/build/android/pylib/base/test_run_factory.py
+++ b/build/android/pylib/base/test_run_factory.py
@@ -5,12 +5,10 @@
 from pylib.gtest import gtest_test_instance
 from pylib.instrumentation import instrumentation_test_instance
 from pylib.junit import junit_test_instance
-from pylib.linker import linker_test_instance
 from pylib.monkey import monkey_test_instance
 from pylib.local.device import local_device_environment
 from pylib.local.device import local_device_gtest_run
 from pylib.local.device import local_device_instrumentation_test_run
-from pylib.local.device import local_device_linker_test_run
 from pylib.local.device import local_device_monkey_test_run
 from pylib.local.machine import local_machine_environment
 from pylib.local.machine import local_machine_junit_test_run
@@ -24,9 +22,6 @@
                   instrumentation_test_instance.InstrumentationTestInstance):
       return (local_device_instrumentation_test_run
               .LocalDeviceInstrumentationTestRun(env, test_instance))
-    if isinstance(test_instance, linker_test_instance.LinkerTestInstance):
-      return (local_device_linker_test_run
-              .LocalDeviceLinkerTestRun(env, test_instance))
     if isinstance(test_instance, monkey_test_instance.MonkeyTestInstance):
       return (local_device_monkey_test_run
               .LocalDeviceMonkeyTestRun(env, test_instance))
diff --git a/build/android/pylib/linker/__init__.py b/build/android/pylib/linker/__init__.py
deleted file mode 100644
index 9228df89..0000000
--- a/build/android/pylib/linker/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
diff --git a/build/android/pylib/linker/linker_test_instance.py b/build/android/pylib/linker/linker_test_instance.py
deleted file mode 100644
index 53978cb..0000000
--- a/build/android/pylib/linker/linker_test_instance.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from pylib.base import test_instance
-from pylib.constants import host_paths
-from pylib.linker import test_case
-from pylib.utils import test_filter
-
-with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
-  import unittest_util
-
-
-class LinkerTestInstance(test_instance.TestInstance):
-
-  def __init__(self, args):
-    super(LinkerTestInstance, self).__init__()
-    self._test_apk = args.test_apk
-    self._test_filter = test_filter.InitializeFilterFromArgs(args)
-
-  @property
-  def test_apk(self):
-    return self._test_apk
-
-  @property
-  def test_filter(self):
-    return self._test_filter
-
-  def GetTests(self):
-    tests = [test_case.LinkerSharedRelroTest()]
-
-    if self._test_filter:
-      filtered_names = unittest_util.FilterTestNames(
-          (t.qualified_name for t in tests), self._test_filter)
-      tests = [
-          t for t in tests
-          if t.qualified_name in filtered_names]
-
-    return tests
-
-  def SetUp(self):
-    pass
-
-  def TearDown(self):
-    pass
-
-  def TestType(self):
-    return 'linker'
diff --git a/build/android/pylib/linker/test_case.py b/build/android/pylib/linker/test_case.py
deleted file mode 100644
index 47933c2..0000000
--- a/build/android/pylib/linker/test_case.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Base class for linker-specific test cases.
-
-   The custom dynamic linker can only be tested through a custom test case
-   for various technical reasons:
-
-     - It's an 'invisible feature', i.e. it doesn't expose a new API or
-       behaviour, all it does is save RAM when loading native libraries.
-
-     - Checking that it works correctly requires several things that do not
-       fit the existing GTest-based and instrumentation-based tests:
-
-         - Native test code needs to be run in both the browser and renderer
-           process at the same time just after loading native libraries, in
-           a completely asynchronous way.
-
-         - Each test case requires restarting a whole new application process
-           with a different command-line.
-
-         - Enabling test support in the Linker code requires building a special
-           APK with a flag to activate special test-only support code in the
-           Linker code itself.
-
-       Host-driven tests have also been tried, but since they're really
-       sub-classes of instrumentation tests, they didn't work well either.
-
-   To build and run, refer to android_linker_testing.md.
-"""
-# pylint: disable=R0201
-
-from __future__ import print_function
-
-import logging
-import re
-
-from devil.android import device_errors
-from devil.android.sdk import intent
-from pylib.base import base_test_result
-
-
-ResultType = base_test_result.ResultType
-
-_PACKAGE_NAME = 'org.chromium.chromium_linker_test_apk'
-_ACTIVITY_NAME = '.ChromiumLinkerTestActivity'
-
-# Logcat filters used during each test. Only the 'chromium' one is really
-# needed, but the logs are added to the TestResult in case of error, and
-# it is handy to have others as well when troubleshooting.
-_LOGCAT_FILTERS = ['*:s', 'chromium:v', 'cr_chromium:v',
-                   'cr_ChromiumAndroidLinker:v', 'cr_LibraryLoader:v',
-                   'cr_LinkerTest:v']
-#_LOGCAT_FILTERS = ['*:v']  ## DEBUG
-
-# Regular expression used to match status lines in logcat.
-_RE_BROWSER_STATUS_LINE = re.compile(r' BROWSER_LINKER_TEST: (FAIL|SUCCESS)$')
-_RE_RENDERER_STATUS_LINE = re.compile(r' RENDERER_LINKER_TEST: (FAIL|SUCCESS)$')
-
-def _StartActivityAndWaitForLinkerTestStatus(device, timeout):
-  """Force-start an activity and wait up to |timeout| seconds until the full
-     linker test status lines appear in the logcat, recorded through |device|.
-  Args:
-    device: A DeviceUtils instance.
-    timeout: Timeout in seconds
-  Returns:
-    A (status, logs) tuple, where status is a ResultType constant, and logs
-    if the final logcat output as a string.
-  """
-
-  # 1. Start recording logcat with appropriate filters.
-  with device.GetLogcatMonitor(filter_specs=_LOGCAT_FILTERS) as logmon:
-
-    # 2. Force-start activity.
-    device.StartActivity(
-        intent.Intent(package=_PACKAGE_NAME, activity=_ACTIVITY_NAME),
-        force_stop=True)
-
-    # 3. Wait up to |timeout| seconds until the test status is in the logcat.
-    result = ResultType.PASS
-    try:
-      browser_match = logmon.WaitFor(_RE_BROWSER_STATUS_LINE, timeout=timeout)
-      logging.debug('Found browser match: %s', browser_match.group(0))
-      renderer_match = logmon.WaitFor(_RE_RENDERER_STATUS_LINE,
-                                      timeout=timeout)
-      logging.debug('Found renderer match: %s', renderer_match.group(0))
-      if (browser_match.group(1) != 'SUCCESS'
-          or renderer_match.group(1) != 'SUCCESS'):
-        result = ResultType.FAIL
-    except device_errors.CommandTimeoutError:
-      result = ResultType.TIMEOUT
-
-    logcat = device.adb.Logcat(dump=True)
-
-  logmon.Close()
-  return result, '\n'.join(logcat)
-
-
-class LibraryLoadMap(dict):
-  """A helper class to pretty-print a map of library names to load addresses."""
-  def __str__(self):
-    items = ['\'%s\': 0x%x' % (name, address) for \
-        (name, address) in self.iteritems()]
-    return '{%s}' % (', '.join(items))
-
-  def __repr__(self):
-    return 'LibraryLoadMap(%s)' % self.__str__()
-
-
-class AddressList(list):
-  """A helper class to pretty-print a list of load addresses."""
-  def __str__(self):
-    items = ['0x%x' % address for address in self]
-    return '[%s]' % (', '.join(items))
-
-  def __repr__(self):
-    return 'AddressList(%s)' % self.__str__()
-
-
-class LinkerTestCaseBase(object):
-  """Base class for linker test cases."""
-
-  def __init__(self):
-    """Creates a test case."""
-    test_suffix = 'ForLegacyLinker'
-    class_name = self.__class__.__name__
-    self.qualified_name = '%s.%s' % (class_name, test_suffix)
-    self.tagged_name = self.qualified_name
-
-  def _RunTest(self, _device):
-    """Runs the test, must be overridden.
-    Args:
-      _device: A DeviceUtils interface.
-    Returns:
-      A (status, log) tuple, where <status> is a ResultType constant, and <log>
-      is the logcat output captured during the test in case of error, or None
-      in case of success.
-    """
-    return ResultType.FAIL, 'Unimplemented _RunTest() method!'
-
-  def Run(self, device):
-    """Runs the test on a given device.
-    Args:
-      device: Name of target device where to run the test.
-    Returns:
-      A base_test_result.TestRunResult() instance.
-    """
-    margin = 8
-    print('[ %-*s ] %s' % (margin, 'RUN', self.tagged_name))
-    logging.info('Running linker test: %s', self.tagged_name)
-
-    # Run the test.
-    status, logs = self._RunTest(device)
-
-    result_text = 'OK'
-    if status == ResultType.FAIL:
-      result_text = 'FAILED'
-    elif status == ResultType.TIMEOUT:
-      result_text = 'TIMEOUT'
-    print('[ %*s ] %s' % (margin, result_text, self.tagged_name))
-
-    return base_test_result.BaseTestResult(self.tagged_name, status, log=logs)
-
-
-  def __str__(self):
-    return self.tagged_name
-
-  def __repr__(self):
-    return self.tagged_name
-
-
-class LinkerSharedRelroTest(LinkerTestCaseBase):
-  """A linker test case to check the status of shared RELRO sections.
-
-    The core of the checks performed here are pretty simple:
-
-      - Clear the logcat and start recording with an appropriate set of filters.
-      - Create the command-line appropriate for the test-case.
-      - Start the activity (always forcing a cold start).
-      - Every second, look at the current content of the filtered logcat lines
-        and look for instances of the following:
-
-            BROWSER_LINKER_TEST: <status>
-            RENDERER_LINKER_TEST: <status>
-
-        where <status> can be either FAIL or SUCCESS. These lines can appear
-        in any order in the logcat. Once both browser and renderer status are
-        found, stop the loop. Otherwise timeout after 30 seconds.
-
-        Note that there can be other lines beginning with BROWSER_LINKER_TEST:
-        and RENDERER_LINKER_TEST:, but are not followed by a <status> code.
-
-      - The test case passes if the <status> for both the browser and renderer
-        process are SUCCESS. Otherwise its a fail.
-  """
-  def _RunTest(self, device):
-    # Wait up to 30 seconds until the linker test status is in the logcat.
-    return _StartActivityAndWaitForLinkerTestStatus(device, timeout=30)
diff --git a/build/android/pylib/local/device/local_device_linker_test_run.py b/build/android/pylib/local/device/local_device_linker_test_run.py
deleted file mode 100644
index 2a1520e..0000000
--- a/build/android/pylib/local/device/local_device_linker_test_run.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import logging
-import sys
-import traceback
-
-from pylib.base import base_test_result
-from pylib.linker import test_case
-from pylib.local.device import local_device_environment
-from pylib.local.device import local_device_test_run
-
-
-class LinkerExceptionTestResult(base_test_result.BaseTestResult):
-  """Test result corresponding to a python exception in a host-custom test."""
-
-  def __init__(self, test_name, exc_info):
-    """Constructs a LinkerExceptionTestResult object.
-
-    Args:
-      test_name: name of the test which raised an exception.
-      exc_info: exception info, ostensibly from sys.exc_info().
-    """
-    exc_type, exc_value, exc_traceback = exc_info
-    trace_info = ''.join(traceback.format_exception(exc_type, exc_value,
-                                                    exc_traceback))
-    log_msg = 'Exception:\n' + trace_info
-
-    super(LinkerExceptionTestResult, self).__init__(
-        test_name,
-        base_test_result.ResultType.FAIL,
-        log="%s %s" % (exc_type, log_msg))
-
-
-class LocalDeviceLinkerTestRun(local_device_test_run.LocalDeviceTestRun):
-
-  def _CreateShards(self, tests):
-    return tests
-
-  def _GetTests(self):
-    return self._test_instance.GetTests()
-
-  def _GetUniqueTestName(self, test):
-    return test.qualified_name
-
-  def _RunTest(self, device, test):
-    assert isinstance(test, test_case.LinkerTestCaseBase)
-
-    try:
-      result = test.Run(device)
-    except Exception: # pylint: disable=broad-except
-      logging.exception('Caught exception while trying to run test: ' +
-                        test.tagged_name)
-      exc_info = sys.exc_info()
-      result = LinkerExceptionTestResult(test.tagged_name, exc_info)
-
-    return result, None
-
-  def SetUp(self):
-    @local_device_environment.handle_shard_failures_with(
-        on_failure=self._env.BlacklistDevice)
-    def individual_device_set_up(dev):
-      dev.Install(self._test_instance.test_apk)
-
-    self._env.parallel_devices.pMap(individual_device_set_up)
-
-  def _ShouldShard(self):
-    return True
-
-  def TearDown(self):
-    pass
-
-  def TestPackage(self):
-    pass
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index 29802b6..94a99a9 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -160,15 +160,11 @@
 pylib/instrumentation/test_result.py
 pylib/junit/__init__.py
 pylib/junit/junit_test_instance.py
-pylib/linker/__init__.py
-pylib/linker/linker_test_instance.py
-pylib/linker/test_case.py
 pylib/local/__init__.py
 pylib/local/device/__init__.py
 pylib/local/device/local_device_environment.py
 pylib/local/device/local_device_gtest_run.py
 pylib/local/device/local_device_instrumentation_test_run.py
-pylib/local/device/local_device_linker_test_run.py
 pylib/local/device/local_device_monkey_test_run.py
 pylib/local/device/local_device_test_run.py
 pylib/local/emulator/__init__.py
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 42667ab..9573394 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -499,12 +499,6 @@
       "-Xclang",
       "-instcombine-lower-dbg-declare=0",
     ]
-
-    if (!is_chromeos && default_toolchain != "//build/toolchain/cros:target") {
-      # TODO(https://crbug.com/1049161): Remove '-DCLANG_SPAWN_CC1=ON' from build.py instead
-      # once this change has marinated a bit.
-      cflags += [ "-fintegrated-cc1" ]
-    }
   }
 
   # C11/C++11 compiler flags setup.
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni
index 0ef29cf..f62f305 100644
--- a/build/config/ios/rules.gni
+++ b/build/config/ios/rules.gni
@@ -1204,10 +1204,6 @@
         get_label_info("$_target_name($default_toolchain)", "target_gen_dir")
 
     _framework_headers_target = _target_name + "_framework_headers"
-    _framework_headers_config = _target_name + "_framework_headers_config"
-    config(_framework_headers_config) {
-      framework_dirs = [ _default_toolchain_root_out_dir ]
-    }
 
     _headers_map_config = _target_name + "_headers_map"
     _header_map_filename =
@@ -1218,6 +1214,11 @@
     }
   }
 
+  _framework_headers_config = _target_name + "_framework_headers_config"
+  config(_framework_headers_config) {
+    framework_dirs = [ _default_toolchain_root_out_dir ]
+  }
+
   _arch_shared_library_source = _target_name + "_arch_shared_library_sources"
   _arch_shared_library_target = _target_name + "_arch_shared_library"
   _lipo_shared_library_target = _target_name + "_shared_library"
diff --git a/build/config/profiling/OWNERS b/build/config/profiling/OWNERS
new file mode 100644
index 0000000..225ce18
--- /dev/null
+++ b/build/config/profiling/OWNERS
@@ -0,0 +1,3 @@
+liaoyuke@chromium.org
+sajjadm@chromium.org
+sebmarchand@chromium.org
diff --git a/build/config/profiling/profiling.gni b/build/config/profiling/profiling.gni
new file mode 100644
index 0000000..7dca6eac
--- /dev/null
+++ b/build/config/profiling/profiling.gni
@@ -0,0 +1,12 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/coverage/coverage.gni")
+
+declare_args() {
+  use_clang_profiling = use_clang_coverage
+}
+
+assert(!use_clang_profiling || is_clang,
+       "Clang Source-based profiling requires clang.")
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index 262f868..f0cfd8a 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -5,7 +5,7 @@
 import("//build/config/chrome_build.gni")
 import("//build/config/chromecast_build.gni")
 import("//build/config/chromeos/args.gni")
-import("//build/config/coverage/coverage.gni")
+import("//build/config/profiling/profiling.gni")
 import("//build/toolchain/toolchain.gni")
 
 declare_args() {
@@ -167,7 +167,8 @@
   # work well for fuzzing builds. Since fuzzing builds already disable the
   # sandbox when dumping coverage, limit the sandbox-only path to non-fuzzing
   # builds.
-  use_clang_coverage_inside_sandbox = use_clang_coverage && !use_fuzzing_engine
+  use_clang_profiling_inside_sandbox =
+      use_clang_profiling && !use_fuzzing_engine
 
   # Detect overflow/underflow for global objects.
   #
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 0f152dd83..5c35c82b 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200311.0.1
\ No newline at end of file
+0.20200311.1.1
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 6f4b47690..5c35c82b 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200310.3.1
\ No newline at end of file
+0.20200311.1.1
\ No newline at end of file
diff --git a/build/toolchain/clang_code_coverage_wrapper.py b/build/toolchain/clang_code_coverage_wrapper.py
index 7d6a514e..757c0c9 100755
--- a/build/toolchain/clang_code_coverage_wrapper.py
+++ b/build/toolchain/clang_code_coverage_wrapper.py
@@ -123,13 +123,13 @@
 
 # Map of force lists indexed by target OS.
 _COVERAGE_FORCE_LIST_MAP = {
-    # clang_coverage.cc refers to the symbol `__llvm_profile_dump` from the
+    # clang_profiling.cc refers to the symbol `__llvm_profile_dump` from the
     # profiling runtime. In a partial coverage build, it is possible for a
-    # binary to include clang_coverage.cc but have no instrumented files, thus
+    # binary to include clang_profiling.cc but have no instrumented files, thus
     # causing an unresolved symbol error because the profiling runtime will not
     # be linked in. Therefore we force coverage for this file to ensure that
     # any target that includes it will also get the profiling runtime.
-    'win': [r'..\..\base\test\clang_coverage.cc'],
+    'win': [r'..\..\base\test\clang_profiling.cc'],
 }
 
 
diff --git a/build/util/version.gni b/build/util/version.gni
index 77bd9dd8..4cdb682 100644
--- a/build/util/version.gni
+++ b/build/util/version.gni
@@ -56,7 +56,7 @@
     target_cpu,
   ]
 
-  if (!public_android_sdk) {
+  if (defined(public_android_sdk) && !public_android_sdk) {
     _script_arguments += [ "--next" ]
   }
 }
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index ca12389..a7e6abcb 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -205,6 +205,7 @@
   "junit/src/org/chromium/chrome/browser/tab/TabBrowserControlsConstraintsHelperTest.java",
   "junit/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelperTest.java",
   "junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java",
+  "junit/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegateTest.java",
   "junit/src/org/chromium/chrome/browser/tabstate/TabStateUnitTest.java",
   "junit/src/org/chromium/chrome/browser/tasks/EngagementTimeUtilTest.java",
   "junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index 4f4cdfb..9e0cb8f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -16,6 +16,7 @@
 import android.widget.ScrollView;
 
 import org.chromium.base.ObserverList;
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantActionsCarouselCoordinator;
@@ -30,12 +31,12 @@
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel;
 import org.chromium.chrome.browser.compositor.CompositorViewResizer;
-import org.chromium.chrome.browser.tab.TabViewAndroidDelegate;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.ApplicationViewportInsetSupplier;
 
 /**
  * Coordinator responsible for the Autofill Assistant bottom bar.
@@ -53,6 +54,7 @@
     private final AssistantRootViewContainer mRootViewContainer;
     @Nullable
     private WebContents mWebContents;
+    private ApplicationViewportInsetSupplier mWindowApplicationInsetSupplier;
 
     // Child coordinators.
     private final AssistantHeaderCoordinator mHeaderCoordinator;
@@ -60,6 +62,7 @@
     private final AssistantFormCoordinator mFormCoordinator;
     private final AssistantActionsCarouselCoordinator mActionsCoordinator;
     private final AssistantPeekHeightCoordinator mPeekHeightCoordinator;
+    private final ObservableSupplierImpl<Integer> mInsetSupplier = new ObservableSupplierImpl<>();
     private AssistantInfoBoxCoordinator mInfoBoxCoordinator;
     private AssistantCollectUserDataCoordinator mPaymentRequestCoordinator;
     private final AssistantGenericUiCoordinator mGenericUiCoordinator;
@@ -84,6 +87,10 @@
         mModel = model;
         mBottomSheetController = controller;
 
+        mWindowApplicationInsetSupplier =
+                activity.getWindowAndroid().getApplicationBottomInsetProvider();
+        mWindowApplicationInsetSupplier.addSupplier(mInsetSupplier);
+
         BottomSheetContent currentSheetContent = controller.getCurrentSheetContent();
         if (currentSheetContent instanceof AssistantBottomSheetContent) {
             mContent = (AssistantBottomSheetContent) currentSheetContent;
@@ -262,6 +269,7 @@
      */
     public void destroy() {
         resetVisualViewportHeight();
+        mWindowApplicationInsetSupplier.removeSupplier(mInsetSupplier);
 
         mInfoBoxCoordinator.destroy();
         mInfoBoxCoordinator = null;
@@ -396,10 +404,7 @@
         }
 
         mLastVisualViewportResizing = resizing;
-        TabViewAndroidDelegate chromeDelegate =
-                (TabViewAndroidDelegate) mWebContents.getViewAndroidDelegate();
-        assert chromeDelegate != null;
-        chromeDelegate.insetViewportBottom(resizing);
+        mInsetSupplier.set(resizing);
     }
 
     // Implementation of methods from AutofillAssistantSizeManager.
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantGenericUiDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantGenericUiDelegate.java
index 3907687..17be058 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantGenericUiDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantGenericUiDelegate.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.autofill_assistant.generic_ui;
 
+import android.support.annotation.Nullable;
+
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
@@ -28,11 +30,13 @@
                 mNativeAssistantGenericUiDelegate, AssistantGenericUiDelegate.this, identifier);
     }
 
-    void onListPopupSelectionChanged(String identifier, AssistantValue value) {
+    void onListPopupSelectionChanged(String selectedIndicesIdentifier,
+            AssistantValue selectedIndices, @Nullable String selectedNamesIdentifier,
+            @Nullable AssistantValue selectedNames) {
         assert mNativeAssistantGenericUiDelegate != 0;
         AssistantGenericUiDelegateJni.get().onListPopupSelectionChanged(
-                mNativeAssistantGenericUiDelegate, AssistantGenericUiDelegate.this, identifier,
-                value);
+                mNativeAssistantGenericUiDelegate, AssistantGenericUiDelegate.this,
+                selectedIndicesIdentifier, selectedIndices, selectedNamesIdentifier, selectedNames);
     }
 
     void onCalendarPopupDateChanged(String identifier, AssistantValue value) {
@@ -52,7 +56,9 @@
         void onViewClicked(long nativeAssistantGenericUiDelegate, AssistantGenericUiDelegate caller,
                 String identifier);
         void onListPopupSelectionChanged(long nativeAssistantGenericUiDelegate,
-                AssistantGenericUiDelegate caller, String identifier, AssistantValue value);
+                AssistantGenericUiDelegate caller, String selectedIndicesIdentifier,
+                AssistantValue selectedIndices, @Nullable String selectedNamesIdentifier,
+                @Nullable AssistantValue selectedNames);
         void onCalendarPopupDateChanged(long nativeAssistantGenericUiDelegate,
                 AssistantGenericUiDelegate caller, String identifier, AssistantValue value);
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
index 982e2d5..129d38f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
@@ -9,6 +9,7 @@
 import android.content.Context;
 import android.support.annotation.Nullable;
 import android.view.View;
+import android.widget.TextView;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
@@ -34,18 +35,26 @@
     @CalledByNative
     private static void showListPopup(Context context, String[] itemNames,
             @PopupItemType int[] itemTypes, int[] selectedItems, boolean multiple,
-            String identifier, AssistantGenericUiDelegate delegate) {
+            String selectedIndicesIdentifier, @Nullable String selectedNamesIdentifier,
+            AssistantGenericUiDelegate delegate) {
         assert (itemNames.length == itemTypes.length);
         List<SelectPopupItem> popupItems = new ArrayList<>();
         for (int i = 0; i < itemNames.length; i++) {
             popupItems.add(new SelectPopupItem(itemNames[i], itemTypes[i]));
         }
 
-        SelectPopupDialog dialog = new SelectPopupDialog(context,
-                (indices)
-                        -> delegate.onListPopupSelectionChanged(
-                                identifier, new AssistantValue(indices)),
-                popupItems, multiple, selectedItems);
+        SelectPopupDialog dialog = new SelectPopupDialog(context, (indices) -> {
+            AssistantValue selectedNamesValue = null;
+            if (selectedNamesIdentifier != null) {
+                String[] selectedNames = new String[indices != null ? indices.length : 0];
+                for (int i = 0; i < selectedNames.length; ++i) {
+                    selectedNames[i] = itemNames[indices[i]];
+                }
+                selectedNamesValue = new AssistantValue(selectedNames);
+            }
+            delegate.onListPopupSelectionChanged(selectedIndicesIdentifier,
+                    new AssistantValue(indices), selectedNamesIdentifier, selectedNamesValue);
+        }, popupItems, multiple, selectedItems);
         dialog.show();
     }
 
@@ -85,4 +94,13 @@
                 maxDate.getDateTimes().get(0).getTimeInUtcMillis(), -1, null);
         return true;
     }
+
+    @CalledByNative
+    private static boolean setTextViewText(View view, String text) {
+        if (!(view instanceof TextView)) {
+            return false;
+        }
+        ((TextView) view).setText(text);
+        return true;
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java
index ec4aeb4..efacc2d4 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java
@@ -53,6 +53,7 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.CollectUserDataResultProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ColorProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ComputeValueProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.DateFormatProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.DateList;
 import org.chromium.chrome.browser.autofill_assistant.proto.DateProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.DividerViewProto;
@@ -74,6 +75,7 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.ProcessedActionStatusProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.SetModelValueProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.SetTextProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.SetUserActionsProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ShapeDrawableProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ShowCalendarPopupProto;
@@ -84,6 +86,7 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto.PresentationProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.TextViewProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ToStringProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.UserActionList;
 import org.chromium.chrome.browser.autofill_assistant.proto.UserActionProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ValueProto;
@@ -641,36 +644,54 @@
     @MediumTest
     @DisableIf.Build(sdk_is_less_than = 21)
     public void testListPopup() {
-        ViewProto clickableView = (ViewProto) ViewProto.newBuilder()
-                                          .setTextView(TextViewProto.newBuilder().setText(
-                                                  "Shows a list popup when clicked"))
-                                          .setIdentifier("clickableView")
-                                          .build();
-
-        ViewProto rootView =
-                (ViewProto) ViewProto.newBuilder()
-                        .setViewContainer(
-                                ViewContainerProto.newBuilder()
-                                        .setLinearLayout(
-                                                LinearLayoutProto.newBuilder().setOrientation(
-                                                        LinearLayoutProto.Orientation.VERTICAL))
-                                        .addViews(clickableView))
-                        .build();
-
         List<InteractionProto> interactions = new ArrayList<>();
+        interactions.add(
+                (InteractionProto) InteractionProto.newBuilder()
+                        .setTriggerEvent(EventProto.newBuilder().setOnValueChanged(
+                                OnModelValueChangedEventProto.newBuilder().setModelIdentifier(
+                                        "chips")))
+                        .addCallbacks(CallbackProto.newBuilder().setSetUserActions(
+                                SetUserActionsProto.newBuilder().setModelIdentifier("chips")))
+                        .build());
         interactions.add((InteractionProto) InteractionProto.newBuilder()
-                                 .setTriggerEvent(EventProto.newBuilder().setOnViewClicked(
-                                         OnViewClickedEventProto.newBuilder().setViewIdentifier(
-                                                 "clickableView")))
-                                 .addCallbacks(CallbackProto.newBuilder().setShowListPopup(
-                                         ShowListPopupProto.newBuilder()
-                                                 .setItemNamesModelIdentifier("items")
-                                                 .setSelectedItemIndicesModelIdentifier(
-                                                         "selected_items_indices")
-                                                 .setAllowMultiselect(false)))
+                                 .setTriggerEvent(EventProto.newBuilder().setOnUserActionCalled(
+                                         OnUserActionCalled.newBuilder().setUserActionIdentifier(
+                                                 "done_chip")))
+                                 .addCallbacks(CallbackProto.newBuilder().setEndAction(
+                                         EndActionProto.newBuilder().setStatus(
+                                                 ProcessedActionStatusProto.ACTION_APPLIED)))
                                  .build());
+        interactions.add(
+                (InteractionProto) InteractionProto.newBuilder()
+                        .setTriggerEvent(EventProto.newBuilder().setOnViewClicked(
+                                OnViewClickedEventProto.newBuilder().setViewIdentifier(
+                                        "clickableView")))
+                        .addCallbacks(CallbackProto.newBuilder().setShowListPopup(
+                                ShowListPopupProto.newBuilder()
+                                        .setItemNamesModelIdentifier("items")
+                                        .setSelectedItemIndicesModelIdentifier(
+                                                "selected_items_indices")
+                                        .setSelectedItemNamesModelIdentifier("selected_item_names")
+                                        .setAllowMultiselect(false)))
+                        .build());
 
         List<ModelProto.ModelValue> modelValues = new ArrayList<>();
+        modelValues.add((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
+                                .setIdentifier("value_a")
+                                .setValue(ValueProto.newBuilder().setInts(
+                                        IntList.newBuilder().addValues(1)))
+                                .build());
+        modelValues.add(
+                (ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
+                        .setIdentifier("chips")
+                        .setValue(ValueProto.newBuilder().setUserActions(
+                                UserActionList.newBuilder().addValues(
+                                        UserActionProto.newBuilder()
+                                                .setChip(ChipProto.newBuilder()
+                                                                 .setText("Done")
+                                                                 .setType(ChipType.NORMAL_ACTION))
+                                                .setIdentifier("done_chip"))))
+                        .build());
         modelValues.add(
                 (ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
                         .setIdentifier("items")
@@ -681,22 +702,28 @@
         modelValues.add((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
                                 .setIdentifier("selected_items_indices")
                                 .build());
+        modelValues.add((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
+                                .setIdentifier("selected_item_names")
+                                .build());
+
+        GenericUserInterfaceProto genericUserInterface =
+                (GenericUserInterfaceProto) GenericUserInterfaceProto.newBuilder()
+                        .setRootView(ViewProto.newBuilder()
+                                             .setTextView(TextViewProto.newBuilder().setText(
+                                                     "Shows a list popup when clicked"))
+                                             .setIdentifier("clickableView"))
+                        .setInteractions(
+                                InteractionsProto.newBuilder().addAllInteractions(interactions))
+                        .setModel(ModelProto.newBuilder().addAllValues(modelValues))
+                        .build();
 
         ArrayList<ActionProto> list = new ArrayList<>();
         list.add((ActionProto) ActionProto.newBuilder()
-                         .setCollectUserData(
-                                 CollectUserDataProto.newBuilder()
-                                         .setGenericUserInterfacePrepended(
-                                                 GenericUserInterfaceProto.newBuilder()
-                                                         .setRootView(rootView)
-                                                         .setInteractions(
-                                                                 InteractionsProto.newBuilder()
-                                                                         .addAllInteractions(
-                                                                                 interactions))
-                                                         .setModel(ModelProto.newBuilder()
-                                                                           .addAllValues(
-                                                                                   modelValues)))
-                                         .setRequestTermsAndConditions(false))
+                         .setShowGenericUi(
+                                 ShowGenericUiProto.newBuilder()
+                                         .setGenericUserInterface(genericUserInterface)
+                                         .addAllOutputModelIdentifiers(Arrays.asList(
+                                                 "selected_items_indices", "selected_item_names")))
                          .build());
         AutofillAssistantTestScript script = new AutofillAssistantTestScript(
                 (SupportedScriptProto) SupportedScriptProto.newBuilder()
@@ -710,33 +737,20 @@
                 new AutofillAssistantTestService(Collections.singletonList(script));
         startAutofillAssistant(mTestRule.getActivity(), testService);
 
-        waitUntilViewMatchesCondition(withText("Continue"), isCompletelyDisplayed());
+        waitUntilViewMatchesCondition(withText("Done"), isCompletelyDisplayed());
 
         onView(withText("Shows a list popup when clicked")).perform(click());
         onView(withText("09:30 AM")).inRoot(isDialog()).perform(click());
 
-        // Finish action, wait for response and prepare next set of actions.
-        List<ActionProto> nextActions = new ArrayList<>();
-        nextActions.add(
-                (ActionProto) ActionProto.newBuilder()
-                        .setPrompt(PromptProto.newBuilder()
-                                           .setMessage("Finished")
-                                           .addChoices(PromptProto.Choice.newBuilder().setChip(
-                                                   ChipProto.newBuilder()
-                                                           .setType(ChipType.DONE_ACTION)
-                                                           .setText("End"))))
-                        .build());
-        testService.setNextActions(nextActions);
-        waitUntilViewMatchesCondition(withText("Continue"), isEnabled());
         int numNextActionsCalled = testService.getNextActionsCounter();
-        onView(withText("Continue")).perform(click());
+        onView(withContentDescription("Done")).perform(click());
         testService.waitUntilGetNextActions(numNextActionsCalled + 1);
 
         List<ProcessedActionProto> processedActions = testService.getProcessedActions();
         assertThat(processedActions, iterableWithSize(1));
         assertThat(
                 processedActions.get(0).getStatus(), is(ProcessedActionStatusProto.ACTION_APPLIED));
-        CollectUserDataResultProto result = processedActions.get(0).getCollectUserDataResult();
+        ShowGenericUiProto.Result result = processedActions.get(0).getShowGenericUiResult();
         List<ModelProto.ModelValue> resultModelValues = result.getModel().getValuesList();
         assertThat(resultModelValues, iterableWithSize(2));
         assertThat((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
@@ -745,6 +759,12 @@
                                    IntList.newBuilder().addValues(3)))
                            .build(),
                 isIn(resultModelValues));
+        assertThat((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
+                           .setIdentifier("selected_item_names")
+                           .setValue(ValueProto.newBuilder().setStrings(
+                                   StringList.newBuilder().addValues("09:30 AM")))
+                           .build(),
+                isIn(resultModelValues));
     }
 
     @Test
@@ -1030,10 +1050,36 @@
                                                  .setMinDateModelIdentifier("min_date")
                                                  .setMaxDateModelIdentifier("max_date")))
                                  .build());
+        interactions.add(
+                (InteractionProto) InteractionProto.newBuilder()
+                        .setTriggerEvent(EventProto.newBuilder().setOnValueChanged(
+                                OnModelValueChangedEventProto.newBuilder().setModelIdentifier(
+                                        "date")))
+                        .addCallbacks(CallbackProto.newBuilder().setComputeValue(
+                                ComputeValueProto.newBuilder()
+                                        .setResultModelIdentifier("date_string")
+                                        .setToString(
+                                                ToStringProto.newBuilder()
+                                                        .setModelIdentifier("date")
+                                                        .setDateFormat(
+                                                                DateFormatProto.newBuilder()
+                                                                        .setDateFormat(
+                                                                                "EEE, MMM d y")))))
+                        .build());
+        interactions.add(
+                (InteractionProto) InteractionProto.newBuilder()
+                        .setTriggerEvent(EventProto.newBuilder().setOnValueChanged(
+                                OnModelValueChangedEventProto.newBuilder().setModelIdentifier(
+                                        "date_string")))
+                        .addCallbacks(CallbackProto.newBuilder().setSetText(
+                                SetTextProto.newBuilder()
+                                        .setModelIdentifier("date_string")
+                                        .setViewIdentifier("text_view")))
+                        .build());
 
         GenericUserInterfaceProto genericUserInterface =
                 (GenericUserInterfaceProto) GenericUserInterfaceProto.newBuilder()
-                        .setRootView(createTextView("Click me", "text_view"))
+                        .setRootView(createTextView("", "text_view"))
                         .setInteractions(
                                 InteractionsProto.newBuilder().addAllInteractions(interactions))
                         .setModel(ModelProto.newBuilder().addAllValues(modelValues))
@@ -1057,13 +1103,14 @@
                 new AutofillAssistantTestService(Collections.singletonList(script));
         startAutofillAssistant(mTestRule.getActivity(), testService);
 
-        waitUntilViewMatchesCondition(withText("Done"), isCompletelyDisplayed());
+        waitUntilViewMatchesCondition(withText("Wed, Apr 15, 2020"), isCompletelyDisplayed());
 
-        onView(withText("Click me")).perform(click());
+        onView(withText("Wed, Apr 15, 2020")).perform(click());
         onView(withClassName(equalTo(DatePicker.class.getName())))
                 .inRoot(isDialog())
                 .perform(setDate(2020, 7, 13));
         onView(withText(R.string.date_picker_dialog_set)).inRoot(isDialog()).perform(click());
+        waitUntilViewMatchesCondition(withText("Mon, Jul 13, 2020"), isCompletelyDisplayed());
 
         int numNextActionsCalled = testService.getNextActionsCounter();
         onView(withContentDescription("Done")).perform(click());
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
index faafea5b..c763726 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
@@ -75,6 +75,7 @@
 
     @Before
     public void setUp() {
+        setAutofillFeature(true);
         ShadowRecordHistogram.reset();
         MockitoAnnotations.initMocks(this);
         mocker.mock(RecordHistogramJni.TEST_HOOKS, mMockRecordHistogramNatives);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorFieldModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorFieldModel.java
index 8c2f9ce..c8181443 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorFieldModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorFieldModel.java
@@ -11,8 +11,8 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
-import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.autofill.settings.AutofillProfileBridge.DropdownKeyValue;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -193,7 +193,8 @@
      * Constructs a checkbox to show in the editor. It's checked by default.
      *
      * @param checkboxLabel      The label for the checkbox.
-     * @param checkboxPreference The shared preference for the checkbox status.
+     * @param checkboxPreference The shared preference key for the checkbox status. It must be in
+     *                           ChromePreferenceKeys.
      */
     public static EditorFieldModel createCheckbox(
             CharSequence checkboxLabel, CharSequence checkboxPreference) {
@@ -388,16 +389,13 @@
     /** @return Whether the checkbox is checked. */
     public boolean isChecked() {
         assert mInputTypeHint == INPUT_TYPE_HINT_CHECKBOX;
-        return ContextUtils.getAppSharedPreferences().getBoolean(mValue.toString(), true);
+        return SharedPreferencesManager.getInstance().readBoolean(mValue.toString(), true);
     }
 
     /** Sets the checkbox state. */
     public void setIsChecked(boolean isChecked) {
         assert mInputTypeHint == INPUT_TYPE_HINT_CHECKBOX;
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .putBoolean(mValue.toString(), isChecked)
-                .apply();
+        SharedPreferencesManager.getInstance().writeBoolean(mValue.toString(), isChecked);
     }
 
     /** @return The list of icons resource identifiers to display. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index ee50aa6a..f807a51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -276,10 +276,6 @@
         if (tab == null) return null;
 
         TabAssociatedApp.from(tab).setAppId(mConnection.getClientPackageNameForSession(mSession));
-        if (mIntentDataProvider.shouldEnableEmbeddedMediaExperience()) {
-            // Configures web preferences for viewing downloaded media.
-            if (tab.getWebContents() != null) tab.getWebContents().notifyRendererPreferenceUpdate();
-        }
         initializeTab(tab);
         return tab;
     }
@@ -378,10 +374,6 @@
 
         TabAssociatedApp.from(tab).setAppId(mConnection.getClientPackageNameForSession(mSession));
 
-        if (mIntentDataProvider.shouldEnableEmbeddedMediaExperience()) {
-            if (tab.getWebContents() != null) tab.getWebContents().notifyRendererPreferenceUpdate();
-        }
-
         initializeTab(tab);
 
         if (mIntentDataProvider.getTranslateLanguage() != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegate.java
index f3823e9..758f529 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegate.java
@@ -121,6 +121,9 @@
      */
     void maybeAdjustInstantAppExtras(Intent intent, boolean isIntentToInstantApp);
 
+    /** Invoked for intents with user gestures and records the user gesture if desired. */
+    void maybeSetUserGesture(Intent intent);
+
     /**
      * Records the pending incognito URL if desired. Called only if the
      * navigation is occurring in the context of incognito mode.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
index 2fc190e..3f577a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
@@ -599,6 +599,13 @@
     }
 
     @Override
+    public void maybeSetUserGesture(Intent intent) {
+        // The intent can be used to launch Chrome itself, record the user
+        // gesture here so that it can be used later.
+        IntentWithGesturesHandler.getInstance().onNewIntentWithGesture(intent);
+    }
+
+    @Override
     public void maybeSetPendingReferrer(Intent intent, String referrerUrl) {
         IntentHandler.setPendingReferrer(intent, referrerUrl);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
index 377ee98d..bd2bd0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
@@ -26,9 +26,9 @@
 import org.chromium.base.Log;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.embedder_support.util.UrlUtilities;
+import org.chromium.components.external_intents.ExternalIntentsFeatureList;
 import org.chromium.components.external_intents.ExternalIntentsSwitches;
 import org.chromium.components.external_intents.ExternalNavigationParams;
 import org.chromium.components.external_intents.RedirectHandler;
@@ -361,8 +361,8 @@
     /** Wrapper of check against the feature to support overriding for testing. */
     @VisibleForTesting
     boolean blockExternalFormRedirectsWithoutGesture() {
-        return ChromeFeatureList.isEnabled(
-                ChromeFeatureList.INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE);
+        return ExternalIntentsFeatureList.isEnabled(
+                ExternalIntentsFeatureList.INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE);
     }
 
     /**
@@ -687,11 +687,7 @@
                     AiaIntent.SERP, AiaIntent.NUM_ENTRIES);
         }
 
-        // The intent can be used to launch Chrome itself, record the user
-        // gesture here so that it can be used later.
-        if (params.hasUserGesture()) {
-            IntentWithGesturesHandler.getInstance().onNewIntentWithGesture(targetIntent);
-        }
+        if (params.hasUserGesture()) mDelegate.maybeSetUserGesture(targetIntent);
     }
 
     private @OverrideUrlLoadingResult int handleExternalIncognitoIntent(Intent targetIntent,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
index 38bd1d18..4dabdd5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -30,6 +30,7 @@
 import org.chromium.chrome.browser.autofill.prefeditor.EditorModel;
 import org.chromium.chrome.browser.autofill.settings.AutofillProfileBridge.DropdownKeyValue;
 import org.chromium.chrome.browser.payments.PaymentRequestImpl.PaymentRequestServiceObserverForTest;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.components.payments.MethodStrings;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentMethodData;
@@ -92,9 +93,6 @@
     /** The dropdown key that triggers the address editor to add a new billing address. */
     private static final String BILLING_ADDRESS_ADD_NEW = "add";
 
-    /** The shared preference for the 'save card to device' checkbox status. */
-    private static final String CHECK_SAVE_CARD_TO_DEVICE = "check_save_card_to_device";
-
     /** The web contents where the web payments API is invoked. */
     private final WebContents mWebContents;
 
@@ -765,7 +763,7 @@
         if (mSaveCardCheckbox == null) {
             mSaveCardCheckbox = EditorFieldModel.createCheckbox(
                     mContext.getString(R.string.payments_save_card_to_device_checkbox),
-                    CHECK_SAVE_CARD_TO_DEVICE);
+                    ChromePreferenceKeys.PAYMENTS_CHECK_SAVE_CARD_TO_DEVICE);
         }
         editor.addField(mSaveCardCheckbox);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
index a8690c7..b779a7d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
@@ -6,6 +6,8 @@
 
 import android.view.ViewGroup;
 
+import org.chromium.base.Callback;
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.content_public.browser.RenderWidgetHostView;
 import org.chromium.ui.base.ViewAndroidDelegate;
 
@@ -21,9 +23,41 @@
      */
     private int mApplicationViewportInsetBottomPx;
 
+    /** The inset supplier the observer is currently attached to. */
+    private ObservableSupplier<Integer> mCurrentInsetSupplier;
+
     TabViewAndroidDelegate(Tab tab, ViewGroup containerView) {
         super(containerView);
         mTab = (TabImpl) tab;
+
+        Callback<Integer> insetObserver = (inset) -> updateInsetViewportBottom();
+        mCurrentInsetSupplier = tab.getWindowAndroid().getApplicationBottomInsetProvider();
+        mCurrentInsetSupplier.addObserver(insetObserver);
+
+        mTab.addObserver(new EmptyTabObserver() {
+            @Override
+            public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
+                if (isAttached) {
+                    mCurrentInsetSupplier =
+                            tab.getWindowAndroid().getApplicationBottomInsetProvider();
+                    mCurrentInsetSupplier.addObserver(insetObserver);
+                } else {
+                    mCurrentInsetSupplier.removeObserver(insetObserver);
+                    mCurrentInsetSupplier = null;
+                    updateInsetViewportBottom();
+                }
+            }
+
+            @Override
+            public void onShown(Tab tab, int type) {
+                updateInsetViewportBottom();
+            }
+
+            @Override
+            public void onHidden(Tab tab, int reason) {
+                updateInsetViewportBottom();
+            }
+        });
     }
 
     @Override
@@ -45,12 +79,14 @@
                 bottomControlsOffsetY, bottomControlsMinHeightOffsetY);
     }
 
-    /**
-     * Sets the Visual Viewport bottom inset.
-     * @param viewportInsetBottomPx The bottom inset in pixels.  Use {@code 0} for no inset.
-     */
-    public void insetViewportBottom(int viewportInsetBottomPx) {
-        mApplicationViewportInsetBottomPx = viewportInsetBottomPx;
+    /** Sets the Visual Viewport bottom inset. */
+    private void updateInsetViewportBottom() {
+        int inset =
+                mTab.isHidden() || mCurrentInsetSupplier == null ? 0 : mCurrentInsetSupplier.get();
+
+        if (inset == mApplicationViewportInsetBottomPx) return;
+
+        mApplicationViewportInsetBottomPx = inset;
 
         RenderWidgetHostView renderWidgetHostView = mTab.getWebContents().getRenderWidgetHostView();
         if (renderWidgetHostView == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
index a72e4f8..9d1a5b15 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
@@ -5,13 +5,11 @@
 package org.chromium.chrome.browser.webapps;
 
 import android.content.Intent;
-import android.os.Bundle;
 
 import androidx.browser.trusted.sharing.ShareData;
 
 import org.chromium.base.IntentUtils;
 import org.chromium.chrome.browser.flags.ActivityType;
-import org.chromium.content_public.browser.WebContents;
 import org.chromium.webapk.lib.common.WebApkConstants;
 
 /**
@@ -32,13 +30,6 @@
     }
 
     @Override
-    protected void initializeUI(Bundle savedInstance) {
-        super.initializeUI(savedInstance);
-        WebContents webContents = getActivityTab().getWebContents();
-        if (webContents != null) webContents.notifyRendererPreferenceUpdate();
-    }
-
-    @Override
     public boolean shouldPreferLightweightFre(Intent intent) {
         // We cannot use getWebApkPackageName() because
         // {@link WebappActivity#performPreInflationStartup()} may not have been called yet.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkExtras.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkExtras.java
index 30917de..e577f794 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkExtras.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkExtras.java
@@ -20,11 +20,6 @@
     public final String webApkPackageName;
 
     /**
-     * Badge icon to use for notifications.
-     */
-    public final WebappIcon badgeIcon;
-
-    /**
      * Icon to use for the splash screen.
      */
     public final WebappIcon splashIcon;
@@ -101,7 +96,7 @@
     }
 
     public static WebApkExtras createEmpty() {
-        return new WebApkExtras(null /* webApkPackageName */, new WebappIcon(), new WebappIcon(),
+        return new WebApkExtras(null /* webApkPackageName */, new WebappIcon(),
                 false /* isSplashIconMaskable */, 0 /* shellApkVersion */, null /* manifestUrl */,
                 null /* manifestStartUrl */, WebApkDistributor.OTHER,
                 null /* iconUrlToMurmur2HashMap */, new ShareTarget(),
@@ -109,14 +104,13 @@
                 0 /* webApkVersionCode */);
     }
 
-    public WebApkExtras(String webApkPackageName, WebappIcon badgeIcon, WebappIcon splashIcon,
+    public WebApkExtras(String webApkPackageName, WebappIcon splashIcon,
             boolean isSplashIconMaskable, int shellApkVersion, String manifestUrl,
             String manifestStartUrl, @WebApkDistributor int distributor,
             Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget,
             boolean isSplashProvidedByWebApk, List<ShortcutItem> shortcutItems,
             int webApkVersionCode) {
         this.webApkPackageName = webApkPackageName;
-        this.badgeIcon = badgeIcon;
         this.splashIcon = splashIcon;
         this.isSplashIconMaskable = isSplashIconMaskable;
         this.shellApkVersion = shellApkVersion;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
index cea06b7..6408c0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
@@ -134,7 +134,6 @@
      * @param url                      URL that the WebAPK should navigate to when launched.
      * @param scope                    Scope for the WebAPK.
      * @param primaryIcon              Primary icon to show for the WebAPK.
-     * @param badgeIcon                Badge icon to use for notifications.
      * @param splashIcon               Splash icon to use for the splash screen.
      * @param name                     Name of the WebAPK.
      * @param shortName                The short name of the WebAPK.
@@ -165,21 +164,20 @@
      * @param webApkVersionCode        WebAPK's version code.
      */
     public static WebApkInfo create(String url, String scope, WebappIcon primaryIcon,
-            WebappIcon badgeIcon, WebappIcon splashIcon, String name, String shortName,
-            @WebDisplayMode int displayMode, int orientation, int source, long themeColor,
-            long backgroundColor, int defaultBackgroundColor, boolean isPrimaryIconMaskable,
-            boolean isSplashIconMaskable, String webApkPackageName, int shellApkVersion,
-            String manifestUrl, String manifestStartUrl, @WebApkDistributor int distributor,
+            WebappIcon splashIcon, String name, String shortName, @WebDisplayMode int displayMode,
+            int orientation, int source, long themeColor, long backgroundColor,
+            int defaultBackgroundColor, boolean isPrimaryIconMaskable, boolean isSplashIconMaskable,
+            String webApkPackageName, int shellApkVersion, String manifestUrl,
+            String manifestStartUrl, @WebApkDistributor int distributor,
             Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget,
             boolean forceNavigation, boolean isSplashProvidedByWebApk, ShareData shareData,
             List<ShortcutItem> shortcutItems, int webApkVersionCode) {
-        return create(WebApkIntentDataProviderFactory.create(url, scope, primaryIcon, badgeIcon,
-                splashIcon, name, shortName, displayMode, orientation, source, themeColor,
-                backgroundColor, defaultBackgroundColor, isPrimaryIconMaskable,
-                isSplashIconMaskable, webApkPackageName, shellApkVersion, manifestUrl,
-                manifestStartUrl, distributor, iconUrlToMurmur2HashMap, shareTarget,
-                forceNavigation, isSplashProvidedByWebApk, shareData, shortcutItems,
-                webApkVersionCode));
+        return create(WebApkIntentDataProviderFactory.create(url, scope, primaryIcon, splashIcon,
+                name, shortName, displayMode, orientation, source, themeColor, backgroundColor,
+                defaultBackgroundColor, isPrimaryIconMaskable, isSplashIconMaskable,
+                webApkPackageName, shellApkVersion, manifestUrl, manifestStartUrl, distributor,
+                iconUrlToMurmur2HashMap, shareTarget, forceNavigation, isSplashProvidedByWebApk,
+                shareData, shortcutItems, webApkVersionCode));
     }
 
     private static WebApkInfo create(@Nullable BrowserServicesIntentDataProvider provider) {
@@ -191,13 +189,6 @@
     }
 
     /**
-     * Returns the badge icon in Bitmap form.
-     */
-    public WebappIcon badgeIcon() {
-        return getWebApkExtras().badgeIcon;
-    }
-
-    /**
      * Returns the splash icon in Bitmap form.
      */
     public WebappIcon splashIcon() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProviderFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProviderFactory.java
index 4382a63..cfd1dacc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProviderFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProviderFactory.java
@@ -296,7 +296,6 @@
         boolean isPrimaryIconMaskable =
                 primaryMaskableIconId != 0 && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
 
-        int badgeIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.BADGE_ICON_ID, 0);
         int splashIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.SPLASH_ID, 0);
 
         int isSplashIconMaskableBooleanId = IntentUtils.safeGetInt(
@@ -324,7 +323,6 @@
         return create(url, scope,
                 new WebappIcon(webApkPackageName,
                         isPrimaryIconMaskable ? primaryMaskableIconId : primaryIconId),
-                new WebappIcon(webApkPackageName, badgeIconId),
                 new WebappIcon(webApkPackageName, splashIconId), name, shortName, displayMode,
                 orientation, source, themeColor, backgroundColor, defaultBackgroundColor,
                 isPrimaryIconMaskable, isSplashIconMaskable, webApkPackageName, shellApkVersion,
@@ -338,7 +336,6 @@
      * @param url                      URL that the WebAPK should navigate to when launched.
      * @param scope                    Scope for the WebAPK.
      * @param primaryIcon              Primary icon to show for the WebAPK.
-     * @param badgeIcon                Badge icon to use for notifications.
      * @param splashIcon               Splash icon to use for the splash screen.
      * @param name                     Name of the WebAPK.
      * @param shortName                The short name of the WebAPK.
@@ -371,14 +368,14 @@
      * @param webApkVersionCode        WebAPK's version code.
      */
     public static BrowserServicesIntentDataProvider create(String url, String scope,
-            WebappIcon primaryIcon, WebappIcon badgeIcon, WebappIcon splashIcon, String name,
-            String shortName, @WebDisplayMode int displayMode, int orientation, int source,
-            long themeColor, long backgroundColor, int defaultBackgroundColor,
-            boolean isPrimaryIconMaskable, boolean isSplashIconMaskable, String webApkPackageName,
-            int shellApkVersion, String manifestUrl, String manifestStartUrl,
-            @WebApkDistributor int distributor, Map<String, String> iconUrlToMurmur2HashMap,
-            ShareTarget shareTarget, boolean forceNavigation, boolean isSplashProvidedByWebApk,
-            ShareData shareData, List<ShortcutItem> shortcutItems, int webApkVersionCode) {
+            WebappIcon primaryIcon, WebappIcon splashIcon, String name, String shortName,
+            @WebDisplayMode int displayMode, int orientation, int source, long themeColor,
+            long backgroundColor, int defaultBackgroundColor, boolean isPrimaryIconMaskable,
+            boolean isSplashIconMaskable, String webApkPackageName, int shellApkVersion,
+            String manifestUrl, String manifestStartUrl, @WebApkDistributor int distributor,
+            Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget,
+            boolean forceNavigation, boolean isSplashProvidedByWebApk, ShareData shareData,
+            List<ShortcutItem> shortcutItems, int webApkVersionCode) {
         if (manifestStartUrl == null || webApkPackageName == null) {
             Log.e(TAG, "Incomplete data provided: " + manifestStartUrl + ", " + webApkPackageName);
             return null;
@@ -399,10 +396,6 @@
             primaryIcon = new WebappIcon();
         }
 
-        if (badgeIcon == null) {
-            badgeIcon = new WebappIcon();
-        }
-
         if (splashIcon == null) {
             splashIcon = new WebappIcon();
         }
@@ -416,7 +409,7 @@
                 shortName, displayMode, orientation, source,
                 WebappIntentUtils.colorFromLongColor(backgroundColor), defaultBackgroundColor,
                 false /* isIconGenerated */, isPrimaryIconMaskable, forceNavigation);
-        WebApkExtras webApkExtras = new WebApkExtras(webApkPackageName, badgeIcon, splashIcon,
+        WebApkExtras webApkExtras = new WebApkExtras(webApkPackageName, splashIcon,
                 isSplashIconMaskable, shellApkVersion, manifestUrl, manifestStartUrl, distributor,
                 iconUrlToMurmur2HashMap, shareTarget, isSplashProvidedByWebApk, shortcutItems,
                 webApkVersionCode);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
index 3220fe9..7f662dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
@@ -141,10 +141,9 @@
 
         int defaultBackgroundColor = SplashLayout.getDefaultBackgroundColor(appContext);
         WebApkInfo info = WebApkInfo.create(mOldInfo.url(), scopeUrl,
-                new WebappIcon(primaryIconBitmap), null /* badgeIcon*/,
-                new WebappIcon(splashIconBitmap), name, shortName, displayMode, orientation,
-                mOldInfo.source(), themeColor, backgroundColor, defaultBackgroundColor,
-                isPrimaryIconMaskable, false /* isSplashIconMaskable */,
+                new WebappIcon(primaryIconBitmap), new WebappIcon(splashIconBitmap), name,
+                shortName, displayMode, orientation, mOldInfo.source(), themeColor, backgroundColor,
+                defaultBackgroundColor, isPrimaryIconMaskable, false /* isSplashIconMaskable */,
                 mOldInfo.webApkPackageName(), mOldInfo.shellApkVersion(), mOldInfo.manifestUrl(),
                 manifestStartUrl, WebApkDistributor.BROWSER, iconUrlToMurmur2HashMap, shareTarget,
                 mOldInfo.shouldForceNavigation(), mOldInfo.isSplashProvidedByWebApk(), null,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
index 03900b1..a89df8a9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
@@ -336,6 +336,20 @@
 
     @Test
     @SmallTest
+    public void testMaybeSetUserGesture() {
+        ExternalNavigationDelegateImpl delegate = new ExternalNavigationDelegateImpl(
+                mActivityTestRule.getActivity().getActivityTab());
+
+        String url = "http://www.example.com";
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setData(Uri.parse(url));
+
+        delegate.maybeSetUserGesture(intent);
+        Assert.assertTrue(IntentWithGesturesHandler.getInstance().getUserGestureAndClear(intent));
+    }
+
+    @Test
+    @SmallTest
     public void testMaybeSetPendingReferrer() {
         ExternalNavigationDelegateImpl delegate = new ExternalNavigationDelegateImpl(
                 mActivityTestRule.getActivity().getActivityTab());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
index 5051cd8..d20063e5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
@@ -1548,8 +1548,7 @@
                 .withHasUserGesture(true)
                 .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
                         START_OTHER_ACTIVITY);
-        Assert.assertTrue(IntentWithGesturesHandler.getInstance().getUserGestureAndClear(
-                mDelegate.startActivityIntent));
+        Assert.assertTrue(mDelegate.maybeSetUserGestureCalled);
         Assert.assertFalse(mDelegate.startIncognitoIntentCalled);
     }
 
@@ -1565,8 +1564,7 @@
                 .withIsIncognito(true)
                 .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_ASYNC_ACTION,
                         START_INCOGNITO | START_OTHER_ACTIVITY);
-        Assert.assertTrue(IntentWithGesturesHandler.getInstance().getUserGestureAndClear(
-                mDelegate.startActivityIntent));
+        Assert.assertTrue(mDelegate.maybeSetUserGestureCalled);
         Assert.assertTrue(mDelegate.startIncognitoIntentCalled);
     }
 
@@ -1867,6 +1865,11 @@
         }
 
         @Override
+        public void maybeSetUserGesture(Intent intent) {
+            maybeSetUserGestureCalled = true;
+        }
+
+        @Override
         public void maybeSetPendingIncognitoUrl(Intent intent) {}
 
         @Override
@@ -1991,6 +1994,7 @@
 
         public Intent startActivityIntent;
         public boolean startIncognitoIntentCalled;
+        public boolean maybeSetUserGestureCalled;
         public boolean startFileIntentCalled;
         public String defaultSmsPackageName;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 0f7e961..4ac7a17a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -176,7 +176,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             WebappDataStorage storage =
                     WebappRegistry.getInstance().getWebappDataStorage(WEBAPK_ID);
-            WebApkInfo info = WebApkInfo.create("", creationData.scope, null, null, null,
+            WebApkInfo info = WebApkInfo.create("", creationData.scope, null, null,
                     creationData.name, creationData.shortName, creationData.displayMode,
                     creationData.orientation, 0, creationData.themeColor,
                     creationData.backgroundColor, 0, creationData.isPrimaryIconMaskable,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegateTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegateTest.java
new file mode 100644
index 0000000..bab279e
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegateTest.java
@@ -0,0 +1,101 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tab;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.ApplicationViewportInsetSupplier;
+import org.chromium.ui.base.WindowAndroid;
+
+/** Unit tests for the TabViewAndroidDelegate. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class TabViewAndroidDelegateTest {
+    private final ArgumentCaptor<TabObserver> mTabObserverCaptor =
+            ArgumentCaptor.forClass(TabObserver.class);
+
+    @Mock
+    private TabImpl mTab;
+
+    @Mock
+    private WebContents mWebContents;
+
+    @Mock
+    private WindowAndroid mWindowAndroid;
+
+    @Mock
+    private ViewGroup mViewContainer;
+
+    private ApplicationViewportInsetSupplier mApplicationInsetSupplier;
+    private ObservableSupplierImpl<Integer> mFeatureInsetSupplier;
+    private TabViewAndroidDelegate mViewAndroidDelegate;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mFeatureInsetSupplier = new ObservableSupplierImpl<>();
+
+        mApplicationInsetSupplier = ApplicationViewportInsetSupplier.createForTests();
+        mApplicationInsetSupplier.addSupplier(mFeatureInsetSupplier);
+
+        when(mWindowAndroid.getApplicationBottomInsetProvider())
+                .thenReturn(mApplicationInsetSupplier);
+        when(mTab.getWindowAndroid()).thenReturn(mWindowAndroid);
+        when(mTab.getWebContents()).thenReturn(mWebContents);
+
+        mViewAndroidDelegate = new TabViewAndroidDelegate(mTab, mViewContainer);
+        verify(mTab).addObserver(mTabObserverCaptor.capture());
+    }
+
+    @Test
+    public void testInset() {
+        mFeatureInsetSupplier.set(10);
+        assertEquals("The bottom inset for the tab should be non-zero.", 10,
+                mViewAndroidDelegate.getViewportInsetBottom());
+    }
+
+    @Test
+    public void testInset_afterHidden() {
+        mFeatureInsetSupplier.set(10);
+
+        when(mTab.isHidden()).thenReturn(true);
+
+        mTabObserverCaptor.getValue().onHidden(mTab, 0);
+        assertEquals("The bottom inset for the tab should be zero.", 0,
+                mViewAndroidDelegate.getViewportInsetBottom());
+    }
+
+    @Test
+    public void testInset_afterDetachAndAttach() {
+        mFeatureInsetSupplier.set(10);
+
+        assertEquals("The bottom inset for the tab should be non-zero.", 10,
+                mViewAndroidDelegate.getViewportInsetBottom());
+
+        mTabObserverCaptor.getValue().onActivityAttachmentChanged(mTab, false);
+
+        assertEquals("The bottom inset for the tab should be zero.", 0,
+                mViewAndroidDelegate.getViewportInsetBottom());
+
+        mTabObserverCaptor.getValue().onActivityAttachmentChanged(mTab, true);
+
+        assertEquals("The bottom inset for the tab should be non-zero.", 10,
+                mViewAndroidDelegate.getViewportInsetBottom());
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
index 583a07d..826d37a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
@@ -249,7 +249,6 @@
 
         Assert.assertEquals(PRIMARY_MASKABLE_ICON_ID, info.icon().resourceIdForTesting());
         Assert.assertEquals(true, info.isIconAdaptive());
-        Assert.assertEquals(null, info.badgeIcon().bitmap());
         Assert.assertEquals(null, info.splashIcon().bitmap());
 
         WebApkInfo.ShareTarget shareTarget = info.shareTarget();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
index 19e9d30e..7536f415 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
@@ -86,8 +86,6 @@
     private static final String SHORT_NAME = "Short Name";
     private static final String PRIMARY_ICON_URL = "/icon.png";
     private static final String PRIMARY_ICON_MURMUR2_HASH = "3";
-    private static final String BADGE_ICON_URL = "/badge.png";
-    private static final String BADGE_ICON_MURMUR2_HASH = "4";
     private static final @WebDisplayMode int DISPLAY_MODE = WebDisplayMode.UNDEFINED;
     private static final int ORIENTATION = ScreenOrientationValues.DEFAULT;
     private static final long THEME_COLOR = 1L;
@@ -131,8 +129,8 @@
         @Override
         public void storeWebApkUpdateRequestToFile(String updateRequestPath, String startUrl,
                 String scope, String name, String shortName, String primaryIconUrl,
-                Bitmap primaryIcon, boolean isPrimaryIconMaskable, String badgeIconUrl,
-                Bitmap badgeIcon, String[] iconUrls, String[] iconHashes,
+                Bitmap primaryIcon, boolean isPrimaryIconMaskable, String splashIconUrl,
+                Bitmap splashIcon, String[] iconUrls, String[] iconHashes,
                 @WebDisplayMode int displayMode, int orientation, long themeColor,
                 long backgroundColor, String shareTargetAction, String shareTargetParamTitle,
                 String shareTargetParamText, boolean shareTargetParamIsMethodPost,
@@ -207,7 +205,7 @@
 
         @Override
         protected void storeWebApkUpdateRequestToFile(String updateRequestPath, WebApkInfo info,
-                String primaryIconUrl, String badgeIconUrl, boolean isManifestStale,
+                String primaryIconUrl, String splashIconUrl, boolean isManifestStale,
                 @WebApkUpdateReason int updateReason, Callback<Boolean> callback) {
             mStoreUpdateRequestCallback = callback;
             mUpdateName = info.name();
@@ -229,8 +227,6 @@
         public Map<String, String> iconUrlToMurmur2HashMap;
         public String primaryIconUrl;
         public Bitmap primaryIcon;
-        public String badgeIconUrl;
-        public Bitmap badgeIcon;
         public @WebDisplayMode int displayMode;
         public int orientation;
         public long themeColor;
@@ -355,12 +351,9 @@
 
         manifestData.iconUrlToMurmur2HashMap = new HashMap<>();
         manifestData.iconUrlToMurmur2HashMap.put(PRIMARY_ICON_URL, PRIMARY_ICON_MURMUR2_HASH);
-        manifestData.iconUrlToMurmur2HashMap.put(BADGE_ICON_URL, BADGE_ICON_MURMUR2_HASH);
 
         manifestData.primaryIconUrl = PRIMARY_ICON_URL;
         manifestData.primaryIcon = createBitmap(Color.GREEN);
-        manifestData.badgeIconUrl = BADGE_ICON_URL;
-        manifestData.badgeIcon = createBitmap(Color.BLUE);
         manifestData.displayMode = DISPLAY_MODE;
         manifestData.orientation = ORIENTATION;
         manifestData.themeColor = THEME_COLOR;
@@ -383,9 +376,9 @@
 
         final String kPackageName = "org.random.webapk";
         return WebApkInfo.create("", manifestData.scopeUrl,
-                new WebappIcon(manifestData.primaryIcon), new WebappIcon(manifestData.badgeIcon),
-                null, manifestData.name, manifestData.shortName, manifestData.displayMode,
-                manifestData.orientation, -1, manifestData.themeColor, manifestData.backgroundColor,
+                new WebappIcon(manifestData.primaryIcon), null, manifestData.name,
+                manifestData.shortName, manifestData.displayMode, manifestData.orientation, -1,
+                manifestData.themeColor, manifestData.backgroundColor,
                 manifestData.defaultBackgroundColor, false /* isPrimaryIconMaskable */,
                 false /* isSplashIconMaskable*/, kPackageName, -1, WEB_MANIFEST_URL,
                 manifestData.startUrl, WebApkDistributor.BROWSER,
@@ -443,9 +436,9 @@
     private static void onGotManifestData(
             WebApkUpdateManager updateManager, ManifestData fetchedManifestData) {
         String primaryIconUrl = randomIconUrl(fetchedManifestData);
-        String badgeIconUrl = randomIconUrl(fetchedManifestData);
+        String splashIconUrl = randomIconUrl(fetchedManifestData);
         updateManager.onGotManifestData(
-                infoFromManifestData(fetchedManifestData), primaryIconUrl, badgeIconUrl);
+                infoFromManifestData(fetchedManifestData), primaryIconUrl, splashIconUrl);
     }
 
     /**
@@ -501,7 +494,7 @@
         updateIfNeeded(WEBAPK_PACKAGE_NAME, updateManager, androidManifestData.shortcuts);
         assertTrue(updateManager.updateCheckStarted());
         updateManager.onGotManifestData(infoFromManifestData(fetchedManifestData),
-                fetchedManifestData.primaryIconUrl, fetchedManifestData.badgeIconUrl);
+                fetchedManifestData.primaryIconUrl, null);
         return updateManager.updateRequested();
     }
 
@@ -964,20 +957,6 @@
 
     /**
      * Test that an upgrade is requested when:
-     * - WebAPK was generated using icon at {@link BADGE_ICON_URL} from Web Manifest.
-     * - Bitmap at {@link BADGE_ICON_URL} has changed.
-     */
-    @Test
-    public void testBadgeIconChangeShouldUpgrade() {
-        ManifestData fetchedData = defaultManifestData();
-        fetchedData.iconUrlToMurmur2HashMap.put(
-                fetchedData.badgeIconUrl, BADGE_ICON_MURMUR2_HASH + "1");
-        fetchedData.badgeIcon = createBitmap(Color.GREEN);
-        assertTrue(checkUpdateNeededForFetchedManifest(defaultManifestData(), fetchedData));
-    }
-
-    /**
-     * Test that an upgrade is requested when:
      * - WebAPK is generated using icon at {@link PRIMARY_ICON_URL} from Web Manifest.
      * - A new icon URL is added to the Web Manifest. And InstallableManager selects the new icon as
      *   the primary icon.
@@ -991,20 +970,6 @@
     }
 
     /**
-     * Test that an upgrade is requested when:
-     * - WebAPK is generated using icon at {@link BADGE_ICON_URL} from Web Manifest.
-     * - A new icon URL is added to the Web Manifest. And InstallableManager selects the new icon as
-     *   the badge icon.
-     */
-    @Test
-    public void testBadgeIconUrlChangeShouldUpgrade() {
-        ManifestData fetchedData = defaultManifestData();
-        fetchedData.iconUrlToMurmur2HashMap.put("/badge2.png", "44");
-        fetchedData.badgeIconUrl = "/badge2.png";
-        assertTrue(checkUpdateNeededForFetchedManifest(defaultManifestData(), fetchedData));
-    }
-
-    /**
      * Test that an upgrade is not requested if:
      * - icon URL is added to the Web Manifest
      * AND
@@ -1040,7 +1005,6 @@
 
         ManifestData androidManifestData = defaultManifestData();
         androidManifestData.primaryIconUrl = iconUrl1;
-        androidManifestData.badgeIconUrl = badgeUrl1;
         androidManifestData.iconUrlToMurmur2HashMap.clear();
         androidManifestData.iconUrlToMurmur2HashMap.put(iconUrl1, hash1);
         androidManifestData.iconUrlToMurmur2HashMap.put(iconUrl2, hash2);
@@ -1049,7 +1013,6 @@
 
         ManifestData fetchedManifestData = defaultManifestData();
         fetchedManifestData.primaryIconUrl = iconUrl2;
-        fetchedManifestData.badgeIconUrl = badgeUrl2;
         fetchedManifestData.iconUrlToMurmur2HashMap.clear();
         fetchedManifestData.iconUrlToMurmur2HashMap.put(iconUrl1, null);
         fetchedManifestData.iconUrlToMurmur2HashMap.put(iconUrl2, hash2);
diff --git a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java
index 7d12b09..db1368b 100644
--- a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java
+++ b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java
@@ -34,7 +34,6 @@
             "org.chromium.webapk.shell_apk.iconUrlsAndIconMurmur2Hashes";
     public static final String WEB_MANIFEST_URL = "org.chromium.webapk.shell_apk.webManifestUrl";
     public static final String DISTRIBUTOR = "org.chromium.webapk.shell_apk.distributor";
-    public static final String BADGE_ICON_ID = "org.chromium.webapk.shell_apk.badgeIconId";
     public static final String SHARE_ACTION = "org.chromium.webapk.shell_apk.shareAction";
     public static final String SHARE_METHOD = "org.chromium.webapk.shell_apk.shareMethod";
     public static final String SHARE_ENCTYPE = "org.chromium.webapk.shell_apk.shareEnctype";
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml
index ad73ffcf..5e8efc1 100644
--- a/chrome/android/webapk/shell_apk/AndroidManifest.xml
+++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -149,7 +149,6 @@
         <meta-data android:name="org.chromium.webapk.shell_apk.iconUrlsAndIconMurmur2Hashes" android:value="{{{icon_urls_and_icon_murmur2_hashes}}}" />
 
         <meta-data android:name="org.chromium.webapk.shell_apk.webManifestUrl" android:value="{{{web_manifest_url}}}" />
-        {{#badge_icon_id}}<meta-data android:name="org.chromium.webapk.shell_apk.badgeIconId" android:resource="{{{badge_icon_id}}}" />{{/badge_icon_id}}
         <service
             android:name="org.chromium.webapk.shell_apk.WebApkServiceFactory"
             android:exported="true"
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 2504b26..5dfe743 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 122
+current_shell_apk_version = 124
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 0a877e9..b328704e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2999,6 +2999,7 @@
       "//components/crash/android:crash_android",
       "//components/embedder_support/android:util",
       "//components/embedder_support/android:web_contents_delegate",
+      "//components/external_intents/android",
       "//components/feed:buildflags",
       "//components/feed:feature_list",
       "//components/invalidation/impl:feature_list",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 63cbb23..44c8186 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -98,6 +98,7 @@
   "+components/embedder_support",
   "+components/encrypted_messages",
   "+components/exo",
+  "+components/external_intents",
   "+components/favicon_base",
   "+components/favicon/content",
   "+components/favicon/core",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 87f484ca..32071ea 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -177,6 +177,7 @@
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/explore_sites/explore_sites_feature.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
+#include "components/external_intents/android/external_intents_feature_list.h"
 #else  // OS_ANDROID
 #include "chrome/browser/media/router/media_router_feature.h"
 #endif  // OS_ANDROID
@@ -4861,7 +4862,7 @@
      flag_descriptions::kIntentBlockExternalFormRedirectsNoGestureDescription,
      kOsAndroid,
      FEATURE_VALUE_TYPE(
-         chrome::android::kIntentBlockExternalFormRedirectsNoGesture)},
+         external_intents::kIntentBlockExternalFormRedirectsNoGesture)},
     {"recover-from-never-save-android",
      flag_descriptions::kRecoverFromNeverSaveAndroidName,
      flag_descriptions::kRecoverFromNeverSaveAndroidDescription, kOsAndroid,
diff --git a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
index d2b06395..946f8c3 100644
--- a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
+++ b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
@@ -20,19 +20,6 @@
 using base::android::AttachCurrentThread;
 using base::android::JavaParamRef;
 
-namespace {
-// Converts a java string to native. Returns an empty string if input is null.
-std::string SafeConvertJavaStringToNative(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jstring) {
-  std::string native_string;
-  if (jstring) {
-    base::android::ConvertJavaStringToUTF8(env, jstring, &native_string);
-  }
-  return native_string;
-}
-}  // namespace
-
 namespace autofill_assistant {
 
 AssistantCollectUserDataDelegate::AssistantCollectUserDataDelegate(
@@ -59,9 +46,14 @@
     return;
   }
 
-  std::string name = SafeConvertJavaStringToNative(env, jpayer_name);
-  std::string phone = SafeConvertJavaStringToNative(env, jpayer_phone);
-  std::string email = SafeConvertJavaStringToNative(env, jpayer_email);
+  std::string name = ui_controller_android_utils::SafeConvertJavaStringToNative(
+      env, jpayer_name);
+  std::string phone =
+      ui_controller_android_utils::SafeConvertJavaStringToNative(env,
+                                                                 jpayer_phone);
+  std::string email =
+      ui_controller_android_utils::SafeConvertJavaStringToNative(env,
+                                                                 jpayer_email);
 
   auto contact_profile = std::make_unique<autofill::AutofillProfile>();
   contact_profile->SetRawInfo(autofill::ServerFieldType::NAME_FULL,
@@ -139,7 +131,9 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
     const base::android::JavaParamRef<jstring>& jidentifier) {
-  std::string identifier = SafeConvertJavaStringToNative(env, jidentifier);
+  std::string identifier =
+      ui_controller_android_utils::SafeConvertJavaStringToNative(env,
+                                                                 jidentifier);
   ui_controller_->OnLoginChoiceChanged(identifier);
 }
 
@@ -205,7 +199,7 @@
     const base::android::JavaParamRef<jstring>& jkey,
     const base::android::JavaParamRef<jobject>& jvalue) {
   ui_controller_->OnKeyValueChanged(
-      SafeConvertJavaStringToNative(env, jkey),
+      ui_controller_android_utils::SafeConvertJavaStringToNative(env, jkey),
       ui_controller_android_utils::ToNativeValue(env, jvalue));
 }
 
diff --git a/chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.cc
index ec9924a..7637df3 100644
--- a/chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.cc
+++ b/chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.cc
@@ -42,19 +42,20 @@
 void AssistantGenericUiDelegate::OnListPopupSelectionChanged(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
-    const base::android::JavaParamRef<jstring>& jmodel_identifier,
-    const base::android::JavaParamRef<jobject>& jvalue) {
-  std::string identifier;
-  if (jmodel_identifier) {
-    base::android::ConvertJavaStringToUTF8(env, jmodel_identifier, &identifier);
+    const base::android::JavaParamRef<jstring>& jindices_model_identifier,
+    const base::android::JavaParamRef<jobject>& jindicies_value,
+    const base::android::JavaParamRef<jstring>& jnames_model_identifier,
+    const base::android::JavaParamRef<jobject>& jnames_value) {
+  ui_controller_->OnValueChanged(
+      ui_controller_android_utils::SafeConvertJavaStringToNative(
+          env, jindices_model_identifier),
+      ui_controller_android_utils::ToNativeValue(env, jindicies_value));
+  if (jnames_model_identifier) {
+    ui_controller_->OnValueChanged(
+        ui_controller_android_utils::SafeConvertJavaStringToNative(
+            env, jnames_model_identifier),
+        ui_controller_android_utils::ToNativeValue(env, jnames_value));
   }
-
-  ValueProto value;
-  if (jvalue) {
-    value = ui_controller_android_utils::ToNativeValue(env, jvalue);
-  }
-
-  ui_controller_->OnValueChanged(identifier, value);
 }
 
 // TODO(b/145043394): refactor delegate methods into a single SetValue() where
diff --git a/chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.h b/chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.h
index ff0aac40..03467f8 100644
--- a/chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.h
+++ b/chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.h
@@ -23,14 +23,17 @@
       const base::android::JavaParamRef<jobject>& jcaller,
       const base::android::JavaParamRef<jstring>& jview_identifier);
 
-  // The selection in a list popup has changed. |jmodel_identifier| is the model
-  // identifier that the new selection indices should be written to. |jvalue| is
-  // a Java array of integers containing the newly selected indices.
+  // The selection in a list popup has changed. |jindices_model_identifier| is
+  // the model identifier that |jindicies_value| should be written to.
+  // |jnames_model_identifier| is the model identifier that |jnames_value|
+  // should be written to, if specified.
   void OnListPopupSelectionChanged(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
-      const base::android::JavaParamRef<jstring>& jmodel_identifier,
-      const base::android::JavaParamRef<jobject>& jvalue);
+      const base::android::JavaParamRef<jstring>& jindices_model_identifier,
+      const base::android::JavaParamRef<jobject>& jindicies_value,
+      const base::android::JavaParamRef<jstring>& jnames_model_identifier,
+      const base::android::JavaParamRef<jobject>& jnames_value);
 
   // The date in a calendar popup has changed. |jmodel_identifier| is the model
   // identifier that the new date should be written to. |jvalue| is a Java
diff --git a/chrome/browser/android/autofill_assistant/generic_ui_controller_android.cc b/chrome/browser/android/autofill_assistant/generic_ui_controller_android.cc
index a5f9d531f..7587d7c 100644
--- a/chrome/browser/android/autofill_assistant/generic_ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/generic_ui_controller_android.cc
@@ -233,7 +233,7 @@
   auto interaction_handler =
       std::make_unique<InteractionHandlerAndroid>(event_handler, jcontext);
   if (!interaction_handler->AddInteractionsFromProto(
-          proto.interactions(), env, *views, jdelegate, user_model,
+          proto.interactions(), env, views.get(), jdelegate, user_model,
           basic_interactions)) {
     return nullptr;
   }
diff --git a/chrome/browser/android/autofill_assistant/interaction_handler_android.cc b/chrome/browser/android/autofill_assistant/interaction_handler_android.cc
index a07268a..966cf00 100644
--- a/chrome/browser/android/autofill_assistant/interaction_handler_android.cc
+++ b/chrome/browser/android/autofill_assistant/interaction_handler_android.cc
@@ -129,9 +129,6 @@
   }
 
   JNIEnv* env = base::android::AttachCurrentThread();
-  auto jidentifier = base::android::ConvertUTF8ToJavaString(
-      env, proto.selected_item_indices_model_identifier());
-
   std::vector<std::string> item_names_vec;
   std::copy(item_names->strings().values().begin(),
             item_names->strings().values().end(),
@@ -151,7 +148,14 @@
       env, jcontext, base::android::ToJavaArrayOfStrings(env, item_names_vec),
       base::android::ToJavaIntArray(env, item_types_vec),
       base::android::ToJavaIntArray(env, selected_indices_vec),
-      proto.allow_multiselect(), jidentifier, jdelegate);
+      proto.allow_multiselect(),
+      base::android::ConvertUTF8ToJavaString(
+          env, proto.selected_item_indices_model_identifier()),
+      proto.selected_item_names_model_identifier().empty()
+          ? nullptr
+          : base::android::ConvertUTF8ToJavaString(
+                env, proto.selected_item_names_model_identifier()),
+      jdelegate);
 }
 
 void ShowCalendarPopup(base::WeakPtr<UserModel> user_model,
@@ -202,19 +206,52 @@
   }
 }
 
+void SetTextViewText(
+    base::WeakPtr<UserModel> user_model,
+    const SetTextProto& proto,
+    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views) {
+  if (!user_model) {
+    return;
+  }
+
+  auto text = user_model->GetValue(proto.model_identifier());
+  if (!text.has_value()) {
+    DVLOG(2) << "Failed to set text for " << proto.view_identifier() << ": "
+             << proto.model_identifier() << " not found in model";
+    return;
+  }
+  if (text->strings().values_size() != 1) {
+    DVLOG(2) << "Failed to set text for " << proto.view_identifier()
+             << ": expected " << proto.model_identifier()
+             << " to contain single string, but was instead " << *text;
+    return;
+  }
+
+  auto jview = views->find(proto.view_identifier());
+  if (jview == views->end()) {
+    DVLOG(2) << "Failed to set text for " << proto.view_identifier() << ": "
+             << " view not found";
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_AssistantViewInteractions_setTextViewText(
+      env, jview->second,
+      base::android::ConvertUTF8ToJavaString(env, text->strings().values(0)));
+}
+
 base::Optional<EventHandler::EventKey> CreateEventKeyFromProto(
     const EventProto& proto,
     JNIEnv* env,
-    const std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>&
-        views,
+    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views,
     base::android::ScopedJavaGlobalRef<jobject> jdelegate) {
   switch (proto.kind_case()) {
     case EventProto::kOnValueChanged:
       return base::Optional<EventHandler::EventKey>(
           {proto.kind_case(), proto.on_value_changed().model_identifier()});
     case EventProto::kOnViewClicked: {
-      auto jview = views.find(proto.on_view_clicked().view_identifier());
-      if (jview == views.end()) {
+      auto jview = views->find(proto.on_view_clicked().view_identifier());
+      if (jview == views->end()) {
         VLOG(1) << "Invalid click event, no view with id='"
                 << proto.on_view_clicked().view_identifier() << "' found";
         return base::nullopt;
@@ -243,6 +280,7 @@
     const CallbackProto& proto,
     UserModel* user_model,
     BasicInteractions* basic_interactions,
+    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views,
     base::android::ScopedJavaGlobalRef<jobject> jcontext,
     base::android::ScopedJavaGlobalRef<jobject> jdelegate) {
   switch (proto.kind_case()) {
@@ -310,7 +348,20 @@
           base::BindRepeating(&ShowCalendarPopup, user_model->GetWeakPtr(),
                               proto.show_calendar_popup(), jcontext,
                               jdelegate));
-      break;
+    case CallbackProto::kSetText:
+      if (proto.set_text().model_identifier().empty()) {
+        VLOG(1) << "Error creating SetText interaction: "
+                   "model_identifier not set";
+        return base::nullopt;
+      }
+      if (proto.set_text().view_identifier().empty()) {
+        VLOG(1) << "Error creating SetText interaction: "
+                   "view_identifier not set";
+        return base::nullopt;
+      }
+      return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
+          base::BindRepeating(&SetTextViewText, user_model->GetWeakPtr(),
+                              proto.set_text(), views));
     case CallbackProto::KIND_NOT_SET:
       VLOG(1) << "Error creating interaction: kind not set";
       return base::nullopt;
@@ -344,8 +395,7 @@
 bool InteractionHandlerAndroid::AddInteractionsFromProto(
     const InteractionsProto& proto,
     JNIEnv* env,
-    const std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>&
-        views,
+    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views,
     base::android::ScopedJavaGlobalRef<jobject> jdelegate,
     UserModel* user_model,
     BasicInteractions* basic_interactions) {
@@ -363,7 +413,8 @@
 
     for (const auto& callback_proto : interaction_proto.callbacks()) {
       auto callback = CreateInteractionCallbackFromProto(
-          callback_proto, user_model, basic_interactions, jcontext_, jdelegate);
+          callback_proto, user_model, basic_interactions, views, jcontext_,
+          jdelegate);
       if (!callback) {
         VLOG(1) << "Invalid callback for interaction";
         return false;
diff --git a/chrome/browser/android/autofill_assistant/interaction_handler_android.h b/chrome/browser/android/autofill_assistant/interaction_handler_android.h
index 5239efe..115fc07 100644
--- a/chrome/browser/android/autofill_assistant/interaction_handler_android.h
+++ b/chrome/browser/android/autofill_assistant/interaction_handler_android.h
@@ -43,11 +43,11 @@
 
   // Creates callbacks for each interaction in |proto| as well as the
   // corresponding view events in |views|. Returns false if |proto| is invalid.
+  // |views| must outlive this interaction handler.
   bool AddInteractionsFromProto(
       const InteractionsProto& proto,
       JNIEnv* env,
-      const std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>&
-          views,
+      std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views,
       base::android::ScopedJavaGlobalRef<jobject> jdelegate,
       UserModel* user_model,
       BasicInteractions* basic_interactions);
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc b/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
index 753854c9..fb0138a 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
@@ -146,6 +146,9 @@
 ValueProto ToNativeValue(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& jvalue) {
   ValueProto proto;
+  if (!jvalue) {
+    return proto;
+  }
   auto jints = Java_AssistantValue_getIntegers(env, jvalue);
   if (jints) {
     auto* mutable_ints = proto.mutable_ints();
@@ -261,6 +264,16 @@
   Java_AssistantInfoPopup_show(env, jinfo_popup, jcontext);
 }
 
+std::string SafeConvertJavaStringToNative(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& jstring) {
+  std::string native_string;
+  if (jstring) {
+    base::android::ConvertJavaStringToUTF8(env, jstring, &native_string);
+  }
+  return native_string;
+}
+
 }  // namespace ui_controller_android_utils
 
 }  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android_utils.h b/chrome/browser/android/autofill_assistant/ui_controller_android_utils.h
index 4121d171..5f74bff 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android_utils.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android_utils.h
@@ -51,7 +51,8 @@
 base::android::ScopedJavaLocalRef<jobject> ToJavaValue(JNIEnv* env,
                                                        const ValueProto& proto);
 
-// Returns the native equivalent of |jvalue|.
+// Returns the native equivalent of |jvalue|. Returns an empty ValueProto if
+// |jvalue| is null.
 ValueProto ToNativeValue(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& jvalue);
 
@@ -65,6 +66,11 @@
                        base::android::ScopedJavaLocalRef<jobject> jinfo_popup,
                        base::android::ScopedJavaLocalRef<jobject> jcontext);
 
+// Converts a java string to native. Returns an empty string if input is null.
+std::string SafeConvertJavaStringToNative(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& jstring);
+
 }  // namespace ui_controller_android_utils
 
 }  //  namespace autofill_assistant
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index f2b26b1..f3727ded 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -944,6 +944,7 @@
                       TestCase("searchDownloadsWithNoResults"),
                       TestCase("searchDownloadsClearSearchKeyDown"),
                       TestCase("searchDownloadsClearSearch"),
+                      TestCase("searchHidingViaTab"),
                       TestCase("searchHidingTextEntryField")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
index 346b0ce..f80f586 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
@@ -207,7 +207,8 @@
     device_remote_commands_invalidator_ =
         std::make_unique<AffiliatedRemoteCommandsInvalidator>(
             device_cloud_policy_manager_->core(),
-            affiliated_invalidation_service_provider_.get());
+            affiliated_invalidation_service_provider_.get(),
+            PolicyInvalidationScope::kDevice);
   }
 
   SetTimezoneIfPolicyAvailable();
diff --git a/chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.cc b/chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.cc
index 63bb35a..aa6fd70 100644
--- a/chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.cc
@@ -4,15 +4,18 @@
 
 #include "chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.h"
 
+#include "base/time/default_clock.h"
 #include "chrome/browser/policy/cloud/remote_commands_invalidator_impl.h"
 
 namespace policy {
 
 AffiliatedRemoteCommandsInvalidator::AffiliatedRemoteCommandsInvalidator(
     CloudPolicyCore* core,
-    AffiliatedInvalidationServiceProvider* invalidation_service_provider)
+    AffiliatedInvalidationServiceProvider* invalidation_service_provider,
+    PolicyInvalidationScope scope)
     : core_(core),
-      invalidation_service_provider_(invalidation_service_provider) {
+      invalidation_service_provider_(invalidation_service_provider),
+      scope_(scope) {
   invalidation_service_provider_->RegisterConsumer(this);
 }
 
@@ -29,7 +32,8 @@
   }
   // Create a new one if required.
   if (invalidation_service) {
-    invalidator_.reset(new RemoteCommandsInvalidatorImpl(core_));
+    invalidator_.reset(new RemoteCommandsInvalidatorImpl(
+        core_, base::DefaultClock::GetInstance(), scope_));
     invalidator_->Initialize(invalidation_service);
   }
 }
diff --git a/chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.h b/chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.h
index 53decf6..ba2a718b 100644
--- a/chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.h
+++ b/chrome/browser/chromeos/policy/remote_commands/affiliated_remote_commands_invalidator.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/chromeos/policy/affiliated_invalidation_service_provider.h"
+#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
 
 namespace policy {
 
@@ -22,7 +23,8 @@
  public:
   AffiliatedRemoteCommandsInvalidator(
       CloudPolicyCore* core,
-      AffiliatedInvalidationServiceProvider* invalidation_service_provider);
+      AffiliatedInvalidationServiceProvider* invalidation_service_provider,
+      PolicyInvalidationScope scope);
   ~AffiliatedRemoteCommandsInvalidator() override;
 
   // AffiliatedInvalidationServiceProvider::Consumer:
@@ -35,6 +37,8 @@
 
   std::unique_ptr<RemoteCommandsInvalidatorImpl> invalidator_;
 
+  const PolicyInvalidationScope scope_;
+
   DISALLOW_COPY_AND_ASSIGN(AffiliatedRemoteCommandsInvalidator);
 };
 
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index 30ce5f9..db49b69 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -2816,7 +2816,8 @@
   // Create a test uploads.log file in the legacy CSV format. All such kind of
   // record will be ignored because the required source filed is not existing.
   base::Time timestamp = base::Time::Now() - base::TimeDelta::FromHours(1);
-  std::string test_entry = base::StringPrintf("%" PRId64, timestamp.ToTimeT());
+  std::string test_entry =
+      base::StringPrintf("%" PRId64, static_cast<int64_t>(timestamp.ToTimeT()));
   test_entry += ",";
   test_entry.append(kTestUploadId);
   test_entry += ",";
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
index 6cb3ea5..1715b592 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -17,6 +17,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
+#include "base/time/default_clock.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/helper.h"
@@ -794,7 +795,9 @@
 
   core()->StartRemoteCommandsService(
       std::make_unique<UserCommandsFactoryChromeOS>(profile_));
-  invalidator_ = std::make_unique<RemoteCommandsInvalidatorImpl>(core());
+  invalidator_ = std::make_unique<RemoteCommandsInvalidatorImpl>(
+      core(), base::DefaultClock::GetInstance(),
+      PolicyInvalidationScope::kUser);
 
   invalidator_->Initialize(
       invalidation_provider->GetInvalidationServiceForCustomSender(
diff --git a/chrome/browser/downgrade/snapshot_manager.cc b/chrome/browser/downgrade/snapshot_manager.cc
index 43532f06c..fa211cf 100644
--- a/chrome/browser/downgrade/snapshot_manager.cc
+++ b/chrome/browser/downgrade/snapshot_manager.cc
@@ -23,6 +23,13 @@
 
 namespace {
 
+constexpr base::FilePath::StringPieceType kSQLiteJournalSuffix(
+    FILE_PATH_LITERAL("-journal"));
+constexpr base::FilePath::StringPieceType kSQLiteWalSuffix(
+    FILE_PATH_LITERAL("-wal"));
+constexpr base::FilePath::StringPieceType kSQLiteShmSuffix(
+    FILE_PATH_LITERAL("-shm"));
+
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
 enum class SnapshotOperationResult {
@@ -34,25 +41,45 @@
 };
 
 // Copies the item at |user_data_dir|/|relative_path| to
-// |snapshot_dir|/|relative_path| if the item exists.
-// Returns |true| if the item was found at the source and successfully copied.
-// Returns |false| if the item was found at the source but not successfully
-// copied. Returns no value if the file was not at the source.
+// |snapshot_dir|/|relative_path| if the item exists. This also copies all files
+// related to items that are SQLite databases. Returns |true| if the item was
+// found at the source and successfully copied. Returns |false| if the item was
+// found at the source but not successfully copied. Returns no value if the file
+// was not at the source.
 base::Optional<bool> CopyItemToSnapshotDirectory(
     const base::FilePath& relative_path,
     const base::FilePath& user_data_dir,
     const base::FilePath& snapshot_dir,
     bool is_directory) {
-  auto source = user_data_dir.Append(relative_path);
-  auto destination = snapshot_dir.Append(relative_path);
+  const auto source = user_data_dir.Append(relative_path);
+  const auto destination = snapshot_dir.Append(relative_path);
 
   // If nothing exists to be moved, do not consider it a success or a failure.
   if (!base::PathExists(source))
     return base::nullopt;
 
-  return is_directory
-             ? base::CopyDirectory(source, destination, /*recursive=*/true)
-             : base::CopyFile(source, destination);
+  bool copy_success = is_directory ? base::CopyDirectory(source, destination,
+                                                         /*recursive=*/true)
+                                   : base::CopyFile(source, destination);
+
+  if (is_directory)
+    return copy_success;
+
+  // Copy SQLite journal, WAL and SHM files associated with the files that are
+  // snapshotted if they exist.
+  for (const auto& suffix :
+       {kSQLiteJournalSuffix, kSQLiteWalSuffix, kSQLiteShmSuffix}) {
+    const auto sqlite_file_path =
+        base::FilePath(source.value() + base::FilePath::StringType(suffix));
+    if (!base::PathExists(sqlite_file_path))
+      continue;
+
+    const auto destination_journal = base::FilePath(
+        destination.value() + base::FilePath::StringType(suffix));
+    copy_success &= base::CopyFile(sqlite_file_path, destination_journal);
+  }
+
+  return copy_success;
 }
 
 // Returns true if |base_name| matches a user profile directory's format. This
diff --git a/chrome/browser/downgrade/snapshot_manager_unittest.cc b/chrome/browser/downgrade/snapshot_manager_unittest.cc
index 61f77f3..2f017dcb 100644
--- a/chrome/browser/downgrade/snapshot_manager_unittest.cc
+++ b/chrome/browser/downgrade/snapshot_manager_unittest.cc
@@ -37,6 +37,14 @@
     FILE_PATH_LITERAL("User Data File");
 constexpr base::FilePath::StringPieceType kProfileDataFile =
     FILE_PATH_LITERAL("Profile Data File");
+constexpr base::FilePath::StringPieceType kProfileDataJournalFile =
+    FILE_PATH_LITERAL("Profile Data File-journal");
+constexpr base::FilePath::StringPieceType kProfileDataExtFile =
+    FILE_PATH_LITERAL("Profile Data File.ext");
+constexpr base::FilePath::StringPieceType kProfileDataExtWalFile =
+    FILE_PATH_LITERAL("Profile Data File.ext-wal");
+constexpr base::FilePath::StringPieceType kProfileDataExtShmFile =
+    FILE_PATH_LITERAL("Profile Data File.ext-shm");
 
 constexpr std::array<base::FilePath::StringPieceType, 3>
     kProfileDirectoryBaseNames = {FILE_PATH_LITERAL("Default"),
@@ -120,6 +128,8 @@
     return std::vector<SnapshotItemDetails>{
         SnapshotItemDetails(base::FilePath(kProfileDataFile),
                             SnapshotItemDetails::ItemType::kFile, 0),
+        SnapshotItemDetails(base::FilePath(kProfileDataExtFile),
+                            SnapshotItemDetails::ItemType::kFile, 0),
         SnapshotItemDetails(base::FilePath(kProfileDataFolder),
                             SnapshotItemDetails::ItemType::kDirectory, 0)};
   }
@@ -175,6 +185,15 @@
     // Files and folders at Profile Data level that should be snapshotted.
     base::File file(path.Append(kProfileDataFile),
                     base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    base::File file_ext(path.Append(kProfileDataExtFile),
+                        base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    base::File file_journal(path.Append(kProfileDataJournalFile),
+                            base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    base::File file_ext_wal(path.Append(kProfileDataExtWalFile),
+                            base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    base::File file_ext_shm(path.Append(kProfileDataExtShmFile),
+                            base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+
     ASSERT_NO_FATAL_FAILURE(TestFolderAndFiles::CreateFilesAndFolders(
         path.Append(kProfileDataFolder)));
 
@@ -196,6 +215,10 @@
 
   for (const auto& path : absolute_profile_paths) {
     EXPECT_TRUE(base::PathExists(path.Append(kProfileDataFile)));
+    EXPECT_TRUE(base::PathExists(path.Append(kProfileDataExtFile)));
+    EXPECT_TRUE(base::PathExists(path.Append(kProfileDataJournalFile)));
+    EXPECT_TRUE(base::PathExists(path.Append(kProfileDataExtWalFile)));
+    EXPECT_TRUE(base::PathExists(path.Append(kProfileDataExtShmFile)));
     EXPECT_TRUE(base::DirectoryExists(path.Append(kProfileDataFolder)));
     EXPECT_TRUE(TestFolderAndFiles::AllPathExists(path));
   }
@@ -203,6 +226,14 @@
   for (const auto& path : kProfileDirectoryBaseNames) {
     EXPECT_TRUE(
         base::PathExists(snapshot_dir.Append(path).Append(kProfileDataFile)));
+    EXPECT_TRUE(base::PathExists(
+        snapshot_dir.Append(path).Append(kProfileDataJournalFile)));
+    EXPECT_TRUE(base::PathExists(
+        snapshot_dir.Append(path).Append(kProfileDataExtFile)));
+    EXPECT_TRUE(base::PathExists(
+        snapshot_dir.Append(path).Append(kProfileDataExtWalFile)));
+    EXPECT_TRUE(base::PathExists(
+        snapshot_dir.Append(path).Append(kProfileDataExtShmFile)));
     EXPECT_TRUE(base::DirectoryExists(
         snapshot_dir.Append(path).Append(kProfileDataFolder)));
     EXPECT_TRUE(TestFolderAndFiles::NoPathExists(snapshot_dir.Append(path)));
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
index e7be3c24..dc0cb5e 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
@@ -142,7 +142,8 @@
                                         &saved_passwords_presenter_),
       bulk_leak_check_service_adapter_(
           &saved_passwords_presenter_,
-          BulkLeakCheckServiceFactory::GetForProfile(profile_)) {
+          BulkLeakCheckServiceFactory::GetForProfile(profile_),
+          profile_->GetPrefs()) {
   observed_saved_passwords_presenter_.Add(&saved_passwords_presenter_);
   observed_compromised_credentials_provider_.Add(
       &compromised_credentials_provider_);
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc
index ea89dc6e..d8d4d0b 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc
@@ -6,6 +6,8 @@
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h"
+#include "chrome/browser/password_manager/bulk_leak_check_service_factory.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "extensions/browser/extension_system_provider.h"
@@ -25,17 +27,19 @@
 // static
 PasswordsPrivateDelegateFactory*
     PasswordsPrivateDelegateFactory::GetInstance() {
-  return base::Singleton<PasswordsPrivateDelegateFactory>::get();
+  static base::NoDestructor<PasswordsPrivateDelegateFactory> instance;
+  return instance.get();
 }
 
 PasswordsPrivateDelegateFactory::PasswordsPrivateDelegateFactory()
     : BrowserContextKeyedServiceFactory(
           "PasswordsPrivateDelegate",
           BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(BulkLeakCheckServiceFactory::GetInstance());
+  DependsOn(PasswordStoreFactory::GetInstance());
 }
 
-PasswordsPrivateDelegateFactory::~PasswordsPrivateDelegateFactory() {
-}
+PasswordsPrivateDelegateFactory::~PasswordsPrivateDelegateFactory() = default;
 
 KeyedService* PasswordsPrivateDelegateFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
index e28c3bf..7afbd345 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
@@ -5,8 +5,7 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_FACTORY_H_
 #define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_FACTORY_H_
 
-#include "base/macros.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
 namespace context {
@@ -27,8 +26,7 @@
   static PasswordsPrivateDelegateFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<PasswordsPrivateDelegateFactory>;
-
+  friend class base::NoDestructor<PasswordsPrivateDelegateFactory>;
   PasswordsPrivateDelegateFactory();
   ~PasswordsPrivateDelegateFactory() override;
 
@@ -37,8 +35,6 @@
       content::BrowserContext* profile) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
   bool ServiceIsNULLWhileTesting() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateDelegateFactory);
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/favicon/history_ui_favicon_request_handler_factory.cc b/chrome/browser/favicon/history_ui_favicon_request_handler_factory.cc
index 2c09a117a..5b46af7 100644
--- a/chrome/browser/favicon/history_ui_favicon_request_handler_factory.cc
+++ b/chrome/browser/favicon/history_ui_favicon_request_handler_factory.cc
@@ -11,27 +11,14 @@
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/sync/session_sync_service_factory.h"
 #include "components/favicon/core/history_ui_favicon_request_handler_impl.h"
-#include "components/favicon_base/favicon_types.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_service_utils.h"
-#include "components/sync_sessions/open_tabs_ui_delegate.h"
-#include "components/sync_sessions/session_sync_service.h"
 #include "content/public/browser/browser_context.h"
 
 namespace {
 
-favicon_base::FaviconRawBitmapResult GetSyncedFaviconForPageUrl(
-    sync_sessions::SessionSyncService* session_sync_service,
-    const GURL& page_url) {
-  sync_sessions::OpenTabsUIDelegate* open_tabs =
-      session_sync_service->GetOpenTabsUIDelegate();
-  return open_tabs ? open_tabs->GetSyncedFaviconForPageURL(page_url)
-                   : favicon_base::FaviconRawBitmapResult();
-}
-
 bool CanSendHistoryData(syncer::SyncService* sync_service) {
   return syncer::GetUploadToGoogleState(sync_service,
                                         syncer::ModelType::SESSIONS) ==
@@ -60,7 +47,6 @@
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(FaviconServiceFactory::GetInstance());
   DependsOn(LargeIconServiceFactory::GetInstance());
-  DependsOn(SessionSyncServiceFactory::GetInstance());
   DependsOn(ProfileSyncServiceFactory::GetInstance());
 }
 
@@ -77,8 +63,6 @@
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
   return new favicon::HistoryUiFaviconRequestHandlerImpl(
-      base::BindRepeating(&GetSyncedFaviconForPageUrl,
-                          SessionSyncServiceFactory::GetForProfile(profile)),
       base::BindRepeating(&CanSendHistoryData,
                           ProfileSyncServiceFactory::GetForProfile(profile)),
       FaviconServiceFactory::GetForProfile(profile,
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 2d10673..d510c1c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -284,6 +284,11 @@
     "expiry_milestone": 73
   },
   {
+    "name": "autofill-ios-delay-between-fields",
+    "owners": [ "olivierrobin" ],
+    "expiry_milestone": 86
+  },
+  {
     "name": "autofill-keyboard-accessory-view",
     "owners": [ "fhorschig@chromium.org" ],
     "expiry_milestone": 83
@@ -344,6 +349,11 @@
     "expiry_milestone": 84
   },
   {
+    "name": "autofill-show-all-profiles-on-prefilled-forms",
+    "owners": [ "olivierrobin" ],
+    "expiry_milestone": 86
+  },
+  {
     "name": "autofill-use-improved-label-disambiguation",
     "owners": [ "fhorschig" ],
     "expiry_milestone": 83
@@ -3109,6 +3119,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "open-downloads-in-files.app",
+    "owners": [ "ewannpv", "gambard" ],
+    "expiry_milestone": 86
+  },
+  {
     "name": "overlay-new-layout",
     "owners": [ "donnd", "jinsukkim", "contextual-search-eng" ],
     "expiry_milestone": 81
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index f3e6c311..67ec332 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -157,7 +157,6 @@
     &kHorizontalTabSwitcherAndroid,
     &kImmersiveUiMode,
     &kInlineUpdateFlow,
-    &kIntentBlockExternalFormRedirectsNoGesture,
     &kKitKatSupported,
     &kNewPhotoPicker,
     &kNotificationSuspender,
@@ -458,10 +457,6 @@
 const base::Feature kInlineUpdateFlow{"InlineUpdateFlow",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kIntentBlockExternalFormRedirectsNoGesture{
-    "IntentBlockExternalFormRedirectsNoGesture",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kKitKatSupported{"KitKatSupported",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 70aa2fc..21815f7 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -78,7 +78,6 @@
 extern const base::Feature kImmersiveUiMode;
 extern const base::Feature kImprovedA2HS;
 extern const base::Feature kInlineUpdateFlow;
-extern const base::Feature kIntentBlockExternalFormRedirectsNoGesture;
 extern const base::Feature kKitKatSupported;
 extern const base::Feature kLanguagesPreference;
 extern const base::Feature kNewPhotoPicker;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 597c893..b14928cb5 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -310,8 +310,6 @@
             "ImprovedCookieControlsForThirdPartyCookieBlocking";
     public static final String INLINE_UPDATE_FLOW = "InlineUpdateFlow";
     public static final String INSTALLABLE_AMBIENT_BADGE_INFOBAR = "InstallableAmbientBadgeInfoBar";
-    public static final String INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE =
-            "IntentBlockExternalFormRedirectsNoGesture";
     public static final String INTEREST_FEED_CONTENT_SUGGESTIONS = "InterestFeedContentSuggestions";
     public static final String INTEREST_FEED_FEEDBACK = "InterestFeedFeedback";
     public static final String KITKAT_SUPPORTED = "KitKatSupported";
diff --git a/chrome/browser/media/feeds/media_feeds_browsertest.cc b/chrome/browser/media/feeds/media_feeds_browsertest.cc
index eabe0572..076d0fe 100644
--- a/chrome/browser/media/feeds/media_feeds_browsertest.cc
+++ b/chrome/browser/media/feeds/media_feeds_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/media/feeds/media_feeds_contents_observer.h"
 #include "chrome/browser/media/history/media_history_feeds_table.h"
 #include "chrome/browser/media/history/media_history_keyed_service.h"
@@ -123,7 +124,14 @@
             "<link rel=other type=\"application/ld+json\" href=\"/test\"/>",
             false}));
 
-IN_PROC_BROWSER_TEST_P(MediaFeedsBrowserTest, Discover) {
+// Crashes on Mac/Win only.  http://crbug.com/1060626
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#define MAYBE_Discover DISABLED_Discover
+#else
+#define MAYBE_Discover Discover
+#endif
+
+IN_PROC_BROWSER_TEST_P(MediaFeedsBrowserTest, MAYBE_Discover) {
   EXPECT_TRUE(GetDiscoveredFeedURLs().empty());
 
   MediaFeedsContentsObserver* contents_observer =
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
index 70ced8d6..9ae80a2 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -197,7 +197,7 @@
     OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
         ->RegisterOptimizationTypesAndTargets(
             {optimization_guide::proto::NOSCRIPT},
-            /*optimization_targets=*/{});
+            {optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD});
 
     // Set up an OptimizationGuideKeyedService consumer.
     consumer_.reset(new OptimizationGuideConsumerWebContentsObserver(
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
index 338ad01..d2bc264 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -255,6 +255,7 @@
   }
 
   void SetUpCommandLine(base::CommandLine* cmd) override {
+    cmd->AppendSwitch("enable-spdy-proxy-auth");
     cmd->AppendSwitch(optimization_guide::switches::
                           kFetchModelsAndHostModelFeaturesOverrideTimer);
 
diff --git a/chrome/browser/password_manager/password_manager_test_base.cc b/chrome/browser/password_manager/password_manager_test_base.cc
index ba72faa..08aaaea 100644
--- a/chrome/browser/password_manager/password_manager_test_base.cc
+++ b/chrome/browser/password_manager/password_manager_test_base.cc
@@ -19,16 +19,17 @@
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/password_manager/core/browser/mock_password_feature_manager.h"
-#include "components/password_manager/core/browser/password_feature_manager.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/test_password_store.h"
+#include "components/sync/driver/test_sync_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/navigation_handle.h"
@@ -45,56 +46,10 @@
 
 namespace {
 
-// A helper class that synchronously waits until the password store handles a
-// GetLogins() request.
-class PasswordStoreResultsObserver
-    : public password_manager::PasswordStoreConsumer {
- public:
-  PasswordStoreResultsObserver() = default;
-
-  void OnGetPasswordStoreResults(
-      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override {
-    run_loop_.Quit();
-  }
-
-  void Wait() { run_loop_.Run(); }
-
- private:
-  base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(PasswordStoreResultsObserver);
-};
-
-// Custom class is required to enable password generation.
-class CustomPasswordManagerClient : public ChromePasswordManagerClient {
- public:
-  using ChromePasswordManagerClient::ChromePasswordManagerClient;
-  CustomPasswordManagerClient(content::WebContents* contents,
-                              autofill::AutofillClient* autofill_client)
-      : ChromePasswordManagerClient(contents, autofill_client) {
-    ON_CALL(password_feature_manager_, IsGenerationEnabled())
-        .WillByDefault(testing::Return(true));
-  }
-
-  static void CreateForWebContentsWithAutofillClient(
-      content::WebContents* contents,
-      autofill::AutofillClient* autofill_client) {
-    ASSERT_FALSE(FromWebContents(contents));
-    contents->SetUserData(UserDataKey(),
-                          std::make_unique<CustomPasswordManagerClient>(
-                              contents, autofill_client));
-  }
-
-  // PasswordManagerClient:
-  password_manager::PasswordFeatureManager* GetPasswordFeatureManager()
-      override {
-    return &password_feature_manager_;
-  }
-
- private:
-  testing::NiceMock<password_manager::MockPasswordFeatureManager>
-      password_feature_manager_;
-};
+std::unique_ptr<KeyedService> BuildTestSyncService(
+    content::BrowserContext* context) {
+  return std::make_unique<syncer::TestSyncService>();
+}
 
 // ManagePasswordsUIController subclass to capture the UI events.
 class CustomManagePasswordsUIController : public ManagePasswordsUIController {
@@ -435,6 +390,21 @@
   return controller->WaitForFallbackForSaving(timeout);
 }
 
+PasswordStoreResultsObserver::PasswordStoreResultsObserver() = default;
+PasswordStoreResultsObserver::~PasswordStoreResultsObserver() = default;
+
+void PasswordStoreResultsObserver::OnGetPasswordStoreResults(
+    std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+  results_ = std::move(results);
+  run_loop_.Quit();
+}
+
+std::vector<std::unique_ptr<autofill::PasswordForm>>
+PasswordStoreResultsObserver::WaitForResults() {
+  run_loop_.Run();
+  return std::move(results_);
+}
+
 PasswordManagerBrowserTestBase::PasswordManagerBrowserTestBase()
     : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS),
       web_contents_(nullptr) {}
@@ -468,6 +438,7 @@
   ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
 }
 
+// static
 void PasswordManagerBrowserTestBase::SetUpOnMainThreadAndGetNewTab(
     Browser* browser,
     content::WebContents** web_contents) {
@@ -482,19 +453,26 @@
           &password_manager::BuildPasswordStore<
               content::BrowserContext, password_manager::TestPasswordStore>));
 
+  GetNewTab(browser, web_contents);
+}
+
+// static
+void PasswordManagerBrowserTestBase::GetNewTab(
+    Browser* browser,
+    content::WebContents** web_contents) {
   // Add a tab with a customized ManagePasswordsUIController. Thus, we can
   // intercept useful UI events.
-  content::WebContents* tab =
+  content::WebContents* preexisting_tab =
       browser->tab_strip_model()->GetActiveWebContents();
   std::unique_ptr<content::WebContents> owned_web_contents =
       content::WebContents::Create(
-          content::WebContents::CreateParams(tab->GetBrowserContext()));
+          content::WebContents::CreateParams(browser->profile()));
   *web_contents = owned_web_contents.get();
   ASSERT_TRUE(*web_contents);
 
   // ManagePasswordsUIController needs ChromePasswordManagerClient for logging.
   autofill::ChromeAutofillClient::CreateForWebContents(*web_contents);
-  CustomPasswordManagerClient::CreateForWebContentsWithAutofillClient(
+  ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
       *web_contents,
       autofill::ChromeAutofillClient::FromWebContents(*web_contents));
   ASSERT_TRUE(ChromePasswordManagerClient::FromWebContents(*web_contents));
@@ -502,20 +480,24 @@
       new CustomManagePasswordsUIController(*web_contents);
   browser->tab_strip_model()->AppendWebContents(std::move(owned_web_contents),
                                                 true);
-  browser->tab_strip_model()->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
+  if (preexisting_tab) {
+    browser->tab_strip_model()->CloseWebContentsAt(0,
+                                                   TabStripModel::CLOSE_NONE);
+  }
   ASSERT_EQ(controller,
             ManagePasswordsUIController::FromWebContents(*web_contents));
   ASSERT_EQ(*web_contents, browser->tab_strip_model()->GetActiveWebContents());
   ASSERT_FALSE((*web_contents)->IsLoading());
 }
 
+// static
 void PasswordManagerBrowserTestBase::WaitForPasswordStore(Browser* browser) {
   scoped_refptr<password_manager::PasswordStore> password_store =
       PasswordStoreFactory::GetForProfile(browser->profile(),
                                           ServiceAccessType::IMPLICIT_ACCESS);
   PasswordStoreResultsObserver syncer;
   password_store->GetAllLoginsWithAffiliationAndBrandingInformation(&syncer);
-  syncer.Wait();
+  syncer.WaitForResults();
 }
 
 content::WebContents* PasswordManagerBrowserTestBase::WebContents() const {
@@ -681,6 +663,23 @@
   EXPECT_EQ(expected_value, return_value) << "element_id = " << element_id;
 }
 
+void PasswordManagerBrowserTestBase::SetUpInProcessBrowserTestFixture() {
+  will_create_browser_context_services_subscription_ =
+      BrowserContextDependencyManager::GetInstance()
+          ->RegisterWillCreateBrowserContextServicesCallbackForTesting(
+              base::BindRepeating(&PasswordManagerBrowserTestBase::
+                                      OnWillCreateBrowserContextServices));
+}
+
+// static
+void PasswordManagerBrowserTestBase::OnWillCreateBrowserContextServices(
+    content::BrowserContext* context) {
+  // Set up a TestSyncService which will happily return "everything is active"
+  // so that password generation is considered enabled.
+  ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(
+      context, base::BindRepeating(&BuildTestSyncService));
+}
+
 void PasswordManagerBrowserTestBase::AddHSTSHost(const std::string& host) {
   network::mojom::NetworkContext* network_context =
       content::BrowserContext::GetDefaultStoragePartition(browser()->profile())
diff --git a/chrome/browser/password_manager/password_manager_test_base.h b/chrome/browser/password_manager/password_manager_test_base.h
index 7bf799c..d5735a7 100644
--- a/chrome/browser/password_manager/password_manager_test_base.h
+++ b/chrome/browser/password_manager/password_manager_test_base.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/callback_list.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "chrome/browser/ssl/cert_verifier_browser_test.h"
@@ -134,12 +135,34 @@
   DISALLOW_COPY_AND_ASSIGN(BubbleObserver);
 };
 
+// A helper class that synchronously waits until the password store handles a
+// GetLogins() request.
+class PasswordStoreResultsObserver
+    : public password_manager::PasswordStoreConsumer {
+ public:
+  PasswordStoreResultsObserver();
+  ~PasswordStoreResultsObserver() override;
+
+  // Waits for OnGetPasswordStoreResults() and returns the result.
+  std::vector<std::unique_ptr<autofill::PasswordForm>> WaitForResults();
+
+ private:
+  void OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+
+  base::RunLoop run_loop_;
+  std::vector<std::unique_ptr<autofill::PasswordForm>> results_;
+
+  DISALLOW_COPY_AND_ASSIGN(PasswordStoreResultsObserver);
+};
+
 class PasswordManagerBrowserTestBase : public CertVerifierBrowserTest {
  public:
   PasswordManagerBrowserTestBase();
   ~PasswordManagerBrowserTestBase() override;
 
   // InProcessBrowserTest:
+  void SetUpInProcessBrowserTestFixture() override;
   void SetUpOnMainThread() override;
   void TearDownOnMainThread() override;
 
@@ -151,6 +174,11 @@
   static void SetUpOnMainThreadAndGetNewTab(
       Browser* browser,
       content::WebContents** web_contents);
+
+  // Creates a new tab with all the password manager test hooks and returns it
+  // in |web_contents|.
+  static void GetNewTab(Browser* browser, content::WebContents** web_contents);
+
   // Make sure that the password store associated with the given browser
   // processed all the previous calls, calls executed on another thread.
   static void WaitForPasswordStore(Browser* browser);
@@ -217,9 +245,17 @@
   net::EmbeddedTestServer& https_test_server() { return https_test_server_; }
 
  private:
+  static void OnWillCreateBrowserContextServices(
+      content::BrowserContext* context);
+
   net::EmbeddedTestServer https_test_server_;
   // A tab with some hooks injected.
   content::WebContents* web_contents_;
+
+  std::unique_ptr<
+      base::CallbackList<void(content::BrowserContext*)>::Subscription>
+      will_create_browser_context_services_subscription_;
+
   DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTestBase);
 };
 
diff --git a/chrome/browser/payments/android/service_worker_payment_app_bridge.cc b/chrome/browser/payments/android/service_worker_payment_app_bridge.cc
index 3f5cc9b1..e7b5e7b 100644
--- a/chrome/browser/payments/android/service_worker_payment_app_bridge.cc
+++ b/chrome/browser/payments/android/service_worker_payment_app_bridge.cc
@@ -423,7 +423,10 @@
       content::WebContents::FromJavaWebContents(jweb_contents);
 
   payments::ServiceWorkerPaymentAppFinder::GetInstance()->GetAllPaymentApps(
-      url::Origin::FromJavaObject(jorigin), web_contents,
+      url::Origin::FromJavaObject(jorigin),
+      // TODO(crbug.com/1055360): plumb the RenderFrameHost from Java side.
+      nullptr, /* initiator_render_frame_host */
+      web_contents,
       WebDataServiceFactory::GetPaymentManifestWebDataForProfile(
           Profile::FromBrowserContext(web_contents->GetBrowserContext()),
           ServiceAccessType::EXPLICIT_ACCESS),
diff --git a/chrome/browser/payments/service_worker_payment_app_finder_browsertest.cc b/chrome/browser/payments/service_worker_payment_app_finder_browsertest.cc
index 548f1a8d876..452436be 100644
--- a/chrome/browser/payments/service_worker_payment_app_finder_browsertest.cc
+++ b/chrome/browser/payments/service_worker_payment_app_finder_browsertest.cc
@@ -185,7 +185,8 @@
 
     base::RunLoop run_loop;
     ServiceWorkerPaymentAppFinder::GetInstance()->GetAllPaymentApps(
-        url::Origin::Create(GURL("https://chromium.org")), web_contents,
+        url::Origin::Create(GURL("https://chromium.org")),
+        web_contents->GetMainFrame(), web_contents,
         WebDataServiceFactory::GetPaymentManifestWebDataForProfile(
             Profile::FromBrowserContext(context),
             ServiceAccessType::EXPLICIT_ACCESS),
diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator.cc b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc
index 83683d7..0991541 100644
--- a/chrome/browser/policy/cloud/cloud_policy_invalidator.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc
@@ -89,8 +89,6 @@
 const int CloudPolicyInvalidator::kMaxFetchDelayMin = 1000;
 const int CloudPolicyInvalidator::kMaxFetchDelayMax = 300000;
 const int CloudPolicyInvalidator::kInvalidationGracePeriod = 10;
-const int CloudPolicyInvalidator::kUnknownVersionIgnorePeriod = 30;
-const int CloudPolicyInvalidator::kMaxInvalidationTimeDelta = 300;
 
 // static
 const char* CloudPolicyInvalidator::GetPolicyRefreshMetricName(
@@ -334,7 +332,11 @@
   }
 
   // Ignore the invalidation if it is expired.
-  const bool is_expired = IsInvalidationExpired(version);
+  const auto last_fetch_time =
+      base::Time::FromJavaTime(core_->store()->policy()->timestamp());
+  const auto current_time = clock_->Now();
+  const bool is_expired =
+      IsInvalidationExpired(invalidation, last_fetch_time, current_time);
   const bool is_missing_payload = payload.empty();
 
   RecordPolicyInvalidationMetric(scope_, is_expired, is_missing_payload);
@@ -494,27 +496,6 @@
   return changed;
 }
 
-bool CloudPolicyInvalidator::IsInvalidationExpired(int64_t version) {
-  base::Time last_fetch_time =
-      base::Time::FromJavaTime(core_->store()->policy()->timestamp());
-
-  // If the version is unknown, consider the invalidation invalid if the
-  // policy was fetched very recently.
-  if (version < 0) {
-    base::TimeDelta elapsed = clock_->Now() - last_fetch_time;
-    return elapsed.InSeconds() < kUnknownVersionIgnorePeriod;
-  }
-
-  // The invalidation version is the timestamp in microseconds. If the
-  // invalidation occurred before the last policy fetch, then the invalidation
-  // is expired. Time is added to the invalidation to err on the side of not
-  // expired.
-  base::Time invalidation_time = base::Time::UnixEpoch() +
-      base::TimeDelta::FromMicroseconds(version) +
-      base::TimeDelta::FromSeconds(kMaxInvalidationTimeDelta);
-  return invalidation_time < last_fetch_time;
-}
-
 bool CloudPolicyInvalidator::GetInvalidationsEnabled() {
   if (!invalidations_enabled_)
     return false;
diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator.h b/chrome/browser/policy/cloud/cloud_policy_invalidator.h
index bc3c6ba5..a441905 100644
--- a/chrome/browser/policy/cloud/cloud_policy_invalidator.h
+++ b/chrome/browser/policy/cloud/cloud_policy_invalidator.h
@@ -53,14 +53,6 @@
   // once the invalidation service starts up.
   static const int kInvalidationGracePeriod;
 
-  // Time, in seconds, for which unknown version invalidations are ignored after
-  // fetching a policy.
-  static const int kUnknownVersionIgnorePeriod;
-
-  // The max tolerated discrepancy, in seconds, between policy timestamps and
-  // invalidation timestamps when determining if an invalidation is expired.
-  static const int kMaxInvalidationTimeDelta;
-
   // Returns a name of a refresh metric associated with the given scope.
   static const char* GetPolicyRefreshMetricName(PolicyInvalidationScope scope);
   // Returns a name of a FCM refresh metric associated with the given scope.
@@ -170,10 +162,6 @@
   // previous call.
   bool IsPolicyChanged(const enterprise_management::PolicyData* policy);
 
-  // Determine if an invalidation has expired.
-  // |version| is the version of the invalidation, or zero for unknown.
-  bool IsInvalidationExpired(int64_t version);
-
   // Determine if invalidations have been enabled longer than the grace period.
   bool GetInvalidationsEnabled();
 
diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator_unittest.cc b/chrome/browser/policy/cloud/cloud_policy_invalidator_unittest.cc
index f9ca311..b784b33 100644
--- a/chrome/browser/policy/cloud/cloud_policy_invalidator_unittest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_invalidator_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/policy/cloud/policy_invalidation_util.h"
 #include "chrome/browser/policy/cloud/user_cloud_policy_invalidator.h"
 #include "components/invalidation/impl/fake_invalidation_service.h"
 #include "components/invalidation/impl/invalidator_registrar_with_memory.h"
@@ -1055,8 +1056,8 @@
 
   // Invalidations fired before the last fetch time (adjusted by max time delta)
   // should be ignored.
-  base::Time time = Now() - base::TimeDelta::FromSeconds(
-      CloudPolicyInvalidator::kMaxInvalidationTimeDelta + 300);
+  base::Time time = Now() - (invalidation_timeouts::kMaxInvalidationTimeDelta +
+                             base::TimeDelta::FromSeconds(300));
   syncer::Invalidation inv =
       FireInvalidation(POLICY_OBJECT_A, GetVersion(time), "test");
   ASSERT_TRUE(IsInvalidationAcknowledged(inv));
@@ -1089,8 +1090,8 @@
   ASSERT_TRUE(IsInvalidationAcknowledged(inv));
   ASSERT_TRUE(CheckPolicyNotRefreshed());
 
-  AdvanceClock(base::TimeDelta::FromSeconds(
-      CloudPolicyInvalidator::kUnknownVersionIgnorePeriod - 1));
+  AdvanceClock(invalidation_timeouts::kUnknownVersionIgnorePeriod -
+               base::TimeDelta::FromSeconds(1));
   inv = FireUnknownVersionInvalidation(POLICY_OBJECT_A);
   ASSERT_TRUE(IsInvalidationAcknowledged(inv));
   ASSERT_TRUE(CheckPolicyNotRefreshed());
diff --git a/chrome/browser/policy/cloud/policy_invalidation_util.cc b/chrome/browser/policy/cloud/policy_invalidation_util.cc
index 3b30c5c..408dfe4 100644
--- a/chrome/browser/policy/cloud/policy_invalidation_util.cc
+++ b/chrome/browser/policy/cloud/policy_invalidation_util.cc
@@ -43,4 +43,36 @@
   return true;
 }
 
+bool IsInvalidationExpired(const syncer::Invalidation& invalidation,
+                           const base::Time& last_fetch_time,
+                           const base::Time& current_time) {
+  // If the version is unknown, consider the invalidation invalid if the
+  // fetch was very recently.
+  if (invalidation.is_unknown_version()) {
+    base::TimeDelta elapsed = current_time - last_fetch_time;
+    return elapsed < invalidation_timeouts::kUnknownVersionIgnorePeriod;
+  }
+
+  // The invalidation version is the timestamp in microseconds. If the
+  // invalidation occurred before the last fetch, then the invalidation
+  // is expired.
+  base::Time invalidation_time =
+      base::Time::UnixEpoch() +
+      base::TimeDelta::FromMicroseconds(invalidation.version()) +
+      invalidation_timeouts::kMaxInvalidationTimeDelta;
+  return invalidation_time < last_fetch_time;
+}
+
+PolicyInvalidationType GetInvalidationMetric(bool is_missing_payload,
+                                             bool is_expired) {
+  if (is_expired) {
+    if (is_missing_payload)
+      return POLICY_INVALIDATION_TYPE_NO_PAYLOAD_EXPIRED;
+    return POLICY_INVALIDATION_TYPE_EXPIRED;
+  }
+  if (is_missing_payload)
+    return POLICY_INVALIDATION_TYPE_NO_PAYLOAD;
+  return POLICY_INVALIDATION_TYPE_NORMAL;
+}
+
 }  // namespace policy
diff --git a/chrome/browser/policy/cloud/policy_invalidation_util.h b/chrome/browser/policy/cloud/policy_invalidation_util.h
index f36b2aa..3a69e78f 100644
--- a/chrome/browser/policy/cloud/policy_invalidation_util.h
+++ b/chrome/browser/policy/cloud/policy_invalidation_util.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_POLICY_CLOUD_POLICY_INVALIDATION_UTIL_H_
 #define CHROME_BROWSER_POLICY_CLOUD_POLICY_INVALIDATION_UTIL_H_
 
+#include "base/time/time.h"
 #include "components/invalidation/public/invalidation_util.h"
+#include "components/policy/core/common/cloud/enterprise_metrics.h"
 
 namespace enterprise_management {
 
@@ -13,8 +15,29 @@
 
 }  // namespace enterprise_management
 
+namespace syncer {
+
+class Invalidation;
+
+}  // namespace syncer
+
 namespace policy {
 
+namespace invalidation_timeouts {
+
+// Time for which unknown version invalidations are ignored after
+// fetching a policy or command.
+constexpr base::TimeDelta kUnknownVersionIgnorePeriod =
+    base::TimeDelta::FromSeconds(30);
+
+// The max tolerated discrepancy between policy or remote commands
+// timestamps and invalidation timestamps when determining if an invalidation
+// is expired.
+constexpr base::TimeDelta kMaxInvalidationTimeDelta =
+    base::TimeDelta::FromSeconds(300);
+
+}  // namespace invalidation_timeouts
+
 // Returns true if |topic| is a public topic. Topic can be either public or
 // private. Private topic is keyed by GAIA ID, while public isn't, so many
 // users can be subscribed to the same public topic.
@@ -38,6 +61,15 @@
     const enterprise_management::PolicyData& policy,
     syncer::Topic* topic);
 
+// Determines if an invalidation is expired.
+bool IsInvalidationExpired(const syncer::Invalidation& invalidation,
+                           const base::Time& last_fetch_time,
+                           const base::Time& current_time);
+
+// Returns a metric type depended on invalidation's state.
+PolicyInvalidationType GetInvalidationMetric(bool is_missing_payload,
+                                             bool is_expired);
+
 }  // namespace policy
 
 #endif  // CHROME_BROWSER_POLICY_CLOUD_POLICY_INVALIDATION_UTIL_H_
diff --git a/chrome/browser/policy/cloud/remote_commands_invalidator.cc b/chrome/browser/policy/cloud/remote_commands_invalidator.cc
index aeb9d29c..89f6328e 100644
--- a/chrome/browser/policy/cloud/remote_commands_invalidator.cc
+++ b/chrome/browser/policy/cloud/remote_commands_invalidator.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "chrome/browser/policy/cloud/policy_invalidation_util.h"
 #include "components/invalidation/public/invalidation.h"
 #include "components/invalidation/public/invalidation_service.h"
@@ -14,6 +15,7 @@
 #include "components/invalidation/public/invalidator_state.h"
 #include "components/invalidation/public/single_object_invalidation_set.h"
 #include "components/invalidation/public/topic_invalidation_map.h"
+#include "components/policy/core/common/cloud/enterprise_metrics.h"
 
 namespace policy {
 
@@ -94,7 +96,7 @@
   for (const auto& it : list)
     it.Acknowledge();
 
-  DoRemoteCommandsFetch();
+  DoRemoteCommandsFetch(list.back());
 }
 
 std::string RemoteCommandsInvalidator::GetOwnerName() const {
@@ -139,8 +141,11 @@
   UpdateInvalidationsEnabled();
 
   // Update subscription with the invalidation service.
-  CHECK(
-      invalidation_service_->UpdateInterestedTopics(this, /*topics=*/{topic}));
+  const bool success =
+      invalidation_service_->UpdateInterestedTopics(this, /*topics=*/{topic});
+  base::UmaHistogramBoolean(kMetricRemoteCommandInvalidationsRegistrationResult,
+                            success);
+  CHECK(success);
 }
 
 void RemoteCommandsInvalidator::Unregister() {
diff --git a/chrome/browser/policy/cloud/remote_commands_invalidator.h b/chrome/browser/policy/cloud/remote_commands_invalidator.h
index 7ea35436..21f2a39 100644
--- a/chrome/browser/policy/cloud/remote_commands_invalidator.h
+++ b/chrome/browser/policy/cloud/remote_commands_invalidator.h
@@ -15,6 +15,10 @@
 class InvalidationService;
 }  // namespace invalidation
 
+namespace syncer {
+class Invalidation;
+}  // namespace syncer
+
 namespace policy {
 
 // This class provides basic intefaces for an invalidator for remote commands
@@ -65,7 +69,8 @@
 
   // Subclasses must override this method to implement the actual remote
   // commands fetch.
-  virtual void DoRemoteCommandsFetch() = 0;
+  virtual void DoRemoteCommandsFetch(
+      const syncer::Invalidation& invalidation) = 0;
 
   // Subclasses must call this function to set the topic for remote command
   // invalidations.
diff --git a/chrome/browser/policy/cloud/remote_commands_invalidator_impl.cc b/chrome/browser/policy/cloud/remote_commands_invalidator_impl.cc
index 51fbc5c..deccc464 100644
--- a/chrome/browser/policy/cloud/remote_commands_invalidator_impl.cc
+++ b/chrome/browser/policy/cloud/remote_commands_invalidator_impl.cc
@@ -5,13 +5,37 @@
 #include "chrome/browser/policy/cloud/remote_commands_invalidator_impl.h"
 
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/time/clock.h"
+#include "chrome/browser/policy/cloud/policy_invalidation_util.h"
+#include "components/invalidation/public/invalidation.h"
+#include "components/policy/core/common/cloud/enterprise_metrics.h"
 #include "components/policy/core/common/remote_commands/remote_commands_service.h"
 
 namespace policy {
 
+namespace {
+
+const char* GetInvalidationMetricName(PolicyInvalidationScope scope) {
+  switch (scope) {
+    case PolicyInvalidationScope::kUser:
+      return kMetricUserRemoteCommandInvalidations;
+    case PolicyInvalidationScope::kDevice:
+      return kMetricDeviceRemoteCommandInvalidations;
+    case PolicyInvalidationScope::kDeviceLocalAccount:
+      NOTREACHED() << "Unexpected instance of remote commands invalidator with "
+                      "device local account scope.";
+      return "";
+  }
+}
+
+}  // namespace
+
 RemoteCommandsInvalidatorImpl::RemoteCommandsInvalidatorImpl(
-    CloudPolicyCore* core)
-    : core_(core) {
+    CloudPolicyCore* core,
+    base::Clock* clock,
+    PolicyInvalidationScope scope)
+    : core_(core), clock_(clock), scope_(scope) {
   DCHECK(core_);
 }
 
@@ -34,8 +58,12 @@
   core_->store()->RemoveObserver(this);
 }
 
-void RemoteCommandsInvalidatorImpl::DoRemoteCommandsFetch() {
+void RemoteCommandsInvalidatorImpl::DoRemoteCommandsFetch(
+    const syncer::Invalidation& invalidation) {
   DCHECK(core_->remote_commands_service());
+
+  RecordInvalidationMetric(invalidation);
+
   core_->remote_commands_service()->FetchRemoteCommands();
 }
 
@@ -62,4 +90,20 @@
 void RemoteCommandsInvalidatorImpl::OnStoreError(CloudPolicyStore* core) {
 }
 
+void RemoteCommandsInvalidatorImpl::RecordInvalidationMetric(
+    const syncer::Invalidation& invalidation) const {
+  const auto last_fetch_time =
+      base::Time::FromJavaTime(core_->store()->policy()->timestamp());
+  const auto current_time = clock_->Now();
+  const bool is_expired =
+      IsInvalidationExpired(invalidation, last_fetch_time, current_time);
+  const bool is_missing_payload =
+      invalidation.is_unknown_version() || invalidation.payload().empty();
+
+  base::UmaHistogramEnumeration(
+      GetInvalidationMetricName(scope_),
+      GetInvalidationMetric(is_missing_payload, is_expired),
+      POLICY_INVALIDATION_TYPE_SIZE);
+}
+
 }  // namespace policy
diff --git a/chrome/browser/policy/cloud/remote_commands_invalidator_impl.h b/chrome/browser/policy/cloud/remote_commands_invalidator_impl.h
index aca0aef..b8b2581 100644
--- a/chrome/browser/policy/cloud/remote_commands_invalidator_impl.h
+++ b/chrome/browser/policy/cloud/remote_commands_invalidator_impl.h
@@ -9,6 +9,11 @@
 #include "chrome/browser/policy/cloud/remote_commands_invalidator.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
+
+namespace base {
+class Clock;
+}
 
 namespace policy {
 
@@ -19,14 +24,16 @@
                                       public CloudPolicyCore::Observer,
                                       public CloudPolicyStore::Observer {
  public:
-  explicit RemoteCommandsInvalidatorImpl(CloudPolicyCore* core);
+  RemoteCommandsInvalidatorImpl(CloudPolicyCore* core,
+                                base::Clock* clock,
+                                PolicyInvalidationScope scope);
 
   // RemoteCommandsInvalidator:
   void OnInitialize() override;
   void OnShutdown() override;
   void OnStart() override;
   void OnStop() override;
-  void DoRemoteCommandsFetch() override;
+  void DoRemoteCommandsFetch(const syncer::Invalidation& invalidation) override;
 
   // CloudPolicyCore::Observer:
   void OnCoreConnected(CloudPolicyCore* core) override;
@@ -39,8 +46,14 @@
   void OnStoreError(CloudPolicyStore* store) override;
 
  private:
+  void RecordInvalidationMetric(const syncer::Invalidation& invalidation) const;
+
   CloudPolicyCore* const core_;
 
+  const base::Clock* const clock_;
+
+  const PolicyInvalidationScope scope_;
+
   DISALLOW_COPY_AND_ASSIGN(RemoteCommandsInvalidatorImpl);
 };
 
diff --git a/chrome/browser/policy/cloud/remote_commands_invalidator_unittest.cc b/chrome/browser/policy/cloud/remote_commands_invalidator_unittest.cc
index 6e63216c..026dff1 100644
--- a/chrome/browser/policy/cloud/remote_commands_invalidator_unittest.cc
+++ b/chrome/browser/policy/cloud/remote_commands_invalidator_unittest.cc
@@ -21,6 +21,14 @@
 using ::testing::Mock;
 using ::testing::StrictMock;
 
+namespace {
+
+MATCHER_P(InvalidationsEqual, expected_invalidation, "") {
+  return arg.Equals(expected_invalidation);
+}
+
+}  // namespace
+
 namespace policy {
 
 class MockRemoteCommandInvalidator : public RemoteCommandsInvalidator {
@@ -31,7 +39,7 @@
   MOCK_METHOD0(OnShutdown, void());
   MOCK_METHOD0(OnStart, void());
   MOCK_METHOD0(OnStop, void());
-  MOCK_METHOD0(DoRemoteCommandsFetch, void());
+  MOCK_METHOD1(DoRemoteCommandsFetch, void(const syncer::Invalidation&));
 
   void SetInvalidationTopic(const syncer::Topic& topic) {
     em::PolicyData policy_data;
@@ -62,9 +70,12 @@
         syncer::TRANSIENT_INVALIDATION_ERROR);
   }
 
+  syncer::Invalidation CreateInvalidation(const syncer::Topic& topic) {
+    return syncer::Invalidation::InitUnknownVersion(topic);
+  }
+
   syncer::Invalidation FireInvalidation(const syncer::Topic& topic) {
-    const syncer::Invalidation invalidation =
-        syncer::Invalidation::InitUnknownVersion(topic);
+    const syncer::Invalidation invalidation = CreateInvalidation(topic);
     invalidation_service_.EmitInvalidationForTest(invalidation);
     return invalidation;
   }
@@ -122,7 +133,10 @@
   void VerifyInvalidationEnabled(const syncer::Topic& topic) {
     EXPECT_TRUE(invalidator_.invalidations_enabled());
 
-    EXPECT_CALL(invalidator_, DoRemoteCommandsFetch()).Times(1);
+    EXPECT_CALL(
+        invalidator_,
+        DoRemoteCommandsFetch(InvalidationsEqual(CreateInvalidation(topic))))
+        .Times(1);
     const syncer::Invalidation invalidation = FireInvalidation(topic);
 
     base::RunLoop().RunUntilIdle();
@@ -168,7 +182,9 @@
 
   // Fire the invalidation, it should be acknowledged and trigger a remote
   // commands fetch.
-  EXPECT_CALL(invalidator_, DoRemoteCommandsFetch()).Times(1);
+  EXPECT_CALL(invalidator_, DoRemoteCommandsFetch(InvalidationsEqual(
+                                CreateInvalidation(kTestingTopic1))))
+      .Times(1);
   const syncer::Invalidation invalidation2 = FireInvalidation(kTestingTopic1);
 
   base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index d8f4c64..fb43ab9 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -451,6 +451,9 @@
      */
     public static final String OFFLINE_INDICATOR_V2_ENABLED = "offline_indicator_v2_enabled";
 
+    /** The shared preference for the 'save card to device' checkbox status. */
+    public static final String PAYMENTS_CHECK_SAVE_CARD_TO_DEVICE = "check_save_card_to_device";
+
     /** Prefix of the preferences to persist use count of the payment instruments. */
     public static final KeyPrefix PAYMENTS_PAYMENT_INSTRUMENT_USE_COUNT =
             new KeyPrefix("payment_instrument_use_count_*");
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
index 97ed6002c..9c9b20d 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
@@ -125,6 +125,7 @@
                 ChromePreferenceKeys.OFFLINE_AUTO_FETCH_SHOWING_IN_PROGRESS,
                 ChromePreferenceKeys.OFFLINE_AUTO_FETCH_USER_CANCEL_ACTION_IN_PROGRESS,
                 ChromePreferenceKeys.OFFLINE_INDICATOR_V2_ENABLED,
+                ChromePreferenceKeys.PAYMENTS_CHECK_SAVE_CARD_TO_DEVICE,
                 ChromePreferenceKeys.PAYMENTS_PAYMENT_COMPLETE_ONCE,
                 ChromePreferenceKeys.PREFETCH_HAS_NEW_PAGES,
                 ChromePreferenceKeys.PREFETCH_IGNORED_NOTIFICATION_COUNTER,
diff --git a/chrome/browser/previews/previews_browsertest.cc b/chrome/browser/previews/previews_browsertest.cc
index a2a649d..ce8dd50 100644
--- a/chrome/browser/previews/previews_browsertest.cc
+++ b/chrome/browser/previews/previews_browsertest.cc
@@ -439,3 +439,34 @@
     EXPECT_FALSE(noscript_css_requested());
   }
 }
+
+// This test class disables DataSaver.
+class PreviewsDataSaverDisabledBrowserTest
+    : public PreviewsNoScriptBrowserTest {
+ public:
+  PreviewsDataSaverDisabledBrowserTest() = default;
+
+  ~PreviewsDataSaverDisabledBrowserTest() override = default;
+
+  void SetUpCommandLine(base::CommandLine* cmd) override {
+    PreviewsNoScriptBrowserTest::SetUpCommandLine(cmd);
+    cmd->RemoveSwitch("enable-spdy-proxy-auth");
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(ShouldDisablePreview,
+                         PreviewsDataSaverDisabledBrowserTest,
+                         ::testing::Bool());
+
+IN_PROC_BROWSER_TEST_P(PreviewsDataSaverDisabledBrowserTest,
+                       NoPreviewWithDataSaverDisabled) {
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(browser(), https_url());
+
+  // Verify loaded noscript tag triggered js file and not css file.
+  EXPECT_TRUE(noscript_js_requested());
+  EXPECT_FALSE(noscript_css_requested());
+
+  histogram_tester.ExpectTotalCount("OptimizationGuide.ApplyDecision.NoScript",
+                                    0);
+}
diff --git a/chrome/browser/previews/previews_service.cc b/chrome/browser/previews/previews_service.cc
index c11154c..f0bf904 100644
--- a/chrome/browser/previews/previews_service.cc
+++ b/chrome/browser/previews/previews_service.cc
@@ -21,6 +21,7 @@
 #include "chrome/common/chrome_constants.h"
 #include "components/blacklist/opt_out_blacklist/opt_out_store.h"
 #include "components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/previews/content/previews_decider_impl.h"
@@ -197,7 +198,10 @@
   std::unique_ptr<previews::PreviewsOptimizationGuide> previews_opt_guide;
   OptimizationGuideKeyedService* optimization_guide_keyed_service =
       OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
-  if (optimization_guide_keyed_service) {
+  if (optimization_guide_keyed_service &&
+      data_reduction_proxy::DataReductionProxySettings::
+          IsDataSaverEnabledByUser(profile->IsOffTheRecord(),
+                                   profile->GetPrefs())) {
     previews_opt_guide = std::make_unique<previews::PreviewsOptimizationGuide>(
         optimization_guide_keyed_service);
   }
diff --git a/chrome/browser/resources/chromeos/camera/src/js/metrics.js b/chrome/browser/resources/chromeos/camera/src/js/metrics.js
index c1e2aac..8ff1ca4 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/metrics.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/metrics.js
@@ -5,6 +5,8 @@
 import {browserProxy} from './browser_proxy/browser_proxy.js';
 // eslint-disable-next-line no-unused-vars
 import {Intent} from './intent.js';
+// eslint-disable-next-line no-unused-vars
+import {PerfEvent} from './perf.js';
 import * as state from './state.js';
 // eslint-disable-next-line no-unused-vars
 import {Facing} from './type.js';
@@ -141,15 +143,16 @@
 
 /**
  * Returns event builder for the metrics type: perf.
- * @param {string} event The target event type.
+ * @param {PerfEvent} event The target event type.
  * @param {number} duration The duration of the event in ms.
  * @param {Object=} extras Optional information for the event.
  * @return {!analytics.EventBuilder}
  */
 function perfType(event, duration, extras = {}) {
-  const {resolution = ''} = extras;
+  const {resolution = '', facing = ''} = extras;
   return base.category('perf')
       .action(event)
+      .label(facing)
       // Round the duration here since GA expects that the value is an integer.
       // Reference: https://support.google.com/analytics/answer/1033068
       .value(Math.round(duration))
diff --git a/chrome/browser/resources/chromeos/camera/src/js/perf.js b/chrome/browser/resources/chromeos/camera/src/js/perf.js
index 289d637..02b1d457 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/perf.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/perf.js
@@ -12,6 +12,7 @@
  */
 export const PerfEvent = {
   PHOTO_TAKING: 'photo-taking',
+  PHOTO_CAPTURE_SHUTTER: 'photo-capture-shutter',
   PHOTO_CAPTURE_POST_PROCESSING: 'photo-capture-post-processing',
   VIDEO_CAPTURE_POST_PROCESSING: 'video-capture-post-processing',
   PORTRAIT_MODE_CAPTURE_POST_PROCESSING:
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
index 9be798b..480aeb0a 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
@@ -263,7 +263,8 @@
         console.error(e);
       } finally {
         this.take_ = null;
-        state.set(state.State.TAKING, false, {hasError});
+        state.set(
+            state.State.TAKING, false, {hasError, facing: this.facingMode_});
         this.focus();  // Refocus the visible shutter button for ChromeVox.
       }
     })();
@@ -406,7 +407,8 @@
           await this.preview_.start(stream);
           this.facingMode_ = await this.options_.updateValues(stream);
           await this.modes_.updateModeSelectionUI(deviceId);
-          await this.modes_.updateMode(mode, stream, deviceId, captureR);
+          await this.modes_.updateMode(
+              mode, stream, this.facingMode_, deviceId, captureR);
           nav.close(ViewName.WARNING, 'no-camera');
           return true;
         } catch (e) {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
index e05c505..71f7d84 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
@@ -3,10 +3,11 @@
 // found in the LICENSE file.
 
 import {assert, assertInstanceof} from '../../chrome_util.js';
-import {CaptureCandidate,           // eslint-disable-line no-unused-vars
-        ConstraintsPreferrer,       // eslint-disable-line no-unused-vars
-        PhotoConstraintsPreferrer,  // eslint-disable-line no-unused-vars
-        VideoConstraintsPreferrer,  // eslint-disable-line no-unused-vars
+import {
+  CaptureCandidate,           // eslint-disable-line no-unused-vars
+  ConstraintsPreferrer,       // eslint-disable-line no-unused-vars
+  PhotoConstraintsPreferrer,  // eslint-disable-line no-unused-vars
+  VideoConstraintsPreferrer,  // eslint-disable-line no-unused-vars
 } from '../../device/constraints_preferrer.js';
 import {Filenamer} from '../../models/filenamer.js';
 import * as filesystem from '../../models/filesystem.js';
@@ -18,10 +19,14 @@
 import * as sound from '../../sound.js';
 import * as state from '../../state.js';
 import * as toast from '../../toast.js';
-import {Resolution,
-        ResolutionList,  // eslint-disable-line no-unused-vars
+import {
+  Resolution,
+  ResolutionList,  // eslint-disable-line no-unused-vars
 } from '../../type.js';
-import {Mode} from '../../type.js';
+import {
+  Facing,  // eslint-disable-line no-unused-vars
+  Mode,
+} from '../../type.js';
 import * as util from '../../util.js';
 import {RecordTime} from './recordtime.js';
 
@@ -176,6 +181,13 @@
     this.stream_ = null;
 
     /**
+     * Camera facing of current mode.
+     * @type {!Facing}
+     * @private
+     */
+    this.facing_ = Facing.UNKNOWN;
+
+    /**
      * @type {!HTMLElement}
      * @private
      */
@@ -231,8 +243,8 @@
     this.allModes_ = {
       [Mode.VIDEO]: {
         captureFactory: () => new Video(
-            assertInstanceof(this.stream_, MediaStream), createVideoSaver,
-            doSaveVideo),
+            assertInstanceof(this.stream_, MediaStream), this.facing_,
+            createVideoSaver, doSaveVideo),
         isSupported: async () => true,
         constraintsPreferrer: videoPreferrer,
         getV1Constraints: getV1Constraints.bind(this, true),
@@ -241,8 +253,8 @@
       },
       [Mode.PHOTO]: {
         captureFactory: () => new Photo(
-            assertInstanceof(this.stream_, MediaStream), doSavePhoto,
-            this.captureResolution_, playShutterEffect),
+            assertInstanceof(this.stream_, MediaStream), this.facing_,
+            doSavePhoto, this.captureResolution_, playShutterEffect),
         isSupported: async () => true,
         constraintsPreferrer: photoPreferrer,
         getV1Constraints: getV1Constraints.bind(this, false),
@@ -251,8 +263,8 @@
       },
       [Mode.SQUARE]: {
         captureFactory: () => new Square(
-            assertInstanceof(this.stream_, MediaStream), doSavePhoto,
-            this.captureResolution_, playShutterEffect),
+            assertInstanceof(this.stream_, MediaStream), this.facing_,
+            doSavePhoto, this.captureResolution_, playShutterEffect),
         isSupported: async () => true,
         constraintsPreferrer: photoPreferrer,
         getV1Constraints: getV1Constraints.bind(this, false),
@@ -261,8 +273,8 @@
       },
       [Mode.PORTRAIT]: {
         captureFactory: () => new Portrait(
-            assertInstanceof(this.stream_, MediaStream), doSavePhoto,
-            this.captureResolution_, playShutterEffect),
+            assertInstanceof(this.stream_, MediaStream), this.facing_,
+            doSavePhoto, this.captureResolution_, playShutterEffect),
         isSupported: async (deviceId) => {
           if (deviceId === null) {
             return false;
@@ -430,17 +442,19 @@
    * Creates and updates new current mode object.
    * @param {!Mode} mode Classname of mode to be updated.
    * @param {!MediaStream} stream Stream of the new switching mode.
+   * @param {!Facing} facing Camera facing of the current mode.
    * @param {?string} deviceId Device id of currently working video device.
    * @param {?Resolution} captureResolution Capturing resolution width and
    *     height.
    * @return {!Promise}
    */
-  async updateMode(mode, stream, deviceId, captureResolution) {
+  async updateMode(mode, stream, facing, deviceId, captureResolution) {
     if (this.current !== null) {
       await this.current.stopCapture();
     }
     this.updateModeUI_(mode);
     this.stream_ = stream;
+    this.facing_ = facing;
     this.captureResolution_ = captureResolution;
     this.current = this.allModes_[mode].captureFactory();
     if (deviceId && this.captureResolution_) {
@@ -493,10 +507,11 @@
 class ModeBase {
   /**
    * @param {!MediaStream} stream
+   * @param {!Facing} facing
    * @param {?Resolution} captureResolution Capturing resolution width and
    *     height.
    */
-  constructor(stream, captureResolution) {
+  constructor(stream, facing, captureResolution) {
     /**
      * Stream of current mode.
      * @type {!MediaStream}
@@ -505,6 +520,13 @@
     this.stream_ = stream;
 
     /**
+     * Camera facing of current mode.
+     * @type {!Facing}
+     * @protected
+     */
+    this.facing_ = facing;
+
+    /**
      * Capture resolution. May be null on device not support of setting
      * resolution.
      * @type {?Resolution}
@@ -579,11 +601,12 @@
 class Video extends ModeBase {
   /**
    * @param {!MediaStream} stream
+   * @param {!Facing} facing
    * @param {!CreateVideoSaver} createVideoSaver
    * @param {!DoSaveVideo} doSaveVideo
    */
-  constructor(stream, createVideoSaver, doSaveVideo) {
-    super(stream, null);
+  constructor(stream, facing, createVideoSaver, doSaveVideo) {
+    super(stream, facing, null);
 
     /**
      * @type {!CreateVideoSaver}
@@ -662,7 +685,9 @@
     try {
       await this.doSaveVideo_(
           {resolution, duration, videoSaver}, (new Filenamer()).newVideoName());
-      state.set(PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, {resolution});
+      state.set(
+          PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false,
+          {resolution, facing: this.facing_});
     } catch (e) {
       state.set(
           PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, {hasError: true});
@@ -725,12 +750,14 @@
 class Photo extends ModeBase {
   /**
    * @param {!MediaStream} stream
+   * @param {!Facing} facing
    * @param {!DoSavePhoto} doSavePhoto
    * @param {?Resolution} captureResolution
    * @param {!PlayShutterEffect} playShutterEffect
    */
-  constructor(stream, doSavePhoto, captureResolution, playShutterEffect) {
-    super(stream, captureResolution);
+  constructor(
+      stream, facing, doSavePhoto, captureResolution, playShutterEffect) {
+    super(stream, facing, captureResolution);
 
     /**
      * Callback for saving picture.
@@ -805,8 +832,11 @@
       });
     }
 
+    state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, true);
     try {
       const results = await this.crosImageCapture_.takePhoto(photoSettings);
+
+      state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, false, {facing: this.facing_});
       this.playShutterEffect_();
 
       state.set(PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, true);
@@ -814,8 +844,11 @@
       const image = await util.blobToImage(blob);
       const resolution = new Resolution(image.width, image.height);
       await this.doSavePhoto_({resolution, blob}, imageName);
-      state.set(PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, {resolution});
+      state.set(
+          PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false,
+          {resolution, facing: this.facing_});
     } catch (e) {
+      state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, false, {hasError: true});
       state.set(
           PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, {hasError: true});
       toast.show('error_msg_take_photo_failed');
@@ -901,12 +934,14 @@
 class Square extends Photo {
   /**
    * @param {!MediaStream} stream
+   * @param {!Facing} facing
    * @param {!DoSavePhoto} doSavePhoto
    * @param {?Resolution} captureResolution
    * @param {!PlayShutterEffect} playShutterEffect
    */
-  constructor(stream, doSavePhoto, captureResolution, playShutterEffect) {
-    super(stream, doSavePhoto, captureResolution, playShutterEffect);
+  constructor(
+      stream, facing, doSavePhoto, captureResolution, playShutterEffect) {
+    super(stream, facing, doSavePhoto, captureResolution, playShutterEffect);
 
     this.doSavePhoto_ = async (result, ...args) => {
       // Since the image blob after square cut will lose its EXIF including
@@ -948,12 +983,14 @@
 class Portrait extends Photo {
   /**
    * @param {!MediaStream} stream
+   * @param {!Facing} facing
    * @param {!DoSavePhoto} doSavePhoto
    * @param {?Resolution} captureResolution
    * @param {!PlayShutterEffect} playShutterEffect
    */
-  constructor(stream, doSavePhoto, captureResolution, playShutterEffect) {
-    super(stream, doSavePhoto, captureResolution, playShutterEffect);
+  constructor(
+      stream, facing, doSavePhoto, captureResolution, playShutterEffect) {
+    super(stream, facing, doSavePhoto, captureResolution, playShutterEffect);
   }
 
   /**
@@ -1036,6 +1073,7 @@
     }
     await refSave;
     state.set(
-        PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, false, {hasError});
+        PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, false,
+        {hasError, facing: this.facing_});
   }
 }
diff --git a/chrome/browser/resources/settings/autofill_page/password_check.html b/chrome/browser/resources/settings/autofill_page/password_check.html
index 55c527a..005dee6 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check.html
+++ b/chrome/browser/resources/settings/autofill_page/password_check.html
@@ -9,6 +9,7 @@
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="password_check_list_item.html">
 <link rel="import" href="password_manager_proxy.html">
@@ -43,6 +44,16 @@
       }
 
     </style>
+    <!-- The banner is visible if no compromised password was found (yet). -->
+    <template is="dom-if" if="[[shouldShowBanner_(status_, leakedPasswords)]]">
+      <picture>
+        <source srcset="[[bannerImageSrc_(1, status_)]]"
+                media="(prefers-color-scheme: dark)">
+        <img id="bannerImage" src="[[bannerImageSrc_(0, status_)]]">
+      </picture>
+    </template>
+
+    <!-- The header showing progress or result of the check-->
     <div class="settings-box first two-line" id="leakCheckHeader">
       <!-- If the password check concluded, show only a status Icon. -->
       <template is="dom-if" if="[[!isCheckInProgress_(status_)]]">
diff --git a/chrome/browser/resources/settings/autofill_page/password_check.js b/chrome/browser/resources/settings/autofill_page/password_check.js
index d434176..6937b5d 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check.js
+++ b/chrome/browser/resources/settings/autofill_page/password_check.js
@@ -189,7 +189,7 @@
    * @param {!PasswordManagerProxy.PasswordCheckStatus} status
    * @param {!Array<!PasswordManagerProxy.CompromisedCredential>}
    *     leakedPasswords
-   * @return {!string}
+   * @return {string}
    * @private
    */
   getStatusIcon_(status, leakedPasswords) {
@@ -207,7 +207,7 @@
    * @param {!PasswordManagerProxy.PasswordCheckStatus} status
    * @param {!Array<!PasswordManagerProxy.CompromisedCredential>}
    *     leakedPasswords
-   * @return {!string}
+   * @return {string}
    * @private
    */
   getStatusIconClass_(status, leakedPasswords) {
@@ -333,6 +333,31 @@
   },
 
   /**
+   * Returns the chrome:// address where the banner image is located.
+   * @param {boolean} isDarkMode
+   * @return {string}
+   * @private
+   */
+  bannerImageSrc_(isDarkMode) {
+    const type = this.status_.state == CheckState.IDLE ? 'positive' : 'neutral';
+    const suffix = isDarkMode ? '_dark' : '';
+    return `chrome://settings/images/password_check_${type}${suffix}.svg`;
+  },
+
+  /**
+   * Returns true iff the banner should be shown.
+   * @return {boolean}
+   * @private
+   */
+  shouldShowBanner_() {
+    if (this.hasLeakedCredentials_(this.leakedPasswords)) {
+      return false;
+    }
+    return this.status_.state == CheckState.CANCELED ||
+        !this.hasLeaksOrErrors_(this.status_, this.leakedPasswords);
+  },
+
+  /**
    * Returns true if there are leaked credentials or the status is unexpected
    * for a regular password check.
    * @param {!PasswordManagerProxy.PasswordCheckStatus} status
diff --git a/chrome/browser/resources/settings/autofill_page/password_check_list_item.html b/chrome/browser/resources/settings/autofill_page/password_check_list_item.html
index 680b3da..0f6d86b 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check_list_item.html
+++ b/chrome/browser/resources/settings/autofill_page/password_check_list_item.html
@@ -13,12 +13,6 @@
 <dom-module id="password-check-list-item">
   <template>
     <style include="settings-shared passwords-shared">
-      .icon-column {
-        align-items: center;
-        height: 16px;
-        margin-inline-end: 16px;
-      }
-
       #change-password-link-icon {
         height: 16px;
         margin-inline-start: 10px;
@@ -26,15 +20,10 @@
         --iron-icon-fill-color: var(--text-color-action);
       }
 
-      #icon-more-vert {
-        height: 32px;
-        width: 32px;
-      }
-
       #leakedPassword {
         background-color: transparent;
         border: none;
-        color: inherit;
+        margin-inline-start: 4px;
       }
 
       #leaked-item {
@@ -43,39 +32,77 @@
       }
 
       #leaked-info {
+        display: flex;
+        flex: 2;
+        width: 0;
+      }
+
+      #leakUsername {
+        align-items: center;
+        display: flex;
         flex: 1;
       }
-    </style>
-    <div class="list-item" id="leaked-item">
-      <div class="icon-column">
-        <site-favicon url="[[item.changePasswordUrl]]"></site-favicon>
-      </div>
 
-      <div class="info-column two-line" id="leaked-info">
-        <div class="start text">
-          <div id="leakOrigin">[[item.formattedOrigin]]</div>
-          <div>
-            <span class="secondary" id="leakUsername">[[item.username]]</span>
-            <input id="leakedPassword" type="password" value="[[password_]]"
-                readonly disabled>
+      #changePasswordInApp {
+        display: flex;
+        flex: 2;
+        flex-direction: row-reverse;
+      }
+
+      #changePasswordUrl {
+        display: flex;
+        flex: 1;
+        flex-direction: row-reverse;
+        white-space: nowrap;
+      }
+
+      #info-column {
+        display: flex;
+        flex: 1;
+        flex-direction: column;
+      }
+
+      #leakOrigin {
+        direction: rtl;
+        display: flex;
+        justify-content: flex-end;
+      }
+    </style>
+    <div class="list-item" id="leaked-item" focus-row-container>
+      <site-favicon url="[[item.changePasswordUrl]]"></site-favicon>
+      <div id="leaked-info">
+        <div id="info-column" class="no-min-width">
+          <div id="leakOrigin" class="no-min-width">
+            <span class="text-elide">
+              <!-- This bdo tag is necessary to fix the display of domains
+                starting with numbers. -->
+              <bdo dir="ltr">[[item.formattedOrigin]]</bdo>
+            </span>
           </div>
-          <div class="secondary"
-              id="leakType">[[getCompromiseType_(item)]]
+          <div class="no-min-width" id="leakUsername" >
+            <span class="no-min-width text-elide secondary">
+              [[item.username]]
+            </span>
+            <input class="no-min-width secondary" id="leakedPassword"
+                type="password" value="[[password_]]" readonly disabled>
           </div>
-          <div class="secondary"
-              id="elapsedTime">[[item.elapsedTimeSinceCompromise]]
+          <div class="secondary" id="leakType">
+            [[getCompromiseType_(item)]]
+          </div>
+          <div class="secondary" id="elapsedTime">
+            [[item.elapsedTimeSinceCompromise]]
           </div>
         </div>
       </div>
       <template is="dom-if" if="[[item.changePasswordUrl]]">
         <div class="button-container" id="changePasswordUrl">
-        <!-- TODO:(https://crbug.com/1047726) add 'Already changed this password?' link -->
-        <cr-button class="action-button" on-click="onChangePasswordClick_">
-          $i18n{changePasswordButton}
-          <iron-icon icon="cr:open-in-new" id="change-password-link-icon">
-          </iron-icon>
-        </cr-button>
-      </div>
+          <!-- TODO:(https://crbug.com/1047726) add 'Already changed this password?' link -->
+          <cr-button class="action-button" on-click="onChangePasswordClick_">
+            $i18n{changePasswordButton}
+            <iron-icon icon="cr:open-in-new" id="change-password-link-icon">
+            </iron-icon>
+          </cr-button>
+        </div>
       </template>
       <template is="dom-if" if="[[!item.changePasswordUrl]]">
         <span id="changePasswordInApp">$i18n{changePasswordInApp}</span>
diff --git a/chrome/browser/resources/settings/images/password_check_positive.svg b/chrome/browser/resources/settings/images/password_check_positive.svg
new file mode 100644
index 0000000..6e3eeaa
--- /dev/null
+++ b/chrome/browser/resources/settings/images/password_check_positive.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="680" height="120"><style>.B{mask:url(#B)}.C{fill:#bdc0c5}.D{fill:#e8e9eb}.E{fill:#f8f9fa}.F{fill-rule:nonzero}</style><defs><path id="A" d="M0 0h680v120H0z"/></defs><g fill-rule="evenodd"><mask id="B" fill="#fff"><use xlink:href="#A"/></mask><use fill-opacity=".02" xlink:href="#A"/><g class="F B"><path d="M288.585 52.215V47c10.735 0 19.175-3 25.1-8.955 9.3-9.36 9.325-23.015 9.325-23.15l5.25-.04c0 .645 0 15.9-10.815 26.82-6.935 6.995-16.645 10.54-28.85 10.54z" class="D"/><path d="M253.87 83h-5.245a40.45 40.45 0 0 1 5-18c4.605-8.2 14.4-18 34.96-18v5.215c-14.465 0-24.685 5.17-30.385 15.36A35.35 35.35 0 0 0 253.87 83z" fill="#34a751"/><path d="M56 57.915H38.175v-.27a8.91 8.91 0 1 1 17.82 0l.005.27zm-17.275-.54H55.45a8.37 8.37 0 0 0-16.73 0h.005z" class="C"/><g class="D"><path d="M426.825 68.895h-17.28a8.64 8.64 0 0 1 17.28 0zM345 88.66h20v4h-20z"/><path d="M348.268 98.32l10-17.32 3.464 2-10 17.32z"/><path d="M358.268 100.32l-10-17.32 3.464-2 10 17.32z"/></g><path d="M254.6 94.5h-90.7V32.325a2.65 2.65 0 0 1 2.64-2.64h85.44a2.65 2.65 0 0 1 2.64 2.64V94.5z" class="E"/><path d="M250.3 30.225A3.791 3.791 0 0 1 254.08 34v59.945h-89.64V34a3.786 3.786 0 0 1 3.78-3.78h82.08m0-.54h-82.1a4.335 4.335 0 0 0-4.32 4.32v60.5h90.72V34a4.335 4.335 0 0 0-4.32-4.32zm-99.36 68.585h116.64v.54H150.93v-.54z" class="C"/><g stroke="#bdc0c5" stroke-width=".5" class="E"><rect x="112" y="18" width="68" height="92" rx="6"/><path d="M224.75 55.75h-76.5a2.43 2.43 0 0 0-1.773.783 2.725 2.725 0 0 0-.727 1.867v19.2c.001.73.28 1.39.727 1.867.454.484 1.08.782 1.772.783h76.5a2.43 2.43 0 0 0 1.773-.783 2.725 2.725 0 0 0 .727-1.867V58.4a2.73 2.73 0 0 0-.727-1.867 2.426 2.426 0 0 0-1.772-.783z"/></g><g fill="#3a7bee"><rect x="215" y="62" width="2" height="12" rx="1"/><path d="M171 67h10v2h-10z"/><path d="M172.634 71.83l5-8.66 1.732 1-5 8.66z"/><path d="M177.634 72.83l-5-8.66 1.732-1 5 8.66zM185 67h10v2h-10z"/><path d="M186.634 71.83l5-8.66 1.732 1-5 8.66z"/><path d="M191.634 72.83l-5-8.66 1.732-1 5 8.66zM199 67h10v2h-10z"/><path d="M200.634 71.83l5-8.66 1.732 1-5 8.66z"/><path d="M205.634 72.83l-5-8.66 1.732-1 5 8.66z"/></g></g><circle fill="#34a751" cx="146" cy="68" r="20" class="B"/><path fill="#fff" d="M143.214 74.75l-6.964-6.478 1.964-1.827 5 4.638 10.572-9.833 1.964 1.84z" class="B"/><g class="F B"><g stroke="#bdc0c5" stroke-width=".5" class="E"><path d="M146 10h0a7 7 0 0 1 7 7v7h-14v-7a7 7 0 0 1 7-7z"/><circle cx="146" cy="17" r="3"/><path d="M162 24h-32a6 6 0 0 0-6 6v4h44v-4a6 6 0 0 0-6-6z"/></g><path d="M79.6 58.42a12.935 12.935 0 0 0-10.1 4.82A8.62 8.62 0 0 0 58 71.38h34.56c0-7.158-5.802-12.96-12.96-12.96z" fill="#fff"/><path d="M92.83 71.65h-35.1v-.27a8.89 8.89 0 0 1 11.695-8.455A13.23 13.23 0 0 1 92.83 71.38v.27zm-34.555-.54h34a12.69 12.69 0 0 0-22.56-7.7l-.12.15-.18-.065A8.35 8.35 0 0 0 58.26 71.11h.015z" class="C"/><path d="M38.12 82.36l3.705-3.705c7.6 7.6 15.7 11.43 24.075 11.4 13.17-.03 22.855-9.655 22.95-9.75L92.585 84c-.45.46-11.235 11.25-26.6 11.315C56.12 95.345 46.75 91 38.12 82.36z" fill="#f7bb2a"/><path d="M-8.2 79.58l-3.705-3.705a40.45 40.45 0 0 1 16.25-9.18c9.075-2.57 22.945-2.58 37.5 11.96L38.14 82.36C27.9 72.13 17 68.555 5.77 71.735A35.342 35.342 0 0 0-8.2 79.58z" class="D"/><g class="C"><path d="M636.62 39.735c-5.777.008-10.613-4.377-11.168-10.127s3.35-10.98 9.022-12.08 11.248 2.294 12.88 7.835a11.205 11.205 0 0 1-10.733 14.37zm0-21.765a10.58 10.58 0 0 0-9.82 14.5 10.57 10.57 0 0 0 20.298-5.263 10.57 10.57 0 0 0-10.478-9.237zM619 130h-80a6.785 6.785 0 0 1-6.775-6.775v-10h.6v10A6.185 6.185 0 0 0 539 129.38h80a6.185 6.185 0 0 0 6.175-6.175v-43.55A6.185 6.185 0 0 0 619 73.5h-12v-.6h12a6.785 6.785 0 0 1 6.775 6.775V123.2A6.784 6.784 0 0 1 619 130z"/><path d="M607.005 73.48H561l3-.6h43.005z"/><path d="M532.8 100.18h-.6v-20.5a6.78 6.78 0 0 1 6.8-6.8h29.5v.62H539a6.185 6.185 0 0 0-6.175 6.175l-.025 20.505z"/></g><path d="M540 93.28a14.37 14.37 0 0 0-11.205 5.355A9.58 9.58 0 0 0 516 107.68h38.4c0-7.953-6.447-14.4-14.4-14.4z" fill="#fff"/><path d="M554.725 108h-39v-.3a9.88 9.88 0 0 1 13-9.395 14.7 14.7 0 0 1 26 9.395v.3zm-38.395-.6h37.795a14.1 14.1 0 0 0-25.065-8.555l-.135.165-.2-.07c-2.795-.998-5.898-.598-8.35 1.075s-3.953 4.42-4.04 7.385h-.005z" class="C"/><path d="M498.67 29.88L534.33-.525" class="E"/><path d="M498.32 29.468L534-.9l.65.762-35.68 30.377-.648-.76zM641.43 56.08h-5.825a44.942 44.942 0 0 0 5.555 20c5.1 9.145 16 20.05 38.84 20.05V90.3c-16.07 0-27.43-5.745-33.76-17.07a39.127 39.127 0 0 1-4.8-17.14z" class="D"/></g></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/images/password_check_positive_dark.svg b/chrome/browser/resources/settings/images/password_check_positive_dark.svg
new file mode 100644
index 0000000..8474160
--- /dev/null
+++ b/chrome/browser/resources/settings/images/password_check_positive_dark.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="680" height="120"><style>.B{mask:url(#B)}.C{fill:#414447}.D{stroke:#4e5154}.E{stroke-width:.5}.F{fill:#262628}.G{fill-rule:nonzero}</style><defs><path id="A" d="M0 0h680v120H0z"/></defs><g fill-rule="evenodd"><mask id="B" fill="#fff"><use xlink:href="#A"/></mask><use fill-opacity=".1" xlink:href="#A"/><g class="G B"><path d="M288.585 52.215V47c10.735 0 19.175-3 25.1-8.955 9.3-9.36 9.325-23.015 9.325-23.15l5.25-.04c0 .645 0 15.9-10.815 26.82-6.935 6.995-16.645 10.54-28.85 10.54z" class="C"/><path d="M253.87 83h-5.245a40.45 40.45 0 0 1 5-18c4.605-8.2 14.4-18 34.96-18v5.215c-14.465 0-24.685 5.17-30.385 15.36A35.35 35.35 0 0 0 253.87 83z" fill="#81c995"/><g class="C"><path d="M56 57.915H38.175v-.27a8.91 8.91 0 1 1 17.82 0l.005.27zm-17.275-.54H55.45a8.37 8.37 0 0 0-16.73 0h.005zm388.1 11.52h-17.28a8.64 8.64 0 0 1 17.28 0zM345 88.66h20v4h-20z"/><path d="M348.268 98.32l10-17.32 3.464 2-10 17.32z"/><path d="M358.268 100.32l-10-17.32 3.464-2 10 17.32z"/></g><path d="M254.6 94.5h-90.7V32.325a2.65 2.65 0 0 1 2.64-2.64h85.44a2.65 2.65 0 0 1 2.64 2.64V94.5z" class="D E F"/><path d="M250.3 30.225A3.791 3.791 0 0 1 254.08 34v59.945h-89.64V34a3.786 3.786 0 0 1 3.78-3.78h82.08m0-.54h-82.1a4.335 4.335 0 0 0-4.32 4.32v60.5h90.72V34a4.335 4.335 0 0 0-4.32-4.32zm-99.36 68.585h116.64v.54H150.93v-.54z" class="C"/><g class="D E F"><rect x="112" y="18" width="68" height="92" rx="6"/><path d="M224.75 56c1.242.002 2.25 1.075 2.25 2.4v19.2c-.001 1.325-1.008 2.398-2.25 2.4h-76.5c-1.242-.002-2.25-1.075-2.25-2.4V58.4c.001-1.325 1.008-2.398 2.25-2.4h76.5z"/></g><g fill="#8ab4f8"><rect x="215" y="62" width="2" height="12" rx="1"/><path d="M171 67h10v2h-10z"/><path d="M172.634 71.83l5-8.66 1.732 1-5 8.66z"/><path d="M177.634 72.83l-5-8.66 1.732-1 5 8.66zM185 67h10v2h-10z"/><path d="M186.634 71.83l5-8.66 1.732 1-5 8.66z"/><path d="M191.634 72.83l-5-8.66 1.732-1 5 8.66zM199 67h10v2h-10z"/><path d="M200.634 71.83l5-8.66 1.732 1-5 8.66z"/><path d="M205.634 72.83l-5-8.66 1.732-1 5 8.66z"/></g><circle fill="#81c995" cx="146" cy="68" r="20"/></g><path fill="#28282b" d="M143.214 74.75l-6.964-6.478 1.964-1.827 5 4.638 10.572-9.833 1.964 1.84z" class="B"/><g class="G B"><g class="D E F"><path d="M146 10a7 7 0 0 1 7 7v7h-14v-7a7 7 0 0 1 7-7z"/><circle cx="146" cy="17" r="3"/><path d="M162 24h-32a6 6 0 0 0-6 6v4h44v-4a6 6 0 0 0-6-6z"/></g><path d="M79.6 58.42a12.935 12.935 0 0 0-10.1 4.82A8.62 8.62 0 0 0 58 71.38h34.56c0-7.158-5.802-12.96-12.96-12.96z" class="C"/><path d="M92.83 71.65h-35.1v-.27a8.89 8.89 0 0 1 11.695-8.455A13.23 13.23 0 0 1 92.83 71.38v.27zm-34.555-.54h34a12.69 12.69 0 0 0-22.56-7.7l-.12.15-.18-.065A8.35 8.35 0 0 0 58.26 71.11h.015z" fill="#bdc0c5"/><path d="M38.12 82.36l3.705-3.705c7.6 7.6 15.7 11.43 24.075 11.4 13.17-.03 22.855-9.655 22.95-9.75L92.585 84c-.45.46-11.235 11.25-26.6 11.315C56.12 95.345 46.75 91 38.12 82.36z" fill="#f7bb2a"/><path d="M-8.2 79.58l-3.705-3.705a40.45 40.45 0 0 1 16.25-9.18c9.075-2.57 22.945-2.58 37.5 11.96L38.14 82.36C27.9 72.13 17 68.555 5.77 71.735A35.342 35.342 0 0 0-8.2 79.58z" class="C"/><path d="M636.62 39.735c-5.777.008-10.613-4.377-11.168-10.127s3.35-10.98 9.022-12.08 11.248 2.294 12.88 7.835a11.205 11.205 0 0 1-10.733 14.37zm0-21.765a10.58 10.58 0 0 0-9.82 14.5 10.57 10.57 0 0 0 20.298-5.263 10.57 10.57 0 0 0-10.478-9.237zM619 130h-80a6.785 6.785 0 0 1-6.775-6.775v-10h.6v10A6.185 6.185 0 0 0 539 129.38h80a6.185 6.185 0 0 0 6.175-6.175v-43.55A6.185 6.185 0 0 0 619 73.5h-12v-.6h12a6.785 6.785 0 0 1 6.775 6.775V123.2A6.784 6.784 0 0 1 619 130z" fill="#4e5154"/><path d="M607.005 73.48H561l3-.6h43.005z" class="D E F"/><path d="M532.8 100.18h-.6v-20.5a6.78 6.78 0 0 1 6.8-6.8h29.5v.62H539a6.185 6.185 0 0 0-6.175 6.175l-.025 20.505z" fill="#bdc0c5"/><path d="M540 93.28a14.37 14.37 0 0 0-11.205 5.355A9.58 9.58 0 0 0 516 107.68h38.4c0-7.953-6.447-14.4-14.4-14.4z" class="C"/><path d="M554.725 108h-39v-.3a9.88 9.88 0 0 1 13-9.395 14.7 14.7 0 0 1 26 9.395v.3zm-38.395-.6h37.795a14.1 14.1 0 0 0-25.065-8.555l-.135.165-.2-.07c-2.795-.998-5.898-.598-8.35 1.075s-3.953 4.42-4.04 7.385h-.005z" fill="#4e5154"/><path d="M498.67 29.88L534.33-.525m-36.01 29.993L534-.9l.65.762-35.68 30.377-.648-.76zM641.43 56.08h-5.825a44.942 44.942 0 0 0 5.555 20c5.1 9.145 16 20.05 38.84 20.05V90.3c-16.07 0-27.43-5.745-33.76-17.07a39.127 39.127 0 0 1-4.8-17.14z"/></g></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/people_page/sync_controls.js b/chrome/browser/resources/settings/people_page/sync_controls.js
index 795d5b4..563dfea 100644
--- a/chrome/browser/resources/settings/people_page/sync_controls.js
+++ b/chrome/browser/resources/settings/people_page/sync_controls.js
@@ -39,7 +39,7 @@
 Polymer({
   is: 'settings-sync-controls',
 
-  behaviors: [WebUIListenerBehavior],
+  behaviors: [WebUIListenerBehavior, settings.RouteObserverBehavior],
 
   properties: {
     hidden: {
@@ -103,6 +103,14 @@
     }
   },
 
+  /** @protected */
+  currentRouteChanged() {
+    const router = settings.Router.getInstance();
+    if (router.getCurrentRoute() === router.getRoutes().SYNC_ADVANCED) {
+      chrome.metricsPrivate.recordUserAction('Sync_NavigateToSyncAdvancedPage');
+    }
+  },
+
   /**
    * Handler for when the sync preferences are updated.
    * @private
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index c29249e..8af18dc6 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -673,6 +673,12 @@
       <structure name="IDR_SETTINGS_IMAGES_PASSWORD_CHECK_NEUTRAL_DARK_SVG"
                  file="images/password_check_neutral_dark.svg"
                  type="chrome_html" />
+      <structure name="IDR_SETTINGS_IMAGES_PASSWORD_CHECK_POSITIVE_SVG"
+                 file="images/password_check_positive.svg"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_IMAGES_PASSWORD_CHECK_POSITIVE_DARK_SVG"
+                 file="images/password_check_positive_dark.svg"
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_PASSWORDS_SECTION_HTML"
                  file="autofill_page/passwords_section.html"
                  type="chrome_html"
diff --git a/chrome/browser/resources/settings/settings_resources_vulcanized.grd b/chrome/browser/resources/settings/settings_resources_vulcanized.grd
index 19e6cbed..84d4f2f 100644
--- a/chrome/browser/resources/settings/settings_resources_vulcanized.grd
+++ b/chrome/browser/resources/settings/settings_resources_vulcanized.grd
@@ -60,6 +60,14 @@
                file="images/password_check_neutral_dark.svg"
                type="BINDATA"
                compress="gzip" />
+      <include name="IDR_SETTINGS_IMAGES_PASSWORD_CHECK_POSITIVE_SVG"
+               file="images/password_check_positive.svg"
+               type="BINDATA"
+               compress="gzip" />
+      <include name="IDR_SETTINGS_IMAGES_PASSWORD_CHECK_POSITIVE_DARK_SVG"
+               file="images/password_check_positive_dark.svg"
+               type="BINDATA"
+               compress="gzip" />
       <!-- Polymer 3 related files -->
       <include name="IDR_SETTINGS_SETTINGS_V3_HTML"
                file="settings_v3.html"
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index 42cacbd..edfa60b 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -163,8 +163,6 @@
         "download_protection/download_feedback.h",
         "download_protection/download_feedback_service.cc",
         "download_protection/download_feedback_service.h",
-        "download_protection/download_item_request.cc",
-        "download_protection/download_item_request.h",
         "download_protection/download_protection_service.cc",
         "download_protection/download_protection_service.h",
         "download_protection/download_protection_util.cc",
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
index 28cf2dd..2dc937c 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
@@ -538,15 +538,11 @@
   }
 
   // Create a file request for each file.
-  for (size_t i = 0; i < data_.paths.size(); ++i) {
-    if (FileTypeSupported(data_.do_malware_scan, data_.do_dlp_scan,
-                          data_.paths[i])) {
-      PrepareFileRequest(
-          data_.paths[i],
-          base::BindOnce(&DeepScanningDialogDelegate::AnalyzerCallback,
-                         weak_ptr_factory_.GetWeakPtr(), i));
+  for (const base::FilePath& path : data_.paths) {
+    if (FileTypeSupported(data_.do_malware_scan, data_.do_dlp_scan, path)) {
+      PrepareFileRequest(path);
     } else {
-      FileRequestCallback(data_.paths[i],
+      FileRequestCallback(path,
                           BinaryUploadService::Result::UNSUPPORTED_FILE_TYPE,
                           DeepScanningClientResponse());
     }
@@ -555,48 +551,15 @@
   return !text_request_complete_ || file_result_count_ != data_.paths.size();
 }
 
-void DeepScanningDialogDelegate::PrepareFileRequest(base::FilePath path,
-                                                    AnalyzeCallback callback) {
-  base::FilePath::StringType ext(path.FinalExtension());
-  std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
-  if (ext == FILE_PATH_LITERAL(".zip")) {
-    auto analyzer = base::MakeRefCounted<SandboxedZipAnalyzer>(
-        path, std::move(callback), LaunchFileUtilService());
-    analyzer->Start();
-  } else if (ext == FILE_PATH_LITERAL(".rar")) {
-    auto analyzer = base::MakeRefCounted<SandboxedRarAnalyzer>(
-        path, std::move(callback), LaunchFileUtilService());
-    analyzer->Start();
-  } else {
-    std::move(callback).Run(safe_browsing::ArchiveAnalyzerResults());
-  }
-}
-
-void DeepScanningDialogDelegate::AnalyzerCallback(
-    int index,
-    const safe_browsing::ArchiveAnalyzerResults& results) {
-  bool contains_encrypted_parts = std::any_of(
-      results.archived_binary.begin(), results.archived_binary.end(),
-      [](const auto& binary) { return binary.is_encrypted(); });
-
-  // If the file contains encrypted parts and the user is not allowed to use
-  // them, fail the request.
-  if (contains_encrypted_parts) {
-    FileRequestCallback(data_.paths[index],
-                        BinaryUploadService::Result::FILE_ENCRYPTED,
-                        DeepScanningClientResponse());
-    return;
-  }
-
+void DeepScanningDialogDelegate::PrepareFileRequest(base::FilePath path) {
   auto request = std::make_unique<FileSourceRequest>(
-      data_.paths[index],
-      base::BindOnce(&DeepScanningDialogDelegate::FileRequestCallback,
-                     weak_ptr_factory_.GetWeakPtr(), data_.paths[index]));
+      path, base::BindOnce(&DeepScanningDialogDelegate::FileRequestCallback,
+                           weak_ptr_factory_.GetWeakPtr(), path));
 
   FileSourceRequest* request_raw = request.get();
-  request_raw->GetRequestData(base::BindOnce(
-      &DeepScanningDialogDelegate::OnGotFileInfo,
-      weak_ptr_factory_.GetWeakPtr(), std::move(request), data_.paths[index]));
+  request_raw->GetRequestData(
+      base::BindOnce(&DeepScanningDialogDelegate::OnGotFileInfo,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(request), path));
 }
 
 void DeepScanningDialogDelegate::PrepareRequest(
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
index bc2503e..083152fa 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
@@ -248,8 +248,7 @@
   // Prepares an upload request for the file at |path|.  If the file
   // cannot be uploaded it will have a failure verdict added to |result_|.
   // Virtual so that it can be overridden in tests.
-  virtual void PrepareFileRequest(base::FilePath path,
-                                  AnalyzeCallback callback);
+  virtual void PrepareFileRequest(base::FilePath path);
 
   // Prepares an upload request for the given file.
   void AnalyzerCallback(int index,
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.cc
index d080e7bb..9198435 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.cc
@@ -137,20 +137,14 @@
   DeepScanningClientResponse response = status_callback_.is_null()
                                             ? DeepScanningClientResponse()
                                             : status_callback_.Run(path);
-  if (path.empty())
+  if (path.empty()) {
     StringRequestCallback(result_, response);
-  else
+  } else if (encryption_callback_.Run(path)) {
+    FileRequestCallback(path, BinaryUploadService::Result::FILE_ENCRYPTED,
+                        response);
+  } else {
     FileRequestCallback(path, result_, response);
-}
-
-void FakeDeepScanningDialogDelegate::PrepareFileRequest(
-    base::FilePath path,
-    AnalyzeCallback callback) {
-  safe_browsing::ArchiveAnalyzerResults results;
-  if (!encryption_callback_.is_null() && encryption_callback_.Run(path))
-    results.archived_binary.Add()->set_is_encrypted(true);
-
-  std::move(callback).Run(results);
+  }
 }
 
 void FakeDeepScanningDialogDelegate::UploadTextForDeepScanning(
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h b/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h
index 347f399..7dff16b3 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h
@@ -95,8 +95,6 @@
                 std::unique_ptr<BinaryUploadService::Request> request);
 
   // DeepScanningDialogDelegate overrides.
-  void PrepareFileRequest(base::FilePath path,
-                          AnalyzeCallback callback) override;
   void UploadTextForDeepScanning(
       std::unique_ptr<BinaryUploadService::Request> request) override;
   void UploadFileForDeepScanning(
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.cc b/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.cc
index e548ff5..40c4021 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.cc
@@ -6,7 +6,11 @@
 
 #include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
+#include "chrome/browser/file_util_service.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
+#include "chrome/common/safe_browsing/archive_analyzer_results.h"
+#include "chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h"
+#include "chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 
@@ -109,14 +113,52 @@
                      weakptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void FileSourceRequest::OnArchiveAnalysisComplete(
+    DataCallback callback,
+    std::pair<BinaryUploadService::Result, Data> result_and_data,
+    const ArchiveAnalyzerResults& results) {
+  has_cached_result_ = true;
+  set_digest(result_and_data.second.hash);
+  contains_encrypted_parts_ = std::any_of(
+      results.archived_binary.begin(), results.archived_binary.end(),
+      [](const auto& binary) { return binary.is_encrypted(); });
+
+  if (contains_encrypted_parts_)
+    cached_result_ = BinaryUploadService::Result::FILE_ENCRYPTED;
+  else
+    cached_result_ = result_and_data.first;
+
+  cached_data_ = result_and_data.second;
+  std::move(callback).Run(cached_result_, cached_data_);
+}
+
 void FileSourceRequest::OnGotFileData(
     DataCallback callback,
     std::pair<BinaryUploadService::Result, Data> result_and_data) {
-  set_digest(result_and_data.second.hash);
-  has_cached_result_ = true;
-  cached_result_ = result_and_data.first;
-  cached_data_ = result_and_data.second;
-  std::move(callback).Run(result_and_data.first, result_and_data.second);
+  if (result_and_data.first != BinaryUploadService::Result::SUCCESS) {
+    OnArchiveAnalysisComplete(std::move(callback), std::move(result_and_data),
+                              ArchiveAnalyzerResults());
+    return;
+  }
+
+  base::OnceCallback<void(const ArchiveAnalyzerResults& results)>
+      analysis_callback =
+          base::BindOnce(&FileSourceRequest::OnArchiveAnalysisComplete,
+                         weakptr_factory_.GetWeakPtr(), std::move(callback),
+                         std::move(result_and_data));
+  base::FilePath::StringType ext(path_.FinalExtension());
+  std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
+  if (ext == FILE_PATH_LITERAL(".zip")) {
+    auto analyzer = base::MakeRefCounted<SandboxedZipAnalyzer>(
+        path_, std::move(analysis_callback), LaunchFileUtilService());
+    analyzer->Start();
+  } else if (ext == FILE_PATH_LITERAL(".rar")) {
+    auto analyzer = base::MakeRefCounted<SandboxedRarAnalyzer>(
+        path_, std::move(analysis_callback), LaunchFileUtilService());
+    analyzer->Start();
+  } else {
+    std::move(analysis_callback).Run(ArchiveAnalyzerResults());
+  }
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.h b/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.h
index b79f079..697147a 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_FILE_SOURCE_REQUEST_H_
 
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
+#include "chrome/common/safe_browsing/archive_analyzer_results.h"
 
 namespace safe_browsing {
 
@@ -28,7 +29,13 @@
       DataCallback callback,
       std::pair<BinaryUploadService::Result, Data> result_and_data);
 
+  void OnArchiveAnalysisComplete(
+      DataCallback callback,
+      std::pair<BinaryUploadService::Result, Data> result_and_data,
+      const ArchiveAnalyzerResults& results);
+
   bool has_cached_result_;
+  bool contains_encrypted_parts_;
   BinaryUploadService::Result cached_result_;
   Data cached_data_;
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request_unittest.cc
index 62284a8..e097f1f 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/file_source_request_unittest.cc
@@ -6,11 +6,15 @@
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h"
+#include "chrome/common/chrome_paths.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
@@ -249,4 +253,37 @@
   EXPECT_EQ(sync_data.hash, async_data.hash);
 }
 
+TEST(FileSourceRequestTest, DetectsEncryption) {
+  content::BrowserTaskEnvironment browser_task_environment;
+  content::InProcessUtilityThreadHelper in_process_utility_thread_helper;
+
+  base::FilePath test_zip;
+  EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_zip));
+  test_zip = test_zip.AppendASCII("safe_browsing")
+                 .AppendASCII("download_protection")
+                 .AppendASCII("encrypted.zip");
+
+  FileSourceRequest request(test_zip, base::DoNothing());
+
+  BinaryUploadService::Result out_result;
+  BinaryUploadService::Request::Data out_data;
+
+  base::RunLoop run_loop;
+  request.GetRequestData(base::BindLambdaForTesting(
+      [&run_loop, &out_result, &out_data](
+          BinaryUploadService::Result result,
+          const BinaryUploadService::Request::Data& data) {
+        out_result = result;
+        out_data = data;
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+
+  EXPECT_EQ(out_result, BinaryUploadService::Result::FILE_ENCRYPTED);
+  // sha256sum chrome/test/data/safe_browsing/download_protection/encrypted.zip
+  // | tr "[:lower:]" "[:upper:]"
+  EXPECT_EQ(out_data.hash,
+            "701FCEA8B2112FFAB257A8A8DFD3382ABCF047689AB028D42903E3B3AA488D9A");
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 3c381e7..e32785d 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h"
 #include "chrome/browser/safe_browsing/download_protection/deep_scanning_request.h"
 #include "chrome/browser/safe_browsing/download_protection/download_feedback_service.h"
-#include "chrome/browser/safe_browsing/download_protection/download_item_request.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "chrome/common/safe_browsing/download_type_util.h"
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc
index 3f134f54..b049bfce 100644
--- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc
@@ -10,10 +10,10 @@
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
+#include "chrome/browser/safe_browsing//cloud_content_scanning/file_source_request.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
 #include "chrome/browser/safe_browsing/dm_token_utils.h"
-#include "chrome/browser/safe_browsing/download_protection/download_item_request.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "chrome/browser/ui/browser.h"
@@ -175,11 +175,9 @@
   // Indicate we're now scanning the file.
   callback_.Run(DownloadCheckResult::ASYNC_SCANNING);
 
-  auto request = std::make_unique<DownloadItemRequest>(
-      item_, /*read_immediately=*/true,
-      base::BindOnce(&DeepScanningRequest::OnScanComplete,
-                     weak_ptr_factory_.GetWeakPtr()));
-  request->set_filename(item_->GetTargetFilePath().BaseName().AsUTF8Unsafe());
+  auto request = std::make_unique<FileSourceRequest>(
+      item_->GetFullPath(), base::BindOnce(&DeepScanningRequest::OnScanComplete,
+                                           weak_ptr_factory_.GetWeakPtr()));
 
   std::string raw_digest_sha256 = item_->GetHash();
   request->set_digest(
diff --git a/chrome/browser/safe_browsing/download_protection/download_item_request.cc b/chrome/browser/safe_browsing/download_protection/download_item_request.cc
deleted file mode 100644
index 8e3b861..0000000
--- a/chrome/browser/safe_browsing/download_protection/download_item_request.cc
+++ /dev/null
@@ -1,200 +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/safe_browsing/download_protection/download_item_request.h"
-
-#include "base/files/file_path.h"
-#include "base/task/post_task.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/file_util_service.h"
-#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
-#include "chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h"
-#include "chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h"
-#include "components/download/public/common/download_item.h"
-#include "components/prefs/pref_service.h"
-#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace safe_browsing {
-
-namespace {
-
-std::string GetFileContentsBlocking(base::FilePath path) {
-  base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
-  if (!file.IsValid())
-    return "";
-
-  int64_t file_size = file.GetLength();
-  if (static_cast<size_t>(file_size) > BinaryUploadService::kMaxUploadSizeBytes)
-    return "";
-
-  std::string contents;
-  contents.resize(file_size);
-
-  int64_t bytes_read = 0;
-  while (bytes_read < file_size) {
-    int64_t bytes_currently_read =
-        file.ReadAtCurrentPos(&contents[bytes_read], file_size - bytes_read);
-    if (bytes_currently_read == -1)
-      return "";
-
-    bytes_read += bytes_currently_read;
-  }
-
-  return contents;
-}
-
-int GetUnsupportedFiletypesPrefValue() {
-  return g_browser_process->local_state()->GetInteger(
-      prefs::kBlockUnsupportedFiletypes);
-}
-
-}  // namespace
-
-DownloadItemRequest::DownloadItemRequest(download::DownloadItem* item,
-                                         bool read_immediately,
-                                         BinaryUploadService::Callback callback)
-    : Request(std::move(callback)), item_(item), weakptr_factory_(this) {
-  if (read_immediately)
-    ReadFile();
-
-  item_->AddObserver(this);
-}
-
-DownloadItemRequest::~DownloadItemRequest() {
-  if (item_ != nullptr)
-    item_->RemoveObserver(this);
-}
-
-void DownloadItemRequest::GetRequestData(DataCallback callback) {
-  if (item_ == nullptr) {
-    std::move(callback).Run(BinaryUploadService::Result::UNKNOWN, Data());
-    return;
-  }
-
-  if (item_ && static_cast<size_t>(item_->GetTotalBytes()) >
-                   BinaryUploadService::kMaxUploadSizeBytes) {
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(callback),
-                       BinaryUploadService::Result::FILE_TOO_LARGE, Data()));
-    return;
-  }
-
-  bool malware = deep_scanning_request().has_malware_scan_request();
-  bool dlp = deep_scanning_request().has_dlp_scan_request();
-  if (item_ && (malware || dlp) &&
-      !FileTypeSupported(malware, dlp, item_->GetTargetFilePath())) {
-    bool block_file = false;
-    switch (GetUnsupportedFiletypesPrefValue()) {
-      case BLOCK_UNSUPPORTED_FILETYPES_NONE:
-      case BLOCK_UNSUPPORTED_FILETYPES_UPLOADS:
-        block_file = false;
-        break;
-      case BLOCK_UNSUPPORTED_FILETYPES_DOWNLOADS:
-      case BLOCK_UNSUPPORTED_FILETYPES_UPLOADS_AND_DOWNLOADS:
-        block_file = true;
-    }
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(callback),
-                       block_file
-                           ? BinaryUploadService::Result::UNSUPPORTED_FILE_TYPE
-                           : BinaryUploadService::Result::SUCCESS,
-                       Data()));
-    return;
-  }
-
-  if (is_data_valid_) {
-    RunPendingGetFileContentsCallback(std::move(callback));
-    return;
-  }
-
-  pending_callbacks_.push_back(std::move(callback));
-}
-
-void DownloadItemRequest::RunPendingGetFileContentsCallback(
-    DataCallback callback) {
-  if (is_data_encrypted_) {
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(callback),
-                       BinaryUploadService::Result::FILE_ENCRYPTED, Data()));
-    return;
-  }
-
-  if (is_data_valid_) {
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  BinaryUploadService::Result::SUCCESS, data_));
-    return;
-  }
-}
-
-void DownloadItemRequest::OnDownloadUpdated(download::DownloadItem* download) {
-  if (!is_data_valid_ && download == item_ &&
-      item_->GetFullPath() == item_->GetTargetFilePath())
-    ReadFile();
-}
-
-void DownloadItemRequest::OnDownloadDestroyed(
-    download::DownloadItem* download) {
-  if (download == item_)
-    item_ = nullptr;
-}
-
-void DownloadItemRequest::ReadFile() {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
-      base::BindOnce(&GetFileContentsBlocking, item_->GetFullPath()),
-      base::BindOnce(&DownloadItemRequest::OnGotFileContents,
-                     weakptr_factory_.GetWeakPtr()));
-}
-
-void DownloadItemRequest::OnGotFileContents(std::string contents) {
-  data_.contents = std::move(contents);
-  base::FilePath current_path;
-  base::FilePath::StringType extension;
-  if (item_) {
-    current_path = item_->GetFullPath();
-    extension = item_->GetTargetFilePath().Extension();
-  }
-
-  if (extension == FILE_PATH_LITERAL(".zip")) {
-    auto analyzer = base::MakeRefCounted<SandboxedZipAnalyzer>(
-        current_path,
-        base::BindOnce(&DownloadItemRequest::OnCheckedForEncryption,
-                       weakptr_factory_.GetWeakPtr()),
-        LaunchFileUtilService());
-    analyzer->Start();
-  } else if (extension == FILE_PATH_LITERAL(".rar")) {
-    auto analyzer = base::MakeRefCounted<SandboxedRarAnalyzer>(
-        current_path,
-        base::BindOnce(&DownloadItemRequest::OnCheckedForEncryption,
-                       weakptr_factory_.GetWeakPtr()),
-        LaunchFileUtilService());
-    analyzer->Start();
-  } else {
-    OnCheckedForEncryption(ArchiveAnalyzerResults());
-  }
-}
-
-void DownloadItemRequest::OnCheckedForEncryption(
-    const ArchiveAnalyzerResults& results) {
-  is_data_valid_ = true;
-  is_data_encrypted_ = std::any_of(
-      results.archived_binary.begin(), results.archived_binary.end(),
-      [](const auto& binary) { return binary.is_encrypted(); });
-
-  for (auto& callback : pending_callbacks_) {
-    RunPendingGetFileContentsCallback(std::move(callback));
-  }
-
-  pending_callbacks_.clear();
-}
-
-}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/download_protection/download_item_request.h b/chrome/browser/safe_browsing/download_protection/download_item_request.h
deleted file mode 100644
index 82ee386d..0000000
--- a/chrome/browser/safe_browsing/download_protection/download_item_request.h
+++ /dev/null
@@ -1,78 +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_SAFE_BROWSING_DOWNLOAD_PROTECTION_DOWNLOAD_ITEM_REQUEST_H_
-#define CHROME_BROWSER_SAFE_BROWSING_DOWNLOAD_PROTECTION_DOWNLOAD_ITEM_REQUEST_H_
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
-#include "chrome/common/safe_browsing/archive_analyzer_results.h"
-#include "components/download/public/common/download_item.h"
-
-namespace safe_browsing {
-
-// This class implements the BinaryUploadService::Request interface for a
-// particular DownloadItem. It is neither moveable nor copyable.
-class DownloadItemRequest : public BinaryUploadService::Request,
-                            download::DownloadItem::Observer {
- public:
-  // Create a DownloadItemRequest for the given |item|. If |read_immediately| is
-  // true, try to read the file contents right away. Otherwise, wait until the
-  // file has been renamed to its final path. If the caller expects |item| to be
-  // renamed imminently, it's recommended to set |read_immediately| to false,
-  // to avoid race conditions while reading the file.
-  DownloadItemRequest(download::DownloadItem* item,
-                      bool read_immediately,
-                      BinaryUploadService::Callback callback);
-  ~DownloadItemRequest() override;
-
-  DownloadItemRequest(const DownloadItemRequest&) = delete;
-  DownloadItemRequest& operator=(const DownloadItemRequest&) = delete;
-  DownloadItemRequest(DownloadItemRequest&&) = delete;
-  DownloadItemRequest& operator=(DownloadItemRequest&&) = delete;
-
-  // BinaryUploadService::Request implementation.
-  void GetRequestData(DataCallback callback) override;
-
-  // download::DownloadItem::Observer implementation.
-  void OnDownloadDestroyed(download::DownloadItem* download) override;
-  void OnDownloadUpdated(download::DownloadItem* download) override;
-
- private:
-  void ReadFile();
-
-  void OnGotFileContents(std::string contents);
-
-  // Calls to GetFileContents can be deferred if the download item is not yet
-  // renamed to its final location. When ready, this method runs one of those
-  // callbacks. The callbacks are all run asynchronously, as they may delete
-  // |this|.
-  void RunPendingGetFileContentsCallback(DataCallback callback);
-
-  // Called when the file contents have finished being checked for encryption.
-  void OnCheckedForEncryption(const ArchiveAnalyzerResults& results);
-
-  // Pointer the download item for upload. This must be accessed only the UI
-  // thread. Unowned.
-  download::DownloadItem* item_;
-
-  // The file's data.
-  Data data_;
-
-  // Is the |data_| member valid? It becomes valid once the file has been
-  // read successfully.
-  bool is_data_valid_ = false;
-
-  // Is the |data_| member encrypted?
-  bool is_data_encrypted_ = false;
-
-  // All pending callbacks to GetFileContents before the download item is ready.
-  std::vector<DataCallback> pending_callbacks_;
-
-  base::WeakPtrFactory<DownloadItemRequest> weakptr_factory_;
-};
-}  // namespace safe_browsing
-
-#endif  // CHROME_BROWSER_SAFE_BROWSING_DOWNLOAD_PROTECTION_DOWNLOAD_ITEM_REQUEST_H_
diff --git a/chrome/browser/safe_browsing/download_protection/download_item_request_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_item_request_unittest.cc
deleted file mode 100644
index 9055108..0000000
--- a/chrome/browser/safe_browsing/download_protection/download_item_request_unittest.cc
+++ /dev/null
@@ -1,92 +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/safe_browsing/download_protection/download_item_request.h"
-
-#include "base/bind_helpers.h"
-#include "base/callback_forward.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/run_loop.h"
-#include "components/download/public/common/mock_download_item.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace safe_browsing {
-
-using ::testing::Return;
-using ::testing::ReturnRef;
-
-class DownloadItemRequestTest : public ::testing::TestWithParam<bool> {
- public:
-  DownloadItemRequestTest()
-      : item_(),
-        request_(&item_, /*read_immediately=*/false, base::DoNothing()) {}
-
-  void SetUp() override {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    download_path_ = temp_dir_.GetPath().AppendASCII("download_location");
-    download_temporary_path_ =
-        temp_dir_.GetPath().AppendASCII("temporary_location");
-
-    base::File file(download_path_,
-                    base::File::FLAG_CREATE | base::File::FLAG_WRITE);
-    ASSERT_TRUE(file.IsValid());
-
-    download_contents_ = large_contents() ? std::string(51 * 1024 * 1024, 'a')
-                                          : "download contents";
-    file.Write(0, download_contents_.c_str(), download_contents_.size());
-    file.Close();
-
-    EXPECT_CALL(item_, GetTotalBytes())
-        .WillRepeatedly(Return(download_contents_.size()));
-    EXPECT_CALL(item_, GetTargetFilePath())
-        .WillRepeatedly(ReturnRef(download_path_));
-    EXPECT_CALL(item_, GetFullPath()).WillRepeatedly(ReturnRef(download_path_));
-  }
-
-  bool large_contents() const { return GetParam(); }
-
- protected:
-  content::BrowserTaskEnvironment task_environment_;
-  download::MockDownloadItem item_;
-  DownloadItemRequest request_;
-  base::ScopedTempDir temp_dir_;
-  base::FilePath download_path_;
-  base::FilePath download_temporary_path_;
-  std::string download_contents_;
-};
-
-TEST_P(DownloadItemRequestTest, GetsContentsWaitsUntilRename) {
-  ON_CALL(item_, GetFullPath())
-      .WillByDefault(ReturnRef(download_temporary_path_));
-
-  std::string download_contents = "";
-  request_.GetRequestData(base::BindOnce(
-      [](std::string* target_contents, BinaryUploadService::Result result,
-         const BinaryUploadService::Request::Data& data) {
-        *target_contents = data.contents;
-      },
-      &download_contents));
-  content::RunAllTasksUntilIdle();
-  EXPECT_EQ(download_contents, "");
-
-  ON_CALL(item_, GetFullPath()).WillByDefault(ReturnRef(download_path_));
-  item_.NotifyObserversDownloadUpdated();
-
-  content::RunAllTasksUntilIdle();
-
-  // The contents should not be read if they are too large.
-  if (large_contents())
-    EXPECT_EQ(download_contents, "");
-  else
-    EXPECT_EQ(download_contents, "download contents");
-}
-
-INSTANTIATE_TEST_SUITE_P(DownloadItemRequestTest,
-                         DownloadItemRequestTest,
-                         testing::Bool());
-
-}  // namespace safe_browsing
diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc
index f131e22..3d942d3c 100644
--- a/chrome/browser/shell_integration_win.cc
+++ b/chrome/browser/shell_integration_win.cc
@@ -735,20 +735,6 @@
   // This needs to happen (e.g. so that the appid is fixed and the
   // run-time Chrome icon is merged with the taskbar shortcut), but it is not an
   // urgent task.
-  base::FilePath taskbar_path;
-  if (!base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_path)) {
-    NOTREACHED();
-    return;
-  }
-
-  // Migrate any pinned shortcuts in ImplicitApps sub-directories.
-  base::FilePath implicit_apps_path;
-  if (!base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS,
-                              &implicit_apps_path)) {
-    NOTREACHED();
-    return;
-  }
-
   // MigrateTaskbarPinsCallback just calls MigrateShortcutsInPathInternal
   // several times with different parameters.  Each call may or may not load
   // DLL's. Since the callback may take the loader lock several times, and this
@@ -761,10 +747,16 @@
   base::ThreadPool::CreateCOMSTATaskRunner(
       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
        base::ThreadPolicy::MUST_USE_FOREGROUND})
-      ->PostTaskAndReply(FROM_HERE,
-                         base::BindOnce(&MigrateTaskbarPinsCallback,
-                                        taskbar_path, implicit_apps_path),
-                         std::move(completion_callback));
+      ->PostTaskAndReply(
+          FROM_HERE, base::BindOnce([]() {
+            base::FilePath taskbar_path;
+            base::FilePath implicit_apps_path;
+            base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_path);
+            base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS,
+                                   &implicit_apps_path);
+            MigrateTaskbarPinsCallback(taskbar_path, implicit_apps_path);
+          }),
+          std::move(completion_callback));
 }
 
 void MigrateTaskbarPinsCallback(const base::FilePath& taskbar_path,
@@ -775,8 +767,12 @@
     return;
   base::FilePath chrome_proxy_path(web_app::GetChromeProxyPath());
 
-  MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
-                                       taskbar_path);
+  if (!taskbar_path.empty()) {
+    MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
+                                         taskbar_path);
+  }
+  if (implicit_apps_path.empty())
+    return;
   base::FileEnumerator directory_enum(implicit_apps_path, /*recursive=*/false,
                                       base::FileEnumerator::DIRECTORIES);
   for (base::FilePath implicit_app_sub_directory = directory_enum.Next();
diff --git a/chrome/browser/sync/test/integration/password_manager_sync_test.cc b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
new file mode 100644
index 0000000..f836f9b9
--- /dev/null
+++ b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
@@ -0,0 +1,256 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "base/callback_list.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/password_manager/account_storage/account_password_store_factory.h"
+#include "chrome/browser/password_manager/password_manager_test_base.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/sync/test/integration/passwords_helper.h"
+#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/integration/secondary_account_helper.h"
+#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
+#include "chrome/browser/sync/test/integration/sync_test.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 "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/password_manager/core/browser/password_manager_test_utils.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
+#include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "components/sync/driver/profile_sync_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
+#include "net/dns/mock_host_resolver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using testing::ElementsAre;
+
+MATCHER_P2(MatchesLogin, username, password, "") {
+  return arg->username_value == base::UTF8ToUTF16(username) &&
+         arg->password_value == base::UTF8ToUTF16(password);
+}
+
+// Note: This helper applies to ChromeOS too, but is currently unused there. So
+// define it out to prevent a compile error due to the unused function.
+#if !defined(OS_CHROMEOS)
+void GetNewTab(Browser* browser, content::WebContents** web_contents) {
+  PasswordManagerBrowserTestBase::GetNewTab(browser, web_contents);
+}
+#endif  // !defined(OS_CHROMEOS)
+
+class PasswordSyncActiveChecker : public SingleClientStatusChangeChecker {
+ public:
+  explicit PasswordSyncActiveChecker(syncer::ProfileSyncService* service)
+      : SingleClientStatusChangeChecker(service) {}
+  ~PasswordSyncActiveChecker() override = default;
+
+  // SingleClientStatusChangeChecker.
+  bool IsExitConditionSatisfied(std::ostream* os) override {
+    return service()->GetActiveDataTypes().Has(syncer::PASSWORDS);
+  }
+};
+
+// This test fixture is similar to SingleClientPasswordsSyncTest, but it also
+// sets up all the necessary test hooks etc for PasswordManager code (like
+// PasswordManagerBrowserTestBase), to allow for integration tests covering
+// both Sync and the PasswordManager.
+class PasswordManagerSyncTest : public SyncTest {
+ public:
+  PasswordManagerSyncTest() : SyncTest(SINGLE_CLIENT) {
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{switches::kSyncUSSPasswords,
+                              password_manager::features::
+                                  kEnablePasswordsAccountStorage},
+        /*disabled_features=*/{});
+  }
+  ~PasswordManagerSyncTest() override = default;
+
+  void SetUpInProcessBrowserTestFixture() override {
+    SyncTest::SetUpInProcessBrowserTestFixture();
+
+    test_signin_client_factory_ =
+        secondary_account_helper::SetUpSigninClient(&test_url_loader_factory_);
+
+    will_create_browser_context_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterWillCreateBrowserContextServicesCallbackForTesting(
+                base::BindRepeating(&PasswordManagerSyncTest::
+                                        OnWillCreateBrowserContextServices));
+  }
+
+  static void OnWillCreateBrowserContextServices(
+      content::BrowserContext* context) {
+    // Use TestPasswordStore to remove a possible race. Normally the
+    // PasswordStore does its database manipulation on a background thread,
+    // which creates a possible race during navigation. Specifically the
+    // PasswordManager will ignore any forms in a page if the load from the
+    // PasswordStore has not completed.
+    // TODO(crbug.com/1058339): Investigate whether these test doubles are
+    // really required, or whether we can use the real stores and add some
+    // waiting logic.
+    PasswordStoreFactory::GetInstance()->SetTestingFactory(
+        context,
+        base::BindRepeating(&password_manager::BuildPasswordStoreWithArgs<
+                                content::BrowserContext,
+                                password_manager::TestPasswordStore, bool>,
+                            /*is_account_store=*/false));
+    AccountPasswordStoreFactory::GetInstance()->SetTestingFactory(
+        context,
+        base::BindRepeating(&password_manager::BuildPasswordStoreWithArgs<
+                                content::BrowserContext,
+                                password_manager::TestPasswordStore, bool>,
+                            /*is_account_store=*/true));
+  }
+
+  void SetUpOnMainThread() override {
+    SyncTest::SetUpOnMainThread();
+
+    ASSERT_TRUE(embedded_test_server()->Start());
+    host_resolver()->AddRule("*", "127.0.0.1");
+  }
+
+  void TearDownOnMainThread() override {
+    ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+    SyncTest::TearDownOnMainThread();
+  }
+
+  // Synchronously reads all credentials from the profile password store and
+  // returns them.
+  std::vector<std::unique_ptr<autofill::PasswordForm>>
+  GetAllLoginsFromProfilePasswordStore() {
+    scoped_refptr<password_manager::PasswordStore> password_store =
+        passwords_helper::GetPasswordStore(0);
+    PasswordStoreResultsObserver syncer;
+    password_store->GetAllLoginsWithAffiliationAndBrandingInformation(&syncer);
+    return syncer.WaitForResults();
+  }
+
+  // Synchronously reads all credentials from the account password store and
+  // returns them.
+  std::vector<std::unique_ptr<autofill::PasswordForm>>
+  GetAllLoginsFromAccountPasswordStore() {
+    scoped_refptr<password_manager::PasswordStore> password_store =
+        passwords_helper::GetAccountPasswordStore(0);
+    PasswordStoreResultsObserver syncer;
+    password_store->GetAllLoginsWithAffiliationAndBrandingInformation(&syncer);
+    return syncer.WaitForResults();
+  }
+
+  void NavigateToFile(content::WebContents* web_contents,
+                      const std::string& path) {
+    ASSERT_EQ(web_contents,
+              GetBrowser(0)->tab_strip_model()->GetActiveWebContents());
+    NavigationObserver observer(web_contents);
+    GURL url = embedded_test_server()->GetURL(path);
+    ui_test_utils::NavigateToURL(GetBrowser(0), url);
+    observer.Wait();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+
+  secondary_account_helper::ScopedSigninClientFactory
+      test_signin_client_factory_;
+
+  std::unique_ptr<
+      base::CallbackList<void(content::BrowserContext*)>::Subscription>
+      will_create_browser_context_services_subscription_;
+};
+
+#if !defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest, ChooseDestinationStore) {
+  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+  content::WebContents* web_contents = nullptr;
+  GetNewTab(GetBrowser(0), &web_contents);
+
+  // Setup Sync for a secondary account (i.e. in transport mode).
+  secondary_account_helper::SignInSecondaryAccount(
+      GetProfile(0), &test_url_loader_factory_, "user@email.com");
+  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
+  ASSERT_FALSE(GetSyncService(0)->IsSyncFeatureEnabled());
+
+  // Let the user opt in to the passwords account storage, and wait for it to
+  // become active.
+  password_manager_util::SetAccountStorageOptIn(GetProfile(0)->GetPrefs(),
+                                                GetSyncService(0), true);
+  PasswordSyncActiveChecker(GetSyncService(0)).Wait();
+  ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::PASSWORDS));
+
+  // Part 1: Save a password; it should go into the account store by default.
+  {
+    // Navigate to a page with a password form.
+    NavigateToFile(web_contents, "/password/password_form.html");
+
+    // Fill out and submit the password form.
+    NavigationObserver observer(web_contents);
+    std::string fill_and_submit =
+        "document.getElementById('username_field').value = 'accountuser';"
+        "document.getElementById('password_field').value = 'accountpass';"
+        "document.getElementById('input_submit_button').click()";
+    ASSERT_TRUE(content::ExecJs(web_contents, fill_and_submit));
+    observer.Wait();
+
+    // Save the password and check the store.
+    BubbleObserver bubble_observer(web_contents);
+    ASSERT_TRUE(bubble_observer.IsSavePromptShownAutomatically());
+    bubble_observer.AcceptSavePrompt();
+
+    std::vector<std::unique_ptr<autofill::PasswordForm>> account_credentials =
+        GetAllLoginsFromAccountPasswordStore();
+    EXPECT_THAT(account_credentials,
+                ElementsAre(MatchesLogin("accountuser", "accountpass")));
+  }
+
+  // Part 2: Mimic the user choosing to save locally; now a newly saved password
+  // should end up in the profile store.
+  password_manager_util::SetDefaultPasswordStore(
+      GetProfile(0)->GetPrefs(), GetSyncService(0),
+      autofill::PasswordForm::Store::kProfileStore);
+  {
+    // Navigate to a page with a password form.
+    // TODO(crbug.com/1058339): If we use the same URL as in part 1 here, then
+    // the test fails because the *account* data gets filled and submitted
+    // again. This is because the password manager is "smart" and prefers
+    // user-typed values (including autofilled-on-pageload ones) over
+    // script-provided values, see
+    // https://cs.chromium.org/chromium/src/components/autofill/content/renderer/form_autofill_util.cc?rcl=e38f0c99fe45ef81bd09d97f235c3dee64e2bd9f&l=1749
+    // and
+    // https://cs.chromium.org/chromium/src/components/autofill/content/renderer/password_autofill_agent.cc?rcl=63830d3f4b7f5fceec9609d83cf909d0cad04bb2&l=1855
+    // Some PasswordManager browser tests work around this by disabling
+    // autofill on pageload, see PasswordManagerBrowserTestWithAutofillDisabled.
+    // NavigateToFile(web_contents, "/password/password_form.html");
+    NavigateToFile(web_contents, "/password/simple_password.html");
+
+    // Fill out and submit the password form.
+    NavigationObserver observer(web_contents);
+    std::string fill_and_submit =
+        "document.getElementById('username_field').value = 'localuser';"
+        "document.getElementById('password_field').value = 'localpass';"
+        "document.getElementById('input_submit_button').click()";
+    ASSERT_TRUE(content::ExecJs(web_contents, fill_and_submit));
+    observer.Wait();
+
+    // Save the password and check the store.
+    BubbleObserver bubble_observer(web_contents);
+    ASSERT_TRUE(bubble_observer.IsSavePromptShownAutomatically());
+    bubble_observer.AcceptSavePrompt();
+
+    std::vector<std::unique_ptr<autofill::PasswordForm>> profile_credentials =
+        GetAllLoginsFromProfilePasswordStore();
+    EXPECT_THAT(profile_credentials,
+                ElementsAre(MatchesLogin("localuser", "localpass")));
+  }
+}
+#endif  // !defined(OS_CHROMEOS)
+
+}  // namespace
diff --git a/chrome/browser/ui/ash/homescreen_interactive_uitest.cc b/chrome/browser/ui/ash/homescreen_interactive_uitest.cc
index 3044e771..dab02f9 100644
--- a/chrome/browser/ui/ash/homescreen_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/homescreen_interactive_uitest.cc
@@ -52,7 +52,8 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_F(HomescreenTest, ShowHideLauncher) {
+// Disabled: crbug.com/1060648
+IN_PROC_BROWSER_TEST_F(HomescreenTest, DISABLED_ShowHideLauncher) {
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
 
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_button.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_button.cc
index 2a40adf..50ad644 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_button.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_button.cc
@@ -47,6 +47,13 @@
   return extensions_container_->GetToolbarActionSize();
 }
 
+gfx::Size ExtensionsToolbarButton::GetMinimumSize() const {
+  const int icon_size = GetIconSize();
+  gfx::Size min_size(icon_size, icon_size);
+  min_size.SetToMin(GetPreferredSize());
+  return min_size;
+}
+
 void ExtensionsToolbarButton::OnBoundsChanged(
     const gfx::Rect& previous_bounds) {
   // Because this button is in a container and doesn't necessarily take up the
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_button.h b/chrome/browser/ui/views/extensions/extensions_toolbar_button.h
index b2d33a7f..73263d55 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_button.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_button.h
@@ -27,6 +27,7 @@
  private:
   // ToolbarButton:
   gfx::Size CalculatePreferredSize() const override;
+  gfx::Size GetMinimumSize() const override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   const char* GetClassName() const override;
 
diff --git a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
index 29b2eec..101fc03 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
@@ -282,7 +282,8 @@
   else
     combobox->SetSelectedRow(1);
 
-  // TODO(crbug.com/1044038): SetAccessibleName of the combobox.
+  // TODO(crbug.com/1044038): Use an internationalized string instead.
+  combobox->SetAccessibleName(base::ASCIIToUTF16("Destination"));
   return combobox;
 }
 
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 2f9147e..5e9036d 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -336,6 +336,11 @@
   html_source->AddResourcePath(
       "images/password_check_neutral_dark.svg",
       IDR_SETTINGS_IMAGES_PASSWORD_CHECK_NEUTRAL_DARK_SVG);
+  html_source->AddResourcePath("images/password_check_positive.svg",
+                               IDR_SETTINGS_IMAGES_PASSWORD_CHECK_POSITIVE_SVG);
+  html_source->AddResourcePath(
+      "images/password_check_positive_dark.svg",
+      IDR_SETTINGS_IMAGES_PASSWORD_CHECK_POSITIVE_DARK_SVG);
 
   // Only used in Polymer 3, see https://crbug.com/1026426.
   html_source->AddResourcePath("settings.js", IDR_SETTINGS_SETTINGS_ROLLUP_JS);
diff --git a/chrome/credential_provider/eventlog/BUILD.gn b/chrome/credential_provider/eventlog/BUILD.gn
index 6ddc4355..a0e01ac 100644
--- a/chrome/credential_provider/eventlog/BUILD.gn
+++ b/chrome/credential_provider/eventlog/BUILD.gn
@@ -24,7 +24,7 @@
     "gcp_eventlog_provider.cc",
   ]
 
-  if (!is_asan && !use_clang_coverage) {
+  if (!is_asan && !use_clang_profiling) {
     no_default_deps = true
     ldflags = [ "/NOENTRY" ]
   }
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc
index 502fc4f..6a53ac6 100644
--- a/chrome/installer/util/shell_util.cc
+++ b/chrome/installer/util/shell_util.cc
@@ -1603,8 +1603,8 @@
       folder_to_append = InstallUtil::GetChromeAppsShortcutDirName();
       break;
     case SHORTCUT_LOCATION_TASKBAR_PINS:
-      dir_key = base::DIR_TASKBAR_PINS;
-      break;
+      // This directory isn't guaranteed to exist.
+      return base::PathService::Get(base::DIR_TASKBAR_PINS, path);
     case SHORTCUT_LOCATION_APP_SHORTCUTS:
       // TODO(huangs): Move GetAppShortcutsFolder() logic into base_paths_win.
       return GetAppShortcutsFolder(level, path);
diff --git a/chrome/services/util_win/util_win_impl.cc b/chrome/services/util_win/util_win_impl.cc
index 9ff113e..32f1035 100644
--- a/chrome/services/util_win/util_win_impl.cc
+++ b/chrome/services/util_win/util_win_impl.cc
@@ -188,21 +188,24 @@
 
 bool IsPinnedToTaskbarHelper::GetResult() {
   base::FilePath current_exe;
-  base::PathService::Get(base::FILE_EXE, &current_exe);
+  if (!base::PathService::Get(base::FILE_EXE, &current_exe))
+    return false;
 
   InstallUtil::ProgramCompare current_exe_compare(current_exe);
   // Look into the "Quick Launch\User Pinned\TaskBar" folder.
   base::FilePath taskbar_pins_dir;
-  base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_pins_dir);
-  if (DirectoryContainsPinnedShortcutForProgram(taskbar_pins_dir,
+  if (base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_pins_dir) &&
+      DirectoryContainsPinnedShortcutForProgram(taskbar_pins_dir,
                                                 current_exe_compare)) {
     return true;
   }
 
   // Check all folders in ImplicitAppShortcuts.
   base::FilePath implicit_app_shortcuts_dir;
-  base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS,
-                         &implicit_app_shortcuts_dir);
+  if (!base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS,
+                              &implicit_app_shortcuts_dir)) {
+    return false;
+  }
   base::FileEnumerator directory_enum(implicit_app_shortcuts_dir, false,
                                       base::FileEnumerator::DIRECTORIES);
   for (base::FilePath directory = directory_enum.Next(); !directory.empty();
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 58eb1a27..2763ff2 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4968,7 +4968,6 @@
       "../browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc",
       "../browser/safe_browsing/download_protection/download_feedback_service_unittest.cc",
       "../browser/safe_browsing/download_protection/download_feedback_unittest.cc",
-      "../browser/safe_browsing/download_protection/download_item_request_unittest.cc",
       "../browser/safe_browsing/download_protection/download_protection_service_unittest.cc",
       "../browser/safe_browsing/download_protection/file_analyzer_unittest.cc",
       "../browser/safe_browsing/download_protection/path_sanitizer_unittest.cc",
@@ -6283,6 +6282,7 @@
       "../browser/sync/test/integration/enable_disable_test.cc",
       "../browser/sync/test/integration/local_sync_test.cc",
       "../browser/sync/test/integration/migration_test.cc",
+      "../browser/sync/test/integration/password_manager_sync_test.cc",
       "../browser/sync/test/integration/single_client_app_settings_sync_test.cc",
       "../browser/sync/test/integration/single_client_apps_sync_test.cc",
       "../browser/sync/test/integration/single_client_autofill_profile_sync_test.cc",
@@ -6334,6 +6334,7 @@
     ]
 
     data = [
+      "//chrome/test/data/password/",
       "//chrome/test/data/sync/",
       "//net/tools/testserver/",
       "//third_party/pywebsocket3/src/mod_pywebsocket/",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/webapps/WebApkInfoBuilder.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/webapps/WebApkInfoBuilder.java
index e60d038..dc70df9 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/webapps/WebApkInfoBuilder.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/webapps/WebApkInfoBuilder.java
@@ -50,7 +50,7 @@
      * Builds {@link WebApkInfo} object using options that have been set.
      */
     public WebApkInfo build() {
-        return WebApkInfo.create(mUrl, mScope, null, null, null, null, null, mDisplayMode,
+        return WebApkInfo.create(mUrl, mScope, null, null, null, null, mDisplayMode,
                 ScreenOrientationValues.DEFAULT, ShortcutSource.UNKNOWN,
                 ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING,
                 ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING, Color.WHITE,
diff --git a/chrome/test/data/extensions/test_file_with_trusted_types.html b/chrome/test/data/extensions/test_file_with_trusted_types.html
index 85485a05..e5e85c4 100644
--- a/chrome/test/data/extensions/test_file_with_trusted_types.html
+++ b/chrome/test/data/extensions/test_file_with_trusted_types.html
@@ -1,7 +1,7 @@
 <!doctype html>
 <html>
 <head>
-  <meta http-equiv="Content-Security-Policy" content="trusted-types *">
+  <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'">
   <title>Define a restrictive CSP trusted-types directive.</title>
 </head>
 <body>
diff --git a/chrome/test/data/webui/settings/password_check_test.js b/chrome/test/data/webui/settings/password_check_test.js
index 747411a9..75b5169 100644
--- a/chrome/test/data/webui/settings/password_check_test.js
+++ b/chrome/test/data/webui/settings/password_check_test.js
@@ -39,7 +39,8 @@
   }
 
   function isElementVisible(element) {
-    return !!element && !element.hidden && element.style.display != 'none';
+    return !!element && !element.hidden && element.style.display != 'none' &&
+        element.offsetParent !== null;  // Considers parents hiding |element|.
   }
 
   /**
@@ -642,6 +643,112 @@
             section.$.controlPasswordCheckButton.innerText);
       });
     });
+
+    // Test that the banner is in a state that shows the positive confirmation
+    // after a leak check finished.
+    test('testShowsPositiveBannerWhenIdle', function() {
+      const data = passwordManager.data;
+      assertEquals(PasswordCheckState.IDLE, data.checkStatus.state);
+      assertEquals(0, data.leakedCredentials.length);
+
+      const checkPasswordSection = createCheckPasswordSection();
+      return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
+        Polymer.dom.flush();
+        assertTrue(isElementVisible(checkPasswordSection.$$('#bannerImage')));
+        expectEquals(
+            'chrome://settings/images/password_check_positive.svg',
+            checkPasswordSection.$$('#bannerImage').src);
+      });
+    });
+
+    // Test that the banner is in a state that shows that the leak check is
+    // in progress but hasn't found anything yet.
+    test('testShowsNeutralBannerWhenRunning', function() {
+      const data = passwordManager.data;
+      assertEquals(0, data.leakedCredentials.length);
+      data.checkStatus = autofill_test_util.makePasswordCheckStatus(
+          /*state=*/ PasswordCheckState.RUNNING, /*checked=*/ 1,
+          /*remaining=*/ 5);
+
+      const checkPasswordSection = createCheckPasswordSection();
+      return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
+        Polymer.dom.flush();
+        assertTrue(isElementVisible(checkPasswordSection.$$('#bannerImage')));
+        expectEquals(
+            'chrome://settings/images/password_check_neutral.svg',
+            checkPasswordSection.$$('#bannerImage').src);
+      });
+    });
+
+    // Test that the banner is in a state that shows that the leak check is
+    // in progress but hasn't found anything yet.
+    test('testShowsNeutralBannerWhenCanceled', function() {
+      const data = passwordManager.data;
+      assertEquals(0, data.leakedCredentials.length);
+      data.checkStatus = autofill_test_util.makePasswordCheckStatus(
+          /*state=*/ PasswordCheckState.CANCELED);
+
+      const checkPasswordSection = createCheckPasswordSection();
+      return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
+        Polymer.dom.flush();
+        assertTrue(isElementVisible(checkPasswordSection.$$('#bannerImage')));
+        expectEquals(
+            'chrome://settings/images/password_check_neutral.svg',
+            checkPasswordSection.$$('#bannerImage').src);
+      });
+    });
+
+    // Test that the banner isn't visible as soon as the first leak is detected.
+    test('testLeaksHideBannerWhenRunning', function() {
+      const data = passwordManager.data;
+      data.checkStatus = autofill_test_util.makePasswordCheckStatus(
+          /*state=*/ PasswordCheckState.RUNNING, /*checked=*/ 1,
+          /*remaining=*/ 5);
+      data.leakedCredentials = [
+          autofill_test_util.makeCompromisedCredential(
+              'one.com', 'test4', 'LEAKED'),
+      ];
+
+      const checkPasswordSection = createCheckPasswordSection();
+      return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
+        Polymer.dom.flush();
+        expectFalse(isElementVisible(checkPasswordSection.$$('#bannerImage')));
+      });
+    });
+
+    // Test that the banner isn't visible if a leak is detected after a check.
+    test('testLeaksHideBannerWhenIdle', function() {
+      const data = passwordManager.data;
+      data.checkStatus = autofill_test_util.makePasswordCheckStatus(
+          /*state=*/ PasswordCheckState.IDLE);
+      data.leakedCredentials = [
+          autofill_test_util.makeCompromisedCredential(
+              'one.com', 'test4', 'LEAKED'),
+      ];
+
+      const checkPasswordSection = createCheckPasswordSection();
+      return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
+        Polymer.dom.flush();
+        expectFalse(isElementVisible(checkPasswordSection.$$('#bannerImage')));
+      });
+    });
+
+    // Test that the banner isn't visible if a leak is detected after canceling.
+    test('testLeaksHideBannerWhenCanceled', function() {
+      const data = passwordManager.data;
+      data.checkStatus = autofill_test_util.makePasswordCheckStatus(
+          /*state=*/ PasswordCheckState.CANCELED);
+      data.leakedCredentials = [
+          autofill_test_util.makeCompromisedCredential(
+              'one.com', 'test4', 'LEAKED'),
+      ];
+
+      const checkPasswordSection = createCheckPasswordSection();
+      return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
+        Polymer.dom.flush();
+        expectFalse(isElementVisible(checkPasswordSection.$$('#bannerImage')));
+      });
+    });
   });
   // #cr_define_end
 });
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index b24749c..4504f3b3 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -43,7 +43,6 @@
     content::BrowserContext* browser_context,
     scoped_refptr<content::SiteInstance> site_instance) {
   content::WebContents::CreateParams create_params(browser_context, nullptr);
-  create_params.routing_id = MSG_ROUTING_NONE;
   create_params.site_instance = site_instance;
   return content::WebContents::Create(create_params);
 }
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index a8b2ca9..cf2af3d 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12966.0.0
\ No newline at end of file
+12968.0.0
\ No newline at end of file
diff --git a/components/autofill_assistant/browser/basic_interactions.cc b/components/autofill_assistant/browser/basic_interactions.cc
index 4c8abea5..6b838803f 100644
--- a/components/autofill_assistant/browser/basic_interactions.cc
+++ b/components/autofill_assistant/browser/basic_interactions.cc
@@ -4,6 +4,9 @@
 
 #include "components/autofill_assistant/browser/basic_interactions.h"
 #include "base/bind_helpers.h"
+#include "base/i18n/time_formatting.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "components/autofill_assistant/browser/script_executor_delegate.h"
 #include "components/autofill_assistant/browser/trigger_context.h"
 #include "components/autofill_assistant/browser/user_model.h"
@@ -78,6 +81,74 @@
   return true;
 }
 
+bool ValueToString(UserModel* user_model,
+                   const std::string& result_model_identifier,
+                   const ToStringProto& proto) {
+  auto value = user_model->GetValue(proto.model_identifier());
+  std::string result;
+
+  if (!value.has_value()) {
+    DVLOG(2) << "Error evaluating " << __func__ << ": "
+             << proto.model_identifier() << " not found in model";
+    return false;
+  }
+  if (!AreAllValuesOfSize({*value}, 1)) {
+    DVLOG(2) << "Error evaluating " << __func__
+             << ": expected single value, but got a list instead";
+    return false;
+  }
+  if (AreAllValuesOfType({*value}, ValueProto::kUserActions)) {
+    DVLOG(2) << "Error evaluating " << __func__
+             << ": does not support stringifying user actions";
+    return false;
+  }
+  switch (value->kind_case()) {
+    case ValueProto::kStrings:
+      result = value->strings().values(0);
+      break;
+    case ValueProto::kBooleans:
+      result = value->booleans().values(0) ? "true" : "false";
+      break;
+    case ValueProto::kInts:
+      result = base::NumberToString(value->ints().values(0));
+      break;
+    case ValueProto::kUserActions:
+      NOTREACHED();
+      return false;
+    case ValueProto::kDates: {
+      if (proto.date_format().date_format().empty()) {
+        DVLOG(2) << "Error evaluating " << __func__ << ": date_format not set";
+        return false;
+      }
+      auto date = value->dates().values(0);
+      base::Time::Exploded exploded_time = {date.year(),
+                                            date.month(),
+                                            /* day_of_week = */ -1,
+                                            date.day(),
+                                            /* hour = */ 0,
+                                            /* minute = */ 0,
+                                            /* second = */ 0,
+                                            /* millisecond = */ 0};
+      base::Time time;
+      if (!base::Time::FromLocalExploded(exploded_time, &time)) {
+        DVLOG(2) << "Error evaluating " << __func__ << ": invalid date "
+                 << *value;
+        return false;
+      }
+
+      result = base::UTF16ToUTF8(base::TimeFormatWithPattern(
+          time, proto.date_format().date_format().c_str()));
+      break;
+    }
+    case ValueProto::KIND_NOT_SET:
+      DVLOG(2) << "Error evaluating " << __func__ << ": kind not set";
+      return false;
+  }
+
+  user_model->SetValue(result_model_identifier, SimpleValue(result));
+  return true;
+}
+
 }  // namespace
 
 base::WeakPtr<BasicInteractions> BasicInteractions::GetWeakPtr() {
@@ -129,6 +200,14 @@
       }
       return BooleanNot(delegate_->GetUserModel(),
                         proto.result_model_identifier(), proto.boolean_not());
+    case ComputeValueProto::kToString:
+      if (proto.to_string().model_identifier().empty()) {
+        DVLOG(2) << "Error computing ComputeValue::ToString: "
+                    "model_identifier not specified";
+        return false;
+      }
+      return ValueToString(delegate_->GetUserModel(),
+                           proto.result_model_identifier(), proto.to_string());
     case ComputeValueProto::KIND_NOT_SET:
       DVLOG(2) << "Error computing value: kind not set";
       return false;
diff --git a/components/autofill_assistant/browser/basic_interactions_unittest.cc b/components/autofill_assistant/browser/basic_interactions_unittest.cc
index 51d8423a..f153fd3 100644
--- a/components/autofill_assistant/browser/basic_interactions_unittest.cc
+++ b/components/autofill_assistant/browser/basic_interactions_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/autofill_assistant/browser/basic_interactions.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/icu_test_util.h"
 #include "base/test/mock_callback.h"
 #include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
 #include "components/autofill_assistant/browser/interactions.pb.h"
@@ -12,6 +13,16 @@
 
 namespace autofill_assistant {
 
+namespace {
+DateProto CreateDateProto(int year, int month, int day) {
+  DateProto proto;
+  proto.set_year(year);
+  proto.set_month(month);
+  proto.set_day(day);
+  return proto;
+}
+}  // namespace
+
 class BasicInteractionsTest : public testing::Test {
  protected:
   BasicInteractionsTest() { delegate_.SetUserModel(&user_model_); }
@@ -127,6 +138,64 @@
   EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
 }
 
+TEST_F(BasicInteractionsTest, ComputeValueToString) {
+  ComputeValueProto proto;
+  proto.mutable_to_string()->set_model_identifier("value");
+  user_model_.SetValue("value", SimpleValue(1));
+
+  // Result model identifier not set.
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+
+  // Integer
+  proto.set_result_model_identifier("output");
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+  EXPECT_EQ(*user_model_.GetValue("output"), SimpleValue(std::string("1")));
+
+  // Boolean
+  user_model_.SetValue("value", SimpleValue(true));
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+  EXPECT_EQ(*user_model_.GetValue("output"), SimpleValue(std::string("true")));
+
+  // String
+  user_model_.SetValue("value", SimpleValue(std::string("test asd")));
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+  EXPECT_EQ(*user_model_.GetValue("output"),
+            SimpleValue(std::string("test asd")));
+
+  // Date without format fails.
+  user_model_.SetValue("value", SimpleValue(CreateDateProto(2020, 10, 23)));
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+
+  // Date with format succeeds.
+  {
+    base::test::ScopedRestoreICUDefaultLocale locale(std::string("en_US"));
+    proto.mutable_to_string()->mutable_date_format()->set_date_format(
+        "EEE, MMM d y");
+    EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+    EXPECT_EQ(*user_model_.GetValue("output"),
+              SimpleValue(std::string("Fri, Oct 23, 2020")));
+  }
+
+  // Date in german locale.
+  {
+    base::test::ScopedRestoreICUDefaultLocale locale(std::string("de_DE"));
+    EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+    EXPECT_EQ(*user_model_.GetValue("output"),
+              SimpleValue(std::string("Fr., 23. Okt. 2020")));
+  }
+
+  // Empty value fails.
+  user_model_.SetValue("value", ValueProto());
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+
+  // Multi value fails.
+  ValueProto multi_value;
+  multi_value.mutable_booleans()->add_values(true);
+  multi_value.mutable_booleans()->add_values(false);
+  user_model_.SetValue("value", multi_value);
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+}
+
 TEST_F(BasicInteractionsTest, EndActionWithoutCallbackFails) {
   EndActionProto proto;
   ASSERT_DEATH(basic_interactions_.EndAction(proto),
diff --git a/components/autofill_assistant/browser/interactions.proto b/components/autofill_assistant/browser/interactions.proto
index 460c88ae..c390f55 100644
--- a/components/autofill_assistant/browser/interactions.proto
+++ b/components/autofill_assistant/browser/interactions.proto
@@ -35,6 +35,7 @@
     SetUserActionsProto set_user_actions = 5;
     EndActionProto end_action = 6;
     ShowCalendarPopupProto show_calendar_popup = 7;
+    SetTextProto set_text = 8;
   }
 }
 
@@ -81,6 +82,8 @@
     BooleanOrProto boolean_or = 3;
     // Computes the logical NOT of the specified model identifiers.
     BooleanNotProto boolean_not = 4;
+    // Creates a string representation of the specified value.
+    ToStringProto to_string = 5;
   }
 
   // The model identifier to write the result to.
@@ -107,6 +110,21 @@
   optional string model_identifier = 1;
 }
 
+// Creates a string representation of the specified value.
+message ToStringProto {
+  // The model identifier to stringify.
+  optional string model_identifier = 1;
+
+  // Optional format options.
+  oneof format_options { DateFormatProto date_format = 2; }
+}
+
+// A format string for a date, such as "EEE, MMM d". See
+// http://userguide.icu-project.org/formatparse/datetime for full specification.
+message DateFormatProto {
+  optional string date_format = 1;
+}
+
 // Displays a standard info popup.
 message ShowInfoPopupProto {
   optional InfoPopupProto info_popup = 1;
@@ -134,6 +152,9 @@
   optional string selected_item_indices_model_identifier = 3;
   // Whether to allow the selection of multiple items or not.
   optional bool allow_multiselect = 4;
+  // Optional output model identifier to store the names of the selected items
+  // in a StringList.
+  optional string selected_item_names_model_identifier = 5;
 }
 
 // Sets the list of available user actions. User actions are either direct
@@ -166,3 +187,12 @@
   // DateList.
   optional string max_date_model_identifier = 3;
 }
+
+// Modifies the displayed text of a text view.
+message SetTextProto {
+  // The model identifier containing the text to set. Must point to a single
+  // string.
+  optional string model_identifier = 1;
+  // The text view to modify. Must point to a text view.
+  optional string view_identifier = 2;
+}
diff --git a/components/external_intents/android/BUILD.gn b/components/external_intents/android/BUILD.gn
index f12ab019..52ade7e 100644
--- a/components/external_intents/android/BUILD.gn
+++ b/components/external_intents/android/BUILD.gn
@@ -6,8 +6,31 @@
 
 android_library("java") {
   sources = [
+    "java/src/org/chromium/components/external_intents/ExternalIntentsFeatureList.java",
     "java/src/org/chromium/components/external_intents/ExternalIntentsSwitches.java",
     "java/src/org/chromium/components/external_intents/ExternalNavigationParams.java",
     "java/src/org/chromium/components/external_intents/RedirectHandler.java",
   ]
+
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+  deps = [
+    "//base:base_java",
+    "//base:jni_java",
+  ]
+}
+
+generate_jni("jni_headers") {
+  sources = [ "java/src/org/chromium/components/external_intents/ExternalIntentsFeatureList.java" ]
+}
+
+static_library("android") {
+  sources = [
+    "external_intents_feature_list.cc",
+    "external_intents_feature_list.h",
+  ]
+
+  deps = [
+    ":jni_headers",
+    "//base",
+  ]
 }
diff --git a/components/external_intents/android/external_intents_feature_list.cc b/components/external_intents/android/external_intents_feature_list.cc
new file mode 100644
index 0000000..e442aca
--- /dev/null
+++ b/components/external_intents/android/external_intents_feature_list.cc
@@ -0,0 +1,53 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/external_intents/android/external_intents_feature_list.h"
+
+#include <jni.h>
+#include <stddef.h>
+#include <string>
+
+#include "base/android/jni_string.h"
+#include "components/external_intents/android/jni_headers/ExternalIntentsFeatureList_jni.h"
+
+namespace external_intents {
+
+namespace {
+
+// Array of features exposed through the Java ExternalIntentsFeatureList API.
+const base::Feature* kFeaturesExposedToJava[] = {
+    &kIntentBlockExternalFormRedirectsNoGesture,
+};
+
+const base::Feature* FindFeatureExposedToJava(const std::string& feature_name) {
+  for (const auto* feature : kFeaturesExposedToJava) {
+    if (feature->name == feature_name)
+      return feature;
+  }
+  NOTREACHED()
+      << "Queried feature cannot be found in ExternalIntentsFeatureList: "
+      << feature_name;
+  return nullptr;
+}
+
+}  // namespace
+
+// Alphabetical:
+const base::Feature kIntentBlockExternalFormRedirectsNoGesture{
+    "IntentBlockExternalFormRedirectsNoGesture",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+static jboolean JNI_ExternalIntentsFeatureList_IsInitialized(JNIEnv* env) {
+  return !!base::FeatureList::GetInstance();
+}
+
+static jboolean JNI_ExternalIntentsFeatureList_IsEnabled(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& jfeature_name) {
+  const base::Feature* feature = FindFeatureExposedToJava(
+      base::android::ConvertJavaStringToUTF8(env, jfeature_name));
+  return base::FeatureList::IsEnabled(*feature);
+}
+
+}  // namespace external_intents
diff --git a/components/external_intents/android/external_intents_feature_list.h b/components/external_intents/android/external_intents_feature_list.h
new file mode 100644
index 0000000..02388aa
--- /dev/null
+++ b/components/external_intents/android/external_intents_feature_list.h
@@ -0,0 +1,17 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURE_LIST_H_
+#define COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURE_LIST_H_
+
+#include "base/feature_list.h"
+
+namespace external_intents {
+
+// Alphabetical:
+extern const base::Feature kIntentBlockExternalFormRedirectsNoGesture;
+
+}  // namespace external_intents
+
+#endif  // COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURE_LIST_H_
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatureList.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatureList.java
new file mode 100644
index 0000000..96cf47df
--- /dev/null
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatureList.java
@@ -0,0 +1,70 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.external_intents;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.library_loader.LibraryLoader;
+
+/**
+ * Java accessor for base/feature_list.h state.
+ *
+ * This class provides methods to access values of feature flags registered in
+ * |kFeaturesExposedToJava| in components/external_intents/android/external_intents_feature_list.cc.
+ *
+ */
+@JNINamespace("external_intents")
+@MainDex
+public abstract class ExternalIntentsFeatureList {
+    /** Prevent instantiation. */
+    private ExternalIntentsFeatureList() {}
+
+    /**
+     * @return Whether the native FeatureList is initialized or not.
+     */
+    private static boolean isNativeInitialized() {
+        if (!LibraryLoader.getInstance().isInitialized()) return false;
+        // Even if the native library is loaded, the C++ FeatureList might not be initialized yet.
+        // In that case, accessing it will not immediately fail, but instead cause a crash later
+        // when it is initialized. Return whether the native FeatureList has been initialized,
+        // so the return value can be tested, or asserted for a more actionable stack trace
+        // on failure.
+        //
+        // The FeatureList is however guaranteed to be initialized by the time
+        // AsyncInitializationActivity#finishNativeInitialization is called.
+        return ExternalIntentsFeatureListJni.get().isInitialized();
+    }
+
+    /**
+     * Returns whether the specified feature is enabled or not.
+     *
+     * Note: Features queried through this API must be added to the array
+     * |kFeaturesExposedToJava| in
+     * components/external_intents/android/external_intents_feature_list.cc.
+     *
+     * Calling this has the side effect of bucketing this client, which may cause an experiment to
+     * be marked as active.
+     *
+     * Should be called only after native is loaded.
+     *
+     * @param featureName The name of the feature to query.
+     * @return Whether the feature is enabled or not.
+     */
+    public static boolean isEnabled(String featureName) {
+        assert isNativeInitialized();
+        return ExternalIntentsFeatureListJni.get().isEnabled(featureName);
+    }
+
+    /** Alphabetical: */
+    public static final String INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE =
+            "IntentBlockExternalFormRedirectsNoGesture";
+
+    @NativeMethods
+    interface Natives {
+        boolean isInitialized();
+        boolean isEnabled(String featureName);
+    }
+}
diff --git a/components/favicon/core/history_ui_favicon_request_handler.h b/components/favicon/core/history_ui_favicon_request_handler.h
index 0a774c2..50fcc2ae 100644
--- a/components/favicon/core/history_ui_favicon_request_handler.h
+++ b/components/favicon/core/history_ui_favicon_request_handler.h
@@ -23,16 +23,16 @@
 };
 
 // Keyed service for handling favicon requests made by a history UI, forwarding
-// them to local storage, sync or Google server accordingly. This service should
+// them to local storage or Google server accordingly. This service should
 // only be used by the UIs listed in the HistoryUiFaviconRequestOrigin enum.
 // Requests must be made by page url, as opposed to icon url.
 class HistoryUiFaviconRequestHandler : public KeyedService {
  public:
   // Requests favicon bitmap at |page_url| of size |desired_size_in_pixel|.
-  // Tries to fetch the icon from local storage and falls back to sync, or the
-  // Google favicon server if user settings allow to query it using history
-  // data. If a non-empty |icon_url_for_uma| (optional) is passed, it will
-  // be used to record UMA about the grouping of requests to the favicon server.
+  // Tries to fetch the icon from local storage and falls back to the Google
+  // favicon server if user settings allow to query it using history data.
+  // If a non-empty |icon_url_for_uma| (optional) is passed, it will be used to
+  // record UMA about the grouping of requests to the favicon server.
   virtual void GetRawFaviconForPageURL(
       const GURL& page_url,
       int desired_size_in_pixel,
@@ -40,12 +40,8 @@
       HistoryUiFaviconRequestOrigin request_origin_for_uma,
       const GURL& icon_url_for_uma) = 0;
 
-  // Requests favicon image at |page_url|.
-  // Tries to fetch the icon from local storage and falls back to sync, or the
-  // Google favicon server if user settings allow to query it using history
-  // data.
-  // If a non-empty |icon_url_for_uma| (optional) is passed, it will be used to
-  // record UMA about the grouping of requests to the favicon server.
+  // Requests favicon image at |page_url|. The same fallback considerations for
+  // GetRawFaviconForPageURL apply.
   // This method is only called by desktop code.
   virtual void GetFaviconImageForPageURL(
       const GURL& page_url,
diff --git a/components/favicon/core/history_ui_favicon_request_handler_impl.cc b/components/favicon/core/history_ui_favicon_request_handler_impl.cc
index c112f4a..8051b2d4 100644
--- a/components/favicon/core/history_ui_favicon_request_handler_impl.cc
+++ b/components/favicon/core/history_ui_favicon_request_handler_impl.cc
@@ -97,13 +97,11 @@
 }  // namespace
 
 HistoryUiFaviconRequestHandlerImpl::HistoryUiFaviconRequestHandlerImpl(
-    const SyncedFaviconGetter& synced_favicon_getter,
     const CanSendHistoryDataGetter& can_send_history_data_getter,
     FaviconService* favicon_service,
     LargeIconService* large_icon_service)
     : favicon_service_(favicon_service),
       large_icon_service_(large_icon_service),
-      synced_favicon_getter_(synced_favicon_getter),
       can_send_history_data_getter_(can_send_history_data_getter) {
   DCHECK(favicon_service);
   DCHECK(large_icon_service);
@@ -186,19 +184,7 @@
     return;
   }
 
-  favicon_base::FaviconRawBitmapResult sync_bitmap_result =
-      synced_favicon_getter_.Run(page_url);
-  if (sync_bitmap_result.is_valid()) {
-    // If request to sync succeeds, resize bitmap to desired size and send.
-    RecordFaviconAvailabilityAndLatencyMetric(
-        origin_for_uma, request_start_time_for_uma, FaviconAvailability::kSync);
-    std::move(response_callback)
-        .Run(favicon_base::ResizeFaviconBitmapResult({sync_bitmap_result},
-                                                     desired_size_in_pixel));
-    return;
-  }
-
-  // If sync does not have the favicon, send empty response.
+  // Send empty response.
   RecordFaviconAvailabilityAndLatencyMetric(origin_for_uma,
                                             request_start_time_for_uma,
                                             FaviconAvailability::kNotAvailable);
@@ -246,21 +232,7 @@
     return;
   }
 
-  favicon_base::FaviconRawBitmapResult sync_bitmap_result =
-      synced_favicon_getter_.Run(page_url);
-  if (sync_bitmap_result.is_valid()) {
-    // If request to sync succeeds, convert the retrieved bitmap to image and
-    // send.
-    RecordFaviconAvailabilityAndLatencyMetric(
-        origin_for_uma, request_start_time_for_uma, FaviconAvailability::kSync);
-    favicon_base::FaviconImageResult sync_image_result;
-    sync_image_result.image =
-        gfx::Image::CreateFrom1xPNGBytes(sync_bitmap_result.bitmap_data.get());
-    std::move(response_callback).Run(sync_image_result);
-    return;
-  }
-
-  // If sync does not have the favicon, send empty response.
+  // Send empty response.
   RecordFaviconAvailabilityAndLatencyMetric(origin_for_uma,
                                             request_start_time_for_uma,
                                             FaviconAvailability::kNotAvailable);
diff --git a/components/favicon/core/history_ui_favicon_request_handler_impl.h b/components/favicon/core/history_ui_favicon_request_handler_impl.h
index 71a3be4..e9bbd04 100644
--- a/components/favicon/core/history_ui_favicon_request_handler_impl.h
+++ b/components/favicon/core/history_ui_favicon_request_handler_impl.h
@@ -22,8 +22,9 @@
 enum class FaviconAvailability {
   // Icon recovered from local storage (but may originally come from server).
   kLocal = 0,
+  // DEPRECATED: No icon is retrieved using sync in this layer anymore.
   // Icon recovered using sync.
-  kSync = 1,
+  kDeprecatedSync = 1,
   // Icon not found.
   kNotAvailable = 2,
   kMaxValue = kNotAvailable,
@@ -33,18 +34,12 @@
 class HistoryUiFaviconRequestHandlerImpl
     : public HistoryUiFaviconRequestHandler {
  public:
-  // Callback that requests the synced bitmap for a page url.
-  using SyncedFaviconGetter =
-      base::RepeatingCallback<favicon_base::FaviconRawBitmapResult(
-          const GURL&)>;
-
   // Callback that checks whether user settings allow to query the favicon
   // server using history data (in particular it must check that history sync is
   // enabled and no custom passphrase is set).
   using CanSendHistoryDataGetter = base::RepeatingCallback<bool()>;
 
   HistoryUiFaviconRequestHandlerImpl(
-      const SyncedFaviconGetter& synced_favicon_getter,
       const CanSendHistoryDataGetter& can_send_history_data_getter,
       FaviconService* favicon_service,
       LargeIconService* large_icon_service);
@@ -65,9 +60,9 @@
 
  private:
   // Called after the first attempt to retrieve the icon bitmap from local
-  // storage. If request succeeded, sends the result. Otherwise attempts to
-  // retrieve from sync or the Google favicon server depending on the result
-  // given by |can_send_history_data_getter_|.
+  // storage. If request succeeded, sends the result. Otherwise, if allowed by
+  // user settings, (i.e. if |can_send_history_data_getter_| returns true),
+  // attempts to retrieve from the Google favicon server.
   void OnBitmapLocalDataAvailable(
       const GURL& page_url,
       int desired_size_in_pixel,
@@ -78,9 +73,9 @@
       const favicon_base::FaviconRawBitmapResult& bitmap_result);
 
   // Called after the first attempt to retrieve the icon image from local
-  // storage. If request succeeded, sends the result. Otherwise attempts to
-  // retrieve from sync or the Google favicon server depending on the result
-  // given by |can_send_history_data_getter_|.
+  // storage. If request succeeded, sends the result. Otherwise, if allowed by
+  // user settings, (i.e. if |can_send_history_data_getter_| returns true),
+  // attempts to retrieve from the Google favicon server.
   void OnImageLocalDataAvailable(
       const GURL& page_url,
       favicon_base::FaviconImageCallback response_callback,
@@ -117,8 +112,6 @@
 
   LargeIconService* const large_icon_service_;
 
-  SyncedFaviconGetter const synced_favicon_getter_;
-
   CanSendHistoryDataGetter const can_send_history_data_getter_;
 
   // Map from a group identifier to the number of callbacks in that group which
diff --git a/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc b/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
index d0bfd83d..22a1a405 100644
--- a/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
+++ b/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
@@ -211,27 +211,17 @@
  public:
   HistoryUiFaviconRequestHandlerImplTest()
       : mock_large_icon_service_(&mock_favicon_service_),
-        history_ui_favicon_request_handler_(synced_favicon_getter_.Get(),
-                                            can_send_history_data_getter_.Get(),
+        history_ui_favicon_request_handler_(can_send_history_data_getter_.Get(),
                                             &mock_favicon_service_,
                                             &mock_large_icon_service_) {
     // Allow sending history data by default.
     ON_CALL(can_send_history_data_getter_, Run()).WillByDefault(Return(true));
-
-    // Sync will by default respond it does not contain any icon. Same is done
-    // for the FaviconService and LargeIconService fakes in their constructors.
-    ON_CALL(synced_favicon_getter_, Run(_)).WillByDefault([](auto) {
-      return favicon_base::FaviconRawBitmapResult();
-    });
   }
 
  protected:
   testing::NiceMock<MockFaviconServiceWithFake> mock_favicon_service_;
   testing::NiceMock<MockLargeIconServiceWithFake> mock_large_icon_service_;
   testing::NiceMock<base::MockCallback<
-      HistoryUiFaviconRequestHandlerImpl::SyncedFaviconGetter>>
-      synced_favicon_getter_;
-  testing::NiceMock<base::MockCallback<
       HistoryUiFaviconRequestHandlerImpl::CanSendHistoryDataGetter>>
       can_send_history_data_getter_;
   base::HistogramTester histogram_tester_;
@@ -245,7 +235,6 @@
   EXPECT_CALL(mock_favicon_service_,
               GetRawFaviconForPageURL(GURL(kDummyPageUrl), _,
                                       kDefaultDesiredSizeInPixel, _, _, _));
-  EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconRawBitmapResult result;
   history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
@@ -259,26 +248,6 @@
       std::string(kLatencyHistogramName) + kDummyOriginHistogramSuffix, 1);
 }
 
-TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetSyncBitmap) {
-  ON_CALL(can_send_history_data_getter_, Run()).WillByDefault(Return(false));
-  EXPECT_CALL(mock_favicon_service_,
-              GetRawFaviconForPageURL(GURL(kDummyPageUrl), _,
-                                      kDefaultDesiredSizeInPixel, _, _, _));
-  EXPECT_CALL(synced_favicon_getter_, Run(GURL(kDummyPageUrl)))
-      .WillOnce([](auto) { return CreateTestBitmapResult(); });
-  favicon_base::FaviconRawBitmapResult result;
-  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
-      GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
-      base::BindOnce(&StoreBitmap, &result), kDummyOrigin,
-      /*icon_url_for_uma=*/GURL());
-  EXPECT_TRUE(result.is_valid());
-  histogram_tester_.ExpectUniqueSample(
-      std::string(kAvailabilityHistogramName) + kDummyOriginHistogramSuffix,
-      FaviconAvailability::kSync, 1);
-  histogram_tester_.ExpectTotalCount(
-      std::string(kLatencyHistogramName) + kDummyOriginHistogramSuffix, 1);
-}
-
 TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetLocalBitmap) {
   mock_favicon_service_.StoreMockLocalFavicon(GURL(kDummyPageUrl));
   EXPECT_CALL(mock_favicon_service_,
@@ -286,7 +255,6 @@
                                       kDefaultDesiredSizeInPixel, _, _, _));
   EXPECT_CALL(mock_large_icon_service_,
               TouchIconFromGoogleServer(GURL(kDummyIconUrl)));
-  EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconRawBitmapResult result;
   history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
@@ -329,7 +297,6 @@
 TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetEmptyImage) {
   EXPECT_CALL(mock_favicon_service_,
               GetFaviconImageForPageURL(GURL(kDummyPageUrl), _, _));
-  EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconImageResult result;
   history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
       GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result), kDummyOrigin,
@@ -342,31 +309,12 @@
       std::string(kLatencyHistogramName) + kDummyOriginHistogramSuffix, 1);
 }
 
-TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetSyncImage) {
-  ON_CALL(can_send_history_data_getter_, Run()).WillByDefault(Return(false));
-  EXPECT_CALL(mock_favicon_service_,
-              GetFaviconImageForPageURL(GURL(kDummyPageUrl), _, _));
-  EXPECT_CALL(synced_favicon_getter_, Run(GURL(kDummyPageUrl)))
-      .WillOnce([](auto) { return CreateTestBitmapResult(); });
-  favicon_base::FaviconImageResult result;
-  history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
-      GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result), kDummyOrigin,
-      /*icon_url_for_uma=*/GURL());
-  EXPECT_FALSE(result.image.IsEmpty());
-  histogram_tester_.ExpectUniqueSample(
-      std::string(kAvailabilityHistogramName) + kDummyOriginHistogramSuffix,
-      FaviconAvailability::kSync, 1);
-  histogram_tester_.ExpectTotalCount(
-      std::string(kLatencyHistogramName) + kDummyOriginHistogramSuffix, 1);
-}
-
 TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetLocalImage) {
   mock_favicon_service_.StoreMockLocalFavicon(GURL(kDummyPageUrl));
   EXPECT_CALL(mock_favicon_service_,
               GetFaviconImageForPageURL(GURL(kDummyPageUrl), _, _));
   EXPECT_CALL(mock_large_icon_service_,
               TouchIconFromGoogleServer(GURL(kDummyIconUrl)));
-  EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconImageResult result;
   history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
       GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result), kDummyOrigin,
@@ -389,7 +337,6 @@
               GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
                   GURL(kDummyPageUrl), _,
                   /*should_trim_url_path=*/false, _, _));
-  EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconImageResult result;
   history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
       GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result), kDummyOrigin,
@@ -428,29 +375,5 @@
       /*icon_url_for_uma=*/GURL());
 }
 
-TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldResizeSyncBitmap) {
-  const int kDesiredSizeInPixel = 32;
-  ON_CALL(can_send_history_data_getter_, Run()).WillByDefault(Return(false));
-  EXPECT_CALL(mock_favicon_service_,
-              GetRawFaviconForPageURL(GURL(kDummyPageUrl), _,
-                                      kDesiredSizeInPixel, _, _, _))
-      .WillOnce([](auto, auto, auto, auto,
-                   favicon_base::FaviconRawBitmapCallback callback, auto) {
-        std::move(callback).Run(favicon_base::FaviconRawBitmapResult());
-        return kDummyTaskId;
-      });
-  // Have sync return bitmap of different size from the one requested.
-  EXPECT_CALL(synced_favicon_getter_, Run(GURL(kDummyPageUrl)))
-      .WillOnce([](auto) { return CreateTestBitmapResult(16); });
-  favicon_base::FaviconRawBitmapResult result;
-  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
-      GURL(kDummyPageUrl), kDesiredSizeInPixel,
-      base::BindOnce(&StoreBitmap, &result), kDummyOrigin,
-      /*icon_url_for_uma=*/GURL());
-  EXPECT_TRUE(result.is_valid());
-  EXPECT_EQ(gfx::Size(kDesiredSizeInPixel, kDesiredSizeInPixel),
-            result.pixel_size);
-}
-
 }  // namespace
 }  // namespace favicon
diff --git a/components/gwp_asan/client/guarded_page_allocator.cc b/components/gwp_asan/client/guarded_page_allocator.cc
index eb82ef0..3c1bc44 100644
--- a/components/gwp_asan/client/guarded_page_allocator.cc
+++ b/components/gwp_asan/client/guarded_page_allocator.cc
@@ -347,8 +347,9 @@
     if (!oom_hit_) {
       if (++consecutive_failed_allocations_ == kOutOfMemoryCount) {
         oom_hit_ = true;
+        size_t allocations = total_allocations_ - kOutOfMemoryCount;
         base::AutoUnlock unlock(lock_);
-        std::move(oom_callback_).Run(total_allocations_ - kOutOfMemoryCount);
+        std::move(oom_callback_).Run(allocations);
       }
     }
     return false;
diff --git a/components/password_manager/core/browser/leak_detection_delegate.cc b/components/password_manager/core/browser/leak_detection_delegate.cc
index 35061e84..07e6569 100644
--- a/components/password_manager/core/browser/leak_detection_delegate.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate.cc
@@ -28,8 +28,9 @@
 
 using Logger = autofill::SavePasswordProgressLogger;
 
-void LogString(PasswordManagerClient* client, Logger::StringID string_id) {
-  if (password_manager_util::IsLoggingActive(client)) {
+void LogString(const PasswordManagerClient* client,
+               Logger::StringID string_id) {
+  if (client && password_manager_util::IsLoggingActive(client)) {
     BrowserSavePasswordProgressLogger logger(client->GetLogManager());
     logger.LogMessage(string_id);
   }
@@ -47,37 +48,8 @@
   if (client_->IsIncognito())
     return;
 
-  bool is_leak_protection_on = client_->GetPrefs()->GetBoolean(
-      password_manager::prefs::kPasswordLeakDetectionEnabled);
-
-  // Leak detection can only start if:
-  // 1. The user has not opted out and Safe Browsing is turned on, or
-  // 2. The user is an enhanced protection user
-  // Safe Browsing is only available on non-IOS.
-#if defined(OS_IOS)
-  if (!is_leak_protection_on) {
-    LogString(client_, Logger::STRING_LEAK_DETECTION_DISABLED_FEATURE);
+  if (!CanStartLeakCheck(*client_->GetPrefs(), client_))
     return;
-  }
-#else
-  safe_browsing::SafeBrowsingState sb_state =
-      safe_browsing::GetSafeBrowsingState(*client_->GetPrefs());
-  switch (sb_state) {
-    case safe_browsing::NO_SAFE_BROWSING:
-      LogString(client_, Logger::STRING_LEAK_DETECTION_DISABLED_SAFE_BROWSING);
-      return;
-    case safe_browsing::STANDARD_PROTECTION:
-      if (!is_leak_protection_on) {
-        LogString(client_, Logger::STRING_LEAK_DETECTION_DISABLED_FEATURE);
-        return;
-      }
-      // feature is on.
-      break;
-    case safe_browsing::ENHANCED_PROTECTION:
-      // feature is on.
-      break;
-  }
-#endif
 
   if (form.username_value.empty())
     return;
@@ -156,4 +128,37 @@
   }
 }
 
+bool CanStartLeakCheck(const PrefService& prefs,
+                       const PasswordManagerClient* client) {
+  const bool is_leak_protection_on =
+      prefs.GetBoolean(password_manager::prefs::kPasswordLeakDetectionEnabled);
+
+  // Leak detection can only start if:
+  // 1. The user has not opted out and Safe Browsing is turned on, or
+  // 2. The user is an enhanced protection user
+  // Safe Browsing is only available on non-IOS.
+#if defined(OS_IOS)
+  if (!is_leak_protection_on)
+    LogString(client, Logger::STRING_LEAK_DETECTION_DISABLED_FEATURE);
+  return is_leak_protection_on;
+#else
+  safe_browsing::SafeBrowsingState sb_state =
+      safe_browsing::GetSafeBrowsingState(prefs);
+  switch (sb_state) {
+    case safe_browsing::NO_SAFE_BROWSING:
+      LogString(client, Logger::STRING_LEAK_DETECTION_DISABLED_SAFE_BROWSING);
+      return false;
+    case safe_browsing::STANDARD_PROTECTION:
+      if (!is_leak_protection_on)
+        LogString(client, Logger::STRING_LEAK_DETECTION_DISABLED_FEATURE);
+      return is_leak_protection_on;
+    case safe_browsing::ENHANCED_PROTECTION:
+      // feature is on.
+      break;
+  }
+
+  return true;
+#endif
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection_delegate.h b/components/password_manager/core/browser/leak_detection_delegate.h
index 3822690..d74bce3 100644
--- a/components/password_manager/core/browser/leak_detection_delegate.h
+++ b/components/password_manager/core/browser/leak_detection_delegate.h
@@ -17,6 +17,8 @@
 struct PasswordForm;
 }  // namespace autofill
 
+class PrefService;
+
 namespace password_manager {
 
 class LeakDetectionCheck;
@@ -77,6 +79,11 @@
   std::unique_ptr<LeakDetectionDelegateHelper> helper_;
 };
 
+// Determines whether the leak check can be started depending on |prefs|. Will
+// use |client| for logging if non-null.
+bool CanStartLeakCheck(const PrefService& prefs,
+                       const PasswordManagerClient* client = nullptr);
+
 }  // namespace password_manager
 
 #endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_H_
diff --git a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
index 8c428fa..3933d5a 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
@@ -207,6 +207,7 @@
   delegate().StartLeakCheck(form);
 
   EXPECT_TRUE(delegate().leak_check());
+  EXPECT_TRUE(CanStartLeakCheck(*pref_service()));
 }
 
 TEST_F(LeakDetectionDelegateTest, StartCheckWithEnhancedProtection) {
@@ -225,6 +226,7 @@
   delegate().StartLeakCheck(form);
 
   EXPECT_TRUE(delegate().leak_check());
+  EXPECT_TRUE(CanStartLeakCheck(*pref_service()));
 }
 
 TEST_F(LeakDetectionDelegateTest, DoNotStartCheckWithoutSafeBrowsing) {
@@ -237,6 +239,7 @@
   delegate().StartLeakCheck(form);
 
   EXPECT_FALSE(delegate().leak_check());
+  EXPECT_FALSE(CanStartLeakCheck(*pref_service()));
 }
 
 TEST_F(LeakDetectionDelegateTest, DoNotStartLeakCheckIfLeakCheckIsOff) {
@@ -249,6 +252,7 @@
   delegate().StartLeakCheck(form);
 
   EXPECT_FALSE(delegate().leak_check());
+  EXPECT_FALSE(CanStartLeakCheck(*pref_service()));
 }
 #endif
 
diff --git a/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.cc b/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.cc
index 3add05b3..95eed08 100644
--- a/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.cc
+++ b/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.cc
@@ -12,7 +12,9 @@
 #include "components/autofill/core/common/password_form.h"
 #include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
 #include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
+#include "components/password_manager/core/browser/leak_detection_delegate.h"
 #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
+#include "components/prefs/pref_service.h"
 
 namespace password_manager {
 
@@ -41,10 +43,12 @@
 
 BulkLeakCheckServiceAdapter::BulkLeakCheckServiceAdapter(
     SavedPasswordsPresenter* presenter,
-    BulkLeakCheckService* service)
-    : presenter_(presenter), service_(service) {
+    BulkLeakCheckService* service,
+    PrefService* prefs)
+    : presenter_(presenter), service_(service), prefs_(prefs) {
   DCHECK(presenter_);
   DCHECK(service_);
+  DCHECK(prefs_);
   presenter_->AddObserver(this);
 }
 
@@ -91,11 +95,13 @@
 }
 
 void BulkLeakCheckServiceAdapter::OnEdited(const PasswordForm& form) {
-  // Here no extra canonicalization is needed, as there are no other forms we
-  // could de-dupe before we pass it on to the service.
-  std::vector<LeakCheckCredential> credentials;
-  credentials.emplace_back(form.username_value, form.password_value);
-  service_->CheckUsernamePasswordPairs(std::move(credentials));
+  if (CanStartLeakCheck(*prefs_)) {
+    // Here no extra canonicalization is needed, as there are no other forms we
+    // could de-dupe before we pass it on to the service.
+    std::vector<LeakCheckCredential> credentials;
+    credentials.emplace_back(form.username_value, form.password_value);
+    service_->CheckUsernamePasswordPairs(std::move(credentials));
+  }
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h b/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h
index 29923115..bd2c6ab 100644
--- a/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h
+++ b/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h
@@ -13,6 +13,8 @@
 struct PasswordForm;
 }
 
+class PrefService;
+
 namespace password_manager {
 
 // This class serves as an apdater for the BulkLeakCheckService and exposes an
@@ -20,7 +22,8 @@
 class BulkLeakCheckServiceAdapter : public SavedPasswordsPresenter::Observer {
  public:
   BulkLeakCheckServiceAdapter(SavedPasswordsPresenter* presenter,
-                              BulkLeakCheckService* service);
+                              BulkLeakCheckService* service,
+                              PrefService* prefs);
   ~BulkLeakCheckServiceAdapter() override;
 
   // Instructs the adapter to start a check. This is a no-op in case a check is
@@ -47,6 +50,8 @@
   // null and must outlive the adapter.
   SavedPasswordsPresenter* presenter_ = nullptr;
   BulkLeakCheckService* service_ = nullptr;
+
+  PrefService* prefs_ = nullptr;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter_unittest.cc b/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter_unittest.cc
index b6630c1..a31030b1d 100644
--- a/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter_unittest.cc
+++ b/components/password_manager/core/browser/ui/bulk_leak_check_service_adapter_unittest.cc
@@ -20,6 +20,10 @@
 #include "components/password_manager/core/browser/leak_detection/mock_leak_detection_check_factory.h"
 #include "components/password_manager/core/browser/test_password_store.h"
 #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "components/sync/model/syncable_service.h"
 #include "services/network/test/test_shared_url_loader_factory.h"
@@ -95,6 +99,11 @@
     service_.set_leak_factory(std::move(factory));
     store_->Init(syncer::SyncableService::StartSyncFlare(),
                  /*prefs=*/nullptr);
+    prefs_.registry()->RegisterBooleanPref(prefs::kPasswordLeakDetectionEnabled,
+                                           true);
+    prefs_.registry()->RegisterBooleanPref(::prefs::kSafeBrowsingEnabled, true);
+    prefs_.registry()->RegisterBooleanPref(::prefs::kSafeBrowsingEnhanced,
+                                           false);
   }
 
   ~BulkLeakCheckServiceAdapterTest() override {
@@ -105,6 +114,7 @@
   TestPasswordStore& store() { return *store_; }
   SavedPasswordsPresenter& presenter() { return presenter_; }
   MockLeakDetectionCheckFactory& factory() { return *factory_; }
+  PrefService& prefs() { return prefs_; }
   BulkLeakCheckServiceAdapter& adapter() { return adapter_; }
 
   void RunUntilIdle() { task_env_.RunUntilIdle(); }
@@ -119,7 +129,8 @@
       identity_test_env_.identity_manager(),
       base::MakeRefCounted<network::TestSharedURLLoaderFactory>()};
   MockLeakDetectionCheckFactory* factory_ = nullptr;
-  BulkLeakCheckServiceAdapter adapter_{&presenter_, &service_};
+  TestingPrefServiceSimple prefs_;
+  BulkLeakCheckServiceAdapter adapter_{&presenter_, &service_, &prefs_};
 };
 
 }  // namespace
@@ -216,9 +227,26 @@
             adapter().GetBulkLeakCheckState());
 }
 
+// Tests that editing a password through the presenter does not result in
+// another call to CheckCredentials with a corresponding change to the checked
+// password if the corresponding prefs are not set.
+TEST_F(BulkLeakCheckServiceAdapterTest, OnEditedNoPrefs) {
+  prefs().SetBoolean(prefs::kPasswordLeakDetectionEnabled, false);
+  prefs().SetBoolean(::prefs::kSafeBrowsingEnabled, false);
+
+  PasswordForm password =
+      MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+  store().AddLogin(password);
+  RunUntilIdle();
+
+  EXPECT_CALL(factory(), TryCreateBulkLeakCheck).Times(0);
+  presenter().EditPassword(password, base::ASCIIToUTF16(kPassword2));
+}
+
 // Tests that editing a password through the presenter will result in another
-// call to CheckCredentials with a corresponding change to the checked password.
-TEST_F(BulkLeakCheckServiceAdapterTest, OnEdited) {
+// call to CheckCredentials with a corresponding change to the checked password
+// if the corresponding prefs are set.
+TEST_F(BulkLeakCheckServiceAdapterTest, OnEditedWithPrefs) {
   PasswordForm password =
       MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
   store().AddLogin(password);
diff --git a/components/payments/content/installable_payment_app_crawler.cc b/components/payments/content/installable_payment_app_crawler.cc
index ee78b07..bce2884 100644
--- a/components/payments/content/installable_payment_app_crawler.cc
+++ b/components/payments/content/installable_payment_app_crawler.cc
@@ -15,10 +15,13 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/manifest_icon_downloader.h"
 #include "content/public/browser/payment_app_provider.h"
 #include "content/public/browser/permission_controller.h"
 #include "content/public/browser/permission_type.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
@@ -33,6 +36,7 @@
 // TODO(crbug.com/782270): Add integration tests for this class.
 InstallablePaymentAppCrawler::InstallablePaymentAppCrawler(
     const url::Origin& merchant_origin,
+    content::RenderFrameHost* initiator_render_frame_host,
     content::WebContents* web_contents,
     PaymentManifestDownloader* downloader,
     PaymentManifestParser* parser,
@@ -40,6 +44,7 @@
     : WebContentsObserver(web_contents),
       log_(web_contents),
       merchant_origin_(merchant_origin),
+      initiator_render_frame_host_(initiator_render_frame_host),
       downloader_(downloader),
       parser_(parser),
       number_of_payment_method_manifest_to_download_(0),
@@ -401,6 +406,13 @@
   }
 
   number_of_web_app_icons_to_download_and_decode_++;
+
+  content::GlobalFrameRoutingId frame_routing_id;
+  if (initiator_render_frame_host_) {
+    frame_routing_id = content::GlobalFrameRoutingId(
+        initiator_render_frame_host_->GetProcess()->GetID(),
+        initiator_render_frame_host_->GetRoutingID());
+  }
   bool can_download_icon = content::ManifestIconDownloader::Download(
       web_contents(), downloader_->FindTestServerURL(best_icon_url),
       IconSizeCalculator::IdealIconHeight(native_view),
@@ -409,7 +421,8 @@
           &InstallablePaymentAppCrawler::OnPaymentWebAppIconDownloadAndDecoded,
           weak_ptr_factory_.GetWeakPtr(), method_manifest_url,
           web_app_manifest_url),
-      false /* square_only */);
+      false, /* square_only */
+      frame_routing_id);
   DCHECK(can_download_icon);
 }
 
diff --git a/components/payments/content/installable_payment_app_crawler.h b/components/payments/content/installable_payment_app_crawler.h
index cad6cb6..fdad1391 100644
--- a/components/payments/content/installable_payment_app_crawler.h
+++ b/components/payments/content/installable_payment_app_crawler.h
@@ -26,6 +26,7 @@
 class GURL;
 
 namespace content {
+class RenderFrameHost;
 class WebContents;
 }
 
@@ -40,18 +41,21 @@
       std::map<GURL, std::unique_ptr<WebAppInstallationInfo>>,
       const std::string& error_message)>;
 
-  // |merchant_origin| should be the origin of the iframe that created the
+  // |merchant_origin| is the origin of the iframe that created the
   // PaymentRequest object. It is used by security features like
   // 'Sec-Fetch-Site' and 'Cross-Origin-Resource-Policy'.
+  // |initiator_render_frame_host| is the iframe for |merchant_origin|.
   //
   // The owner of InstallablePaymentAppCrawler owns |downloader|, |parser| and
   // |cache|. They should live until |finished_using_resources| parameter to
   // Start() method is called.
-  InstallablePaymentAppCrawler(const url::Origin& merchant_origin,
-                               content::WebContents* web_contents,
-                               PaymentManifestDownloader* downloader,
-                               PaymentManifestParser* parser,
-                               PaymentManifestWebDataService* cache);
+  InstallablePaymentAppCrawler(
+      const url::Origin& merchant_origin,
+      content::RenderFrameHost* initiator_render_frame_host,
+      content::WebContents* web_contents,
+      PaymentManifestDownloader* downloader,
+      PaymentManifestParser* parser,
+      PaymentManifestWebDataService* cache);
   ~InstallablePaymentAppCrawler() override;
 
   // Starts the crawling process. All the url based payment methods in
@@ -107,6 +111,7 @@
 
   DeveloperConsoleLogger log_;
   const url::Origin merchant_origin_;
+  content::RenderFrameHost* initiator_render_frame_host_;
   PaymentManifestDownloader* downloader_;
   PaymentManifestParser* parser_;
   FinishedCrawlingCallback callback_;
diff --git a/components/payments/content/payment_app_factory.h b/components/payments/content/payment_app_factory.h
index 281dbe6..a33a2c55 100644
--- a/components/payments/content/payment_app_factory.h
+++ b/components/payments/content/payment_app_factory.h
@@ -21,6 +21,7 @@
 }  // namespace autofill
 
 namespace content {
+class RenderFrameHost;
 class WebContents;
 }  // namespace content
 
@@ -46,6 +47,7 @@
     virtual const GURL& GetTopOrigin() = 0;
     virtual const GURL& GetFrameOrigin() = 0;
     virtual const url::Origin& GetFrameSecurityOrigin() = 0;
+    virtual content::RenderFrameHost* GetInitiatorRenderFrameHost() const = 0;
     virtual const std::vector<autofill::AutofillProfile*>&
     GetBillingProfiles() = 0;
     virtual bool IsRequestedAutofillDataAvailable() = 0;
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index fae8e9e..cc9c24e 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -97,6 +97,7 @@
     mojo::PendingReceiver<mojom::PaymentRequest> receiver,
     ObserverForTest* observer_for_testing)
     : web_contents_(web_contents),
+      initiator_render_frame_host_(render_frame_host),
       log_(web_contents_),
       delegate_(std::move(delegate)),
       manager_(manager),
@@ -186,8 +187,8 @@
       std::move(options), std::move(details), std::move(method_data),
       /*observer=*/this, delegate_->GetApplicationLocale());
   state_ = std::make_unique<PaymentRequestState>(
-      web_contents_, top_level_origin_, frame_origin_, frame_security_origin_,
-      spec_.get(),
+      web_contents_, initiator_render_frame_host_, top_level_origin_,
+      frame_origin_, frame_security_origin_, spec_.get(),
       /*delegate=*/this, delegate_->GetApplicationLocale(),
       delegate_->GetPersonalDataManager(), delegate_.get(),
       base::BindRepeating(&PaymentRequest::SetInvokedServiceWorkerIdentity,
diff --git a/components/payments/content/payment_request.h b/components/payments/content/payment_request.h
index 3e8b289..18ec889 100644
--- a/components/payments/content/payment_request.h
+++ b/components/payments/content/payment_request.h
@@ -184,6 +184,7 @@
                                            bool warn_localhost_or_file);
 
   content::WebContents* web_contents_;
+  content::RenderFrameHost* initiator_render_frame_host_;
   DeveloperConsoleLogger log_;
   std::unique_ptr<ContentPaymentRequestDelegate> delegate_;
   // |manager_| owns this PaymentRequest.
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 6240af7..65dbd1a 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -33,6 +33,7 @@
 #include "components/payments/core/payment_app.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payments_experimental_features.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/common/content_features.h"
 
 namespace payments {
@@ -56,6 +57,7 @@
 
 PaymentRequestState::PaymentRequestState(
     content::WebContents* web_contents,
+    content::RenderFrameHost* initiator_render_frame_host,
     const GURL& top_level_origin,
     const GURL& frame_origin,
     const url::Origin& frame_security_origin,
@@ -67,6 +69,7 @@
     const ServiceWorkerPaymentApp::IdentityCallback& sw_identity_callback,
     JourneyLogger* journey_logger)
     : web_contents_(web_contents),
+      initiator_render_frame_host_(initiator_render_frame_host),
       top_origin_(top_level_origin),
       frame_origin_(frame_origin),
       frame_security_origin_(frame_security_origin),
@@ -118,6 +121,11 @@
   return frame_security_origin_;
 }
 
+content::RenderFrameHost* PaymentRequestState::GetInitiatorRenderFrameHost()
+    const {
+  return initiator_render_frame_host_;
+}
+
 const std::vector<autofill::AutofillProfile*>&
 PaymentRequestState::GetBillingProfiles() {
   return shipping_profiles_;
diff --git a/components/payments/content/payment_request_state.h b/components/payments/content/payment_request_state.h
index 9d61e98..3fbf03d 100644
--- a/components/payments/content/payment_request_state.h
+++ b/components/payments/content/payment_request_state.h
@@ -33,6 +33,10 @@
 class RegionDataLoader;
 }  // namespace autofill
 
+namespace content {
+class RenderFrameHost;
+}  // namespace content
+
 namespace payments {
 
 class ContentPaymentRequestDelegate;
@@ -112,6 +116,7 @@
 
   PaymentRequestState(
       content::WebContents* web_contents,
+      content::RenderFrameHost* initiator_render_frame_host,
       const GURL& top_level_origin,
       const GURL& frame_origin,
       const url::Origin& frame_security_origin,
@@ -131,6 +136,7 @@
   const GURL& GetTopOrigin() override;
   const GURL& GetFrameOrigin() override;
   const url::Origin& GetFrameSecurityOrigin() override;
+  content::RenderFrameHost* GetInitiatorRenderFrameHost() const override;
   const std::vector<autofill::AutofillProfile*>& GetBillingProfiles() override;
   bool IsRequestedAutofillDataAvailable() override;
   bool MayCrawlForInstallablePaymentApps() override;
@@ -336,6 +342,7 @@
                                 SectionSelectionStatus selection_status);
 
   content::WebContents* web_contents_;
+  content::RenderFrameHost* initiator_render_frame_host_;
   const GURL top_origin_;
   const GURL frame_origin_;
   const url::Origin frame_security_origin_;
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index 1c57423..76f8f1a 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -80,7 +80,8 @@
     PaymentAppServiceFactory::SetForTesting(
         std::make_unique<PaymentAppService>());
     state_ = std::make_unique<PaymentRequestState>(
-        /*web_contents=*/nullptr, GURL("https://example.com"),
+        /*web_contents=*/nullptr,
+        /*render_frame_host=*/nullptr, GURL("https://example.com"),
         GURL("https://example.com/pay"),
         url::Origin::Create(GURL("https://example.com")), spec_.get(), this,
         "en-US", &test_personal_data_manager_, &test_payment_request_delegate_,
diff --git a/components/payments/content/service_worker_payment_app_factory.cc b/components/payments/content/service_worker_payment_app_factory.cc
index 66c0d9c..0e86a59 100644
--- a/components/payments/content/service_worker_payment_app_factory.cc
+++ b/components/payments/content/service_worker_payment_app_factory.cc
@@ -126,7 +126,8 @@
   ServiceWorkerPaymentAppCreator* creator_raw_pointer = creator.get();
   creators_[creator_raw_pointer] = std::move(creator);
   ServiceWorkerPaymentAppFinder::GetInstance()->GetAllPaymentApps(
-      delegate->GetFrameSecurityOrigin(), delegate->GetWebContents(),
+      delegate->GetFrameSecurityOrigin(),
+      delegate->GetInitiatorRenderFrameHost(), delegate->GetWebContents(),
       delegate->GetPaymentRequestDelegate()->GetPaymentManifestWebDataService(),
       delegate->GetSpec()->method_data(),
       delegate->MayCrawlForInstallablePaymentApps(),
diff --git a/components/payments/content/service_worker_payment_app_finder.cc b/components/payments/content/service_worker_payment_app_finder.cc
index f67f435b..25fd140 100644
--- a/components/payments/content/service_worker_payment_app_finder.cc
+++ b/components/payments/content/service_worker_payment_app_finder.cc
@@ -24,6 +24,7 @@
 #include "components/payments/core/payment_manifest_downloader.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/stored_payment_app.h"
 #include "content/public/browser/web_contents.h"
@@ -99,6 +100,7 @@
   // until |finished_using_resources_callback| has run.
   void GetAllPaymentApps(
       const url::Origin& merchant_origin,
+      content::RenderFrameHost* initiator_render_frame_host,
       content::WebContents* web_contents,
       std::unique_ptr<PaymentManifestDownloader> downloader,
       scoped_refptr<PaymentManifestWebDataService> cache,
@@ -120,8 +122,8 @@
             features::kWebPaymentsJustInTimePaymentApp)) {
       // Construct crawler in constructor to allow it observe the web_contents.
       crawler_ = std::make_unique<InstallablePaymentAppCrawler>(
-          merchant_origin, web_contents, downloader_.get(), parser_.get(),
-          cache_.get());
+          merchant_origin, initiator_render_frame_host, web_contents,
+          downloader_.get(), parser_.get(), cache_.get());
       if (ignore_port_in_origin_comparison_for_testing_)
         crawler_->IgnorePortInOriginComparisonForTesting();
     }
@@ -284,6 +286,7 @@
 
 void ServiceWorkerPaymentAppFinder::GetAllPaymentApps(
     const url::Origin& merchant_origin,
+    content::RenderFrameHost* initiator_render_frame_host,
     content::WebContents* web_contents,
     scoped_refptr<PaymentManifestWebDataService> cache,
     const std::vector<mojom::PaymentMethodDataPtr>& requested_method_data,
@@ -306,9 +309,9 @@
   }
 
   self_delete_factory->GetAllPaymentApps(
-      merchant_origin, web_contents, std::move(downloader), cache,
-      requested_method_data, may_crawl_for_installable_payment_apps,
-      std::move(callback),
+      merchant_origin, initiator_render_frame_host, web_contents,
+      std::move(downloader), cache, requested_method_data,
+      may_crawl_for_installable_payment_apps, std::move(callback),
       std::move(finished_writing_cache_callback_for_testing));
 }
 
diff --git a/components/payments/content/service_worker_payment_app_finder.h b/components/payments/content/service_worker_payment_app_finder.h
index afc68c1..f05730f 100644
--- a/components/payments/content/service_worker_payment_app_finder.h
+++ b/components/payments/content/service_worker_payment_app_finder.h
@@ -27,6 +27,7 @@
 }  // namespace base
 
 namespace content {
+class RenderFrameHost;
 class WebContents;
 }  // namespace content
 
@@ -70,6 +71,7 @@
   // The method should be called on the UI thread.
   void GetAllPaymentApps(
       const url::Origin& merchant_origin,
+      content::RenderFrameHost* initiator_render_frame_host,
       content::WebContents* web_contents,
       scoped_refptr<PaymentManifestWebDataService> cache,
       const std::vector<mojom::PaymentMethodDataPtr>& requested_method_data,
diff --git a/components/performance_manager/graph/process_node_impl.cc b/components/performance_manager/graph/process_node_impl.cc
index 8fc6caf..f2f52e9 100644
--- a/components/performance_manager/graph/process_node_impl.cc
+++ b/components/performance_manager/graph/process_node_impl.cc
@@ -22,6 +22,10 @@
 
 ProcessNodeImpl::~ProcessNodeImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Crash if this process node is destroyed while still hosting a worker node.
+  // TODO(https://crbug.com/1058705): Turn this into a DCHECK once the issue is
+  //                                  resolved.
+  CHECK(worker_nodes_.empty());
 }
 
 void ProcessNodeImpl::Bind(
diff --git a/components/policy/core/common/cloud/enterprise_metrics.cc b/components/policy/core/common/cloud/enterprise_metrics.cc
index 035fc39..835bae3be 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.cc
+++ b/components/policy/core/common/cloud/enterprise_metrics.cc
@@ -41,4 +41,12 @@
 const char kMetricPolicyInvalidationRegistrationFcm[] =
     "Enterprise.FCMInvalidationService.PolicyInvalidationsRegistrationResult";
 
+const char kMetricUserRemoteCommandInvalidations[] =
+    "Enterprise.UserRemoteCommandInvalidations";
+const char kMetricDeviceRemoteCommandInvalidations[] =
+    "Enterprise.DeviceRemoteCommandInvalidations";
+
+const char kMetricRemoteCommandInvalidationsRegistrationResult[] =
+    "Enterprise.RemoteCommandInvalidationsRegistrationResult";
+
 }  // namespace policy
diff --git a/components/policy/core/common/cloud/enterprise_metrics.h b/components/policy/core/common/cloud/enterprise_metrics.h
index e06edd2e..90b55b7 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.h
+++ b/components/policy/core/common/cloud/enterprise_metrics.h
@@ -244,6 +244,12 @@
 POLICY_EXPORT extern const char kMetricPolicyInvalidationRegistration[];
 POLICY_EXPORT extern const char kMetricPolicyInvalidationRegistrationFcm[];
 
+POLICY_EXPORT extern const char kMetricUserRemoteCommandInvalidations[];
+POLICY_EXPORT extern const char kMetricDeviceRemoteCommandInvalidations[];
+
+POLICY_EXPORT extern const char
+    kMetricRemoteCommandInvalidationsRegistrationResult[];
+
 }  // namespace policy
 
 #endif  // COMPONENTS_POLICY_CORE_COMMON_CLOUD_ENTERPRISE_METRICS_H_
diff --git a/components/safe_browsing/core/db/v4_local_database_manager.cc b/components/safe_browsing/core/db/v4_local_database_manager.cc
index d58fffe..c16b552f 100644
--- a/components/safe_browsing/core/db/v4_local_database_manager.cc
+++ b/components/safe_browsing/core/db/v4_local_database_manager.cc
@@ -188,6 +188,21 @@
   COUNT,
 };
 
+void RecordTimeSinceLastUpdateHistograms(const base::Time& last_response_time) {
+  bool response_received = !last_response_time.is_null();
+  UMA_HISTOGRAM_BOOLEAN(
+      "SafeBrowsing.V4LocalDatabaseManager.HasReceivedUpdateResponse",
+      response_received);
+
+  if (!response_received)
+    return;
+
+  base::TimeDelta time_since_update = base::Time::Now() - last_response_time;
+  UMA_HISTOGRAM_LONG_TIMES_100(
+      "SafeBrowsing.V4LocalDatabaseManager.TimeSinceLastUpdateResponse",
+      time_since_update);
+}
+
 }  // namespace
 
 V4LocalDatabaseManager::PendingCheck::PendingCheck(
@@ -335,6 +350,8 @@
   bool safe_synchronously = HandleCheck(std::move(check));
   UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.CheckBrowseUrl.HasLocalMatch",
                         !safe_synchronously);
+  RecordTimeSinceLastUpdateHistograms(
+      v4_update_protocol_manager_->last_response_time());
   return safe_synchronously;
 }
 
diff --git a/components/safe_browsing/core/db/v4_update_protocol_manager.cc b/components/safe_browsing/core/db/v4_update_protocol_manager.cc
index e4a6f6a..c36864b 100644
--- a/components/safe_browsing/core/db/v4_update_protocol_manager.cc
+++ b/components/safe_browsing/core/db/v4_update_protocol_manager.cc
@@ -449,4 +449,8 @@
   }
 }
 
+const base::Time& V4UpdateProtocolManager::last_response_time() const {
+  return last_response_time_;
+}
+
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_update_protocol_manager.h b/components/safe_browsing/core/db/v4_update_protocol_manager.h
index e1cc1bc7..068b245 100644
--- a/components/safe_browsing/core/db/v4_update_protocol_manager.h
+++ b/components/safe_browsing/core/db/v4_update_protocol_manager.h
@@ -73,6 +73,10 @@
   // Populates the UpdateInfo message.
   void CollectUpdateInfo(DatabaseManagerInfo::UpdateInfo* database_info);
 
+  // The time that a response was last received from the server. This will
+  // have a null value if no response has been received.
+  const base::Time& last_response_time() const;
+
  protected:
   // Constructs a V4UpdateProtocolManager that issues network requests using
   // |url_loader_factory|. It schedules updates to get the hash prefixes for
diff --git a/components/services/storage/indexed_db/leveldb/leveldb_state.cc b/components/services/storage/indexed_db/leveldb/leveldb_state.cc
index cfd009f4..c2f1275 100644
--- a/components/services/storage/indexed_db/leveldb/leveldb_state.cc
+++ b/components/services/storage/indexed_db/leveldb/leveldb_state.cc
@@ -4,8 +4,10 @@
 
 #include "components/services/storage/indexed_db/leveldb/leveldb_state.h"
 
+#include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/synchronization/waitable_event.h"
 #include "third_party/leveldatabase/src/include/leveldb/env.h"
 
 namespace content {
@@ -43,24 +45,20 @@
       name_for_tracing_(std::move(name_for_tracing)),
       destruction_requested_(false) {}
 
-bool LevelDBState::RequestDestruction(
-    base::OnceClosure on_state_destruction,
-    scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  if (destruction_requested_.exchange(true, std::memory_order_relaxed))
-    return false;
-
-  DCHECK(!on_destruction_);
-  DCHECK(!on_destruction_task_runner_);
-  on_destruction_ = std::move(on_state_destruction);
-  on_destruction_task_runner_ = std::move(task_runner);
-  return true;
+void LevelDBState::RequestDestruction(
+    base::WaitableEvent* signal_on_destruction) {
+  DCHECK(signal_on_destruction);
+  bool destruct_already_requested =
+      destruction_requested_.exchange(true, std::memory_order_relaxed);
+  CHECK(!destruct_already_requested)
+      << "RequestDestruction can only be called one time.";
+  DCHECK(!signal_on_destruction_);
+  signal_on_destruction_ = signal_on_destruction;
 }
 
 LevelDBState::~LevelDBState() {
-  if (on_destruction_) {
-    on_destruction_task_runner_->PostTask(FROM_HERE,
-                                          std::move(on_destruction_));
-  }
+  if (signal_on_destruction_)
+    signal_on_destruction_->Signal();
   if (!db_)
     return;
   base::TimeTicks begin_time = base::TimeTicks::Now();
diff --git a/components/services/storage/indexed_db/leveldb/leveldb_state.h b/components/services/storage/indexed_db/leveldb/leveldb_state.h
index 044f8c7..e2bfa1bb 100644
--- a/components/services/storage/indexed_db/leveldb/leveldb_state.h
+++ b/components/services/storage/indexed_db/leveldb/leveldb_state.h
@@ -9,15 +9,17 @@
 #include <memory>
 #include <utility>
 
-#include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_refptr.h"
 #include "base/sequenced_task_runner.h"
 #include "third_party/leveldatabase/src/include/leveldb/comparator.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/filter_policy.h"
 
+namespace base {
+class WaitableEvent;
+}  // namespace base
+
 namespace content {
 
 // Encapsulates a leveldb database and comparator, allowing them to be used
@@ -35,16 +37,18 @@
       std::unique_ptr<leveldb::DB> in_memory_database,
       std::string name_for_tracing);
 
-  // Returns if this call was successfully the first call to request destruction
-  // of this state. Can be called on any thread. The given |task_runner| will be
-  // used to call the |on_destruction| closure, which is called on the
-  // destruction of this state.
-  bool RequestDestruction(base::OnceClosure on_destruction,
-                          scoped_refptr<base::SequencedTaskRunner> task_runner);
+  // Can only be called once. |signal_on_destruction| must outlive this class,
+  // and will be signaled on the destruction of this state (in the destructor).
+  // Can be called on any thread.
+  void RequestDestruction(base::WaitableEvent* signal_on_destruction);
 
   bool destruction_requested() const {
     return destruction_requested_.load(std::memory_order_relaxed);
   }
+  // Only valid if destruction_requested() returns true.
+  base::WaitableEvent* destruction_event() const {
+    return signal_on_destruction_;
+  }
 
   const leveldb::Comparator* comparator() const { return comparator_; }
   leveldb::DB* db() const { return db_.get(); }
@@ -74,11 +78,10 @@
   // This member transitions from false to true at most once in the instance's
   // lifetime.
   std::atomic_bool destruction_requested_;
-  // These members are written only once (when |destruction_requested_|
-  // transitions from false to true) and read only once in the destructor, so
-  // they are thread-compatible.
-  base::OnceClosure on_destruction_;
-  scoped_refptr<base::SequencedTaskRunner> on_destruction_task_runner_;
+  // |signal_on_destruction_| is written only once (when
+  // |destruction_requested_| transitions from false to true) and read only once
+  // in the destructor, so it is thread-compatible.
+  base::WaitableEvent* signal_on_destruction_ = nullptr;
 };
 
 }  // namespace content
diff --git a/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks_unittest.cc b/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks_unittest.cc
index 697b902..8905a11f7 100644
--- a/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks_unittest.cc
+++ b/components/services/storage/indexed_db/scopes/leveldb_scopes_tasks_unittest.cc
@@ -139,8 +139,7 @@
                         CleanupScopeTask::CleanupMode::kExecuteCleanupTasks,
                         kWriteBatchSizeForTesting);
 
-  leveldb_->RequestDestruction(base::DoNothing(),
-                               base::SequencedTaskRunnerHandle::Get());
+  leveldb_->RequestDestruction(&leveldb_close_event_);
   leveldb::Status s = task.Run();
   ASSERT_TRUE(s.ok()) << s.ToString();
   EXPECT_TRUE(LoadAt(delete_range_start_key_).ok());
diff --git a/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc b/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc
index c290cef5..bb1605f 100644
--- a/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc
+++ b/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.cc
@@ -9,6 +9,7 @@
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event_watcher.h"
 #include "base/system/sys_info.h"
 #include "base/test/bind_test_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -64,12 +65,26 @@
 void LevelDBScopesTestBase::CloseScopesAndDestroyLevelDBState() {
   if (leveldb_) {
     base::RunLoop loop;
-    if (leveldb_->RequestDestruction(loop.QuitClosure(),
-                                     base::SequencedTaskRunnerHandle::Get())) {
-      leveldb_.reset();
-      loop.Run();
+    base::WaitableEvent* leveldb_close_event_ptr;
+    base::WaitableEventWatcher event_watcher;
+    if (leveldb_->destruction_requested()) {
+      leveldb_close_event_ptr = leveldb_->destruction_event();
+    } else {
+      leveldb_close_event_ptr = &leveldb_close_event_;
+      leveldb_->RequestDestruction(leveldb_close_event_ptr);
     }
+    event_watcher.StartWatching(
+        leveldb_close_event_ptr,
+        base::BindLambdaForTesting([&](base::WaitableEvent*) { loop.Quit(); }),
+        base::SequencedTaskRunnerHandle::Get());
     leveldb_.reset();
+    loop.Run();
+    // There is a possible race in |leveldb_close_event| where the signaling
+    // thread is still in the WaitableEvent::Signal() method. To ensure that
+    // the other thread exits their Signal method, any method on the
+    // WaitableEvent can be called to acquire the internal lock (which will
+    // subsequently wait for the other thread to exit the Signal method).
+    EXPECT_TRUE(leveldb_close_event_ptr->IsSignaled());
   }
 }
 
diff --git a/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.h b/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.h
index 0cad1d3..7538df3c 100644
--- a/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.h
+++ b/components/services/storage/indexed_db/scopes/leveldb_scopes_test_utils.h
@@ -103,6 +103,8 @@
   // may need to run cleanup tasks that close files residing in the former.
   base::ScopedTempDir temp_directory_;
   base::test::TaskEnvironment task_env_;
+  // For use with calling leveldb_->RequestDestruction(...);
+  base::WaitableEvent leveldb_close_event_;
 
   const std::string simple_lock_begin_ = "0000000001";
   const std::string simple_lock_end_ = "0000000010";
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index e10cad9..962d24e 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -43,7 +43,7 @@
   deps = [
     "//base",
     "//base:base_static",
-    "//base:clang_coverage_buildflags",
+    "//base:clang_profiling_buildflags",
     "//base/third_party/dynamic_annotations",
     "//build:branding_buildflags",
     "//cc",
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index ab64a262..fad3fc5 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -48,6 +48,7 @@
 #include "third_party/iaccessible2/ia2_api_all.h"
 #include "third_party/isimpledom/ISimpleDOMNode.h"
 #include "ui/accessibility/accessibility_switches.h"
+#include "ui/accessibility/ax_event_generator.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 
@@ -907,10 +908,42 @@
 
 }  // namespace
 
+//
 // Tests ----------------------------------------------------------------------
+//
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+                       TestAlwaysFireFocusEventAfterNavigationComplete) {
+  ASSERT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
+
+  // Users of Jaws or NVDA screen readers might not realize that the virtual
+  // buffer has been loaded, if focus hasn't been placed in the document after
+  // navigating to a new page. If a focus event is not received by the screen
+  // reader, it might "think" that focus is outside the web contents.
+  //
+  // We can't use "LoadInitialAccessibilityTreeFromHtml" because it waits for
+  // the "kLoadComplete" event, and the "kFocus" and "kLoadComplete" events are
+  // not guaranteed to be sent in the same order every time, neither do we need
+  // to enforce such an ordering. However, we do need to ensure that at the
+  // point when the "kFocus" event is sent, the document is fully loaded.
+  AccessibilityNotificationWaiter waiter(
+      shell()->web_contents(), ui::kAXModeComplete,
+      ui::AXEventGenerator::Event::FOCUS_CHANGED);
+  GURL html_data_url("data:text/html,<p>Hello world.</p>");
+  ASSERT_TRUE(NavigateToURL(shell(), html_data_url));
+  waiter.WaitForNotification();
+
+  // Check that the page has indeed loaded.
+  AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
+  AccessibleChecker paragraph_checker(L"", L"P", IA2_ROLE_PARAGRAPH, L"");
+  document_checker.AppendExpectedChild(&paragraph_checker);
+  AccessibleChecker text_checker(L"Hello world.", ROLE_SYSTEM_STATICTEXT, L"");
+  paragraph_checker.AppendExpectedChild(&text_checker);
+  document_checker.CheckAccessible(GetRendererAccessible());
+}
 
 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestBusyAccessibilityTree) {
-  EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
+  ASSERT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
 
   // The initial accessible returned should have state STATE_SYSTEM_BUSY while
   // the accessibility tree is being requested from the renderer.
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 589673f..cf6d248f 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -327,30 +327,6 @@
       base::Unretained(host), method);
 }
 
-template <typename WorkerHost, typename Interface>
-base::RepeatingCallback<void(const url::Origin&,
-                             mojo::PendingReceiver<Interface>)>
-BindWorkerReceiverForOriginAndCOEP(
-    void (RenderProcessHost::*method)(const network::CrossOriginEmbedderPolicy&,
-                                      const url::Origin&,
-                                      mojo::PendingReceiver<Interface>),
-    WorkerHost* host,
-    const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy) {
-  return base::BindRepeating(
-      [](WorkerHost* host,
-         void (RenderProcessHost::*method)(
-             const network::CrossOriginEmbedderPolicy&, const url::Origin&,
-             mojo::PendingReceiver<Interface>),
-         const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
-         const url::Origin& origin, mojo::PendingReceiver<Interface> receiver) {
-        RenderProcessHost* process_host = host->GetProcessHost();
-        if (process_host)
-          (process_host->*method)(cross_origin_embedder_policy, origin,
-                                  std::move(receiver));
-      },
-      base::Unretained(host), method, cross_origin_embedder_policy);
-}
-
 template <typename... Args>
 void RunOrPostTaskToBindServiceWorkerReceiver(
     ServiceWorkerProviderHost* host,
@@ -428,33 +404,6 @@
 }
 
 template <typename Interface>
-base::RepeatingCallback<void(const ServiceWorkerVersionInfo&,
-                             mojo::PendingReceiver<Interface>)>
-BindServiceWorkerReceiverForOriginAndCOEP(
-    void (RenderProcessHost::*method)(const network::CrossOriginEmbedderPolicy&,
-                                      const url::Origin&,
-                                      mojo::PendingReceiver<Interface>),
-    ServiceWorkerProviderHost* host,
-    const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy) {
-  return base::BindRepeating(
-      [](ServiceWorkerProviderHost* host,
-         void (RenderProcessHost::*method)(
-             const network::CrossOriginEmbedderPolicy&, const url::Origin&,
-             mojo::PendingReceiver<Interface>),
-         const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
-         const ServiceWorkerVersionInfo& info,
-         mojo::PendingReceiver<Interface> receiver) {
-        auto origin = info.script_origin;
-        RunOrPostTaskToBindServiceWorkerReceiver<
-            const network::CrossOriginEmbedderPolicy&, const url::Origin&,
-            mojo::PendingReceiver<Interface>>(host, method,
-                                              cross_origin_embedder_policy,
-                                              origin, std::move(receiver));
-      },
-      base::Unretained(host), method, cross_origin_embedder_policy);
-}
-
-template <typename Interface>
 void EmptyBinderForFrame(RenderFrameHost* host,
                          mojo::PendingReceiver<Interface> receiver) {
   DLOG(ERROR) << "Empty binder for interface " << Interface::Name_
@@ -790,6 +739,8 @@
   map->Add<blink::mojom::QuicTransportConnector>(
       base::BindRepeating(&DedicatedWorkerHost::CreateQuicTransportConnector,
                           base::Unretained(host)));
+  map->Add<blink::mojom::CacheStorage>(base::BindRepeating(
+      &DedicatedWorkerHost::BindCacheStorage, base::Unretained(host)));
 #if !defined(OS_ANDROID)
   map->Add<blink::mojom::SerialService>(base::BindRepeating(
       &DedicatedWorkerHost::BindSerialService, base::Unretained(host)));
@@ -825,12 +776,6 @@
   map->Add<blink::mojom::QuotaDispatcherHost>(
       BindWorkerReceiverForOriginAndFrameId(
           &RenderProcessHost::BindQuotaDispatcherHost, host));
-
-  // render process host binders taking a Cross-Origin-Embedder-Policy and an
-  // origin.
-  map->Add<blink::mojom::CacheStorage>(BindWorkerReceiverForOriginAndCOEP(
-      &RenderProcessHost::BindCacheStorage, host,
-      host->cross_origin_embedder_policy()));
 }
 
 void PopulateBinderMap(DedicatedWorkerHost* host,
@@ -866,6 +811,8 @@
       &SharedWorkerHost::CreateAppCacheBackend, base::Unretained(host)));
   map->Add<blink::mojom::QuicTransportConnector>(base::BindRepeating(
       &SharedWorkerHost::CreateQuicTransportConnector, base::Unretained(host)));
+  map->Add<blink::mojom::CacheStorage>(base::BindRepeating(
+      &SharedWorkerHost::BindCacheStorage, base::Unretained(host)));
 
   // render process host binders
   map->Add<media::mojom::VideoDecodePerfHistory>(
@@ -899,14 +846,6 @@
   map->Add<blink::mojom::QuotaDispatcherHost>(
       BindWorkerReceiverForOriginAndFrameId(
           &RenderProcessHost::BindQuotaDispatcherHost, host));
-
-  // render process host binders taking a Cross-Origin-Embedder-Policy and an
-  // origin.
-  // TODO(https://crbug.com/1031542): Add support enforcing CORP in
-  // cache.match() for SharedWorker
-  map->Add<blink::mojom::CacheStorage>(BindWorkerReceiverForOriginAndCOEP(
-      &RenderProcessHost::BindCacheStorage, host,
-      network::CrossOriginEmbedderPolicy()));
 }
 
 void PopulateBinderMap(SharedWorkerHost* host,
@@ -943,6 +882,8 @@
   map->Add<blink::mojom::QuicTransportConnector>(base::BindRepeating(
       &ServiceWorkerProviderHost::CreateQuicTransportConnector,
       base::Unretained(host)));
+  map->Add<blink::mojom::CacheStorage>(base::BindRepeating(
+      &ServiceWorkerProviderHost::BindCacheStorage, base::Unretained(host)));
   map->Add<blink::mojom::BadgeService>(
       base::BindRepeating(&BindBadgeServiceForServiceWorker, host));
 
@@ -1009,15 +950,6 @@
   map->Add<blink::mojom::QuotaDispatcherHost>(
       BindServiceWorkerReceiverForOriginAndFrameId(
           &RenderProcessHost::BindQuotaDispatcherHost, host));
-
-  // render process host bind taking a Cross-Origin-Embedder-Policy and an
-  // origin.
-  // TODO(https://crbug.com/1031542): Add support enforcing CORP in
-  // cache.match() for ServiceWorker
-  map->Add<blink::mojom::CacheStorage>(
-      BindServiceWorkerReceiverForOriginAndCOEP(
-          &RenderProcessHost::BindCacheStorage, host,
-          network::CrossOriginEmbedderPolicy()));
 }
 
 void PopulateBinderMap(ServiceWorkerProviderHost* host,
diff --git a/content/browser/browser_process_sub_thread.cc b/content/browser/browser_process_sub_thread.cc
index 70dd877..f7c731e 100644
--- a/content/browser/browser_process_sub_thread.cc
+++ b/content/browser/browser_process_sub_thread.cc
@@ -5,7 +5,7 @@
 #include "content/browser/browser_process_sub_thread.h"
 
 #include "base/bind.h"
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/compiler_specific.h"
 #include "base/debug/alias.h"
 #include "base/metrics/histogram_macros.h"
@@ -162,8 +162,8 @@
         service_manager::SandboxType::kNetwork) {
       // This ensures that cookies and cache are flushed to disk on shutdown.
       // https://crbug.com/841001
-#if BUILDFLAG(CLANG_COVERAGE)
-      // On coverage build, browser_tests runs 10x slower.
+#if BUILDFLAG(CLANG_PROFILING)
+      // On profiling build, browser_tests runs 10x slower.
       const int kMaxSecondsToWaitForNetworkProcess = 100;
 #elif defined(OS_CHROMEOS)
       // ChromeOS will kill the browser process if it doesn't shut down within
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index 757a82d..57d627b 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/i18n/icu_util.h"
@@ -43,7 +43,7 @@
       start_time_(base::TimeTicks::Now()),
 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) ||  \
     defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
-    defined(UNDEFINED_SANITIZER) || BUILDFLAG(CLANG_COVERAGE)
+    defined(UNDEFINED_SANITIZER) || BUILDFLAG(CLANG_PROFILING)
       terminate_child_on_shutdown_(false)
 #else
       terminate_child_on_shutdown_(terminate_on_shutdown)
diff --git a/content/browser/child_process_task_port_provider_mac_unittest.cc b/content/browser/child_process_task_port_provider_mac_unittest.cc
index 3f0c02d2..ed599f76 100644
--- a/content/browser/child_process_task_port_provider_mac_unittest.cc
+++ b/content/browser/child_process_task_port_provider_mac_unittest.cc
@@ -8,7 +8,7 @@
 
 #include <vector>
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/mac/scoped_mach_port.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
@@ -35,8 +35,8 @@
 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
   MOCK_METHOD1(SetIPCLoggingEnabled, void(bool));
 #endif
-#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
-  MOCK_METHOD1(SetCoverageFile, void(base::File));
+#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
+  MOCK_METHOD1(SetProfilingFile, void(base::File));
 #endif
   MOCK_METHOD1(GetBackgroundTracingAgentProvider,
                void(mojo::PendingReceiver<
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.cc b/content/browser/devtools/service_worker_devtools_agent_host.cc
index 111f2afa..02c8487 100644
--- a/content/browser/devtools/service_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/service_worker_devtools_agent_host.cc
@@ -71,6 +71,8 @@
     const GURL& url,
     const GURL& scope,
     bool is_installed_version,
+    base::Optional<network::CrossOriginEmbedderPolicy>
+        cross_origin_embedder_policy,
     const base::UnguessableToken& devtools_worker_token)
     : DevToolsAgentHostImpl(devtools_worker_token.ToString()),
       state_(WORKER_NOT_READY),
@@ -83,7 +85,8 @@
       url_(url),
       scope_(scope),
       version_installed_time_(is_installed_version ? base::Time::Now()
-                                                   : base::Time()) {
+                                                   : base::Time()),
+      cross_origin_embedder_policy_(std::move(cross_origin_embedder_policy)) {
   NotifyCreated();
 }
 
@@ -173,6 +176,11 @@
     UpdateIsAttached(true);
 }
 
+void ServiceWorkerDevToolsAgentHost::UpdateCrossOriginEmbedderPolicy(
+    network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
+  cross_origin_embedder_policy_ = std::move(cross_origin_embedder_policy);
+}
+
 void ServiceWorkerDevToolsAgentHost::WorkerRestarted(int worker_process_id,
                                                      int worker_route_id) {
   DCHECK_EQ(WORKER_TERMINATED, state_);
@@ -206,13 +214,20 @@
     return;
   }
   const url::Origin origin = url::Origin::Create(url_);
-  // TODO(https://crbug.com/1039613): Get the COEP for the service worker
-  // and pass it to each factory bundle.
+
+  // Use the default CrossOriginEmbedderPolicy if
+  // |cross_origin_embedder_policy_| is nullopt. It's acceptable because the
+  // factory bundles are updated with correct COEP value before any subresource
+  // requests in that case.
   auto script_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
-      rph, worker_route_id_, origin, network::CrossOriginEmbedderPolicy(),
+      rph, worker_route_id_, origin,
+      cross_origin_embedder_policy_ ? cross_origin_embedder_policy_.value()
+                                    : network::CrossOriginEmbedderPolicy(),
       ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript);
   auto subresource_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
-      rph, worker_route_id_, origin, network::CrossOriginEmbedderPolicy(),
+      rph, worker_route_id_, origin,
+      cross_origin_embedder_policy_ ? cross_origin_embedder_policy_.value()
+                                    : network::CrossOriginEmbedderPolicy(),
       ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource);
 
   if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.h b/content/browser/devtools/service_worker_devtools_agent_host.h
index aa33022..b2bc1ce 100644
--- a/content/browser/devtools/service_worker_devtools_agent_host.h
+++ b/content/browser/devtools/service_worker_devtools_agent_host.h
@@ -14,6 +14,7 @@
 #include "base/unguessable_token.h"
 #include "content/browser/devtools/devtools_agent_host_impl.h"
 #include "content/browser/devtools/service_worker_devtools_manager.h"
+#include "services/network/public/cpp/cross_origin_embedder_policy.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
 
 namespace content {
@@ -35,6 +36,8 @@
       const GURL& url,
       const GURL& scope,
       bool is_installed_version,
+      base::Optional<network::CrossOriginEmbedderPolicy>
+          cross_origin_embedder_policy,
       const base::UnguessableToken& devtools_worker_token);
 
   // DevToolsAgentHost overrides.
@@ -50,6 +53,8 @@
   void WorkerReadyForInspection(
       mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote,
       mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver);
+  void UpdateCrossOriginEmbedderPolicy(
+      network::CrossOriginEmbedderPolicy cross_origin_embedder_policy);
   void WorkerDestroyed();
   void WorkerVersionInstalled();
   void WorkerVersionDoomed();
@@ -97,6 +102,8 @@
   GURL scope_;
   base::Time version_installed_time_;
   base::Time version_doomed_time_;
+  base::Optional<network::CrossOriginEmbedderPolicy>
+      cross_origin_embedder_policy_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDevToolsAgentHost);
 };
diff --git a/content/browser/devtools/service_worker_devtools_manager.cc b/content/browser/devtools/service_worker_devtools_manager.cc
index 19f53dab..442c93d6 100644
--- a/content/browser/devtools/service_worker_devtools_manager.cc
+++ b/content/browser/devtools/service_worker_devtools_manager.cc
@@ -52,6 +52,8 @@
     const GURL& url,
     const GURL& scope,
     bool is_installed_version,
+    base::Optional<network::CrossOriginEmbedderPolicy>
+        cross_origin_embedder_policy,
     base::UnguessableToken* devtools_worker_token,
     bool* pause_on_start) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -66,10 +68,10 @@
   if (it == terminated_hosts_.end()) {
     *devtools_worker_token = base::UnguessableToken::Create();
     scoped_refptr<ServiceWorkerDevToolsAgentHost> host =
-        new ServiceWorkerDevToolsAgentHost(worker_process_id, worker_route_id,
-                                           context, context_weak, version_id,
-                                           url, scope, is_installed_version,
-                                           *devtools_worker_token);
+        new ServiceWorkerDevToolsAgentHost(
+            worker_process_id, worker_route_id, context, context_weak,
+            version_id, url, scope, is_installed_version,
+            cross_origin_embedder_policy, *devtools_worker_token);
     live_hosts_[worker_id] = host;
     *pause_on_start = debug_service_worker_on_start_;
     for (auto& observer : observer_list_) {
@@ -107,6 +109,20 @@
     host->Inspect();
 }
 
+void ServiceWorkerDevToolsManager::UpdateCrossOriginEmbedderPolicy(
+    int worker_process_id,
+    int worker_route_id,
+    network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  const WorkerId worker_id(worker_process_id, worker_route_id);
+  auto it = live_hosts_.find(worker_id);
+  if (it == live_hosts_.end())
+    return;
+  scoped_refptr<ServiceWorkerDevToolsAgentHost> host = it->second;
+  host->UpdateCrossOriginEmbedderPolicy(
+      std::move(cross_origin_embedder_policy));
+}
+
 void ServiceWorkerDevToolsManager::WorkerVersionInstalled(int worker_process_id,
                                                           int worker_route_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/devtools/service_worker_devtools_manager.h b/content/browser/devtools/service_worker_devtools_manager.h
index 83ac10a8..aab651b 100644
--- a/content/browser/devtools/service_worker_devtools_manager.h
+++ b/content/browser/devtools/service_worker_devtools_manager.h
@@ -16,6 +16,7 @@
 #include "base/observer_list.h"
 #include "base/unguessable_token.h"
 #include "content/common/content_export.h"
+#include "services/network/public/cpp/cross_origin_embedder_policy.h"
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
 #include "url/gurl.h"
@@ -66,6 +67,8 @@
                      const GURL& url,
                      const GURL& scope,
                      bool is_installed_version,
+                     base::Optional<network::CrossOriginEmbedderPolicy>
+                         cross_origin_embedder_policy,
                      base::UnguessableToken* devtools_worker_token,
                      bool* pause_on_start);
   void WorkerReadyForInspection(
@@ -73,6 +76,10 @@
       int worker_route_id,
       mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote,
       mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver);
+  void UpdateCrossOriginEmbedderPolicy(
+      int worker_process_id,
+      int worker_route_id,
+      network::CrossOriginEmbedderPolicy cross_origin_embedder_policy);
   void WorkerVersionInstalled(int worker_process_id, int worker_route_id);
   void WorkerVersionDoomed(int worker_process_id, int worker_route_id);
   void WorkerDestroyed(int worker_process_id, int worker_route_id);
diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc
index a803a8b9..7b0c02d 100644
--- a/content/browser/frame_host/frame_tree.cc
+++ b/content/browser/frame_host/frame_tree.cc
@@ -384,14 +384,12 @@
 
 scoped_refptr<RenderViewHostImpl> FrameTree::CreateRenderViewHost(
     SiteInstance* site_instance,
-    int32_t routing_id,
     int32_t main_frame_routing_id,
-    int32_t widget_routing_id,
     bool swapped_out) {
   RenderViewHostImpl* rvh =
       static_cast<RenderViewHostImpl*>(RenderViewHostFactory::Create(
           site_instance, render_view_delegate_, render_widget_delegate_,
-          routing_id, main_frame_routing_id, widget_routing_id, swapped_out));
+          main_frame_routing_id, swapped_out));
   RegisterRenderViewHost(rvh);
   return base::WrapRefCounted(rvh);
 }
diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h
index f859e576..0b44b163 100644
--- a/content/browser/frame_host/frame_tree.h
+++ b/content/browser/frame_host/frame_tree.h
@@ -207,9 +207,7 @@
   // of this object.
   scoped_refptr<RenderViewHostImpl> CreateRenderViewHost(
       SiteInstance* site_instance,
-      int32_t routing_id,
       int32_t main_frame_routing_id,
-      int32_t widget_routing_id,
       bool swapped_out);
 
   // Returns the existing RenderViewHost for a new RenderFrameHost.
diff --git a/content/browser/frame_host/frame_tree_unittest.cc b/content/browser/frame_host/frame_tree_unittest.cc
index 79c50ef..8b7b4ba 100644
--- a/content/browser/frame_host/frame_tree_unittest.cc
+++ b/content/browser/frame_host/frame_tree_unittest.cc
@@ -166,7 +166,7 @@
   // itself. Instead, leave them in "not live" state, which is indicated by the
   // * after the frame id, since this test cares about the shape, not the
   // frame liveness.
-  EXPECT_EQ("3: []", GetTreeState(frame_tree));
+  EXPECT_EQ("1: []", GetTreeState(frame_tree));
 
   constexpr auto kOwnerType = blink::FrameOwnerElementType::kIframe;
   // Simulate attaching a series of frames to build the frame tree.
@@ -208,7 +208,7 @@
       blink::mojom::FrameOwnerProperties(), false, kOwnerType);
 
   EXPECT_EQ(
-      "3: [14: [244: [], 245: []], "
+      "1: [14: [244: [], 245: []], "
       "15: [255 'no children node': []], "
       "16: []]",
       GetTreeState(frame_tree));
@@ -276,7 +276,7 @@
 
   // Now that's it's fully built, verify the tree structure is as expected.
   EXPECT_EQ(
-      "3: [14: [244: [], 245: []], "
+      "1: [14: [244: [], 245: []], "
       "15: [255 'no children node': []], "
       "16: [264: [], 265: [], 266: [], "
       "267 'node with deep subtree': "
@@ -290,27 +290,27 @@
   FrameTreeNode* child_245 = child_14->child_at(1);
   FrameTreeNode* child_555 = child_267->child_at(0)->child_at(0)->child_at(0);
   FrameTreeNode* child_655 = child_555->child_at(0);
-  EXPECT_EQ("3 14 15 16 244 245 255 264 265 266 267 268 365 455 555 655",
+  EXPECT_EQ("1 14 15 16 244 245 255 264 265 266 267 268 365 455 555 655",
             GetTraversalOrder(frame_tree, nullptr));
-  EXPECT_EQ("3", GetTraversalOrder(frame_tree, root));
-  EXPECT_EQ("3 14 15 16 255 264 265 266 267 268 365 455 555 655",
+  EXPECT_EQ("1", GetTraversalOrder(frame_tree, root));
+  EXPECT_EQ("1 14 15 16 255 264 265 266 267 268 365 455 555 655",
             GetTraversalOrder(frame_tree, child_14));
-  EXPECT_EQ("3 14 15 16 244 245 255 264 265 266 267 268 365 455 555 655",
+  EXPECT_EQ("1 14 15 16 244 245 255 264 265 266 267 268 365 455 555 655",
             GetTraversalOrder(frame_tree, child_244));
-  EXPECT_EQ("3 14 15 16 244 245 255 264 265 266 267 268 365 455 555 655",
+  EXPECT_EQ("1 14 15 16 244 245 255 264 265 266 267 268 365 455 555 655",
             GetTraversalOrder(frame_tree, child_245));
-  EXPECT_EQ("3 14 15 16 244 245 264 265 266 267 268 365 455 555 655",
+  EXPECT_EQ("1 14 15 16 244 245 264 265 266 267 268 365 455 555 655",
             GetTraversalOrder(frame_tree, child_15));
-  EXPECT_EQ("3 14 15 16 244 245 255 264 265 266 267 268",
+  EXPECT_EQ("1 14 15 16 244 245 255 264 265 266 267 268",
             GetTraversalOrder(frame_tree, child_267));
-  EXPECT_EQ("3 14 15 16 244 245 255 264 265 266 267 268 365 455 555",
+  EXPECT_EQ("1 14 15 16 244 245 255 264 265 266 267 268 365 455 555",
             GetTraversalOrder(frame_tree, child_555));
-  EXPECT_EQ("3 14 15 16 244 245 255 264 265 266 267 268 365 455 555 655",
+  EXPECT_EQ("1 14 15 16 244 245 255 264 265 266 267 268 365 455 555 655",
             GetTraversalOrder(frame_tree, child_655));
 
   frame_tree->RemoveFrame(child_555);
   EXPECT_EQ(
-      "3: [14: [244: [], 245: []], "
+      "1: [14: [244: [], 245: []], "
       "15: [255 'no children node': []], "
       "16: [264: [], 265: [], 266: [], "
       "267 'node with deep subtree': "
@@ -319,7 +319,7 @@
 
   frame_tree->RemoveFrame(child_16->child_at(1));
   EXPECT_EQ(
-      "3: [14: [244: [], 245: []], "
+      "1: [14: [244: [], 245: []], "
       "15: [255 'no children node': []], "
       "16: [264: [], 266: [], "
       "267 'node with deep subtree': "
@@ -328,7 +328,7 @@
 
   frame_tree->RemoveFrame(root->child_at(1));
   EXPECT_EQ(
-      "3: [14: [244: [], 245: []], "
+      "1: [14: [244: [], 245: []], "
       "16: [264: [], 266: [], "
       "267 'node with deep subtree': "
       "[365: [455: []]], 268: []]]",
@@ -460,7 +460,7 @@
 TEST_F(FrameTreeTest, ObserverWalksTreeDuringFrameCreation) {
   TreeWalkingWebContentsLogger activity(contents());
   contents()->NavigateAndCommit(GURL("http://www.google.com"));
-  EXPECT_EQ("RenderFrameCreated(3) -> 3: []", activity.GetLog());
+  EXPECT_EQ("RenderFrameCreated(1) -> 1: []", activity.GetLog());
 
   FrameTree* frame_tree = contents()->GetFrameTree();
   FrameTreeNode* root = frame_tree->root();
@@ -474,8 +474,8 @@
       base::UnguessableToken::Create(), blink::FramePolicy(),
       blink::mojom::FrameOwnerProperties(), kOwnerType);
   EXPECT_EQ(
-      "RenderFrameHostChanged(new)(14) -> 3: []\n"
-      "RenderFrameCreated(14) -> 3: [14: []]",
+      "RenderFrameHostChanged(new)(14) -> 1: []\n"
+      "RenderFrameCreated(14) -> 1: [14: []]",
       activity.GetLog());
   main_test_rfh()->OnCreateChildFrame(
       18, CreateStubInterfaceProviderReceiver(),
@@ -484,13 +484,13 @@
       base::UnguessableToken::Create(), blink::FramePolicy(),
       blink::mojom::FrameOwnerProperties(), kOwnerType);
   EXPECT_EQ(
-      "RenderFrameHostChanged(new)(18) -> 3: [14: []]\n"
-      "RenderFrameCreated(18) -> 3: [14: [], 18: []]",
+      "RenderFrameHostChanged(new)(18) -> 1: [14: []]\n"
+      "RenderFrameCreated(18) -> 1: [14: [], 18: []]",
       activity.GetLog());
   frame_tree->RemoveFrame(root->child_at(0));
-  EXPECT_EQ("RenderFrameDeleted(14) -> 3: [18: []]", activity.GetLog());
+  EXPECT_EQ("RenderFrameDeleted(14) -> 1: [18: []]", activity.GetLog());
   frame_tree->RemoveFrame(root->child_at(0));
-  EXPECT_EQ("RenderFrameDeleted(18) -> 3: []", activity.GetLog());
+  EXPECT_EQ("RenderFrameDeleted(18) -> 1: []", activity.GetLog());
 }
 
 // Make sure that WebContentsObservers see a consistent view of the tree after
@@ -498,7 +498,7 @@
 TEST_F(FrameTreeTest, ObserverWalksTreeAfterCrash) {
   TreeWalkingWebContentsLogger activity(contents());
   contents()->NavigateAndCommit(GURL("http://www.google.com"));
-  EXPECT_EQ("RenderFrameCreated(3) -> 3: []", activity.GetLog());
+  EXPECT_EQ("RenderFrameCreated(1) -> 1: []", activity.GetLog());
 
   constexpr auto kOwnerType = blink::FrameOwnerElementType::kIframe;
   main_test_rfh()->OnCreateChildFrame(
@@ -508,8 +508,8 @@
       base::UnguessableToken::Create(), blink::FramePolicy(),
       blink::mojom::FrameOwnerProperties(), kOwnerType);
   EXPECT_EQ(
-      "RenderFrameHostChanged(new)(22) -> 3: []\n"
-      "RenderFrameCreated(22) -> 3: [22: []]",
+      "RenderFrameHostChanged(new)(22) -> 1: []\n"
+      "RenderFrameCreated(22) -> 1: [22: []]",
       activity.GetLog());
   main_test_rfh()->OnCreateChildFrame(
       23, CreateStubInterfaceProviderReceiver(),
@@ -518,17 +518,17 @@
       base::UnguessableToken::Create(), blink::FramePolicy(),
       blink::mojom::FrameOwnerProperties(), kOwnerType);
   EXPECT_EQ(
-      "RenderFrameHostChanged(new)(23) -> 3: [22: []]\n"
-      "RenderFrameCreated(23) -> 3: [22: [], 23: []]",
+      "RenderFrameHostChanged(new)(23) -> 1: [22: []]\n"
+      "RenderFrameCreated(23) -> 1: [22: [], 23: []]",
       activity.GetLog());
 
   // Crash the renderer
   main_test_rfh()->GetProcess()->SimulateCrash();
   EXPECT_EQ(
-      "RenderProcessGone -> 3*: [22*: [], 23*: []]\n"
-      "RenderFrameDeleted(23) -> 3*: []\n"
-      "RenderFrameDeleted(22) -> 3*: []\n"
-      "RenderFrameDeleted(3) -> 3*: []",
+      "RenderProcessGone -> 1*: [22*: [], 23*: []]\n"
+      "RenderFrameDeleted(23) -> 1*: []\n"
+      "RenderFrameDeleted(22) -> 1*: []\n"
+      "RenderFrameDeleted(1) -> 1*: []",
       activity.GetLog());
 }
 
@@ -540,7 +540,7 @@
   FrameTreeNode* root = frame_tree->root();
   int process_id = root->current_frame_host()->GetProcess()->GetID();
 
-  ASSERT_EQ("3: []", GetTreeState(frame_tree));
+  ASSERT_EQ("1: []", GetTreeState(frame_tree));
 
   // Simulate attaching a frame from mismatched process id.
   ASSERT_FALSE(frame_tree->AddFrame(
@@ -550,7 +550,7 @@
       base::UnguessableToken::Create(), blink::FramePolicy(),
       blink::mojom::FrameOwnerProperties(), false,
       blink::FrameOwnerElementType::kIframe));
-  ASSERT_EQ("3: []", GetTreeState(frame_tree));
+  ASSERT_EQ("1: []", GetTreeState(frame_tree));
 }
 
 // Ensure that frames removed while a process has crashed are not preserved in
diff --git a/content/browser/frame_host/interstitial_page_impl.cc b/content/browser/frame_host/interstitial_page_impl.cc
index c1bb496..09cec85 100644
--- a/content/browser/frame_host/interstitial_page_impl.cc
+++ b/content/browser/frame_host/interstitial_page_impl.cc
@@ -602,9 +602,8 @@
       SessionStorageNamespaceImpl::Create(dom_storage_context);
 
   // Use the RenderViewHost from our FrameTree.
-  frame_tree_->root()->render_manager()->Init(
-      site_instance.get(), MSG_ROUTING_NONE, MSG_ROUTING_NONE, MSG_ROUTING_NONE,
-      false);
+  frame_tree_->root()->render_manager()->Init(site_instance.get(),
+                                              MSG_ROUTING_NONE, false);
   return frame_tree_->root()->current_frame_host()->render_view_host();
 }
 
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 147bce2..9a394012 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1821,30 +1821,45 @@
   auto cross_origin_embedder_policy =
       response_head_->cross_origin_embedder_policy;
   if (base::FeatureList::IsEnabled(network::features::kCrossOriginIsolation)) {
-    // https://mikewest.github.io/corpp/#integration-html
+    // https://mikewest.github.io/corpp/#process-navigation-response
     if (auto* const parent_frame = GetParentFrame()) {
       const auto& parent_coep = parent_frame->cross_origin_embedder_policy();
       const auto& url = common_params_->url;
-      if (parent_coep.value ==
-          network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp) {
-        if (url.SchemeIsBlob() || url.SchemeIs(url::kDataScheme)) {
-          // Some special URLs not loaded using the network are inheriting the
-          // Cross-Origin-Embedder-Policy header from their parent.
-          cross_origin_embedder_policy.value =
-              network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+      constexpr auto kRequireCorp =
+          network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+      constexpr auto kNone =
+          network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+
+      // Some special URLs not loaded using the network are inheriting the
+      // Cross-Origin-Embedder-Policy header from their parent.
+      const bool has_allowed_scheme =
+          url.SchemeIsBlob() || url.SchemeIs(url::kDataScheme);
+      if (parent_coep.value == kRequireCorp && has_allowed_scheme) {
+        cross_origin_embedder_policy.value = kRequireCorp;
+      }
+
+      auto* const coep_reporter = parent_frame->coep_reporter();
+      if (parent_coep.report_only_value == kRequireCorp &&
+          !has_allowed_scheme && cross_origin_embedder_policy.value == kNone &&
+          coep_reporter) {
+        coep_reporter->QueueNavigationReport(redirect_chain_[0],
+                                             /*report_only=*/true);
+      }
+      if (parent_coep.value == kRequireCorp &&
+          cross_origin_embedder_policy.value == kNone) {
+        if (coep_reporter) {
+          coep_reporter->QueueNavigationReport(redirect_chain_[0],
+                                               /*report_only=*/false);
         }
-        if (cross_origin_embedder_policy.value ==
-            network::mojom::CrossOriginEmbedderPolicyValue::kNone) {
-          OnRequestFailedInternal(network::URLLoaderCompletionStatus(
-                                      network::BlockedByResponseReason::
-                                          kCoepFrameResourceNeedsCoepHeader),
-                                  false /* skip_throttles */,
-                                  base::nullopt /* error_page_content */,
-                                  false /* collapse_frame */);
-          // DO NOT ADD CODE after this. The previous call to
-          // OnRequestFailedInternal has destroyed the NavigationRequest.
-          return;
-        }
+        OnRequestFailedInternal(network::URLLoaderCompletionStatus(
+                                    network::BlockedByResponseReason::
+                                        kCoepFrameResourceNeedsCoepHeader),
+                                false /* skip_throttles */,
+                                base::nullopt /* error_page_content */,
+                                false /* collapse_frame */);
+        // DO NOT ADD CODE after this. The previous call to
+        // OnRequestFailedInternal has destroyed the NavigationRequest.
+        return;
       }
     }
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 0d33a622..362d1b0 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -2297,9 +2297,7 @@
   // Initialize the RenderFrameHost for the new node.  We always create child
   // frames in the same SiteInstance as the current frame, and they can swap to
   // a different one if they navigate away.
-  child->render_manager()->Init(GetSiteInstance(),
-                                render_view_host()->GetRoutingID(),
-                                frame_routing_id, MSG_ROUTING_NONE, false);
+  child->render_manager()->Init(GetSiteInstance(), frame_routing_id, false);
 
   // Other renderer processes in this BrowsingInstance may need to find out
   // about the new frame.  Create a proxy for the child frame in all
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 88e0657..d9e3b753 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -285,13 +285,10 @@
 }
 
 void RenderFrameHostManager::Init(SiteInstance* site_instance,
-                                  int32_t view_routing_id,
                                   int32_t frame_routing_id,
-                                  int32_t widget_routing_id,
                                   bool renderer_initiated_creation) {
   DCHECK(site_instance);
-  SetRenderFrameHost(CreateRenderFrameHost(site_instance, view_routing_id,
-                                           frame_routing_id, widget_routing_id,
+  SetRenderFrameHost(CreateRenderFrameHost(site_instance, frame_routing_id,
                                            renderer_initiated_creation));
 
   // Notify the delegate of the creation of the current RenderFrameHost.
@@ -2044,9 +2041,7 @@
 std::unique_ptr<RenderFrameHostImpl>
 RenderFrameHostManager::CreateRenderFrameHost(
     SiteInstance* site_instance,
-    int32_t view_routing_id,
     int32_t frame_routing_id,
-    int32_t widget_routing_id,
     bool renderer_initiated_creation) {
   if (frame_routing_id == MSG_ROUTING_NONE)
     frame_routing_id = site_instance->GetProcess()->GetNextRoutingID();
@@ -2058,24 +2053,9 @@
 
   if (frame_tree_node_->IsMainFrame()) {
     if (!render_view_host) {
-      render_view_host = frame_tree->CreateRenderViewHost(
-          site_instance, view_routing_id, frame_routing_id, widget_routing_id,
-          /*swapped_out=*/false);
-    }
-    // TODO(avi): It's a bit bizarre that this logic lives here instead of in
-    // CreateRenderFrame(). It turns out that FrameTree::CreateRenderViewHost
-    // doesn't /always/ create a new RenderViewHost. It first tries to find an
-    // already existing one to reuse by a SiteInstance lookup. If it finds one,
-    // then the supplied routing IDs are completely ignored.
-    // CreateRenderFrame() could do this lookup too, but it seems redundant to
-    // do this lookup in two places. This is a good yak shave to clean up, or,
-    // if just ignored, should be an easy cleanup once RenderViewHostImpl has-a
-    // RenderWidgetHostImpl. https://crbug.com/545684
-    if (view_routing_id == MSG_ROUTING_NONE) {
-      widget_routing_id = render_view_host->GetWidget()->GetRoutingID();
-    } else {
-      DCHECK_NE(view_routing_id, widget_routing_id);
-      DCHECK_EQ(view_routing_id, render_view_host->GetRoutingID());
+      render_view_host =
+          frame_tree->CreateRenderViewHost(site_instance, frame_routing_id,
+                                           /*swapped_out=*/false);
     }
   }
   CHECK(render_view_host);
@@ -2144,8 +2124,8 @@
          render_frame_host_->must_be_replaced() || IsRenderDocumentEnabled());
 
   std::unique_ptr<RenderFrameHostImpl> new_render_frame_host =
-      CreateRenderFrameHost(instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE,
-                            MSG_ROUTING_NONE, false);
+      CreateRenderFrameHost(instance, MSG_ROUTING_NONE,
+                            /*renderer_initiated_creation=*/false);
   DCHECK_EQ(new_render_frame_host->GetSiteInstance(), instance);
 
   // Prevent the process from exiting while we're trying to navigate in it.
@@ -2236,7 +2216,7 @@
       // Before creating a new RenderFrameProxyHost, ensure a RenderViewHost
       // exists for |instance|, as it creates the page level structure in Blink.
       render_view_host = frame_tree_node_->frame_tree()->CreateRenderViewHost(
-          instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE, MSG_ROUTING_NONE,
+          instance, /*frame_routing_id=*/MSG_ROUTING_NONE,
           /*swapped_out=*/true);
     }
     proxy = CreateRenderFrameProxyHost(instance, std::move(render_view_host));
diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h
index 8741164..1a49e27 100644
--- a/content/browser/frame_host/render_frame_host_manager.h
+++ b/content/browser/frame_host/render_frame_host_manager.h
@@ -183,9 +183,7 @@
 
   // For arguments, see WebContentsImpl constructor.
   void Init(SiteInstance* site_instance,
-            int32_t view_routing_id,
             int32_t frame_routing_id,
-            int32_t widget_routing_id,
             bool renderer_initiated_creation);
 
   // Returns the currently active RenderFrameHost.
@@ -695,9 +693,7 @@
   // Creates a RenderFrameHost and corresponding RenderViewHost if necessary.
   std::unique_ptr<RenderFrameHostImpl> CreateRenderFrameHost(
       SiteInstance* instance,
-      int32_t view_routing_id,
       int32_t frame_routing_id,
-      int32_t widget_routing_id,
       bool renderer_initiated_creation);
 
   // Create and initialize a speculative RenderFrameHost for an ongoing
diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
index 0d2a062..59c25e44 100644
--- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -23,6 +23,8 @@
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/synchronization/waitable_event_watcher.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/test/bind_test_util.h"
@@ -361,15 +363,29 @@
         base::RunLoop loop;
         IndexedDBOriginState* per_origin_factory =
             factory->GetOriginFactory(origin);
-        per_origin_factory->backing_store()
-            ->db()
-            ->leveldb_state()
-            ->RequestDestruction(loop.QuitClosure(),
-                                 base::SequencedTaskRunnerHandle::Get());
+
+        auto* leveldb_state =
+            per_origin_factory->backing_store()->db()->leveldb_state();
+
+        base::WaitableEvent leveldb_close_event;
+        base::WaitableEventWatcher event_watcher;
+        leveldb_state->RequestDestruction(&leveldb_close_event);
+        event_watcher.StartWatching(
+            &leveldb_close_event,
+            base::BindLambdaForTesting(
+                [&](base::WaitableEvent*) { loop.Quit(); }),
+            base::SequencedTaskRunnerHandle::Get());
+
         idb_context_->ForceCloseSync(
             origin,
             storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
         loop.Run();
+        // There is a possible race in |leveldb_close_event| where the signaling
+        // thread is still in the WaitableEvent::Signal() method. To ensure that
+        // the other thread exits their Signal method, any method on the
+        // WaitableEvent can be called to acquire the internal lock (which will
+        // subsequently wait for the other thread to exit the Signal method).
+        EXPECT_TRUE(leveldb_close_event.IsSignaled());
       }
       // All leveldb databases are closed, and they can be deleted.
       for (auto origin : idb_context_->GetAllOrigins()) {
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.cc b/content/browser/indexed_db/indexed_db_factory_impl.cc
index 7d3ae3ed..017d721 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.cc
+++ b/content/browser/indexed_db/indexed_db_factory_impl.cc
@@ -769,7 +769,7 @@
   // Scopes must be single sequence to keep methods like ForceClose synchronous.
   // See https://crbug.com/980685
   s = backing_store->db()->scopes()->StartRecoveryAndCleanupTasks(
-      LevelDBScopes::TaskRunnerMode::kUseCurrentSequence);
+      LevelDBScopes::TaskRunnerMode::kNewCleanupAndRevertSequences);
 
   if (UNLIKELY(!s.ok())) {
     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 301a9f4..33e50e4 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -90,32 +90,18 @@
       // deletion of the leveldb state. Once the states are no longer around,
       // delete all of the databases on disk.
       auto open_factory_origins = factory->GetOpenOrigins();
-      base::RunLoop loop;
-      auto callback = base::BarrierClosure(
-          open_factory_origins.size(), base::BindLambdaForTesting([&]() {
-            // All leveldb databases are closed, and they can be deleted.
-            for (auto origin : context_->GetAllOrigins()) {
-              bool success = false;
-              storage::mojom::IndexedDBControlAsyncWaiter waiter(
-                  context_.get());
-              waiter.DeleteForOrigin(origin, &success);
-              EXPECT_TRUE(success);
-            }
-            loop.Quit();
-          }));
       for (auto origin : open_factory_origins) {
-        IndexedDBOriginState* per_origin_factory =
-            factory->GetOriginFactory(origin);
-        per_origin_factory->backing_store()
-            ->db()
-            ->leveldb_state()
-            ->RequestDestruction(callback,
-                                 base::SequencedTaskRunnerHandle::Get());
         context_->ForceCloseSync(
             origin,
             storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
       }
-      loop.Run();
+      // All leveldb databases are closed, and they can be deleted.
+      for (auto origin : context_->GetAllOrigins()) {
+        bool success = false;
+        storage::mojom::IndexedDBControlAsyncWaiter waiter(context_.get());
+        waiter.DeleteForOrigin(origin, &success);
+        EXPECT_TRUE(success);
+      }
     }
     if (temp_dir_.IsValid())
       ASSERT_TRUE(temp_dir_.Delete());
diff --git a/content/browser/indexed_db/indexed_db_origin_state.cc b/content/browser/indexed_db/indexed_db_origin_state.cc
index 2bd391c..4f63b3f 100644
--- a/content/browser/indexed_db/indexed_db_origin_state.cc
+++ b/content/browser/indexed_db/indexed_db_origin_state.cc
@@ -13,6 +13,7 @@
 #include "base/feature_list.h"
 #include "base/rand_util.h"
 #include "base/stl_util.h"
+#include "base/synchronization/waitable_event.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h"
@@ -109,8 +110,16 @@
 
 IndexedDBOriginState::~IndexedDBOriginState() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (backing_store_ && backing_store_->IsBlobCleanupPending())
+  if (!backing_store_)
+    return;
+  if (backing_store_->IsBlobCleanupPending())
     backing_store_->ForceRunBlobCleanup();
+
+  base::WaitableEvent leveldb_destruct_event;
+  backing_store_->db()->leveldb_state()->RequestDestruction(
+      &leveldb_destruct_event);
+  backing_store_.reset();
+  leveldb_destruct_event.Wait();
 }
 
 void IndexedDBOriginState::AbortAllTransactions(bool compact) {
diff --git a/content/browser/indexed_db/indexed_db_origin_state.h b/content/browser/indexed_db/indexed_db_origin_state.h
index b2a6cc9..5aba88ea 100644
--- a/content/browser/indexed_db/indexed_db_origin_state.h
+++ b/content/browser/indexed_db/indexed_db_origin_state.h
@@ -213,7 +213,7 @@
   ClosingState closing_stage_ = ClosingState::kNotClosing;
   base::OneShotTimer close_timer_;
   const std::unique_ptr<DisjointRangeLockManager> lock_manager_;
-  const std::unique_ptr<IndexedDBBackingStore> backing_store_;
+  std::unique_ptr<IndexedDBBackingStore> backing_store_;
 
   OriginDBMap databases_;
   // This is the refcount for the number of IndexedDBOriginStateHandle's given
diff --git a/content/browser/indexed_db/indexed_db_unittest.cc b/content/browser/indexed_db/indexed_db_unittest.cc
index 1a08961..f3666863 100644
--- a/content/browser/indexed_db/indexed_db_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_unittest.cc
@@ -119,33 +119,20 @@
       // deletion of the leveldb state. Once the states are no longer around,
       // delete all of the databases on disk.
       auto open_factory_origins = factory->GetOpenOrigins();
-      base::RunLoop loop;
-      auto callback = base::BarrierClosure(
-          open_factory_origins.size(), base::BindLambdaForTesting([&]() {
-            // All leveldb databases are closed, and they can be deleted.
-            for (auto origin : context_->GetAllOrigins()) {
-              bool success = false;
-              storage::mojom::IndexedDBControlAsyncWaiter waiter(
-                  context_.get());
-              waiter.DeleteForOrigin(origin, &success);
-              EXPECT_TRUE(success);
-            }
-            loop.Quit();
-          }));
       for (auto origin : open_factory_origins) {
-        IndexedDBOriginState* per_origin_factory =
-            factory->GetOriginFactory(origin);
-        per_origin_factory->backing_store()
-            ->db()
-            ->leveldb_state()
-            ->RequestDestruction(callback,
-                                 base::SequencedTaskRunnerHandle::Get());
         context_->ForceCloseSync(
             origin,
             storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
       }
-      loop.Run();
+      // All leveldb databases are closed, and they can be deleted.
+      for (auto origin : context_->GetAllOrigins()) {
+        bool success = false;
+        storage::mojom::IndexedDBControlAsyncWaiter waiter(context_.get());
+        waiter.DeleteForOrigin(origin, &success);
+        EXPECT_TRUE(success);
+      }
     }
+
     if (temp_dir_.IsValid())
       ASSERT_TRUE(temp_dir_.Delete());
   }
diff --git a/content/browser/net/cross_origin_embedder_policy_reporter.cc b/content/browser/net/cross_origin_embedder_policy_reporter.cc
index e0a7c58..b435f1c 100644
--- a/content/browser/net/cross_origin_embedder_policy_reporter.cc
+++ b/content/browser/net/cross_origin_embedder_policy_reporter.cc
@@ -45,6 +45,26 @@
       std::move(body));
 }
 
+void CrossOriginEmbedderPolicyReporter::QueueNavigationReport(
+    const GURL& blocked_url,
+    bool report_only) {
+  const base::Optional<std::string>& endpoint =
+      report_only ? report_only_endpoint_ : endpoint_;
+  if (!endpoint) {
+    return;
+  }
+  url::Replacements<char> replacements;
+  replacements.ClearUsername();
+  replacements.ClearPassword();
+  base::DictionaryValue body;
+  body.SetString("type", "navigation");
+  body.SetString("blocked-url",
+                 blocked_url.ReplaceComponents(replacements).spec());
+  storage_partition_->GetNetworkContext()->QueueReport(
+      "coep", *endpoint, context_url_, /*user_agent=*/base::nullopt,
+      std::move(body));
+}
+
 void CrossOriginEmbedderPolicyReporter::Clone(
     mojo::PendingReceiver<network::mojom::CrossOriginEmbedderPolicyReporter>
         receiver) {
diff --git a/content/browser/net/cross_origin_embedder_policy_reporter.h b/content/browser/net/cross_origin_embedder_policy_reporter.h
index 7317460..4aa4f06 100644
--- a/content/browser/net/cross_origin_embedder_policy_reporter.h
+++ b/content/browser/net/cross_origin_embedder_policy_reporter.h
@@ -48,6 +48,10 @@
       mojo::PendingReceiver<network::mojom::CrossOriginEmbedderPolicyReporter>
           receiver) override;
 
+  // https://mikewest.github.io/corpp/#abstract-opdef-queue-coep-navigation-violation
+  // Queue a violation report for COEP mismatch for nested frame navigation.
+  void QueueNavigationReport(const GURL& blocked_url, bool report_only);
+
  private:
   // See the class comment.
   StoragePartition* const storage_partition_;
diff --git a/content/browser/net/cross_origin_embedder_policy_reporter_unittest.cc b/content/browser/net/cross_origin_embedder_policy_reporter_unittest.cc
index 8cd8411..93a2043f 100644
--- a/content/browser/net/cross_origin_embedder_policy_reporter_unittest.cc
+++ b/content/browser/net/cross_origin_embedder_policy_reporter_unittest.cc
@@ -10,12 +10,15 @@
 #include "base/values.h"
 #include "content/public/test/test_storage_partition.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/public/cpp/cross_origin_embedder_policy.h"
 #include "services/network/test/test_network_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
 namespace {
 
+using network::CrossOriginEmbedderPolicy;
+
 class TestNetworkContext : public network::TestNetworkContext {
  public:
   struct Report {
@@ -55,20 +58,27 @@
   StoragePartition* storage_partition() { return &storage_partition_; }
   const TestNetworkContext& network_context() const { return network_context_; }
 
-  base::Value CreateBody(base::StringPiece s) {
+  base::Value CreateBodyForCorp(base::StringPiece s) {
     base::Value dict(base::Value::Type::DICTIONARY);
     dict.SetKey("type", base::Value("corp"));
     dict.SetKey("blocked-url", base::Value(s));
     return dict;
   }
 
+  base::Value CreateBodyForNavigation(base::StringPiece s) {
+    base::Value dict(base::Value::Type::DICTIONARY);
+    dict.SetKey("type", base::Value("navigation"));
+    dict.SetKey("blocked-url", base::Value(s));
+    return dict;
+  }
+
  private:
   base::test::TaskEnvironment task_environment_;
   TestNetworkContext network_context_;
   TestStoragePartition storage_partition_;
 };
 
-TEST_F(CrossOriginEmbedderPolicyReporterTest, NullEndpoints) {
+TEST_F(CrossOriginEmbedderPolicyReporterTest, NullEndpointsForCorp) {
   const GURL kContextUrl("https://example.com/path");
   CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
                                              base::nullopt, base::nullopt);
@@ -81,7 +91,7 @@
   EXPECT_TRUE(network_context().reports().empty());
 }
 
-TEST_F(CrossOriginEmbedderPolicyReporterTest, Basic) {
+TEST_F(CrossOriginEmbedderPolicyReporterTest, BasicCorp) {
   const GURL kContextUrl("https://example.com/path");
   CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
                                              "e1", "e2");
@@ -99,14 +109,15 @@
   EXPECT_EQ(r1.type, "coep");
   EXPECT_EQ(r1.group, "e1");
   EXPECT_EQ(r1.url, kContextUrl);
-  EXPECT_EQ(r1.body, CreateBody("https://www1.example.com/x#foo?bar=baz"));
+  EXPECT_EQ(r1.body,
+            CreateBodyForCorp("https://www1.example.com/x#foo?bar=baz"));
   EXPECT_EQ(r2.type, "coep");
   EXPECT_EQ(r2.group, "e2");
   EXPECT_EQ(r2.url, kContextUrl);
-  EXPECT_EQ(r2.body, CreateBody("http://www2.example.com:41/y"));
+  EXPECT_EQ(r2.body, CreateBodyForCorp("http://www2.example.com:41/y"));
 }
 
-TEST_F(CrossOriginEmbedderPolicyReporterTest, UserAndPass) {
+TEST_F(CrossOriginEmbedderPolicyReporterTest, UserAndPassForCorp) {
   const GURL kContextUrl("https://example.com/path");
   CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
                                              "e1", "e2");
@@ -123,11 +134,11 @@
   EXPECT_EQ(r1.type, "coep");
   EXPECT_EQ(r1.group, "e1");
   EXPECT_EQ(r1.url, kContextUrl);
-  EXPECT_EQ(r1.body, CreateBody("https://www1.example.com/x"));
+  EXPECT_EQ(r1.body, CreateBodyForCorp("https://www1.example.com/x"));
   EXPECT_EQ(r2.type, "coep");
   EXPECT_EQ(r2.group, "e2");
   EXPECT_EQ(r2.url, kContextUrl);
-  EXPECT_EQ(r2.body, CreateBody("https://www2.example.com/y"));
+  EXPECT_EQ(r2.body, CreateBodyForCorp("https://www2.example.com/y"));
 }
 
 TEST_F(CrossOriginEmbedderPolicyReporterTest, Clone) {
@@ -152,11 +163,75 @@
   EXPECT_EQ(r1.type, "coep");
   EXPECT_EQ(r1.group, "e1");
   EXPECT_EQ(r1.url, kContextUrl);
-  EXPECT_EQ(r1.body, CreateBody("https://www1.example.com/x"));
+  EXPECT_EQ(r1.body, CreateBodyForCorp("https://www1.example.com/x"));
   EXPECT_EQ(r2.type, "coep");
   EXPECT_EQ(r2.group, "e2");
   EXPECT_EQ(r2.url, kContextUrl);
-  EXPECT_EQ(r2.body, CreateBody("https://www2.example.com/y"));
+  EXPECT_EQ(r2.body, CreateBodyForCorp("https://www2.example.com/y"));
+}
+
+TEST_F(CrossOriginEmbedderPolicyReporterTest, NullEndpointsForNavigation) {
+  const GURL kContextUrl("https://example.com/path");
+  CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
+                                             base::nullopt, base::nullopt);
+
+  reporter.QueueNavigationReport(GURL("https://www1.example.com/y"),
+                                 /*report_only=*/false);
+  reporter.QueueNavigationReport(GURL("https://www2.example.com/x"),
+                                 /*report_only=*/true);
+
+  EXPECT_TRUE(network_context().reports().empty());
+}
+
+TEST_F(CrossOriginEmbedderPolicyReporterTest, BasicNavigation) {
+  const GURL kContextUrl("https://example.com/path");
+  CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
+                                             "e1", "e2");
+  CrossOriginEmbedderPolicy child_coep;
+  child_coep.report_only_value =
+      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+
+  reporter.QueueNavigationReport(GURL("https://www1.example.com/x#foo?bar=baz"),
+                                 /*report_only=*/false);
+  reporter.QueueNavigationReport(GURL("http://www2.example.com:41/y"),
+                                 /*report_only=*/true);
+
+  ASSERT_EQ(2u, network_context().reports().size());
+  const Report& r1 = network_context().reports()[0];
+  const Report& r2 = network_context().reports()[1];
+
+  EXPECT_EQ(r1.type, "coep");
+  EXPECT_EQ(r1.group, "e1");
+  EXPECT_EQ(r1.url, kContextUrl);
+  EXPECT_EQ(r1.body,
+            CreateBodyForNavigation("https://www1.example.com/x#foo?bar=baz"));
+  EXPECT_EQ(r2.type, "coep");
+  EXPECT_EQ(r2.group, "e2");
+  EXPECT_EQ(r2.url, kContextUrl);
+  EXPECT_EQ(r2.body, CreateBodyForNavigation("http://www2.example.com:41/y"));
+}
+
+TEST_F(CrossOriginEmbedderPolicyReporterTest, UserAndPassForNavigation) {
+  const GURL kContextUrl("https://example.com/path");
+  CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
+                                             "e1", "e2");
+  reporter.QueueNavigationReport(GURL("https://u:p@www1.example.com/x"),
+                                 /*report_only=*/false);
+  reporter.QueueNavigationReport(GURL("https://u:p@www2.example.com/y"),
+                                 /*report_only=*/true);
+
+  ASSERT_EQ(2u, network_context().reports().size());
+  const Report& r1 = network_context().reports()[0];
+  const Report& r2 = network_context().reports()[1];
+
+  EXPECT_EQ(r1.type, "coep");
+  EXPECT_EQ(r1.group, "e1");
+  EXPECT_EQ(r1.url, kContextUrl);
+  EXPECT_EQ(r1.body, CreateBodyForNavigation("https://www1.example.com/x"));
+  EXPECT_EQ(r2.type, "coep");
+  EXPECT_EQ(r2.group, "e2");
+  EXPECT_EQ(r2.url, kContextUrl);
+  EXPECT_EQ(r2.body, CreateBodyForNavigation("https://www2.example.com/y"));
 }
 
 }  // namespace
diff --git a/content/browser/renderer_host/frame_connector_delegate.cc b/content/browser/renderer_host/frame_connector_delegate.cc
index 4a2eb5d..29fb3387 100644
--- a/content/browser/renderer_host/frame_connector_delegate.cc
+++ b/content/browser/renderer_host/frame_connector_delegate.cc
@@ -45,10 +45,10 @@
   render_widget_host->SetAutoResize(visual_properties.auto_resize_enabled,
                                     visual_properties.min_size_for_auto_resize,
                                     visual_properties.max_size_for_auto_resize);
-  render_widget_host->SetPageScaleState(
+  render_widget_host->SetVisualPropertiesFromParentFrame(
       visual_properties.page_scale_factor,
-      visual_properties.is_pinch_gesture_active);
-  render_widget_host->SetCompositorViewport(
+      visual_properties.is_pinch_gesture_active,
+      visual_properties.visible_viewport_size,
       visual_properties.compositor_viewport);
 
   render_widget_host->SynchronizeVisualProperties();
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index c725561..ceb94cf 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -19,7 +19,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/command_line.h"
 #include "base/containers/adapters.h"
 #include "base/debug/alias.h"
@@ -293,8 +293,8 @@
 #include "services/service_manager/zygote/common/zygote_handle.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
-#include "content/common/coverage_utils.h"
+#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
+#include "content/common/profiling_utils.h"
 #endif
 
 namespace content {
@@ -3580,8 +3580,8 @@
   child_process_->SetIPCLoggingEnabled(IPC::Logging::GetInstance()->Enabled());
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
-  child_process_->SetCoverageFile(OpenCoverageFile());
+#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
+  child_process_->SetProfilingFile(OpenProfilingFile());
 #endif
 }
 
diff --git a/content/browser/renderer_host/render_view_host_factory.cc b/content/browser/renderer_host/render_view_host_factory.cc
index b92cc3c4..f7e65761 100644
--- a/content/browser/renderer_host/render_view_host_factory.cc
+++ b/content/browser/renderer_host/render_view_host_factory.cc
@@ -25,27 +25,10 @@
     SiteInstance* instance,
     RenderViewHostDelegate* delegate,
     RenderWidgetHostDelegate* widget_delegate,
-    int32_t routing_id,
     int32_t main_frame_routing_id,
-    int32_t widget_routing_id,
     bool swapped_out) {
-  // RenderViewHost creation can be either browser-driven (by the user opening a
-  // new tab) or renderer-driven (by script calling window.open, etc).
-  //
-  // In the browser-driven case, the routing ID of the view is lazily assigned:
-  // this is signified by passing MSG_ROUTING_NONE for |routing_id|.
-  if (routing_id == MSG_ROUTING_NONE) {
-    DCHECK_EQ(widget_routing_id, MSG_ROUTING_NONE);
-    routing_id = instance->GetProcess()->GetNextRoutingID();
-    widget_routing_id = instance->GetProcess()->GetNextRoutingID();
-  } else {
-    // Otherwise, in the renderer-driven case, the routing ID of the view is
-    // already set. This is due to the fact that a sync render->browser IPC is
-    // involved. In order to quickly reply to the sync IPC, the routing IDs are
-    // assigned as early as possible. The IO thread immediately sends a reply to
-    // the sync IPC, while deferring the creation of the actual Host objects to
-    // the UI thread.
-  }
+  int32_t routing_id = instance->GetProcess()->GetNextRoutingID();
+  int32_t widget_routing_id = instance->GetProcess()->GetNextRoutingID();
   if (factory_) {
     return factory_->CreateRenderViewHost(instance, delegate, widget_delegate,
                                           routing_id, main_frame_routing_id,
diff --git a/content/browser/renderer_host/render_view_host_factory.h b/content/browser/renderer_host/render_view_host_factory.h
index 6098562c..862f96e 100644
--- a/content/browser/renderer_host/render_view_host_factory.h
+++ b/content/browser/renderer_host/render_view_host_factory.h
@@ -27,9 +27,7 @@
   static RenderViewHost* Create(SiteInstance* instance,
                                 RenderViewHostDelegate* delegate,
                                 RenderWidgetHostDelegate* widget_delegate,
-                                int32_t routing_id,
                                 int32_t main_frame_routing_id,
-                                int32_t widget_routing_id,
                                 bool swapped_out);
 
   // Returns true if there is currently a globally-registered factory.
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index 1118824..62bd672 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -146,10 +146,6 @@
   return ukm::kInvalidSourceId;
 }
 
-gfx::Size RenderWidgetHostDelegate::GetAutoResizeSize() {
-  return gfx::Size();
-}
-
 WebContents* RenderWidgetHostDelegate::GetAsWebContents() {
   return nullptr;
 }
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index 3250e910..234baae 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -313,20 +313,9 @@
   // not a WebContents, returns nullptr.
   virtual WebContents* GetAsWebContents();
 
-  // Gets the size set by a top-level frame with auto-resize enabled.
-  virtual gfx::Size GetAutoResizeSize();
-
-  // Reset the auto-size value, to indicate that auto-size is no longer active.
-  virtual void ResetAutoResizeSize() {}
-
   // Returns true if there is context menu shown on page.
   virtual bool IsShowingContextMenuOnPage() const;
 
-  // Notifies all renderers in a page about changes to the size of the visible
-  // viewport.
-  virtual void NotifyVisibleViewportSizeChanged(
-      const gfx::Size& visible_viewport_size) {}
-
   // Returns the focused frame across all delegates, or nullptr if none.
   virtual RenderFrameHostImpl* GetFocusedFrameFromFocusedDelegate();
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 51d09ae..40b9e63 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -779,21 +779,86 @@
   visual_properties.min_size_for_auto_resize = min_size_for_auto_resize_;
   visual_properties.max_size_for_auto_resize = max_size_for_auto_resize_;
 
-  visual_properties.page_scale_factor = page_scale_factor_;
-  visual_properties.is_pinch_gesture_active = is_pinch_gesture_active_;
-
   visual_properties.new_size = view_->GetRequestedRendererSize();
+
+  // This widget is for a frame that is the main frame of the outermost frame
+  // tree. That makes it the top-most frame. OR this is a non-frame widget.
+  const bool is_top_most_widget = !view_->IsRenderWidgetHostViewChildFrame();
+  // This widget is for a frame, but not the main frame of its frame tree.
+  const bool is_child_frame_widget =
+      view_->IsRenderWidgetHostViewChildFrame() && !owner_delegate_;
+
+  // These properties come from the main frame RenderWidget and flow down the
+  // tree of RenderWidgets. Some properties are global across all nested
+  // WebContents/frame trees. Some properties are global only within their
+  // WebContents/frame tree.
+  //
+  // Each child frame RenderWidgetHost that inherits values gets them from their
+  // parent RenderWidget in the renderer process. It then passes them along to
+  // its own RenderWidget, and the process repeats down the tree.
+  //
+  // The plumbing goes:
+  // 1. Browser:    parent RenderWidgetHost
+  // 2. IPC           -> WidgetMsg_UpdateVisualProperties
+  // 3. Renderer A: parent RenderWidget
+  //                  (sometimes blink involved)
+  // 4. Renderer A: child  RenderFrameProxy
+  // 5. IPC           -> FrameHostMsg_SynchronizeVisualProperties
+  // 6. Browser:    child  CrossProcessFrameConnector
+  // 7. Browser:    parent RenderWidgetHost (We're here if |is_child_frame|.)
+  // 8. IPC           -> WidgetMsg_UpdateVisualProperties
+  // 9. Renderer B: child  RenderWidget
+
+  // This property comes from the top-level main frame.
+  if (is_top_most_widget) {
+    visual_properties.compositor_viewport_pixel_rect =
+        gfx::Rect(view_->GetCompositorViewportPixelSize());
+  } else {
+    visual_properties.compositor_viewport_pixel_rect =
+        properties_from_parent_local_root_.compositor_viewport;
+    if (!IsUseZoomForDSFEnabled()) {
+      // If UseZoomForDSF is not used, the coordinates were not scaled by DSF
+      // when coming from the renderer.
+      visual_properties.compositor_viewport_pixel_rect =
+          gfx::ScaleToEnclosingRect(
+              visual_properties.compositor_viewport_pixel_rect,
+              visual_properties.screen_info.device_scale_factor);
+    }
+  }
+
+  // These properties come from the top-level main frame's renderer. The
+  // top-level main frame in the browser doesn't specify a value.
+  if (!is_top_most_widget) {
+    visual_properties.page_scale_factor =
+        properties_from_parent_local_root_.page_scale_factor;
+    visual_properties.is_pinch_gesture_active =
+        properties_from_parent_local_root_.is_pinch_gesture_active;
+  }
+
+  // The |visible_viewport_size| is affected by auto-resize which is magical and
+  // tricky.
+  //
+  // For the top-level main frame, auto resize ends up asynchronously resizing
+  // the widget's RenderWidgetHostView and the size will show up there, so
+  // nothing needs to be written in here.
+  //
+  // For nested main frames, auto resize happens in the renderer so we need to
+  // store the size on this class and use that. When auto-resize is not enabled
+  // we use the size of the nested main frame's RenderWidgetHostView.
+  //
+  // For child frames, we always use the value provided from the parent.
+  //
+  // For non-frame widgets, there is no auto-resize and we behave like the top-
+  // level main frame.
+  gfx::Size viewport;
+  if (is_child_frame_widget)
+    viewport = properties_from_parent_local_root_.visible_viewport_size;
+  else
+    viewport = view_->GetVisibleViewportSize();
+  visual_properties.visible_viewport_size = viewport;
+
   visual_properties.capture_sequence_number = view_->GetCaptureSequenceNumber();
-  // For OOPIFs, use the compositor viewport received from the FrameConnector.
-  visual_properties.compositor_viewport_pixel_rect =
-      view_->IsRenderWidgetHostViewChildFrame()
-          ? gfx::ScaleToEnclosingRect(
-                compositor_viewport_,
-                IsUseZoomForDSFEnabled()
-                    ? 1.f
-                    : visual_properties.screen_info.device_scale_factor)
-          : gfx::Rect(view_->GetCompositorViewportPixelSize());
-  visual_properties.visible_viewport_size = view_->GetVisibleViewportSize();
+
   // TODO(ccameron): GetLocalSurfaceId is not synchronized with the device
   // scale factor of the surface. Fix this.
   viz::LocalSurfaceIdAllocation local_surface_id_allocation =
@@ -869,22 +934,8 @@
   visual_properties->scroll_focused_node_into_view =
       scroll_focused_node_into_view;
 
-  bool visible_viewport_size_changed =
-      !old_visual_properties_ ||
-      old_visual_properties_->visible_viewport_size !=
-          visual_properties->visible_viewport_size;
-
   Send(new WidgetMsg_UpdateVisualProperties(routing_id_, *visual_properties));
 
-  // TODO(danakj): All visual properties should go through
-  // WidgetMsg_UpdateVisualProperties in order to be synchronized by the
-  // LocalSurfaceIdAllocation which synchronizes in the display compositor for
-  // a global atomic screen update across all frame widgets. This should move.
-  if (delegate() && visible_viewport_size_changed) {
-    delegate()->NotifyVisibleViewportSizeChanged(
-        visual_properties->visible_viewport_size);
-  }
-
   bool width_changed =
       !old_visual_properties_ || old_visual_properties_->new_size.width() !=
                                      visual_properties->new_size.width();
@@ -1984,6 +2035,19 @@
   return view_ ? view_->IsMouseLocked() : false;
 }
 
+void RenderWidgetHostImpl::SetVisualPropertiesFromParentFrame(
+    float page_scale_factor,
+    bool is_pinch_gesture_active,
+    const gfx::Size& visible_viewport_size,
+    const gfx::Rect& compositor_viewport) {
+  properties_from_parent_local_root_.page_scale_factor = page_scale_factor;
+  properties_from_parent_local_root_.is_pinch_gesture_active =
+      is_pinch_gesture_active;
+  properties_from_parent_local_root_.visible_viewport_size =
+      visible_viewport_size;
+  properties_from_parent_local_root_.compositor_viewport = compositor_viewport;
+}
+
 void RenderWidgetHostImpl::SetAutoResize(bool enable,
                                          const gfx::Size& min_size,
                                          const gfx::Size& max_size) {
@@ -1992,17 +2056,6 @@
   max_size_for_auto_resize_ = max_size;
 }
 
-void RenderWidgetHostImpl::SetPageScaleState(float page_scale_factor,
-                                             bool is_pinch_gesture_active) {
-  page_scale_factor_ = page_scale_factor;
-  is_pinch_gesture_active_ = is_pinch_gesture_active;
-}
-
-void RenderWidgetHostImpl::SetCompositorViewport(
-    const gfx::Rect& compositor_viewport) {
-  compositor_viewport_ = compositor_viewport;
-}
-
 void RenderWidgetHostImpl::Destroy(bool also_delete) {
   DCHECK(!destroyed_);
   destroyed_ = true;
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 5c9ddfc..b6f95fd 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -581,8 +581,20 @@
     renderer_initialized_ = renderer_initialized;
   }
 
+  // Store values received in a child frame RenderWidgetHost from a parent
+  // RenderWidget, in order to pass them to the renderer and continue their
+  // propagation down the RenderWidget tree.
+  void SetVisualPropertiesFromParentFrame(
+      float page_scale_factor,
+      bool is_pinch_gesture_active,
+      const gfx::Size& visible_viewport_size,
+      const gfx::Rect& compositor_viewport);
+
   // Indicates if the render widget host should track the render widget's size
   // as opposed to visa versa.
+  // In main frame RenderWidgetHosts this controls the value for the frame tree.
+  // In child frame RenderWidgetHosts this value comes from the parent
+  // RenderWidget and should be propagated down the RenderWidgetTree.
   void SetAutoResize(bool enable,
                      const gfx::Size& min_size,
                      const gfx::Size& max_size);
@@ -748,12 +760,6 @@
   // Add/ClearPendingUserActivation() for details.
   bool RemovePendingUserActivationIfAvailable();
 
-  // Allows the main frame's page scale state to be tracked.
-  void SetPageScaleState(float page_scale_factor, bool is_pinch_gesture_active);
-
-  // Tracks the compositor viewport requested for an OOPIF subframe.
-  void SetCompositorViewport(const gfx::Rect& compositor_viewport);
-
  protected:
   // ---------------------------------------------------------------------------
   // The following method is overridden by RenderViewHost to send upwards to
@@ -1037,20 +1043,30 @@
   // True if the render widget host should track the render widget's size as
   // opposed to visa versa.
   bool auto_resize_enabled_ = false;
-
   // The minimum size for the render widget if auto-resize is enabled.
   gfx::Size min_size_for_auto_resize_;
-
   // The maximum size for the render widget if auto-resize is enabled.
   gfx::Size max_size_for_auto_resize_;
 
-  // The page-scale factor of the main-frame.
-  float page_scale_factor_ = 1.f;
+  // These properties are propagated down the RenderWidget tree from the main
+  // frame to a child frame RenderWidgetHost. They are not used on a top-level
+  // RenderWidgetHost. The child frame RenderWidgetHost stores these values to
+  // pass them to the renderer, instead of computing them for itself. It
+  // collects them and passes them though WidgetMsg_UpdateVisualProperties so
+  // that the renderer receives updates in an atomic fashion along with a
+  // synchronization token for the compositor in a LocalSurfaceIdAllocation.
+  struct MainFramePropagationProperties {
+    // The page-scale factor of the main-frame.
+    float page_scale_factor = 1.f;
 
-  // True when the renderer is currently undergoing a pinch-zoom gesture.
-  bool is_pinch_gesture_active_ = false;
+    // True when the renderer is currently undergoing a pinch-zoom gesture.
+    bool is_pinch_gesture_active = false;
 
-  gfx::Rect compositor_viewport_;
+    // The size of the main frame's widget in DIP.
+    gfx::Size visible_viewport_size;
+
+    gfx::Rect compositor_viewport;
+  } properties_from_parent_local_root_;
 
   bool waiting_for_screen_rects_ack_ = false;
   gfx::Rect last_view_screen_rect_;
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
index 1dcef0b..6fc2ae55 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -531,10 +531,6 @@
 void RenderWidgetHostViewBase::DisableAutoResize(const gfx::Size& new_size) {
   if (!new_size.IsEmpty())
     SetSize(new_size);
-  // This clears the cached value in the WebContents, so that OOPIFs will
-  // stop using it.
-  if (host()->delegate())
-    host()->delegate()->ResetAutoResizeSize();
   host()->SetAutoResize(false, gfx::Size(), gfx::Size());
   host()->SynchronizeVisualProperties();
 }
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 3e1df86c..709115ea 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -265,38 +265,15 @@
 }
 
 gfx::Size RenderWidgetHostViewChildFrame::GetVisibleViewportSize() {
-  // For subframes, the visual viewport corresponds to the main frame size, so
-  // this bubbles up to the parent until it hits the main frame's
-  // RenderWidgetHostView.
-  //
-  // Currently this excludes webview guests, since they expect the visual
-  // viewport to return the guest's size rather than the page's; one reason why
-  // is that Blink ends up using the visual viewport to calculate things like
-  // window.innerWidth/innerHeight for main frames, and a guest is considered
-  // to be a main frame.  This should be cleaned up eventually.
-  bool is_guest = BrowserPluginGuest::IsGuest(RenderViewHostImpl::From(host()));
-  if (frame_connector_ && !is_guest) {
-    // An auto-resize set by the top-level frame overrides what would be
-    // reported by embedding RenderWidgetHostViews.
-    if (host()->delegate() &&
-        !host()->delegate()->GetAutoResizeSize().IsEmpty())
-      return host()->delegate()->GetAutoResizeSize();
+  // For subframes, the visual viewport corresponds to the main frame size so
+  // this method would not even be called, the main frame's value should be
+  // used instead. However a nested WebContents will have a ChildFrame view used
+  // for the main frame.
+  DCHECK(host()->owner_delegate());
 
-    RenderWidgetHostView* parent_view =
-        frame_connector_->GetParentRenderWidgetHostView();
-    // The parent_view can be null in unit tests when using a TestWebContents.
-    if (parent_view)
-      return parent_view->GetVisibleViewportSize();
-  }
-
-  gfx::Rect bounds = GetViewBounds();
-
-  // It doesn't make sense to set insets on an OOP iframe. The only time this
-  // should happen is when the virtual keyboard comes up on a <webview>.
-  if (is_guest)
-    bounds.Inset(insets_);
-
-  return bounds.size();
+  gfx::Rect requested_rect(GetRequestedRendererSize());
+  requested_rect.Inset(insets_);
+  return requested_rect.size();
 }
 
 void RenderWidgetHostViewChildFrame::SetInsets(const gfx::Insets& insets) {
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
index 2444928..50f70db 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
@@ -5,35 +5,47 @@
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
+#include "build/build_config.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/portal/portal.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/common/view_messages.h"
+#include "content/common/widget_messages.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "content/test/content_browser_test_utils_internal.h"
+#include "content/test/portal/portal_created_observer.h"
 #include "content/test/test_content_browser_client.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/common/features.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace content {
 
-class RenderWidgetHostViewChildFrameTest : public ContentBrowserTest {
+class RenderWidgetHostViewChildFrameBrowserTest : public ContentBrowserTest {
  public:
-  RenderWidgetHostViewChildFrameTest() {}
+  RenderWidgetHostViewChildFrameBrowserTest() = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     IsolateAllSitesForTesting(command_line);
+
+    scoped_feature_list_.InitAndEnableFeature(blink::features::kPortals);
   }
 
   void SetUpOnMainThread() override {
@@ -42,13 +54,6 @@
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 
-  void CheckScreenWidth(RenderFrameHost* render_frame_host) {
-    int width =
-        ExecuteScriptAndGetValue(render_frame_host, "window.screen.width")
-            .GetInt();
-    EXPECT_EQ(expected_screen_width_, width);
-  }
-
   // Tests that the FrameSinkId of each child frame has been updated by the
   // RenderFrameProxy.
   void CheckFrameSinkId(RenderFrameHost* render_frame_host) {
@@ -76,6 +81,38 @@
               actual_frame_sink_id_.sink_id());
   }
 
+  Portal* CreatePortalToUrl(WebContentsImpl* host_contents,
+                            GURL portal_url,
+                            int number_of_navigations = 1) {
+    EXPECT_GE(number_of_navigations, 1);
+    RenderFrameHostImpl* main_frame = host_contents->GetMainFrame();
+
+    // Create portal and wait for navigation.
+    PortalCreatedObserver portal_created_observer(main_frame);
+    TestNavigationObserver navigation_observer(nullptr, number_of_navigations);
+    navigation_observer.set_wait_event(
+        TestNavigationObserver::WaitEvent::kNavigationFinished);
+    navigation_observer.StartWatchingNewWebContents();
+    EXPECT_TRUE(ExecJs(
+        main_frame, JsReplace("{"
+                              "  let portal = document.createElement('portal');"
+                              "  portal.src = $1;"
+                              "  portal.setAttribute('id', 'portal');"
+                              "  document.body.appendChild(portal);"
+                              "}",
+                              portal_url)));
+    Portal* portal = portal_created_observer.WaitUntilPortalCreated();
+    navigation_observer.StopWatchingNewWebContents();
+
+    WebContentsImpl* portal_contents = portal->GetPortalContents();
+    EXPECT_TRUE(portal_contents);
+
+    navigation_observer.WaitForNavigationFinished();
+    EXPECT_TRUE(WaitForLoadStop(portal_contents));
+
+    return portal;
+  }
+
   void GiveItSomeTime() {
     base::RunLoop run_loop;
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
@@ -87,17 +124,15 @@
     expected_frame_sink_id_ = frame_sink_id;
   }
 
-  void set_expected_screen_width(int width) { expected_screen_width_ = width; }
-
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   viz::FrameSinkId expected_frame_sink_id_;
-  int expected_screen_width_;
 
-  DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewChildFrameTest);
+  DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewChildFrameBrowserTest);
 };
 
 // Tests that the screen is properly reflected for RWHVChildFrame.
-IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameTest, Screen) {
+IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameBrowserTest, Screen) {
   GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
@@ -114,51 +149,305 @@
       ExecuteScriptAndGetValue(shell()->web_contents()->GetMainFrame(),
                                "window.screen.width")
           .GetInt();
-  set_expected_screen_width(main_frame_screen_width);
   EXPECT_NE(main_frame_screen_width, 0);
 
+  auto check_screen_width = [&](RenderFrameHost* frame_host) {
+    int width =
+        ExecuteScriptAndGetValue(frame_host, "window.screen.width").GetInt();
+    EXPECT_EQ(width, main_frame_screen_width);
+  };
   shell()->web_contents()->ForEachFrame(
-      base::BindRepeating(&RenderWidgetHostViewChildFrameTest::CheckScreenWidth,
-                          base::Unretained(this)));
+      base::BindLambdaForTesting(check_screen_width));
 }
 
-// Test that auto-resize sizes in the top frame are propagated to OOPIF
-// RenderWidgetHostViews. See https://crbug.com/726743.
-IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameTest,
-                       ChildFrameAutoResizeUpdate) {
-  EXPECT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL(
-                   "a.com", "/cross_site_iframe_factory.html?a(b)")));
+class OutgoingVisualPropertiesIPCWatcher {
+ public:
+  OutgoingVisualPropertiesIPCWatcher(
+      RenderProcessHostImpl* rph,
+      base::RepeatingCallback<void(const VisualProperties&)> callback)
+      : rph_(rph), callback_(std::move(callback)) {
+    rph_->SetIpcSendWatcherForTesting(
+        base::BindRepeating(&OutgoingVisualPropertiesIPCWatcher::OnMessage,
+                            base::Unretained(this)));
+  }
+  ~OutgoingVisualPropertiesIPCWatcher() {
+    rph_->SetIpcSendWatcherForTesting(base::NullCallback());
+  }
 
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetFrameTree()
-                            ->root();
-  root->current_frame_host()
-      ->GetRenderWidgetHost()
-      ->GetView()
-      ->EnableAutoResize(gfx::Size(0, 0), gfx::Size(100, 100));
+ private:
+  void OnMessage(const IPC::Message& message) {
+    IPC_BEGIN_MESSAGE_MAP(OutgoingVisualPropertiesIPCWatcher, message)
+      IPC_MESSAGE_HANDLER(WidgetMsg_UpdateVisualProperties, ProcessMessage)
+    IPC_END_MESSAGE_MAP()
+  }
 
-  RenderWidgetHostView* rwhv =
-      root->child_at(0)->current_frame_host()->GetRenderWidgetHost()->GetView();
+  void ProcessMessage(const VisualProperties& props) { callback_.Run(props); }
 
-  // Fake an auto-resize update from the parent renderer.
-  viz::LocalSurfaceId local_surface_id(10, 10,
-                                       base::UnguessableToken::Create());
-  cc::RenderFrameMetadata metadata;
-  metadata.viewport_size_in_pixels = gfx::Size(75, 75);
-  metadata.local_surface_id_allocation =
-      viz::LocalSurfaceIdAllocation(local_surface_id, base::TimeTicks::Now());
-  RenderFrameMetadataProvider::Observer* metadata_receiver =
-      root->current_frame_host()->GetRenderWidgetHost();
-  metadata_receiver->OnLocalSurfaceIdChanged(metadata);
+  RenderProcessHostImpl* rph_;
+  base::RepeatingCallback<void(const VisualProperties&)> callback_;
+};
 
-  // The child frame's RenderWidgetHostView should now use the auto-resize value
-  // for its visible viewport.
-  EXPECT_EQ(gfx::Size(75, 75), rwhv->GetVisibleViewportSize());
+// Auto-resize is only implemented for Ash and GuestViews. So we need to inject
+// an implementation that actually resizes the top level widget.
+class AutoResizeWebContentsDelegate : public WebContentsDelegate {
+  void ResizeDueToAutoResize(WebContents* web_contents,
+                             const gfx::Size& new_size) override {
+    RenderWidgetHostView* view =
+        web_contents->GetTopLevelRenderWidgetHostView();
+    view->SetSize(new_size);
+  }
+};
+
+// Test that the |visible_viewport_size| from the top frame is propagated to all
+// local roots (aka RenderWidgets):
+// a) Initially upon adding the child frame (we create this scenario by
+// navigating a child on b.com to c.com, where we already have a RenderProcess
+// active for c.com).
+// b) When resizing the top level widget.
+// c) When auto-resize is enabled for the top level main frame and the renderer
+// resizes the top level widget.
+// d) When auto-resize is enabled for the nested main frame and the renderer
+// resizes the nested widget.
+// See https://crbug.com/726743 and https://crbug.com/1050635.
+IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameBrowserTest,
+                       VisualPropertiesPropagation_VisibleViewportSize) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  auto* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents());
+  FrameTreeNode* root = web_contents->GetFrameTree()->root();
+  RenderWidgetHostView* root_view =
+      root->current_frame_host()->GetRenderWidgetHost()->GetView();
+
+  Portal* portal = CreatePortalToUrl(web_contents, main_url);
+  WebContentsImpl* nested_contents = portal->GetPortalContents();
+  FrameTreeNode* nested_root = nested_contents->GetFrameTree()->root();
+  RenderWidgetHostView* nested_root_view =
+      nested_root->current_frame_host()->GetRenderWidgetHost()->GetView();
+
+  // Watch processes for a.com and c.com, as we will throw away b.com when we
+  // navigate it below.
+  auto* root_rph = static_cast<RenderProcessHostImpl*>(
+      root->current_frame_host()->GetProcess());
+  auto* child_rph = static_cast<RenderProcessHostImpl*>(
+      root->child_at(1)->current_frame_host()->GetProcess());
+  ASSERT_NE(root_rph, child_rph);
+
+  auto* nested_root_rph = static_cast<RenderProcessHostImpl*>(
+      nested_root->current_frame_host()->GetProcess());
+  auto* nested_child_rph = static_cast<RenderProcessHostImpl*>(
+      nested_root->child_at(1)->current_frame_host()->GetProcess());
+  ASSERT_NE(nested_root_rph, nested_child_rph);
+
+  const gfx::Size initial_size = root_view->GetVisibleViewportSize();
+  const gfx::Size nested_initial_size =
+      nested_root_view->GetVisibleViewportSize();
+  ASSERT_NE(initial_size, nested_initial_size);
+
+  // We should see the top level widget's size in the visible_viewport_size
+  // in both local roots. When a child local root is added in the parent
+  // renderer, the value is propagated down through the browser to the child
+  // renderer's RenderWidget.
+  //
+  // This property is not directly visible in the renderer, so we can only
+  // check that the value is sent to the appropriate RenderWidget.
+  {
+    base::RunLoop loop;
+
+    gfx::Size child_visible_viewport_size;
+    OutgoingVisualPropertiesIPCWatcher child_watcher(
+        child_rph,
+        base::BindLambdaForTesting([&](const VisualProperties& props) {
+          child_visible_viewport_size = props.visible_viewport_size;
+
+          if (child_visible_viewport_size == initial_size)
+            loop.Quit();
+        }));
+
+    GURL cross_site_url(
+        embedded_test_server()->GetURL("c.com", "/title2.html"));
+    NavigateFrameToURL(root->child_at(0), cross_site_url);
+
+    // Wait to see the size sent to the child RenderWidget.
+    loop.Run();
+
+    // The child widget was also informed of the same size.
+    EXPECT_EQ(initial_size, child_visible_viewport_size);
+  }
+
+  // Same check as abvoe but for a nested WebContents.
+  {
+    base::RunLoop loop;
+
+    gfx::Size child_visible_viewport_size;
+    OutgoingVisualPropertiesIPCWatcher child_watcher(
+        nested_child_rph,
+        base::BindLambdaForTesting([&](const VisualProperties& props) {
+          child_visible_viewport_size = props.visible_viewport_size;
+
+          if (child_visible_viewport_size == nested_initial_size)
+            loop.Quit();
+        }));
+
+    GURL cross_site_url(
+        embedded_test_server()->GetURL("c.com", "/title2.html"));
+    NavigateFrameToURL(nested_root->child_at(0), cross_site_url);
+
+    // Wait to see the size sent to the child RenderWidget.
+    loop.Run();
+
+    // The child widget was also informed of the same size.
+    EXPECT_EQ(nested_initial_size, child_visible_viewport_size);
+  }
+
+// This part of the test does not work well on Android, for a few reasons:
+// 1. RenderWidgetHostViewAndroid can not be resized, the Java objects need to
+// be resized somehow through ui::ViewAndroid.
+// 2. AutoResize on Android does not size to the min/max bounds specified, it
+// ends up ignoring them and sizing to the screen (I think).
+// Luckily this test is verifying interactions and behaviour of
+// RenderWidgetHostImpl - RenderWidget - RenderFrameProxy -
+// CrossProcessFrameConnector, and this isn't Android-specific code.
+#if !defined(OS_ANDROID)
+
+  // Resize the top level widget to cause its |visible_viewport_size| to be
+  // changed. The change should propagate down to the child RenderWidget.
+  {
+    base::RunLoop loop;
+
+    const gfx::Size resize_to(initial_size.width() - 10,
+                              initial_size.height() - 10);
+
+    gfx::Size root_visible_viewport_size;
+    gfx::Size child_visible_viewport_size;
+    OutgoingVisualPropertiesIPCWatcher root_watcher(
+        root_rph,
+        base::BindLambdaForTesting([&](const VisualProperties& props) {
+          root_visible_viewport_size = props.visible_viewport_size;
+        }));
+    OutgoingVisualPropertiesIPCWatcher child_watcher(
+        child_rph,
+        base::BindLambdaForTesting([&](const VisualProperties& props) {
+          child_visible_viewport_size = props.visible_viewport_size;
+
+          if (child_visible_viewport_size == resize_to)
+            loop.Quit();
+        }));
+
+    root_view->SetSize(resize_to);
+
+    // Wait to see both RenderWidgets receive the message.
+    loop.Run();
+
+    // The top level widget was resized.
+    EXPECT_EQ(resize_to, root_visible_viewport_size);
+    // The child widget was also informed of the same size.
+    EXPECT_EQ(resize_to, child_visible_viewport_size);
+  }
+
+  // Same check as above but resizing the nested WebContents' main frame
+  // instead.
+  // Resize the top level widget to cause its |visible_viewport_size| to be
+  // changed. The change should propagate down to the child RenderWidget.
+  {
+    base::RunLoop loop;
+
+    const gfx::Size resize_to(nested_initial_size.width() - 10,
+                              nested_initial_size.height() - 10);
+
+    gfx::Size root_visible_viewport_size;
+    gfx::Size child_visible_viewport_size;
+    OutgoingVisualPropertiesIPCWatcher root_watcher(
+        nested_root_rph,
+        base::BindLambdaForTesting([&](const VisualProperties& props) {
+          root_visible_viewport_size = props.visible_viewport_size;
+        }));
+    OutgoingVisualPropertiesIPCWatcher child_watcher(
+        nested_child_rph,
+        base::BindLambdaForTesting([&](const VisualProperties& props) {
+          child_visible_viewport_size = props.visible_viewport_size;
+
+          if (child_visible_viewport_size == resize_to)
+            loop.Quit();
+        }));
+
+    EXPECT_TRUE(ExecJs(
+        root->current_frame_host(),
+        JsReplace("document.getElementById('portal').style.width = '$1px';"
+                  "document.getElementById('portal').style.height = '$2px';",
+                  resize_to.width(), resize_to.height())));
+
+    // Wait to see both RenderWidgets receive the message.
+    loop.Run();
+
+    // The top level widget was resized.
+    EXPECT_EQ(resize_to, root_visible_viewport_size);
+    // The child widget was also informed of the same size.
+    EXPECT_EQ(resize_to, child_visible_viewport_size);
+  }
+
+  // Informs the top-level frame it can auto-resize. It will shrink down to its
+  // minimum window size based on the empty content we've loaded, which is
+  // 100x100.
+  //
+  // When the renderer resizes, thanks to our AutoResizeWebContentsDelegate
+  // the top level widget will resize. That size will be reflected in the
+  // visible_viewport_size which is sent back through the top level RenderWidget
+  // to propagte down to child local roots.
+  //
+  // This property is not directly visible in the renderer, so we can only
+  // check that the value is sent to both RenderWidgets.
+  {
+    base::RunLoop loop;
+
+    const gfx::Size auto_resize_to(105, 100);
+
+    gfx::Size root_visible_viewport_size;
+    gfx::Size child_visible_viewport_size;
+    OutgoingVisualPropertiesIPCWatcher root_watcher(
+        root_rph,
+        base::BindLambdaForTesting([&](const VisualProperties& props) {
+          root_visible_viewport_size = props.visible_viewport_size;
+        }));
+    OutgoingVisualPropertiesIPCWatcher child_watcher(
+        child_rph,
+        base::BindLambdaForTesting([&](const VisualProperties& props) {
+          child_visible_viewport_size = props.visible_viewport_size;
+
+          if (child_visible_viewport_size == auto_resize_to)
+            loop.Quit();
+        }));
+
+    // Replace the WebContentsDelegate so that we can use the auto-resize
+    // changes to adjust the size of the top widget.
+    WebContentsDelegate* old_delegate = shell()->web_contents()->GetDelegate();
+    AutoResizeWebContentsDelegate resize_delegate;
+    shell()->web_contents()->SetDelegate(&resize_delegate);
+
+    root_view->EnableAutoResize(auto_resize_to, auto_resize_to);
+
+    // Wait for the renderer side to resize itself and the RenderWidget
+    // waterfall to pass the new |visible_viewport_size| down.
+    loop.Run();
+
+    // The top level widget was resized to match the auto-resized renderer.
+    EXPECT_EQ(auto_resize_to, root_visible_viewport_size);
+    // The child widget was also informed of the same size.
+    EXPECT_EQ(auto_resize_to, child_visible_viewport_size);
+
+    shell()->web_contents()->SetDelegate(old_delegate);
+  }
+
+  // TODO(danakj): We'd like to run the same check as above but tell the main
+  // frame of the nested WebContents that it can auto-resize. However this seems
+  // to get through to the main frame's RenderWidget and propagate correctly but
+  // no size change occurs in the renderer.
+#endif
 }
 
 // Validate that OOPIFs receive presentation feedbacks.
-IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameTest,
+IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameBrowserTest,
                        PresentationFeedback) {
   base::HistogramTester histogram_tester;
   GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 09119efd..eee6eef6 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -76,6 +76,16 @@
       std::move(host_receiver));
 }
 
+void NotifyUpdateCrossOriginEmbedderPolicyOnUI(
+    int worker_process_id,
+    int worker_route_id,
+    network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  ServiceWorkerDevToolsManager::GetInstance()->UpdateCrossOriginEmbedderPolicy(
+      worker_process_id, worker_route_id,
+      std::move(cross_origin_embedder_policy));
+}
+
 void NotifyWorkerDestroyedOnUI(int worker_process_id, int worker_route_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ServiceWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
@@ -106,7 +116,6 @@
     ,
     std::unique_ptr<
         blink::PendingURLLoaderFactoryBundle> /* factory_bundle_for_renderer */,
-    mojo::PendingRemote<blink::mojom::CacheStorage>,
     const base::Optional<base::TimeDelta>& thread_hop_time,
     const base::Optional<base::Time>& ui_post_time)>;
 
@@ -124,7 +133,8 @@
     int embedded_worker_id,
     base::WeakPtr<ServiceWorkerProcessManager> process_manager,
     bool can_use_existing_process,
-    const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
+    const base::Optional<network::CrossOriginEmbedderPolicy>&
+        cross_origin_embedder_policy,
     blink::mojom::EmbeddedWorkerStartParamsPtr params,
     mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient> receiver,
     ServiceWorkerContextCore* context,
@@ -156,7 +166,6 @@
                                   std::move(devtools_proxy),
                                   std::move(factory_bundle_for_new_scripts),
                                   std::move(factory_bundle_for_renderer),
-                                  mojo::NullRemote() /* cache_storage */,
                                   thread_hop_time, ui_post_time));
     return;
   }
@@ -176,8 +185,7 @@
         base::BindOnce(std::move(callback), status, std::move(params),
                        std::move(process_info), std::move(devtools_proxy),
                        std::move(factory_bundle_for_new_scripts),
-                       std::move(factory_bundle_for_renderer),
-                       mojo::NullRemote() /* cache_storage */, thread_hop_time,
+                       std::move(factory_bundle_for_renderer), thread_hop_time,
                        ui_post_time));
     return;
   }
@@ -188,18 +196,6 @@
   // rph->IsInitializedAndNotDead().
   CHECK(rph);
 
-  // Create cache storage now as an optimization, so the service worker can use
-  // the Cache Storage API immediately on startup.
-  mojo::PendingRemote<blink::mojom::CacheStorage> cache_storage;
-  if (base::FeatureList::IsEnabled(
-          blink::features::kEagerCacheStorageSetupForServiceWorkers)) {
-    // TODO(https://crbug.com/1031542): Add support enforcing CORP in
-    // cache.match() for ServiceWorker.
-    rph->BindCacheStorage(network::CrossOriginEmbedderPolicy(),
-                          url::Origin::Create(params->script_url),
-                          cache_storage.InitWithNewPipeAndPassReceiver());
-  }
-
   // Bind |receiver|, which is attached to |EmbeddedWorkerInstance::client_|, to
   // the process. If the process dies, |client_|'s connection error callback
   // will be called on the core thread.
@@ -211,8 +207,8 @@
   ServiceWorkerDevToolsManager::GetInstance()->WorkerCreated(
       process_id, routing_id, context, weak_context,
       params->service_worker_version_id, params->script_url, params->scope,
-      params->is_installed, &params->devtools_worker_token,
-      &params->wait_for_debugger);
+      params->is_installed, cross_origin_embedder_policy,
+      &params->devtools_worker_token, &params->wait_for_debugger);
   params->service_worker_route_id = routing_id;
   // Create DevToolsProxy here to ensure that the WorkerCreated() call is
   // balanced by DevToolsProxy's destructor calling WorkerDestroyed().
@@ -267,8 +263,8 @@
       base::BindOnce(std::move(callback), status, std::move(params),
                      std::move(process_info), std::move(devtools_proxy),
                      std::move(factory_bundle_for_new_scripts),
-                     std::move(factory_bundle_for_renderer),
-                     std::move(cache_storage), thread_hop_time, ui_post_time));
+                     std::move(factory_bundle_for_renderer), thread_hop_time,
+                     ui_post_time));
 }
 
 bool HasSentStartWorker(EmbeddedWorkerInstance::StartingPhase phase) {
@@ -301,6 +297,20 @@
     rph->OnForegroundServiceWorkerRemoved();
 }
 
+void BindCacheStorageOnUIThread(
+    int process_id,
+    url::Origin origin,
+    network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,
+    mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto* rph = RenderProcessHost::FromID(process_id);
+  if (!rph)
+    return;
+
+  rph->BindCacheStorage(cross_origin_embedder_policy, origin,
+                        std::move(receiver));
+}
+
 }  // namespace
 
 // Created on UI thread and moved to core thread. Proxies notifications to
@@ -340,6 +350,21 @@
     }
   }
 
+  void NotifyUpdateCrossOriginEmbedderPolicy(
+      network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
+    DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+    if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
+      NotifyUpdateCrossOriginEmbedderPolicyOnUI(
+          process_id_, agent_route_id_,
+          std::move(cross_origin_embedder_policy));
+    } else {
+      ui_task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(NotifyUpdateCrossOriginEmbedderPolicyOnUI,
+                                    process_id_, agent_route_id_,
+                                    std::move(cross_origin_embedder_policy)));
+    }
+  }
+
   void NotifyWorkerVersionInstalled() {
     DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
     if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
@@ -528,10 +553,10 @@
     return skip_recording_startup_time_;
   }
 
-  void Start(
-      blink::mojom::EmbeddedWorkerStartParamsPtr params,
-      const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
-      StatusCallback sent_start_callback) {
+  void Start(blink::mojom::EmbeddedWorkerStartParamsPtr params,
+             const base::Optional<network::CrossOriginEmbedderPolicy>&
+                 cross_origin_embedder_policy,
+             StatusCallback sent_start_callback) {
     DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
     DCHECK(instance_->context_);
     TRACE_EVENT_WITH_FLOW0(
@@ -559,7 +584,7 @@
     if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
       SetupOnUIThread(
           instance_->embedded_worker_id(), process_manager,
-          can_use_existing_process, std::move(cross_origin_embedder_policy),
+          can_use_existing_process, cross_origin_embedder_policy,
           std::move(params), std::move(receiver_), context.get(), context,
           base::nullopt,
           base::BindOnce(&StartTask::OnSetupCompleted,
@@ -570,7 +595,7 @@
           base::BindOnce(
               &SetupOnUIThread, instance_->embedded_worker_id(),
               process_manager, can_use_existing_process,
-              std::move(cross_origin_embedder_policy), std::move(params),
+              cross_origin_embedder_policy, std::move(params),
               std::move(receiver_), context.get(), context,
               base::make_optional<base::Time>(base::Time::Now()),
               base::BindOnce(&StartTask::OnSetupCompleted,
@@ -592,7 +617,6 @@
           factory_bundle_for_new_scripts,
       std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
           factory_bundle_for_renderer,
-      mojo::PendingRemote<blink::mojom::CacheStorage> cache_storage,
       const base::Optional<base::TimeDelta>& thread_hop_time,
       const base::Optional<base::Time>& ui_post_time) {
     DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
@@ -665,7 +689,13 @@
               std::move(factory_bundle_for_new_scripts));
     }
 
-    params->provider_info->cache_storage = std::move(cache_storage);
+    // Create cache storage now as an optimization, so the service worker can
+    // use the Cache Storage API immediately on startup.
+    if (base::FeatureList::IsEnabled(
+            blink::features::kEagerCacheStorageSetupForServiceWorkers)) {
+      instance_->BindCacheStorage(params->provider_info->cache_storage
+                                      .InitWithNewPipeAndPassReceiver());
+    }
 
     instance_->SendStartWorker(std::move(params));
     std::move(sent_start_callback_).Run(blink::ServiceWorkerStatusCode::kOk);
@@ -901,10 +931,18 @@
   if (!inflight_start_task_)
     return;
 
+  // COEP could be nullopt if it's in unittests.
+  // TODO(shimazu): Set COEP in the failing unit tests.
+  if (devtools_proxy_ && owner_version_->cross_origin_embedder_policy()) {
+    devtools_proxy_->NotifyUpdateCrossOriginEmbedderPolicy(
+        owner_version_->cross_origin_embedder_policy().value());
+  }
+
   // Renderer side has started to launch the worker thread.
   starting_phase_ = SCRIPT_LOADED;
   owner_version_->OnMainScriptLoaded();
-  // |this| may be destroyed by the callback.
+
+  BindCacheStorageInternal();
 }
 
 void EmbeddedWorkerInstance::OnWorkerVersionInstalled() {
@@ -1026,6 +1064,13 @@
   }
 }
 
+void EmbeddedWorkerInstance::BindCacheStorage(
+    mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
+  DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  pending_cache_storage_receivers_.push_back(std::move(receiver));
+  BindCacheStorageInternal();
+}
+
 base::WeakPtr<EmbeddedWorkerInstance> EmbeddedWorkerInstance::AsWeakPtr() {
   return weak_factory_.GetWeakPtr();
 }
@@ -1040,7 +1085,8 @@
     RenderProcessHost* rph,
     int routing_id,
     const url::Origin& origin,
-    const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
+    const base::Optional<network::CrossOriginEmbedderPolicy>&
+        cross_origin_embedder_policy,
     ContentBrowserClient::URLLoaderFactoryType factory_type) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto factory_bundle =
@@ -1070,8 +1116,14 @@
 
   factory_params->client_security_state =
       network::mojom::ClientSecurityState::New();
+
+  // Without PlzServiceWorker, the COEP header might no be known initially for
+  // new ServiceWorker. The default COEP header is used instead here. Later, the
+  // subresource loader factories will be updated with the correct COEP header.
+  // See: https://chromium-review.googlesource.com/c/chromium/src/+/2029403
   factory_params->client_security_state->cross_origin_embedder_policy =
-      std::move(cross_origin_embedder_policy);
+      cross_origin_embedder_policy ? cross_origin_embedder_policy.value()
+                                   : network::CrossOriginEmbedderPolicy();
 
   rph->CreateURLLoaderFactory(std::move(default_factory_receiver),
                               std::move(factory_params));
@@ -1291,4 +1343,23 @@
   return script_loader_factory_remote;
 }
 
+void EmbeddedWorkerInstance::BindCacheStorageInternal() {
+  DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  // Without PlzServiceWorker, the COEP header might not be known initially.
+  // The in-flight CacheStorage requests are kept until the main script has
+  // loaded the headers and the COEP one is known.
+  if (!owner_version_->cross_origin_embedder_policy())
+    return;
+
+  for (auto& receiver : pending_cache_storage_receivers_) {
+    RunOrPostTaskOnThread(
+        FROM_HERE, BrowserThread::UI,
+        base::BindOnce(content::BindCacheStorageOnUIThread, process_id(),
+                       owner_version_->script_origin(),
+                       owner_version_->cross_origin_embedder_policy().value(),
+                       std::move(receiver)));
+  }
+  pending_cache_storage_receivers_.clear();
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index cc4a164..7079655c 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -220,6 +220,9 @@
       std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle,
       std::unique_ptr<blink::PendingURLLoaderFactoryBundle> subresource_bundle);
 
+  void BindCacheStorage(
+      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver);
+
   base::WeakPtr<EmbeddedWorkerInstance> AsWeakPtr();
 
   // The below can only be called on the UI thread. The returned factory may be
@@ -229,7 +232,8 @@
       RenderProcessHost* rph,
       int routing_id,
       const url::Origin& origin,
-      const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
+      const base::Optional<network::CrossOriginEmbedderPolicy>&
+          cross_origin_embedder_policy,
       ContentBrowserClient::URLLoaderFactoryType factory_type);
 
  private:
@@ -304,6 +308,8 @@
   MakeScriptLoaderFactoryRemote(
       std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle);
 
+  void BindCacheStorageInternal();
+
   base::WeakPtr<ServiceWorkerContextCore> context_;
   ServiceWorkerVersion* owner_version_;
 
@@ -362,6 +368,11 @@
   mojo::Remote<blink::mojom::SubresourceLoaderUpdater>
       subresource_loader_updater_;
 
+  // Hold in-flight CacheStorage requests. They will be bound when the
+  // ServiceWorker COEP header will be known.
+  std::vector<mojo::PendingReceiver<blink::mojom::CacheStorage>>
+      pending_cache_storage_receivers_;
+
   base::WeakPtrFactory<EmbeddedWorkerInstance> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstance);
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 8a036eb..ea6396cb 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -2069,8 +2069,7 @@
   scoped_refptr<ServiceWorkerRegistration> registration =
       update_helper_->SetupInitialRegistration(kNewVersionOrigin);
   ASSERT_TRUE(registration.get());
-  EXPECT_EQ(CrossOriginEmbedderPolicyNone(),
-            registration->active_version()->cross_origin_embedder_policy());
+  EXPECT_FALSE(registration->active_version()->cross_origin_embedder_policy());
 
   registration->AddListener(update_helper_);
 
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 08a2349..68e512f5 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -22,6 +22,7 @@
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/origin_util.h"
 #include "mojo/public/cpp/bindings/message.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom.h"
@@ -110,6 +111,15 @@
                      std::move(receiver)));
 }
 
+void ServiceWorkerProviderHost::BindCacheStorage(
+    mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
+  DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  DCHECK(!base::FeatureList::IsEnabled(
+      blink::features::kEagerCacheStorageSetupForServiceWorkers));
+  running_hosted_version_->embedded_worker()->BindCacheStorage(
+      std::move(receiver));
+}
+
 base::WeakPtr<ServiceWorkerProviderHost>
 ServiceWorkerProviderHost::GetWeakPtr() {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 1307bb24..b65bd3c 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -70,6 +70,9 @@
 
   void CreateQuicTransportConnector(
       mojo::PendingReceiver<blink::mojom::QuicTransportConnector> receiver);
+  // Used only when EagerCacheStorageSetupForServiceWorkers is disabled.
+  void BindCacheStorage(
+      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver);
 
   content::ServiceWorkerContainerHost* container_host() {
     return container_host_.get();
diff --git a/content/browser/service_worker/service_worker_registry.cc b/content/browser/service_worker/service_worker_registry.cc
index 30316d84..bee7dce 100644
--- a/content/browser/service_worker/service_worker_registry.cc
+++ b/content/browser/service_worker/service_worker_registry.cc
@@ -311,7 +311,13 @@
   data->script_response_time = version->GetInfo().script_response_time;
   for (const blink::mojom::WebFeature feature : version->used_features())
     data->used_features.push_back(feature);
-  data->cross_origin_embedder_policy = version->cross_origin_embedder_policy();
+
+  // The ServiceWorkerVersion's COEP might be null if it is stored before
+  // loading the main script. This happens in many unittests.
+  if (version->cross_origin_embedder_policy()) {
+    data->cross_origin_embedder_policy =
+        version->cross_origin_embedder_policy().value();
+  }
 
   auto resources = std::make_unique<ResourceList>();
   version->script_cache_map()->GetResources(resources.get());
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index aaaa8e31..64c3b10 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -212,7 +212,8 @@
     int process_id,
     int routing_id,
     const url::Origin& origin,
-    network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,
+    base::Optional<network::CrossOriginEmbedderPolicy>
+        cross_origin_embedder_policy,
     CreateFactoryBundleForSubresourceOnUICallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto* rph = RenderProcessHost::FromID(process_id);
@@ -1688,6 +1689,14 @@
     container_host_by_uuid.second->CountFeature(feature);
 }
 
+void ServiceWorkerVersion::set_cross_origin_embedder_policy(
+    network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
+  // Once it is set, the CrossOriginEmbedderPolicy is immutable.
+  DCHECK(!cross_origin_embedder_policy_ ||
+         cross_origin_embedder_policy_ == cross_origin_embedder_policy);
+  cross_origin_embedder_policy_ = std::move(cross_origin_embedder_policy);
+}
+
 // static
 bool ServiceWorkerVersion::IsInstalled(ServiceWorkerVersion::Status status) {
   switch (status) {
@@ -2330,7 +2339,7 @@
     network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
   compared_script_info_map_ = std::move(compared_script_info_map);
   updated_script_url_ = updated_script_url;
-  cross_origin_embedder_policy_ = cross_origin_embedder_policy;
+  set_cross_origin_embedder_policy(cross_origin_embedder_policy);
 }
 
 const std::map<GURL, ServiceWorkerUpdateChecker::ComparedScriptInfo>&
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 3c725c3..288ab528 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -521,10 +521,9 @@
   }
 
   void set_cross_origin_embedder_policy(
-      network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
-    cross_origin_embedder_policy_ = cross_origin_embedder_policy;
-  }
-  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy() const {
+      network::CrossOriginEmbedderPolicy cross_origin_embedder_policy);
+  const base::Optional<network::CrossOriginEmbedderPolicy>&
+  cross_origin_embedder_policy() const {
     return cross_origin_embedder_policy_;
   }
 
@@ -901,9 +900,17 @@
   ServiceWorkerMetrics::Site site_for_uma_;
 
   // Cross-Origin-Embedder-Policy for the service worker script. This persists
-  // in the disk. kNone is set if this is a brand-new service worker whose main
-  // script is not loaded yet.
-  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy_;
+  // in the disk.
+  //
+  // On brand new service workers, the COEP value is not known initially. It
+  // will be set in PrepareForUpdate(), after the main script has been processed
+  // by the renderer process.
+  //
+  // PlzServiceWorker(https://crbug.com/996511):
+  // Once landed, there is no more need to use an base::Optional here. The COEP
+  // header is going to be known from the beginning and can be mark as 'const'.
+  base::Optional<network::CrossOriginEmbedderPolicy>
+      cross_origin_embedder_policy_;
 
   Status status_ = NEW;
   std::unique_ptr<EmbeddedWorkerInstance> embedded_worker_;
diff --git a/content/browser/service_worker/service_worker_version_browsertest.cc b/content/browser/service_worker/service_worker_version_browsertest.cc
index cee0621..02faf88 100644
--- a/content/browser/service_worker/service_worker_version_browsertest.cc
+++ b/content/browser/service_worker/service_worker_version_browsertest.cc
@@ -1655,8 +1655,7 @@
   RunOnCoreThread(base::BindOnce(
       &ServiceWorkerVersionBrowserTest::SetUpRegistrationOnCoreThread,
       base::Unretained(this), "/service_worker/generated"));
-  EXPECT_EQ(CrossOriginEmbedderPolicyNone(),
-            version_->cross_origin_embedder_policy());
+  EXPECT_FALSE(version_->cross_origin_embedder_policy());
 
   // Once it's started, the worker script is read from the network and the COEP
   // value is set to the version.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 2b5e6c8..056153a0 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1634,29 +1634,6 @@
     GetOuterWebContents()->NotifyNavigationStateChanged(changed_flags);
 }
 
-void WebContentsImpl::NotifyVisibleViewportSizeChanged(
-    const gfx::Size& visible_viewport_size) {
-  // This viewport size is in screen coordinates, but will be handed to blink
-  // which expects coordinates including the device scale factor when
-  // UseZoomForDSF is enabled.
-  // TODO(danakj): This scaling should be done in the renderer where emulation
-  // may override the device scale factor.
-  gfx::Size visible_viewport_size_for_blink = visible_viewport_size;
-  if (IsUseZoomForDSFEnabled()) {
-    ScreenInfo info;
-    GetMainFrame()->GetRenderWidgetHost()->GetScreenInfo(&info);
-
-    visible_viewport_size_for_blink =
-        gfx::ScaleToCeiledSize(visible_viewport_size, info.device_scale_factor);
-  }
-
-  // TODO(danakj): This should be part of VisualProperties and walk down the
-  // RenderWidget tree like other VisualProperties do, in order to set the
-  // value in each WebView holds a part of the local frame tree.
-  SendPageMessage(new PageMsg_UpdatePageVisualProperties(
-      MSG_ROUTING_NONE, visible_viewport_size_for_blink));
-}
-
 RenderFrameHostImpl* WebContentsImpl::GetFocusedFrameFromFocusedDelegate() {
   FrameTreeNode* focused_node =
       GetFocusedWebContents()->frame_tree_.GetFocusedFrame();
@@ -2070,14 +2047,6 @@
   if (!params.last_active_time.is_null())
     last_active_time_ = params.last_active_time;
 
-  // The routing ids must either all be set or all be unset.
-  DCHECK((params.routing_id == MSG_ROUTING_NONE &&
-          params.main_frame_routing_id == MSG_ROUTING_NONE &&
-          params.main_frame_widget_routing_id == MSG_ROUTING_NONE) ||
-         (params.routing_id != MSG_ROUTING_NONE &&
-          params.main_frame_routing_id != MSG_ROUTING_NONE &&
-          params.main_frame_widget_routing_id != MSG_ROUTING_NONE));
-
   scoped_refptr<SiteInstance> site_instance = params.site_instance;
   if (!site_instance)
     site_instance = SiteInstance::Create(params.browser_context);
@@ -2086,24 +2055,9 @@
         ->PreventAssociationWithSpareProcess();
   }
 
-  // A main RenderFrameHost always has a RenderWidgetHost, since it is always a
-  // local root by definition.
-  // TODO(avi): Once RenderViewHostImpl has-a RenderWidgetHostImpl, it will no
-  // longer be necessary to eagerly grab a routing ID for the view.
-  // https://crbug.com/545684
-  int32_t view_routing_id = params.routing_id;
-  int32_t main_frame_widget_routing_id = params.main_frame_widget_routing_id;
-  if (main_frame_widget_routing_id == MSG_ROUTING_NONE) {
-    view_routing_id = site_instance->GetProcess()->GetNextRoutingID();
-    main_frame_widget_routing_id =
-        site_instance->GetProcess()->GetNextRoutingID();
-  }
-
-  DCHECK_NE(view_routing_id, main_frame_widget_routing_id);
-
-  GetRenderManager()->Init(
-      site_instance.get(), view_routing_id, params.main_frame_routing_id,
-      main_frame_widget_routing_id, params.renderer_initiated_creation);
+  GetRenderManager()->Init(site_instance.get(),
+                           /*frame_routing_id=*/MSG_ROUTING_NONE,
+                           params.renderer_initiated_creation);
 
   // blink::FrameTree::setName always keeps |unique_name| empty in case of a
   // main frame - let's do the same thing here.
@@ -2938,22 +2892,6 @@
   // objects.
   create_params.renderer_initiated_creation = !is_new_browsing_instance;
 
-  // If |is_new_browsing_instance| is true, defer routing_id allocation
-  // to the WebContentsImpl::Create() call. This is required because with
-  // a new browsing instance, WebContentsImpl::Create() may elect a different
-  // SiteInstance from |site_instance| (which happens if |site_instance| is
-  // nullptr for example).
-  //
-  // TODO(ajwong): This routing id allocation should be pushed down further
-  // into WebContentsImpl::Create().
-  if (!is_new_browsing_instance) {
-    create_params.routing_id = opener->GetProcess()->GetNextRoutingID();
-    create_params.main_frame_routing_id =
-        opener->GetProcess()->GetNextRoutingID();
-    create_params.main_frame_widget_routing_id =
-        opener->GetProcess()->GetNextRoutingID();
-  }
-
   std::unique_ptr<WebContentsImpl> new_contents;
   if (!is_guest) {
     create_params.context = view_->GetNativeView();
@@ -2996,9 +2934,10 @@
     // TODO(ajwong): This should be keyed off the RenderFrame routing id or the
     // FrameTreeNode id instead of the routing id of the Widget for the main
     // frame.  https://crbug.com/545684
-    DCHECK_NE(MSG_ROUTING_NONE, create_params.main_frame_routing_id);
-    GlobalRoutingID id(render_process_id,
-                       create_params.main_frame_widget_routing_id);
+    int32_t main_frame_routing_id = new_contents_impl->GetMainFrame()
+                                        ->GetRenderWidgetHost()
+                                        ->GetRoutingID();
+    GlobalRoutingID id(render_process_id, main_frame_routing_id);
     pending_contents_[id] = std::move(new_contents);
     AddDestructionObserver(new_contents_impl);
   }
@@ -3568,32 +3507,10 @@
   if (render_widget_host != GetRenderViewHost()->GetWidget())
     return;
 
-  auto_resize_size_ = new_size;
-
-  // Out-of-process iframe visible viewport sizes usually come from the
-  // top-level RenderWidgetHostView, but when auto-resize is enabled on the
-  // top frame then that size is used instead.
-  for (FrameTreeNode* node : frame_tree_.Nodes()) {
-    if (node->current_frame_host()->is_local_root()) {
-      RenderWidgetHostImpl* host =
-          node->current_frame_host()->GetRenderWidgetHost();
-      if (host != render_widget_host)
-        host->SynchronizeVisualProperties();
-    }
-  }
-
   if (delegate_)
     delegate_->ResizeDueToAutoResize(this, new_size);
 }
 
-gfx::Size WebContentsImpl::GetAutoResizeSize() {
-  return auto_resize_size_;
-}
-
-void WebContentsImpl::ResetAutoResizeSize() {
-  auto_resize_size_ = gfx::Size();
-}
-
 WebContents* WebContentsImpl::OpenURL(const OpenURLParams& params) {
 #if DCHECK_IS_ON()
   DCHECK(params.Valid());
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 7ac8d4ba..c3731f17 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -826,10 +826,6 @@
                               bool width_changed) override;
   void ResizeDueToAutoResize(RenderWidgetHostImpl* render_widget_host,
                              const gfx::Size& new_size) override;
-  gfx::Size GetAutoResizeSize() override;
-  void ResetAutoResizeSize() override;
-  void NotifyVisibleViewportSizeChanged(
-      const gfx::Size& visible_viewport_size) override;
   RenderFrameHostImpl* GetFocusedFrameFromFocusedDelegate() override;
   void OnVerticalScrollDirectionChanged(
       viz::VerticalScrollDirection scroll_direction) override;
@@ -1806,10 +1802,6 @@
   // this overrides |preferred_size_|.
   gfx::Size preferred_size_for_capture_;
 
-  // Size set by a top-level frame with auto-resize enabled. This is needed by
-  // out-of-process iframes for their visible viewport size.
-  gfx::Size auto_resize_size_;
-
   // When device emulation is enabled, override the size of current and newly
   // created render views/widgets.
   gfx::Size device_emulation_size_;
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index fcc4e6ce..0b7b2be 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -394,6 +394,13 @@
                               std::move(receiver));
 }
 
+void DedicatedWorkerHost::BindCacheStorage(
+    mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  worker_process_host_->BindCacheStorage(cross_origin_embedder_policy_,
+                                         worker_origin_, std::move(receiver));
+}
+
 void DedicatedWorkerHost::CreateNestedDedicatedWorker(
     mojo::PendingReceiver<blink::mojom::DedicatedWorkerHostFactory> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index 03bfd609..0e947e1 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -80,10 +80,6 @@
 
   RenderProcessHost* GetProcessHost() { return worker_process_host_; }
   const url::Origin& GetWorkerOrigin() { return worker_origin_; }
-  const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy()
-      const {
-    return cross_origin_embedder_policy_;
-  }
 
   void CreateIdleManager(
       mojo::PendingReceiver<blink::mojom::IdleManager> receiver);
@@ -97,6 +93,8 @@
       mojo::PendingReceiver<blink::mojom::WebSocketConnector> receiver);
   void CreateQuicTransportConnector(
       mojo::PendingReceiver<blink::mojom::QuicTransportConnector> receiver);
+  void BindCacheStorage(
+      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver);
 
 #if !defined(OS_ANDROID)
   void BindSerialService(
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 8ae2dc6d..c0bc9d3 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -337,6 +337,17 @@
                               std::move(receiver));
 }
 
+void SharedWorkerHost::BindCacheStorage(
+    mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // TODO(https://crbug.com/1031542): Add support enforcing CORP in
+  // cache.match() for SharedWorker by providing the correct value here.
+  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy;
+  const url::Origin origin = url::Origin::Create(instance().url());
+  worker_process_host_->BindCacheStorage(cross_origin_embedder_policy, origin,
+                                         std::move(receiver));
+}
+
 void SharedWorkerHost::Destruct() {
   // Ask the service to destroy |this| which will terminate the worker.
   service_->DestroyHost(this);
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index bcbf0ce..3a7bddea 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -108,6 +108,8 @@
       mojo::PendingReceiver<blink::mojom::AppCacheBackend> receiver);
   void CreateQuicTransportConnector(
       mojo::PendingReceiver<blink::mojom::QuicTransportConnector> receiver);
+  void BindCacheStorage(
+      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver);
 
   // Causes this instance to be deleted, which will terminate the worker. May
   // be done based on a UI action.
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index 921dcd8..36574e6 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -10,7 +10,7 @@
 
 #include "base/base_switches.h"
 #include "base/bind.h"
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/alias.h"
@@ -84,7 +84,7 @@
 #include "base/mac/mach_port_rendezvous.h"
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
+#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
 #include <stdio.h>
 #if defined(OS_WIN)
 #include <io.h>
@@ -372,8 +372,8 @@
                                   weak_main_thread_, std::move(receiver)));
   }
 
-#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
-  void SetCoverageFile(base::File file) override {
+#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
+  void SetProfilingFile(base::File file) override {
     // TODO(crbug.com/985574) Remove Android check when possible.
 #if defined(OS_POSIX) && !defined(OS_ANDROID)
     // Take the file descriptor so that |file| does not close it.
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 35eb5cde..af7b2f3 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -411,10 +411,10 @@
     deps += [ "//third_party/fuchsia-sdk/sdk/pkg/fdio" ]
   }
 
-  if (use_clang_coverage_inside_sandbox) {
+  if (use_clang_profiling_inside_sandbox) {
     sources += [
-      "coverage_utils.cc",
-      "coverage_utils.h",
+      "profiling_utils.cc",
+      "profiling_utils.h",
     ]
   }
 }
@@ -476,8 +476,8 @@
   if (is_linux || is_chromeos) {
     enabled_features += [ "supports_thread_priorities" ]
   }
-  if (use_clang_coverage_inside_sandbox) {
-    enabled_features += [ "clang_coverage_inside_sandbox" ]
+  if (use_clang_profiling_inside_sandbox) {
+    enabled_features += [ "clang_profiling_inside_sandbox" ]
   }
 
   import_dirs = [ "//mojo/services" ]
diff --git a/content/common/child_process.mojom b/content/common/child_process.mojom
index b981e6a..d85e2810 100644
--- a/content/common/child_process.mojom
+++ b/content/common/child_process.mojom
@@ -91,7 +91,8 @@
   // depends on the process type and potentially on the Content embedder.
   BindReceiver(mojo_base.mojom.GenericPendingReceiver receiver);
 
-  // Sets the coverage file for the child process.
-  [EnableIf=clang_coverage_inside_sandbox]
-  SetCoverageFile(mojo_base.mojom.File file);
+  // Sets the profiling file for the child process.
+  // Used for the coverage builds.
+  [EnableIf=clang_profiling_inside_sandbox]
+  SetProfilingFile(mojo_base.mojom.File file);
 };
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc
index 5543ebf..06f21f0 100644
--- a/content/common/child_process_host_impl.cc
+++ b/content/common/child_process_host_impl.cc
@@ -7,7 +7,7 @@
 #include <limits>
 
 #include "base/atomic_sequence_num.h"
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/command_line.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
@@ -42,8 +42,8 @@
 #include "content/common/mac_helpers.h"
 #endif  // OS_LINUX
 
-#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
-#include "content/common/coverage_utils.h"
+#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
+#include "content/common/profiling_utils.h"
 #endif
 
 namespace {
@@ -207,8 +207,8 @@
   child_process_->SetIPCLoggingEnabled(enabled);
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
-  child_process_->SetCoverageFile(OpenCoverageFile());
+#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
+  child_process_->SetProfilingFile(OpenProfilingFile());
 #endif
 
   opening_channel_ = true;
diff --git a/content/common/common_param_traits_macros.h b/content/common/common_param_traits_macros.h
index 0d2d07e..0701e83 100644
--- a/content/common/common_param_traits_macros.h
+++ b/content/common/common_param_traits_macros.h
@@ -46,11 +46,11 @@
   IPC_STRUCT_TRAITS_MEMBER(min_size_for_auto_resize)
   IPC_STRUCT_TRAITS_MEMBER(max_size_for_auto_resize)
   IPC_STRUCT_TRAITS_MEMBER(new_size)
+  IPC_STRUCT_TRAITS_MEMBER(visible_viewport_size)
   IPC_STRUCT_TRAITS_MEMBER(compositor_viewport_pixel_rect)
   IPC_STRUCT_TRAITS_MEMBER(browser_controls_params)
   IPC_STRUCT_TRAITS_MEMBER(scroll_focused_node_into_view)
   IPC_STRUCT_TRAITS_MEMBER(local_surface_id_allocation)
-  IPC_STRUCT_TRAITS_MEMBER(visible_viewport_size)
   IPC_STRUCT_TRAITS_MEMBER(is_fullscreen_granted)
   IPC_STRUCT_TRAITS_MEMBER(display_mode)
   IPC_STRUCT_TRAITS_MEMBER(capture_sequence_number)
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 8b8cc90..4bdc050 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -212,15 +212,16 @@
 IPC_STRUCT_TRAITS_BEGIN(content::FrameVisualProperties)
   IPC_STRUCT_TRAITS_MEMBER(screen_info)
   IPC_STRUCT_TRAITS_MEMBER(auto_resize_enabled)
+  IPC_STRUCT_TRAITS_MEMBER(visible_viewport_size)
   IPC_STRUCT_TRAITS_MEMBER(min_size_for_auto_resize)
   IPC_STRUCT_TRAITS_MEMBER(max_size_for_auto_resize)
-  IPC_STRUCT_TRAITS_MEMBER(screen_space_rect)
-  IPC_STRUCT_TRAITS_MEMBER(local_frame_size)
-  IPC_STRUCT_TRAITS_MEMBER(compositor_viewport)
   IPC_STRUCT_TRAITS_MEMBER(capture_sequence_number)
   IPC_STRUCT_TRAITS_MEMBER(zoom_level)
   IPC_STRUCT_TRAITS_MEMBER(page_scale_factor)
   IPC_STRUCT_TRAITS_MEMBER(is_pinch_gesture_active)
+  IPC_STRUCT_TRAITS_MEMBER(screen_space_rect)
+  IPC_STRUCT_TRAITS_MEMBER(local_frame_size)
+  IPC_STRUCT_TRAITS_MEMBER(compositor_viewport)
   IPC_STRUCT_TRAITS_MEMBER(local_surface_id_allocation)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/content/common/frame_visual_properties.h b/content/common/frame_visual_properties.h
index 9659e81..24964d7 100644
--- a/content/common/frame_visual_properties.h
+++ b/content/common/frame_visual_properties.h
@@ -22,35 +22,27 @@
 
   FrameVisualProperties& operator=(const FrameVisualProperties& other);
 
-  // Information about the screen (dpi, depth, etc..).
+  // These fields are values from VisualProperties, see comments there for
+  // descriptions. They exist here to propagate from each RenderWidget to its
+  // child RenderWidgets. Here they are flowing from RenderWidget in a parent
+  // renderer process up to the RenderWidgetHost for a child RenderWidget in
+  // another renderer process. That RenderWidgetHost would then be responsible
+  // for passing it along to the child RenderWidget.
   ScreenInfo screen_info;
-
-  // Whether or not blink should be in auto-resize mode.
   bool auto_resize_enabled = false;
-
-  // The minimum size for Blink if auto-resize is enabled.
+  bool is_pinch_gesture_active = false;
+  uint32_t capture_sequence_number = 0u;
+  double zoom_level = 0;
+  float page_scale_factor = 1.f;
+  gfx::Size visible_viewport_size;
   gfx::Size min_size_for_auto_resize;
-
-  // The maximum size for Blink if auto-resize is enabled.
   gfx::Size max_size_for_auto_resize;
 
-  gfx::Rect screen_space_rect;
-
-  gfx::Size local_frame_size;
-
   // The size of the compositor viewport, to match the sub-frame's surface.
   gfx::Rect compositor_viewport;
 
-  uint32_t capture_sequence_number = 0u;
-
-  // This represents the page zoom level for a WebContents.
-  // (0 is the default value which results in 1.0 zoom factor.)
-  double zoom_level = 0;
-
-  // Tracks the page-scale factor and whether the frame is currently in an
-  // active pinch-zoom gesture.
-  float page_scale_factor = 1.f;
-  bool is_pinch_gesture_active = false;
+  gfx::Rect screen_space_rect;
+  gfx::Size local_frame_size;
 
   // The time at which the viz::LocalSurfaceId used to submit this was
   // allocated.
diff --git a/content/common/page_messages.h b/content/common/page_messages.h
index 24fa71dce..bab308c3 100644
--- a/content/common/page_messages.h
+++ b/content/common/page_messages.h
@@ -33,11 +33,6 @@
 
 IPC_MESSAGE_ROUTED1(PageMsg_AudioStateChanged, bool /* is_audio_playing */)
 
-// Sent to renderers with remote main frames when page-related visual properties
-// change.
-IPC_MESSAGE_ROUTED1(PageMsg_UpdatePageVisualProperties,
-                    gfx::Size /* VisualViewport size */)
-
 // Sent to all renderers, instructing them to freeze or unfreeze all frames that
 // belongs to this page.
 IPC_MESSAGE_ROUTED1(PageMsg_SetPageFrozen, bool /* frozen */)
diff --git a/content/common/coverage_utils.cc b/content/common/profiling_utils.cc
similarity index 91%
rename from content/common/coverage_utils.cc
rename to content/common/profiling_utils.cc
index 609f503..48772eb 100644
--- a/content/common/coverage_utils.cc
+++ b/content/common/profiling_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/coverage_utils.h"
+#include "content/common/profiling_utils.h"
 
 #include <memory>
 
@@ -23,7 +23,7 @@
 
 namespace content {
 
-base::File OpenCoverageFile() {
+base::File OpenProfilingFile() {
   base::ScopedAllowBlockingForTesting allows_blocking;
   std::unique_ptr<base::Environment> env(base::Environment::Create());
   std::string prof_template;
@@ -41,6 +41,8 @@
 
   // sajjadm@ and liaoyuke@ experimentally determined that a size 4 pool works
   // well for the coverage builder.
+  // TODO(https://crbug.com/1059335): Check if this is an appropriate value for
+  // the PGO builds.
   int pool_index = base::RandInt(0, 3);
   std::string filename = base::StrCat(
       {"child_pool-", base::NumberToString(pool_index), ".profraw"});
diff --git a/content/common/coverage_utils.h b/content/common/profiling_utils.h
similarity index 62%
rename from content/common/coverage_utils.h
rename to content/common/profiling_utils.h
index 24d8270..f9e6e17 100644
--- a/content/common/coverage_utils.h
+++ b/content/common/profiling_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_COMMON_COVERAGE_UTILS_H_
-#define CONTENT_COMMON_COVERAGE_UTILS_H_
+#ifndef CONTENT_COMMON_PROFILING_UTILS_H_
+#define CONTENT_COMMON_PROFILING_UTILS_H_
 
 #include <string>
 
@@ -11,8 +11,8 @@
 
 namespace content {
 
-base::File OpenCoverageFile();
+base::File OpenProfilingFile();
 
 }  // namespace content
 
-#endif  // CONTENT_COMMON_COVERAGE_UTILS_H_
+#endif  // CONTENT_COMMON_PROFILING_UTILS_H_
diff --git a/content/common/visual_properties.h b/content/common/visual_properties.h
index 599c6b0f..82ffc7e 100644
--- a/content/common/visual_properties.h
+++ b/content/common/visual_properties.h
@@ -72,9 +72,23 @@
   // The size for the widget in DIPs.
   gfx::Size new_size;
 
-  // The rect of compositor's viewport in pixels. Note that this may differ in
-  // size from a ScaleToCeiledSize of |new_size| due to Android's keyboard or
-  // due to rounding particulars.
+  // The size of the area of the widget that is visible to the user, in DIPs.
+  // The visible area may be empty if the visible area does not intersect with
+  // the widget, for example in the case of a child frame that is entirely
+  // scrolled out of the main frame's viewport. It may also be smaller than the
+  // widget's size in |new_size| due to the UI hiding part of the widget, such
+  // as with an on-screen keyboard.
+  gfx::Size visible_viewport_size;
+
+  // The rect of compositor's viewport in pixels. Note that for top level
+  // widgets this is roughly the DSF scaled new_size put into a rect. For child
+  // frame widgets it is a pixel-perfect bounds of the visible region of the
+  // widget. The size would be similar to visible_viewport_size, but in physical
+  // pixels and computed via very different means.
+  // TODO(danakj): It would be super nice to remove one of |new_size|,
+  // |visible_viewport_size| and |compositor_viewport_pixel_rect|. Their values
+  // overlap in purpose, creating a very confusing situation about which to use
+  // for what, and how they should relate or not.
   gfx::Rect compositor_viewport_pixel_rect;
 
   // Browser controls params such as top and bottom controls heights, whether
@@ -88,11 +102,6 @@
   // The local surface ID to use (if valid) and its allocation time.
   base::Optional<viz::LocalSurfaceIdAllocation> local_surface_id_allocation;
 
-  // The size of the visible viewport, which may be smaller than the view if the
-  // view is partially occluded (e.g. by a virtual keyboard).  The size is in
-  // DPI-adjusted pixels.
-  gfx::Size visible_viewport_size;
-
   // Indicates whether tab-initiated fullscreen was granted.
   bool is_fullscreen_granted = false;
 
diff --git a/content/public/browser/web_contents.cc b/content/public/browser/web_contents.cc
index ce7342f6..d1d8ff8 100644
--- a/content/public/browser/web_contents.cc
+++ b/content/public/browser/web_contents.cc
@@ -22,9 +22,6 @@
       opener_render_frame_id(MSG_ROUTING_NONE),
       opener_suppressed(false),
       created_with_opener(false),
-      routing_id(MSG_ROUTING_NONE),
-      main_frame_routing_id(MSG_ROUTING_NONE),
-      main_frame_widget_routing_id(MSG_ROUTING_NONE),
       initially_hidden(false),
       guest_delegate(nullptr),
       context(nullptr),
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index f808d01..96d22f68 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -142,14 +142,6 @@
     // (e.g., for blocked popups).
     bool created_with_opener;
 
-    // The routing ids of the RenderView, main RenderFrame, and the widget for
-    // the main RenderFrame. Either all routing IDs must be provided or all must
-    // be MSG_ROUTING_NONE to have WebContents make the assignment. If provided,
-    // these routing IDs are associated with |site_instance->GetProcess()|.
-    int32_t routing_id;
-    int32_t main_frame_routing_id;
-    int32_t main_frame_widget_routing_id;
-
     // The name of the top-level frame of the new window. It is non-empty
     // when creating a named window (e.g. <a target="foo"> or
     // window.open('', 'bar')).
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 914cf95..7823050 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2973,6 +2973,12 @@
   render_view_->SetDeviceScaleFactor(use_zoom_for_dsf, device_scale_factor);
 }
 
+void RenderFrameImpl::SetVisibleViewportSizeForChildLocalRootOnRenderView(
+    const gfx::Size& visible_viewport_size) {
+  DCHECK(frame_->Parent());  // Only called for child local roots.
+  render_view_->SetVisibleViewportSizeForChildLocalRoot(visible_viewport_size);
+}
+
 void RenderFrameImpl::AddMessageToConsole(
     blink::mojom::ConsoleMessageLevel level,
     const std::string& message) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 29c48c5..d34fa34 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -806,14 +806,23 @@
   media::MediaPermission* GetMediaPermission();
 
   // Proxies the call to set the zoom level over to the RenderViewImpl and
-  // returns its result.
+  // returns its result. Meant to be called by the |render_widget_| in order to
+  // get access to the RenderViewImpl.
   bool SetZoomLevelOnRenderView(double zoom_level);
   // Proxies the call to set the prefer compositing flag over to the
-  // RenderViewImpl.
+  // RenderViewImpl. Meant to be called by the |render_widget_| in order to get
+  // access to the RenderViewImpl.
   void SetPreferCompositingToLCDTextEnabledOnRenderView(bool prefer);
   // Proxies the call to set the device scale factor over to the RenderViewImpl.
+  // Meant to be called by the |render_widget_| in order to get access to the
+  // RenderViewImpl.
   void SetDeviceScaleFactorOnRenderView(bool use_zoom_for_dsf,
                                         float device_scale_factor);
+  // Proxies the call to set the visible viewport size over to the
+  // RenderViewImpl. Meant to be called by the |render_widget_| in order to get
+  // access to the RenderViewImpl.
+  void SetVisibleViewportSizeForChildLocalRootOnRenderView(
+      const gfx::Size& visible_viewport_size);
 
   // Sends the current frame's navigation state to the browser.
   void SendUpdateState();
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index c0cc2ba..cad3bfce 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -215,31 +215,44 @@
 // Verify a subframe RenderWidget properly processes its viewport being
 // resized.
 TEST_F(RenderFrameImplTest, FrameResize) {
+  // Make an update where the widget's size and the visible_viewport_size
+  // are not the same.
   VisualProperties visual_properties;
-  gfx::Size size(200, 200);
-  visual_properties.screen_info = ScreenInfo();
-  visual_properties.new_size = size;
-  visual_properties.compositor_viewport_pixel_rect = gfx::Rect(size);
-  visual_properties.visible_viewport_size = size;
-  visual_properties.is_fullscreen_granted = false;
+  gfx::Size widget_size(400, 200);
+  gfx::Size visible_size(350, 170);
+  visual_properties.new_size = widget_size;
+  visual_properties.compositor_viewport_pixel_rect = gfx::Rect(widget_size);
+  visual_properties.visible_viewport_size = visible_size;
+
+  RenderWidget* main_frame_widget =
+      GetMainRenderFrame()->GetLocalRootRenderWidget();
 
   // The main frame's widget will receive the resize message before the
   // subframe's widget, and it will set the size for the WebView.
-  RenderWidget* main_frame_widget =
-      GetMainRenderFrame()->GetLocalRootRenderWidget();
-  WidgetMsg_UpdateVisualProperties resize_message(
-      main_frame_widget->routing_id(), visual_properties);
+  {
+    WidgetMsg_UpdateVisualProperties resize_message(
+        main_frame_widget->routing_id(), visual_properties);
+    main_frame_widget->OnMessageReceived(resize_message);
+  }
+  // The main frame widget's size is the "widget size", not the visible viewport
+  // size, which is given to blink separately.
+  EXPECT_EQ(gfx::Size(view_->GetWebView()->MainFrameWidget()->Size()),
+            widget_size);
+  EXPECT_EQ(gfx::SizeF(view_->GetWebView()->VisualViewportSize()),
+            gfx::SizeF(visible_size));
+  // The main frame doesn't change other local roots directly.
+  EXPECT_NE(gfx::Size(frame_widget()->GetWebWidget()->Size()), visible_size);
 
-  main_frame_widget->OnMessageReceived(resize_message);
-  EXPECT_EQ(view_->GetWebView()->MainFrameWidget()->Size(),
-            blink::WebSize(size));
-  EXPECT_NE(frame_widget()->GetWebWidget()->Size(), blink::WebSize(size));
+  // A subframe in the same process does not modify the WebView.
+  {
+    WidgetMsg_UpdateVisualProperties resize_message_subframe(
+        frame_widget()->routing_id(), visual_properties);
+    frame_widget()->OnMessageReceived(resize_message_subframe);
+  }
+  EXPECT_EQ(gfx::Size(frame_widget()->GetWebWidget()->Size()), widget_size);
 
-  // The subframe sets the size only for itself.
-  WidgetMsg_UpdateVisualProperties resize_message_subframe(
-      frame_widget()->routing_id(), visual_properties);
-  frame_widget()->OnMessageReceived(resize_message_subframe);
-  EXPECT_EQ(frame_widget()->GetWebWidget()->Size(), blink::WebSize(size));
+  // A subframe in another process would use the |visible_viewport_size| as its
+  // size.
 }
 
 // Verify a subframe RenderWidget properly processes a WasShown message.
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index cb2f95f..676217a 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -304,6 +304,14 @@
   SynchronizeVisualProperties();
 }
 
+void RenderFrameProxy::OnVisibleViewportSizeChanged(
+    const gfx::Size& visible_viewport_size) {
+  DCHECK(ancestor_render_widget_);
+
+  pending_visual_properties_.visible_viewport_size = visible_viewport_size;
+  SynchronizeVisualProperties();
+}
+
 void RenderFrameProxy::UpdateCaptureSequenceNumber(
     uint32_t capture_sequence_number) {
   DCHECK(ancestor_render_widget_);
@@ -509,6 +517,8 @@
           pending_visual_properties_.page_scale_factor ||
       sent_visual_properties_->is_pinch_gesture_active !=
           pending_visual_properties_.is_pinch_gesture_active ||
+      sent_visual_properties_->visible_viewport_size !=
+          pending_visual_properties_.visible_viewport_size ||
       sent_visual_properties_->compositor_viewport !=
           pending_visual_properties_.compositor_viewport ||
       capture_sequence_number_changed;
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 708d49d..d202bf5 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -127,21 +127,18 @@
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override;
 
-  // Out-of-process child frames receive a signal from RenderWidget when the
-  // ScreenInfo has changed.
+  // Propagate VisualProperties updates from a local root RenderWidget to the
+  // child RenderWidget represented by this proxy, which is hosted in another
+  // renderer frame tree.
+  // TODO(danakj): These should all be grouped into a single method, then we
+  // would only get one update per UpdateVisualProperties IPC received in the
+  // RenderWidget, and we would only need to send one update to the browser as
+  // a result.
   void OnScreenInfoChanged(const ScreenInfo& screen_info);
-
-  // Out-of-process child frames receive a signal from RenderWidget when the
-  // zoom level has changed.
   void OnZoomLevelChanged(double zoom_level);
-
-  // Out-of-process child frames receive a signal from RenderWidget when the
-  // page scale factor has changed, and/or a pinch-zoom gesture starts/ends.
   void OnPageScaleFactorChanged(float page_scale_factor,
                                 bool is_pinch_gesture_active);
-
-  // Invoked by RenderWidget when a new capture sequence number was set,
-  // indicating that surfaces should be synchronized.
+  void OnVisibleViewportSizeChanged(const gfx::Size& visible_viewport_size);
   void UpdateCaptureSequenceNumber(uint32_t capture_sequence_number);
 
   // Pass replicated information, such as security origin, to this
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 1b137014..9c452f4 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1086,8 +1086,10 @@
 
 void RenderViewImpl::ResizeWebWidgetForWidget(
     const gfx::Size& widget_size,
+    const gfx::Size& visible_viewport_size,
     cc::BrowserControlsParams browser_controls_params) {
-  GetWebView()->ResizeWithBrowserControls(widget_size, browser_controls_params);
+  GetWebView()->ResizeWithBrowserControls(widget_size, visible_viewport_size,
+                                          browser_controls_params);
 }
 
 void RenderViewImpl::SetScreenMetricsEmulationParametersForWidget(
@@ -1181,8 +1183,6 @@
     IPC_MESSAGE_HANDLER(PageMsg_SetHistoryOffsetAndLength,
                         OnSetHistoryOffsetAndLength)
     IPC_MESSAGE_HANDLER(PageMsg_AudioStateChanged, OnAudioStateChanged)
-    IPC_MESSAGE_HANDLER(PageMsg_UpdatePageVisualProperties,
-                        OnUpdatePageVisualProperties)
     IPC_MESSAGE_HANDLER(PageMsg_SetPageFrozen, SetPageFrozen)
     IPC_MESSAGE_HANDLER(PageMsg_PutPageIntoBackForwardCache,
                         PutPageIntoBackForwardCache)
@@ -1447,6 +1447,23 @@
     GetWebView()->SetDeviceScaleFactor(device_scale_factor);
 }
 
+void RenderViewImpl::SetVisibleViewportSizeForChildLocalRoot(
+    const gfx::Size& visible_viewport_size) {
+  // The main frame is updated on a different path. If we're in the same frame
+  // tree as it, there's nothing to do for child local roots.
+  if (main_render_frame_)
+    return;
+
+  // RenderWidgets in a RenderView's frame tree without a local main frame
+  // set the size of the WebView to be the |visible_viewport_size|, in order
+  // to limit compositing in (out of process) child frames to what is visible.
+  //
+  // Note that child frames in the same process/RenderView frame tree as the
+  // main frame do not do this in order to not clobber the source of truth in
+  // the main frame.
+  GetWebView()->Resize(visible_viewport_size);
+}
+
 void RenderViewImpl::PropagatePageZoomToNewlyAttachedFrame(
     bool use_zoom_for_dsf,
     float device_scale_factor) {
@@ -1741,29 +1758,6 @@
                            /*initial_setting=*/false);
 }
 
-void RenderViewImpl::OnUpdatePageVisualProperties(
-    const gfx::Size& viewport_size_for_blink) {
-  // TODO(https://crbug.com/998273): Handle visual_properties appropriately.
-  // Using this pathway to update the visual viewport should only happen for
-  // remote main frames. Local main frames will update the viewport size by
-  // RenderWidget calling RenderViewImpl::ResizeVisualViewport() directly.
-  // TODO(danakj): This should be part of VisualProperties and walk down the
-  // RenderWidget tree like other VisualProperties do, in order to set the
-  // value in each WebView holds a part of the local frame tree.
-  if (!main_render_frame_)
-    GetWebView()->Resize(viewport_size_for_blink);
-}
-
-void RenderViewImpl::ResizeVisualViewportForWidget(
-    const gfx::Size& scaled_viewport_size) {
-  // This function is currently only called for local main frames. Once remote
-  // main frames no longer have a RenderWidget, they may also route through
-  // here via RenderViewImpl::OnUpdateLocalMainFramePageVisualProperties(). In
-  // that case, WebViewImpl will need to implement its Size() function based on
-  // something other than the widget size.
-  GetWebView()->ResizeVisualViewport(scaled_viewport_size);
-}
-
 void RenderViewImpl::SetPageFrozen(bool frozen) {
   if (GetWebView())
     GetWebView()->SetPageFrozen(frozen);
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index ce65c24..78fa090 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -160,6 +160,13 @@
   // Passes along the device scale factor to the WebView.
   void SetDeviceScaleFactor(bool use_zoom_for_dsf, float device_scale_factor);
 
+  // Passes along the visible viewport size to the WebView, for child local
+  // roots when there is no local main frame present. When a local main frame
+  // exists in this renderer's frame tree, its value should persist and this
+  // method does nothing.
+  void SetVisibleViewportSizeForChildLocalRoot(
+      const gfx::Size& visible_viewport_size);
+
   // Passes along the page zoom to the WebView to set it on a newly attached
   // LocalFrame.
   void PropagatePageZoomToNewlyAttachedFrame(bool use_zoom_for_dsf,
@@ -374,12 +381,11 @@
   void DidCompletePageScaleAnimationForWidget() override;
   void ResizeWebWidgetForWidget(
       const gfx::Size& widget_size,
+      const gfx::Size& visible_viewport_size,
       cc::BrowserControlsParams browser_controls_params) override;
   void SetScreenMetricsEmulationParametersForWidget(
       bool enabled,
       const blink::WebDeviceEmulationParams& params) override;
-  void ResizeVisualViewportForWidget(
-      const gfx::Size& scaled_viewport_size) override;
 
   // Old WebLocalFrameClient implementations
   // ----------------------------------------
@@ -417,8 +423,6 @@
 
   // Page message handlers -----------------------------------------------------
   void OnPageVisibilityChanged(PageVisibilityState visibility_state);
-  void OnUpdateScreenInfo(const ScreenInfo& screen_info);
-  void OnUpdatePageVisualProperties(const gfx::Size& visible_viewport_size);
   void SetPageFrozen(bool frozen);
   void PutPageIntoBackForwardCache();
   void RestorePageFromBackForwardCache(base::TimeTicks navigation_start);
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 09e791f..2370f1c 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -661,6 +661,61 @@
     const VisualProperties& visual_properties_from_browser) {
   TRACE_EVENT0("renderer", "RenderWidget::OnUpdateVisualProperties");
 
+  // UpdateVisualProperties is used to receive properties from the browser
+  // process for this RenderWidget. There are roughly 4 types of
+  // VisualProperties.
+  // TODO(danakj): Splitting these 4 types of properties apart and making them
+  // more explicit could be super useful to understanding this code.
+  // 1. Unique to each RenderWidget. Computed by the RenderWidgetHost and passed
+  //    to the RenderWidget which consumes it here.
+  //    Example: new_size.
+  // 2. Global properties, which are given to each RenderWidget (to maintain
+  //    the requirement that a RenderWidget is updated atomically). These
+  //    properties are usually the same for every RenderWidget, except when
+  //    device emulation changes them in the main frame RenderWidget only.
+  //    Example: screen_info.
+  // 3. Computed in the renderer of the main frame RenderWidget (in blink
+  //    usually). Passed down through the waterfall dance to child frame
+  //    RenderWidgets. Here that step is performed by passing the value along
+  //    to all RenderFrameProxy objects that are below this RenderWidgets in the
+  //    frame tree. The main frame (top level) RenderWidget ignores this value
+  //    from its RenderWidgetHost since it is controlled in the renderer. Child
+  //    frame RenderWidgets consume the value from their RenderWidgetHost.
+  //    Example: page_scale_factor.
+  // 4. Computed independently in the renderer for each RenderWidget (in blink
+  //    usually). Passed down from the parent to the child RenderWidgets through
+  //    the waterfall dance, but the value only travels one step - the child
+  //    frame RenderWidget would compute values for grandchild RenderWidgets
+  //    independently. Here the value is passed to child frame RenderWidgets by
+  //    passing the value along to all RenderFrameProxy objects that are below
+  //    this RenderWidget in the frame tree. Each RenderWidget consumes this
+  //    value when it is received from its RenderWidgetHost.
+  //    Example: compositor_viewport_pixel_rect.
+  // For each of these properties:
+  //   If the RenderView/WebView also knows these properties, each RenderWidget
+  //   will pass them along to the RenderView as it receives it, even if there
+  //   are multiple RenderWidgets related to the same RenderView.
+  //   However when the main frame in the renderer is the source of truth,
+  //   then child widgets must not clobber that value! In all cases child frames
+  //   do not need to update state in the RenderView when a local main frame is
+  //   present as it always sets the value first.
+  //   TODO(danakj): This does create a race if there are multiple
+  //   UpdateVisualProperties updates flowing through the RenderWidget tree at
+  //   the same time, and it seems that only one RenderWidget for each
+  //   RenderView should be responsible for this update.
+  //
+  //   This operation is done by going through RenderFrameImpl to pass the value
+  //   to the RenderViewImpl. While this class does not use RenderViewImpl
+  //   directly, it speaks through the RenderFrameImpl::*OnRenderView() methods.
+  //   TODO(danakj): A more explicit API to give values from here to RenderView
+  //   and/or WebView would be nice. Also a more explicit API to give values to
+  //   the RenderFrameProxy in one go, instead of setting each property
+  //   independently, causing an update IPC from the RenderFrameProxy for each
+  //   one.
+  //
+  //   See also:
+  //   https://docs.google.com/document/d/1G_fR1D_0c1yke8CqDMddoKrDGr3gy5t_ImEH4hKNIII/edit#
+
   VisualProperties visual_properties = visual_properties_from_browser;
   // Web tests can override the device scale factor in the renderer.
   if (device_scale_factor_for_testing_) {
@@ -808,18 +863,18 @@
         // order to send IPCs that query and change compositing state. So
         // ResizeWebWidget() must come after this call, as it runs the entire
         // document lifecycle.
-        //
-        // TODO(danakj): Only the top-most RenderWidget per RenderView should
-        // be responsible for setting values onto the RenderView.
         render_frame->SetPreferCompositingToLCDTextEnabledOnRenderView(
             ComputePreferCompositingToLCDText(
                 compositor_deps_, screen_info_.device_scale_factor));
       }
 
+      // Store this even when auto-resizing, it is the size of the full viewport
+      // used for clipping, and this value is propagated down the RenderWidget
+      // hierarchy via the VisualProperties waterfall.
+      visible_viewport_size_ = visual_properties.visible_viewport_size;
+
       if (!auto_resize_mode_) {
         display_mode_ = visual_properties.display_mode;
-
-        visible_viewport_size_ = visual_properties.visible_viewport_size;
         size_ = visual_properties.new_size;
         ResizeWebWidget();
       }
@@ -865,6 +920,11 @@
   if (old_visible_viewport_size != visible_viewport_size_) {
     for (auto& render_frame : render_frames_)
       render_frame.ResetHasScrolledFocusedEditableIntoView();
+
+    // Propagate changes down to child local root RenderWidgets and
+    // BrowserPlugins in other frame trees/processes.
+    for (auto& observer : render_frame_proxies_)
+      observer.OnVisibleViewportSizeChanged(visible_viewport_size_);
   }
   // TODO(crbug.com/939118): ScrollFocusedNodeIntoViewForWidget does not work
   // when the focused node is inside an OOPIF. This code path where
@@ -939,8 +999,6 @@
   RenderFrameImpl* render_frame =
       RenderFrameImpl::FromWebFrame(GetFrameWidget()->LocalRoot());
 
-  // TODO(danakj): Only the top-most RenderWidget per RenderView should be
-  // responsible for setting values onto the RenderView.
   bool zoom_level_changed = render_frame->SetZoomLevelOnRenderView(zoom_level);
   if (zoom_level_changed) {
     // Hide popups when the zoom changes.
@@ -951,6 +1009,7 @@
 
     // Propagate changes down to child local root RenderWidgets and
     // BrowserPlugins in other frame trees/processes.
+    zoom_level_ = zoom_level;
     for (auto& observer : render_frame_proxies_)
       observer.OnZoomLevelChanged(zoom_level);
   }
@@ -1511,27 +1570,37 @@
         size_, GetOriginalScreenInfo().device_scale_factor);
   }
 
-  if (delegate()) {
-    // The visual viewport size given to blink is scaled by the (non-emulated,
-    // see https://crbug.com/819903) device scale factor (if UseZoomForDSF is
-    // enabled).
-    gfx::Size visible_viewport_size_for_blink;
-    if (!compositor_deps_->IsUseZoomForDSFEnabled()) {
-      visible_viewport_size_for_blink = visible_viewport_size_;
-    } else {
-      visible_viewport_size_for_blink = gfx::ScaleToCeiledSize(
-          visible_viewport_size_, GetOriginalScreenInfo().device_scale_factor);
-    }
+  // The |visible_viewport_size| given to blink is scaled by the (non-emulated,
+  // see https://crbug.com/819903) device scale factor (if UseZoomForDSF is
+  // enabled).
+  gfx::Size visible_viewport_size_for_blink;
+  if (!compositor_deps_->IsUseZoomForDSFEnabled()) {
+    visible_viewport_size_for_blink = visible_viewport_size_;
+  } else {
+    visible_viewport_size_for_blink = gfx::ScaleToCeiledSize(
+        visible_viewport_size_, GetOriginalScreenInfo().device_scale_factor);
+  }
 
+  if (delegate()) {
     // When associated with a RenderView, the RenderView is in control of the
     // main frame's size, because it includes other factors for top and bottom
     // controls.
     delegate()->ResizeWebWidgetForWidget(size_for_blink,
+                                         visible_viewport_size_for_blink,
                                          browser_controls_params_);
-    delegate()->ResizeVisualViewportForWidget(visible_viewport_size_for_blink);
   } else {
-    // When not associated with a RenderView, the RenderWidget is in control of
-    // the frame's (or other type of widget's) size.
+    // Child frames set the |visible_viewport_size| on the RenderView/WebView to
+    // limit the size blink tries to composite when the widget is not visible,
+    // such as when it is scrolled out of the main frame's view.
+    if (for_frame()) {
+      RenderFrameImpl* render_frame =
+          RenderFrameImpl::FromWebFrame(GetFrameWidget()->LocalRoot());
+      render_frame->SetVisibleViewportSizeForChildLocalRootOnRenderView(
+          visible_viewport_size_for_blink);
+    }
+
+    // For child frame widgets, popups, and pepper, the RenderWidget is in
+    // control of the WebWidget's size.
     GetWebWidget()->Resize(size_for_blink);
   }
 }
@@ -2115,9 +2184,6 @@
     // default value once we get to OnSynchronizeVisualProperties. Thus we
     // call into blink unconditionally and let it early out if it's already
     // set.
-    //
-    // TODO(danakj): Only the top-most RenderWidget per RenderView should
-    // be responsible for setting values onto the RenderView.
     render_frame->SetDeviceScaleFactorOnRenderView(
         compositor_deps_->IsUseZoomForDSFEnabled(),
         screen_info_.device_scale_factor);
@@ -3374,12 +3440,16 @@
 
 void RenderWidget::RegisterRenderFrameProxy(RenderFrameProxy* proxy) {
   render_frame_proxies_.AddObserver(proxy);
-  // Page scale factor is propagated down the RenderWidget tree (across
-  // frame trees). A new RenderFrameProxy means there is a new child
-  // RenderWidget in another frame tree. In order for it to hear about
-  // the page scale factor we pass along the last seen value here.
+
+  // These properties are propagated down the RenderWidget tree through
+  // the RenderFrameProxy (see explanation in OnUpdateVisualProperties()).
+  // When a new RenderFrameProxy is added, we propagate them immediately.
+
   proxy->OnPageScaleFactorChanged(page_scale_factor_from_mainframe_,
                                   is_pinch_gesture_active_from_mainframe_);
+  proxy->OnScreenInfoChanged(GetOriginalScreenInfo());
+  proxy->OnZoomLevelChanged(zoom_level_);
+  proxy->OnVisibleViewportSizeChanged(visible_viewport_size_);
 }
 
 void RenderWidget::UnregisterRenderFrameProxy(RenderFrameProxy* proxy) {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index ed595b5..60b849ac 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -873,6 +873,13 @@
   // The size of the visible viewport in pixels.
   gfx::Size visible_viewport_size_;
 
+  // Stores the zoom level to propagate to new child RenderWidgets. Initialized
+  // to 0 to match the value in RenderViewImpl, but this will be the value being
+  // propagated down the RenderWidget tree, whereas the value in RenderViewImpl
+  // is derived from these as RenderWidgets update their corresponding
+  // RenderViewImpls.
+  double zoom_level_ = 0;
+
   // Whether the WebWidget is in auto resize mode, which is used for example
   // by extension popups.
   bool auto_resize_mode_ = false;
@@ -976,8 +983,12 @@
 
   scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue_;
 
-  // Lists of RenderFrameProxy objects that need to be notified of
-  // compositing-related events (e.g. DidCommitCompositorFrame).
+  // Lists of RenderFrameProxy objects for which this RenderWidget is their
+  // local root. Each of these represents a child local root RenderWidget in
+  // another RenderView frame tree. For values that are propagated from
+  // a parent RenderWidget to its children, they are plumbed through the
+  // RenderFrameProxys in this list, which bounce those values through the
+  // browser to the child RenderWidget in the correct process.
   base::ObserverList<RenderFrameProxy>::Unchecked render_frame_proxies_;
 
   // A list of RenderFrames associated with this RenderWidget. Notifications
diff --git a/content/renderer/render_widget_delegate.h b/content/renderer/render_widget_delegate.h
index 85da7fe..bd7d362 100644
--- a/content/renderer/render_widget_delegate.h
+++ b/content/renderer/render_widget_delegate.h
@@ -70,6 +70,7 @@
   // happens.
   virtual void ResizeWebWidgetForWidget(
       const gfx::Size& size,
+      const gfx::Size& visible_viewport_size,
       cc::BrowserControlsParams browser_controls_params) = 0;
 
   // Called when RenderWidget services RenderWidgetScreenMetricsEmulatorDelegate
@@ -77,11 +78,6 @@
   virtual void SetScreenMetricsEmulationParametersForWidget(
       bool enabled,
       const blink::WebDeviceEmulationParams& params) = 0;
-
-  // Called when the VisualViewport needs to be updated. Expects coordinates
-  // scaled to account for DeviceScaleFactor.
-  virtual void ResizeVisualViewportForWidget(
-      const gfx::Size& scaled_viewport_size) = 0;
 };
 
 }  // namespace content
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index a5378e32..17d1ba6 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -474,12 +474,12 @@
   void DidReceiveSetFocusEventForWidget() override {}
   void DidCommitCompositorFrameForWidget() override {}
   void DidCompletePageScaleAnimationForWidget() override {}
-  void ResizeWebWidgetForWidget(const gfx::Size& size,
+  void ResizeWebWidgetForWidget(const gfx::Size& main_frame_widget_size,
+                                const gfx::Size& visible_viewport_size,
                                 cc::BrowserControlsParams) override {}
   void SetScreenMetricsEmulationParametersForWidget(
       bool enabled,
       const blink::WebDeviceEmulationParams& params) override {}
-  void ResizeVisualViewportForWidget(const gfx::Size& viewport_size) override {}
 };
 
 // Tests that the value of VisualProperties::is_pinch_gesture_active is
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index a7d3937..68235ab6 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -245,106 +245,6 @@
   ]
 }
 
-if (current_cpu != "x64") {
-  chromium_linker_test_manifest =
-      "$target_gen_dir/linker_test_apk/AndroidManifest.xml"
-
-  jinja_template("chromium_linker_test_manifest") {
-    testonly = true
-    input = "linker_test_apk/AndroidManifest.xml.jinja2"
-    output = chromium_linker_test_manifest
-  }
-
-  android_resources("linker_resources") {
-    testonly = true
-    resource_dirs = [ "linker_test_apk/res" ]
-    android_manifest = chromium_linker_test_manifest
-    android_manifest_dep = ":chromium_linker_test_manifest"
-  }
-
-  _linker_test_apk_target_name = "chromium_linker_test_apk__apk"
-  _linker_test_apk_test_runner_target_name =
-      "chromium_linker_test_apk__test_runner_script"
-  _linker_test_jni_registration_header =
-      "$target_gen_dir/linker_test_apk/linker_test_jni_registration.h"
-
-  android_apk(_linker_test_apk_target_name) {
-    testonly = true
-    deps = [
-      ":content_shell_assets",
-      ":content_shell_java",
-      ":linker_resources",
-      "//base:base_java",
-      "//base:jni_java",
-      "//content/public/android:content_java",
-      "//ui/android:ui_java",
-    ]
-    android_manifest = chromium_linker_test_manifest
-    android_manifest_dep = ":chromium_linker_test_manifest"
-    apk_name = "ChromiumLinkerTest"
-    shared_libraries = [ ":linker_test" ]
-    use_chromium_linker = true
-    enable_chromium_linker_tests = true
-    jni_registration_header = _linker_test_jni_registration_header
-    sources = [
-      "linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java",
-      "linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java",
-      "linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java",
-    ]
-    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
-    processor_args_javac = [ skip_gen_jni_arg ]
-  }
-
-  test_runner_script(_linker_test_apk_test_runner_target_name) {
-    test_name = "chromium_linker_test_apk"
-    test_type = "linker"
-    apk_target = ":$_linker_test_apk_target_name"
-    ignore_all_data_deps = true
-  }
-
-  group("chromium_linker_test_apk") {
-    testonly = true
-    deps = [
-      ":$_linker_test_apk_target_name",
-      ":$_linker_test_apk_test_runner_target_name",
-    ]
-  }
-
-  shared_library("linker_test") {
-    testonly = true
-    sources = [
-      "linker_test_apk/chromium_linker_test_android.cc",
-      "linker_test_apk/chromium_linker_test_linker_tests.cc",
-      _linker_test_jni_registration_header,
-    ]
-
-    deps = [
-      ":${_linker_test_apk_target_name}__final_jni",
-      ":linker_test_jni_headers",
-      "//content/shell:content_shell_lib",
-
-      # Required to include "content/public/browser/android/compositor.h"
-      # in chromium_linker_test_android.cc :-(
-      "//skia",
-      "//third_party/re2",
-    ]
-
-    # Explicit dependency required for JNI registration to be able to
-    # find the native side functions.
-    if (is_component_build) {
-      deps += [
-        "//device/gamepad",
-        "//media/midi",
-      ]
-    }
-  }
-
-  generate_jni("linker_test_jni_headers") {
-    testonly = true
-    sources = [ "linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java" ]
-  }
-}
-
 android_library("content_shell_browsertests_java") {
   testonly = true
   deps = [
diff --git a/content/shell/android/linker_test_apk/AndroidManifest.xml.jinja2 b/content/shell/android/linker_test_apk/AndroidManifest.xml.jinja2
deleted file mode 100644
index 834e677..0000000
--- a/content/shell/android/linker_test_apk/AndroidManifest.xml.jinja2
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- Copyright 2013 The Chromium Authors. All rights reserved.
-
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.chromium.chromium_linker_test_apk">
-
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-    <uses-permission android:name="android.permission.CAMERA" />
-    <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
-    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
-    <uses-permission android:name="android.permission.VIBRATE"/>
-    <uses-permission android:name="android.permission.WAKE_LOCK"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
-    <application android:name="ChromiumLinkerTestApplication"
-            android:label="ChromiumLinkerTest">
-        <activity android:name="ChromiumLinkerTestActivity"
-                  android:launchMode="singleTask"
-                  android:theme="@android:style/Theme.Holo.Light.NoActionBar"
-                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
-                  android:hardwareAccelerated="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
-            </intent-filter>
-        </activity>
-        <!-- The following service entries exist in order to allow us to
-             start more than one sandboxed process. -->
-
-        <!-- NOTE: If you change the values of "android:process" for any of the below services,
-             you also need to update kHelperProcessExecutableName in chrome_constants.cc. -->
-        {% set num_sandboxed_services = 40 %}
-        <meta-data android:name="org.chromium.content.browser.NUM_SANDBOXED_SERVICES"
-                   android:value="{{ num_sandboxed_services }}"/>
-        {% for i in range(num_sandboxed_services) %}
-        <service android:name="org.chromium.content.app.SandboxedProcessService{{ i }}"
-                 android:process=":sandboxed_process{{ i }}"
-                 android:isolatedProcess="true"
-                 android:exported="false" />
-        {% endfor %}
-
-        {% set num_privileged_services = 5 %}
-        <meta-data android:name="org.chromium.content.browser.NUM_PRIVILEGED_SERVICES"
-                   android:value="{{ num_privileged_services }}"/>
-        {% for i in range(num_privileged_services) %}
-        <service android:name="org.chromium.content.app.PrivilegedProcessService{{ i }}"
-                 android:process=":privileged_process{{ i }}"
-                 android:isolatedProcess="false"
-                 android:exported="false" />
-        {% endfor %}
-    </application>
-</manifest>
diff --git a/content/shell/android/linker_test_apk/chromium_linker_test_android.cc b/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
deleted file mode 100644
index 300a15a3..0000000
--- a/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/android/jni_android.h"
-#include "base/android/library_loader/library_loader_hooks.h"
-#include "base/bind.h"
-#include "content/public/app/content_jni_onload.h"
-#include "content/public/app/content_main.h"
-#include "content/shell/android/linker_test_apk/linker_test_jni_registration.h"
-#include "content/shell/app/shell_main_delegate.h"
-
-// This is called by the VM when the shared library is first loaded.
-JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  base::android::InitVM(vm);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env) ||
-      !content::android::OnJNIOnLoadInit()) {
-    return -1;
-  }
-  content::SetContentMainDelegate(new content::ShellMainDelegate());
-  return JNI_VERSION_1_4;
-}
diff --git a/content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.cc b/content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.cc
deleted file mode 100644
index a949d30e..0000000
--- a/content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file implements the native methods of
-// org.content.chromium.app.LinkerTests
-// Unlike the content of linker_jni.cc, it is part of the content library and
-// can thus use base/ and the C++ STL.
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <sys/mman.h>
-#include <string>
-#include <vector>
-
-#include "base/debug/proc_maps_linux.h"
-#include "base/logging.h"
-#include "base/strings/stringprintf.h"
-#include "content/shell/android/linker_test_jni_headers/LinkerTests_jni.h"
-#include "third_party/re2/src/re2/re2.h"
-
-using base::android::JavaParamRef;
-
-namespace content {
-
-namespace {
-
-using base::debug::MappedMemoryRegion;
-
-jboolean RunChecks(bool in_browser_process) {
-  // IMPORTANT NOTE: The Python test control script reads the logcat for
-  // lines like:
-  //   BROWSER_LINKER_TEST: <status>
-  //   RENDERER_LINKER_TEST: <status>
-  //
-  // Where <status> can be either SUCCESS or FAIL. Other lines starting
-  // with the same prefixes, but not using SUCCESS or FAIL are ignored.
-  const char* prefix =
-      in_browser_process ? "BROWSER_LINKER_TEST: " : "RENDERER_LINKER_TEST: ";
-
-  // The RELRO section(s) will appear in /proc/self/maps as a mapped memory
-  // region for a file with a recognizable name. For the LegacyLinker the
-  // full name will be something like:
-  //
-  //   "/dev/ashmem/RELRO:<libname> (deleted)"
-  //
-  // and for the ModernLinker, something like:
-  //
-  //   "/data/data/org.chromium.chromium_linker_test_apk/
-  //       app_chromium_linker_test/RELRO:<libname> (deleted)"
-  //
-  // Where <libname> is the library name and '(deleted)' is actually
-  // added by the kernel to indicate there is no corresponding file
-  // on the filesystem.
-  //
-  // For regular builds, there is only one library, and thus one RELRO
-  // section, but for the component build, there are several libraries,
-  // each one with its own RELRO.
-  static const char kLegacyRelroSectionPattern[] = "/dev/ashmem/RELRO:.*";
-  static const char kModernRelroSectionPattern[] = "/data/.*/RELRO:.*";
-
-  // Parse /proc/self/maps and builds a list of region mappings in this
-  // process.
-  std::string maps;
-  base::debug::ReadProcMaps(&maps);
-  if (maps.empty()) {
-    LOG(ERROR) << prefix << "FAIL Cannot parse /proc/self/maps";
-    return false;
-  }
-
-  std::vector<MappedMemoryRegion> regions;
-  base::debug::ParseProcMaps(maps, &regions);
-  if (regions.empty()) {
-    LOG(ERROR) << prefix << "FAIL Cannot read memory mappings in this process";
-    return false;
-  }
-
-  const RE2 legacy_linker_re(kLegacyRelroSectionPattern);
-  const RE2 modern_linker_re(kModernRelroSectionPattern);
-
-  int num_shared_relros = 0;
-  int num_bad_shared_relros = 0;
-
-  for (size_t n = 0; n < regions.size(); ++n) {
-    MappedMemoryRegion& region = regions[n];
-
-    const std::string path = region.path;
-    const bool is_legacy_relro = re2::RE2::FullMatch(path, legacy_linker_re);
-    const bool is_modern_relro = re2::RE2::FullMatch(path, modern_linker_re);
-
-    if (is_legacy_relro && is_modern_relro) {
-      LOG(ERROR) << prefix
-                 << "FAIL RELRO cannot be both Legacy and Modern (test error)";
-      return false;
-    }
-
-    if (!is_legacy_relro && !is_modern_relro) {
-      // Ignore any mapping that isn't a shared RELRO.
-      continue;
-    }
-
-    num_shared_relros++;
-
-    void* region_start = reinterpret_cast<void*>(region.start);
-    void* region_end = reinterpret_cast<void*>(region.end);
-
-    // Check that it is mapped read-only.
-    const uint8_t expected_flags = MappedMemoryRegion::READ;
-    const uint8_t expected_mask = MappedMemoryRegion::READ |
-                                  MappedMemoryRegion::WRITE |
-                                  MappedMemoryRegion::EXECUTE;
-
-    uint8_t region_flags = region.permissions & expected_mask;
-    if (region_flags != expected_flags) {
-      LOG(ERROR)
-          << prefix
-          << base::StringPrintf(
-                 "Shared RELRO section at %p-%p is not mapped read-only. "
-                 "Protection flags are %d (%d expected)!",
-                 region_start,
-                 region_end,
-                 region_flags,
-                 expected_flags);
-      num_bad_shared_relros++;
-      continue;
-    }
-
-    // Shared RELROs implemented by ModernLinker are not in ashmem. ModernLinker
-    // (via android_dlopen_ext()) maps everything with MAP_PRIVATE rather than
-    // MAP_SHARED. Remapping such a RELRO section read-write will therefore
-    // succeed, but it is not a problem. The memory copy-on-writes, and updates
-    // are not visible to either the mapped file or other processes mapping the
-    // same file. So... we skip the remap test for ModernLinker.
-    if (is_modern_relro) {
-      continue;
-    }
-
-    // Check that trying to remap it read-write fails with EACCES
-    size_t region_size = region.end - region.start;
-    int ret = ::mprotect(region_start, region_size, PROT_READ | PROT_WRITE);
-    if (ret != -1) {
-      LOG(ERROR)
-          << prefix
-          << base::StringPrintf(
-                 "Shared RELRO section at %p-%p could be remapped read-write!?",
-                 region_start,
-                 region_end);
-      num_bad_shared_relros++;
-      // Just in case.
-      ::mprotect(region_start, region_size, PROT_READ);
-    } else if (errno != EACCES) {
-      LOG(ERROR) << prefix << base::StringPrintf(
-                                  "Shared RELRO section at %p-%p failed "
-                                  "read-write mprotect with "
-                                  "unexpected error %d (EACCES:%d wanted): %s",
-                                  region_start,
-                                  region_end,
-                                  errno,
-                                  EACCES,
-                                  strerror(errno));
-      num_bad_shared_relros++;
-    }
-  }
-
-  VLOG(0) << prefix
-          << base::StringPrintf(
-                 "There are %d shared RELRO sections in this process, of which "
-                 "%d are bad",
-                 num_shared_relros, num_bad_shared_relros);
-
-  if (num_bad_shared_relros > 0) {
-    LOG(ERROR) << prefix << "FAIL Bad RELROs sections in this process";
-    return false;
-  }
-
-  if (num_shared_relros == 0) {
-    LOG(ERROR) << prefix
-               << "FAIL Missing shared RELRO sections in this process!";
-    return false;
-  }
-
-  VLOG(0) << prefix << "SUCCESS";
-  return true;
-}
-
-}  // namespace
-
-jboolean JNI_LinkerTests_CheckForSharedRelros(JNIEnv* env,
-                                              jboolean in_browser_process) {
-  return RunChecks(in_browser_process);
-}
-
-}  // namespace content
diff --git a/content/shell/android/linker_test_apk/res/layout/test_activity.xml b/content/shell/android/linker_test_apk/res/layout/test_activity.xml
deleted file mode 100644
index f018927..0000000
--- a/content/shell/android/linker_test_apk/res/layout/test_activity.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- Copyright 2013 The Chromium Authors. All rights reserved.
-
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent" android:layout_height="match_parent"
-    android:orientation="vertical">
-    <org.chromium.content_shell.ShellManager
-        android:id="@+id/shell_container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-</LinearLayout>
diff --git a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java
deleted file mode 100644
index 2da5bfa..0000000
--- a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chromium_linker_test_apk;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import org.chromium.base.Log;
-import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.base.library_loader.Linker;
-import org.chromium.content_public.browser.BrowserStartupController;
-import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_shell.Shell;
-import org.chromium.content_shell.ShellManager;
-import org.chromium.ui.base.ActivityWindowAndroid;
-
-/**
- * Test activity used for verifying the different configuration options for the ContentLinker.
- */
-public class ChromiumLinkerTestActivity extends Activity {
-    private static final String TAG = "LinkerTest";
-
-    private ShellManager mShellManager;
-    private ActivityWindowAndroid mWindowAndroid;
-
-    @Override
-    public void onCreate(final Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // Setup the TestRunner class name.
-        Linker.setupForTesting(Linker.LINKER_IMPLEMENTATION_LEGACY,
-                "org.chromium.chromium_linker_test_apk.LinkerTests");
-
-        // Load the library in the browser process, this will also run the test
-        // runner in this process.
-        LibraryLoader.getInstance().ensureInitialized();
-
-        // Now, start a new renderer process by creating a new view.
-        // This will run the test runner in the renderer process.
-
-        LayoutInflater inflater =
-                (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        View view = inflater.inflate(R.layout.test_activity, null);
-        mShellManager = view.findViewById(R.id.shell_container);
-        mWindowAndroid = new ActivityWindowAndroid(this, false);
-        mShellManager.setWindow(mWindowAndroid);
-
-        mShellManager.setStartupUrl("about:blank");
-
-        BrowserStartupController.getInstance().startBrowserProcessesAsync(
-                LibraryProcessType.PROCESS_BROWSER, true, false,
-                new BrowserStartupController.StartupCallback() {
-                    @Override
-                    public void onSuccess() {
-                        finishInitialization(savedInstanceState);
-                    }
-
-                    @Override
-                    public void onFailure() {
-                        initializationFailed();
-                    }
-                });
-
-        // TODO(digit): Ensure that after the content view is initialized,
-        // the program finishes().
-    }
-
-    private void finishInitialization(Bundle savedInstanceState) {
-        String shellUrl = ShellManager.DEFAULT_SHELL_URL;
-        mShellManager.launchShell(shellUrl);
-    }
-
-    private void initializationFailed() {
-        Log.e(TAG, "ContentView initialization failed.");
-        finish();
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        mWindowAndroid.saveInstanceState(outState);
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        WebContents webContents = getActiveWebContents();
-        if (webContents != null) webContents.onHide();
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-
-        WebContents webContents = getActiveWebContents();
-        if (webContents != null) webContents.onHide();
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-        mWindowAndroid.onActivityResult(requestCode, resultCode, data);
-    }
-
-    /**
-     * @return The {@link WebContents} owned by the currently visible {@link Shell} or null if
-     *         one is not showing.
-     */
-    public WebContents getActiveWebContents() {
-        if (mShellManager == null) return null;
-        Shell shell = mShellManager.getActiveShell();
-        return shell != null ? shell.getWebContents() : null;
-    }
-}
diff --git a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java
deleted file mode 100644
index a646bf8..0000000
--- a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chromium_linker_test_apk;
-
-import android.app.Application;
-import android.content.Context;
-
-import org.chromium.base.BuildConfig;
-import org.chromium.base.ContextUtils;
-import org.chromium.base.PathUtils;
-import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.base.multidex.ChromiumMultiDexInstaller;
-import org.chromium.ui.base.ResourceBundle;
-
-/**
- * Application for testing the Chromium Linker
- */
-public class ChromiumLinkerTestApplication extends Application {
-    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chromium_linker_test";
-
-    @Override
-    protected void attachBaseContext(Context base) {
-        super.attachBaseContext(base);
-        boolean isBrowserProcess = !ContextUtils.getProcessName().contains(":");
-        LibraryLoader.getInstance().setLibraryProcessType(isBrowserProcess
-                        ? LibraryProcessType.PROCESS_BROWSER
-                        : LibraryProcessType.PROCESS_CHILD);
-        if (BuildConfig.IS_MULTIDEX_ENABLED) {
-            ChromiumMultiDexInstaller.install(this);
-        }
-        ContextUtils.initApplicationContext(this);
-        ResourceBundle.setNoAvailableLocalePaks();
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
-    }
-}
diff --git a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java
deleted file mode 100644
index 0405afc8..0000000
--- a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chromium_linker_test_apk;
-
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.NativeMethods;
-import org.chromium.base.library_loader.Linker;
-
-/**
- * A class that is only used in linker test APK to perform runtime checks
- * in the current process.
- */
-@JNINamespace("content")
-public class LinkerTests implements Linker.TestRunner {
-    private static final String TAG = "LinkerTest";
-
-    public LinkerTests() {}
-
-    @Override
-    public boolean runChecks(boolean isBrowserProcess) {
-        return LinkerTestsJni.get().checkForSharedRelros(isBrowserProcess);
-    }
-
-    @NativeMethods
-    interface Natives {
-        // Check that there are shared RELRO sections in the current process,
-        // and that they are properly mapped read-only. Returns true on success.
-        boolean checkForSharedRelros(boolean isBrowserProcess);
-    }
-}
diff --git a/docs/chromoting_android_hacking.md b/docs/chromoting_android_hacking.md
index 0bae0f3..2d21bfb 100644
--- a/docs/chromoting_android_hacking.md
+++ b/docs/chromoting_android_hacking.md
@@ -104,7 +104,6 @@
 <classpathentry kind="src" path="content/shell/android/java/src"/>
 <classpathentry kind="src" path="content/shell/android/shell_apk/src"/>
 <classpathentry kind="src" path="content/shell/android/javatests/src"/>
-<classpathentry kind="src" path="content/shell/android/linker_test_apk/src"/>
 <classpathentry kind="lib" path="third_party/android_sdk/public/platforms/android-27/data/layoutlib.jar"/>
 <classpathentry kind="lib" path="third_party/android_sdk/public/platforms/android-27/android.jar"/>
 <classpathentry kind="output" path="out/bin"/>
diff --git a/docs/fuchsia_build_instructions.md b/docs/fuchsia_build_instructions.md
index 11bcc6c7..5c5d2ac 100644
--- a/docs/fuchsia_build_instructions.md
+++ b/docs/fuchsia_build_instructions.md
@@ -190,6 +190,11 @@
 ```
 3. Add users to the "kvm" group, and have them login again, to pick-up the new
 group.
+```shell
+$ sudo adduser <user> kvm
+$ exit
+[log in again]
+```
 
 ### Running test suites
 
diff --git a/gpu/command_buffer/common/shared_image_usage.h b/gpu/command_buffer/common/shared_image_usage.h
index 76cdd80..9fef1ad 100644
--- a/gpu/command_buffer/common/shared_image_usage.h
+++ b/gpu/command_buffer/common/shared_image_usage.h
@@ -35,6 +35,9 @@
   // TODO(crbug.com/969114): This usage is currently not supported in GL/Vulkan
   // interop cases.
   SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE = 1 << 9,
+  // Image will be used in a platform specific API that requires a native buffer
+  // allocation.
+  SHARED_IMAGE_USAGE_NATIVE_BUFFER = 1 << 10,
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc
index c27839ce..994bb856 100644
--- a/gpu/command_buffer/service/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -386,6 +386,7 @@
   bool share_between_gl_vulkan = gl_usage && vulkan_usage;
   bool using_interop_factory = share_between_gl_vulkan || using_dawn ||
                                share_between_gl_metal ||
+                               (usage & SHARED_IMAGE_USAGE_NATIVE_BUFFER) ||
                                (share_between_threads && vulkan_usage);
 
   // TODO(vasilyt): Android required AHB for overlays
diff --git a/gpu/ipc/service/gpu_watchdog_thread_v2.cc b/gpu/ipc/service/gpu_watchdog_thread_v2.cc
index cfaa7b0..bdd9869 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_v2.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread_v2.cc
@@ -741,6 +741,10 @@
       // number to calculate the number of users who had already quit.
       RecordNumOfUsersWaitingWithExtraThreadTimeHistogram(
           count_of_more_gpu_thread_time_allowed_);
+
+      // Used by GPU.WatchdogThread.WaitTime later
+      time_in_wait_for_full_thread_time_ =
+          count_of_more_gpu_thread_time_allowed_ * watchdog_timeout_;
     }
   }
 }
@@ -761,6 +765,23 @@
       GpuWatchdogTimeoutHistogram(GpuWatchdogTimeoutEvent::kProgressAfterWait);
       base::UmaHistogramExactLinear(
           "GPU.WatchdogThread.WaitTime.ProgressAfterWait", count, kMax);
+
+#if defined(OS_WIN)
+      // Add the time the GPU thread was given for the full thread time up to 60
+      // seconds. GPU.WatchdogThread.WaitTime is essentially equal to
+      // GPU.WatchdogThread.WaitTime.ProgressAfterWait on non-Windows systems.
+      base::TimeDelta wait_time = base::TimeDelta::FromSeconds(count);
+      wait_time += time_in_wait_for_full_thread_time_;
+
+      constexpr base::TimeDelta kMinTime = base::TimeDelta::FromSeconds(1);
+      constexpr base::TimeDelta kMaxTime = base::TimeDelta::FromSeconds(150);
+      constexpr int kBuckets = 50;
+
+      // The time the GPU main thread takes to finish a task after a "hang" is
+      // dectedted.
+      base::UmaHistogramCustomTimes("GPU.WatchdogThread.WaitTime", wait_time,
+                                    kMinTime, kMaxTime, kBuckets);
+#endif
     }
   }
 }
diff --git a/gpu/ipc/service/gpu_watchdog_thread_v2.h b/gpu/ipc/service/gpu_watchdog_thread_v2.h
index ba51a10..6056afb 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_v2.h
+++ b/gpu/ipc/service/gpu_watchdog_thread_v2.h
@@ -168,6 +168,10 @@
   // continue due to not enough thread time.
   int count_of_more_gpu_thread_time_allowed_ = 0;
 
+  // The total timeout, up to 60 seconds, the watchdog thread waits for the GPU
+  // main thread to get full thread time.
+  base::TimeDelta time_in_wait_for_full_thread_time_;
+
   // After detecting GPU hang and continuing running through
   // OnGpuWatchdogTimeout for the max cycles, the GPU main thread still cannot
   // get the full thread time.
diff --git a/ios/chrome/app/tests_fake_hook.mm b/ios/chrome/app/tests_fake_hook.mm
index a65067f9..27d3c5a 100644
--- a/ios/chrome/app/tests_fake_hook.mm
+++ b/ios/chrome/app/tests_fake_hook.mm
@@ -28,6 +28,9 @@
 bool DisableUpdateService() {
   return false;
 }
+policy::ConfigurationPolicyProvider* GetOverriddenPlatformPolicyProvider() {
+  return nullptr;
+}
 void SetUpTestsIfPresent() {}
 void RunTestsIfPresent() {}
 
diff --git a/ios/chrome/app/tests_hook.h b/ios/chrome/app/tests_hook.h
index ab4ac8a..f6a939e 100644
--- a/ios/chrome/app/tests_hook.h
+++ b/ios/chrome/app/tests_hook.h
@@ -5,6 +5,10 @@
 #ifndef IOS_CHROME_APP_TESTS_HOOK_H_
 #define IOS_CHROME_APP_TESTS_HOOK_H_
 
+namespace policy {
+class ConfigurationPolicyProvider;
+}
+
 namespace tests_hook {
 
 // Returns true if app group access should be disabled as tests don't have the
@@ -32,6 +36,10 @@
 // infobar won't be shown during testing.
 bool DisableUpdateService();
 
+// Returns a policy provider that should be installed as the platform policy
+// provider when testing. May return nullptr.
+policy::ConfigurationPolicyProvider* GetOverriddenPlatformPolicyProvider();
+
 // Global integration tests setup.  This is not used by EarlGrey-based
 // integration tests.
 void SetUpTestsIfPresent();
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index 88312ac..7d1e209 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -214,6 +214,7 @@
     "//components/variations/field_trial_config",
     "//components/variations/service",
     "//components/version_info",
+    "//ios/chrome/app:tests_hook",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/browser_state:browser_state_impl",
     "//ios/chrome/browser/component_updater",
diff --git a/ios/chrome/browser/application_context_impl.cc b/ios/chrome/browser/application_context_impl.cc
index 02b33e7..308339ab 100644
--- a/ios/chrome/browser/application_context_impl.cc
+++ b/ios/chrome/browser/application_context_impl.cc
@@ -40,6 +40,7 @@
 #include "components/update_client/configurator.h"
 #include "components/update_client/update_query_params.h"
 #include "components/variations/service/variations_service.h"
+#include "ios/chrome/app/tests_hook.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.h"
@@ -423,6 +424,15 @@
       DCHECK(ui::ResourceBundle::HasSharedInstance());
       browser_policy_connector_ = std::make_unique<BrowserPolicyConnectorIOS>(
           base::Bind(&BuildPolicyHandlerList));
+
+      // Install a mock platform policy provider, if running under EG2 and one
+      // is supplied.
+      policy::ConfigurationPolicyProvider* test_policy_provider =
+          tests_hook::GetOverriddenPlatformPolicyProvider();
+      if (test_policy_provider) {
+        browser_policy_connector_->SetPolicyProviderForTesting(
+            test_policy_provider);
+      }
     }
   }
   return browser_policy_connector_.get();
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 8d07bbf..a2044d5e 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -457,7 +457,7 @@
          autofill::features::kAutofillUseMobileLabelDisambiguation,
          kAutofillUseMobileLabelDisambiguationVariations,
          "AutofillUseMobileLabelDisambiguation")},
-    {"enable-autofill-prune-suggestions",
+    {"autofill-prune-suggestions",
      flag_descriptions::kAutofillPruneSuggestionsName,
      flag_descriptions::kAutofillPruneSuggestionsDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(autofill::features::kAutofillPruneSuggestions)},
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl.h b/ios/chrome/browser/overlays/overlay_presenter_impl.h
index 8c20cff..5c4994ce 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl.h
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl.h
@@ -130,6 +130,9 @@
       override;
   void OverlayPresentationContextDidChangePresentationCapabilities(
       OverlayPresentationContext* presentation_context) override;
+  void OverlayPresentationContextDidMoveToWindow(
+      OverlayPresentationContext* presentation_context,
+      UIWindow* window) override;
 
   // WebStateListObserver:
   void WebStateInsertedAt(WebStateList* web_state_list,
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl.mm b/ios/chrome/browser/overlays/overlay_presenter_impl.mm
index 6bbd426..941a082 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl.mm
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl.mm
@@ -201,12 +201,18 @@
   if (!presentation_context_ || presentation_context_->IsShowingOverlayUI())
     return;
 
-  // No presentation is necessary if there is no active reqeust or the context
-  // is unable to show it.
+  // No presentation is necessary if there is no active reqeust.
   OverlayRequest* request = GetActiveRequest();
-  if (!request || !presentation_context_->CanShowUIForRequest(request))
+  if (!request)
     return;
 
+  // Presentation cannot occur if the context is currently unable to show the UI
+  // for |request|.  Attempt to prepare the presentation context for |request|.
+  if (!presentation_context_->CanShowUIForRequest(request)) {
+    presentation_context_->PrepareToShowOverlayUI(request);
+    return;
+  }
+
   presenting_ = true;
   presented_request_ = request;
 
@@ -407,6 +413,14 @@
     PresentOverlayForActiveRequest();
 }
 
+void OverlayPresenterImpl::OverlayPresentationContextDidMoveToWindow(
+    OverlayPresentationContext* presentation_context,
+    UIWindow* window) {
+  DCHECK_EQ(presentation_context_, presentation_context);
+  if (!presenting_ && window)
+    PresentOverlayForActiveRequest();
+}
+
 #pragma mark - WebStateListObserver
 
 void OverlayPresenterImpl::WebStateInsertedAt(WebStateList* web_state_list,
diff --git a/ios/chrome/browser/overlays/public/overlay_presentation_context.h b/ios/chrome/browser/overlays/public/overlay_presentation_context.h
index 1162f7b..79b8a21 100644
--- a/ios/chrome/browser/overlays/public/overlay_presentation_context.h
+++ b/ios/chrome/browser/overlays/public/overlay_presentation_context.h
@@ -50,6 +50,15 @@
   // Whether overlay UI is currently shown in the context.
   virtual bool IsShowingOverlayUI() const = 0;
 
+  // Instructs the presentation context to prepare itself to show the overlay UI
+  // for |request|.  If successful, updates the context's presentation
+  // capabilities to those required for |request|'s UI.  Has no effect if the
+  // context is incapable of supporting |request|.  For example, the context
+  // cannot support UIPresentationCapabilities::kPresented until it is added to
+  // a window (OverlayPresentationContextObserver can be used to detect window
+  // changes).
+  virtual void PrepareToShowOverlayUI(OverlayRequest* request) = 0;
+
   // Called to show the overlay UI for |request|. |presentation_callback| must
   // be called when the UI is finished being presented. |dismissal_callback|
   // must be stored and called whenever the UI is finished being dismissed for
diff --git a/ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h b/ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h
index 7023d92..618f20a 100644
--- a/ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h
+++ b/ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h
@@ -5,6 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_OBSERVER_H_
 #define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_OBSERVER_H_
 
+#import <UIKit/UIKit.h>
+
 #include "base/observer_list_types.h"
 #import "ios/chrome/browser/overlays/public/overlay_presentation_context.h"
 
@@ -22,6 +24,11 @@
   // Called after |presentation_context|'s activation state changes.
   virtual void OverlayPresentationContextDidChangePresentationCapabilities(
       OverlayPresentationContext* presentation_context) {}
+
+  // Called when |presentation_context| moves to |window|.
+  virtual void OverlayPresentationContextDidMoveToWindow(
+      OverlayPresentationContext* presentation_context,
+      UIWindow* window) {}
 };
 
 #endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_OBSERVER_H_
diff --git a/ios/chrome/browser/overlays/public/test_modality/BUILD.gn b/ios/chrome/browser/overlays/public/test_modality/BUILD.gn
index ebdad47..7948c549 100644
--- a/ios/chrome/browser/overlays/public/test_modality/BUILD.gn
+++ b/ios/chrome/browser/overlays/public/test_modality/BUILD.gn
@@ -9,6 +9,8 @@
     "test_contained_overlay_request_config.mm",
     "test_presented_overlay_request_config.h",
     "test_presented_overlay_request_config.mm",
+    "test_resizing_presented_overlay_request_config.h",
+    "test_resizing_presented_overlay_request_config.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.h b/ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.h
new file mode 100644
index 0000000..c52114b6
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.h
@@ -0,0 +1,29 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_TEST_MODALITY_TEST_RESIZING_PRESENTED_OVERLAY_REQUEST_CONFIG_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_TEST_MODALITY_TEST_RESIZING_PRESENTED_OVERLAY_REQUEST_CONFIG_H_
+
+#import <QuartzCore/QuartzCore.h>
+
+#include "ios/chrome/browser/overlays/public/overlay_request_config.h"
+
+// An OverlayRequestConfig to use in tests for presented UIViewControllers that
+// resize their presentation container views.
+class TestResizingPresentedOverlay
+    : public OverlayRequestConfig<TestResizingPresentedOverlay> {
+ public:
+  ~TestResizingPresentedOverlay() override;
+
+  // The frame of the presented view in window coordinates.
+  const CGRect& frame() { return frame_; }
+
+ private:
+  OVERLAY_USER_DATA_SETUP(TestResizingPresentedOverlay);
+  TestResizingPresentedOverlay(const CGRect& frame);
+
+  const CGRect frame_;
+};
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_TEST_MODALITY_TEST_RESIZING_PRESENTED_OVERLAY_REQUEST_CONFIG_H_
diff --git a/ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.mm b/ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.mm
new file mode 100644
index 0000000..390b4542
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.mm
@@ -0,0 +1,16 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+OVERLAY_USER_DATA_SETUP_IMPL(TestResizingPresentedOverlay);
+
+TestResizingPresentedOverlay::TestResizingPresentedOverlay(const CGRect& frame)
+    : frame_(frame) {}
+
+TestResizingPresentedOverlay::~TestResizingPresentedOverlay() = default;
diff --git a/ios/chrome/browser/overlays/test/BUILD.gn b/ios/chrome/browser/overlays/test/BUILD.gn
index 16756cc..35d7c785 100644
--- a/ios/chrome/browser/overlays/test/BUILD.gn
+++ b/ios/chrome/browser/overlays/test/BUILD.gn
@@ -5,8 +5,8 @@
 source_set("test") {
   testonly = true
   sources = [
-    "fake_overlay_presentation_context.cc",
     "fake_overlay_presentation_context.h",
+    "fake_overlay_presentation_context.mm",
     "fake_overlay_request_callback_installer.cc",
     "fake_overlay_request_callback_installer.h",
     "fake_overlay_request_cancel_handler.cc",
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
index a14bb13..9c9cdd6 100644
--- a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
+++ b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
@@ -48,6 +48,7 @@
       UIPresentationCapabilities capabilities) const override;
   bool CanShowUIForRequest(OverlayRequest* request) const override;
   bool IsShowingOverlayUI() const override;
+  void PrepareToShowOverlayUI(OverlayRequest* request) override;
   void ShowOverlayUI(OverlayRequest* request,
                      OverlayPresentationCallback presentation_callback,
                      OverlayDismissalCallback dismissal_callback) override;
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.mm
similarity index 93%
rename from ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc
rename to ios/chrome/browser/overlays/test/fake_overlay_presentation_context.mm
index 55053f3..f718227b 100644
--- a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc
+++ b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.mm
@@ -9,6 +9,10 @@
 #include "ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h"
 #include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 FakeOverlayPresentationContext::FakeOverlayPresentationContext() = default;
 FakeOverlayPresentationContext::~FakeOverlayPresentationContext() = default;
 
@@ -88,6 +92,9 @@
   return false;
 }
 
+void FakeOverlayPresentationContext::PrepareToShowOverlayUI(
+    OverlayRequest* request) {}
+
 void FakeOverlayPresentationContext::ShowOverlayUI(
     OverlayRequest* request,
     OverlayPresentationCallback presentation_callback,
@@ -102,8 +109,7 @@
   SimulateDismissalForRequest(request, OverlayDismissalReason::kHiding);
 }
 
-void FakeOverlayPresentationContext::CancelOverlayUI(
-    OverlayRequest* request) {
+void FakeOverlayPresentationContext::CancelOverlayUI(OverlayRequest* request) {
   FakeUIState& state = states_[request];
   if (state.presentation_state == PresentationState::kPresented) {
     SimulateDismissalForRequest(request, OverlayDismissalReason::kCancellation);
diff --git a/ios/chrome/browser/policy/BUILD.gn b/ios/chrome/browser/policy/BUILD.gn
index e0abaedc..914ff53 100644
--- a/ios/chrome/browser/policy/BUILD.gn
+++ b/ios/chrome/browser/policy/BUILD.gn
@@ -46,3 +46,61 @@
     "//ios/chrome/browser:utils",
   ]
 }
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "test_platform_policy_provider.cc",
+    "test_platform_policy_provider.h",
+  ]
+
+  deps = [ "//base" ]
+
+  public_deps = [ "//components/policy/core/common:test_support" ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("eg2_tests") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+
+  sources = [
+    "policy_egtest.mm",
+    "policy_egtest_app_interface.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/strings",
+    "//ios/chrome/browser:utils",
+    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing/earl_grey:eg_test_support+eg2",
+    "//ios/third_party/earl_grey2:test_lib",
+    "//ui/base",
+  ]
+
+  libs = [ "UIKit.framework" ]
+}
+
+source_set("eg_app_support+eg2") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "policy_egtest_app_interface.h",
+    "policy_egtest_app_interface.mm",
+  ]
+  deps = [
+    ":policy",
+    "//base",
+    "//components/policy/core/browser",
+    "//components/policy/core/common",
+    "//ios/chrome/browser",
+  ]
+  libs = [ "Foundation.framework" ]
+}
diff --git a/ios/chrome/browser/policy/policy_egtest.mm b/ios/chrome/browser/policy/policy_egtest.mm
new file mode 100644
index 0000000..c858a79c
--- /dev/null
+++ b/ios/chrome/browser/policy/policy_egtest.mm
@@ -0,0 +1,123 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/testing/earl_grey/earl_grey_test.h"
+
+#include <memory>
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/chrome_switches.h"
+#import "ios/chrome/browser/policy/policy_egtest_app_interface.h"
+#include "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#include "ios/chrome/test/earl_grey/chrome_test_case.h"
+#include "ios/testing/earl_grey/app_launch_configuration.h"
+#include "ios/testing/earl_grey/app_launch_manager.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(PolicyEGTestAppInterface)
+
+namespace {
+
+// Returns the value of a given policy, looked up in the current platform policy
+// provider.
+std::unique_ptr<base::Value> GetPlatformPolicy(const std::string& key) {
+  std::string json_representation =
+      base::SysNSStringToUTF8([PolicyEGTestAppInterface
+          valueForPlatformPolicy:base::SysUTF8ToNSString(key)]);
+  JSONStringValueDeserializer deserializer(json_representation);
+  return deserializer.Deserialize(/*error_code=*/nullptr,
+                                  /*error_message=*/nullptr);
+}
+
+}  // namespace
+
+// Test case to verify that enterprise policies are set and respected.
+@interface PolicyTestCase : ChromeTestCase
+@end
+
+@implementation PolicyTestCase
+
+- (AppLaunchConfiguration)appConfigurationForTestCase {
+  // Use commandline args to insert fake policy data into NSUserDefaults. To the
+  // app, this policy data will appear under the
+  // "com.apple.configuration.managed" key.
+  AppLaunchConfiguration config;
+  config.additional_args.push_back(std::string("--") +
+                                   switches::kEnableEnterprisePolicy);
+  config.relaunch_policy = NoForceRelaunchAndResetState;
+  return config;
+}
+
+// Tests that about:policy is available.
+- (void)testAboutPolicy {
+  [ChromeEarlGrey loadURL:GURL("chrome://policy")];
+  [ChromeEarlGrey waitForWebStateContainingText:l10n_util::GetStringUTF8(
+                                                    IDS_POLICY_SHOW_UNSET)];
+}
+
+@end
+
+// Test case that uses the production platform policy provider.
+@interface PolicyPlatformProviderTestCase : ChromeTestCase
+@end
+
+@implementation PolicyPlatformProviderTestCase
+
+- (AppLaunchConfiguration)appConfigurationForTestCase {
+  AppLaunchConfiguration config;
+  config.additional_args.push_back(std::string("--") +
+                                   switches::kEnableEnterprisePolicy);
+
+  // Commandline flags that start with a single "-" are automatically added to
+  // the NSArgumentDomain in NSUserDefaults. Set fake policy data that can be
+  // read by the production platform policy provider.
+  config.additional_args.push_back("-com.apple.configuration.managed");
+  config.additional_args.push_back("{ChromePolicy={"
+                                   "DefaultSearchProviderName=Test;"
+                                   "NotARegisteredPolicy=Unknown;"
+                                   "};}");
+  config.relaunch_policy = NoForceRelaunchAndResetState;
+  return config;
+}
+
+// Tests that policies are properly loaded from NSUserDefaults when using the
+// production platform policy provider.
+
+// Tests the value of a policy that was explicitly set.
+- (void)testProductionPlatformProviderPolicyExplicitlySet {
+  std::unique_ptr<base::Value> searchValue =
+      GetPlatformPolicy("DefaultSearchProviderName");
+  GREYAssertTrue(searchValue && searchValue->is_string(),
+                 @"searchSuggestValue was not of type string");
+  GREYAssertEqual(searchValue->GetString(), "Test",
+                  @"searchSuggestValue had an unexpected value");
+}
+
+// Test the value of a policy that exists in the schema but was not explicitly
+// set.
+- (void)testProductionPlatformProviderPolicyNotSet {
+  std::unique_ptr<base::Value> blocklistValue =
+      GetPlatformPolicy("URLBlacklist");
+  GREYAssertTrue(blocklistValue && blocklistValue->is_none(),
+                 @"blocklistValue was unexpectedly present");
+}
+
+// Test the value of a policy that was set in the configuration but is unknown
+// to the policy system.
+- (void)testProductionPlatformProviderPolicyUnknown {
+  std::unique_ptr<base::Value> unknownValue =
+      GetPlatformPolicy("NotARegisteredPolicy");
+  GREYAssertTrue(unknownValue && unknownValue->is_string(),
+                 @"unknownValue was not of type string");
+  GREYAssertEqual(unknownValue->GetString(), "Unknown",
+                  @"unknownValue had an unexpected value");
+}
+
+@end
diff --git a/ios/chrome/browser/policy/policy_egtest_app_interface.h b/ios/chrome/browser/policy/policy_egtest_app_interface.h
new file mode 100644
index 0000000..3d1febb
--- /dev/null
+++ b/ios/chrome/browser/policy/policy_egtest_app_interface.h
@@ -0,0 +1,19 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_POLICY_POLICY_EGTEST_APP_INTERFACE_H_
+#define IOS_CHROME_BROWSER_POLICY_POLICY_EGTEST_APP_INTERFACE_H_
+
+#import <Foundation/Foundation.h>
+
+@interface PolicyEGTestAppInterface : NSObject
+
+// Returns a JSON-encoded representation of the value for the given |policyKey|.
+// Looks for the policy in the platform policy provider under the CHROME policy
+// namespace.
++ (NSString*)valueForPlatformPolicy:(NSString*)policyKey;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_POLICY_POLICY_EGTEST_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/policy/policy_egtest_app_interface.mm b/ios/chrome/browser/policy/policy_egtest_app_interface.mm
new file mode 100644
index 0000000..004f384
--- /dev/null
+++ b/ios/chrome/browser/policy/policy_egtest_app_interface.mm
@@ -0,0 +1,65 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/policy/policy_egtest_app_interface.h"
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/values.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/policy/browser_policy_connector_ios.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Returns a JSON-encoded string representing the given |pref|. If |pref| is
+// nullptr, returns a string representing a base::Value of type NONE.
+NSString* SerializedValue(const base::Value* value) {
+  base::Value none_value(base::Value::Type::NONE);
+
+  if (!value) {
+    value = &none_value;
+  }
+  DCHECK(value);
+
+  std::string serialized_value;
+  JSONStringValueSerializer serializer(&serialized_value);
+  serializer.Serialize(*value);
+  return base::SysUTF8ToNSString(serialized_value);
+}
+
+}
+
+@implementation PolicyEGTestAppInterface
+
++ (NSString*)valueForPlatformPolicy:(NSString*)policyKey {
+  const std::string key = base::SysNSStringToUTF8(policyKey);
+
+  BrowserPolicyConnectorIOS* connector =
+      GetApplicationContext()->GetBrowserPolicyConnector();
+  if (!connector) {
+    return SerializedValue(nullptr);
+  }
+
+  const policy::ConfigurationPolicyProvider* platformProvider =
+      connector->GetPlatformProvider();
+  if (!platformProvider) {
+    return SerializedValue(nullptr);
+  }
+
+  const policy::PolicyBundle& policyBundle = platformProvider->policies();
+  const policy::PolicyMap& policyMap = policyBundle.Get(
+      policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, ""));
+  return SerializedValue(policyMap.GetValue(key));
+}
+
+@end
diff --git a/ios/chrome/browser/policy/test_platform_policy_provider.cc b/ios/chrome/browser/policy/test_platform_policy_provider.cc
new file mode 100644
index 0000000..c0c11eb5
--- /dev/null
+++ b/ios/chrome/browser/policy/test_platform_policy_provider.cc
@@ -0,0 +1,13 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/policy/test_platform_policy_provider.h"
+
+#include "base/no_destructor.h"
+
+policy::MockConfigurationPolicyProvider* GetTestPlatformPolicyProvider() {
+  static base::NoDestructor<policy::MockConfigurationPolicyProvider> provider;
+  provider->SetAutoRefresh();
+  return provider.get();
+}
diff --git a/ios/chrome/browser/policy/test_platform_policy_provider.h b/ios/chrome/browser/policy/test_platform_policy_provider.h
new file mode 100644
index 0000000..d393eecc
--- /dev/null
+++ b/ios/chrome/browser/policy/test_platform_policy_provider.h
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_POLICY_TEST_PLATFORM_POLICY_PROVIDER_H_
+#define IOS_CHROME_BROWSER_POLICY_TEST_PLATFORM_POLICY_PROVIDER_H_
+
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+
+// Returns a singleton mock that can be installed as the platform policy
+// provider when testing. Subsequent calls to this method will return the same
+// object, which can then be used to update the current set of policies.
+policy::MockConfigurationPolicyProvider* GetTestPlatformPolicyProvider();
+
+#endif  // IOS_CHROME_BROWSER_POLICY_TEST_PLATFORM_POLICY_PROVIDER_H_
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 2fd0d4a..9616e1c 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -2877,10 +2877,6 @@
     [overlays addObject:childOverlayView];
   }
 
-  // The overlay container supports at most one overlay view, either by
-  // presentation or by containment.
-  DCHECK(!presentedOverlayView || !childOverlayView);
-
   return overlays;
 }
 
diff --git a/ios/chrome/browser/ui/download/ar_quick_look_egtest.mm b/ios/chrome/browser/ui/download/ar_quick_look_egtest.mm
index 2444740..598c2e2 100644
--- a/ios/chrome/browser/ui/download/ar_quick_look_egtest.mm
+++ b/ios/chrome/browser/ui/download/ar_quick_look_egtest.mm
@@ -37,6 +37,11 @@
 
 namespace {
 
+#if defined(CHROME_EARL_GREY_2)
+// Use separate timeout for EG2 tests to accomodate for IPC delays.
+const NSTimeInterval kWaitForARPresentationTimeout = 30.0;
+#endif  // CHROME_EARL_GREY_2
+
 // USDZ landing page and download request handler.
 std::unique_ptr<net::test_server::HttpResponse> GetResponse(
     const net::test_server::HttpRequest& request) {
@@ -111,8 +116,9 @@
   // presentation.
   XCUIApplication* app = [[XCUIApplication alloc] init];
   XCUIElement* goodTitle = app.staticTexts[@"good"];
-  GREYAssert([goodTitle waitForExistenceWithTimeout:kWaitForDownloadTimeout],
-             @"AR preview dialog UI was not presented");
+  GREYAssert(
+      [goodTitle waitForExistenceWithTimeout:kWaitForARPresentationTimeout],
+      @"AR preview dialog UI was not presented");
 #else
 #error Must define either CHROME_EARL_GREY_1 or CHROME_EARL_GREY_2.
 #endif
@@ -143,7 +149,7 @@
   XCUIApplication* app = [[XCUIApplication alloc] init];
   XCUIElement* goodTitle = app.staticTexts[@"good"];
   GREYAssertFalse(
-      [goodTitle waitForExistenceWithTimeout:kWaitForDownloadTimeout],
+      [goodTitle waitForExistenceWithTimeout:kWaitForARPresentationTimeout],
       @"AR preview dialog UI was presented");
 #else
 #error Must define either CHROME_EARL_GREY_1 or CHROME_EARL_GREY_2.
@@ -175,7 +181,7 @@
   XCUIApplication* app = [[XCUIApplication alloc] init];
   XCUIElement* goodTitle = app.staticTexts[@"good"];
   GREYAssertFalse(
-      [goodTitle waitForExistenceWithTimeout:kWaitForDownloadTimeout],
+      [goodTitle waitForExistenceWithTimeout:kWaitForARPresentationTimeout],
       @"AR preview dialog UI was presented");
 #else
 #error Must define either CHROME_EARL_GREY_1 or CHROME_EARL_GREY_2.
@@ -207,7 +213,7 @@
   XCUIApplication* app = [[XCUIApplication alloc] init];
   XCUIElement* goodTitle = app.staticTexts[@"good"];
   GREYAssertFalse(
-      [goodTitle waitForExistenceWithTimeout:kWaitForDownloadTimeout],
+      [goodTitle waitForExistenceWithTimeout:kWaitForARPresentationTimeout],
       @"AR preview dialog UI was presented");
 #else
 #error Must define either CHROME_EARL_GREY_1 or CHROME_EARL_GREY_2.
diff --git a/ios/chrome/browser/ui/overlays/BUILD.gn b/ios/chrome/browser/ui/overlays/BUILD.gn
index 7f757b0..67f7f5a 100644
--- a/ios/chrome/browser/ui/overlays/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/BUILD.gn
@@ -8,13 +8,17 @@
     "overlay_coordinator_factory.h",
   ]
   sources = [
+    "overlay_container_coordinator+initialization.h",
     "overlay_container_coordinator.mm",
     "overlay_coordinator_factory+initialization.h",
     "overlay_coordinator_factory.mm",
+    "overlay_presentation_context_coordinator.h",
+    "overlay_presentation_context_coordinator.mm",
     "overlay_presentation_context_fullscreen_disabler.h",
     "overlay_presentation_context_fullscreen_disabler.mm",
     "overlay_presentation_context_impl.h",
     "overlay_presentation_context_impl.mm",
+    "overlay_presentation_context_impl_delegate.h",
     "overlay_request_ui_state.h",
     "overlay_request_ui_state.mm",
   ]
@@ -28,8 +32,8 @@
   ]
 
   deps = [
-    ":container_ui",
     ":coordinators",
+    ":ui",
     "//base",
     "//ios/chrome/browser/main",
     "//ios/chrome/browser/overlays",
@@ -42,15 +46,29 @@
   ]
 }
 
-source_set("container_ui") {
+source_set("ui") {
   sources = [
     "overlay_container_view_controller.h",
     "overlay_container_view_controller.mm",
+    "overlay_presentation_context_view_controller.h",
+    "overlay_presentation_context_view_controller.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
 
-  deps = [ "//base" ]
+  deps = [
+    ":presentation_controller",
+    "//base",
+  ]
+}
+
+source_set("presentation_controller") {
+  sources = [
+    "overlay_presentation_controller.h",
+    "overlay_presentation_controller.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
 }
 
 source_set("coordinators") {
@@ -75,6 +93,8 @@
 
 source_set("util") {
   sources = [
+    "overlay_presentation_context_util.h",
+    "overlay_presentation_context_util.mm",
     "overlay_request_mediator_util.h",
     "overlay_request_mediator_util.mm",
   ]
@@ -91,7 +111,12 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "overlay_container_coordinator_unittest.mm",
+    "overlay_container_view_controller_unittest.mm",
+    "overlay_presentation_context_coordinator_unittest.mm",
     "overlay_presentation_context_fullscreen_disabler_unittest.mm",
+    "overlay_presentation_context_impl_unittest.mm",
+    "overlay_presentation_context_view_controller_unittest.mm",
     "overlay_request_mediator_unittest.mm",
     "overlay_request_mediator_util_unittest.mm",
     "overlay_request_ui_state_unittest.mm",
@@ -102,14 +127,17 @@
   deps = [
     ":coordinators",
     ":overlays",
+    ":ui",
     ":util",
     "//base/test:test_support",
     "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/test_modality",
     "//ios/chrome/browser/overlays/public/web_content_area",
     "//ios/chrome/browser/overlays/test",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/browser/ui/overlays/test",
+    "//ios/chrome/browser/ui/overlays/test_modality",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/test:test_support",
     "//ios/web/public/test",
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_coordinator+initialization.h b/ios/chrome/browser/ui/overlays/overlay_container_coordinator+initialization.h
new file mode 100644
index 0000000..748bcf7
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_container_coordinator+initialization.h
@@ -0,0 +1,27 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_CONTAINER_COORDINATOR_INITIALIZATION_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_CONTAINER_COORDINATOR_INITIALIZATION_H_
+
+#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
+
+class OverlayPresentationContextImpl;
+
+// TODO(crbug.com/1056837): This initializer is only necessary to prevent the
+// test modality code from getting compiled into releases, and can be removed
+// once OverlayModality is converted from an enum to a class.
+@interface OverlayContainerCoordinator (Initialization)
+
+// Initializer for a coordinator that manages the base UIViewController for
+// overlay UI implemented using child UIViewControllers for |context| at
+// |modality|.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                       presentationContext:
+                           (OverlayPresentationContextImpl*)context;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_CONTAINER_COORDINATOR_INITIALIZATION_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_coordinator.h b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.h
index 5533d57..89a5e5d 100644
--- a/ios/chrome/browser/ui/overlays/overlay_container_coordinator.h
+++ b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.h
@@ -10,17 +10,20 @@
 #include "ios/chrome/browser/overlays/public/overlay_modality.h"
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 
-// Coordinator that manages displaying of UI for OverlayRequests.  An instance
-// of this coordinator should be created for each Browser at every
-// OverlayModality.
+// Coordinator that manages the container view in which overlay UI is displayed.
+// The coordinator's view controller should be used to display overlay UI
+// implemented using child UIViewControllers.
 @interface OverlayContainerCoordinator : ChromeCoordinator
 
 // Initializer for an overlay container that presents overlay for |browser| at
 // |modality|.
+// TODO(crbug.com/1056837): This is not marked as NS_DESIGNATED_INITIALIZER to
+// facilitate the creation of OverlayContainerCoordinators for
+// OverlayModality::kTesting.  Annotate as NS_DESIGNATED_INITIALIZER once
+// OverlayModality is converted from an enum type.
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
-                                  modality:(OverlayModality)modality
-    NS_DESIGNATED_INITIALIZER;
+                                  modality:(OverlayModality)modality;
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                               browserState:(ChromeBrowserState*)browserState
     NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm
index 59d774e..9cae1e9 100644
--- a/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm
@@ -3,15 +3,17 @@
 // found in the LICENSE file.
 
 #import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator+initialization.h"
 
-#include <map>
 #include <memory>
 
 #include "base/logging.h"
 #include "base/scoped_observer.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/overlays/overlay_container_view_controller.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.h"
 #import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_delegate.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -19,15 +21,18 @@
 #endif
 
 @interface OverlayContainerCoordinator () <
-    OverlayContainerViewControllerDelegate>
+    OverlayContainerViewControllerDelegate,
+    OverlayPresentationContextImplDelegate>
 // Whether the coordinator is started.
 @property(nonatomic, assign, getter=isStarted) BOOL started;
 // The presentation context used by OverlayPresenter to drive presentation for
 // this container.
-@property(nonatomic, readonly)
+@property(nonatomic, assign, readonly)
     OverlayPresentationContextImpl* presentationContext;
-// The modality being handled by the container.
-@property(nonatomic, assign) OverlayModality modality;
+// The coordinator that manages the base view for overlay UI displayed using
+// UIViewController presentation.
+@property(nonatomic, strong)
+    OverlayPresentationContextCoordinator* presentationContextCoordinator;
 @end
 
 @implementation OverlayContainerCoordinator
@@ -35,17 +40,11 @@
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
                                   modality:(OverlayModality)modality {
-  if (self = [super initWithBaseViewController:viewController
-                                       browser:browser]) {
-    OverlayPresentationContextImpl::Container::CreateForUserData(browser,
-                                                                 browser);
-    _presentationContext =
-        OverlayPresentationContextImpl::Container::FromUserData(browser)
-            ->PresentationContextForModality(modality);
-    DCHECK(_presentationContext);
-    _modality = modality;
-  }
-  return self;
+  OverlayPresentationContextImpl* context =
+      OverlayPresentationContextImpl::FromBrowser(browser, modality);
+  return [self initWithBaseViewController:viewController
+                                  browser:browser
+                      presentationContext:context];
 }
 
 #pragma mark - ChromeCoordinator
@@ -54,27 +53,39 @@
   if (self.started)
     return;
   self.started = YES;
-  // Create the container view controller and add it to the base view
-  // controller.
+  // Create the container view controller.
   OverlayContainerViewController* viewController =
       [[OverlayContainerViewController alloc] init];
   viewController.definesPresentationContext = YES;
   viewController.delegate = self;
   _viewController = viewController;
+  // Set the coordinator as the delegate for the presentation context.
+  self.presentationContext->SetDelegate(self);
+  // Create the presentation context coordinator.  It is started when the
+  // container view controller is finished being added to the window, as its
+  // presentation would no-op if started too early.
+  self.presentationContextCoordinator =
+      [[OverlayPresentationContextCoordinator alloc]
+          initWithBaseViewController:_viewController
+                             browser:self.browser
+                 presentationContext:self.presentationContext];
+  // Add the container view controller to the hierarchy.
   UIView* containerView = _viewController.view;
   containerView.translatesAutoresizingMaskIntoConstraints = NO;
   [self.baseViewController addChildViewController:_viewController];
   [self.baseViewController.view addSubview:containerView];
   AddSameConstraints(containerView, self.baseViewController.view);
   [_viewController didMoveToParentViewController:self.baseViewController];
-  self.presentationContext->SetCoordinator(self);
 }
 
 - (void)stop {
   if (!self.started)
     return;
   self.started = NO;
-  self.presentationContext->SetCoordinator(nil);
+  self.presentationContext->SetDelegate(nil);
+  // Clean up the presentation context coordinator.
+  [self.presentationContextCoordinator stop];
+  self.presentationContextCoordinator = nil;
   // Remove the container view and reset the view controller.
   [_viewController willMoveToParentViewController:nil];
   [_viewController.view removeFromSuperview];
@@ -87,7 +98,53 @@
 - (void)containerViewController:
             (OverlayContainerViewController*)containerViewController
                 didMoveToWindow:(UIWindow*)window {
-  self.presentationContext->WindowDidChange();
+  self.presentationContext->SetWindow(window);
+}
+
+#pragma mark - OverlayPresentationContextImplDelegate
+
+- (void)updatePresentationContext:(OverlayPresentationContextImpl*)context
+      forPresentationCapabilities:
+          (OverlayPresentationContext::UIPresentationCapabilities)capabilities {
+  DCHECK_EQ(self.presentationContext, context);
+  DCHECK(self.started);
+  DCHECK(self.viewController);
+
+  // Update the context's container view controller.
+  bool needsContainer =
+      capabilities &
+      OverlayPresentationContext::UIPresentationCapabilities::kContained;
+  self.presentationContext->SetContainerViewController(
+      needsContainer ? self.viewController : nil);
+
+  // Start or stop the presentation context coordinator depending on whether
+  // it is required to support |capabilities|.
+  if (capabilities &
+      OverlayPresentationContext::UIPresentationCapabilities::kPresented) {
+    // The coordinator cannot be started if its base UIViewController doesn't
+    // belong to a window.  The context will re-request the kPresented
+    // capability when the view moves to a window.
+    if (self.viewController.view.window)
+      [self.presentationContextCoordinator start];
+  } else {
+    [self.presentationContextCoordinator stop];
+  }
+}
+
+@end
+
+@implementation OverlayContainerCoordinator (Initialization)
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                       presentationContext:
+                           (OverlayPresentationContextImpl*)context {
+  if (self = [super initWithBaseViewController:viewController
+                                       browser:browser]) {
+    DCHECK(context);
+    _presentationContext = context;
+  }
+  return self;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_coordinator_unittest.mm b/ios/chrome/browser/ui/overlays/overlay_container_coordinator_unittest.mm
new file mode 100644
index 0000000..99ce990
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_container_coordinator_unittest.mm
@@ -0,0 +1,111 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator+initialization.h"
+
+#import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_contained_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_presented_overlay_request_config.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_util.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.h"
+#include "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h"
+#import "ios/chrome/browser/ui/overlays/test/test_overlay_presentation_context.h"
+#include "ios/chrome/test/scoped_key_window.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
+
+using base::test::ios::WaitUntilConditionOrTimeout;
+using base::test::ios::kWaitForUIElementTimeout;
+
+// Test fixture for OverlayContainerCoordinator.
+class OverlayContainerCoordinatorTest : public PlatformTest {
+ public:
+  OverlayContainerCoordinatorTest()
+      : browser_(std::make_unique<TestBrowser>()),
+        context_(browser_.get()),
+        root_view_controller_([[UIViewController alloc] init]),
+        coordinator_([[OverlayContainerCoordinator alloc]
+            initWithBaseViewController:root_view_controller_
+                               browser:browser_.get()
+                   presentationContext:&context_]) {
+    root_view_controller_.definesPresentationContext = YES;
+    scoped_window_.Get().rootViewController = root_view_controller_;
+  }
+  ~OverlayContainerCoordinatorTest() override {
+    // The browser needs to be destroyed before |context_| so that observers
+    // can be unhooked due to BrowserDestroyed().  This is not a problem for
+    // non-test OverlayPresentationContextImpls since they're owned by the
+    // Browser and get destroyed after BrowserDestroyed() is called.
+    browser_ = nullptr;
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  std::unique_ptr<TestBrowser> browser_;
+  TestOverlayPresentationContext context_;
+  ScopedKeyWindow scoped_window_;
+  UIViewController* root_view_controller_ = nil;
+  OverlayContainerCoordinator* coordinator_ = nil;
+};
+
+// Tests that the coordinator updates its OverlayPresentationContext's
+// presentation capabilities when started and stopped.
+TEST_F(OverlayContainerCoordinatorTest, UpdatePresentationCapabilities) {
+  ASSERT_FALSE(OverlayPresentationContextSupportsContainedUI(&context_));
+
+  // Start the coordinator and verify that the presentation context begins
+  // supporting contained overlay UI.
+  [coordinator_ start];
+  std::unique_ptr<OverlayRequest> request =
+      OverlayRequest::CreateWithConfig<TestContainedOverlay>();
+  context_.PrepareToShowOverlayUI(request.get());
+  EXPECT_TRUE(OverlayPresentationContextSupportsContainedUI(&context_));
+
+  // Stop the coordinator and verify that the presentation context no longer
+  // supports contained overlay UI.
+  [coordinator_ stop];
+  EXPECT_FALSE(OverlayPresentationContextSupportsContainedUI(&context_));
+}
+
+// Tests that the coordinator sets up the presentation context upon being added
+// to the window.
+TEST_F(OverlayContainerCoordinatorTest, PresentationContextSetup) {
+  ASSERT_FALSE(OverlayPresentationContextSupportsPresentedUI(&context_));
+
+  // Start the coordinator.  This will add it to the key window, triggering the
+  // presentation of the UIViewController that will be used as the base for
+  // overlay UI implemented using presentation.
+  [coordinator_ start];
+  std::unique_ptr<OverlayRequest> request =
+      OverlayRequest::CreateWithConfig<TestPresentedOverlay>();
+  context_.PrepareToShowOverlayUI(request.get());
+  UIViewController* presented_view_controller =
+      coordinator_.viewController.presentedViewController;
+  ASSERT_TRUE(presented_view_controller);
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return presented_view_controller.presentingViewController &&
+           !presented_view_controller.beingPresented;
+  }));
+
+  // Once |presented_view_controller| is finished being presented, it should
+  // update the presentation capabilities to allow presented overlay UI.
+  EXPECT_TRUE(OverlayPresentationContextSupportsPresentedUI(&context_));
+
+  // Stop the container coordinator and wait for |presented_view_controller| to
+  // finish being dismissed, verifying that the context no longer supports
+  // presented overlay UI.
+  [coordinator_ stop];
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return !presented_view_controller.presentingViewController;
+  }));
+  EXPECT_FALSE(OverlayPresentationContextSupportsPresentedUI(&context_));
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_view_controller.mm b/ios/chrome/browser/ui/overlays/overlay_container_view_controller.mm
index 75ab844..62aa3bb 100644
--- a/ios/chrome/browser/ui/overlays/overlay_container_view_controller.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_container_view_controller.mm
@@ -22,6 +22,17 @@
   return hitView == self ? nil : hitView;
 }
 
+- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
+  // Only register touches that land inside a subview.  Otherwise, return NO to
+  // allow touches to continue to the underlying UI.
+  for (UIView* subview in self.subviews) {
+    CGPoint adjustedPoint = [subview convertPoint:point fromView:self];
+    if ([subview pointInside:adjustedPoint withEvent:event])
+      return YES;
+  }
+  return NO;
+}
+
 - (void)didMoveToWindow {
   [super didMoveToWindow];
   [self.viewController.delegate containerViewController:self.viewController
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_view_controller_unittest.mm b/ios/chrome/browser/ui/overlays/overlay_container_view_controller_unittest.mm
new file mode 100644
index 0000000..6b78cf1
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_container_view_controller_unittest.mm
@@ -0,0 +1,93 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_container_view_controller.h"
+
+#import "ios/chrome/browser/main/test_browser.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_presented_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
+#include "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h"
+#import "ios/chrome/browser/ui/overlays/test_modality/test_presented_overlay_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.h"
+#include "ios/chrome/test/scoped_key_window.h"
+#include "ios/web/public/test/web_task_environment.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for OverlayContainerViewController.
+class OverlayContainerViewControllerTest : public PlatformTest {
+ public:
+  OverlayContainerViewControllerTest()
+      : root_view_controller_([[UIViewController alloc] init]),
+        delegate_(OCMStrictProtocolMock(
+            @protocol(OverlayContainerViewControllerDelegate))),
+        view_controller_([[OverlayContainerViewController alloc] init]) {
+    scoped_window_.Get().rootViewController = root_view_controller_;
+    view_controller_.delegate = delegate_;
+  }
+  ~OverlayContainerViewControllerTest() override {
+    EXPECT_OCMOCK_VERIFY(delegate_);
+  }
+
+ protected:
+  ScopedKeyWindow scoped_window_;
+  UIViewController* root_view_controller_ = nil;
+  id<OverlayContainerViewControllerDelegate> delegate_ = nil;
+  OverlayContainerViewController* view_controller_ = nil;
+};
+
+// Verifies that the view controller notifies its delegate when its view's
+// window is changed.
+TEST_F(OverlayContainerViewControllerTest, MoveToWindow) {
+  OCMExpect([delegate_ containerViewController:view_controller_
+                               didMoveToWindow:scoped_window_.Get()]);
+  [root_view_controller_.view addSubview:view_controller_.view];
+
+  OCMExpect([delegate_ containerViewController:view_controller_
+                               didMoveToWindow:nil]);
+  [view_controller_.view removeFromSuperview];
+}
+
+// Verifies that the container view ignores touches that fall outside of any
+// subviews.
+TEST_F(OverlayContainerViewControllerTest, TouchHandling) {
+  // The container view will be laid out with |frame|.  A subview will be added
+  // to the view and laid out |subview_inset| from the edges of the view.
+  CGRect frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
+  CGFloat subview_inset = 25.0;
+  CGRect subview_frame = CGRectInset(frame, subview_inset, subview_inset);
+
+  // |center| is the center of the container view, which will land in the center
+  // of the added subview.  |corner| is a point halfway between the container
+  // view's origin and the subview's origin.
+  CGPoint center = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
+  CGPoint corner = CGPointMake(subview_inset / 2.0, subview_inset / 2.0);
+
+  // Set up the view hierarchy using the calculated frames.
+  UIView* view = view_controller_.view;
+  view.frame = frame;
+  UIView* subview = [[UIView alloc] initWithFrame:subview_frame];
+  [view addSubview:subview];
+
+  // Verify that touches are ignored when they fall outside of any subview.
+  EXPECT_FALSE([view hitTest:corner withEvent:nil]);
+  EXPECT_FALSE([view pointInside:corner withEvent:nil]);
+
+  // Verify that touches are handled and forwarded to |subview| when falling
+  // inside |subview|'s bounds.
+  EXPECT_EQ([view hitTest:center withEvent:nil], subview);
+  EXPECT_TRUE([view pointInside:center withEvent:nil]);
+
+  // Remove the subview and verify that touches in the center are ignored.
+  [subview removeFromSuperview];
+  EXPECT_FALSE([view hitTest:center withEvent:nil]);
+  EXPECT_FALSE([view pointInside:center withEvent:nil]);
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.h b/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.h
new file mode 100644
index 0000000..d73f740f0
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.h
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#include "ios/chrome/browser/overlays/public/overlay_modality.h"
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
+
+class OverlayPresentationContextImpl;
+
+// Coordinator whose presentation context is used for overlay presentation.
+// Manages a UIViewController that is presented over the overlay container's
+// UIViewController presentation context.  This is necessary due to UIKit's
+// custom UIViewController presentation implementation.  The only way to
+// present non-modally with UIModalPresentationStyleCustom is by using a custom
+// UIPresentationController whose |shouldPresentInFullscreen| property is NO.
+// When such a presentation controller is provided, UIKit traverses the view
+// hierarchy to find the nearest presented UIViewController.  By presenting a
+// UIViewController over a child UIViewController whose
+// |definesPresentationContext| property is YES, this coordinator inserts a
+// UIKit presentation context upon which custom presentation can occur.
+@interface OverlayPresentationContextCoordinator : ChromeCoordinator
+
+// Initializer for an overlay container that presents overlay for |browser| at
+// |modality|.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                       presentationContext:
+                           (OverlayPresentationContextImpl*)context
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:(ChromeBrowserState*)browserState
+    NS_UNAVAILABLE;
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+// The view controller whose presentation context is used to present overlays.
+@property(nonatomic, readonly) UIViewController* viewController;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.mm
new file mode 100644
index 0000000..3442db5c9
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.mm
@@ -0,0 +1,83 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.h"
+
+#include <memory>
+
+#import "base/ios/block_types.h"
+#include "base/logging.h"
+#include "base/scoped_observer.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface OverlayPresentationContextCoordinator ()
+// Whether the coordinator is started.
+@property(nonatomic, assign, getter=isStarted) BOOL started;
+// The presentation context used by OverlayPresenter to drive presentation for
+// this container.
+@property(nonatomic, assign, readonly)
+    OverlayPresentationContextImpl* presentationContext;
+@end
+
+@implementation OverlayPresentationContextCoordinator
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                       presentationContext:
+                           (OverlayPresentationContextImpl*)context {
+  if (self = [super initWithBaseViewController:viewController
+                                       browser:browser]) {
+    DCHECK(context);
+    _presentationContext = context;
+  }
+  return self;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  if (self.started)
+    return;
+  self.started = YES;
+  // Create the presentation context view controller and present it over the
+  // base view's presentation context.
+  _viewController = [[OverlayPresentationContextViewController alloc] init];
+  _viewController.definesPresentationContext = YES;
+  _viewController.modalPresentationStyle =
+      UIModalPresentationOverCurrentContext;
+  DCHECK(self.baseViewController.definesPresentationContext);
+  // Supply the view controller to the presentation context upon completion of
+  // the presentation.  If the coordinator is deallocated before the completion
+  // block is called, then the presentation context's view controller will
+  // remain null, which is correct behavior since the coordinator is unable to
+  // support overlay UI presentation.
+  __weak __typeof(self) weakSelf = self;
+  ProceduralBlock completion = ^{
+    if (!weakSelf)
+      return;
+    __typeof(self) strongSelf = weakSelf;
+    strongSelf.presentationContext->SetPresentationContextViewController(
+        strongSelf.viewController);
+  };
+  [self.baseViewController presentViewController:_viewController
+                                        animated:NO
+                                      completion:completion];
+}
+
+- (void)stop {
+  if (!self.started)
+    return;
+  self.started = NO;
+  self.presentationContext->SetPresentationContextViewController(nil);
+  // Dismiss the presentation context view controller.
+  [self.baseViewController dismissViewControllerAnimated:NO completion:nil];
+  _viewController = nil;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator_unittest.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator_unittest.mm
new file mode 100644
index 0000000..07f8599
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator_unittest.mm
@@ -0,0 +1,87 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.h"
+
+#import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_presented_overlay_request_config.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_util.h"
+#include "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h"
+#import "ios/chrome/browser/ui/overlays/test/test_overlay_presentation_context.h"
+#include "ios/chrome/test/scoped_key_window.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
+
+using base::test::ios::WaitUntilConditionOrTimeout;
+using base::test::ios::kWaitForUIElementTimeout;
+
+// Test fixture for OverlayPresentationContextCoordinator.
+class OverlayPresentationContextCoordinatorTest : public PlatformTest {
+ public:
+  OverlayPresentationContextCoordinatorTest()
+      : browser_(std::make_unique<TestBrowser>()),
+        context_(browser_.get()),
+        root_view_controller_([[UIViewController alloc] init]),
+        coordinator_([[OverlayPresentationContextCoordinator alloc]
+            initWithBaseViewController:root_view_controller_
+                               browser:browser_.get()
+                   presentationContext:&context_]) {
+    root_view_controller_.definesPresentationContext = YES;
+    scoped_window_.Get().rootViewController = root_view_controller_;
+  }
+  ~OverlayPresentationContextCoordinatorTest() override {
+    // The browser needs to be destroyed before |context_| so that observers
+    // can be unhooked due to BrowserDestroyed().  This is not a problem for
+    // non-test OverlayPresentationContextImpls since they're owned by the
+    // Browser and get destroyed after BrowserDestroyed() is called.
+    browser_ = nullptr;
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  std::unique_ptr<TestBrowser> browser_;
+  TestOverlayPresentationContext context_;
+  ScopedKeyWindow scoped_window_;
+  UIViewController* root_view_controller_ = nil;
+  OverlayPresentationContextCoordinator* coordinator_ = nil;
+};
+
+// Tests that the coordinator updates its OverlayPresentationContext's
+// presentation capabilities when started and stopped.
+TEST_F(OverlayPresentationContextCoordinatorTest,
+       UpdatePresentationCapabilities) {
+  ASSERT_FALSE(OverlayPresentationContextSupportsPresentedUI(&context_));
+
+  // Start the coordinator and wait until the view is finished being presented.
+  // This is necessary because UIViewController presentation is asynchronous,
+  // even when performed without animation.
+  [coordinator_ start];
+  UIViewController* view_controller = coordinator_.viewController;
+  ASSERT_TRUE(view_controller);
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return view_controller.presentingViewController &&
+           !view_controller.beingPresented;
+  }));
+
+  // Verify that the presentation context supports presentation.
+  EXPECT_TRUE(OverlayPresentationContextSupportsPresentedUI(&context_));
+
+  // Stop the coordinator and wait until the view is finished being dismissed.
+  // This is necessary because UIViewController presentation is asynchronous,
+  // even when performed without animation.
+  [coordinator_ stop];
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return !view_controller.presentingViewController;
+  }));
+
+  // Verify that the presentation context no longer supports presentation.
+  EXPECT_FALSE(OverlayPresentationContextSupportsPresentedUI(&context_));
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
index a6d6902..6b0475df 100644
--- a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
@@ -5,6 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_IMPL_H_
 #define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_IMPL_H_
 
+#import <UIKit/UIKit.h>
+
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #import "ios/chrome/browser/main/browser_observer.h"
@@ -17,17 +19,23 @@
 #import "ios/chrome/browser/ui/overlays/overlay_request_ui_state.h"
 
 @class OverlayRequestCoordinatorFactory;
-@class OverlayContainerCoordinator;
+@class OverlayPresentationContextCoordinator;
+@protocol OverlayPresentationContextImplDelegate;
 
 // Implementation of OverlayPresentationContext.  An instance of this class
 // exists for every OverlayModality for each Browser.  This delegate is scoped
 // to the Browser because it needs to store state even when a Browser's UI is
-// not on screen.  When a Browser's UI is shown, the OverlayContainerCoordinator
-// for each of its OverlayModalities will supply itself to the delegate, which
-// will then present the UI using the container coordinator's presentation
-// context.
+// not on screen.  When a Browser's UI is shown, the view controllers for each
+// modality are set up.  When the presentation context is supplied with a
+// container or presentation context UIViewController, its presentation
+// capabilities are updated and supported overlay UI can begin being shown in
+// the context.
 class OverlayPresentationContextImpl : public OverlayPresentationContext {
  public:
+  // Returns the OverlayPresentationContextImpl for |browser| at |modality|.
+  static OverlayPresentationContextImpl* FromBrowser(Browser* browser,
+                                                     OverlayModality modality);
+
   ~OverlayPresentationContextImpl() override;
 
   // Container that stores the UI delegate for each modality.  Usage example:
@@ -51,14 +59,21 @@
         ui_delegates_;
   };
 
-  // The OverlayContainerCoordinator is used to present the overlay UI at the
-  // correct modality in the app.  Should only be set when the coordinator is
-  // started.
-  OverlayContainerCoordinator* coordinator() const { return coordinator_; }
-  void SetCoordinator(OverlayContainerCoordinator* coordinator);
+  // The context's delegate.
+  void SetDelegate(id<OverlayPresentationContextImplDelegate> delegate);
 
-  // Called when |coordinator_|'s view was moved to a new window.
-  void WindowDidChange();
+  // The window in which overlay UI will be presented.
+  void SetWindow(UIWindow* window);
+
+  // The UIViewController used for overlays displayed using child
+  // UIViewControllers.  Setting to a new value updates the presentation
+  // capabilities to include kContained.
+  void SetContainerViewController(UIViewController* view_controller);
+
+  // The UIViewController used for overlays displayed using presented
+  // UIViewControllers.  Setting to a new value updates the presentation
+  // capabilities to include kPresented.
+  void SetPresentationContextViewController(UIViewController* view_controller);
 
   // OverlayPresentationContext:
   void AddObserver(OverlayPresentationContextObserver* observer) override;
@@ -69,6 +84,7 @@
       UIPresentationCapabilities capabilities) const override;
   bool CanShowUIForRequest(OverlayRequest* request) const override;
   bool IsShowingOverlayUI() const override;
+  void PrepareToShowOverlayUI(OverlayRequest* request) override;
   void ShowOverlayUI(OverlayRequest* request,
                      OverlayPresentationCallback presentation_callback,
                      OverlayDismissalCallback dismissal_callback) override;
@@ -88,12 +104,27 @@
   // present the UI for |request|.
   void SetRequest(OverlayRequest* request);
 
+  // Returns whether |request| uses a child UIViewController.  If false, the
+  // request's UI is shown using presentation.
+  bool RequestUsesChildViewController(OverlayRequest* request) const;
+
+  // Returns the base view controller to use for |request|'s coordinator, or
+  // nullptr if the base has not been provided.  |container_view_controller_| is
+  // returned if |request| uses a child UIViewController, and
+  // |presentation_context_view_controller_| is returned if |request| uses
+  // UIViewController presentation.
+  UIViewController* GetBaseViewController(OverlayRequest* request) const;
+
   // Returns the UI state for |request|.
   OverlayRequestUIState* GetRequestUIState(OverlayRequest* request);
 
-  // Updates |coordinator_| and |presentation_capabilities_| using
-  // |coordinator|.
-  void UpdateForCoordinator(OverlayContainerCoordinator* coordinator);
+  // Returns the presentation capabilities required to show |request|.
+  UIPresentationCapabilities GetRequiredPresentationCapabilities(
+      OverlayRequest* request) const;
+
+  // Updates the presentation capabilities based on the provided
+  // UIViewControllers.
+  void UpdatePresentationCapabilities();
 
   // Shows the UI for the presented request using the container coordinator.
   void ShowUIForPresentedRequest();
@@ -149,15 +180,22 @@
   // The coordinator factory that provides the UI for the overlays at this
   // modality.
   OverlayRequestCoordinatorFactory* coordinator_factory_ = nil;
-  // The coordinator responsible for presenting the UI delegate's UI.
-  OverlayContainerCoordinator* coordinator_ = nil;
+  // The context's delegate.
+  __weak id<OverlayPresentationContextImplDelegate> delegate_ = nil;
+  // The window in which overlay UI will be presented.
+  UIWindow* window_ = nil;
+  // The UIViewController used as the base for overlays UI displayed using child
+  // UIViewControllers.
+  UIViewController* container_view_controller_ = nil;
+  // The UIViewController used as the base for overlays displayed using
+  // presented UIViewControllers.
+  UIViewController* presentation_context_view_controller_ = nil;
   // The presentation capabilities of |coordinator_|'s view controller.
   UIPresentationCapabilities presentation_capabilities_ =
       UIPresentationCapabilities::kNone;
-  // The request that is currently presented by |presenter_|.  The UI for this
-  // request might not yet be visible if no OverlayContainerCoordinator has been
-  // provided.  When a new request is presented, the UI state for the request
-  // will be added to |states_|.
+  // The request that is currently presented by |presenter_|.  When a new
+  // request is presented, the UI state for the request will be added to
+  // |states_|.
   OverlayRequest* request_ = nullptr;
   // Map storing the UI state for each OverlayRequest.
   std::map<OverlayRequest*, std::unique_ptr<OverlayRequestUIState>> states_;
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
index 7af1b67..6ee79f90b 100644
--- a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
@@ -12,13 +12,24 @@
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h"
 #import "ios/chrome/browser/overlays/public/overlay_presenter.h"
-#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
 #import "ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_delegate.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+// static
+OverlayPresentationContextImpl* OverlayPresentationContextImpl::FromBrowser(
+    Browser* browser,
+    OverlayModality modality) {
+  OverlayPresentationContextImpl::Container::CreateForUserData(browser,
+                                                               browser);
+  return OverlayPresentationContextImpl::Container::FromUserData(browser)
+      ->PresentationContextForModality(modality);
+}
+
 #pragma mark - OverlayPresentationContextImpl::Container
 
 OVERLAY_USER_DATA_SETUP_IMPL(OverlayPresentationContextImpl::Container);
@@ -71,19 +82,49 @@
 
 #pragma mark Public
 
-void OverlayPresentationContextImpl::SetCoordinator(
-    OverlayContainerCoordinator* coordinator) {
-  if (coordinator_ == coordinator)
+void OverlayPresentationContextImpl::SetDelegate(
+    id<OverlayPresentationContextImplDelegate> delegate) {
+  if (delegate_ == delegate)
     return;
+  // Reset the presentation capabilities.
+  container_view_controller_ = nil;
+  presentation_context_view_controller_ = nil;
+  UpdatePresentationCapabilities();
 
-  UpdateForCoordinator(coordinator);
+  delegate_ = delegate;
 
-  // The new coordinator should be started before provided to the UI delegate.
-  DCHECK(!coordinator_ || coordinator_.viewController);
+  // The context is only capable of presenting once the delegate is provided.
+  presenter_->SetPresentationContext(delegate_ ? this : nullptr);
 }
 
-void OverlayPresentationContextImpl::WindowDidChange() {
-  UpdateForCoordinator(coordinator_);
+void OverlayPresentationContextImpl::SetWindow(UIWindow* window) {
+  if (window_ == window)
+    return;
+  window_ = window;
+  for (auto& observer : observers_) {
+    observer.OverlayPresentationContextDidMoveToWindow(this, window_);
+  }
+}
+
+void OverlayPresentationContextImpl::SetContainerViewController(
+    UIViewController* view_controller) {
+  if (container_view_controller_ == view_controller)
+    return;
+  container_view_controller_ = view_controller;
+  UpdatePresentationCapabilities();
+}
+
+void OverlayPresentationContextImpl::SetPresentationContextViewController(
+    UIViewController* view_controller) {
+  if (presentation_context_view_controller_ == view_controller)
+    return;
+  presentation_context_view_controller_ = view_controller;
+  // |view_controller| should not be provided to the context until it is fully
+  // presented in a window.
+  DCHECK(!view_controller ||
+         (view_controller.presentationController.containerView.window &&
+          !view_controller.beingPresented && !view_controller.beingDismissed));
+  UpdatePresentationCapabilities();
 }
 
 #pragma mark OverlayPresentationContext
@@ -106,11 +147,8 @@
 bool OverlayPresentationContextImpl::CanShowUIForRequest(
     OverlayRequest* request,
     UIPresentationCapabilities capabilities) const {
-  BOOL uses_child_view_controller = [coordinator_factory_
-      coordinatorForRequestUsesChildViewController:request];
   UIPresentationCapabilities required_capability =
-      uses_child_view_controller ? UIPresentationCapabilities::kContained
-                                 : UIPresentationCapabilities::kPresented;
+      GetRequiredPresentationCapabilities(request);
   return !!(capabilities & required_capability);
 }
 
@@ -123,6 +161,19 @@
   return !!request_;
 }
 
+void OverlayPresentationContextImpl::PrepareToShowOverlayUI(
+    OverlayRequest* request) {
+  // Early return if the request is already supported.
+  if (CanShowUIForRequest(request))
+    return;
+
+  // Request the delegate to prepare for overlay UI with |required_capability|.
+  UIPresentationCapabilities required_capabilities =
+      GetRequiredPresentationCapabilities(request);
+  [delegate_ updatePresentationContext:this
+           forPresentationCapabilities:required_capabilities];
+}
+
 void OverlayPresentationContextImpl::ShowOverlayUI(
     OverlayRequest* request,
     OverlayPresentationCallback presentation_callback,
@@ -139,9 +190,7 @@
 }
 
 void OverlayPresentationContextImpl::HideOverlayUI(OverlayRequest* request) {
-  DCHECK(CanShowUIForRequest(request));
   DCHECK_EQ(request_, request);
-  DCHECK(CanShowUIForRequest(request));
 
   OverlayRequestUIState* state = GetRequestUIState(request_);
   DCHECK(state->has_callback());
@@ -153,7 +202,6 @@
 
 void OverlayPresentationContextImpl::CancelOverlayUI(
     OverlayRequest* request) {
-  DCHECK(CanShowUIForRequest(request));
   // No cleanup required if there is no UI state for |request|.  This can
   // occur when cancelling an OverlayRequest whose UI has never been
   // presented.
@@ -200,25 +248,48 @@
     // The UI state should be created before resetting the presented request.
     DCHECK(GetRequestUIState(request_));
     ShowUIForPresentedRequest();
+  } else {
+    // Inform the delegate that no presentation capabilities are currently
+    // required.
+    [delegate_ updatePresentationContext:this
+             forPresentationCapabilities:UIPresentationCapabilities::kNone];
   }
 }
 
+bool OverlayPresentationContextImpl::RequestUsesChildViewController(
+    OverlayRequest* request) const {
+  return [coordinator_factory_
+      coordinatorForRequestUsesChildViewController:request];
+}
+
+UIViewController* OverlayPresentationContextImpl::GetBaseViewController(
+    OverlayRequest* request) const {
+  return RequestUsesChildViewController(request)
+             ? container_view_controller_
+             : presentation_context_view_controller_;
+}
+
 OverlayRequestUIState* OverlayPresentationContextImpl::GetRequestUIState(
     OverlayRequest* request) {
   return request ? states_[request].get() : nullptr;
 }
 
-void OverlayPresentationContextImpl::UpdateForCoordinator(
-    OverlayContainerCoordinator* coordinator) {
+OverlayPresentationContext::UIPresentationCapabilities
+OverlayPresentationContextImpl::GetRequiredPresentationCapabilities(
+    OverlayRequest* request) const {
+  BOOL uses_child_view_controller = [coordinator_factory_
+      coordinatorForRequestUsesChildViewController:request];
+  return uses_child_view_controller ? UIPresentationCapabilities::kContained
+                                    : UIPresentationCapabilities::kPresented;
+}
+
+void OverlayPresentationContextImpl::UpdatePresentationCapabilities() {
   UIPresentationCapabilities capabilities = UIPresentationCapabilities::kNone;
-  UIViewController* view_controller = coordinator.viewController;
-  // Any UIViewController can contain overlay UI as a child.
-  if (view_controller) {
+  if (container_view_controller_) {
     capabilities = static_cast<UIPresentationCapabilities>(
         capabilities | UIPresentationCapabilities::kContained);
   }
-  // Only UIViewControllers attached to a window can present overlay UI.
-  if (view_controller.view.window) {
+  if (presentation_context_view_controller_) {
     capabilities = static_cast<UIPresentationCapabilities>(
         capabilities | UIPresentationCapabilities::kPresented);
   }
@@ -232,7 +303,6 @@
   }
 
   presentation_capabilities_ = capabilities;
-  coordinator_ = coordinator;
 
   if (capabilities_changed) {
     for (auto& observer : observers_) {
@@ -250,14 +320,14 @@
 
   // Create the coordinator if necessary.
   OverlayRequestUIState* state = GetRequestUIState(request_);
-  UIViewController* container_view_controller = coordinator_.viewController;
   OverlayRequestCoordinator* overlay_coordinator = state->coordinator();
+  UIViewController* base_view_controller = GetBaseViewController(request_);
   if (!overlay_coordinator ||
-      overlay_coordinator.baseViewController != container_view_controller) {
-    overlay_coordinator = [coordinator_factory_
-        newCoordinatorForRequest:request_
-                        delegate:&coordinator_delegate_
-              baseViewController:container_view_controller];
+      overlay_coordinator.baseViewController != base_view_controller) {
+    overlay_coordinator =
+        [coordinator_factory_ newCoordinatorForRequest:request_
+                                              delegate:&coordinator_delegate_
+                                    baseViewController:base_view_controller];
     state->OverlayUIWillBePresented(overlay_coordinator);
   }
 
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_delegate.h b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_delegate.h
new file mode 100644
index 0000000..5f90320
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_delegate.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_IMPL_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_IMPL_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
+
+// Delegate protocol used by OverlayPresentationContextImpl to set up the view
+// hierarchy to support displaying overlay UI.
+@protocol OverlayPresentationContextImplDelegate <NSObject>
+
+// Instructs the delegate to set up the base UIViewController for overlay UI
+// that requires |capababilities| and provide it to |context|.
+- (void)updatePresentationContext:(OverlayPresentationContextImpl*)context
+      forPresentationCapabilities:
+          (OverlayPresentationContext::UIPresentationCapabilities)capabilities;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_IMPL_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_unittest.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_unittest.mm
new file mode 100644
index 0000000..6b4a4d3
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_unittest.mm
@@ -0,0 +1,364 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
+
+#include "base/bind.h"
+#import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#include "ios/chrome/browser/overlays/public/overlay_presentation_context_observer.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_contained_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_presented_overlay_request_config.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl_delegate.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_util.h"
+#import "ios/chrome/browser/ui/overlays/test/test_overlay_presentation_context.h"
+#import "ios/chrome/test/scoped_key_window.h"
+#include "ios/web/public/test/web_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using base::test::ios::WaitUntilConditionOrTimeout;
+using base::test::ios::kWaitForUIElementTimeout;
+
+@class FakeOverlayPresenationContextDelegate;
+
+namespace {
+
+// Mock observer for the presentation context.
+class MockOverlayPresentationContextImplObserver
+    : public OverlayPresentationContextObserver {
+ public:
+  MockOverlayPresentationContextImplObserver() {}
+  ~MockOverlayPresentationContextImplObserver() {}
+
+  MOCK_METHOD2(OverlayPresentationContextWillChangePresentationCapabilities,
+               void(OverlayPresentationContext*,
+                    OverlayPresentationContext::UIPresentationCapabilities));
+  MOCK_METHOD1(OverlayPresentationContextDidChangePresentationCapabilities,
+               void(OverlayPresentationContext*));
+  MOCK_METHOD2(OverlayPresentationContextDidMoveToWindow,
+               void(OverlayPresentationContext*, UIWindow*));
+};
+
+// Returns the presentation capabilities for a context whose contained and
+// presented overlay UI support is described by |supports_contained| and
+// |supports_presented|.
+OverlayPresentationContext::UIPresentationCapabilities GetCapabilities(
+    bool supports_contained,
+    bool supports_presented) {
+  int capabilities =
+      OverlayPresentationContext::UIPresentationCapabilities::kNone;
+  if (supports_contained) {
+    capabilities =
+        capabilities |
+        OverlayPresentationContext::UIPresentationCapabilities::kContained;
+  }
+  if (supports_presented) {
+    capabilities =
+        capabilities |
+        OverlayPresentationContext::UIPresentationCapabilities::kPresented;
+  }
+  return static_cast<OverlayPresentationContext::UIPresentationCapabilities>(
+      capabilities);
+}
+
+}  // namespace
+
+class OverlayPresentationContextImplTest;
+
+// Fake delegate to use for tests.
+@interface FakeOverlayPresenationContextDelegate
+    : NSObject <OverlayPresentationContextImplDelegate>
+@property(nonatomic, assign) OverlayPresentationContextImplTest* test;
+@end
+
+// Test fixture for OverlayPresentationContextImpl.
+class OverlayPresentationContextImplTest : public PlatformTest {
+ public:
+  OverlayPresentationContextImplTest()
+      : browser_(std::make_unique<TestBrowser>()),
+        context_(browser_.get()),
+        delegate_([[FakeOverlayPresenationContextDelegate alloc] init]),
+        root_view_controller_([[UIViewController alloc] init]) {
+    root_view_controller_.definesPresentationContext = YES;
+    scoped_window_.Get().rootViewController = root_view_controller_;
+    delegate_.test = this;
+    context_.SetDelegate(delegate_);
+    context_.AddObserver(&observer_);
+    EXPECT_CALL(observer_, OverlayPresentationContextDidMoveToWindow(
+                               &context_, scoped_window_.Get()));
+    context_.SetWindow(scoped_window_.Get());
+  }
+  ~OverlayPresentationContextImplTest() override {
+    context_.RemoveObserver(&observer_);
+    // The browser needs to be destroyed before |context_| so that observers
+    // can be unhooked due to BrowserDestroyed().  This is not a problem for
+    // non-test OverlayPresentationContextImpls since they're owned by the
+    // Browser and get destroyed after BrowserDestroyed() is called.
+    browser_ = nullptr;
+  }
+
+  // Setter for whether the presentation context should support overlay UI
+  // implemented using child UIViewControllers.
+  void SetSupportsContainedOverlayUI() {
+    if (supports_contained_ui_)
+      return;
+
+    // Updating the support for contained overlay UI will notifiy the observer
+    // of this change.
+    EXPECT_CALL(observer_,
+                OverlayPresentationContextWillChangePresentationCapabilities(
+                    &context_, GetCapabilities(true, supports_presented_ui_)));
+    EXPECT_CALL(
+        observer_,
+        OverlayPresentationContextDidChangePresentationCapabilities(&context_));
+
+    supports_contained_ui_ = true;
+
+    context_.SetContainerViewController(root_view_controller_);
+
+    // Check that the presentation capabilities have been updated.
+    ASSERT_EQ(supports_contained_ui_,
+              OverlayPresentationContextSupportsContainedUI(&context_));
+  }
+
+  // Setter for whether the presentation context should support overlay UI
+  // implemented using presented UIViewControllers.
+  void SetSupportsPresentedOverlayUI() {
+    if (supports_presented_ui_)
+      return;
+
+    // Updating the support for presented overlay UI will notifiy the observer
+    // of this change.
+    EXPECT_CALL(observer_,
+                OverlayPresentationContextWillChangePresentationCapabilities(
+                    &context_, GetCapabilities(supports_contained_ui_, true)));
+    EXPECT_CALL(
+        observer_,
+        OverlayPresentationContextDidChangePresentationCapabilities(&context_));
+
+    supports_presented_ui_ = true;
+
+    // Present a UIViewController over |root_view_controller_|'s context, then
+    // supply the view controller to the presentation context.
+    UIViewController* presentation_context_view_controller =
+        [[UIViewController alloc] init];
+    presentation_context_view_controller.definesPresentationContext = YES;
+    presentation_context_view_controller.modalPresentationStyle =
+        UIModalPresentationOverCurrentContext;
+    __block bool presentation_finished = NO;
+    [root_view_controller_
+        presentViewController:presentation_context_view_controller
+                     animated:NO
+                   completion:^{
+                     context_.SetPresentationContextViewController(
+                         presentation_context_view_controller);
+                     presentation_finished = YES;
+                   }];
+
+    ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+      return presentation_finished;
+    }));
+
+    // Check that the presentation capabilities have been updated.
+    ASSERT_EQ(supports_presented_ui_,
+              OverlayPresentationContextSupportsPresentedUI(&context_));
+  }
+
+  // Shows the overlay UI for |request| in the context.
+  void ShowOverlayUI(OverlayRequest* request) {
+    overlay_presentation_finished_ = false;
+    overlay_dismissal_finished_ = false;
+    overlay_dismissal_reason_ = OverlayDismissalReason::kUserInteraction;
+    context_.ShowOverlayUI(request, base::BindOnce(^{
+                             overlay_presentation_finished_ = true;
+                           }),
+                           base::BindOnce(^(OverlayDismissalReason reason) {
+                             overlay_dismissal_finished_ = true;
+                             overlay_dismissal_reason_ = reason;
+                           }));
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  std::unique_ptr<TestBrowser> browser_;
+  TestOverlayPresentationContext context_;
+  MockOverlayPresentationContextImplObserver observer_;
+  FakeOverlayPresenationContextDelegate* delegate_ = nil;
+  ScopedKeyWindow scoped_window_;
+  UIViewController* root_view_controller_ = nil;
+  bool overlay_presentation_finished_ = false;
+  bool overlay_dismissal_finished_ = false;
+  OverlayDismissalReason overlay_dismissal_reason_ =
+      OverlayDismissalReason::kUserInteraction;
+
+ private:
+  // Support for presented or contained UI should only be updated using the
+  // setters above.
+  bool supports_contained_ui_ = false;
+  bool supports_presented_ui_ = false;
+};
+
+// FakeOverlayPresenationContextDelegate implementation needs to be declared
+// after the test fixture so that it can call its public API.
+@implementation FakeOverlayPresenationContextDelegate
+
+- (void)updatePresentationContext:(OverlayPresentationContextImpl*)context
+      forPresentationCapabilities:
+          (OverlayPresentationContext::UIPresentationCapabilities)capabilities {
+  if (capabilities &
+      OverlayPresentationContext::UIPresentationCapabilities::kContained) {
+    self.test->SetSupportsContainedOverlayUI();
+  }
+  if (capabilities &
+      OverlayPresentationContext::UIPresentationCapabilities::kPresented) {
+    self.test->SetSupportsPresentedOverlayUI();
+  }
+}
+
+@end
+
+// Tests that neither contained nor presented overlay UI can be shown in the
+// context if no view controllers have been provided.
+TEST_F(OverlayPresentationContextImplTest, NoPresentationCapabilities) {
+  ASSERT_EQ(context_.GetPresentationCapabilities(),
+            OverlayPresentationContext::UIPresentationCapabilities::kNone);
+
+  std::unique_ptr<OverlayRequest> contained_request =
+      OverlayRequest::CreateWithConfig<TestContainedOverlay>();
+  EXPECT_FALSE(context_.CanShowUIForRequest(contained_request.get()));
+  std::unique_ptr<OverlayRequest> presented_request =
+      OverlayRequest::CreateWithConfig<TestPresentedOverlay>();
+  EXPECT_FALSE(context_.CanShowUIForRequest(presented_request.get()));
+}
+
+// Tests that contained overlay UI can be shown if the container
+// UIViewController is provided.
+TEST_F(OverlayPresentationContextImplTest, ContainedPresentationCapability) {
+  std::unique_ptr<OverlayRequest> contained_request =
+      OverlayRequest::CreateWithConfig<TestContainedOverlay>();
+  context_.PrepareToShowOverlayUI(contained_request.get());
+  EXPECT_TRUE(context_.CanShowUIForRequest(contained_request.get()));
+
+  std::unique_ptr<OverlayRequest> presented_request =
+      OverlayRequest::CreateWithConfig<TestPresentedOverlay>();
+  EXPECT_FALSE(context_.CanShowUIForRequest(presented_request.get()));
+}
+
+// Tests that presented overlay UI can be shown if the presentation context
+// UIViewController is provided.
+TEST_F(OverlayPresentationContextImplTest, PresentedPresentationCapability) {
+  std::unique_ptr<OverlayRequest> presented_request =
+      OverlayRequest::CreateWithConfig<TestPresentedOverlay>();
+  context_.PrepareToShowOverlayUI(presented_request.get());
+  EXPECT_TRUE(context_.CanShowUIForRequest(presented_request.get()));
+
+  std::unique_ptr<OverlayRequest> contained_request =
+      OverlayRequest::CreateWithConfig<TestContainedOverlay>();
+  EXPECT_FALSE(context_.CanShowUIForRequest(contained_request.get()));
+}
+
+// Tests that CanShowRequest() returns the expected value when the presentation
+// capabilities are pass in.
+TEST_F(OverlayPresentationContextImplTest, CanShowRequest) {
+  std::unique_ptr<OverlayRequest> contained_request =
+      OverlayRequest::CreateWithConfig<TestContainedOverlay>();
+  EXPECT_TRUE(context_.CanShowUIForRequest(
+      contained_request.get(),
+      OverlayPresentationContext::UIPresentationCapabilities::kContained));
+  EXPECT_FALSE(context_.CanShowUIForRequest(
+      contained_request.get(),
+      OverlayPresentationContext::UIPresentationCapabilities::kPresented));
+
+  std::unique_ptr<OverlayRequest> presented_request =
+      OverlayRequest::CreateWithConfig<TestPresentedOverlay>();
+  EXPECT_FALSE(context_.CanShowUIForRequest(
+      presented_request.get(),
+      OverlayPresentationContext::UIPresentationCapabilities::kContained));
+  EXPECT_TRUE(context_.CanShowUIForRequest(
+      presented_request.get(),
+      OverlayPresentationContext::UIPresentationCapabilities::kPresented));
+}
+
+// Tests the presentation flow for contained overlay UI.
+TEST_F(OverlayPresentationContextImplTest, ContainedOverlayUI) {
+  std::unique_ptr<OverlayRequest> request =
+      OverlayRequest::CreateWithConfig<TestContainedOverlay>();
+  context_.PrepareToShowOverlayUI(request.get());
+  ASSERT_EQ(0U, root_view_controller_.view.subviews.count);
+  ASSERT_EQ(0U, root_view_controller_.childViewControllers.count);
+
+  // Show the UI for |request| and verify that the overlay UI is added to the
+  // |root_view_controller_|'s view.
+  ShowOverlayUI(request.get());
+  EXPECT_EQ(1U, root_view_controller_.view.subviews.count);
+  EXPECT_EQ(1U, root_view_controller_.childViewControllers.count);
+  EXPECT_TRUE(overlay_presentation_finished_);
+
+  // Hide the overlay UI and verify that it was removed from
+  // |root_view_controller_|'s view.
+  context_.HideOverlayUI(request.get());
+  EXPECT_EQ(0U, root_view_controller_.view.subviews.count);
+  EXPECT_EQ(0U, root_view_controller_.childViewControllers.count);
+  EXPECT_TRUE(overlay_dismissal_finished_);
+  EXPECT_EQ(OverlayDismissalReason::kHiding, overlay_dismissal_reason_);
+
+  // Show the UI again, then cancel it and verify that the view was removed.
+  ShowOverlayUI(request.get());
+  context_.CancelOverlayUI(request.get());
+  EXPECT_EQ(0U, root_view_controller_.view.subviews.count);
+  EXPECT_EQ(0U, root_view_controller_.childViewControllers.count);
+  EXPECT_TRUE(overlay_dismissal_finished_);
+  EXPECT_EQ(OverlayDismissalReason::kCancellation, overlay_dismissal_reason_);
+}
+
+// Tests the presentation flow for presented overlay UI.
+TEST_F(OverlayPresentationContextImplTest, PresentedOverlayUI) {
+  std::unique_ptr<OverlayRequest> request =
+      OverlayRequest::CreateWithConfig<TestPresentedOverlay>();
+  context_.PrepareToShowOverlayUI(request.get());
+  UIViewController* presentation_base_view_controller =
+      root_view_controller_.presentedViewController;
+  ASSERT_TRUE(presentation_base_view_controller);
+  ASSERT_FALSE(presentation_base_view_controller.presentedViewController);
+
+  // Blocks used to determine when presentation and dismissal is finished.
+  bool (^presentation_completion_condition)(void) = ^bool {
+    UIViewController* presented_view_controller =
+        presentation_base_view_controller.presentedViewController;
+    return presented_view_controller &&
+           !presented_view_controller.beingPresented &&
+           overlay_presentation_finished_;
+  };
+  bool (^dismissal_completion_condition)(void) = ^bool {
+    return !presentation_base_view_controller.presentedViewController &&
+           overlay_dismissal_finished_;
+  };
+
+  // Show the UI for |request| and verify that the overlay UI is presented over
+  // |presentation_base_view_controller|.
+  ShowOverlayUI(request.get());
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout,
+                                          presentation_completion_condition));
+
+  // Hide the overlay UI and verify that it was dismissed.
+  context_.HideOverlayUI(request.get());
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout,
+                                          dismissal_completion_condition));
+  EXPECT_EQ(OverlayDismissalReason::kHiding, overlay_dismissal_reason_);
+
+  // Show the UI again, then cancel it and verify that the view was removed.
+  ShowOverlayUI(request.get());
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout,
+                                          presentation_completion_condition));
+  context_.CancelOverlayUI(request.get());
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout,
+                                          dismissal_completion_condition));
+  EXPECT_EQ(OverlayDismissalReason::kCancellation, overlay_dismissal_reason_);
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_util.h b/ios/chrome/browser/ui/overlays/overlay_presentation_context_util.h
new file mode 100644
index 0000000..ed71940
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_util.h
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_UTIL_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_UTIL_H_
+
+class OverlayPresentationContext;
+
+// Returns whether |context|'s UIPresentationCapabilities currently support
+// overlay UI implemented with contained UIViewControllers.
+bool OverlayPresentationContextSupportsContainedUI(
+    OverlayPresentationContext* context);
+
+// Returns whether |context|'s UIPresentationCapabilities currently support
+// overlay UI implemented with presented UIViewControllers.
+bool OverlayPresentationContextSupportsPresentedUI(
+    OverlayPresentationContext* context);
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_UTIL_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_util.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_util.mm
new file mode 100644
index 0000000..4151e657
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_util.mm
@@ -0,0 +1,27 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_util.h"
+
+#import "ios/chrome/browser/overlays/public/overlay_presentation_context.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+bool OverlayPresentationContextSupportsContainedUI(
+    OverlayPresentationContext* context) {
+  if (!context)
+    return false;
+  return context->GetPresentationCapabilities() &
+         OverlayPresentationContext::UIPresentationCapabilities::kContained;
+}
+
+bool OverlayPresentationContextSupportsPresentedUI(
+    OverlayPresentationContext* context) {
+  if (!context)
+    return false;
+  return context->GetPresentationCapabilities() &
+         OverlayPresentationContext::UIPresentationCapabilities::kPresented;
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.h b/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.h
new file mode 100644
index 0000000..8a1b02e8
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.h
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_VIEW_CONTROLLER_H_
+
+#import <UIKIt/UIKit.h>
+
+// View controller that manages the presentation context upon which overlay UI
+// is presented.
+@interface OverlayPresentationContextViewController : UIViewController
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTEXT_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.mm
new file mode 100644
index 0000000..16f8b78
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.mm
@@ -0,0 +1,113 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.h"
+
+#include "base/logging.h"
+#import "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface OverlayPresentationContextViewController ()
+// The view used to lay out the presentation context.  The presentation context
+// view is resized to match |layoutView|'s frame.
+@property(nonatomic, readonly) UIView* layoutView;
+// Whether the presented view controller is presented using an
+// OverlayPresentationController whose |resizesPresentationContainer| property
+// returns YES.
+@property(nonatomic, readonly) BOOL presentedViewControllerResizesContainer;
+@end
+
+@implementation OverlayPresentationContextViewController
+
+#pragma mark - Accessors
+
+- (UIView*)layoutView {
+  // If there is no overlay UI displayed using presentation or containment, the
+  // presentation context should be laid out with an empty frame to allow
+  // touches to pass freely to the underlying browser UI.
+  UIViewController* presentedViewController = self.presentedViewController;
+  if (!presentedViewController)
+    return nil;
+
+  // If overlay UI is displayed using custom UIViewController presentation with
+  // an OverlayPresentationController that resizes the container view, the
+  // presentation context should match the presented view's container.
+  if (self.presentedViewControllerResizesContainer)
+    return self.presentedViewController.presentationController.containerView;
+
+  // For all other UIViewController presentation, the context should be laid out
+  // to match the presenter view.
+  return self.presentingViewController.view;
+}
+
+- (BOOL)presentedViewControllerResizesContainer {
+  // The non-strict cast returns nil if the presented UIViewController does not
+  // use an OverlayPresentationController.  This results in this selector
+  // returning NO for these UIViewControllers.
+  return base::mac::ObjCCast<OverlayPresentationController>(
+             self.presentedViewController.presentationController)
+      .resizesPresentationContainer;
+}
+
+#pragma mark - UIViewController
+
+- (void)viewDidLayoutSubviews {
+  UIView* view = self.view;
+  UIView* containerView = self.presentationController.containerView;
+  UIView* layoutView = self.layoutView;
+  UIWindow* window = layoutView.window;
+  CGRect layoutFrame = [window convertRect:layoutView.bounds
+                                  fromView:layoutView];
+  // Lay out the presentation context and its container view to match
+  // |layoutView|.
+  if (layoutView) {
+    containerView.frame = [containerView.superview convertRect:layoutFrame
+                                                      fromView:window];
+    view.frame = [view.superview convertRect:layoutFrame fromView:window];
+  } else {
+    containerView.frame = CGRectZero;
+    view.frame = CGRectZero;
+  }
+  // If |layoutView| is not laid out using constraints, its frame may have been
+  // updated by the container and presentation context layout above.  Reset the
+  // frame by converting back from the window coordinates.
+  if (self.presentedViewControllerResizesContainer &&
+      layoutView.translatesAutoresizingMaskIntoConstraints) {
+    layoutView.frame = [layoutView.superview convertRect:layoutFrame
+                                                fromView:window];
+  }
+}
+
+- (void)presentViewController:(UIViewController*)viewController
+                     animated:(BOOL)animated
+                   completion:(void (^)(void))completion {
+  // Trigger a layout of the presentation context before presenting.  This
+  // allows the presentation context to be resized appropriately during the the
+  // presentation animation.
+  [self.view setNeedsLayout];
+
+  [super presentViewController:viewController
+                      animated:animated
+                    completion:completion];
+}
+
+- (void)dismissViewControllerAnimated:(BOOL)animated
+                           completion:(void (^)(void))completion {
+  // Create an updated completion block that triggers layout after the dismissal
+  // finishes.  This will resize the presentation context to CGRectZero so that
+  // touches can be handled by the underlying browser UI once the presented
+  // overlay is removed.
+  [super dismissViewControllerAnimated:animated
+                            completion:^{
+                              if (completion)
+                                completion();
+                              [self.view setNeedsLayout];
+                            }];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller_unittest.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller_unittest.mm
new file mode 100644
index 0000000..7cf8226
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller_unittest.mm
@@ -0,0 +1,188 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_view_controller.h"
+
+#import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_presented_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
+#include "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h"
+#import "ios/chrome/browser/ui/overlays/test_modality/test_presented_overlay_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.h"
+#include "ios/chrome/test/scoped_key_window.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
+
+using base::test::ios::WaitUntilConditionOrTimeout;
+using base::test::ios::kWaitForUIElementTimeout;
+
+namespace {
+// The frame of resizable presened overlay UI.
+const CGRect kWindowFrame = {{100.0, 100.0}, {100.0, 100.0}};
+}  // namespace
+
+// Test fixture for OverlayPresentationContextViewController.
+class OverlayPresentationContextViewControllerTest : public PlatformTest {
+ public:
+  OverlayPresentationContextViewControllerTest()
+      : root_view_controller_([[UIViewController alloc] init]),
+        view_controller_(
+            [[OverlayPresentationContextViewController alloc] init]) {
+    root_view_controller_.definesPresentationContext = YES;
+    scoped_window_.Get().rootViewController = root_view_controller_;
+    // Present |view_controller_| over |root_view_controller_| without animation
+    // and wait for the presentation to finish.
+    view_controller_.modalPresentationStyle =
+        UIModalPresentationOverCurrentContext;
+    __block bool presentation_finished = NO;
+    [root_view_controller_ presentViewController:view_controller_
+                                        animated:NO
+                                      completion:^{
+                                        presentation_finished = YES;
+                                      }];
+    EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+      return presentation_finished;
+    }));
+  }
+  ~OverlayPresentationContextViewControllerTest() override {
+    // Dismisses |view_controller_| and waits for the dismissal to finish.
+    __block bool dismissal_finished = NO;
+    [root_view_controller_ dismissViewControllerAnimated:NO
+                                              completion:^{
+                                                dismissal_finished = YES;
+                                              }];
+    EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+      return dismissal_finished;
+    }));
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  TestBrowser browser_;
+  FakeOverlayRequestCoordinatorDelegate delegate_;
+  ScopedKeyWindow scoped_window_;
+  UIViewController* root_view_controller_ = nil;
+  OverlayPresentationContextViewController* view_controller_ = nil;
+};
+
+// Tests that |view_controller_|'s frame is CGRectZero when there is no overlay
+// UI presented upon it.
+TEST_F(OverlayPresentationContextViewControllerTest, NoPresentedUI) {
+  CGRect container_view_frame =
+      view_controller_.presentationController.containerView.frame;
+  EXPECT_TRUE(CGRectEqualToRect(container_view_frame, CGRectZero));
+  CGRect frame = view_controller_.view.frame;
+  EXPECT_TRUE(CGRectEqualToRect(frame, CGRectZero));
+}
+
+// Tests that |view_controller_|'s frame is the same as its presenter while
+// showing overlay UI presented over its context.
+TEST_F(OverlayPresentationContextViewControllerTest,
+       PresentedOverCurrentContext) {
+  // Create a fake overlay coordinator that presents its UI over
+  // |view_controller_|.
+  std::unique_ptr<OverlayRequest> request =
+      OverlayRequest::CreateWithConfig<TestPresentedOverlay>();
+  TestPresentedOverlayCoordinator* coordinator =
+      [[TestPresentedOverlayCoordinator alloc]
+          initWithBaseViewController:view_controller_
+                             browser:&browser_
+                             request:request.get()
+                            delegate:&delegate_];
+
+  // Start the coordinator and wait for its presentation to finish.
+  [coordinator startAnimated:NO];
+  UIViewController* overlay_view_controller = coordinator.viewController;
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return overlay_view_controller.presentingViewController &&
+           !overlay_view_controller.beingPresented;
+  }));
+
+  // Verify that the presentation context is resized to
+  // |root_view_controller_|'s view.
+  UIView* root_view = root_view_controller_.view;
+  CGRect root_window_frame = [root_view convertRect:root_view.bounds
+                                             toView:nil];
+  UIView* container_view =
+      view_controller_.presentationController.containerView;
+  EXPECT_TRUE(CGRectEqualToRect(
+      [container_view convertRect:container_view.bounds toView:nil],
+      root_window_frame));
+  UIView* view = view_controller_.view;
+  EXPECT_TRUE(CGRectEqualToRect([view convertRect:view.bounds toView:nil],
+                                root_window_frame));
+
+  // Stop the coordinator and wait for its dismissal to finish.
+  [coordinator stopAnimated:NO];
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return !overlay_view_controller.presentingViewController;
+  }));
+
+  // Verify that the views are resized to CGRectZero when nothing is presented
+  // upon it.
+  EXPECT_TRUE(CGRectEqualToRect(container_view.frame, CGRectZero));
+  EXPECT_TRUE(CGRectEqualToRect(view.frame, CGRectZero));
+}
+
+// Tests that |view_controller_|'s frame is the same as its presented view's
+// container view if it is shown using custom UIViewController presentation that
+// resizes the contianer view.
+TEST_F(OverlayPresentationContextViewControllerTest, ResizingPresentedOverlay) {
+  // Create a fake overlay coordinator that presents its UI over
+  // |view_controller_| and resizes its presentation container view to
+  // kWindowFrame.
+  std::unique_ptr<OverlayRequest> request =
+      OverlayRequest::CreateWithConfig<TestResizingPresentedOverlay>(
+          kWindowFrame);
+  TestResizingPresentedOverlayCoordinator* coordinator =
+      [[TestResizingPresentedOverlayCoordinator alloc]
+          initWithBaseViewController:view_controller_
+                             browser:&browser_
+                             request:request.get()
+                            delegate:&delegate_];
+
+  // Start the coordinator and wait for its presentation to finish.
+  [coordinator startAnimated:NO];
+  UIViewController* overlay_view_controller = coordinator.viewController;
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return overlay_view_controller.presentingViewController &&
+           !overlay_view_controller.beingPresented;
+  }));
+
+  // Verify that the presentation context is resized kWindowFrame.
+  UIView* container_view =
+      view_controller_.presentationController.containerView;
+  EXPECT_TRUE(CGRectEqualToRect(
+      [container_view convertRect:container_view.bounds toView:nil],
+      kWindowFrame));
+  UIView* view = view_controller_.view;
+  EXPECT_TRUE(CGRectEqualToRect([view convertRect:view.bounds toView:nil],
+                                kWindowFrame));
+
+  // Verify that resizing the presentation context container doesn't affect the
+  // layout of the overlay container.
+  UIView* overlay_container_view =
+      overlay_view_controller.presentationController.containerView;
+  EXPECT_TRUE(CGRectEqualToRect([overlay_container_view convertRect:view.bounds
+                                                             toView:nil],
+                                kWindowFrame));
+
+  // Stop the coordinator and wait for its dismissal to finish.
+  [coordinator stopAnimated:NO];
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return !overlay_view_controller.presentingViewController;
+  }));
+
+  // Verify that the views are resized to CGRectZero when nothing is presented
+  // upon it.
+  EXPECT_TRUE(CGRectEqualToRect(container_view.frame, CGRectZero));
+  EXPECT_TRUE(CGRectEqualToRect(view.frame, CGRectZero));
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_controller.h b/ios/chrome/browser/ui/overlays/overlay_presentation_controller.h
new file mode 100644
index 0000000..20cf30db
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_controller.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+@protocol OverlayPresentationControllerObserver;
+
+// Presentation controller used for overlays presented using custom
+// UIViewController presentation.
+@interface OverlayPresentationController : UIPresentationController
+
+// Whether the presentation controller resizes the presentation container view.
+// When set to YES, the overlay presentation context view will be resized to fit
+// the presented view so that touches that fall outside of the overlay can be
+// forwarded to the underlying browser UI.  Presentation controllers that return
+// YES for this property must not lay out their presented views in relation to
+// the presenter.  Returns NO by default.
+@property(nonatomic, readonly) BOOL resizesPresentationContainer;
+
+// Subclasses must notify the superclass when their container views lay out
+// their subviews.
+- (void)containerViewWillLayoutSubviews NS_REQUIRES_SUPER;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTATION_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_controller.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_controller.mm
new file mode 100644
index 0000000..c7ca8b8
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_controller.mm
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation OverlayPresentationController
+
+#pragma mark - Accessors
+
+- (BOOL)resizesPresentationContainer {
+  return NO;
+}
+
+#pragma mark - UIPresentationController
+
+- (BOOL)shouldPresentInFullscreen {
+  return NO;
+}
+
+- (void)containerViewWillLayoutSubviews {
+  [super containerViewWillLayoutSubviews];
+  // Trigger a layout pass for the presenting view controller.  This allows the
+  // presentation context to resize itself to match the presented overlay UI if
+  // |resizesPresentationContainer| is YES.
+  [self.presentingViewController.view setNeedsLayout];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/test/test_overlay_presentation_context.h b/ios/chrome/browser/ui/overlays/test/test_overlay_presentation_context.h
index 71fa9a5..22d3badd 100644
--- a/ios/chrome/browser/ui/overlays/test/test_overlay_presentation_context.h
+++ b/ios/chrome/browser/ui/overlays/test/test_overlay_presentation_context.h
@@ -8,6 +8,9 @@
 #import "ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h"
 
 // OverlayPresentationContextImpl for OverlayModality::kTesting.
+// TODO(crbug.com/1056837): This class is only necessary to prevent the test
+// modality code from getting compiled into releases, and can be removed once
+// OverlayModality is converted from an enum to a class.
 class TestOverlayPresentationContext : public OverlayPresentationContextImpl {
  public:
   explicit TestOverlayPresentationContext(Browser* browser);
diff --git a/ios/chrome/browser/ui/overlays/test/test_overlay_request_coordinator_factory.h b/ios/chrome/browser/ui/overlays/test/test_overlay_request_coordinator_factory.h
index 4d2e808a..ae55a8c8 100644
--- a/ios/chrome/browser/ui/overlays/test/test_overlay_request_coordinator_factory.h
+++ b/ios/chrome/browser/ui/overlays/test/test_overlay_request_coordinator_factory.h
@@ -8,6 +8,9 @@
 #import "ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h"
 
 // OverlayRequestCoordinatorFactory for OverlayModality::kTesting.
+// TODO(crbug.com/1056837): This class is only necessary to prevent the test
+// modality code from getting compiled into releases, and can be removed once
+// OverlayModality is converted from an enum to a class.
 @interface TestOverlayRequestCoordinatorFactory
     : OverlayRequestCoordinatorFactory
 // Initializer for a factory that vends OverlayRequestCoordinators for |browser|
diff --git a/ios/chrome/browser/ui/overlays/test_modality/BUILD.gn b/ios/chrome/browser/ui/overlays/test_modality/BUILD.gn
index 4329593..be6d01e 100644
--- a/ios/chrome/browser/ui/overlays/test_modality/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/test_modality/BUILD.gn
@@ -9,6 +9,8 @@
     "test_contained_overlay_coordinator.mm",
     "test_presented_overlay_coordinator.h",
     "test_presented_overlay_coordinator.mm",
+    "test_resizing_presented_overlay_coordinator.h",
+    "test_resizing_presented_overlay_coordinator.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -18,6 +20,7 @@
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/test_modality",
     "//ios/chrome/browser/ui/overlays:coordinators",
+    "//ios/chrome/browser/ui/overlays:presentation_controller",
     "//ios/chrome/common/ui/util",
   ]
 }
@@ -27,6 +30,7 @@
   sources = [
     "test_contained_overlay_coordinator_unittest.mm",
     "test_presented_overlay_coordinator_unittest.mm",
+    "test_resizing_presented_overlay_coordinator_unittest.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.h b/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.h
new file mode 100644
index 0000000..ff37a24
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.h
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_MODALITY_TEST_RESIZING_PRESENTED_OVERLAY_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_MODALITY_TEST_RESIZING_PRESENTED_OVERLAY_COORDINATOR_H_
+
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
+
+// Coordinator to use in tests for overlay UI supported using presented
+// UIViewControllers that resize their presentation container views.
+@interface TestResizingPresentedOverlayCoordinator : OverlayRequestCoordinator
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_MODALITY_TEST_RESIZING_PRESENTED_OVERLAY_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.mm
new file mode 100644
index 0000000..5c40a54
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.mm
@@ -0,0 +1,138 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.h"
+
+#import "ios/chrome/browser/overlays/public/test_modality/test_presented_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presentation_controller.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator+subclassing.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#pragma mark - FakeResizingPresentationController
+
+@interface FakeResizingPresentationController : OverlayPresentationController
+// The frame of the presentation container view in window coordinates.
+@property(nonatomic, readonly) CGRect windowFrame;
+// Initializer for a presentation controller that lays its presentation
+// container view with |windowFrame|.
+- (instancetype)
+    initWithPresentedViewController:(UIViewController*)presentedViewController
+           presentingViewController:(UIViewController*)presentingViewController
+                        windowFrame:(CGRect)windowFrame
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)
+    initWithPresentedViewController:(UIViewController*)presentedViewController
+           presentingViewController:(UIViewController*)presentingViewController
+    NS_UNAVAILABLE;
+@end
+
+@implementation FakeResizingPresentationController
+
+- (instancetype)
+    initWithPresentedViewController:(UIViewController*)presentedViewController
+           presentingViewController:(UIViewController*)presentingViewController
+                        windowFrame:(CGRect)windowFrame {
+  if (self = [super initWithPresentedViewController:presentedViewController
+                           presentingViewController:presentingViewController]) {
+    _windowFrame = windowFrame;
+  }
+  return self;
+}
+
+#pragma mark OverlayPresentationController
+
+- (BOOL)resizesPresentationContainer {
+  return YES;
+}
+
+#pragma mark UIPresentationController
+
+- (void)presentationTransitionWillBegin {
+  [self updateLayout];
+}
+
+- (void)containerViewWillLayoutSubviews {
+  [self updateLayout];
+  [super containerViewWillLayoutSubviews];
+}
+
+// Lays out the container view according to the window frame.
+- (void)updateLayout {
+  self.containerView.frame =
+      [self.containerView.superview convertRect:self.windowFrame fromView:nil];
+  self.presentedView.frame = self.containerView.bounds;
+}
+
+@end
+
+#pragma mark - TestResizingPresentedOverlayCoordinator
+
+@interface TestResizingPresentedOverlayCoordinator () <
+    UIViewControllerTransitioningDelegate>
+@property(nonatomic, readwrite) UIViewController* presentedViewController;
+@end
+
+@implementation TestResizingPresentedOverlayCoordinator
+
+#pragma mark OverlayRequestCoordinator
+
++ (const OverlayRequestSupport*)requestSupport {
+  return TestResizingPresentedOverlay::RequestSupport();
+}
+
+- (UIViewController*)viewController {
+  return self.presentedViewController;
+}
+
+- (void)startAnimated:(BOOL)animated {
+  if (self.started)
+    return;
+  self.presentedViewController = [[UIViewController alloc] init];
+  self.viewController.modalPresentationStyle = UIModalPresentationCustom;
+  self.viewController.transitioningDelegate = self;
+  [self.baseViewController
+      presentViewController:self.viewController
+                   animated:animated
+                 completion:^{
+                   self.delegate->OverlayUIDidFinishPresentation(self.request);
+                 }];
+  self.started = YES;
+}
+
+- (void)stopAnimated:(BOOL)animated {
+  if (!self.started)
+    return;
+  [self.baseViewController
+      dismissViewControllerAnimated:animated
+                         completion:^{
+                           self.delegate->OverlayUIDidFinishDismissal(
+                               self.request);
+                         }];
+  self.presentedViewController = nil;
+  self.started = NO;
+}
+
+#pragma mark UIViewControllerTransitioningDelegate
+
+- (UIPresentationController*)
+    presentationControllerForPresentedViewController:
+        (UIViewController*)presented
+                            presentingViewController:
+                                (UIViewController*)presenting
+                                sourceViewController:(UIViewController*)source {
+  CGRect windowFrame =
+      self.request->GetConfig<TestResizingPresentedOverlay>()->frame();
+  return [[FakeResizingPresentationController alloc]
+      initWithPresentedViewController:presented
+             presentingViewController:presenting
+                          windowFrame:windowFrame];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator_unittest.mm b/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator_unittest.mm
new file mode 100644
index 0000000..7c49fac
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator_unittest.mm
@@ -0,0 +1,79 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/overlays/test_modality/test_resizing_presented_overlay_coordinator.h"
+
+#import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/test_modality/test_resizing_presented_overlay_request_config.h"
+#include "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h"
+#include "ios/chrome/test/scoped_key_window.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
+
+using base::test::ios::WaitUntilConditionOrTimeout;
+using base::test::ios::kWaitForUIElementTimeout;
+
+namespace {
+// The frame of the overlay UI presentation container view in window
+// coordinates.
+const CGRect kWindowFrame = {{100.0, 100.0}, {100.0, 100.0}};
+}  // namespace
+
+// Test fixture for TestResizingPresentedOverlayCoordinator.
+class TestResizingPresentedOverlayCoordinatorTest : public PlatformTest {
+ public:
+  TestResizingPresentedOverlayCoordinatorTest()
+      : root_view_controller_([[UIViewController alloc] init]),
+        request_(OverlayRequest::CreateWithConfig<TestResizingPresentedOverlay>(
+            kWindowFrame)),
+        coordinator_([[TestResizingPresentedOverlayCoordinator alloc]
+            initWithBaseViewController:root_view_controller_
+                               browser:&browser_
+                               request:request_.get()
+                              delegate:&delegate_]) {
+    scoped_window_.Get().rootViewController = root_view_controller_;
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  TestBrowser browser_;
+  ScopedKeyWindow scoped_window_;
+  UIViewController* root_view_controller_ = nil;
+  std::unique_ptr<OverlayRequest> request_;
+  FakeOverlayRequestCoordinatorDelegate delegate_;
+  TestResizingPresentedOverlayCoordinator* coordinator_ = nil;
+};
+
+// Tests that the coordinator sets up its view correctly.
+TEST_F(TestResizingPresentedOverlayCoordinatorTest, ViewSetup) {
+  // Start the coordinator and wait until the view is finished being presented.
+  // This is necessary because UIViewController presentation is asynchronous,
+  // even when performed without animation.
+  [coordinator_ startAnimated:NO];
+  UIViewController* view_controller = coordinator_.viewController;
+  ASSERT_TRUE(view_controller);
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return view_controller.presentingViewController &&
+           !view_controller.beingPresented;
+  }));
+
+  // Verify that |coordinator_|'s presentation container view is resized to
+  // kWindowFrame.
+  UIView* container_view = view_controller.presentationController.containerView;
+  CGRect container_view_frame =
+      [container_view convertRect:container_view.bounds toView:nil];
+  EXPECT_TRUE(CGRectEqualToRect(container_view_frame, kWindowFrame));
+
+  // Stop the coordinator and wait for the dismissal to finish.
+  [coordinator_ stopAnimated:NO];
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
+    return !view_controller.presentingViewController;
+  }));
+}
diff --git a/ios/chrome/browser/web/navigation_egtest.mm b/ios/chrome/browser/web/navigation_egtest.mm
index cab4855..925b8a6 100644
--- a/ios/chrome/browser/web/navigation_egtest.mm
+++ b/ios/chrome/browser/web/navigation_egtest.mm
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/ios/ios_util.h"
 #import "base/test/ios/wait_util.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -583,6 +584,13 @@
 // Tests that navigating forward from NTP works when resuming from session
 // restore. This is a regression test for https://crbug.com/814790.
 - (void)testRestoreHistoryToNTPAndNavigateForward {
+#if TARGET_IPHONE_SIMULATOR
+  if (!base::ios::IsRunningOnIOS13OrLater() && ![ChromeEarlGrey isIPadIdiom]) {
+    // This test is failing on one bot for that very specific configuration. See
+    // https://crbug.com/1059496 for more info.
+    EARL_GREY_TEST_DISABLED(@"Failing on iPhone 12 simulator.");
+  }
+#endif
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
   const GURL destinationURL = self.testServer->GetURL(kSimpleFileBasedTestURL);
   [ChromeEarlGrey loadURL:destinationURL];
@@ -603,6 +611,13 @@
 // Tests that restoring a placeholder URL is correctly restored.  This is a
 // regression test from http://crbug.com/1011758.
 - (void)testRestoreHistoryToPlaceholderURL {
+#if TARGET_IPHONE_SIMULATOR
+  if (!base::ios::IsRunningOnIOS13OrLater() && ![ChromeEarlGrey isIPadIdiom]) {
+    // This test is failing on one bot for that very specific configuration. See
+    // https://crbug.com/1059496 for more info.
+    EARL_GREY_TEST_DISABLED(@"Failing on iPhone 12 simulator.");
+  }
+#endif
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
   const GURL destinationURL("chrome://crash");
   [ChromeEarlGrey loadURL:destinationURL];
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index cbdb213..d2cfff5 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -342,7 +342,12 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [ "eg_tests_hook.mm" ]
-  deps = [ "//ios/chrome/app:tests_hook" ]
+  deps = [
+    "//base",
+    "//components/policy/core/common:test_support",
+    "//ios/chrome/app:tests_hook",
+    "//ios/chrome/browser/policy:test_support",
+  ]
 }
 
 source_set("eg_app_support+eg2") {
@@ -393,6 +398,7 @@
     "//ios/chrome/browser/ntp:features",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/passwords:eg_app_support+eg2",
+    "//ios/chrome/browser/policy:eg_app_support+eg2",
     "//ios/chrome/browser/translate:eg_app_support+eg2",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/authentication:eg_app_support+eg2",
diff --git a/ios/chrome/test/earl_grey/eg_tests_hook.mm b/ios/chrome/test/earl_grey/eg_tests_hook.mm
index 57a3cee..7edf593 100644
--- a/ios/chrome/test/earl_grey/eg_tests_hook.mm
+++ b/ios/chrome/test/earl_grey/eg_tests_hook.mm
@@ -4,6 +4,9 @@
 
 #include "ios/chrome/app/tests_hook.h"
 
+#include "base/command_line.h"
+#include "ios/chrome/browser/policy/test_platform_policy_provider.h"
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -34,6 +37,16 @@
   return true;
 }
 
+policy::ConfigurationPolicyProvider* GetOverriddenPlatformPolicyProvider() {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          "com.apple.configuration.managed")) {
+    DVLOG(1) << "Policy data present in NSUserDefaults, not installing test "
+                "platform provider";
+    return nullptr;
+  }
+  return GetTestPlatformPolicyProvider();
+}
+
 void SetUpTestsIfPresent() {
   // No-op for Earl Grey.
 }
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index 2aba2c9..0ff1a55b 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -58,6 +58,7 @@
     "//ios/chrome/browser/net:eg2_tests",
     "//ios/chrome/browser/ntp_tiles:eg2_tests",
     "//ios/chrome/browser/passwords:eg2_tests",
+    "//ios/chrome/browser/policy:eg2_tests",
     "//ios/chrome/browser/prerender:eg2_tests",
     "//ios/chrome/browser/translate:eg2_tests",
     "//ios/chrome/browser/ui/autofill:eg2_tests",
diff --git a/ios/testing/earl_grey/coverage_utils.mm b/ios/testing/earl_grey/coverage_utils.mm
index ded91960..02f921b 100644
--- a/ios/testing/earl_grey/coverage_utils.mm
+++ b/ios/testing/earl_grey/coverage_utils.mm
@@ -4,15 +4,15 @@
 
 #import "ios/testing/earl_grey/coverage_utils.h"
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #import "testing/coverage_util_ios.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE)
-#include "base/test/clang_coverage.h"
+#if BUILDFLAG(CLANG_PROFILING)
+#include "base/test/profiling_utils.h"
 extern "C" void __llvm_profile_reset_counters(void);
 #endif
 
@@ -23,17 +23,17 @@
 }
 
 + (void)resetCoverageProfileCounters {
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_PROFILING)
   // In this call, the already-dump flag is also reset, so that the same file
   // can be dumped to again.
   __llvm_profile_reset_counters();
-#endif  // BUILDFLAG(CLANG_COVERAGE)
+#endif  // BUILDFLAG(CLANG_PROFILING)
 }
 
 + (void)writeClangCoverageProfile {
-#if BUILDFLAG(CLANG_COVERAGE)
-  base::WriteClangCoverageProfile();
-#endif  // BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_PROFILING)
+  base::WriteClangProfilingProfile();
+#endif  // BUILDFLAG(CLANG_PROFILING)
 }
 
 @end
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index f285a72..a94b3d0 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -214,6 +214,7 @@
   "src/components/LibraryInfo/src/MDCLibraryInfo.h",
   "src/components/LibraryInfo/src/MaterialLibraryInfo.h",
   "src/components/List/src/MDCBaseCell.h",
+  "src/components/List/src/MDCSelfSizingLayoutAttributes.h",
   "src/components/List/src/MDCSelfSizingStereoCell.h",
   "src/components/List/src/MaterialList.h",
   "src/components/List/src/Theming/MDCBaseCell+MaterialTheming.h",
@@ -971,6 +972,7 @@
   "src/components/LibraryInfo/src/MaterialLibraryInfo.h",
   "src/components/List/src/MDCBaseCell.h",
   "src/components/List/src/MDCBaseCell.m",
+  "src/components/List/src/MDCSelfSizingLayoutAttributes.h",
   "src/components/List/src/MDCSelfSizingStereoCell.h",
   "src/components/List/src/MDCSelfSizingStereoCell.m",
   "src/components/List/src/MaterialList.h",
diff --git a/media/capture/mojom/BUILD.gn b/media/capture/mojom/BUILD.gn
index a2aaecee..bfa974c 100644
--- a/media/capture/mojom/BUILD.gn
+++ b/media/capture/mojom/BUILD.gn
@@ -23,24 +23,6 @@
   export_header_blink = "third_party/blink/public/platform/web_common.h"
 }
 
-component("video_capture_mojom_support") {
-  sources = [
-    "video_capture_types_mojom_traits.cc",
-    "video_capture_types_mojom_traits.h",
-  ]
-  public_deps = [
-    ":video_capture_shared_cpp_sources",
-    "//media/base/ipc",
-    "//media/capture:capture_base",
-  ]
-  deps = [
-    "//media",
-    "//media/mojo/mojom",
-    "//ui/gfx/geometry/mojom:mojom_traits",
-  ]
-  defines = [ "IS_MEDIA_CAPTURE_MOJOM_TRAITS_IMPL" ]
-}
-
 mojom("image_capture") {
   sources = [ "image_capture.mojom" ]
 
diff --git a/media/capture/mojom/video_capture_types.typemap b/media/capture/mojom/video_capture_types.typemap
index 39fac0c..bbe592f 100644
--- a/media/capture/mojom/video_capture_types.typemap
+++ b/media/capture/mojom/video_capture_types.typemap
@@ -9,8 +9,24 @@
   "//media/capture/video/video_capture_device_descriptor.h",
   "//media/capture/video/video_capture_device_info.h",
 ]
+
 traits_headers = [ "//media/capture/mojom/video_capture_types_mojom_traits.h" ]
-public_deps = [ "//media/capture/mojom:video_capture_mojom_support" ]
+
+sources = [
+  "//media/capture/mojom/video_capture_types_mojom_traits.cc",
+]
+
+deps = [
+  "//media",
+  "//media/capture:capture_base",
+  "//media/mojo/mojom",
+  "//ui/gfx/geometry/mojom:mojom_traits",
+]
+
+public_deps = [
+  "//media/base/ipc",
+]
+
 type_mappings = [
   "media.mojom.ResolutionChangePolicy=::media::ResolutionChangePolicy",
   "media.mojom.PowerLineFrequency=::media::PowerLineFrequency",
diff --git a/media/capture/mojom/video_capture_types_for_blink.typemap b/media/capture/mojom/video_capture_types_for_blink.typemap
new file mode 100644
index 0000000..485c4179
--- /dev/null
+++ b/media/capture/mojom/video_capture_types_for_blink.typemap
@@ -0,0 +1,32 @@
+# 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.
+
+mojom = "//media/capture/mojom/video_capture_types.mojom"
+
+public_headers = [
+  "//media/capture/video_capture_types.h",
+  "//media/capture/video/video_capture_device_descriptor.h",
+  "//media/capture/video/video_capture_device_info.h",
+]
+
+traits_headers = [ "//media/capture/mojom/video_capture_types_mojom_traits.h" ]
+
+deps = [
+  "//media",
+  "//media/capture:capture_base",
+]
+
+type_mappings = [
+  "media.mojom.ResolutionChangePolicy=::media::ResolutionChangePolicy",
+  "media.mojom.PowerLineFrequency=::media::PowerLineFrequency",
+  "media.mojom.VideoCapturePixelFormat=::media::VideoPixelFormat",
+  "media.mojom.VideoCaptureBufferType=::media::VideoCaptureBufferType",
+  "media.mojom.VideoCaptureError=::media::VideoCaptureError",
+  "media.mojom.VideoCaptureFrameDropReason=::media::VideoCaptureFrameDropReason",
+  "media.mojom.VideoCaptureFormat=::media::VideoCaptureFormat",
+  "media.mojom.VideoCaptureParams=::media::VideoCaptureParams",
+  "media.mojom.VideoCaptureDeviceDescriptor=::media::VideoCaptureDeviceDescriptor",
+  "media.mojom.VideoCaptureDeviceInfo=::media::VideoCaptureDeviceInfo",
+  "media.mojom.VideoFacingMode=::media::VideoFacingMode",
+]
diff --git a/media/capture/mojom/video_capture_types_mojom_traits.h b/media/capture/mojom/video_capture_types_mojom_traits.h
index ebe98f62..4dc3b55 100644
--- a/media/capture/mojom/video_capture_types_mojom_traits.h
+++ b/media/capture/mojom/video_capture_types_mojom_traits.h
@@ -6,7 +6,7 @@
 #define MEDIA_CAPTURE_MOJOM_VIDEO_CAPTURE_TYPES_MOJOM_TRAITS_H_
 
 #include "media/base/video_facing.h"
-#include "media/capture/mojom/video_capture_types.mojom-shared.h"
+#include "media/capture/mojom/video_capture_types.mojom.h"
 #include "media/capture/video/video_capture_device_descriptor.h"
 #include "media/capture/video/video_capture_device_info.h"
 #include "media/capture/video_capture_types.h"
@@ -14,9 +14,8 @@
 namespace mojo {
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::ResolutionChangePolicy,
-               media::ResolutionChangePolicy> {
+struct EnumTraits<media::mojom::ResolutionChangePolicy,
+                  media::ResolutionChangePolicy> {
   static media::mojom::ResolutionChangePolicy ToMojom(
       media::ResolutionChangePolicy policy);
 
@@ -25,8 +24,7 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::PowerLineFrequency, media::PowerLineFrequency> {
+struct EnumTraits<media::mojom::PowerLineFrequency, media::PowerLineFrequency> {
   static media::mojom::PowerLineFrequency ToMojom(
       media::PowerLineFrequency frequency);
 
@@ -35,8 +33,8 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::VideoCapturePixelFormat, media::VideoPixelFormat> {
+struct EnumTraits<media::mojom::VideoCapturePixelFormat,
+                  media::VideoPixelFormat> {
   static media::mojom::VideoCapturePixelFormat ToMojom(
       media::VideoPixelFormat input);
   static bool FromMojom(media::mojom::VideoCapturePixelFormat input,
@@ -44,9 +42,8 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::VideoCaptureBufferType,
-               media::VideoCaptureBufferType> {
+struct EnumTraits<media::mojom::VideoCaptureBufferType,
+                  media::VideoCaptureBufferType> {
   static media::mojom::VideoCaptureBufferType ToMojom(
       media::VideoCaptureBufferType buffer_type);
 
@@ -55,8 +52,7 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::VideoCaptureError, media::VideoCaptureError> {
+struct EnumTraits<media::mojom::VideoCaptureError, media::VideoCaptureError> {
   static media::mojom::VideoCaptureError ToMojom(
       media::VideoCaptureError buffer_type);
 
@@ -65,9 +61,8 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::VideoCaptureFrameDropReason,
-               media::VideoCaptureFrameDropReason> {
+struct EnumTraits<media::mojom::VideoCaptureFrameDropReason,
+                  media::VideoCaptureFrameDropReason> {
   static media::mojom::VideoCaptureFrameDropReason ToMojom(
       media::VideoCaptureFrameDropReason buffer_type);
 
@@ -76,25 +71,22 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::VideoFacingMode, media::VideoFacingMode> {
+struct EnumTraits<media::mojom::VideoFacingMode, media::VideoFacingMode> {
   static media::mojom::VideoFacingMode ToMojom(media::VideoFacingMode input);
   static bool FromMojom(media::mojom::VideoFacingMode input,
                         media::VideoFacingMode* output);
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::VideoCaptureApi, media::VideoCaptureApi> {
+struct EnumTraits<media::mojom::VideoCaptureApi, media::VideoCaptureApi> {
   static media::mojom::VideoCaptureApi ToMojom(media::VideoCaptureApi input);
   static bool FromMojom(media::mojom::VideoCaptureApi input,
                         media::VideoCaptureApi* output);
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    EnumTraits<media::mojom::VideoCaptureTransportType,
-               media::VideoCaptureTransportType> {
+struct EnumTraits<media::mojom::VideoCaptureTransportType,
+                  media::VideoCaptureTransportType> {
   static media::mojom::VideoCaptureTransportType ToMojom(
       media::VideoCaptureTransportType input);
   static bool FromMojom(media::mojom::VideoCaptureTransportType input,
@@ -102,9 +94,8 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    StructTraits<media::mojom::VideoCaptureFormatDataView,
-                 media::VideoCaptureFormat> {
+struct StructTraits<media::mojom::VideoCaptureFormatDataView,
+                    media::VideoCaptureFormat> {
   static const gfx::Size& frame_size(const media::VideoCaptureFormat& format) {
     return format.frame_size;
   }
@@ -123,9 +114,8 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    StructTraits<media::mojom::VideoCaptureParamsDataView,
-                 media::VideoCaptureParams> {
+struct StructTraits<media::mojom::VideoCaptureParamsDataView,
+                    media::VideoCaptureParams> {
   static media::VideoCaptureFormat requested_format(
       const media::VideoCaptureParams& params) {
     return params.requested_format;
@@ -156,9 +146,8 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    StructTraits<media::mojom::VideoCaptureDeviceDescriptorDataView,
-                 media::VideoCaptureDeviceDescriptor> {
+struct StructTraits<media::mojom::VideoCaptureDeviceDescriptorDataView,
+                    media::VideoCaptureDeviceDescriptor> {
   static const std::string& display_name(
       const media::VideoCaptureDeviceDescriptor& input) {
     return input.display_name();
@@ -194,9 +183,8 @@
 };
 
 template <>
-struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
-    StructTraits<media::mojom::VideoCaptureDeviceInfoDataView,
-                 media::VideoCaptureDeviceInfo> {
+struct StructTraits<media::mojom::VideoCaptureDeviceInfoDataView,
+                    media::VideoCaptureDeviceInfo> {
   static const media::VideoCaptureDeviceDescriptor& descriptor(
       const media::VideoCaptureDeviceInfo& input) {
     return input.descriptor;
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index 2d25b96..1a41207 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -329,13 +329,10 @@
     if (is_chromeos) {
       sources += [ "backend/cups_ipp_helper_unittest.cc" ]
     } else {
-      sources += [ "backend/cups_helper_unittest.cc" ]
-      if (is_linux) {
-        sources += [ "backend/print_backend_cups_linux_unittest.cc" ]
-      }
-      if (is_mac) {
-        sources += [ "backend/print_backend_cups_mac_unittest.cc" ]
-      }
+      sources += [
+        "backend/cups_helper_unittest.cc",
+        "backend/print_backend_cups_unittest.cc",
+      ]
     }
   }
 }
diff --git a/printing/backend/print_backend_cups_linux_unittest.cc b/printing/backend/print_backend_cups_linux_unittest.cc
deleted file mode 100644
index ae11eb2..0000000
--- a/printing/backend/print_backend_cups_linux_unittest.cc
+++ /dev/null
@@ -1,34 +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 <cups/cups.h>
-
-#include "printing/backend/print_backend.h"
-#include "printing/backend/print_backend_cups.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace printing {
-
-TEST(PrintBackendCupsLinuxTest, PrinterBasicInfoFromCUPS) {
-  const char kName[] = "printer";
-  cups_dest_t* printer = nullptr;
-  ASSERT_EQ(1, cupsAddDest(kName, nullptr, 0, &printer));
-
-  int num_options = 0;
-  cups_option_t* options = nullptr;
-  num_options =
-      cupsAddOption("printer-info", "description", num_options, &options);
-  printer->num_options = num_options;
-  printer->options = options;
-
-  PrinterBasicInfo printer_info;
-  PrintBackendCUPS::PrinterBasicInfoFromCUPS(*printer, &printer_info);
-  cupsFreeDests(1, printer);
-
-  EXPECT_EQ("printer", printer_info.printer_name);
-  EXPECT_EQ("printer", printer_info.display_name);
-  EXPECT_EQ("description", printer_info.printer_description);
-}
-
-}  // namespace printing
diff --git a/printing/backend/print_backend_cups_mac_unittest.cc b/printing/backend/print_backend_cups_mac_unittest.cc
deleted file mode 100644
index 6732df0..0000000
--- a/printing/backend/print_backend_cups_mac_unittest.cc
+++ /dev/null
@@ -1,35 +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 <cups/cups.h>
-
-#include "printing/backend/print_backend.h"
-#include "printing/backend/print_backend_cups.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace printing {
-
-TEST(PrintBackendCupsMacTest, PrinterBasicInfoFromCUPS) {
-  const char kName[] = "printer";
-  cups_dest_t* printer = nullptr;
-  ASSERT_EQ(1, cupsAddDest(kName, nullptr, 0, &printer));
-
-  int num_options = 0;
-  cups_option_t* options = nullptr;
-  num_options = cupsAddOption("printer-info", "name", num_options, &options);
-  num_options = cupsAddOption("printer-make-and-model", "description",
-                              num_options, &options);
-  printer->num_options = num_options;
-  printer->options = options;
-
-  PrinterBasicInfo printer_info;
-  PrintBackendCUPS::PrinterBasicInfoFromCUPS(*printer, &printer_info);
-  cupsFreeDests(1, printer);
-
-  EXPECT_EQ("printer", printer_info.printer_name);
-  EXPECT_EQ("name", printer_info.display_name);
-  EXPECT_EQ("description", printer_info.printer_description);
-}
-
-}  // namespace printing
diff --git a/printing/backend/print_backend_cups_unittest.cc b/printing/backend/print_backend_cups_unittest.cc
new file mode 100644
index 0000000..aef9f13
--- /dev/null
+++ b/printing/backend/print_backend_cups_unittest.cc
@@ -0,0 +1,50 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/print_backend_cups.h"
+
+#include <cups/cups.h>
+
+#include "build/build_config.h"
+#include "printing/backend/print_backend.h"
+#include "printing/backend/print_backend_consts.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace printing {
+
+TEST(PrintBackendCupsTest, PrinterBasicInfoFromCUPS) {
+  constexpr char kName[] = "printer";
+  cups_dest_t* printer = nullptr;
+  ASSERT_EQ(
+      1, cupsAddDest(kName, /*instance=*/nullptr, /*num_dests=*/0, &printer));
+
+  int num_options = 0;
+  cups_option_t* options = nullptr;
+#if defined(OS_MACOSX)
+  num_options =
+      cupsAddOption(kCUPSOptPrinterInfo, "info", num_options, &options);
+  num_options = cupsAddOption(kCUPSOptPrinterMakeAndModel, "description",
+                              num_options, &options);
+#else
+  num_options =
+      cupsAddOption(kCUPSOptPrinterInfo, "description", num_options, &options);
+#endif
+  printer->num_options = num_options;
+  printer->options = options;
+
+  PrinterBasicInfo printer_info;
+  EXPECT_TRUE(
+      PrintBackendCUPS::PrinterBasicInfoFromCUPS(*printer, &printer_info));
+  cupsFreeDests(/*num_dests=*/1, printer);
+
+  EXPECT_EQ(kName, printer_info.printer_name);
+#if defined(OS_MACOSX)
+  EXPECT_EQ("info", printer_info.display_name);
+#else
+  EXPECT_EQ(kName, printer_info.display_name);
+#endif
+  EXPECT_EQ("description", printer_info.printer_description);
+}
+
+}  // namespace printing
diff --git a/remoting/protocol/transport_context.cc b/remoting/protocol/transport_context.cc
index 48ee156..e6144e4 100644
--- a/remoting/protocol/transport_context.cc
+++ b/remoting/protocol/transport_context.cc
@@ -11,6 +11,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "remoting/base/logging.h"
 #include "remoting/base/url_request.h"
 #include "remoting/protocol/port_allocator_factory.h"
 #include "third_party/webrtc/rtc_base/socket_address.h"
@@ -31,6 +32,28 @@
 constexpr base::TimeDelta kMinimumIceConfigLifetime =
     base::TimeDelta::FromHours(1);
 
+void PrintIceConfig(const IceConfig& ice_config) {
+  HOST_LOG << "Received IceConfig:";
+  HOST_LOG << "  STUN: [";
+  for (auto& stun_server : ice_config.stun_servers) {
+    HOST_LOG << "    " << stun_server.ToString() << ",";
+  }
+  HOST_LOG << "  ]";
+  HOST_LOG << "  TURN: [";
+  for (auto& turn_server : ice_config.turn_servers) {
+    HOST_LOG << "    {";
+    HOST_LOG << "      username: " << turn_server.credentials.username;
+    HOST_LOG << "      password: " << turn_server.credentials.password;
+    for (auto& port : turn_server.ports) {
+      HOST_LOG << "      port: " << port.address.ToString();
+    }
+    HOST_LOG << "    },";
+  }
+  HOST_LOG << "  ]";
+  HOST_LOG << "  expiration time: " << ice_config.expiration_time;
+  HOST_LOG << "  max_bitrate_kbps: " << ice_config.max_bitrate_kbps;
+}
+
 }  // namespace
 
 #if !defined(OS_NACL)
@@ -81,6 +104,7 @@
   // Don't need to make ICE config request if both STUN and Relay are disabled.
   if ((network_settings_.flags & (NetworkSettings::NAT_TRAVERSAL_STUN |
                                   NetworkSettings::NAT_TRAVERSAL_RELAY)) == 0) {
+    HOST_LOG << "Skipping ICE Config request as STUN and RELAY are disabled";
     return;
   }
 
@@ -108,6 +132,8 @@
   ice_config_[relay_mode] = ice_config;
   ice_config_request_[relay_mode].reset();
 
+  PrintIceConfig(ice_config);
+
   auto& callback_list = pending_ice_config_callbacks_[relay_mode];
   while (!callback_list.empty()) {
     callback_list.begin()->Run(ice_config);
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
index e13fbc3..712f969 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -11,7 +11,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
@@ -128,7 +128,7 @@
 #endif  // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||
         // defined(MEMORY_SANITIZER)
 
-#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
+#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
   if (SyscallSets::IsPrctl(sysno)) {
     return Allow();
   }
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
index ddf69c49..fc36187c 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -24,7 +24,7 @@
 #include <time.h>
 #include <unistd.h>
 
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/files/scoped_file.h"
 #include "base/macros.h"
 #include "base/posix/eintr_wrapper.h"
@@ -345,7 +345,7 @@
 #define PR_CAPBSET_READ 23
 #endif
 
-#if !BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
+#if !BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
 BPF_DEATH_TEST_C(BaselinePolicy,
                  PrctlSigsys,
                  DEATH_SEGV_MESSAGE(GetPrctlErrorMessageContentForTests()),
diff --git a/testing/android/native_test/native_test_launcher.cc b/testing/android/native_test/native_test_launcher.cc
index c66af52b..9fd87ca8 100644
--- a/testing/android/native_test/native_test_launcher.cc
+++ b/testing/android/native_test/native_test_launcher.cc
@@ -18,7 +18,7 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/at_exit.h"
 #include "base/base_switches.h"
-#include "base/clang_coverage_buildflags.h"
+#include "base/clang_profiling_buildflags.h"
 #include "base/command_line.h"
 #include "base/debug/debugger.h"
 #include "base/files/file_path.h"
@@ -33,8 +33,8 @@
 #include "testing/android/native_test/native_test_jni_headers/NativeTest_jni.h"
 #include "testing/android/native_test/native_test_util.h"
 
-#if BUILDFLAG(CLANG_COVERAGE)
-#include "base/test/clang_coverage.h"
+#if BUILDFLAG(CLANG_PROFILING)
+#include "base/test/clang_profiling.h"
 #endif
 
 using base::android::JavaParamRef;
@@ -140,9 +140,9 @@
   ScopedMainEntryLogger scoped_main_entry_logger;
   main(argc, &argv[0]);
 
-// Explicitly write coverage data to LLVM profile file.
-#if BUILDFLAG(CLANG_COVERAGE)
-  base::WriteClangCoverageProfile();
+// Explicitly write profiling data to LLVM profile file.
+#if BUILDFLAG(CLANG_PROFILING)
+  base::WriteClangProfilingProfile();
 #endif
 }
 
diff --git a/testing/buildbot/filters/android.emulator.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator.chrome_public_test_apk.filter
index 71d1a545..b540cde 100644
--- a/testing/buildbot/filters/android.emulator.chrome_public_test_apk.filter
+++ b/testing/buildbot/filters/android.emulator.chrome_public_test_apk.filter
@@ -40,3 +40,6 @@
 -org.chromium.chrome.browser.autofill_assistant.AutofillAssistantBottomsheetTest.testNoResize
 -org.chromium.chrome.browser.autofill_assistant.AutofillAssistantBottomsheetTest.testResizeLayoutViewport
 -org.chromium.chrome.browser.autofill_assistant.AutofillAssistantBottomsheetTest.testResizeVisualViewport
+
+# crbug.com/1059046
+-org.chromium.chrome.browser.customtabs.CustomTabFromChromeExternalNavigationTest.testIntentWithRedirectToApp
diff --git a/testing/libfuzzer/fuzzer_test.gni b/testing/libfuzzer/fuzzer_test.gni
index 3ad8d15..9674919 100644
--- a/testing/libfuzzer/fuzzer_test.gni
+++ b/testing/libfuzzer/fuzzer_test.gni
@@ -24,6 +24,7 @@
 # - ubsan_options - UndefinedBehaviorSanitizer options.
 # - seed_corpus - a directory with seed corpus.
 # - seed_corpus_deps - dependencies for generating the seed corpus.
+# - grammar - defines a grammar used by a grammar based mutator.
 #
 # If use_libfuzzer gn flag is defined, then proper fuzzer would be build.
 # Without use_libfuzzer or use_afl a unit-test style binary would be built on
@@ -90,7 +91,7 @@
     if (defined(invoker.dict) || defined(invoker.libfuzzer_options) ||
         defined(invoker.asan_options) || defined(invoker.msan_options) ||
         defined(invoker.ubsan_options) ||
-        defined(invoker.environment_variables)) {
+        defined(invoker.environment_variables) || defined(invoker.grammar)) {
       if (defined(invoker.dict)) {
         # Copy dictionary to output.
         copy(target_name + "_dict_copy") {
@@ -142,6 +143,11 @@
           args += invoker.environment_variables
         }
 
+        if (defined(invoker.grammar)) {
+          args += [ "--grammar" ]
+          args += invoker.grammar
+        }
+
         outputs = [ "$root_build_dir/$config_file_name" ]
       }
       test_deps += [ ":" + config_file_name ]
diff --git a/testing/libfuzzer/gen_fuzzer_config.py b/testing/libfuzzer/gen_fuzzer_config.py
index bde9e14..5a0792ba 100755
--- a/testing/libfuzzer/gen_fuzzer_config.py
+++ b/testing/libfuzzer/gen_fuzzer_config.py
@@ -40,6 +40,7 @@
   parser.add_argument('--asan_options', nargs='+', default=[])
   parser.add_argument('--msan_options', nargs='+', default=[])
   parser.add_argument('--ubsan_options', nargs='+', default=[])
+  parser.add_argument('--grammar', nargs='+', default=[])
   parser.add_argument(
       '--environment_variables',
       nargs='+',
@@ -49,7 +50,8 @@
 
   # Script shouldn't be invoked without any arguments, but just in case.
   if not (args.dict or args.libfuzzer_options or args.environment_variables or
-          args.asan_options or args.msan_options or args.ubsan_options):
+          args.asan_options or args.msan_options or args.ubsan_options or
+          args.grammar):
     return
 
   config = ConfigParser.ConfigParser()
@@ -70,6 +72,9 @@
   AddSectionOptions(config, 'ubsan',
                     [option.split('=') for option in args.ubsan_options])
 
+  AddSectionOptions(config, 'grammar',
+                    [option.split('=') for option in args.grammar])
+
   AddSectionOptions(
       config, 'env',
       [option.split('=') for option in args.environment_variables])
diff --git a/testing/scripts/common.py b/testing/scripts/common.py
index a948205..f7b34541 100644
--- a/testing/scripts/common.py
+++ b/testing/scripts/common.py
@@ -56,11 +56,6 @@
   # behavior of the script.
   parser.add_argument('--args', type=parse_json, default=[])
 
-  parser.add_argument(
-      '--use-src-side-runtest-py', action='store_true',
-      help='Use the src-side copy of runtest.py, as opposed to the build-side '
-           'one')
-
   subparsers = parser.add_subparsers()
 
   run_parser = subparsers.add_parser('run')
diff --git a/third_party/android_crazy_linker/src/android_linker_testing.md b/third_party/android_crazy_linker/src/android_linker_testing.md
index 8c224c29..f38955b 100644
--- a/third_party/android_crazy_linker/src/android_linker_testing.md
+++ b/third_party/android_crazy_linker/src/android_linker_testing.md
@@ -4,13 +4,6 @@
 versions where dynamic linking is not as advanced. It provides
 `android_dlopen_ext` functionality, RELRO sharing, compressed relocations, etc.
 
-For crazy reasons outlined in `linker/test_case.py` this linker cannot be tested
-using GTest or instrumentation test, hence it also carries a custom testing
-framework. The tests are not run as part of CQ, but it is still desirable to run
-them before landing changes in code locations listed below.
-
-Sorry.
-
 These instructions assume
 [Building Chromium for Android](android_build_instructions.md) as a
 prerequisite.
@@ -23,12 +16,6 @@
 base/android/java/src/org/chromium/base/library_loader
 ```
 
-The tests themselves are living mostly in these places:
-```
-build/android/pylib/linker/test_case.py
-content/shell/android
-```
-
 ## Running native tests
 
 This will run both unittests and regression tests:
@@ -40,16 +27,6 @@
 Verbosity of the output can be increased by setting `CRAZY_DEBUG` to 1 in
 `crazy_linker_debug.h`.
 
-## Running Java Tests
-
-We recommend running these tests in Release mode, as there are known
-complications in testing with the component build. Setting `Linker.DEBUG` to
-`true` should also help increase verbosity of the output.
-```
-autoninja -C out/Release chromium_linker_test_apk
-out/Release/bin/run_chromium_linker_test_apk
-```
-
 ## Fuzzer Tests
 
 There are also a few tests for fuzzing the ZIP parser. The instructions to run
diff --git a/third_party/blink/perf_tests/shadow_dom/custom-detail-summary.js b/third_party/blink/perf_tests/shadow_dom/custom-detail-summary.js
index 10717e3d..cbf6b47 100644
--- a/third_party/blink/perf_tests/shadow_dom/custom-detail-summary.js
+++ b/third_party/blink/perf_tests/shadow_dom/custom-detail-summary.js
@@ -11,7 +11,7 @@
 customElements.define("my-detail", class extends HTMLElement {
   constructor() {
     super();
-    this.attachShadow({ mode: "open", slotting: "manual" });
+    this.attachShadow({ mode: "open", slotAssignment: "manual" });
   }
   connectedCallback() {
     const target = this;
diff --git a/third_party/blink/perf_tests/shadow_dom/declarative-api.html b/third_party/blink/perf_tests/shadow_dom/declarative-api.html
index e18cfd9..68481f8 100644
--- a/third_party/blink/perf_tests/shadow_dom/declarative-api.html
+++ b/third_party/blink/perf_tests/shadow_dom/declarative-api.html
@@ -4,7 +4,7 @@
 </div>
 <script>
   const host = document.querySelector("#host");
-  const shadow_root = host.attachShadow({ mode: 'open', slotting: 'auto' });
+  const shadow_root = host.attachShadow({ mode: 'open', slotAssignment: 'auto' });
   const slot1 = document.createElement("slot");
   const child1 = document.createElement("child");
   shadow_root.appendChild(slot1);
diff --git a/third_party/blink/perf_tests/shadow_dom/imperative-api-appendchild.html b/third_party/blink/perf_tests/shadow_dom/imperative-api-appendchild.html
index 18cef3a..ef2aba97 100644
--- a/third_party/blink/perf_tests/shadow_dom/imperative-api-appendchild.html
+++ b/third_party/blink/perf_tests/shadow_dom/imperative-api-appendchild.html
@@ -4,7 +4,7 @@
 </div>
 <script>
   const host = document.querySelector("#host");
-  const shadow_root = host.attachShadow({ mode: 'open', slotting: 'manual' });
+  const shadow_root = host.attachShadow({ mode: 'open', slotAssignment: 'manual' });
   const slot1 = document.createElement("slot");
   const child1 = document.createElement("child");
   slot1.assign([child1]);
diff --git a/third_party/blink/perf_tests/shadow_dom/imperative-api-assign.html b/third_party/blink/perf_tests/shadow_dom/imperative-api-assign.html
index 4c8a1e6d..06417d8 100644
--- a/third_party/blink/perf_tests/shadow_dom/imperative-api-assign.html
+++ b/third_party/blink/perf_tests/shadow_dom/imperative-api-assign.html
@@ -4,7 +4,7 @@
 </div>
 <script>
   const host = document.querySelector("#host");
-  const shadow_root = host.attachShadow({ mode: 'open', slotting: 'manual' });
+  const shadow_root = host.attachShadow({ mode: 'open', slotAssignment: 'manual' });
   const slot1 = document.createElement("slot");
   const child1 = document.createElement("child");
   shadow_root.appendChild(slot1);
diff --git a/third_party/blink/perf_tests/shadow_dom/imperative-api-assigned-elements.html b/third_party/blink/perf_tests/shadow_dom/imperative-api-assigned-elements.html
index 806bc47..6d63ba2 100644
--- a/third_party/blink/perf_tests/shadow_dom/imperative-api-assigned-elements.html
+++ b/third_party/blink/perf_tests/shadow_dom/imperative-api-assigned-elements.html
@@ -4,7 +4,7 @@
 </div>
 <script>
   const host = document.querySelector("#host");
-  const shadow_root = host.attachShadow({ mode: 'open', slotting: 'manual' });
+  const shadow_root = host.attachShadow({ mode: 'open', slotAssignment: 'manual' });
   const slot1 = document.createElement("slot");
   const child1 = document.createElement("child");
   slot1.assign([child1]);
diff --git a/third_party/blink/perf_tests/shadow_dom/imperative-api-assigned-slot.html b/third_party/blink/perf_tests/shadow_dom/imperative-api-assigned-slot.html
index 9ab6794..f77b996e 100644
--- a/third_party/blink/perf_tests/shadow_dom/imperative-api-assigned-slot.html
+++ b/third_party/blink/perf_tests/shadow_dom/imperative-api-assigned-slot.html
@@ -4,7 +4,7 @@
 </div>
 <script>
   const host = document.querySelector("#host");
-  const shadow_root = host.attachShadow({ mode: 'open', slotting: 'manual' });
+  const shadow_root = host.attachShadow({ mode: 'open', slotAssignment: 'manual' });
   const slot1 = document.createElement("slot");
   const child1 = document.createElement("child");
   slot1.assign([child1]);
diff --git a/third_party/blink/perf_tests/shadow_dom/imperative-api-insertbefore.html b/third_party/blink/perf_tests/shadow_dom/imperative-api-insertbefore.html
index 375a36c..7e4882c 100644
--- a/third_party/blink/perf_tests/shadow_dom/imperative-api-insertbefore.html
+++ b/third_party/blink/perf_tests/shadow_dom/imperative-api-insertbefore.html
@@ -5,7 +5,7 @@
 </div>
 <script>
   const host = document.querySelector("#host");
-  const shadow_root = host.attachShadow({ mode: 'open', slotting: 'manual' });
+  const shadow_root = host.attachShadow({ mode: 'open', slotAssignment: 'manual' });
   const slot1 = document.createElement("slot");
   const child1 = document.createElement("child");
   const child = document.querySelector("#child");
diff --git a/third_party/blink/perf_tests/shadow_dom/imperative-api.html b/third_party/blink/perf_tests/shadow_dom/imperative-api.html
index 186a72dd..56ca1c9 100644
--- a/third_party/blink/perf_tests/shadow_dom/imperative-api.html
+++ b/third_party/blink/perf_tests/shadow_dom/imperative-api.html
@@ -6,7 +6,7 @@
 <script>
   const host = document.querySelector("#host");
   const child1 = document.querySelector("#child1");
-  const shadow_root = host.attachShadow({ mode: "open", slotting: "manual" });
+  const shadow_root = host.attachShadow({ mode: "open", slotAssignment: "manual" });
   window.onload = function() {
     PerfTestRunner.measureTime({
       description: "Measure performance of slot assignment in manual-slotting mode in shadow root.",
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 82490ead..fced37a4 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2525,6 +2525,8 @@
   kQuicTransport = 3184,
   kQuicTransportStreamApis = 3185,
   kQuicTransportDatagramApis = 3186,
+  kV8Document_GetAnimations_Method = 3187,
+  kV8ShadowRoot_GetAnimations_Method = 3188,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h
index 8f319c1..c2c079e5 100644
--- a/third_party/blink/public/web/web_view.h
+++ b/third_party/blink/public/web/web_view.h
@@ -238,13 +238,6 @@
   // Get the visual viewport's size in CSS pixels.
   virtual gfx::SizeF VisualViewportSize() const = 0;
 
-  // Resizes the unscaled (page scale = 1.0) visual viewport. Normally the
-  // unscaled visual viewport is the same size as the main frame. The passed
-  // size becomes the size of the viewport when page scale = 1. This
-  // is used to shrink the visible viewport to allow things like the ChromeOS
-  // virtual keyboard to overlay over content but allow scrolling it into view.
-  virtual void ResizeVisualViewport(const WebSize&) = 0;
-
   // Sets the default minimum, and maximum page scale. These will be overridden
   // by the page or by the overrides below if they are set.
   virtual void SetDefaultPageScaleLimits(float min_scale, float max_scale) = 0;
@@ -301,19 +294,28 @@
 
   virtual float ZoomFactorForDeviceScaleFactor() = 0;
 
+  // This method is used for testing.
   // Resize the view at the same time as changing the state of the top
   // controls. If |browser_controls_shrink_layout| is true, the embedder shrunk
   // the WebView size by the browser controls height.
   virtual void ResizeWithBrowserControls(
-      const WebSize&,
+      const WebSize& main_frame_widget_size,
       float top_controls_height,
       float bottom_controls_height,
       bool browser_controls_shrink_layout) = 0;
+  // This method is used for testing.
+  // Resizes the unscaled (page scale = 1.0) visual viewport. Normally the
+  // unscaled visual viewport is the same size as the main frame. The passed
+  // size becomes the size of the viewport when page scale = 1. This
+  // is used to shrink the visible viewport to allow things like the ChromeOS
+  // virtual keyboard to overlay over content but allow scrolling it into view.
+  virtual void ResizeVisualViewport(const WebSize&) = 0;
 
   // Same as ResizeWithBrowserControls(const WebSize&,float,float,bool), but
   // includes all browser controls params such as the min heights.
   virtual void ResizeWithBrowserControls(
-      const WebSize&,
+      const WebSize& main_frame_widget_size,
+      const WebSize& visible_viewport_size,
       cc::BrowserControlsParams browser_controls_params) = 0;
 
   // Same as ResizeWithBrowserControls, but keeps the same BrowserControl
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni
index 7f1dd40..5c2451e6 100644
--- a/third_party/blink/renderer/bindings/generated_in_core.gni
+++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -107,8 +107,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_selection_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_shadow_root_mode.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_shadow_root_mode.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_shadow_root_slotting_mode.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_shadow_root_slotting_mode.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_slot_assignment_mode.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_slot_assignment_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_supported_type.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_supported_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_text_track_kind.cc",
diff --git a/third_party/blink/renderer/core/animation/animatable.cc b/third_party/blink/renderer/core/animation/animatable.cc
index d41b7c8..48505ebb 100644
--- a/third_party/blink/renderer/core/animation/animatable.cc
+++ b/third_party/blink/renderer/core/animation/animatable.cc
@@ -105,7 +105,8 @@
     return animations;
 
   for (const auto& animation :
-       element->GetDocument().GetDocumentAnimations().getAnimations()) {
+       element->GetDocument().GetDocumentAnimations().getAnimations(
+           element->GetTreeScope())) {
     DCHECK(animation->effect());
     // TODO(gtsteel) make this use the idl properties
     Element* target = To<KeyframeEffect>(animation->effect())->EffectTarget();
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.cc b/third_party/blink/renderer/core/animation/animation_timeline.cc
index f6b8996..3a67e258 100644
--- a/third_party/blink/renderer/core/animation/animation_timeline.cc
+++ b/third_party/blink/renderer/core/animation/animation_timeline.cc
@@ -63,6 +63,19 @@
   return result ? base::make_optional(result->InSecondsF()) : base::nullopt;
 }
 
+String AnimationTimeline::phase() const {
+  switch (Phase()) {
+    case TimelinePhase::kInactive:
+      return "inactive";
+    case TimelinePhase::kBefore:
+      return "before";
+    case TimelinePhase::kActive:
+      return "active";
+    case TimelinePhase::kAfter:
+      return "after";
+  }
+}
+
 void AnimationTimeline::ClearOutdatedAnimation(Animation* animation) {
   DCHECK(!animation->Outdated());
   outdated_animation_count_--;
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.h b/third_party/blink/renderer/core/animation/animation_timeline.h
index 7654135..1a0a55e 100644
--- a/third_party/blink/renderer/core/animation/animation_timeline.h
+++ b/third_party/blink/renderer/core/animation/animation_timeline.h
@@ -14,6 +14,8 @@
 
 class Document;
 
+enum class TimelinePhase { kInactive, kBefore, kActive, kAfter };
+
 class CORE_EXPORT AnimationTimeline : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -26,10 +28,12 @@
   base::Optional<double> CurrentTime();
   base::Optional<double> CurrentTimeSeconds();
 
+  String phase() const;
+  virtual TimelinePhase Phase() const = 0;
+
   virtual bool IsDocumentTimeline() const { return false; }
   virtual bool IsScrollTimeline() const { return false; }
   virtual bool IsActive() const = 0;
-  virtual String phase() const = 0;
   // Returns the initial start time for animations that are linked to this
   // timeline. This method gets invoked when initializing the start time of an
   // animation on this timeline for the first time. It exists because the
diff --git a/third_party/blink/renderer/core/animation/document_animation.h b/third_party/blink/renderer/core/animation/document_animation.h
index dbaf712..0b51df67 100644
--- a/third_party/blink/renderer/core/animation/document_animation.h
+++ b/third_party/blink/renderer/core/animation/document_animation.h
@@ -19,10 +19,6 @@
   static DocumentTimeline* timeline(Document& document) {
     return &document.Timeline();
   }
-
-  static HeapVector<Member<Animation>> getAnimations(Document& document) {
-    return document.GetDocumentAnimations().getAnimations();
-  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/document_animation.idl b/third_party/blink/renderer/core/animation/document_animation.idl
index abefa7f..7a3eabb0 100644
--- a/third_party/blink/renderer/core/animation/document_animation.idl
+++ b/third_party/blink/renderer/core/animation/document_animation.idl
@@ -9,6 +9,4 @@
     RuntimeEnabled=WebAnimationsAPI
 ] partial interface Document {
     readonly attribute DocumentTimeline timeline;
-
-    sequence<Animation> getAnimations();
 };
diff --git a/third_party/blink/renderer/core/animation/document_animations.cc b/third_party/blink/renderer/core/animation/document_animations.cc
index 3a68315e..104c0de 100644
--- a/third_party/blink/renderer/core/animation/document_animations.cc
+++ b/third_party/blink/renderer/core/animation/document_animations.cc
@@ -121,7 +121,8 @@
     timeline->ScheduleNextService();
 }
 
-HeapVector<Member<Animation>> DocumentAnimations::getAnimations() {
+HeapVector<Member<Animation>> DocumentAnimations::getAnimations(
+    const TreeScope& tree_scope) {
   // This method implements the Document::getAnimations method defined in the
   // web-animations-1 spec.
   // https://drafts.csswg.org/web-animations-1/#dom-document-getanimations
@@ -130,9 +131,9 @@
   document_->UpdateStyleAndLayoutTree();
   HeapVector<Member<Animation>> animations;
   if (document_->GetPage())
-    animations = document_->GetPage()->Animator().GetAnimations(document_);
+    animations = document_->GetPage()->Animator().GetAnimations(tree_scope);
   else
-    GetAnimationsTargetingDocument(document_, animations);
+    GetAnimationsTargetingTreeScope(animations, tree_scope);
 
   std::sort(animations.begin(), animations.end(), CompareAnimations);
   return animations;
@@ -143,9 +144,9 @@
   visitor->Trace(timelines_);
 }
 
-void DocumentAnimations::GetAnimationsTargetingDocument(
-    Document* document_,
-    HeapVector<Member<Animation>>& animations) {
+void DocumentAnimations::GetAnimationsTargetingTreeScope(
+    HeapVector<Member<Animation>>& animations,
+    const TreeScope& tree_scope) {
   // This method follows the timelines in a given docmuent and append all the
   // animations to the reference animations.
   for (auto& timeline : timelines_) {
@@ -156,13 +157,12 @@
                                    !animation->effect()->IsInEffect())) {
         continue;
       }
-      if (auto* effect = DynamicTo<KeyframeEffect>(animation->effect())) {
-        Element* target = effect->target();
-        if (!target || !target->isConnected() ||
-            document_ != target->GetDocument()) {
-          continue;
-        }
-      }
+      auto* effect = DynamicTo<KeyframeEffect>(animation->effect());
+      Element* target = effect->target();
+      if (!target || !target->isConnected())
+        continue;
+      if (&tree_scope != &target->GetTreeScope())
+        continue;
       animations.push_back(animation);
     }
   }
diff --git a/third_party/blink/renderer/core/animation/document_animations.h b/third_party/blink/renderer/core/animation/document_animations.h
index a570ceb..2627b12 100644
--- a/third_party/blink/renderer/core/animation/document_animations.h
+++ b/third_party/blink/renderer/core/animation/document_animations.h
@@ -51,8 +51,8 @@
   void UpdateAnimationTimingForAnimationFrame();
   bool NeedsAnimationTimingUpdate();
   void UpdateAnimationTimingIfNeeded();
-  void GetAnimationsTargetingDocument(Document*,
-                                      HeapVector<Member<Animation>>&);
+  void GetAnimationsTargetingTreeScope(HeapVector<Member<Animation>>&,
+                                       const TreeScope&);
 
   // Updates existing animations as part of generating a new (document
   // lifecycle) frame. Note that this considers and updates state for
@@ -61,7 +61,7 @@
       DocumentLifecycle::LifecycleState required_lifecycle_state,
       const PaintArtifactCompositor* paint_artifact_compositor);
 
-  HeapVector<Member<Animation>> getAnimations();
+  HeapVector<Member<Animation>> getAnimations(const TreeScope&);
   void Trace(Visitor*);
 
  private:
diff --git a/third_party/blink/renderer/core/animation/document_animations_test.cc b/third_party/blink/renderer/core/animation/document_animations_test.cc
index 5588947d..c67f99b3 100644
--- a/third_party/blink/renderer/core/animation/document_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/document_animations_test.cc
@@ -23,7 +23,7 @@
   MockAnimationTimeline(Document* document) : AnimationTimeline(document) {}
 
   MOCK_CONST_METHOD0(IsActive, bool());
-  MOCK_CONST_METHOD0(phase, String());
+  MOCK_CONST_METHOD0(Phase, TimelinePhase());
   MOCK_METHOD0(InitialStartTimeForAnimations,
                base::Optional<base::TimeDelta>());
   MOCK_METHOD0(NeedsAnimationTimingUpdate, bool());
diff --git a/third_party/blink/renderer/core/animation/document_timeline.cc b/third_party/blink/renderer/core/animation/document_timeline.cc
index 613f486..c6675c40 100644
--- a/third_party/blink/renderer/core/animation/document_timeline.cc
+++ b/third_party/blink/renderer/core/animation/document_timeline.cc
@@ -187,11 +187,11 @@
   return result;
 }
 
-String DocumentTimeline::phase() const {
+TimelinePhase DocumentTimeline::Phase() const {
   if (IsActive()) {
-    return "active";
+    return TimelinePhase::kActive;
   }
-  return "inactive";
+  return TimelinePhase::kInactive;
 }
 
 void DocumentTimeline::PauseAnimationsForTesting(double pause_time) {
diff --git a/third_party/blink/renderer/core/animation/document_timeline.h b/third_party/blink/renderer/core/animation/document_timeline.h
index ff6e5cb..c7f8c6d 100644
--- a/third_party/blink/renderer/core/animation/document_timeline.h
+++ b/third_party/blink/renderer/core/animation/document_timeline.h
@@ -75,7 +75,7 @@
   Animation* Play(AnimationEffect*);
 
   bool IsActive() const override;
-  String phase() const override;
+  TimelinePhase Phase() const override;
   base::Optional<base::TimeDelta> InitialStartTimeForAnimations() override;
   bool HasPendingUpdates() const {
     return !animations_needing_update_.IsEmpty();
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.cc b/third_party/blink/renderer/core/animation/scroll_timeline.cc
index ac0973c..1d9fa53 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.cc
@@ -145,31 +145,21 @@
   return layout_box && layout_box->HasOverflowClip();
 }
 
-String ScrollTimeline::phase() const {
-  // TODO(crbug.com/1046833) - Not yet Implemented
-  return "inactive";
-}
-
-// Scroll-linked animations are initialized with the start time of zero.
-base::Optional<base::TimeDelta>
-ScrollTimeline::InitialStartTimeForAnimations() {
-  return base::TimeDelta();
-}
-
-void ScrollTimeline::ScheduleNextService() {
-  DCHECK_EQ(outdated_animation_count_, 0U);
-  if (AnimationsNeedingUpdateCount() == 0)
-    return;
-  if (CurrentTimeInternal() != last_current_time_internal_)
-    ScheduleServiceOnNextFrame();
+TimelinePhase ScrollTimeline::Phase() const {
+  return ComputePhaseAndCurrentTime().phase;
 }
 
 base::Optional<base::TimeDelta> ScrollTimeline::CurrentTimeInternal() {
+  return ComputePhaseAndCurrentTime().current_time;
+}
+
+ScrollTimeline::PhaseAndTime ScrollTimeline::ComputePhaseAndCurrentTime()
+    const {
   // 1. If scroll timeline is inactive, return an unresolved time value.
   // https://github.com/WICG/scroll-animations/issues/31
   // https://wicg.github.io/scroll-animations/#current-time-algorithm
   if (!IsActive()) {
-    return base::nullopt;
+    return {TimelinePhase::kInactive, base::nullopt};
   }
   LayoutBox* layout_box = resolved_scroll_source_->GetLayoutBox();
   // 2. Otherwise, let current scroll offset be the current scroll offset of
@@ -188,10 +178,10 @@
   if (current_offset < resolved_start_scroll_offset) {
     // Return an unresolved time value if fill is none or forwards.
     if (fill_ == Timing::FillMode::NONE || fill_ == Timing::FillMode::FORWARDS)
-      return base::nullopt;
+      return {TimelinePhase::kBefore, base::nullopt};
 
     // Otherwise, return 0.
-    return base::TimeDelta();
+    return {TimelinePhase::kBefore, base::TimeDelta()};
   }
 
   // 4. If current scroll offset is greater than or equal to endScrollOffset:
@@ -202,26 +192,37 @@
     if (resolved_end_scroll_offset < max_offset &&
         (fill_ == Timing::FillMode::NONE ||
          fill_ == Timing::FillMode::BACKWARDS)) {
-      return base::nullopt;
+      return {TimelinePhase::kAfter, base::nullopt};
     }
 
     // Otherwise, return the effective time range.
-    return base::TimeDelta::FromMillisecondsD(time_range_);
-  }
-
-  // This is not by the spec, but avoids a negative current time.
-  // See https://github.com/WICG/scroll-animations/issues/20
-  if (resolved_start_scroll_offset >= resolved_end_scroll_offset) {
-    return base::nullopt;
+    return {TimelinePhase::kAfter,
+            base::TimeDelta::FromMillisecondsD(time_range_)};
   }
 
   // 5. Return the result of evaluating the following expression:
   //   ((current scroll offset - startScrollOffset) /
   //      (endScrollOffset - startScrollOffset)) * effective time range
-  return base::TimeDelta::FromMillisecondsD(
-      ((current_offset - resolved_start_scroll_offset) /
-       (resolved_end_scroll_offset - resolved_start_scroll_offset)) *
-      time_range_);
+  base::Optional<base::TimeDelta> calculated_current_time =
+      base::TimeDelta::FromMillisecondsD(
+          ((current_offset - resolved_start_scroll_offset) /
+           (resolved_end_scroll_offset - resolved_start_scroll_offset)) *
+          time_range_);
+  return {TimelinePhase::kActive, calculated_current_time};
+}
+
+// Scroll-linked animations are initialized with the start time of zero.
+base::Optional<base::TimeDelta>
+ScrollTimeline::InitialStartTimeForAnimations() {
+  return base::TimeDelta();
+}
+
+void ScrollTimeline::ScheduleNextService() {
+  DCHECK_EQ(outdated_animation_count_, 0U);
+  if (AnimationsNeedingUpdateCount() == 0)
+    return;
+  if (CurrentTimeInternal() != last_current_time_internal_)
+    ScheduleServiceOnNextFrame();
 }
 
 Element* ScrollTimeline::scrollSource() {
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.h b/third_party/blink/renderer/core/animation/scroll_timeline.h
index 39e8517a4..a359e43 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.h
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.h
@@ -54,7 +54,7 @@
   // have a CSS layout box, or if its layout box is not a scroll container.
   // https://github.com/WICG/scroll-animations/issues/31
   bool IsActive() const override;
-  String phase() const override;
+  TimelinePhase Phase() const override;
   base::Optional<base::TimeDelta> InitialStartTimeForAnimations() override;
 
   void ScheduleNextService() override;
@@ -102,6 +102,13 @@
   Member<Element> scroll_source_;
   Member<Node> resolved_scroll_source_;
 
+  struct PhaseAndTime {
+    TimelinePhase phase;
+    base::Optional<base::TimeDelta> current_time;
+  };
+
+  PhaseAndTime ComputePhaseAndCurrentTime() const;
+
   ScrollDirection orientation_;
   Member<CSSPrimitiveValue> start_scroll_offset_;
   Member<CSSPrimitiveValue> end_scroll_offset_;
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_test.cc b/third_party/blink/renderer/core/animation/scroll_timeline_test.cc
index 432e696..25ac9078 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_test.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_test.cc
@@ -157,11 +157,74 @@
       ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
 
   bool current_time_is_null = false;
-  scrollable_area->SetScrollOffset(ScrollOffset(0, 50),
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 20),
                                    mojom::blink::ScrollType::kProgrammatic);
+  EXPECT_EQ(scroll_timeline->phase(), "before");
   scroll_timeline->currentTime(current_time_is_null);
   EXPECT_TRUE(current_time_is_null);
   EXPECT_TRUE(scroll_timeline->IsActive());
+
+  current_time_is_null = false;
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 60),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  EXPECT_EQ(scroll_timeline->phase(), "before");
+  scroll_timeline->currentTime(current_time_is_null);
+  EXPECT_TRUE(current_time_is_null);
+  EXPECT_TRUE(scroll_timeline->IsActive());
+
+  current_time_is_null = false;
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 100),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  EXPECT_EQ(scroll_timeline->phase(), "after");
+  scroll_timeline->currentTime(current_time_is_null);
+  EXPECT_TRUE(current_time_is_null);
+  EXPECT_TRUE(scroll_timeline->IsActive());
+}
+
+TEST_F(ScrollTimelineTest, PhasesAreCorrectWhenUsingOffsets) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #scroller { overflow: scroll; width: 100px; height: 100px; }
+      #spacer { height: 1000px; }
+    </style>
+    <div id='scroller'>
+      <div id ='spacer'></div>
+    </div>
+  )HTML");
+
+  LayoutBoxModelObject* scroller =
+      ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller"));
+  ASSERT_TRUE(scroller);
+  ASSERT_TRUE(scroller->HasOverflowClip());
+  PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea();
+  ASSERT_TRUE(scrollable_area);
+  ScrollTimelineOptions* options = ScrollTimelineOptions::Create();
+  DoubleOrScrollTimelineAutoKeyword time_range =
+      DoubleOrScrollTimelineAutoKeyword::FromDouble(100);
+  options->setTimeRange(time_range);
+  options->setScrollSource(GetElementById("scroller"));
+  options->setStartScrollOffset("10px");
+  options->setEndScrollOffset("90px");
+  ScrollTimeline* scroll_timeline =
+      ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
+
+  EXPECT_EQ(scroll_timeline->phase(), "before");
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 10),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  EXPECT_EQ(scroll_timeline->phase(), "active");
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 50),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  EXPECT_EQ(scroll_timeline->phase(), "active");
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 90),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  EXPECT_EQ(scroll_timeline->phase(), "after");
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 100),
+                                   mojom::blink::ScrollType::kProgrammatic);
+  EXPECT_EQ(scroll_timeline->phase(), "after");
 }
 
 TEST_F(ScrollTimelineTest,
diff --git a/third_party/blink/renderer/core/css/vision_deficiency.cc b/third_party/blink/renderer/core/css/vision_deficiency.cc
index 66e9d39..5ff98d95 100644
--- a/third_party/blink/renderer/core/css/vision_deficiency.cc
+++ b/third_party/blink/renderer/core/css/vision_deficiency.cc
@@ -41,8 +41,7 @@
           "0.000 0.000 0.000 1.000 0.000 "
           "\"/>");
     case VisionDeficiency::kBlurredVision:
-      return CreateFilterDataUrl(
-          "<feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"2\"/>");
+      return CreateFilterDataUrl("<feGaussianBlur stdDeviation=\"2\"/>");
     case VisionDeficiency::kDeuteranomaly:
       return CreateFilterDataUrl(
           "<feColorMatrix values=\""
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index b270bc84..990f321 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -148,6 +148,8 @@
 void DisplayLockContext::UpdateActivationObservationIfNeeded() {
   if (!document_) {
     is_observed_ = false;
+    is_registered_for_lifecycle_notifications_ = false;
+    needs_intersection_lock_check_ = false;
     return;
   }
 
@@ -165,10 +167,36 @@
     document_->RegisterDisplayLockActivationObservation(element_);
   } else if (!should_observe && is_observed_) {
     document_->UnregisterDisplayLockActivationObservation(element_);
+    // We don't need intersection lock checks if we are not observing
+    // intersections anymore.
+    needs_intersection_lock_check_ = false;
+    UpdateLifecycleNotificationRegistration();
   }
   is_observed_ = should_observe;
 }
 
+bool DisplayLockContext::NeedsLifecycleNotifications() const {
+  return HasResolver() || needs_intersection_lock_check_;
+}
+
+void DisplayLockContext::UpdateLifecycleNotificationRegistration() {
+  if (!document_ || !document_->View()) {
+    is_registered_for_lifecycle_notifications_ = false;
+    return;
+  }
+
+  bool needs_notifications = NeedsLifecycleNotifications();
+  if (needs_notifications == is_registered_for_lifecycle_notifications_)
+    return;
+
+  is_registered_for_lifecycle_notifications_ = needs_notifications;
+  if (needs_notifications) {
+    document_->View()->RegisterForLifecycleNotifications(this);
+  } else {
+    document_->View()->UnregisterFromLifecycleNotifications(this);
+  }
+}
+
 void DisplayLockContext::SetActivatable(uint16_t activatable_mask) {
   if (IsLocked()) {
     // If we're locked, the activatable mask might change the activation
@@ -193,6 +221,9 @@
   update_budget_.reset();
   state_ = kLocked;
 
+  needs_intersection_lock_check_ = false;
+  UpdateLifecycleNotificationRegistration();
+
   // We're no longer activated, so if the signal didn't run yet, we should
   // cancel it.
   weak_factory_.InvalidateWeakPtrs();
@@ -283,11 +314,11 @@
 void DisplayLockContext::MakeResolver(ScriptState* script_state,
                                       Member<ScriptPromiseResolver>* resolver) {
   DCHECK(ConnectedToView());
-  document_->View()->RegisterForLifecycleNotifications(this);
   *resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  UpdateLifecycleNotificationRegistration();
 }
 
-bool DisplayLockContext::HasResolver() {
+bool DisplayLockContext::HasResolver() const {
   return update_resolver_;
 }
 
@@ -323,8 +354,7 @@
       break;
   }
   *resolver = nullptr;
-  if (!HasResolver() && ConnectedToView())
-    document_->View()->UnregisterFromLifecycleNotifications(this);
+  UpdateLifecycleNotificationRegistration();
 }
 
 bool DisplayLockContext::ShouldPerformUpdatePhase(
@@ -487,6 +517,57 @@
 
 void DisplayLockContext::ClearActivated() {
   css_is_activated_ = false;
+  // If we are no longer activated, then we're either committing or acquiring a
+  // lock. In either case, we don't need to rely on lifecycle observations to
+  // become hidden.
+  // TODO(vmpstr): This needs refactoring.
+  needs_intersection_lock_check_ = false;
+  UpdateLifecycleNotificationRegistration();
+}
+
+void DisplayLockContext::NotifyIsIntersectingViewport() {
+  // If we are now intersecting, then we are definitely not nested in a locked
+  // subtree and we don't need to lock as a result.
+  needs_intersection_lock_check_ = false;
+  UpdateLifecycleNotificationRegistration();
+
+  if (!IsLocked())
+    return;
+
+  DCHECK(IsActivatable(DisplayLockActivationReason::kViewportIntersection));
+  CommitForActivationWithSignal(
+      element_, DisplayLockActivationReason::kViewportIntersection);
+}
+
+void DisplayLockContext::NotifyIsNotIntersectingViewport() {
+  if (IsLocked()) {
+    DCHECK(!needs_intersection_lock_check_);
+    return;
+  }
+
+  // There are two situations we need to consider here:
+  // 1. We are off-screen but not nested in any other lock. This means we should
+  //    re-lock (also verify that the reason we're in this state is that we're
+  //    activated).
+  // 2. We are in a nested locked context. This means we don't actually know
+  //    whether we should lock or not. In order to avoid needless dirty of the
+  //    layout and style trees up to the nested context, we remain unlocked.
+  //    However, we also need to ensure that we relock if we become unnested.
+  //    So, we simply delay this check to the next frame (via LocalFrameView),
+  //    which will call this function again and so we can perform the check
+  //    again.
+  DCHECK(ConnectedToView());
+  auto* locked_ancestor =
+      DisplayLockUtilities::NearestLockedExclusiveAncestor(*element_);
+  if (locked_ancestor) {
+    needs_intersection_lock_check_ = true;
+    UpdateLifecycleNotificationRegistration();
+  } else {
+    DCHECK(IsActivated());
+    ClearActivated();
+    StartAcquire();
+    DCHECK(!needs_intersection_lock_check_);
+  }
 }
 
 bool DisplayLockContext::ShouldCommitForActivation(
@@ -814,7 +895,7 @@
 
   // Since we're observing the lifecycle updates, ensure that we listen to the
   // right document's view.
-  if (HasResolver()) {
+  if (is_registered_for_lifecycle_notifications_) {
     if (old_document.View())
       old_document.View()->UnregisterFromLifecycleNotifications(this);
     if (document_->View())
@@ -832,11 +913,28 @@
 }
 
 void DisplayLockContext::WillStartLifecycleUpdate(const LocalFrameView& view) {
+  DCHECK(NeedsLifecycleNotifications());
+  // If we have an update budget, then forward the call to it, so that it can
+  // prepare for the lifecycle by propagating the next phase's dirty bits.
   if (update_budget_)
     update_budget_->OnLifecycleChange(view.CurrentLifecycleData());
+
+  // We might have delayed processing intersection observation update (signal
+  // that we were not intersecting) because this context was nested in another
+  // locked context. At the start of the lifecycle, we should check whether
+  // that is still true. In other words, this call will check if we're still
+  // nested. If we are, we won't do anything. If we're not, then we will lock
+  // this context.
+  //
+  // Note that when we are no longer nested and and we have not received any
+  // notifications from the intersection observer, it means that we are not
+  // visible.
+  if (needs_intersection_lock_check_)
+    NotifyIsNotIntersectingViewport();
 }
 
 void DisplayLockContext::DidFinishLifecycleUpdate(const LocalFrameView& view) {
+  DCHECK(NeedsLifecycleNotifications());
   if (state_ == kCommitting) {
     FinishUpdateResolver(kResolve);
     state_ = kUnlocked;
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index 8b44ff1..83c80cd 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -144,6 +144,11 @@
   // Clear the activated flag.
   void ClearActivated();
 
+  // Is called by the intersection observer callback to inform us of the
+  // intersection state.
+  void NotifyIsIntersectingViewport();
+  void NotifyIsNotIntersectingViewport();
+
   // Acquire the lock, should only be called when unlocked.
   void StartAcquire();
   // Initiate a commit.
@@ -282,10 +287,14 @@
   }
 
  private:
-  friend class DisplayLockContextTest;
+  // Test friends.
   friend class DisplayLockBudgetTest;
-  friend class DisplayLockSuspendedHandle;
+  friend class DisplayLockContextRenderingTest;
+  friend class DisplayLockContextTest;
+
+  // Production friends.
   friend class DisplayLockBudget;
+  friend class DisplayLockSuspendedHandle;
 
   class StateChangeHelper {
     DISALLOW_NEW();
@@ -342,7 +351,7 @@
   // Helper functions to resolve the update/commit promises.
   enum ResolverState { kResolve, kReject, kDetach };
   void MakeResolver(ScriptState*, Member<ScriptPromiseResolver>*);
-  bool HasResolver();
+  bool HasResolver() const;
   void FinishUpdateResolver(ResolverState, const char* reject_reason = nullptr);
   void FinishResolver(Member<ScriptPromiseResolver>*,
                       ResolverState,
@@ -381,6 +390,12 @@
   // Scheduled by CommitForActivationWithSignal.
   void FireActivationEvent(Element* activated_element);
 
+  // Determines whether or not we need lifecycle notifications.
+  bool NeedsLifecycleNotifications() const;
+  // Updates the lifecycle notification registration based on whether we need
+  // the notifications.
+  void UpdateLifecycleNotificationRegistration();
+
   std::unique_ptr<DisplayLockBudget> update_budget_;
 
   Member<ScriptPromiseResolver> update_resolver_;
@@ -429,6 +444,17 @@
   // valid for CSS version of subtree-visibility.
   bool css_is_activated_ = false;
 
+  // Is set to true if we are registered for lifecycle notifications.
+  bool is_registered_for_lifecycle_notifications_ = false;
+
+  // This is set to true when we have delayed locking ourselves due to viewport
+  // intersection (or lack thereof) because we were nested in a locked subtree.
+  // In that case, we register for lifecycle notifications and check every time
+  // if we are still nested.
+  bool needs_intersection_lock_check_ = false;
+
+  // TODO(vmpstr): This is only needed while we're still sending activation
+  // events.
   base::WeakPtrFactory<DisplayLockContext> weak_factory_{this};
 };
 
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc b/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
index d211278..bfc8ec75 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
@@ -1700,6 +1700,10 @@
   DisplayLockContextRenderingTest()
       : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()),
         ScopedCSSSubtreeVisibilityHiddenMatchableForTest(true) {}
+
+  bool IsObservingLifecycle(DisplayLockContext* context) const {
+    return context->is_registered_for_lifecycle_notifications_;
+  }
 };
 
 TEST_F(DisplayLockContextRenderingTest, FrameDocumentRemovedWhileAcquire) {
@@ -1821,4 +1825,282 @@
   EXPECT_EQ(total_count, 4u);
 }
 
+TEST_F(DisplayLockContextRenderingTest,
+       NestedLockDoesNotInvalidateOnHideOrShow) {
+  SetHtmlInnerHTML(R"HTML(
+    <style>
+      .auto { subtree-visibility: auto; }
+      .hidden { subtree-visibility: hidden; }
+      .item { height: 10px; }
+      /* this is important to not invalidate layout when we hide the element! */
+      #outer { contain: style layout; }
+    </style>
+    <div id=outer>
+      <div id=unrelated>
+        <div id=inner class=auto>Content</div>
+      </div>
+    </div>
+  )HTML");
+
+  auto* inner_element = GetDocument().getElementById("inner");
+  auto* unrelated_element = GetDocument().getElementById("unrelated");
+  auto* outer_element = GetDocument().getElementById("outer");
+
+  UpdateAllLifecyclePhasesForTest();
+  // Intersection observer notifications run as a task.
+  test::RunPendingTasks();
+
+  // The intersection observation will unlock inner, which will cause a dirty
+  // layout bit to be propagated.
+  EXPECT_TRUE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_TRUE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_TRUE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_TRUE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Clear the layout.
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Verify lock state.
+  auto* inner_context = inner_element->GetDisplayLockContext();
+  ASSERT_TRUE(inner_context);
+  EXPECT_TRUE(inner_context->IsActivated());
+  EXPECT_FALSE(inner_context->IsLocked());
+
+  // Lock outer.
+  outer_element->setAttribute(html_names::kClassAttr, "hidden");
+  // Ensure the lock processes (but don't run intersection observation tasks
+  // yet).
+  UpdateAllLifecyclePhasesForTest();
+
+  // Verify the lock exists.
+  auto* outer_context = outer_element->GetDisplayLockContext();
+  ASSERT_TRUE(outer_context);
+  EXPECT_TRUE(outer_context->IsLocked());
+
+  // Everything should be layout clean.
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Inner context should not be observing the lifecycle.
+  EXPECT_FALSE(IsObservingLifecycle(inner_context));
+
+  // Run intersection observer notifications.
+  test::RunPendingTasks();
+
+  // Run the following checks a few times since we should be observing
+  // lifecycle.
+  for (int i = 0; i < 3; ++i) {
+    // It shouldn't change the fact that we're layout clean.
+    EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+    EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+    EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+    EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+    EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+    EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+    // Because we skipped hiding the element, inner_context should be observing
+    // lifecycle.
+    EXPECT_TRUE(IsObservingLifecycle(inner_context));
+
+    UpdateAllLifecyclePhasesForTest();
+  }
+
+  // Unlock outer.
+  outer_element->setAttribute(html_names::kClassAttr, "");
+  // Ensure the lock processes (but don't run intersection observation tasks
+  // yet).
+  UpdateAllLifecyclePhasesForTest();
+
+  // Note that although we're not nested, we're still observing the lifecycle
+  // because we don't yet know whether we should or should not hide and we only
+  // make this decision _before_ the lifecycle actually unlocked outer.
+  EXPECT_TRUE(IsObservingLifecycle(inner_context));
+
+  // Verify the lock is gone.
+  EXPECT_FALSE(outer_context->IsLocked());
+
+  // Everything should be layout clean.
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Run intersection observer notifications.
+  test::RunPendingTasks();
+
+  // We now should know we're visible and so we're not observing the lifecycle.
+  EXPECT_FALSE(IsObservingLifecycle(inner_context));
+
+  // Also we should still be activated and unlocked.
+  EXPECT_TRUE(inner_context->IsActivated());
+  EXPECT_FALSE(inner_context->IsLocked());
+
+  // Everything should be layout clean.
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+}
+
+TEST_F(DisplayLockContextRenderingTest, NestedLockDoesHideWhenItIsOffscreen) {
+  SetHtmlInnerHTML(R"HTML(
+    <style>
+      .auto { subtree-visibility: auto; }
+      .hidden { subtree-visibility: hidden; }
+      .item { height: 10px; }
+      /* this is important to not invalidate layout when we hide the element! */
+      #outer { contain: style layout; }
+      .spacer { height: 10000px; }
+    </style>
+    <div id=future_spacer></div>
+    <div id=outer>
+      <div id=unrelated>
+        <div id=inner class=auto>Content</div>
+      </div>
+    </div>
+  )HTML");
+
+  auto* inner_element = GetDocument().getElementById("inner");
+  auto* unrelated_element = GetDocument().getElementById("unrelated");
+  auto* outer_element = GetDocument().getElementById("outer");
+
+  UpdateAllLifecyclePhasesForTest();
+  // Intersection observer notifications run as a task.
+  test::RunPendingTasks();
+
+  // The intersection observation will unlock inner, which will cause a dirty
+  // layout bit to be propagated.
+  EXPECT_TRUE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_TRUE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_TRUE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_TRUE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Clear the layout.
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Verify lock state.
+  auto* inner_context = inner_element->GetDisplayLockContext();
+  ASSERT_TRUE(inner_context);
+  EXPECT_TRUE(inner_context->IsActivated());
+  EXPECT_FALSE(inner_context->IsLocked());
+
+  // Lock outer.
+  outer_element->setAttribute(html_names::kClassAttr, "hidden");
+  // Ensure the lock processes (but don't run intersection observation tasks
+  // yet).
+  UpdateAllLifecyclePhasesForTest();
+
+  // Verify the lock exists.
+  auto* outer_context = outer_element->GetDisplayLockContext();
+  ASSERT_TRUE(outer_context);
+  EXPECT_TRUE(outer_context->IsLocked());
+
+  // Everything should be layout clean.
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Inner context should not be observing the lifecycle.
+  EXPECT_FALSE(IsObservingLifecycle(inner_context));
+
+  // Run intersection observer notifications.
+  test::RunPendingTasks();
+
+  // It shouldn't change the fact that we're layout clean.
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Let future spacer become a real spacer!
+  GetDocument()
+      .getElementById("future_spacer")
+      ->setAttribute(html_names::kClassAttr, "spacer");
+
+  UpdateAllLifecyclePhasesForTest();
+
+  // Because we skipped hiding the element, inner_context should be observing
+  // lifecycle.
+  EXPECT_TRUE(IsObservingLifecycle(inner_context));
+
+  // Unlock outer.
+  outer_element->setAttribute(html_names::kClassAttr, "");
+  // Ensure the lock processes (but don't run intersection observation tasks
+  // yet).
+  UpdateAllLifecyclePhasesForTest();
+
+  // Note that although we're not nested, we're still observing the lifecycle
+  // because we don't yet know whether we should or should not hide and we only
+  // make this decision _before_ the lifecycle actually unlocked outer.
+  EXPECT_TRUE(IsObservingLifecycle(inner_context));
+
+  // Verify the lock is gone.
+  EXPECT_FALSE(outer_context->IsLocked());
+
+  // Everything should be layout clean.
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  // Run intersection observer notifications.
+  test::RunPendingTasks();
+
+  // We're still invisible, and we don't know that we're not nested so we're
+  // still observing the lifecycle.
+  EXPECT_TRUE(IsObservingLifecycle(inner_context));
+
+  // We're unlocked for now.
+  EXPECT_TRUE(inner_context->IsActivated());
+  EXPECT_FALSE(inner_context->IsLocked());
+
+  // Everything should be layout clean.
+  EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
+  EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
+
+  UpdateAllLifecyclePhasesForTest();
+
+  // We figured out that we're actually invisible so no need to observe the
+  // lifecycle.
+  EXPECT_FALSE(IsObservingLifecycle(inner_context));
+
+  // We're locked.
+  EXPECT_FALSE(inner_context->IsActivated());
+  EXPECT_TRUE(inner_context->IsLocked());
+}
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 88fb6ee..e7dbc7c 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -8501,21 +8501,9 @@
     auto* context = entry->target()->GetDisplayLockContext();
     DCHECK(context);
     if (entry->isIntersecting()) {
-      if (!context->IsLocked())
-        continue;
-      DCHECK(context->ShouldCommitForActivation(
-          DisplayLockActivationReason::kViewportIntersection));
-      context->CommitForActivationWithSignal(
-          entry->target(), DisplayLockActivationReason::kViewportIntersection);
+      context->NotifyIsIntersectingViewport();
     } else {
-      // If we're not visible, but are observing viewport intersections, it
-      // means that we're either locked (in which case we should remain locked),
-      // or we've been activated (in which case we should relock).
-      DCHECK(context->IsLocked() || context->IsActivated());
-      if (context->IsLocked())
-        continue;
-      context->ClearActivated();
-      context->StartAcquire();
+      context->NotifyIsNotIntersectingViewport();
     }
   }
 }
diff --git a/third_party/blink/renderer/core/dom/document_or_shadow_root.h b/third_party/blink/renderer/core/dom/document_or_shadow_root.h
index 6e0427e3..cfed2ae4 100644
--- a/third_party/blink/renderer/core/dom/document_or_shadow_root.h
+++ b/third_party/blink/renderer/core/dom/document_or_shadow_root.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_OR_SHADOW_ROOT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_OR_SHADOW_ROOT_H_
 
+#include "third_party/blink/renderer/core/animation/document_animation.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
@@ -50,6 +51,15 @@
     return tree_scope.GetSelection();
   }
 
+  static HeapVector<Member<Animation>> getAnimations(Document& document) {
+    return document.GetDocumentAnimations().getAnimations(document);
+  }
+
+  static HeapVector<Member<Animation>> getAnimations(ShadowRoot& shadow_root) {
+    return shadow_root.GetDocument().GetDocumentAnimations().getAnimations(
+        shadow_root);
+  }
+
   static Element* elementFromPoint(TreeScope& tree_scope, double x, double y) {
     return tree_scope.ElementFromPoint(x, y);
   }
diff --git a/third_party/blink/renderer/core/dom/document_or_shadow_root.idl b/third_party/blink/renderer/core/dom/document_or_shadow_root.idl
index f637e1a98..212bde84 100644
--- a/third_party/blink/renderer/core/dom/document_or_shadow_root.idl
+++ b/third_party/blink/renderer/core/dom/document_or_shadow_root.idl
@@ -10,6 +10,9 @@
     // Selection API
     // https://w3c.github.io/selection-api/#extensions-to-document-interface
     [Affects=Nothing] Selection? getSelection();
+    // Web-Animation-API
+    //https://drafts.csswg.org/web-animations/#extensions-to-the-documentorshadowroot-interface-mixin
+    [RuntimeEnabled=WebAnimationsAPI, Measure] sequence<Animation> getAnimations();
     // CSSOM View Module
     // https://drafts.csswg.org/cssom-view/#extensions-to-the-document-interface
     Element? elementFromPoint(double x, double y);
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index c3cb3f9..0a353465 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -3710,7 +3710,7 @@
   DCHECK(!shadow_root_init_dict->hasMode() || !GetShadowRoot());
   bool delegates_focus = shadow_root_init_dict->hasDelegatesFocus() &&
                          shadow_root_init_dict->delegatesFocus();
-  bool manual_slotting = shadow_root_init_dict->slotting() == "manual";
+  bool manual_slotting = shadow_root_init_dict->slotAssignment() == "manual";
   return &AttachShadowRootInternal(type, delegates_focus, manual_slotting);
 }
 
@@ -3740,8 +3740,9 @@
   GetDocument().SetShadowCascadeOrder(ShadowCascadeOrder::kShadowCascadeV1);
   ShadowRoot& shadow_root = CreateAndAttachShadowRoot(type);
   shadow_root.SetDelegatesFocus(delegates_focus);
-  shadow_root.SetSlotting(manual_slotting ? ShadowRootSlotting::kManual
-                                          : ShadowRootSlotting::kAuto);
+  shadow_root.SetSlotAssignmentMode(manual_slotting
+                                        ? SlotAssignmentMode::kManual
+                                        : SlotAssignmentMode::kAuto);
   return shadow_root;
 }
 
diff --git a/third_party/blink/renderer/core/dom/shadow_root.cc b/third_party/blink/renderer/core/dom/shadow_root.cc
index a21966fa..215a8af 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.cc
+++ b/third_party/blink/renderer/core/dom/shadow_root.cc
@@ -70,7 +70,7 @@
       type_(static_cast<unsigned>(type)),
       registered_with_parent_shadow_root_(false),
       delegates_focus_(false),
-      slotting_(static_cast<unsigned>(ShadowRootSlotting::kAuto)),
+      slot_assignment_mode_(static_cast<unsigned>(SlotAssignmentMode::kAuto)),
       needs_distribution_recalc_(false),
       unused_(0) {
   if (IsV0())
@@ -108,8 +108,8 @@
   return nullptr;
 }
 
-void ShadowRoot::SetSlotting(ShadowRootSlotting slotting) {
-  slotting_ = static_cast<unsigned>(slotting);
+void ShadowRoot::SetSlotAssignmentMode(SlotAssignmentMode assignment_mode) {
+  slot_assignment_mode_ = static_cast<unsigned>(assignment_mode);
 }
 
 String ShadowRoot::innerHTML() const {
diff --git a/third_party/blink/renderer/core/dom/shadow_root.h b/third_party/blink/renderer/core/dom/shadow_root.h
index 6cdb76d1..edecd12 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.h
+++ b/third_party/blink/renderer/core/dom/shadow_root.h
@@ -46,7 +46,7 @@
 
 enum class ShadowRootType { V0, kOpen, kClosed, kUserAgent };
 
-enum class ShadowRootSlotting { kManual, kAuto };
+enum class SlotAssignmentMode { kManual, kAuto };
 
 class CORE_EXPORT ShadowRoot final : public DocumentFragment, public TreeScope {
   DEFINE_WRAPPERTYPEINFO();
@@ -146,9 +146,16 @@
   void SetDelegatesFocus(bool flag) { delegates_focus_ = flag; }
   bool delegatesFocus() const { return delegates_focus_; }
 
-  void SetSlotting(ShadowRootSlotting slotting);
-  bool IsManualSlotting() {
-    return slotting_ == static_cast<unsigned>(ShadowRootSlotting::kManual);
+  void SetSlotAssignmentMode(SlotAssignmentMode assignment);
+  bool IsManualSlotting() const {
+    return slot_assignment_mode_ ==
+           static_cast<unsigned>(SlotAssignmentMode::kManual);
+  }
+  SlotAssignmentMode GetSlotAssignmentMode() const {
+    return static_cast<SlotAssignmentMode>(slot_assignment_mode_);
+  }
+  String slotAssignment() const {
+    return IsManualSlotting() ? "manual" : "auto";
   }
 
   bool ContainsShadowRoots() const { return child_shadow_root_count_; }
@@ -181,7 +188,7 @@
   unsigned type_ : 2;
   unsigned registered_with_parent_shadow_root_ : 1;
   unsigned delegates_focus_ : 1;
-  unsigned slotting_ : 1;
+  unsigned slot_assignment_mode_ : 1;
   unsigned needs_distribution_recalc_ : 1;
   unsigned unused_ : 10;
 
diff --git a/third_party/blink/renderer/core/dom/shadow_root.idl b/third_party/blink/renderer/core/dom/shadow_root.idl
index d8d12cd..0080770 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.idl
+++ b/third_party/blink/renderer/core/dom/shadow_root.idl
@@ -34,6 +34,7 @@
     // https://crbug.com/1058762 has been fixed.
     [CEReactions, CustomElementCallbacks, RaisesException=Setter] attribute [TreatNullAs=EmptyString, StringContext=TrustedHTML] DOMString innerHTML;
     readonly attribute boolean delegatesFocus;
+    [RuntimeEnabled=ManualSlotting] readonly attribute SlotAssignmentMode slotAssignment;
 };
 
 ShadowRoot includes DocumentOrShadowRoot;
diff --git a/third_party/blink/renderer/core/dom/shadow_root_init.idl b/third_party/blink/renderer/core/dom/shadow_root_init.idl
index 0857f2a..ed46420 100644
--- a/third_party/blink/renderer/core/dom/shadow_root_init.idl
+++ b/third_party/blink/renderer/core/dom/shadow_root_init.idl
@@ -5,10 +5,10 @@
 // Spec: https://w3c.github.io/webcomponents/spec/shadow/#shadowrootinit-dictionary
 
 enum ShadowRootMode { "open", "closed" };
-enum ShadowRootSlottingMode { "manual", "auto" };
+enum SlotAssignmentMode { "manual", "auto" };
 
 dictionary ShadowRootInit {
     required ShadowRootMode mode;
     boolean delegatesFocus;
-    [RuntimeEnabled=ManualSlotting] ShadowRootSlottingMode slotting;
+    [RuntimeEnabled=ManualSlotting] SlotAssignmentMode slotAssignment;
 };
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index ac78c7f3..bcf5d6b 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1258,7 +1258,8 @@
   GetPage()->GetVisualViewport().ClampToBoundaries();
 }
 
-void WebViewImpl::UpdateICBAndResizeViewport() {
+void WebViewImpl::UpdateICBAndResizeViewport(
+    const IntSize& visible_viewport_size) {
   // We'll keep the initial containing block size from changing when the top
   // controls hide so that the ICB will always be the same size as the
   // viewport with the browser controls shown.
@@ -1278,13 +1279,18 @@
                                            .GetViewportDescription());
   UpdateMainFrameLayoutSize();
 
-  GetPage()->GetVisualViewport().SetSize(size_);
+  GetPage()->GetVisualViewport().SetSize(visible_viewport_size);
 
   if (MainFrameImpl()->GetFrameView()) {
     MainFrameImpl()->GetFrameView()->SetInitialViewportSize(icb_size);
     if (!MainFrameImpl()->GetFrameView()->NeedsLayout())
       resize_viewport_anchor_->ResizeFrameView(MainFrameSize());
   }
+
+  // The boundaries are not properly established until after the frame view is
+  // also resized, as demonstrated by
+  // VisualViewportTest.TestBrowserControlsAdjustmentAndResize.
+  GetPage()->GetVisualViewport().ClampToBoundaries();
 }
 
 void WebViewImpl::UpdateBrowserControlsConstraint(
@@ -1303,7 +1309,7 @@
        constraint == cc::BrowserControlsState::kBoth) ||
       (old_permitted_state == cc::BrowserControlsState::kBoth &&
        constraint == cc::BrowserControlsState::kHidden)) {
-    UpdateICBAndResizeViewport();
+    UpdateICBAndResizeViewport(GetPage()->GetVisualViewport().Size());
   }
 }
 
@@ -1349,7 +1355,9 @@
   return GetPage()->GetBrowserControls();
 }
 
-void WebViewImpl::ResizeViewWhileAnchored(cc::BrowserControlsParams params) {
+void WebViewImpl::ResizeViewWhileAnchored(
+    cc::BrowserControlsParams params,
+    const IntSize& visible_viewport_size) {
   DCHECK(MainFrameImpl());
 
   GetBrowserControls().SetParams(params);
@@ -1360,7 +1368,7 @@
     TextAutosizer::DeferUpdatePageInfo defer_update_page_info(GetPage());
     LocalFrameView* frame_view = MainFrameImpl()->GetFrameView();
     IntSize old_size = frame_view->Size();
-    UpdateICBAndResizeViewport();
+    UpdateICBAndResizeViewport(visible_viewport_size);
     IntSize new_size = frame_view->Size();
     frame_view->MarkViewportConstrainedObjectsForLayout(
         old_size.Width() != new_size.Width(),
@@ -1382,28 +1390,35 @@
     float bottom_controls_height,
     bool browser_controls_shrink_layout) {
   ResizeWithBrowserControls(
-      new_size, {top_controls_height, GetBrowserControls().TopMinHeight(),
-                 bottom_controls_height, GetBrowserControls().BottomMinHeight(),
-                 GetBrowserControls().AnimateHeightChanges(),
-                 browser_controls_shrink_layout});
+      new_size, new_size,
+      {top_controls_height, GetBrowserControls().TopMinHeight(),
+       bottom_controls_height, GetBrowserControls().BottomMinHeight(),
+       GetBrowserControls().AnimateHeightChanges(),
+       browser_controls_shrink_layout});
 }
 
 void WebViewImpl::ResizeWithBrowserControls(
-    const WebSize& new_size,
+    const WebSize& main_frame_widget_size,
+    const WebSize& visible_viewport_size,
     cc::BrowserControlsParams browser_controls_params) {
-  if (should_auto_resize_)
+  if (should_auto_resize_) {
+    // When auto-resizing only the viewport size comes from the browser, while
+    // the widget size is determined in the renderer.
+    ResizeVisualViewport(visible_viewport_size);
     return;
+  }
 
-  if (size_ == new_size &&
+  if (size_ == main_frame_widget_size &&
+      GetPage()->GetVisualViewport().Size() == IntSize(visible_viewport_size) &&
       GetBrowserControls().Params() == browser_controls_params)
     return;
 
   if (GetPage()->MainFrame() && !GetPage()->MainFrame()->IsLocalFrame()) {
     // Viewport resize for a remote main frame does not require any
     // particular action, but the state needs to reflect the correct size
-    // so that it can be used for initalization if the main frame gets
+    // so that it can be used for initialization if the main frame gets
     // swapped to a LocalFrame at a later time.
-    size_ = new_size;
+    size_ = main_frame_widget_size;
     GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(size_);
     GetPage()->GetVisualViewport().SetSize(size_);
     GetPage()->GetBrowserControls().SetParams(browser_controls_params);
@@ -1422,19 +1437,20 @@
 
   bool is_rotation =
       GetPage()->GetSettings().GetMainFrameResizesAreOrientationChanges() &&
-      size_.width && ContentsSize().Width() && new_size.width != size_.width &&
+      size_.width && ContentsSize().Width() &&
+      main_frame_widget_size.width != size_.width &&
       !fullscreen_controller_->IsFullscreenOrTransitioning();
-  size_ = new_size;
+  size_ = main_frame_widget_size;
 
   FloatSize viewport_anchor_coords(viewportAnchorCoordX, viewportAnchorCoordY);
   if (is_rotation) {
     RotationViewportAnchor anchor(*view, visual_viewport,
                                   viewport_anchor_coords,
                                   GetPageScaleConstraintsSet());
-    ResizeViewWhileAnchored(browser_controls_params);
+    ResizeViewWhileAnchored(browser_controls_params, visible_viewport_size);
   } else {
     ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_);
-    ResizeViewWhileAnchored(browser_controls_params);
+    ResizeViewWhileAnchored(browser_controls_params, visible_viewport_size);
   }
   SendResizeEventForMainFrame();
 }
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 1bf1c68..ea6325c 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -136,11 +136,12 @@
   void SetDomainRelaxationForbidden(bool, const WebString& scheme) override;
   void SetWindowFeatures(const WebWindowFeatures&) override;
   void SetOpenedByDOM() override;
-  void ResizeWithBrowserControls(const WebSize&,
+  void ResizeWithBrowserControls(const WebSize& main_frame_widget_size,
                                  float top_controls_height,
                                  float bottom_controls_height,
                                  bool browser_controls_shrink_layout) override;
-  void ResizeWithBrowserControls(const WebSize&,
+  void ResizeWithBrowserControls(const WebSize& main_frame_widget_size,
+                                 const WebSize& visible_viewport_size,
                                  cc::BrowserControlsParams) override;
   WebFrame* MainFrame() override;
   WebLocalFrame* FocusedFrame() override;
@@ -491,8 +492,9 @@
   IntSize ContentsSize() const;
 
   void UpdateBrowserControlsConstraint(cc::BrowserControlsState constraint);
-  void UpdateICBAndResizeViewport();
-  void ResizeViewWhileAnchored(cc::BrowserControlsParams params);
+  void UpdateICBAndResizeViewport(const IntSize& visible_viewport_size);
+  void ResizeViewWhileAnchored(cc::BrowserControlsParams params,
+                               const IntSize& visible_viewport_size);
 
   void UpdateBaseBackgroundColor();
 
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
index aac8b09c..4696ca2 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
@@ -1404,6 +1404,28 @@
   EXPECT_FALSE(csp->AllowTrustedTypePolicy("somepolicy", true));
 }
 
+TEST_F(ContentSecurityPolicyTest, TrustedTypesStarMix) {
+  csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
+  csp->DidReceiveHeader("trusted-types abc * def",
+                        ContentSecurityPolicyType::kEnforce,
+                        ContentSecurityPolicySource::kHTTP);
+  EXPECT_TRUE(csp->AllowTrustedTypePolicy("abc", false));
+  EXPECT_TRUE(csp->AllowTrustedTypePolicy("def", false));
+  EXPECT_TRUE(csp->AllowTrustedTypePolicy("ghi", false));
+  EXPECT_FALSE(csp->AllowTrustedTypePolicy("abc", true));
+  EXPECT_FALSE(csp->AllowTrustedTypePolicy("def", true));
+  EXPECT_FALSE(csp->AllowTrustedTypePolicy("ghi", true));
+}
+
+TEST_F(ContentSecurityPolicyTest, TrustedTypeDupe) {
+  csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
+  csp->DidReceiveHeader("trusted-types somepolicy 'allow-duplicates'",
+                        ContentSecurityPolicyType::kEnforce,
+                        ContentSecurityPolicySource::kHTTP);
+  EXPECT_TRUE(csp->AllowTrustedTypePolicy("somepolicy", false));
+  EXPECT_TRUE(csp->AllowTrustedTypePolicy("somepolicy", true));
+}
+
 TEST_F(ContentSecurityPolicyTest, TrustedTypeDupeStar) {
   csp->BindToDelegate(execution_context->GetContentSecurityPolicyDelegate());
   csp->DidReceiveHeader("trusted-types * 'allow-duplicates'",
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index 5b75657..3cd977b 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -238,7 +238,9 @@
 // WebView resizes the VisualViewport.
 TEST_P(VisualViewportTest, TestResize) {
   InitializeWithDesktopSettings();
-  WebView()->MainFrameWidget()->Resize(IntSize(320, 240));
+  WebView()->ResizeWithBrowserControls(
+      IntSize(320, 240), IntSize(320, 240),
+      WebView()->GetBrowserControls().Params());
 
   NavigateTo("about:blank");
   ForceFullCompositingUpdate();
@@ -252,7 +254,8 @@
 
   // Resizing the WebView should change the VisualViewport.
   web_view_size = IntSize(640, 480);
-  WebView()->MainFrameWidget()->Resize(web_view_size);
+  WebView()->ResizeWithBrowserControls(
+      web_view_size, web_view_size, WebView()->GetBrowserControls().Params());
   EXPECT_EQ(web_view_size, IntSize(WebView()->MainFrameWidget()->Size()));
   EXPECT_EQ(web_view_size, visual_viewport.Size());
 
@@ -276,7 +279,8 @@
   // Vertical scrollbar width and horizontal scrollbar height.
   IntSize scrollbar_size = IntSize(15, 15);
 
-  WebView()->MainFrameWidget()->Resize(size);
+  WebView()->ResizeWithBrowserControls(
+      size, size, WebView()->GetBrowserControls().Params());
 
   // Scroll layout viewport and verify visibleContentRect.
   WebView()->MainFrameImpl()->SetScrollOffset(WebSize(0, 50));
@@ -306,7 +310,9 @@
 // make it appear to stay still). This caused bugs like crbug.com/453859.
 TEST_P(VisualViewportTest, TestResizeAtFullyScrolledPreservesViewportLocation) {
   InitializeWithDesktopSettings();
-  WebView()->MainFrameWidget()->Resize(IntSize(800, 600));
+  WebView()->ResizeWithBrowserControls(
+      IntSize(800, 600), IntSize(800, 600),
+      WebView()->GetBrowserControls().Params());
 
   RegisterMockedHttpURLLoad("content-width-1000.html");
   NavigateTo(base_url_ + "content-width-1000.html");
@@ -332,12 +338,16 @@
   // Shrink the WebView, this should cause both viewports to shrink and
   // WebView should do whatever it needs to do to preserve the visible
   // location.
-  WebView()->MainFrameWidget()->Resize(IntSize(700, 550));
+  WebView()->ResizeWithBrowserControls(
+      IntSize(700, 550), IntSize(800, 600),
+      WebView()->GetBrowserControls().Params());
 
   EXPECT_EQ(expected_location,
             frame_view.GetScrollableArea()->VisibleContentRect().Location());
 
-  WebView()->MainFrameWidget()->Resize(IntSize(800, 600));
+  WebView()->ResizeWithBrowserControls(
+      IntSize(800, 600), IntSize(800, 600),
+      WebView()->GetBrowserControls().Params());
 
   EXPECT_EQ(expected_location,
             frame_view.GetScrollableArea()->VisibleContentRect().Location());
@@ -1450,9 +1460,15 @@
   InitializeWithAndroidSettings();
 
   // Initialize with browser controls showing and shrinking the Blink size.
+  cc::BrowserControlsParams controls;
+  controls.top_controls_height = browser_controls_height;
+  controls.browser_controls_shrink_blink_size = true;
+  // TODO(danakj): The browser (RenderWidgetHostImpl) doesn't shrink the widget
+  // size by the browser controls, only the visible_viewport_size, but this test
+  // shrinks and grows both.
   WebView()->ResizeWithBrowserControls(
-      WebSize(500, visual_viewport_height - browser_controls_height), 20, 0,
-      true);
+      WebSize(500, visual_viewport_height - browser_controls_height),
+      WebSize(500, visual_viewport_height - browser_controls_height), controls);
   WebView()->GetBrowserControls().SetShownRatio(1, 0);
 
   RegisterMockedHttpURLLoad("content-width-1000.html");
@@ -1493,19 +1509,26 @@
 
   ScrollOffset total_expected = visual_viewport_expected + frame_view_expected;
 
-  // Resize the widget to match the browser controls adjustment. Ensure that the
-  // total offset (i.e. what the user sees) doesn't change because of clamping
-  // the offsets to valid values.
-  WebView()->ResizeWithBrowserControls(WebSize(500, visual_viewport_height), 20,
-                                       0, false);
+  // Resize the widget and visible viewport to match the browser controls
+  // adjustment. Ensure that the total offset (i.e. what the user sees) doesn't
+  // change because of clamping the offsets to valid values.
+  controls.browser_controls_shrink_blink_size = false;
+  WebView()->ResizeWithBrowserControls(WebSize(500, visual_viewport_height),
+                                       WebSize(500, visual_viewport_height),
+                                       controls);
 
   EXPECT_EQ(IntSize(500, visual_viewport_height), visual_viewport.Size());
   EXPECT_EQ(FloatSize(250, visual_viewport_height / page_scale),
             visual_viewport.VisibleRect().Size());
   EXPECT_EQ(IntSize(1000, layout_viewport_height),
             frame_view.FrameRect().Size());
+
   EXPECT_EQ(total_expected, visual_viewport.GetScrollOffset() +
                                 frame_view.LayoutViewport()->GetScrollOffset());
+
+  EXPECT_EQ(visual_viewport_expected, visual_viewport.GetScrollOffset());
+  EXPECT_EQ(frame_view_expected,
+            frame_view.LayoutViewport()->GetScrollOffset());
 }
 
 // Tests that a scroll all the way to the bottom while showing the browser
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 6ff2cad1..d8c09436 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -446,10 +446,10 @@
   layout_view->HitTest(location, result);
 
   if (LocalFrame* frame = result.InnerNodeFrame()) {
-    EventHandler::OptionalCursor optional_cursor =
+    base::Optional<Cursor> optional_cursor =
         frame->GetEventHandler().SelectCursor(location, result);
-    if (optional_cursor.IsCursorChange()) {
-      view->SetCursor(optional_cursor.GetCursor());
+    if (optional_cursor.has_value()) {
+      view->SetCursor(optional_cursor.value());
     }
   }
 }
@@ -493,17 +493,17 @@
   return HasEditableStyle(*node);
 }
 
-EventHandler::OptionalCursor EventHandler::SelectCursor(
+base::Optional<Cursor> EventHandler::SelectCursor(
     const HitTestLocation& location,
     const HitTestResult& result) {
   if (scroll_manager_->InResizeMode())
-    return kNoCursorChange;
+    return base::nullopt;
 
   Page* page = frame_->GetPage();
   if (!page)
-    return kNoCursorChange;
+    return base::nullopt;
   if (scroll_manager_->MiddleClickAutoscrollInProgress())
-    return kNoCursorChange;
+    return base::nullopt;
 
   if (result.GetScrollbar() && !result.GetScrollbar()->IsCustomScrollbar())
     return PointerCursor();
@@ -543,7 +543,7 @@
       case kSetCursor:
         return override_cursor;
       case kDoNotSetCursor:
-        return kNoCursorChange;
+        return base::nullopt;
     }
   }
 
@@ -677,7 +677,7 @@
   return PointerCursor();
 }
 
-EventHandler::OptionalCursor EventHandler::SelectAutoCursor(
+base::Optional<Cursor> EventHandler::SelectAutoCursor(
     const HitTestResult& result,
     Node* node,
     const Cursor& i_beam) {
@@ -1061,10 +1061,10 @@
     }
     LocalFrameView* view = frame_->View();
     if ((!is_remote_frame || is_portal) && view) {
-      EventHandler::OptionalCursor optional_cursor =
+      base::Optional<Cursor> optional_cursor =
           SelectCursor(mev.GetHitTestLocation(), mev.GetHitTestResult());
-      if (optional_cursor.IsCursorChange()) {
-        view->SetCursor(optional_cursor.GetCursor());
+      if (optional_cursor.has_value()) {
+        view->SetCursor(optional_cursor.value());
       }
     }
   }
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h
index 49caef6..4b8b63eb 100644
--- a/third_party/blink/renderer/core/input/event_handler.h
+++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -271,27 +271,6 @@
   bool LongTapShouldInvokeContextMenu();
 
  private:
-  enum NoCursorChangeType { kNoCursorChange };
-
-  class OptionalCursor {
-    STACK_ALLOCATED();
-
-   public:
-    OptionalCursor(NoCursorChangeType) : is_cursor_change_(false) {}
-    OptionalCursor(const Cursor& cursor)
-        : is_cursor_change_(true), cursor_(cursor) {}
-
-    bool IsCursorChange() const { return is_cursor_change_; }
-    const Cursor& GetCursor() const {
-      DCHECK(is_cursor_change_);
-      return cursor_;
-    }
-
-   private:
-    bool is_cursor_change_;
-    Cursor cursor_;
-  };
-
   WebInputEventResult HandleMouseMoveOrLeaveEvent(
       const WebMouseEvent&,
       const Vector<WebMouseEvent>& coalesced_events,
@@ -316,11 +295,11 @@
   bool IsSelectingLink(const HitTestResult&);
   bool ShouldShowIBeamForNode(const Node*, const HitTestResult&);
   bool ShouldShowResizeForNode(const Node*, const HitTestLocation&);
-  OptionalCursor SelectCursor(const HitTestLocation& location,
-                              const HitTestResult&);
-  OptionalCursor SelectAutoCursor(const HitTestResult&,
-                                  Node*,
-                                  const Cursor& i_beam);
+  base::Optional<Cursor> SelectCursor(const HitTestLocation& location,
+                                      const HitTestResult&);
+  base::Optional<Cursor> SelectAutoCursor(const HitTestResult&,
+                                          Node*,
+                                          const Cursor& i_beam);
 
   void HoverTimerFired(TimerBase*);
   void CursorUpdateTimerFired(TimerBase*);
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index 02bd77a..60eb256 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/optional.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
@@ -626,7 +627,7 @@
           .GetFrame()
           ->GetEventHandler()
           .SelectCursor(location, result)
-          .GetCursor()
+          .value()
           .GetType(),
       ui::mojom::CursorType::kHand);  // A hand signals ability to navigate.
 }
@@ -649,7 +650,7 @@
                 .GetFrame()
                 ->GetEventHandler()
                 .SelectCursor(location, result)
-                .GetCursor()
+                .value()
                 .GetType(),
             ui::mojom::CursorType::kIBeam);  // An I-beam signals editability.
 }
@@ -668,7 +669,7 @@
                 .GetFrame()
                 ->GetEventHandler()
                 .SelectCursor(location, result)
-                .GetCursor()
+                .value()
                 .GetType(),
             // A north-south resize signals vertical resizability.
             ui::mojom::CursorType::kNorthSouthResize);
@@ -688,7 +689,7 @@
                 .GetFrame()
                 ->GetEventHandler()
                 .SelectCursor(location, result)
-                .GetCursor()
+                .value()
                 .GetType(),
             // An east-west resize signals horizontal resizability.
             ui::mojom::CursorType::kEastWestResize);
@@ -708,7 +709,7 @@
                 .GetFrame()
                 ->GetEventHandler()
                 .SelectCursor(location, result)
-                .GetCursor()
+                .value()
                 .GetType(),
             // An south-east resize signals both horizontal and
             // vertical resizability.
@@ -730,7 +731,7 @@
                 .GetFrame()
                 ->GetEventHandler()
                 .SelectCursor(location, result)
-                .GetCursor()
+                .value()
                 .GetType(),
             // An south-west resize signals both horizontal and
             // vertical resizability when direction is RTL.
@@ -754,7 +755,7 @@
                 .GetFrame()
                 ->GetEventHandler()
                 .SelectCursor(location, result)
-                .GetCursor()
+                .value()
                 .GetType(),
             ui::mojom::CursorType::kSouthEastResize);
 }
@@ -776,7 +777,7 @@
                 .GetFrame()
                 ->GetEventHandler()
                 .SelectCursor(location, result)
-                .GetCursor()
+                .value()
                 .GetType(),
             ui::mojom::CursorType::kSouthEastResize);
 }
diff --git a/third_party/blink/renderer/core/page/page_animator.cc b/third_party/blink/renderer/core/page/page_animator.cc
index 3a83801..ac2ea15 100644
--- a/third_party/blink/renderer/core/page/page_animator.cc
+++ b/third_party/blink/renderer/core/page/page_animator.cc
@@ -182,13 +182,13 @@
   }
 }
 
-HeapVector<Member<Animation>> PageAnimator::GetAnimations(Document* document_) {
+HeapVector<Member<Animation>> PageAnimator::GetAnimations(
+    const TreeScope& tree_scope) {
   HeapVector<Member<Animation>> animations;
   DocumentsVector documents = GetAllDocuments(page_->MainFrame());
-
   for (auto& document : documents) {
-    document->GetDocumentAnimations().GetAnimationsTargetingDocument(
-        document_, animations);
+    document->GetDocumentAnimations().GetAnimationsTargetingTreeScope(
+        animations, tree_scope);
   }
   return animations;
 }
diff --git a/third_party/blink/renderer/core/page/page_animator.h b/third_party/blink/renderer/core/page/page_animator.h
index 0ab0e98d..e32c0ce0 100644
--- a/third_party/blink/renderer/core/page/page_animator.h
+++ b/third_party/blink/renderer/core/page/page_animator.h
@@ -17,6 +17,7 @@
 
 class LocalFrame;
 class Page;
+class TreeScope;
 
 class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> {
  public:
@@ -44,7 +45,7 @@
   void UpdateLifecycleToLayoutClean(LocalFrame& root_frame,
                                     DocumentUpdateReason reason);
   AnimationClock& Clock() { return animation_clock_; }
-  HeapVector<Member<Animation>> GetAnimations(Document*);
+  HeapVector<Member<Animation>> GetAnimations(const TreeScope&);
 
  private:
   void UpdateHitTestOcclusionData(LocalFrame& root_frame);
diff --git a/third_party/blink/renderer/core/page/validation_message_overlay_delegate_test.cc b/third_party/blink/renderer/core/page/validation_message_overlay_delegate_test.cc
index 0c261df..6bf94736 100644
--- a/third_party/blink/renderer/core/page/validation_message_overlay_delegate_test.cc
+++ b/third_party/blink/renderer/core/page/validation_message_overlay_delegate_test.cc
@@ -78,7 +78,8 @@
       To<LocalFrame>(delegate_ptr->GetPageForTesting()->MainFrame())
           ->GetDocument();
   HeapVector<Member<Animation>> animations =
-      internal_document->GetDocumentAnimations().getAnimations();
+      internal_document->GetDocumentAnimations().getAnimations(
+          *internal_document);
   ASSERT_FALSE(animations.IsEmpty());
 
   for (const auto& animation : animations) {
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 4c5c2729..f3bd3a4 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -2788,11 +2788,13 @@
   StringBuilder result;
   result.Append("type=");
   result.Append(CursorTypeToString(cursor.GetType()));
-  result.Append(" hotSpot=");
-  result.AppendNumber(cursor.HotSpot().X());
-  result.Append(',');
-  result.AppendNumber(cursor.HotSpot().Y());
-  if (cursor.GetImage()) {
+  if (cursor.GetType() == ui::mojom::CursorType::kCustom) {
+    result.Append(" hotSpot=");
+    result.AppendNumber(cursor.HotSpot().X());
+    result.Append(',');
+    result.AppendNumber(cursor.HotSpot().Y());
+
+    DCHECK(cursor.GetImage());
     IntSize size = cursor.GetImage()->Size();
     result.Append(" image=");
     result.AppendNumber(size.Width());
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
index e29bb484..8c069d2 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
@@ -23,9 +23,6 @@
 void TrustedTypesCheckForHTMLThrows(const String& string) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   Document& document = dummy_page_holder->GetDocument();
-  document.GetContentSecurityPolicy()->DidReceiveHeader(
-      "trusted-types *", network::mojom::ContentSecurityPolicyType::kEnforce,
-      network::mojom::ContentSecurityPolicySource::kMeta);
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
@@ -46,9 +43,6 @@
 void TrustedTypesCheckForScriptThrows(const String& string) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   Document& document = dummy_page_holder->GetDocument();
-  document.GetContentSecurityPolicy()->DidReceiveHeader(
-      "trusted-types *", network::mojom::ContentSecurityPolicyType::kEnforce,
-      network::mojom::ContentSecurityPolicySource::kMeta);
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
@@ -71,9 +65,6 @@
 void TrustedTypesCheckForScriptURLThrows(const String& string) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   Document& document = dummy_page_holder->GetDocument();
-  document.GetContentSecurityPolicy()->DidReceiveHeader(
-      "trusted-types *", network::mojom::ContentSecurityPolicyType::kEnforce,
-      network::mojom::ContentSecurityPolicySource::kMeta);
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
@@ -98,9 +89,6 @@
     String expected) {
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
   Document& document = dummy_page_holder->GetDocument();
-  document.GetContentSecurityPolicy()->DidReceiveHeader(
-      "trusted-types *", network::mojom::ContentSecurityPolicyType::kEnforce,
-      network::mojom::ContentSecurityPolicySource::kMeta);
   V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   String s = TrustedTypesCheckForScript(
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
index 9ccc5663..f0e12d8 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
@@ -25,7 +25,8 @@
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 #include "third_party/webrtc/api/video/i420_buffer.h"
 #include "third_party/webrtc/api/video/recordable_encoded_frame.h"
-#include "third_party/webrtc/rtc_base/time_utils.h"  // for TimeMicros
+#include "third_party/webrtc/rtc_base/time_utils.h"
+#include "third_party/webrtc/system_wrappers/include/clock.h"
 
 namespace WTF {
 
@@ -145,6 +146,12 @@
 
   // WebRTC Chromium timestamp diff
   const base::TimeDelta time_diff_encoded_;
+
+  // WebRTC real time clock, needed to determine NTP offset.
+  webrtc::Clock* clock_;
+
+  // Offset between NTP clock and WebRTC clock.
+  const int64_t ntp_offset_;
 };
 
 MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::
@@ -163,11 +170,13 @@
                  base::TimeDelta::FromMicroseconds(rtc::TimeMicros())),
       start_timestamp_encoded_(media::kNoTimestamp),
       time_diff_encoded_(base::TimeTicks::Now() - base::TimeTicks() -
-                         base::TimeDelta::FromMicroseconds(rtc::TimeMicros())) {
-}
+                         base::TimeDelta::FromMicroseconds(rtc::TimeMicros())),
+      clock_(webrtc::Clock::GetRealTimeClock()),
+      ntp_offset_(clock_->TimeInMilliseconds() -
+                  clock_->CurrentNtpInMilliseconds()) {}
 
 MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::
-    ~RemoteVideoSourceDelegate() {}
+    ~RemoteVideoSourceDelegate() = default;
 
 void MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::OnFrame(
     const webrtc::VideoFrame& incoming_frame) {
@@ -301,10 +310,11 @@
 
   // Set capture time to the NTP time, which is the estimated capture time
   // converted to the local clock.
-  if (incoming_frame.ntp_time_ms() != 0) {
+  if (incoming_frame.ntp_time_ms() > 0) {
     const base::TimeTicks capture_time =
         base::TimeTicks() +
-        base::TimeDelta::FromMilliseconds(incoming_frame.ntp_time_ms()) +
+        base::TimeDelta::FromMilliseconds(incoming_frame.ntp_time_ms() +
+                                          ntp_offset_) +
         time_diff_;
     video_frame->metadata()->SetTimeTicks(
         media::VideoFrameMetadata::CAPTURE_BEGIN_TIME, capture_time);
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
index 89b73eb..61eb7c4c 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
@@ -28,6 +28,7 @@
 #include "third_party/webrtc/api/rtp_packet_infos.h"
 #include "third_party/webrtc/api/video/color_space.h"
 #include "third_party/webrtc/api/video/i420_buffer.h"
+#include "third_party/webrtc/system_wrappers/include/clock.h"
 #include "ui/gfx/color_space.h"
 
 namespace blink {
@@ -327,6 +328,11 @@
       kProcessingFinish - webrtc::TimeDelta::Millis(1.0e3 * kProcessingTime);
   const webrtc::Timestamp kCaptureTime =
       kProcessingStart - webrtc::TimeDelta::Millis(20.0);
+  webrtc::Clock* clock = webrtc::Clock::GetRealTimeClock();
+  const int64_t ntp_offset =
+      clock->CurrentNtpInMilliseconds() - clock->TimeInMilliseconds();
+  const webrtc::Timestamp kCaptureTimeNtp =
+      kCaptureTime + webrtc::TimeDelta::Millis(ntp_offset);
   // Expected capture time in Chromium epoch.
   base::TimeTicks kExpectedCaptureTime =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(kCaptureTime.ms()) +
@@ -348,7 +354,7 @@
       webrtc::VideoFrame::Builder()
           .set_video_frame_buffer(buffer)
           .set_timestamp_rtp(kRtpTimestamp)
-          .set_ntp_time_ms(kCaptureTime.ms())
+          .set_ntp_time_ms(kCaptureTimeNtp.ms())
           .set_packet_infos(webrtc::RtpPacketInfos(packet_infos))
           .build();
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 6ef6f717..a98b0c8 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -2585,7 +2585,8 @@
   if (sender_it == rtp_senders_.end()) {
     // Create new sender (with empty stream set).
     sender = MakeGarbageCollected<RTCRtpSender>(
-        this, std::move(web_sender), kind, track, MediaStreamVector());
+        this, std::move(web_sender), kind, track, MediaStreamVector(),
+        force_encoded_video_insertable_streams());
     rtp_senders_.push_back(sender);
   } else {
     // Update existing sender (not touching the stream set).
@@ -2618,7 +2619,8 @@
   if (receiver_it == rtp_receivers_.end()) {
     // Create new receiver.
     receiver = MakeGarbageCollected<RTCRtpReceiver>(
-        this, std::move(platform_receiver), track, MediaStreamVector());
+        this, std::move(platform_receiver), track, MediaStreamVector(),
+        force_encoded_video_insertable_streams());
     // Receiving tracks should be muted by default. SetReadyState() propagates
     // the related state changes to ensure it is muted on all layers. It also
     // fires events - which is not desired - but because they fire synchronously
@@ -2917,7 +2919,8 @@
   }
   DCHECK(FindReceiver(*platform_receiver) == rtp_receivers_.end());
   RTCRtpReceiver* rtp_receiver = MakeGarbageCollected<RTCRtpReceiver>(
-      this, std::move(platform_receiver), track, streams);
+      this, std::move(platform_receiver), track, streams,
+      force_encoded_video_insertable_streams());
   rtp_receivers_.push_back(rtp_receiver);
   ScheduleDispatchEvent(MakeGarbageCollected<RTCTrackEvent>(
       rtp_receiver, rtp_receiver->track(), streams, nullptr));
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
index 574c99f1..cf0f12a3 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
@@ -1871,7 +1871,8 @@
     blink::RtpSenderState sender_state = transceiver_state.MoveSenderState();
     DCHECK(sender_state.is_initialized());
     rtp_senders_.push_back(std::make_unique<blink::RTCRtpSenderImpl>(
-        native_peer_connection_, track_adapter_map_, std::move(sender_state)));
+        native_peer_connection_, track_adapter_map_, std::move(sender_state),
+        force_encoded_video_insertable_streams_));
     platform_transceiver = std::make_unique<blink::RTCRtpSenderOnlyTransceiver>(
         std::make_unique<blink::RTCRtpSenderImpl>(*rtp_senders_.back().get()));
   } else {
@@ -2307,7 +2308,8 @@
       blink::RTCRtpReceiverImpl::getId(receiver_state.webrtc_receiver().get());
   DCHECK(FindReceiver(receiver_id) == rtp_receivers_.end());
   auto rtp_receiver = std::make_unique<blink::RTCRtpReceiverImpl>(
-      native_peer_connection_, std::move(receiver_state));
+      native_peer_connection_, std::move(receiver_state),
+      force_encoded_video_insertable_streams_);
   rtp_receivers_.push_back(
       std::make_unique<blink::RTCRtpReceiverImpl>(*rtp_receiver));
   if (peer_connection_tracker_) {
@@ -2617,7 +2619,7 @@
     // Create a new transceiver, including a sender and a receiver.
     transceiver = std::make_unique<blink::RTCRtpTransceiverImpl>(
         native_peer_connection_, track_adapter_map_,
-        std::move(transceiver_state));
+        std::move(transceiver_state), force_encoded_video_insertable_streams_);
     rtp_transceivers_.push_back(transceiver->ShallowCopy());
     DCHECK(FindSender(blink::RTCRtpSenderImpl::getId(webrtc_sender.get())) ==
            rtp_senders_.end());
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
index 3447f49..de828dab 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
@@ -13,13 +13,20 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_capability.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_parameters.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_source.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_insertable_streams.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h"
 #include "third_party/blink/renderer/modules/peerconnection/web_rtc_stats_report_callback_resolver.h"
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -30,13 +37,19 @@
 RTCRtpReceiver::RTCRtpReceiver(RTCPeerConnection* pc,
                                std::unique_ptr<RTCRtpReceiverPlatform> receiver,
                                MediaStreamTrack* track,
-                               MediaStreamVector streams)
+                               MediaStreamVector streams,
+                               bool force_encoded_video_insertable_streams)
     : pc_(pc),
       receiver_(std::move(receiver)),
       track_(track),
-      streams_(std::move(streams)) {
+      streams_(std::move(streams)),
+      force_encoded_video_insertable_streams_(
+          force_encoded_video_insertable_streams) {
+  DCHECK(pc_);
   DCHECK(receiver_);
   DCHECK(track_);
+  if (force_encoded_video_insertable_streams_)
+    RegisterEncodedVideoStreamCallback();
 }
 
 MediaStreamTrack* RTCRtpReceiver::track() const {
@@ -145,9 +158,20 @@
 RTCInsertableStreams* RTCRtpReceiver::createEncodedVideoStreams(
     ScriptState* script_state,
     ExceptionState& exception_state) {
-  exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                    "Not supported");
-  return nullptr;
+  if (!force_encoded_video_insertable_streams_) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "Encoded video streams not requested at PC initialization");
+    return nullptr;
+  }
+  if (encoded_video_streams_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Encoded video streams already created");
+    return nullptr;
+  }
+
+  InitializeEncodedVideoStreams(script_state);
+  return encoded_video_streams_;
 }
 
 RTCRtpReceiverPlatform* RTCRtpReceiver::platform_receiver() {
@@ -195,6 +219,9 @@
   visitor->Trace(transport_);
   visitor->Trace(streams_);
   visitor->Trace(transceiver_);
+  visitor->Trace(video_from_depacketizer_underlying_source_);
+  visitor->Trace(video_to_decoder_underlying_sink_);
+  visitor->Trace(encoded_video_streams_);
   ScriptWrappable::Trace(visitor);
 }
 
@@ -287,4 +314,74 @@
   return parameters;
 }
 
+void RTCRtpReceiver::RegisterEncodedVideoStreamCallback() {
+  DCHECK(!platform_receiver()
+              ->GetEncodedVideoStreamTransformer()
+              ->HasTransformerCallback());
+  platform_receiver()
+      ->GetEncodedVideoStreamTransformer()
+      ->SetTransformerCallback(WTF::BindRepeating(
+          &RTCRtpReceiver::OnFrameFromDepacketizer, WrapWeakPersistent(this)));
+}
+
+void RTCRtpReceiver::UnregisterEncodedVideoStreamCallback() {
+  DCHECK(!platform_receiver()
+              ->GetEncodedVideoStreamTransformer()
+              ->HasTransformerCallback());
+  platform_receiver()
+      ->GetEncodedVideoStreamTransformer()
+      ->ResetTransformerCallback();
+}
+
+void RTCRtpReceiver::InitializeEncodedVideoStreams(ScriptState* script_state) {
+  DCHECK(!encoded_video_streams_);
+  DCHECK(!video_from_depacketizer_underlying_source_);
+  DCHECK(!video_to_decoder_underlying_sink_);
+  DCHECK(force_encoded_video_insertable_streams_);
+
+  encoded_video_streams_ = RTCInsertableStreams::Create();
+
+  // Set up readable.
+  video_from_depacketizer_underlying_source_ =
+      MakeGarbageCollected<RTCEncodedVideoUnderlyingSource>(
+          script_state,
+          WTF::Bind(&RTCRtpReceiver::UnregisterEncodedVideoStreamCallback,
+                    WrapWeakPersistent(this)));
+  // The high water mark for the readable stream is set to 0 so that frames are
+  // removed from the queue right away, without introducing a new buffer.
+  encoded_video_streams_->setReadableStream(
+      ReadableStream::CreateWithCountQueueingStrategy(
+          script_state, video_from_depacketizer_underlying_source_,
+          /*high_water_mark=*/0));
+
+  // Set up writable.
+  video_to_decoder_underlying_sink_ =
+      MakeGarbageCollected<RTCEncodedVideoUnderlyingSink>(
+          script_state,
+          WTF::BindRepeating(
+              [](RTCRtpReceiver* receiver)
+                  -> RTCEncodedVideoStreamTransformer* {
+                return receiver ? receiver->platform_receiver()
+                                      ->GetEncodedVideoStreamTransformer()
+                                : nullptr;
+              },
+              WrapWeakPersistent(this)));
+  // The high water mark for the stream is set to 1 so that the stream seems
+  // ready to write, but without queuing frames.
+  encoded_video_streams_->setWritableStream(
+      WritableStream::CreateWithCountQueueingStrategy(
+          script_state, video_to_decoder_underlying_sink_,
+          /*high_water_mark=*/1));
+}
+
+void RTCRtpReceiver::OnFrameFromDepacketizer(
+    std::unique_ptr<webrtc::video_coding::EncodedFrame> encoded_video_frame,
+    std::vector<uint8_t> additional_data,
+    uint32_t ssrc) {
+  if (video_from_depacketizer_underlying_source_) {
+    video_from_depacketizer_underlying_source_->OnFrameFromSource(
+        std::move(encoded_video_frame), std::move(additional_data));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
index 8380bc9..82e1c06 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
@@ -21,8 +21,16 @@
 #include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.h"
 
+namespace webrtc {
+namespace video_coding {
+class EncodedFrame;
+}  // namespace video_coding
+}  // namespace webrtc
+
 namespace blink {
 class RTCDtlsTransport;
+class RTCEncodedVideoUnderlyingSource;
+class RTCEncodedVideoUnderlyingSink;
 class RTCInsertableStreams;
 class RTCPeerConnection;
 class RTCRtpCapabilities;
@@ -37,7 +45,8 @@
   RTCRtpReceiver(RTCPeerConnection*,
                  std::unique_ptr<RTCRtpReceiverPlatform>,
                  MediaStreamTrack*,
-                 MediaStreamVector);
+                 MediaStreamVector,
+                 bool force_encoded_video_insertable_streams);
 
   static RTCRtpCapabilities* getCapabilities(const String& kind);
 
@@ -65,9 +74,16 @@
   void Trace(Visitor*) override;
 
  private:
-  Member<RTCPeerConnection> pc_;
   void SetContributingSourcesNeedsUpdating();
+  void RegisterEncodedVideoStreamCallback();
+  void UnregisterEncodedVideoStreamCallback();
+  void InitializeEncodedVideoStreams(ScriptState*);
+  void OnFrameFromDepacketizer(
+      std::unique_ptr<webrtc::video_coding::EncodedFrame> frame,
+      std::vector<uint8_t> additional_data,
+      uint32_t ssrc);
 
+  Member<RTCPeerConnection> pc_;
   std::unique_ptr<RTCRtpReceiverPlatform> receiver_;
   Member<MediaStreamTrack> track_;
   Member<RTCDtlsTransport> transport_;
@@ -83,6 +99,13 @@
   // observed delay may differ depending on the congestion control. |nullopt|
   // means default value must be used.
   base::Optional<double> playout_delay_hint_;
+
+  // Insertable Streams support
+  bool force_encoded_video_insertable_streams_;
+  Member<RTCEncodedVideoUnderlyingSource>
+      video_from_depacketizer_underlying_source_;
+  Member<RTCEncodedVideoUnderlyingSink> video_to_decoder_underlying_sink_;
+  Member<RTCInsertableStreams> encoded_video_streams_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
index f22e061..e2584ef 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
@@ -135,7 +136,8 @@
  public:
   RTCRtpReceiverInternal(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
-      RtpReceiverState state)
+      RtpReceiverState state,
+      bool force_encoded_video_insertable_streams)
       : native_peer_connection_(std::move(native_peer_connection)),
         main_task_runner_(state.main_task_runner()),
         signaling_task_runner_(state.signaling_task_runner()),
@@ -143,6 +145,12 @@
         state_(std::move(state)) {
     DCHECK(native_peer_connection_);
     DCHECK(state_.is_initialized());
+    if (force_encoded_video_insertable_streams) {
+      transformer_ =
+          std::make_unique<RTCEncodedVideoStreamTransformer>(main_task_runner_);
+      webrtc_receiver_->SetDepacketizerToDecoderFrameTransformer(
+          transformer_->Delegate());
+    }
   }
 
   const RtpReceiverState& state() const {
@@ -189,6 +197,10 @@
         blink::ToAbslOptional(delay_seconds));
   }
 
+  RTCEncodedVideoStreamTransformer* GetEncodedVideoStreamTransformer() const {
+    return transformer_.get();
+  }
+
  private:
   friend class WTF::ThreadSafeRefCounted<RTCRtpReceiverInternal,
                                          RTCRtpReceiverInternalTraits>;
@@ -214,6 +226,7 @@
   const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   const scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
   const scoped_refptr<webrtc::RtpReceiverInterface> webrtc_receiver_;
+  std::unique_ptr<RTCEncodedVideoStreamTransformer> transformer_;
   RtpReceiverState state_;
 };
 
@@ -240,10 +253,12 @@
 
 RTCRtpReceiverImpl::RTCRtpReceiverImpl(
     scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
-    RtpReceiverState state)
+    RtpReceiverState state,
+    bool force_encoded_video_insertable_streams)
     : internal_(base::MakeRefCounted<RTCRtpReceiverInternal>(
           std::move(native_peer_connection),
-          std::move(state))) {}
+          std::move(state),
+          force_encoded_video_insertable_streams)) {}
 
 RTCRtpReceiverImpl::RTCRtpReceiverImpl(const RTCRtpReceiverImpl& other)
     : internal_(other.internal_) {}
@@ -316,6 +331,11 @@
   internal_->SetJitterBufferMinimumDelay(delay_seconds);
 }
 
+RTCEncodedVideoStreamTransformer*
+RTCRtpReceiverImpl::GetEncodedVideoStreamTransformer() const {
+  return internal_->GetEncodedVideoStreamTransformer();
+}
+
 RTCRtpReceiverOnlyTransceiver::RTCRtpReceiverOnlyTransceiver(
     std::unique_ptr<RTCRtpReceiverPlatform> receiver)
     : receiver_(std::move(receiver)) {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.h
index df2b197..6f9b1c1 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.h
@@ -24,6 +24,8 @@
 
 namespace blink {
 
+class RTCEncodedVideoStreamTransformer;
+
 // This class represents the state of a receiver; a snapshot of what a
 // webrtc-layer receiver looked like when it was inspected on the signaling
 // thread such that this information can be moved to the main thread in a single
@@ -113,7 +115,8 @@
 
   RTCRtpReceiverImpl(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
-      RtpReceiverState state);
+      RtpReceiverState state,
+      bool force_encoded_video_insertable_streams);
   RTCRtpReceiverImpl(const RTCRtpReceiverImpl& other);
   ~RTCRtpReceiverImpl() override;
 
@@ -135,6 +138,8 @@
   std::unique_ptr<webrtc::RtpParameters> GetParameters() const override;
   void SetJitterBufferMinimumDelay(
       base::Optional<double> delay_seconds) override;
+  RTCEncodedVideoStreamTransformer* GetEncodedVideoStreamTransformer()
+      const override;
 
  private:
   class RTCRtpReceiverInternal;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl_test.cc
index 169fa78a..10642804 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl_test.cc
@@ -59,7 +59,8 @@
   }
 
   std::unique_ptr<RTCRtpReceiverImpl> CreateReceiver(
-      scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track) {
+      scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track,
+      bool force_encoded_video_insertable_streams = false) {
     std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
         track_ref;
     base::RunLoop run_loop;
@@ -76,8 +77,9 @@
         main_thread_, dependency_factory_->GetWebRtcSignalingTaskRunner(),
         mock_webrtc_receiver_.get(), std::move(track_ref), {});
     state.Initialize();
-    return std::make_unique<RTCRtpReceiverImpl>(peer_connection_.get(),
-                                                std::move(state));
+    return std::make_unique<RTCRtpReceiverImpl>(
+        peer_connection_.get(), std::move(state),
+        force_encoded_video_insertable_streams);
   }
 
   scoped_refptr<blink::TestWebRTCStatsReportObtainer> GetStats() {
@@ -117,6 +119,7 @@
   EXPECT_FALSE(receiver_->Track().IsNull());
   EXPECT_EQ(receiver_->Track().Id().Utf8(), webrtc_track->id());
   EXPECT_EQ(receiver_->state().track_ref()->webrtc_track(), webrtc_track);
+  EXPECT_FALSE(receiver_->GetEncodedVideoStreamTransformer());
 }
 
 TEST_F(RTCRtpReceiverImplTest, ShallowCopy) {
@@ -165,4 +168,12 @@
   EXPECT_EQ(stats->Timestamp(), 1.234);
 }
 
+TEST_F(RTCRtpReceiverImplTest, CreateReceiverWithInsertableStream) {
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("webrtc_track");
+  receiver_ = CreateReceiver(webrtc_track,
+                             /*force_encoded_video_insertable_streams=*/true);
+  EXPECT_TRUE(receiver_->GetEncodedVideoStreamTransformer());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index ec2155f..a3bdcfa 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -16,11 +16,16 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_capability.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtp_header_extension_parameters.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
 #include "third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_source.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_error_util.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_insertable_streams.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_void_request_script_promise_resolver_impl.h"
@@ -29,6 +34,7 @@
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h"
+#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_void_request.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -340,15 +346,20 @@
                            std::unique_ptr<RTCRtpSenderPlatform> sender,
                            String kind,
                            MediaStreamTrack* track,
-                           MediaStreamVector streams)
+                           MediaStreamVector streams,
+                           bool force_encoded_video_insertable_streams)
     : pc_(pc),
       sender_(std::move(sender)),
       kind_(std::move(kind)),
       track_(track),
-      streams_(std::move(streams)) {
+      streams_(std::move(streams)),
+      force_encoded_video_insertable_streams_(
+          force_encoded_video_insertable_streams) {
   DCHECK(pc_);
   DCHECK(sender_);
   DCHECK(!track || kind_ == track->kind());
+  if (force_encoded_video_insertable_streams_)
+    RegisterEncodedVideoStreamCallback();
 }
 
 MediaStreamTrack* RTCRtpSender::track() {
@@ -596,9 +607,20 @@
 RTCInsertableStreams* RTCRtpSender::createEncodedVideoStreams(
     ScriptState* script_state,
     ExceptionState& exception_state) {
-  exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                    "Not supported");
-  return nullptr;
+  if (!force_encoded_video_insertable_streams_) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "Encoded video streams not requested at PC initialization");
+    return nullptr;
+  }
+  if (encoded_video_streams_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Encoded video streams already created");
+    return nullptr;
+  }
+
+  InitializeEncodedVideoStreams(script_state);
+  return encoded_video_streams_;
 }
 
 void RTCRtpSender::Trace(Visitor* visitor) {
@@ -609,6 +631,9 @@
   visitor->Trace(streams_);
   visitor->Trace(last_returned_parameters_);
   visitor->Trace(transceiver_);
+  visitor->Trace(video_from_encoder_underlying_source_);
+  visitor->Trace(video_to_packetizer_underlying_sink_);
+  visitor->Trace(encoded_video_streams_);
   ScriptWrappable::Trace(visitor);
 }
 
@@ -668,4 +693,67 @@
   return capabilities;
 }
 
+void RTCRtpSender::RegisterEncodedVideoStreamCallback() {
+  DCHECK(!web_sender()
+              ->GetEncodedVideoStreamTransformer()
+              ->HasTransformerCallback());
+  web_sender()->GetEncodedVideoStreamTransformer()->SetTransformerCallback(
+      WTF::BindRepeating(&RTCRtpSender::OnFrameFromEncoder,
+                         WrapWeakPersistent(this)));
+}
+
+void RTCRtpSender::UnregisterEncodedVideoStreamCallback() {
+  web_sender()->GetEncodedVideoStreamTransformer()->ResetTransformerCallback();
+}
+
+void RTCRtpSender::InitializeEncodedVideoStreams(ScriptState* script_state) {
+  DCHECK(!video_from_encoder_underlying_source_);
+  DCHECK(!video_to_packetizer_underlying_sink_);
+  DCHECK(!encoded_video_streams_);
+  DCHECK(force_encoded_video_insertable_streams_);
+
+  encoded_video_streams_ = RTCInsertableStreams::Create();
+
+  // Set up readable stream.
+  video_from_encoder_underlying_source_ =
+      MakeGarbageCollected<RTCEncodedVideoUnderlyingSource>(
+          script_state,
+          WTF::Bind(&RTCRtpSender::UnregisterEncodedVideoStreamCallback,
+                    WrapWeakPersistent(this)));
+  // The high water mark for the readable stream is set to 0 so that frames are
+  // removed from the queue right away, without introducing any buffering.
+  encoded_video_streams_->setReadableStream(
+      ReadableStream::CreateWithCountQueueingStrategy(
+          script_state, video_from_encoder_underlying_source_,
+          /*high_water_mark=*/0));
+
+  // Set up writable stream.
+  video_to_packetizer_underlying_sink_ =
+      MakeGarbageCollected<RTCEncodedVideoUnderlyingSink>(
+          script_state,
+          WTF::BindRepeating(
+              [](RTCRtpSender* sender) -> RTCEncodedVideoStreamTransformer* {
+                return sender ? sender->web_sender()
+                                    ->GetEncodedVideoStreamTransformer()
+                              : nullptr;
+              },
+              WrapWeakPersistent(this)));
+  // The high water mark for the stream is set to 1 so that the stream is
+  // ready to write, but without queuing frames.
+  encoded_video_streams_->setWritableStream(
+      WritableStream::CreateWithCountQueueingStrategy(
+          script_state, video_to_packetizer_underlying_sink_,
+          /*high_water_mark=*/1));
+}
+
+void RTCRtpSender::OnFrameFromEncoder(
+    std::unique_ptr<webrtc::video_coding::EncodedFrame> frame,
+    std::vector<uint8_t> additional_data,
+    uint32_t ssrc) {
+  if (video_from_encoder_underlying_source_) {
+    video_from_encoder_underlying_source_->OnFrameFromSource(
+        std::move(frame), std::move(additional_data));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
index c9d86b0..16d2bce 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
@@ -19,16 +19,25 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/webrtc/api/rtp_transceiver_interface.h"
 
+namespace webrtc {
+namespace video_coding {
+class EncodedFrame;
+}  // namespace video_coding
+}  // namespace webrtc
+
 namespace blink {
 
 class ExceptionState;
 class MediaStreamTrack;
 class RTCDtlsTransport;
 class RTCDTMFSender;
+class RTCEncodedVideoUnderlyingSink;
+class RTCEncodedVideoUnderlyingSource;
 class RTCInsertableStreams;
 class RTCPeerConnection;
 class RTCRtpCapabilities;
 class RTCRtpTransceiver;
+class RTCInsertableStreams;
 
 webrtc::RtpEncodingParameters ToRtpEncodingParameters(
     const RTCRtpEncodingParameters*);
@@ -48,7 +57,8 @@
                std::unique_ptr<RTCRtpSenderPlatform>,
                String kind,
                MediaStreamTrack*,
-               MediaStreamVector streams);
+               MediaStreamVector streams,
+               bool force_encoded_video_insertable_streams);
 
   MediaStreamTrack* track();
   RTCDtlsTransport* transport();
@@ -79,6 +89,14 @@
   void Trace(Visitor*) override;
 
  private:
+  void RegisterEncodedVideoStreamCallback();
+  void UnregisterEncodedVideoStreamCallback();
+  void InitializeEncodedVideoStreams(ScriptState*);
+  void OnFrameFromEncoder(
+      std::unique_ptr<webrtc::video_coding::EncodedFrame> frame,
+      std::vector<uint8_t> additional_data,
+      uint32_t ssrc);
+
   Member<RTCPeerConnection> pc_;
   std::unique_ptr<RTCRtpSenderPlatform> sender_;
   // The spec says that "kind" should be looked up in transceiver, but keeping
@@ -90,6 +108,12 @@
   MediaStreamVector streams_;
   Member<RTCRtpSendParameters> last_returned_parameters_;
   Member<RTCRtpTransceiver> transceiver_;
+
+  // Insertable Streams support
+  bool force_encoded_video_insertable_streams_;
+  Member<RTCEncodedVideoUnderlyingSource> video_from_encoder_underlying_source_;
+  Member<RTCEncodedVideoUnderlyingSink> video_to_packetizer_underlying_sink_;
+  Member<RTCInsertableStreams> encoded_video_streams_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
index 7722e11..91599bb 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/logging.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h"
+#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_void_request.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
@@ -183,7 +184,8 @@
   RTCRtpSenderInternal(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
-      RtpSenderState state)
+      RtpSenderState state,
+      bool force_encoded_video_insertable_streams)
       : native_peer_connection_(std::move(native_peer_connection)),
         track_map_(std::move(track_map)),
         main_task_runner_(state.main_task_runner()),
@@ -192,6 +194,12 @@
         state_(std::move(state)) {
     DCHECK(track_map_);
     DCHECK(state_.is_initialized());
+    if (force_encoded_video_insertable_streams) {
+      transformer_ =
+          std::make_unique<RTCEncodedVideoStreamTransformer>(main_task_runner_);
+      webrtc_sender_->SetEncoderToPacketizerFrameTransformer(
+          transformer_->Delegate());
+    }
   }
 
   const RtpSenderState& state() const {
@@ -307,6 +315,10 @@
                             WrapRefCounted(this), stream_ids));
   }
 
+  RTCEncodedVideoStreamTransformer* GetEncodedVideoStreamTransformer() const {
+    return transformer_.get();
+  }
+
  private:
   friend class WTF::ThreadSafeRefCounted<RTCRtpSenderInternal,
                                          RTCRtpSenderInternalTraits>;
@@ -393,6 +405,7 @@
   const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   const scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
   const scoped_refptr<webrtc::RtpSenderInterface> webrtc_sender_;
+  std::unique_ptr<RTCEncodedVideoStreamTransformer> transformer_;
   RtpSenderState state_;
   webrtc::RtpParameters parameters_;
 };
@@ -421,11 +434,13 @@
 RTCRtpSenderImpl::RTCRtpSenderImpl(
     scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
     scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
-    RtpSenderState state)
+    RtpSenderState state,
+    bool force_encoded_video_insertable_streams)
     : internal_(base::MakeRefCounted<RTCRtpSenderInternal>(
           std::move(native_peer_connection),
           std::move(track_map),
-          std::move(state))) {}
+          std::move(state),
+          force_encoded_video_insertable_streams)) {}
 
 RTCRtpSenderImpl::RTCRtpSenderImpl(const RTCRtpSenderImpl& other)
     : internal_(other.internal_) {}
@@ -522,6 +537,11 @@
   return internal_->RemoveFromPeerConnection(pc);
 }
 
+RTCEncodedVideoStreamTransformer*
+RTCRtpSenderImpl::GetEncodedVideoStreamTransformer() const {
+  return internal_->GetEncodedVideoStreamTransformer();
+}
+
 RTCRtpSenderOnlyTransceiver::RTCRtpSenderOnlyTransceiver(
     std::unique_ptr<blink::RTCRtpSenderPlatform> sender)
     : sender_(std::move(sender)) {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.h
index cc0aec92..5ba8472a 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.h
@@ -23,6 +23,8 @@
 
 namespace blink {
 
+class RTCEncodedVideoStreamTransformer;
+
 // This class represents the state of a sender; a snapshot of what a
 // webrtc-layer sender looked like when it was inspected on the signaling thread
 // such that this information can be moved to the main thread in a single
@@ -120,7 +122,8 @@
   RTCRtpSenderImpl(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
-      RtpSenderState state);
+      RtpSenderState state,
+      bool force_encoded_video_insertable_streams);
   RTCRtpSenderImpl(const RTCRtpSenderImpl& other);
   ~RTCRtpSenderImpl() override;
   RTCRtpSenderImpl& operator=(const RTCRtpSenderImpl& other);
@@ -145,6 +148,8 @@
   void GetStats(RTCStatsReportCallback,
                 const Vector<webrtc::NonStandardGroupId>&) override;
   void SetStreams(const Vector<String>& stream_ids) override;
+  RTCEncodedVideoStreamTransformer* GetEncodedVideoStreamTransformer()
+      const override;
 
   // The ReplaceTrack() that takes a blink::RTCVoidRequest is implemented on
   // top of this, which returns the result in a callback instead. Allows doing
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
index 8d180e74..f88d0c24 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
@@ -96,7 +96,8 @@
         std::vector<std::string>());
     sender_state.Initialize();
     return std::make_unique<RTCRtpSenderImpl>(
-        peer_connection_.get(), track_map_, std::move(sender_state));
+        peer_connection_.get(), track_map_, std::move(sender_state),
+        /*force_encoded_video_insertable_streams=*/false);
   }
 
   // Calls replaceTrack(), which is asynchronous, returning a callback that when
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
index 250c8df..12b89fa 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
@@ -186,15 +186,18 @@
   RTCRtpTransceiverInternal(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
-      RtpTransceiverState state)
+      RtpTransceiverState state,
+      bool force_encoded_video_insertable_streams)
       : main_task_runner_(state.main_task_runner()),
         signaling_task_runner_(state.signaling_task_runner()),
         webrtc_transceiver_(state.webrtc_transceiver()),
         state_(std::move(state)) {
     sender_ = std::make_unique<blink::RTCRtpSenderImpl>(
-        native_peer_connection, track_map, state_.MoveSenderState());
+        native_peer_connection, track_map, state_.MoveSenderState(),
+        force_encoded_video_insertable_streams);
     receiver_ = std::make_unique<blink::RTCRtpReceiverImpl>(
-        native_peer_connection, state_.MoveReceiverState());
+        native_peer_connection, state_.MoveReceiverState(),
+        force_encoded_video_insertable_streams);
   }
 
   const RtpTransceiverState& state() const {
@@ -297,11 +300,13 @@
 RTCRtpTransceiverImpl::RTCRtpTransceiverImpl(
     scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
     scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
-    RtpTransceiverState transceiver_state)
+    RtpTransceiverState transceiver_state,
+    bool force_encoded_video_insertable_streams)
     : internal_(base::MakeRefCounted<RTCRtpTransceiverInternal>(
           std::move(native_peer_connection),
           std::move(track_map),
-          std::move(transceiver_state))) {}
+          std::move(transceiver_state),
+          force_encoded_video_insertable_streams)) {}
 
 RTCRtpTransceiverImpl::RTCRtpTransceiverImpl(const RTCRtpTransceiverImpl& other)
     : internal_(other.internal_) {}
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h
index 7e900aa..804c8ce 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h
@@ -152,7 +152,8 @@
   RTCRtpTransceiverImpl(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
-      RtpTransceiverState state);
+      RtpTransceiverState state,
+      bool force_encoded_video_insertable_streams);
   RTCRtpTransceiverImpl(const RTCRtpTransceiverImpl& other);
   ~RTCRtpTransceiverImpl() override;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
index 7553443..b22e1971 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
@@ -254,8 +254,9 @@
   EXPECT_FALSE(transceiver_state.is_initialized());
   transceiver_state.Initialize();
 
-  RTCRtpTransceiverImpl transceiver(peer_connection_.get(), track_map_,
-                                    std::move(transceiver_state));
+  RTCRtpTransceiverImpl transceiver(
+      peer_connection_.get(), track_map_, std::move(transceiver_state),
+      /*force_encoded_video_insertable_streams=*/false);
   EXPECT_TRUE(transceiver.Mid().IsNull());
   EXPECT_TRUE(transceiver.Sender());
   EXPECT_TRUE(transceiver.Receiver());
@@ -298,8 +299,9 @@
 
   // Modifying the webrtc transceiver after the initial state was created should
   // not have affected the transceiver state.
-  RTCRtpTransceiverImpl transceiver(peer_connection_.get(), track_map_,
-                                    std::move(initial_transceiver_state));
+  RTCRtpTransceiverImpl transceiver(
+      peer_connection_.get(), track_map_, std::move(initial_transceiver_state),
+      /*force_encoded_video_insertable_streams=*/false);
   EXPECT_TRUE(transceiver.Mid().IsNull());
   EXPECT_TRUE(transceiver.Sender());
   EXPECT_TRUE(transceiver.Receiver());
@@ -343,7 +345,8 @@
     EXPECT_FALSE(transceiver_state.is_initialized());
     transceiver_state.Initialize();
     transceiver.reset(new RTCRtpTransceiverImpl(
-        peer_connection_.get(), track_map_, std::move(transceiver_state)));
+        peer_connection_.get(), track_map_, std::move(transceiver_state),
+        /*force_encoded_video_insertable_streams=*/false));
   }
   DCHECK(transceiver);
   EXPECT_FALSE(transceiver->Stopped());
@@ -402,8 +405,9 @@
   modified_transceiver_state.Initialize();
 
   // Construct a transceiver from the initial state.
-  RTCRtpTransceiverImpl transceiver(peer_connection_.get(), track_map_,
-                                    std::move(initial_transceiver_state));
+  RTCRtpTransceiverImpl transceiver(
+      peer_connection_.get(), track_map_, std::move(initial_transceiver_state),
+      /*force_encoded_video_insertable_streams=*/false);
   // Setting the state with TransceiverStateUpdateMode::kSetDescription should
   // make the transceiver state up-to-date, except leaving
   // "transceiver.direction" and "transceiver.sender.track" unmodified.
diff --git a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
index 7fad5d7..434a7cb 100644
--- a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
+++ b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 
 typemaps = [
-  "//media/capture/mojom/video_capture_types.typemap",
+  "//media/capture/mojom/video_capture_types_for_blink.typemap",
   "//media/mojo/mojom/audio_parameters.typemap",
   "//services/network/public/cpp/http_request_headers.typemap",
   "//services/network/public/cpp/ip_address.typemap",
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h b/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h
index f753e6a..40042a71 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h
@@ -20,6 +20,7 @@
 
 class RTCRtpSource;
 class WebMediaStreamTrack;
+class RTCEncodedVideoStreamTransformer;
 
 // Implementations of this interface keep the corresponding WebRTC-layer
 // receiver alive through reference counting. Multiple |RTCRtpReceiverPlatform|s
@@ -46,6 +47,10 @@
   virtual std::unique_ptr<webrtc::RtpParameters> GetParameters() const = 0;
   virtual void SetJitterBufferMinimumDelay(
       base::Optional<double> delay_seconds) = 0;
+  virtual RTCEncodedVideoStreamTransformer* GetEncodedVideoStreamTransformer()
+      const {
+    return nullptr;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h b/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h
index b9abe44..d474552 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h
@@ -20,6 +20,7 @@
 class RTCVoidRequest;
 class WebMediaStreamTrack;
 class RtcDtmfSenderHandler;
+class RTCEncodedVideoStreamTransformer;
 
 // Implementations of this interface keep the corresponding WebRTC-layer sender
 // alive through reference counting. Multiple |RTCRtpSenderPlatform|s could
@@ -53,6 +54,10 @@
   virtual void GetStats(RTCStatsReportCallback,
                         const Vector<webrtc::NonStandardGroupId>&) = 0;
   virtual void SetStreams(const Vector<String>& stream_ids) = 0;
+  virtual RTCEncodedVideoStreamTransformer* GetEncodedVideoStreamTransformer()
+      const {
+    return nullptr;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/tools/blinkpy/third_party/README.chromium b/third_party/blink/tools/blinkpy/third_party/README.chromium
index 22beedb..4ae445d 100644
--- a/third_party/blink/tools/blinkpy/third_party/README.chromium
+++ b/third_party/blink/tools/blinkpy/third_party/README.chromium
@@ -43,6 +43,7 @@
     for more details on maintenance.
 Local Modifications:
 - Removed all files except for those listed in wpt/WPTWhiteList.
-- Removed a few unimported subcommands from tools/wpt/path.
 - Cherry-picked the server part of https://github.com/web-platform-tests/wpt/pull/21705
   (we cannot yet directly roll to that revision because of MANIFEST v8 changes).
+- Cherry-picked the server part of https://github.com/web-platform-tests/wpt/pull/21845
+  (Same reason with the previous cherry-pick. This enables pywebsocket3 usage).
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/WPTWhiteList b/third_party/blink/tools/blinkpy/third_party/wpt/WPTWhiteList
index 34e0c68..22b82bf 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/WPTWhiteList
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/WPTWhiteList
@@ -5,6 +5,7 @@
 ./tools/__init__.py
 ./tools/ci/commands.json
 ./tools/conftest.py
+./tools/docker/commands.json
 ./tools/gitignore/__init__.py
 ./tools/gitignore/gitignore.py
 ./tools/lint/__init__.py
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh b/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
index 34b2a1c1..e043ecb 100755
--- a/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
@@ -21,9 +21,8 @@
   echo "WPTHead: " `git rev-parse HEAD`
 
   # Apply local changes.
-  git apply $DIR/chromium.patch
-  # Chromium presubmit requires scripts with shebang to be executable.
-  chmod 755 tools/manifest/update.py
+  git cherry-pick 644a206e8ace488eac7e2b2a58a4b5354b02363a
+  git cherry-pick 7e52ecb9b61b73425093d39dbadceb9c6e10b754
 }
 
 function reduce {
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch b/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch
deleted file mode 100644
index e338114b..0000000
--- a/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-diff --git a/tools/wpt/paths b/tools/wpt/paths
-index 4528222fbf..93c97dc02b 100644
---- a/tools/wpt/paths
-+++ b/tools/wpt/paths
-@@ -1,5 +1,3 @@
--tools/ci/
--tools/docker/
- tools/lint/
- tools/manifest/
- tools/serve/
- tools/wpt/
-diff --git a/tools/serve/serve.py b/tools/serve/serve.py
-index 0985810cc5..055b60f1e7 100644
---- a/tools/serve/serve.py
-+++ b/tools/serve/serve.py
-@@ -366,6 +366,7 @@ class RoutesBuilder(object):
-             ("GET", "*.any.serviceworker.html", ServiceWorkersHandler),
-             ("GET", "*.any.worker.js", AnyWorkerHandler),
-             ("GET", "*.asis", handlers.AsIsHandler),
-+            ("GET", "/.well-known/origin-policy", handlers.PythonScriptHandler),
-             ("*", "*.py", handlers.PythonScriptHandler),
-             ("GET", "*", handlers.FileHandler)
-         ]
-@@ -742,6 +743,9 @@ def build_config(override_path=None, **kwargs):
- def _make_subdomains_product(s, depth=2):
-     return {u".".join(x) for x in chain(*(product(s, repeat=i) for i in range(1, depth+1)))}
- 
-+def _make_origin_policy_subdomains(limit):
-+    return {u"op%d" % x for x in range(1,limit+1)}
-+
- 
- _subdomains = {u"www",
-                u"www1",
-@@ -753,6 +757,12 @@ _not_subdomains = {u"nonexistent"}
- 
- _subdomains = _make_subdomains_product(_subdomains)
- 
-+# Origin policy subdomains need to not be reused by any other tests, since origin policies have
-+# origin-wide impacts like installing a CSP or Feature Policy that could interfere with features
-+# under test.
-+# See https://github.com/web-platform-tests/rfcs/pull/44.
-+_subdomains |= _make_origin_policy_subdomains(99)
-+
- _not_subdomains = _make_subdomains_product(_not_subdomains)
- 
- 
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/docker/commands.json b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/docker/commands.json
new file mode 100644
index 0000000..15182cc
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/docker/commands.json
@@ -0,0 +1,6 @@
+{
+    "docker-run": {"path": "frontend.py", "script": "run", "parser": "parser_run", "help": "Run wpt docker image",
+                   "virtualenv": false},
+    "docker-build": {"path": "frontend.py", "script": "build", "help": "Build wpt docker image",
+                     "virtualenv": false}
+}
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/localpaths.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/localpaths.py
index f6e24b4..ce3b41e3 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/localpaths.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/localpaths.py
@@ -6,7 +6,7 @@
 
 sys.path.insert(0, os.path.join(here))
 sys.path.insert(0, os.path.join(here, "wptserve"))
-sys.path.insert(0, os.path.join(here, "pywebsocket"))
+sys.path.insert(0, os.path.join(here, "third_party", "pywebsocket3"))
 sys.path.insert(0, os.path.join(here, "third_party", "atomicwrites"))
 sys.path.insert(0, os.path.join(here, "third_party", "attrs", "src"))
 sys.path.insert(0, os.path.join(here, "third_party", "funcsigs"))
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py
index e1d60962..5317be01 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py
@@ -592,21 +592,9 @@
                     "-w", handlers_root]
 
         if ssl_config is not None:
-            # This is usually done through pywebsocket.main, however we're
-            # working around that to get the server instance and manually
-            # setup the wss server.
-            if pywebsocket._import_ssl():
-                tls_module = pywebsocket._TLS_BY_STANDARD_MODULE
-            elif pywebsocket._import_pyopenssl():
-                tls_module = pywebsocket._TLS_BY_PYOPENSSL
-            else:
-                print("No SSL module available")
-                sys.exit(1)
-
             cmd_args += ["--tls",
                          "--private-key", ssl_config["key_path"],
-                         "--certificate", ssl_config["cert_path"],
-                         "--tls-module", tls_module]
+                         "--certificate", ssl_config["cert_path"]]
 
         if (bind_address):
             cmd_args = ["-H", host] + cmd_args
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/paths b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/paths
index 93c97dc..35867c4 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/paths
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/paths
@@ -1,3 +1,5 @@
+tools/ci/
+tools/docker/
 tools/lint/
 tools/manifest/
 tools/serve/
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/config.py b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/config.py
index e11cae1..7766565 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/config.py
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/config.py
@@ -69,6 +69,31 @@
     def as_dict(self):
         return json_types(self.__dict__)
 
+    # Environment variables are limited in size so we need to prune the most egregious contributors
+    # to size, the origin policy subdomains.
+    def as_dict_for_wd_env_variable(self):
+        result = self.as_dict()
+
+        for key in [
+            ("subdomains",),
+            ("domains", "alt"),
+            ("domains", ""),
+            ("all_domains", "alt"),
+            ("all_domains", ""),
+            ("domains_set",),
+            ("all_domains_set",)
+        ]:
+            target = result
+            for part in key[:-1]:
+                target = target[part]
+            value = target[key[-1]]
+            if isinstance(value, dict):
+                target[key[-1]] = {k:v for (k,v) in iteritems(value) if not k.startswith("op")}
+            else:
+                target[key[-1]] = [x for x in value if not x.startswith("op")]
+
+        return result
+
 
 def json_types(obj):
     if isinstance(obj, dict):
diff --git a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py
index 8680b260..01261e4 100644
--- a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py
+++ b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py
@@ -36,7 +36,7 @@
         self._config_file = fs.join(self._runtime_path, 'wpt.config.json')
 
         finder = PathFinder(fs)
-        path_to_pywebsocket = finder.path_from_chromium_base('third_party', 'pywebsocket', 'src')
+        path_to_pywebsocket = finder.path_from_chromium_base('third_party', 'pywebsocket3', 'src')
         self.path_to_wpt_support = finder.path_from_blink_tools('blinkpy', 'third_party', 'wpt')
         path_to_wpt_root = fs.join(self.path_to_wpt_support, 'wpt')
         path_to_wpt_tests = fs.abspath(fs.join(self._port_obj.web_tests_dir(), 'external', 'wpt'))
diff --git a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py
index b996f07..59680b1f 100644
--- a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py
@@ -59,7 +59,7 @@
         self.assertEqual(server._env, {
             'MOCK_ENVIRON_COPY': '1',
             'PATH': '/bin:/mock/bin',
-            'PYTHONPATH': '/mock-checkout/third_party/pywebsocket/src'
+            'PYTHONPATH': '/mock-checkout/third_party/pywebsocket3/src'
         })
 
     def test_prepare_config(self):
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index 6fd92b7..fc4dbb01 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -119,7 +119,7 @@
 crbug.com/856601 [ Linux ] external/wpt/IndexedDB/interfaces.any.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/appmanifest/idlharness.window.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/bluetooth/idl/idlharness.tentative.window.html [ Pass Timeout ]
-crbug.com/856601 [ Linux ] virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/idlharness.tentative.https.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/idl/idlharness.tentative.https.window.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/cookie-store/idlharness.tentative.https.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/css/css-animations/idlharness.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/css/css-font-loading/idlharness.https.html [ Pass Timeout ]
@@ -190,6 +190,7 @@
 crbug.com/856601 [ Linux ] external/wpt/wake-lock/idlharness.https.any.worker.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] virtual/omt-worker-fetch/external/wpt/fetch/api/idl.any.sharedworker.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/trusted-types/idlharness.tentative.window.html [ Pass Timeout ]
+crbug.com/856601 [ Linux ] external/wpt/trusted-types/idlharness.tentative.https.window.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/hr-time/idlharness.any.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/media-playback-quality/idlharness.window.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/native-file-system/idlharness.https.any.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e606975..4697c7f 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1120,9 +1120,9 @@
 crbug.com/988015 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-003.html [ Failure ]
 crbug.com/994172 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-007.html [ Pass Crash ]
 crbug.com/924142 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-010.html [ Crash Pass ]
-crbug.com/874051 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-001.html [ Failure ]
-crbug.com/874051 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-002.html [ Failure ]
-crbug.com/874051 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-003.html [ Failure ]
+crbug.com/874051 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-001.html [ Crash Failure ]
+crbug.com/874051 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-002.html [ Crash Failure ]
+crbug.com/874051 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-003.html [ Crash Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-list-item-001.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-margin-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-margin-002.xht [ Failure ]
@@ -1204,6 +1204,7 @@
 crbug.com/874506 virtual/layout_ng_block_frag/fast/multicol/event-offset-complex-tree.html [ Failure ]
 crbug.com/874506 virtual/layout_ng_block_frag/fast/multicol/event-offset.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/event-offset-in-nested.html [ Failure ]
+crbug.com/875235 virtual/layout_ng_block_frag/fast/multicol/fieldset-as-multicol.html [ Crash Failure ]
 crbug.com/874506 virtual/layout_ng_block_frag/fast/multicol/filter-in-second-column.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/fixedpos-child-becomes-static.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/float-after-break-after.html [ Failure ]
@@ -1606,6 +1607,13 @@
 
 # Fieldset in NG
 #
+crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/css/css-contain/contain-size-breaks-001.html [ Crash Failure ]
+crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/css/css-contain/contain-size-monolithic-001.html [ Crash Failure ]
+crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/css/css-contain/contain-size-multicol-001.html [ Failure ]
+crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-multicol-001.html [ Failure ]
+crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-multicol-002.html [ Crash Failure ]
+crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-multicol-003.html [ Failure ]
+crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-multicol.html [ Crash Failure ]
 crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow.html [ Failure ]
 crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden.html [ Failure ]
 crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order.html [ Failure ]
@@ -2389,6 +2397,9 @@
 crbug.com/1032016 http/tests/devtools/bindings/inline-styles-binding.js [ Pass Failure ]
 crbug.com/1032016 http/tests/devtools/bindings/livelocation-main-frame-navigated.js [ Pass Failure ]
 
+# Temporarily disabled so we can land a frontend change that removes side-effect handling in the frontend.
+crbug.com/1031243 http/tests/devtools/console-cd-completions.js [ Pass Failure ]
+
 # We only want to run one of the web-animations-api tests in stable mode.
 crbug.com/441553 virtual/stable/web-animations-api/* [ Skip ]
 # These tests *only* run in stable, to verify that these features are unsupported and throw exceptions.
@@ -6583,7 +6594,7 @@
 crbug.com/1048149 [ Mac ] virtual/controls-refresh/color/color-picker-zoom150-bottom-edge-no-nan.html [ Pass Crash ]
 # This used to be [ Pass Crash ], but as of crbug.com/1050039 it started flaking as well.
 crbug.com/1048149 crbug.com/1050121 [ Mac ] virtual/controls-refresh/month-picker/month-picker-appearance-zoom150.html [ Pass Failure Crash Failure ]
-crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-key-operations.html [ Pass Crash ]
+crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-key-operations.html [ Pass Crash Timeout ]
 crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-touch-operations.html [ Pass Crash ]
 crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-type-change-onchange.html [ Pass Crash ]
 crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/date-picker-choose-default-value-after-set-value.html [ Pass Crash ]
@@ -6592,7 +6603,7 @@
 crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-appearance-step.html [ Pass Crash ]
 crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-appearance.html [ Pass Crash ]
 crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-choose-default-value-after-set-value.html [ Pass Crash ]
-crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-key-operations.html [ Pass Crash ]
+crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-key-operations.html [ Pass Crash Timeout ]
 crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-mouse-operations.html [ Pass Crash ]
 crbug.com/1048149 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/month-picker-touch-operations.html [ Pass Crash ]
 
@@ -6618,7 +6629,7 @@
 crbug.com/1050039 [ Mac ] fast/forms/calendar-picker/calendar-picker-type-change-onchange.html [ Pass Failure ]
 crbug.com/1050039 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-appearance.html [ Pass Failure ]
 crbug.com/1050039 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/datetimelocal-picker-choose-default-value-after-set-value.html [ Pass Failure ]
-crbug.com/1050039 [ Mac ] virtual/not-site-per-process/http/tests/devtools/oopif/oopif-storage.js [ Pass Failure ]
+crbug.com/1050039 [ Mac ] virtual/not-site-per-process/http/tests/devtools/oopif/oopif-storage.js [ Pass Failure Timeout ]
 crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-01.html [ Pass Failure ]
 crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-02.html [ Pass Failure ]
 crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-03.html [ Pass Failure ]
@@ -7021,3 +7032,18 @@
 # Temporarily disable to land https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2090785
 crbug.com/1011811 third_party/blink/web_tests/http/tests/devtools/changes/changes-sidebar.js [ Pass Failure ]
 crbug.com/1011811 third_party/blink/web_tests/http/tests/devtools/search/search-in-static.js [ Pass Failure ]
+
+# Sheriff 2020-03-12
+crbug.com/1060175 [ Mac ] css3/calc/simple-calcs-prefixed.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] css3/calc/simple-calcs.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] editing/selection/programmatic-selection-on-mac-is-directionless.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] fast/events/platform-wheelevent-paging-x-in-scrolling-div.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] fast/events/platform-wheelevent-paging-y-in-scrolling-div.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] http/tests/websocket/multiple-connections-throttled.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] virtual/controls-refresh/datetimelocal-picker/datetimelocal-month-switcher-buttons-invalid.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/week-picker-key-operations.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-ax-value-changed-notification.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] virtual/form-controls-refresh-disabled/fast/forms/form-control-with-state-eager-tracing-crashTest.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] virtual/threaded/printing/page-count-with-one-word.html [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] virtual/web-components-v0-disabled/fast/dom/SelectorAPI/resig-SelectorsAPI-test.xhtml [ Pass Timeout ]
+crbug.com/1060175 [ Mac ] virtual/web-components-v0-disabled/fast/dom/zoom-scroll-page-test.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 7ce5e31..3f1380a 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -194,7 +194,7 @@
               "fast/multicol",
               "fragmentation",
               "printing"],
-    "args": ["--enable-blink-features=LayoutNGBlockFragmentation"]
+    "args": ["--enable-blink-features=LayoutNGBlockFragmentation,LayoutNGFieldset"]
   },
   {
     "prefix": "layout_ng_fragment_traversal",
@@ -216,7 +216,7 @@
               "external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain",
               "external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements",
               "fast/forms/fieldset"],
-    "args": ["--enable-blink-features=LayoutNGFieldset"]
+    "args": ["--enable-blink-features=LayoutNGFieldset,LayoutNGBlockFragmentation"]
   },
   {
     "prefix": "dark-mode-grayscale-images",
diff --git a/third_party/blink/web_tests/editing/caret/selection-with-caret-type-progress-expected.txt b/third_party/blink/web_tests/editing/caret/selection-with-caret-type-progress-expected.txt
index 616ec4c..781dff9 100644
--- a/third_party/blink/web_tests/editing/caret/selection-with-caret-type-progress-expected.txt
+++ b/third_party/blink/web_tests/editing/caret/selection-with-caret-type-progress-expected.txt
@@ -3,7 +3,7 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 Try selecting this text by dragging the cursor. Progress cursor should be displayed while doing so.
-PASS currentCursorType is "Progress"
+PASS cursorInfo is "type=Progress"
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/editing/caret/selection-with-caret-type-progress.html b/third_party/blink/web_tests/editing/caret/selection-with-caret-type-progress.html
index 3ed3e85..0c8f7b6b 100644
--- a/third_party/blink/web_tests/editing/caret/selection-with-caret-type-progress.html
+++ b/third_party/blink/web_tests/editing/caret/selection-with-caret-type-progress.html
@@ -28,8 +28,7 @@
     leapForwardAndMove(div.offsetWidth - 10);
 
     var cursorInfo = internals.getCurrentCursorInfo();
-    var currentCursorType = cursorInfo.substring(cursorInfo.indexOf('=') + 1, cursorInfo.lastIndexOf(' '));
-    shouldBeEqualToString('currentCursorType', 'Progress');
+    shouldBeEqualToString('cursorInfo', 'type=Progress');
 }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index e8f0a28..71d9503 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -162295,6 +162295,12 @@
    "fetch/api/cors/cors-preflight-redirect.any.worker-expected.txt": [
     []
    ],
+   "fetch/api/cors/cors-preflight-response-validation.any-expected.txt": [
+    []
+   ],
+   "fetch/api/cors/cors-preflight-response-validation.any.worker-expected.txt": [
+    []
+   ],
    "fetch/api/cors/resources/corspreflight.js": [
     []
    ],
@@ -164893,9 +164899,6 @@
    "html/cross-origin-embedder-policy/sandbox.https.html.headers": [
     []
    ],
-   "html/cross-origin-embedder-policy/service-worker-cache-storage.https-expected.txt": [
-    []
-   ],
    "html/cross-origin-embedder-policy/srcdoc.https.html.headers": [
     []
    ],
@@ -172831,6 +172834,12 @@
    "mathml/presentation-markup/fractions/frac-numalign-denomalign-001-ref.html": [
     []
    ],
+   "mathml/presentation-markup/fractions/frac-parameters-1-expected.txt": [
+    []
+   ],
+   "mathml/presentation-markup/fractions/frac-parameters-2-expected.txt": [
+    []
+   ],
    "mathml/presentation-markup/fractions/frac-parameters-gap-001-ref.html": [
     []
    ],
@@ -236336,6 +236345,12 @@
      {}
     ]
    ],
+   "css/css-values/viewport-units-after-font-load.html": [
+    [
+     "css/css-values/viewport-units-after-font-load.html",
+     {}
+    ]
+   ],
    "css/css-values/viewport-units-css2-001.html": [
     [
      "css/css-values/viewport-units-css2-001.html",
@@ -256956,6 +256971,46 @@
      }
     ]
    ],
+   "fetch/api/cors/cors-preflight-response-validation.any.js": [
+    [
+     "fetch/api/cors/cors-preflight-response-validation.any.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "/common/utils.js"
+       ],
+       [
+        "script",
+        "../resources/utils.js"
+       ],
+       [
+        "script",
+        "/common/get-host-info.sub.js"
+       ]
+      ]
+     }
+    ],
+    [
+     "fetch/api/cors/cors-preflight-response-validation.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "/common/utils.js"
+       ],
+       [
+        "script",
+        "../resources/utils.js"
+       ],
+       [
+        "script",
+        "/common/get-host-info.sub.js"
+       ]
+      ]
+     }
+    ]
+   ],
    "fetch/api/cors/cors-preflight-star.any.js": [
     [
      "fetch/api/cors/cors-preflight-star.any.html",
@@ -469463,6 +469518,10 @@
    "055f3d1fd2d716a68d857738493815c8772bef06",
    "reftest"
   ],
+  "css/css-values/viewport-units-after-font-load.html": [
+   "82e8b73d28dfc57ad572936fe46315d0d0289606",
+   "testharness"
+  ],
   "css/css-values/viewport-units-css2-001.html": [
    "c51237dd8a07546d31eef6f6f9b3c84b6ac2b65b",
    "testharness"
@@ -494775,6 +494834,18 @@
    "6d69b328644a8aa2155b25a153b094fec2bf18de",
    "testharness"
   ],
+  "fetch/api/cors/cors-preflight-response-validation.any-expected.txt": [
+   "17c93ce80ed0f0342c2109c6adab5d5689f481ff",
+   "support"
+  ],
+  "fetch/api/cors/cors-preflight-response-validation.any.js": [
+   "718e351c1d3f09aa41cc2ea7f7b071f4a7c48b2a",
+   "testharness"
+  ],
+  "fetch/api/cors/cors-preflight-response-validation.any.worker-expected.txt": [
+   "17c93ce80ed0f0342c2109c6adab5d5689f481ff",
+   "support"
+  ],
   "fetch/api/cors/cors-preflight-star.any.js": [
    "d76e9a21fd4b946803d8df193fcf00f53921aa9d",
    "testharness"
@@ -501056,7 +501127,7 @@
    "support"
   ],
   "html/cross-origin-embedder-policy/reporting.https.html": [
-   "bb0a6a2b0a8101665a25f2187c138ab0bdfd14e5",
+   "d883ceb939dd4748ab2338cb636e692203882c18",
    "testharness"
   ],
   "html/cross-origin-embedder-policy/reporting.https.html.sub.headers": [
@@ -501219,10 +501290,6 @@
    "6604450991a122e3e241e40b1b9e0516c525389d",
    "support"
   ],
-  "html/cross-origin-embedder-policy/service-worker-cache-storage.https-expected.txt": [
-   "10fe5417406e442745bda425931882c39c2e3e41",
-   "support"
-  ],
   "html/cross-origin-embedder-policy/service-worker-cache-storage.https.html": [
    "873f06ce4ffbf83bca2ac4dbdc04e5b5bf92abb6",
    "testharness"
@@ -522923,10 +522990,18 @@
    "bc9a2f9084471632a88e2b4d56db4557bfbe216e",
    "reftest"
   ],
+  "mathml/presentation-markup/fractions/frac-parameters-1-expected.txt": [
+   "eccd329c085bcd27189413782242561f0c3c3a55",
+   "support"
+  ],
   "mathml/presentation-markup/fractions/frac-parameters-1.html": [
    "b7efbc78ca01a3919b83edfed67c2721025d4e69",
    "testharness"
   ],
+  "mathml/presentation-markup/fractions/frac-parameters-2-expected.txt": [
+   "eb6b9a93c8e1d0a163181908fc25db2c43db7732",
+   "support"
+  ],
   "mathml/presentation-markup/fractions/frac-parameters-2.html": [
    "368fc0676d2135b6cd5381a822dd9ec92bf71512",
    "testharness"
@@ -585012,7 +585087,7 @@
    "support"
   ],
   "tools/requirements_mypy.txt": [
-   "a478015893d71a45953f9651844a35c2a46ba7d2",
+   "988ffe848174f7215278c2c4019f924bed3a61f5",
    "support"
   ],
   "tools/runner/css/bootstrap-theme.min.css": [
@@ -589296,7 +589371,7 @@
    "support"
   ],
   "tools/wptrunner/requirements_firefox.txt": [
-   "ae72940810432bed693c001d4b057a1214fd2dac",
+   "d541a49f3c0992c4a456ce4ebb717908683a464e",
    "support"
   ],
   "tools/wptrunner/requirements_ie.txt": [
@@ -599784,7 +599859,7 @@
    "support"
   ],
   "webrtc/protocol/bundle.https.html": [
-   "fcc9d470b9f7c5c4215da09666e471dfa0a192d1",
+   "61d1ff8ac108297dafe333694ddd1194ff53fe26",
    "testharness"
   ],
   "webrtc/protocol/candidate-exchange.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-after-font-load.html b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-after-font-load.html
new file mode 100644
index 0000000..82e8b73d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-after-font-load.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Values: Viewport units are computed correctly after font load.</title>
+<link rel="author"  title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author"  title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#viewport-relative-lengths">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1620359">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe width=300 height=300 scrolling=no srcdoc=""></iframe>
+<script>
+let t = async_test("Viewport units are correctly updated after resize even if a font load has happened before");
+let iframe = document.querySelector("iframe");
+onload = t.step_func(function() {
+  let doc = iframe.contentDocument;
+  let win = iframe.contentWindow;
+  doc.body.innerHTML = `
+    <div style="width: 100vw; height: 100vh; background: green"></div>
+  `;
+  let div = doc.querySelector("div");
+  let oldWidth = win.getComputedStyle(div).width;
+  let oldHeight = win.getComputedStyle(div).height;
+  assert_equals(oldWidth, win.innerWidth + "px", "Should fill the viewport");
+  assert_equals(oldHeight, win.innerHeight + "px", "Should fill the viewport");
+  let link = doc.createElement("link");
+  link.rel = "stylesheet";
+  link.href = "/fonts/ahem.css";
+  link.onload = t.step_func(function() {
+    iframe.width = 400;
+    win.requestAnimationFrame(t.step_func(function() {
+      win.requestAnimationFrame(t.step_func_done(function() {
+        let newWidth = win.getComputedStyle(div).width;
+        let newHeight = win.getComputedStyle(div).height;
+        assert_equals(newWidth, win.innerWidth + "px", "Should fill the viewport");
+        assert_equals(newHeight, win.innerHeight + "px", "Should fill the viewport");
+        assert_equals(newHeight, oldHeight, "Height shouldn't have changed");
+        assert_not_equals(newWidth, oldWidth, "Width should have changed");
+      }));
+    }));
+  });
+  doc.body.appendChild(link);
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any-expected.txt
new file mode 100644
index 0000000..17c93ce8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Preflight response with a bad Access-Control-Allow-Headers assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Preflight response with a bad Access-Control-Allow-Methods assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any.js b/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any.js
new file mode 100644
index 0000000..718e351
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any.js
@@ -0,0 +1,33 @@
+// META: script=/common/utils.js
+// META: script=../resources/utils.js
+// META: script=/common/get-host-info.sub.js
+
+function corsPreflightResponseValidation(desc, corsUrl, allowHeaders, allowMethods) {
+  var uuid_token = token();
+  var url = corsUrl;
+  var requestInit = {"mode": "cors"};
+  /* Force preflight */
+  requestInit["headers"] = {"x-force-preflight": ""};
+
+  var urlParameters = "?token=" + uuid_token + "&max_age=0";
+  urlParameters += "&allow_headers=x-force-preflight";
+  if (allowHeaders)
+    urlParameters += "," + allowHeaders;
+  if (allowMethods)
+    urlParameters += "&allow_methods="+ allowMethods;
+
+  promise_test(function(test) {
+    return fetch(RESOURCES_DIR + "clean-stash.py?token=" + uuid_token).then(async function(resp) {
+      assert_equals(resp.status, 200, "Clean stash response's status is 200");
+      await promise_rejects_js(test, TypeError, fetch(url + urlParameters, requestInit));
+
+      return fetch(url + urlParameters).then(function(resp) {
+        assert_equals(resp.headers.get("x-did-preflight"), "1", "Preflight request has been made");
+      });
+    });
+  }, desc);
+}
+
+var corsUrl = get_host_info().HTTP_REMOTE_ORIGIN + dirname(location.pathname) + RESOURCES_DIR + "preflight.py";
+corsPreflightResponseValidation("Preflight response with a bad Access-Control-Allow-Headers", corsUrl, "Bad value", null);
+corsPreflightResponseValidation("Preflight response with a bad Access-Control-Allow-Methods", corsUrl, null, "Bad value");
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any.worker-expected.txt
new file mode 100644
index 0000000..17c93ce8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/cors/cors-preflight-response-validation.any.worker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Preflight response with a bad Access-Control-Allow-Headers assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Preflight response with a bad Access-Control-Allow-Methods assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting.https.html
index bb0a6a2..d883ceb 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting.https.html
@@ -28,8 +28,8 @@
   }
 }
 
-let reports = []
-let reportsForReportOnly = []
+const reports = [];
+const reportsForReportOnly = [];
 pollReports('endpoint', reports);
 pollReports('report-only-endpoint', reportsForReportOnly);
 
@@ -48,6 +48,21 @@
   assert_unreached(`A report whose blocked-url is ${blockedUrl} and url is ${contextUrl} is not found.`);
 }
 
+function checkNavigationReportExistence(reports, blockedUrl, contextUrl) {
+  blockedUrl = new URL(blockedUrl, location).href;
+  contextUrl = new URL(contextUrl, location).href;
+  for (const report of reports) {
+    if (report.type !== 'coep' || report.url !== contextUrl ||
+        report.body.type !== 'navigation') {
+      continue;
+    }
+    if (report.body['blocked-url'] === blockedUrl) {
+      return;
+    }
+  }
+  assert_unreached(`A report whose blocked-url is ${blockedUrl} and url is ${contextUrl} is not found.`);
+}
+
 function checkReportNonExistence(reports, blockedUrl, contextUrl) {
   blockedUrl = new URL(blockedUrl, location).href;
   contextUrl = new URL(contextUrl, location).href;
@@ -87,7 +102,8 @@
     fetchInIframe(blockedDueToCoep);
     fetchInIframe(redirect);
 
-    await new Promise(resolve => t.step_timeout(resolve, 3000));
+    // Wait 3 seconds for reports to settle.
+    await wait(3000);
 
     checkReportNonExistence(reports, sameOriginUrl, iframe.src);
     checkReportNonExistence(reports, blockedByPureCorp, iframe.src);
@@ -121,16 +137,18 @@
     }
 
     const suffix = 'navigation-corp';
-    const sameOrigin = `/common/blank.html?${suffix}`;
-    const blockedDueToCoep = `${REMOTE_ORIGIN}/common/blank.html?abc&${suffix}`;
-    const dest = `${REMOTE_ORIGIN}/common/blank.html?xyz&${suffix}`;
+    const coep = `pipe=header(cross-origin-embedder-policy,require-corp)`;
+    const sameOrigin = `/common/blank.html?${coep}&${suffix}`;
+    const blockedDueToCoep = `${REMOTE_ORIGIN}/common/blank.html?${coep}&${suffix}-a`;
+    const dest = `${REMOTE_ORIGIN}/common/blank.html?${coep}&${suffix}-b`;
     const redirect = `/common/redirect.py?location=${encodeURIComponent(dest)}&${suffix}`;
 
     attachFrame(sameOrigin);
     attachFrame(blockedDueToCoep);
     attachFrame(redirect);
 
-    await new Promise(resolve => t.step_timeout(resolve, 3000));
+    // Wait 3 seconds for reports to settle.
+    await wait(3000);
 
     checkReportNonExistence(reports, sameOrigin, iframe.src);
     checkCorpReportExistence(reports, blockedDueToCoep, iframe.src);
@@ -143,4 +161,98 @@
   }
 }, 'navigation CORP');
 
-</script>
+async_test(async (t) => {
+  try {
+    const iframe = document.createElement('iframe');
+    t.add_cleanup(() => iframe.remove());
+
+    const suffix = '&navigation-coep';
+    const corp = 'header(cross-origin-resource-policy,cross-origin)';
+    const noCoep = `pipe=${corp}`;
+    const coep =
+      `pipe=header(cross-origin-embedder-policy,require-corp%3breport-to=%22endpoint%22)|${corp}`;
+    const coepReportOnly =
+      `pipe=header(cross-origin-embedder-policy-report-only,require-corp%3breport-to=%22report-only-endpoint%22)|${corp}`;
+    const path = `/common/blank.html`;
+    const pipes = [noCoep, coep, coepReportOnly];
+    const settings = new Map();
+    settings.set(noCoep, {
+      pipe: noCoep,
+      value: 'unsafe-none',
+      reportOnlyValue: 'unsafe-none',
+    });
+    settings.set(coep, {
+      pipe: coep,
+      value: 'require-corp',
+      reportOnlyValue: 'unsafe-none',
+    });
+    settings.set(coepReportOnly, {
+      pipe: coepReportOnly,
+      value: 'unsafe-none',
+      reportOnlyValue: 'require-corp',
+    });
+
+    function genUrl(pipe) {
+      return `${path}?${pipe}${suffix}`;
+    }
+
+    for (const outer of settings.keys()) {
+      for (const inner of settings.keys()) {
+        const iframe = document.createElement('iframe');
+        t.add_cleanup(() => iframe.remove());
+
+        iframe.src = genUrl(outer);
+        iframe.addEventListener('load', () => {
+          const w = iframe.contentWindow;
+          const d = iframe.contentDocument;
+          const nested = d.createElement('iframe');
+          nested.src = genUrl(inner) + '-nested';
+          d.body.appendChild(nested);
+        }, {once: true});
+        document.body.appendChild(iframe);
+      }
+    }
+
+    // Wait 3 seconds for reports to settle.
+    await wait(3000);
+
+    function check(rs, inner, outer) {
+      checkNavigationReportExistence(
+        rs, genUrl(inner) + '-nested', genUrl(outer));
+    }
+    function checkNoReport(reports, inner, outer) {
+      checkReportNonExistence(
+        reports, genUrl(inner) + '-nested', genUrl(outer));
+    }
+
+    // outer === noCoep
+    checkNoReport(reports, noCoep, noCoep);
+    checkNoReport(reports, coep, noCoep);
+    checkNoReport(reports, coepReportOnly, noCoep);
+    checkNoReport(reportsForReportOnly, noCoep, noCoep);
+    checkNoReport(reportsForReportOnly, coep, noCoep);
+    checkNoReport(reportsForReportOnly, coepReportOnly, noCoep);
+
+    // outer === coep
+    check(reports, noCoep, coep);
+    checkNoReport(reports, coep, coep);
+    check(reports, coepReportOnly, coep);
+    checkNoReport(reportsForReportOnly, noCoep, coep);
+    checkNoReport(reportsForReportOnly, coep, coep);
+    checkNoReport(reportsForReportOnly, coepReportOnly, coep);
+
+    // outer === coepReportOnly
+    checkNoReport(reports, noCoep, coepReportOnly);
+    checkNoReport(reports, coep, coepReportOnly);
+    checkNoReport(reports, coepReportOnly, coepReportOnly);
+    check(reportsForReportOnly, noCoep, coepReportOnly);
+    checkNoReport(reportsForReportOnly, coep, coepReportOnly);
+    check(reportsForReportOnly, coepReportOnly, coepReportOnly);
+
+    t.done();
+  } catch (e) {
+    t.step(() => { throw e });
+  }
+}, 'COEP violation on nested frame navigation');
+
+</script>$
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/service-worker-cache-storage.https-expected.txt b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/service-worker-cache-storage.https-expected.txt
deleted file mode 100644
index 10fe541..0000000
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/service-worker-cache-storage.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS A ServiceWorker with coep-none use CacheStorage to get a corp-undefined response.
-PASS A ServiceWorker with coep-none use CacheStorage to get a corp-cross-origin response.
-FAIL A ServiceWorker with coep-require-corp use CacheStorage to get a corp-undefined response. assert_equals: expected false but got true
-PASS A ServiceWorker with coep-require-corp use CacheStorage to get a corp-cross-origin response.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timeline-phases.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timeline-phases.tentative.html
index 0cc4e128..48ca749 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timeline-phases.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timeline-phases.tentative.html
@@ -22,13 +22,165 @@
 
   promise_test(async t => {
     const timeline = createScrollTimeline(t);
-    assert_equals(timeline.phase, "inactive");
-  }, 'Scroll timeline starts in "inactive" phase.');
-
-  promise_test(async t => {
-    const timeline = createScrollTimeline(t);
     assert_throws_js(TypeError, () => {
       timeline.phase = "after";
     });
   }, 'Setting scroll timeline phase (which is readonly) throws TypeError.');
+
+  const test_cases = {
+    before_start: {
+      name: "before start",
+      scroll_percent: 0.1,
+      timeline_phase: "before"
+    },
+    at_start: {
+      name: "at start",
+      scroll_percent: 0.2,
+      timeline_phase: "active"
+    },
+    in_range: {
+      name: "in range",
+      scroll_percent: 0.5,
+      timeline_phase: "active"
+    },
+    at_end: {
+      name: "at end",
+      scroll_percent: 0.8,
+      timeline_phase: "after"
+    },
+    after_end: {
+      name: "after end",
+      scroll_percent: 0.9,
+      timeline_phase: "after"
+    }
+  }
+
+  for (const test_case_key in test_cases){
+    const test_case = test_cases[test_case_key];
+    test(t => {
+      const timeline = createScrollTimelineWithOffsets(t, "20%", "80%");
+      const scroller = timeline.scrollSource;
+      const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+
+      scroller.scrollTop = test_case.scroll_percent * maxScroll;
+
+      assert_equals(
+        timeline.phase,
+        test_case.timeline_phase,
+        "timeline.phase"
+      );
+    }, "Timeline phase while scroll offset is " + test_case.name);
+  }
+
+  // TODO(crbug.com/1060384): Spec is unclear in this case, revisit this when
+  // desired results have been established.
+  // These test cases are worded strangely because they test an edge case
+  // where startScrollOffset is GREATER THAN endScrollOffset
+  const test_cases_start_offset_greater_than_end_offset = {
+    before_end: {
+      name: "before end",
+      scroll_percent: 0.1,
+      timeline_phase: "before"
+    },
+    at_end: {
+      name: "at end",
+      scroll_percent: 0.2,
+      timeline_phase: "before"
+    },
+    before_start: {
+      name: "before start",
+      scroll_percent: 0.5,
+      timeline_phase: "before"
+    },
+    at_start: {
+      name: "at start",
+      scroll_percent: 0.8,
+      timeline_phase: "after"
+    },
+    after_start: {
+      name: "after start",
+      scroll_percent: 0.9,
+      timeline_phase: "after"
+    }
+  }
+
+  for (const test_case_key in test_cases_start_offset_greater_than_end_offset){
+    const test_case =
+      test_cases_start_offset_greater_than_end_offset[test_case_key];
+    test(t => {
+      const timeline = createScrollTimelineWithOffsets(t, "80%", "20%");
+      const scroller = timeline.scrollSource;
+      const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+
+      scroller.scrollTop = test_case.scroll_percent * maxScroll;
+
+      assert_equals(
+        timeline.phase,
+        test_case.timeline_phase,
+        "timeline.phase"
+      );
+    }, "Timeline phase while start offset is greater than end offset and" +
+    " scroll offset is " + test_case.name);
+  }
+
+  // TODO(crbug.com/1060384): Spec is unclear in this case, revisit this when
+  // desired results have been established.
+  // Test cases where startScrollOffset is EQUAL TO endScrollOffset
+  const test_cases_start_offset_equal_to_end_offset = {
+    before_end: {
+      name: "before start",
+      scroll_percent: 0.3,
+      timeline_phase: "before"
+    },
+    at_end: {
+      name: "at both",
+      scroll_percent: 0.5,
+      timeline_phase: "after"
+    },
+    before_start: {
+      name: "after end",
+      scroll_percent: 0.7,
+      timeline_phase: "after"
+    }
+  }
+
+  for (const test_case_key in test_cases_start_offset_equal_to_end_offset){
+    const test_case =
+      test_cases_start_offset_equal_to_end_offset[test_case_key];
+    test(t => {
+      const timeline = createScrollTimelineWithOffsets(t, "50%", "50%");
+      const scroller = timeline.scrollSource;
+      const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+
+      scroller.scrollTop = test_case.scroll_percent * maxScroll;
+
+      assert_equals(
+        timeline.phase,
+        test_case.timeline_phase,
+        "timeline.phase"
+      );
+    }, "Timeline phase while start offset is equal to end offset and scroll" +
+    " offset is " + test_case.name);
+  }
+
+  test(t => {
+    const timeline = createScrollTimeline(t);
+    const scroller = timeline.scrollSource;
+    // Timeline should be inactive since layout hasn't updated yet
+    assert_equals(timeline.phase, "inactive");
+
+    // Accessing scroller.scrollHeight forces the scroller to update
+    scroller.scrollHeight;
+    assert_equals(timeline.phase, "active");
+
+    // Setting the scroller to display none should make the timeline inactive
+    scroller.style.display = "none"
+
+    // Force another layout
+    scroller.scrollHeight;
+
+    assert_equals(timeline.phase, "inactive");
+  }, 'Scroll timeline starts inactive, can transition to active, and then' +
+  ' back to inactive.');
+
 </script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/testcommon.js b/third_party/blink/web_tests/external/wpt/scroll-animations/testcommon.js
index ca2596885..ab22ff6 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/testcommon.js
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/testcommon.js
@@ -12,6 +12,16 @@
   });
 }
 
+function createScrollTimelineWithOffsets(test, startOffset, endOffset) {
+  return new ScrollTimeline({
+    scrollSource: createScroller(test),
+    orientation: "vertical",
+    startScrollOffset: startOffset,
+    endScrollOffset: endOffset,
+    timeRange: 1000
+  });
+}
+
 function createScrollLinkedAnimation(test, timeline) {
   if(timeline === undefined)
     timeline = createScrollTimeline(test);
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/slots-imperative-slot-api.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/shadow-dom/slots-imperative-slot-api.tentative-expected.txt
index 9dc356fbc..79d2a7d1 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/slots-imperative-slot-api.tentative-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/slots-imperative-slot-api.tentative-expected.txt
@@ -1,6 +1,5 @@
 This is a testharness.js-based test.
-FAIL attachShadow can take slotAssignment parameter. assert_throws_js: others should throw exception function "() => {
-    tTree.host3.attachShadow({ mode: 'open', slotAssignment: 'exceptional' })}" did not throw
+PASS attachShadow can take slotAssignment parameter.
 FAIL Imperative slot API throws exception when not slotAssignment != 'manual'. assert_throws_dom: function "() => { tTree.s1.assign([]); }" did not throw
 FAIL Imperative slot API throws exception when slotable parentNode != slot's host. assert_throws_dom: function "() => { tTree.s2.assign([tTree.c1]); }" did not throw
 FAIL Imperative slot API can assign nodes in manual slot assignment. assert_equals: expected null but got Element node <slot id="s1"></slot>
diff --git a/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt b/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
index a478015..988ffe8 100644
--- a/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
+++ b/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
@@ -1,3 +1,3 @@
-mypy==0.761
+mypy==0.770
 mypy-extensions==0.4.3
 typed-ast==1.4.1
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements_firefox.txt b/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements_firefox.txt
index ae7294081..d541a49 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements_firefox.txt
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements_firefox.txt
@@ -3,9 +3,9 @@
 mozdownload==1.26.0
 mozinstall==2.0.0
 mozleak==0.2
-moznetwork==0.27
+moznetwork==1.1.0
 mozprocess==1.0.0
-mozprofile==2.4.0
+mozprofile==2.5.0
 mozrunner==7.7.0
-mozversion==2.2.0
+mozversion==2.3.0
 psutil==5.7.0
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/idlharness.window-expected.txt
deleted file mode 100644
index 444ec5d..0000000
--- a/third_party/blink/web_tests/external/wpt/web-animations/idlharness.window-expected.txt
+++ /dev/null
@@ -1,147 +0,0 @@
-This is a testharness.js-based test.
-Found 143 tests; 141 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface Document: original interface defined
-PASS Partial interface Document: member names are unique
-PASS Partial interface mixin DocumentOrShadowRoot: original interface mixin defined
-PASS Partial interface mixin DocumentOrShadowRoot: member names are unique
-PASS Partial interface Document[2]: member names are unique
-PASS Partial interface Document[3]: member names are unique
-PASS Element includes Animatable: member names are unique
-PASS Element includes ParentNode: member names are unique
-PASS Element includes NonDocumentTypeChildNode: member names are unique
-PASS Element includes ChildNode: member names are unique
-PASS Element includes Slotable: member names are unique
-PASS Document includes NonElementParentNode: member names are unique
-PASS Document includes DocumentOrShadowRoot: member names are unique
-PASS Document includes ParentNode: member names are unique
-PASS Document includes XPathEvaluatorBase: member names are unique
-PASS Document includes GlobalEventHandlers: member names are unique
-PASS Document includes DocumentAndElementEventHandlers: member names are unique
-PASS DocumentFragment includes NonElementParentNode: member names are unique
-PASS DocumentFragment includes ParentNode: member names are unique
-PASS ShadowRoot includes DocumentOrShadowRoot: member names are unique
-PASS AnimationTimeline interface: existence and properties of interface object
-PASS AnimationTimeline interface object length
-PASS AnimationTimeline interface object name
-PASS AnimationTimeline interface: existence and properties of interface prototype object
-PASS AnimationTimeline interface: existence and properties of interface prototype object's "constructor" property
-PASS AnimationTimeline interface: existence and properties of interface prototype object's @@unscopables property
-PASS AnimationTimeline interface: attribute currentTime
-PASS DocumentTimeline interface: existence and properties of interface object
-PASS DocumentTimeline interface object length
-PASS DocumentTimeline interface object name
-PASS DocumentTimeline interface: existence and properties of interface prototype object
-PASS DocumentTimeline interface: existence and properties of interface prototype object's "constructor" property
-PASS DocumentTimeline interface: existence and properties of interface prototype object's @@unscopables property
-PASS DocumentTimeline must be primary interface of document.timeline
-PASS Stringification of document.timeline
-PASS AnimationTimeline interface: document.timeline must inherit property "currentTime" with the proper type
-PASS Animation interface: existence and properties of interface object
-PASS Animation interface object length
-PASS Animation interface object name
-PASS Animation interface: existence and properties of interface prototype object
-PASS Animation interface: existence and properties of interface prototype object's "constructor" property
-PASS Animation interface: existence and properties of interface prototype object's @@unscopables property
-PASS Animation interface: attribute id
-PASS Animation interface: attribute effect
-PASS Animation interface: attribute timeline
-PASS Animation interface: attribute startTime
-PASS Animation interface: attribute currentTime
-PASS Animation interface: attribute playbackRate
-PASS Animation interface: attribute playState
-PASS Animation interface: attribute replaceState
-PASS Animation interface: attribute pending
-PASS Animation interface: attribute ready
-PASS Animation interface: attribute finished
-PASS Animation interface: attribute onfinish
-PASS Animation interface: attribute oncancel
-PASS Animation interface: attribute onremove
-PASS Animation interface: operation cancel()
-PASS Animation interface: operation finish()
-PASS Animation interface: operation play()
-PASS Animation interface: operation pause()
-PASS Animation interface: operation updatePlaybackRate(double)
-PASS Animation interface: operation reverse()
-PASS Animation interface: operation persist()
-PASS Animation interface: operation commitStyles()
-PASS Animation must be primary interface of new Animation()
-PASS Stringification of new Animation()
-PASS Animation interface: new Animation() must inherit property "id" with the proper type
-PASS Animation interface: new Animation() must inherit property "effect" with the proper type
-PASS Animation interface: new Animation() must inherit property "timeline" with the proper type
-PASS Animation interface: new Animation() must inherit property "startTime" with the proper type
-PASS Animation interface: new Animation() must inherit property "currentTime" with the proper type
-PASS Animation interface: new Animation() must inherit property "playbackRate" with the proper type
-PASS Animation interface: new Animation() must inherit property "playState" with the proper type
-PASS Animation interface: new Animation() must inherit property "replaceState" with the proper type
-PASS Animation interface: new Animation() must inherit property "pending" with the proper type
-PASS Animation interface: new Animation() must inherit property "ready" with the proper type
-PASS Animation interface: new Animation() must inherit property "finished" with the proper type
-PASS Animation interface: new Animation() must inherit property "onfinish" with the proper type
-PASS Animation interface: new Animation() must inherit property "oncancel" with the proper type
-PASS Animation interface: new Animation() must inherit property "onremove" with the proper type
-PASS Animation interface: new Animation() must inherit property "cancel()" with the proper type
-PASS Animation interface: new Animation() must inherit property "finish()" with the proper type
-PASS Animation interface: new Animation() must inherit property "play()" with the proper type
-PASS Animation interface: new Animation() must inherit property "pause()" with the proper type
-PASS Animation interface: new Animation() must inherit property "updatePlaybackRate(double)" with the proper type
-PASS Animation interface: calling updatePlaybackRate(double) on new Animation() with too few arguments must throw TypeError
-PASS Animation interface: new Animation() must inherit property "reverse()" with the proper type
-PASS Animation interface: new Animation() must inherit property "persist()" with the proper type
-PASS Animation interface: new Animation() must inherit property "commitStyles()" with the proper type
-PASS AnimationEffect interface: existence and properties of interface object
-PASS AnimationEffect interface object length
-PASS AnimationEffect interface object name
-PASS AnimationEffect interface: existence and properties of interface prototype object
-PASS AnimationEffect interface: existence and properties of interface prototype object's "constructor" property
-PASS AnimationEffect interface: existence and properties of interface prototype object's @@unscopables property
-PASS AnimationEffect interface: operation getTiming()
-PASS AnimationEffect interface: operation getComputedTiming()
-PASS AnimationEffect interface: operation updateTiming(optional OptionalEffectTiming)
-PASS KeyframeEffect interface: existence and properties of interface object
-PASS KeyframeEffect interface object length
-PASS KeyframeEffect interface object name
-PASS KeyframeEffect interface: existence and properties of interface prototype object
-PASS KeyframeEffect interface: existence and properties of interface prototype object's "constructor" property
-PASS KeyframeEffect interface: existence and properties of interface prototype object's @@unscopables property
-PASS KeyframeEffect interface: attribute target
-PASS KeyframeEffect interface: attribute pseudoElement
-PASS KeyframeEffect interface: attribute composite
-PASS KeyframeEffect interface: operation getKeyframes()
-PASS KeyframeEffect interface: operation setKeyframes(object?)
-PASS KeyframeEffect must be primary interface of new KeyframeEffect(null, null)
-PASS Stringification of new KeyframeEffect(null, null)
-PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "target" with the proper type
-PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "pseudoElement" with the proper type
-PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "composite" with the proper type
-PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "getKeyframes()" with the proper type
-PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "setKeyframes(object?)" with the proper type
-PASS KeyframeEffect interface: calling setKeyframes(object?) on new KeyframeEffect(null, null) with too few arguments must throw TypeError
-PASS AnimationEffect interface: new KeyframeEffect(null, null) must inherit property "getTiming()" with the proper type
-PASS AnimationEffect interface: new KeyframeEffect(null, null) must inherit property "getComputedTiming()" with the proper type
-PASS AnimationEffect interface: new KeyframeEffect(null, null) must inherit property "updateTiming(optional OptionalEffectTiming)" with the proper type
-PASS AnimationEffect interface: calling updateTiming(optional OptionalEffectTiming) on new KeyframeEffect(null, null) with too few arguments must throw TypeError
-PASS AnimationPlaybackEvent interface: existence and properties of interface object
-PASS AnimationPlaybackEvent interface object length
-PASS AnimationPlaybackEvent interface object name
-PASS AnimationPlaybackEvent interface: existence and properties of interface prototype object
-PASS AnimationPlaybackEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS AnimationPlaybackEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS AnimationPlaybackEvent interface: attribute currentTime
-PASS AnimationPlaybackEvent interface: attribute timelineTime
-PASS AnimationPlaybackEvent must be primary interface of new AnimationPlaybackEvent("cancel")
-PASS Stringification of new AnimationPlaybackEvent("cancel")
-PASS AnimationPlaybackEvent interface: new AnimationPlaybackEvent("cancel") must inherit property "currentTime" with the proper type
-PASS AnimationPlaybackEvent interface: new AnimationPlaybackEvent("cancel") must inherit property "timelineTime" with the proper type
-PASS Document interface: attribute timeline
-PASS Document interface: operation getAnimations()
-PASS Document interface: document must inherit property "timeline" with the proper type
-PASS Document interface: document must inherit property "getAnimations()" with the proper type
-FAIL ShadowRoot interface: operation getAnimations() assert_own_property: interface prototype object missing non-static operation expected property "getAnimations" missing
-FAIL ShadowRoot interface: shadowRoot must inherit property "getAnimations()" with the proper type assert_inherits: property "getAnimations" not found in prototype chain
-PASS Element interface: operation animate(object?, optional (unrestricted double or KeyframeAnimationOptions))
-PASS Element interface: operation getAnimations(optional GetAnimationsOptions)
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations-expected.txt
deleted file mode 100644
index d198587..0000000
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-PASS Document.getAnimations() returns an empty sequence for non-animated content
-PASS Document.getAnimations() returns script-generated animations
-PASS Document.getAnimations() returns script-generated animations in the order they were created
-PASS Document.getAnimations() does not return a disconnected node
-PASS Document.getAnimations() does not return an animation with a null target
-PASS Document.getAnimations() returns animations on elements inside same-origin iframes
-PASS iframe.contentDocument.getAnimations() returns animations on elements inside same-origin Document
-FAIL ShadowRoot.getAnimations() return all animations in the shadow tree div.shadowRoot.getAnimations is not a function
-FAIL Document.getAnimations() does NOT return animations in shadow trees assert_array_equals: getAnimations() called on Document does not return animations from shadow trees lengths differ, expected array [] length 0, got [object "[object Animation]"] length 1
-PASS Document.getAnimations() triggers a style change event
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html
index 5d6952b..67addc0 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html
@@ -188,6 +188,20 @@
   );
 }, 'Document.getAnimations() does NOT return animations in shadow trees');
 
+test(t => {
+  const div = createDiv(t);
+  const shadow = div.attachShadow({ mode: 'open' });
+
+  div.animate(gKeyFrames, 100 * MS_PER_SEC)
+
+  assert_array_equals(
+    div.shadowRoot.getAnimations(),
+    [],
+    'getAnimations() called on ShadowRoot does not return animations from'
+    + ' Document'
+  );
+}, 'ShadowRoot.getAnimations() does NOT return animations in parent document');
+
 promise_test(async t => {
   const div = createDiv(t);
   const watcher = EventWatcher(t, div, 'transitionrun');
diff --git a/third_party/blink/web_tests/fast/css-generated-content/pseudo-element-cursor-expected.txt b/third_party/blink/web_tests/fast/css-generated-content/pseudo-element-cursor-expected.txt
index 446e176..88bb9781 100644
--- a/third_party/blink/web_tests/fast/css-generated-content/pseudo-element-cursor-expected.txt
+++ b/third_party/blink/web_tests/fast/css-generated-content/pseudo-element-cursor-expected.txt
@@ -2,7 +2,7 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-Cursor Info: type=Wait hotSpot=0,0
+Cursor Info: type=Wait
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/fast/dom/shadow/anchor-content-projected-expected.txt b/third_party/blink/web_tests/fast/dom/shadow/anchor-content-projected-expected.txt
index badd40b..fa5cec05 100644
--- a/third_party/blink/web_tests/fast/dom/shadow/anchor-content-projected-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/shadow/anchor-content-projected-expected.txt
@@ -3,7 +3,7 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 PASS window.location.hash is "#link-clicked"
 PASS successfullyParsed is true
 
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-change-after-image-load.html b/third_party/blink/web_tests/fast/events/mouse-cursor-change-after-image-load.html
index 031e576..6572e35 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-change-after-image-load.html
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-change-after-image-load.html
@@ -18,7 +18,7 @@
 cursorTest.step(function() {
     assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
 
-    assert_equals(internals.getCurrentCursorInfo(), "type=Pointer hotSpot=0,0");
+    assert_equals(internals.getCurrentCursorInfo(), "type=Pointer");
 
     var target = document.getElementById('target');
     var rect = target.getBoundingClientRect();
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-change-expected.txt b/third_party/blink/web_tests/fast/events/mouse-cursor-change-expected.txt
index d81f8bf..04b0c94 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-change-expected.txt
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-change-expected.txt
@@ -7,19 +7,19 @@
 
 
 Mouse move
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 Mouse down
-Cursor Info: type=Progress hotSpot=0,0
+Cursor Info: type=Progress
 
 
 Mouse hold down, move
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 Mouse up
-Cursor Info: type=Help hotSpot=0,0
+Cursor Info: type=Help
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-expected.txt b/third_party/blink/web_tests/fast/events/mouse-cursor-expected.txt
index 0ef9c7f..b9b116ea 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-expected.txt
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-expected.txt
@@ -7,47 +7,47 @@
 
 
 TEST CASE: Implicit default cursor
-Cursor Info: type=IBeam hotSpot=0,0
+Cursor Info: type=IBeam
 
 
 TEST CASE: Explicit default
-Cursor Info: type=Pointer hotSpot=0,0
+Cursor Info: type=Pointer
 
 
 TEST CASE: Explicit auto
-Cursor Info: type=IBeam hotSpot=0,0
+Cursor Info: type=IBeam
 
 
 TEST CASE: No cursor
-Cursor Info: type=None hotSpot=0,0
+Cursor Info: type=None
 
 
 TEST CASE: Pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: grab
-Cursor Info: type=Grab hotSpot=0,0
+Cursor Info: type=Grab
 
 
 TEST CASE: grabbing
-Cursor Info: type=Grabbing hotSpot=0,0
+Cursor Info: type=Grabbing
 
 
 TEST CASE: -webkit-grab
-Cursor Info: type=Grab hotSpot=0,0
+Cursor Info: type=Grab
 
 
 TEST CASE: -webkit-grabbing
-Cursor Info: type=Grabbing hotSpot=0,0
+Cursor Info: type=Grabbing
 
 
 TEST CASE: Existing 25x25 image
-Cursor Info: type=IBeam hotSpot=0,0
+Cursor Info: type=IBeam
 
 
 TEST CASE: Invalid URL with fallback to pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: Invalid with fallback to 25x25 image
@@ -83,31 +83,31 @@
 
 
 TEST CASE: Over large image with fallback to pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: Local element reference
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: Multiple invalid cursors with fallback to pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: Nonexistent local element reference with fallback to pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: A link with default cursor
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: Link with default cursor overriding wait
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: Wait cursor which should not be affected by unknown cursor rule
-Cursor Info: type=Wait hotSpot=0,0
+Cursor Info: type=Wait
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-image-set-expected.txt b/third_party/blink/web_tests/fast/events/mouse-cursor-image-set-expected.txt
index 5b852a1..efac1960 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-image-set-expected.txt
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-image-set-expected.txt
@@ -25,11 +25,11 @@
 
 
 TEST CASE: Invalid tiny scale with fallback to pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: Over-large image with fallback to pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: 200x200 image at 4x (not over-large in UI pixels)
@@ -71,11 +71,11 @@
 
 
 TEST CASE: Invalid tiny scale with fallback to pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: Over-large image with fallback to pointer
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 
 
 TEST CASE: 200x200 image at 4x (not over-large in UI pixels)
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-multiframecur-expected.txt b/third_party/blink/web_tests/fast/events/mouse-cursor-multiframecur-expected.txt
index 0e64289a..f6cbe55 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-multiframecur-expected.txt
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-multiframecur-expected.txt
@@ -7,7 +7,7 @@
 
 
 TEST CASE: Implicit default cursor
-Cursor Info: type=IBeam hotSpot=0,0
+Cursor Info: type=IBeam
 
 
 TEST CASE: CUR file with 3 frames, largest of which (2nd frame) is 20x12 with hotspot at (18,11).
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-no-mousemove-expected.txt b/third_party/blink/web_tests/fast/events/mouse-cursor-no-mousemove-expected.txt
index 42d1dc5..9c2b154d 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-no-mousemove-expected.txt
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-no-mousemove-expected.txt
@@ -7,8 +7,8 @@
 
 
 TEST CASE: Mouse idle, change cursor should not fire mousemove event
-Cursor Info: type=Pointer hotSpot=0,0
-Cursor Info: type=Help hotSpot=0,0
+Cursor Info: type=Pointer
+Cursor Info: type=Help
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-style-change-iframe-expected.txt b/third_party/blink/web_tests/fast/events/mouse-cursor-style-change-iframe-expected.txt
index 4621ac5..6a41d9e 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-style-change-iframe-expected.txt
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-style-change-iframe-expected.txt
@@ -3,18 +3,18 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 Mouse moved to cursor changing div
-PASS internals.getCurrentCursorInfo() is "type=Hand hotSpot=0,0"
+PASS internals.getCurrentCursorInfo() is "type=Hand"
 Changing cursor style
-PASS internals.getCurrentCursorInfo() is "type=Wait hotSpot=0,0"
+PASS internals.getCurrentCursorInfo() is "type=Wait"
 
 Now move mouse onto iframe above cursor changing div
 PASS document.elementFromPoint(100, y) is frame
 PASS document.elementsFromPoint(100, y).indexOf(container) > 0 is true
 PASS internals.cursorUpdatePending is false
-PASS internals.getCurrentCursorInfo() is "type=IBeam hotSpot=0,0"
+PASS internals.getCurrentCursorInfo() is "type=IBeam"
 
 Changing cursor style of the background should not affect the cursor as it sits over the iframe
-PASS internals.getCurrentCursorInfo() is "type=IBeam hotSpot=0,0"
+PASS internals.getCurrentCursorInfo() is "type=IBeam"
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-style-change-iframe.html b/third_party/blink/web_tests/fast/events/mouse-cursor-style-change-iframe.html
index 690d401..d172ef47 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-style-change-iframe.html
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-style-change-iframe.html
@@ -46,11 +46,11 @@
         frame.onload = function() {
             debug('Mouse moved to cursor changing div');
             eventSender.mouseMoveTo(100, container.offsetTop + 5);
-            shouldBeEqualToString('internals.getCurrentCursorInfo()', 'type=Hand hotSpot=0,0');
+            shouldBeEqualToString('internals.getCurrentCursorInfo()', 'type=Hand');
 
             debug('Changing cursor style');
             container.classList.add('wait');
-            expectCursorUpdate('type=Wait hotSpot=0,0', function() {
+            expectCursorUpdate('type=Wait', function() {
                 debug('');
 
                 debug('Now move mouse onto iframe above cursor changing div');
@@ -59,12 +59,12 @@
                 shouldBe('document.elementFromPoint(100, y)', 'frame');
                 shouldBeTrue('document.elementsFromPoint(100, y).indexOf(container) > 0');
                 shouldBeFalse('internals.cursorUpdatePending');
-                shouldBeEqualToString('internals.getCurrentCursorInfo()', 'type=IBeam hotSpot=0,0');
+                shouldBeEqualToString('internals.getCurrentCursorInfo()', 'type=IBeam');
                 debug('');
 
                 debug('Changing cursor style of the background should not affect the cursor as it sits over the iframe');
                 container.classList.remove('wait');
-                expectCursorUpdate('type=IBeam hotSpot=0,0', function() {
+                expectCursorUpdate('type=IBeam', function() {
                     finishJSTest();
                 }); 
             });
diff --git a/third_party/blink/web_tests/fast/events/mousemove-to-resizer-changes-cursor-expected.txt b/third_party/blink/web_tests/fast/events/mousemove-to-resizer-changes-cursor-expected.txt
index 6b99460..0ad1f568 100644
--- a/third_party/blink/web_tests/fast/events/mousemove-to-resizer-changes-cursor-expected.txt
+++ b/third_party/blink/web_tests/fast/events/mousemove-to-resizer-changes-cursor-expected.txt
@@ -1,7 +1,7 @@
 This tests that hovering over a TEXTAREA resizer turns the mouse cursor into the SouthEastResize cursor.
 
 
-Inside TEXTAREA: type=IBeam hotSpot=0,0
-Over dragger: type=SouthEastResize hotSpot=0,0
-Over BODY: type=Hand hotSpot=0,0
-Over dragger: type=SouthEastResize hotSpot=0,0
+Inside TEXTAREA: type=IBeam
+Over dragger: type=SouthEastResize
+Over BODY: type=Hand
+Over dragger: type=SouthEastResize
diff --git a/third_party/blink/web_tests/fast/events/mousemove-to-scrollbar-changes-cursor-expected.txt b/third_party/blink/web_tests/fast/events/mousemove-to-scrollbar-changes-cursor-expected.txt
index bffc478..b553fbf 100644
--- a/third_party/blink/web_tests/fast/events/mousemove-to-scrollbar-changes-cursor-expected.txt
+++ b/third_party/blink/web_tests/fast/events/mousemove-to-scrollbar-changes-cursor-expected.txt
@@ -1,4 +1,4 @@
 This tests that hovering over a scrollbar resets the mouse cursor to the default pointer.
 
-Hovered pointer: type=Hand hotSpot=0,0
-Scrollbar pointer: type=Pointer hotSpot=0,0
+Hovered pointer: type=Hand
+Scrollbar pointer: type=Pointer
diff --git a/third_party/blink/web_tests/fast/events/resources/middleClickAutoscroll.js b/third_party/blink/web_tests/fast/events/resources/middleClickAutoscroll.js
index 113200a..90b8ceab 100644
--- a/third_party/blink/web_tests/fast/events/resources/middleClickAutoscroll.js
+++ b/third_party/blink/web_tests/fast/events/resources/middleClickAutoscroll.js
@@ -56,8 +56,7 @@
     // Wait for the cursor shape to go back to normal.
     await waitFor(() => {
       var cursorInfo = internals.getCurrentCursorInfo();
-      return cursorInfo == "type=Pointer hotSpot=0,0" ||
-          cursorInfo == "type=IBeam hotSpot=0,0";
+      return cursorInfo == "type=Pointer" || cursorInfo == "type=IBeam";
     });
 
     finishTest();
diff --git a/third_party/blink/web_tests/fast/events/update-mouse-cursor-after-layout-change.html b/third_party/blink/web_tests/fast/events/update-mouse-cursor-after-layout-change.html
index 77da1773..90e40a8 100644
--- a/third_party/blink/web_tests/fast/events/update-mouse-cursor-after-layout-change.html
+++ b/third_party/blink/web_tests/fast/events/update-mouse-cursor-after-layout-change.html
@@ -42,9 +42,9 @@
     var target = document.getElementById('target');
     var rect = target.getBoundingClientRect();
     await mouseMoveTo(rect.left + 3, rect.top + 3);
-    assert_equals(internals.getCurrentCursorInfo(), 'type=IBeam hotSpot=0,0', 'wait for move to target');
+    assert_equals(internals.getCurrentCursorInfo(), 'type=IBeam', 'wait for move to target');
     await mouseClickOn(rect.left + 3, rect.top + 3);
-    assert_equals(internals.getCurrentCursorInfo(), 'type=Wait hotSpot=0,0', 'wait for mouse cursor change');
+    assert_equals(internals.getCurrentCursorInfo(), 'type=Wait', 'wait for mouse cursor change');
   }, 'Tests that there is mouse cursor update when the element underneath the mouse cursor is changed.');
 }
-</script>
\ No newline at end of file
+</script>
diff --git a/third_party/blink/web_tests/fast/inline/middle-continuation-inherits-visibility-from-inline-parent.html b/third_party/blink/web_tests/fast/inline/middle-continuation-inherits-visibility-from-inline-parent.html
index 461478a..123f8ac 100644
--- a/third_party/blink/web_tests/fast/inline/middle-continuation-inherits-visibility-from-inline-parent.html
+++ b/third_party/blink/web_tests/fast/inline/middle-continuation-inherits-visibility-from-inline-parent.html
@@ -15,7 +15,7 @@
     eventSender.dragMode = false;
     eventSender.mouseMoveTo(10, 10);
     var originalInfo = internals.getCurrentCursorInfo();
-    assert_equals(originalInfo, "type=Pointer hotSpot=0,0");
+    assert_equals(originalInfo, "type=Pointer");
   }
 }, 'crbug.com/706324: Middle continuations should inherit the style of their inline parent, so when the cursor is placed over the anonymous block it should remain a pointer.');
 </script>
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-insertable-streams.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-insertable-streams.html
new file mode 100644
index 0000000..dc170e5e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-insertable-streams.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>RTCPeerConnection.getRemoteStreams</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../external/wpt/webrtc/RTCPeerConnection-helper.js">
+</script></head>
+<body>
+<script>
+// TODO(crbug.com/1058021): Move this test to external/wpt/webrtc/ once the
+// Insertable Streams spec is mature enough.
+
+function areArrayBuffersEqual(buffer1, buffer2)
+{
+    if (buffer1.byteLength != buffer2.byteLength)
+      return false;
+    let array1 = new Int8Array(buffer1);
+    var array2 = new Int8Array(buffer2);
+    for (let i = 0 ; i < buffer1.byteLength ; ++i) {
+      if (array1[i] != array2[i])
+        return false;
+    }
+    return true;
+}
+
+promise_test(async t => {
+  const caller = new RTCPeerConnection({forceEncodedVideoInsertableStreams:true});
+  t.add_cleanup(() => caller.close());
+  const callee = new RTCPeerConnection({forceEncodedVideoInsertableStreams:true});
+  t.add_cleanup(() => callee.close());
+
+  const stream = await navigator.mediaDevices.getUserMedia({video:true});
+  const videoTrack = stream.getVideoTracks()[0];
+  t.add_cleanup(() => videoTrack.stop());
+
+  const videoSender = caller.addTrack(videoTrack)
+  const senderStreams = videoSender.createEncodedVideoStreams();
+  const senderReader = senderStreams.readableStream.getReader();
+  const senderWriter = senderStreams.writableStream.getWriter();
+
+  const frames = [];
+  const numFramesPassthrough = 5;
+  const numFramesReplaceData = 5;
+  const numFramesModifyData = 5;
+  const numFramesToSend = numFramesPassthrough + numFramesReplaceData + numFramesModifyData;
+
+  const ontrackPromise = new Promise(resolve => {
+    callee.ontrack = t.step_func(() => {
+      const receivers = callee.getReceivers();
+      let videoReceiver = null;
+      for (const r of receivers) {
+        if (r.track.kind == 'video')
+          videoReceiver = r;
+      }
+      assert_true(videoReceiver !== undefined);
+
+      const receiverStreams =
+          videoReceiver.createEncodedVideoStreams();
+      const receiverReader = receiverStreams.readableStream.getReader();
+      const receiverWriter = receiverStreams.writableStream.getWriter();
+
+      // The WebRTC stack may occassionally deliver duplicate frames on the
+      // receiver side, therefore start twice as many read attempts as sent
+      // frames to account for this.
+      const maxFramesToReceive = numFramesToSend * 2;
+      let numVerifiedFrames = 0;
+      for (let i = 0; i < maxFramesToReceive; i++) {
+        receiverReader.read().then(t.step_func(result => {
+          if (frames[numVerifiedFrames] &&
+              areArrayBuffersEqual(result.value.data, frames[numVerifiedFrames])) {
+            numVerifiedFrames++;
+          } else if (frames[numVerifiedFrames-1] &&
+                     areArrayBuffersEqual(result.value.data, frames[numVerifiedFrames-1])) {
+            // Duplicate frame. It can happen occassionally. Ignore.
+          } else {
+            // Receiving unexpected (nonduplicate) frames is an indication that
+            // frames are not passed correctly between sender and receiver.
+            assert_unreached("Incorrect frame received");
+          }
+
+          if (numVerifiedFrames == numFramesToSend)
+            resolve();
+        }));
+      }
+    });
+  });
+
+  exchangeIceCandidates(caller, callee);
+  await doSignalingHandshake(caller, callee);
+  // Pass frames as they come from the encoder.
+  for (let i = 0; i < numFramesPassthrough; i++) {
+    const result = await senderReader.read()
+    frames.push(result.value.data);
+    senderWriter.write(result.value);
+  }
+
+  // Replace frame data with arbitrary buffers.
+  for (let i = 0; i < numFramesReplaceData; i++) {
+    const result = await senderReader.read()
+
+    const buffer = new ArrayBuffer(100);
+    const int8View = new Int8Array(buffer);
+    int8View.fill(i);
+
+    result.value.data = buffer;
+    frames.push(result.value.data);
+    senderWriter.write(result.value);
+  }
+
+  // Modify frame data.
+  for (let i = 0; i < numFramesReplaceData; i++) {
+    const result = await senderReader.read()
+    const int8View = new Int8Array(result.value.data);
+    int8View.fill(i);
+
+    frames.push(result.value.data);
+    senderWriter.write(result.value);
+  }
+}, 'Frames flow correctly using insertable streams');
+
+promise_test(async t => {
+  const caller = new RTCPeerConnection();
+  t.add_cleanup(() => caller.close());
+  const callee = new RTCPeerConnection();
+  t.add_cleanup(() => callee.close());
+  const stream = await navigator.mediaDevices.getUserMedia({video:true});
+  const videoTrack = stream.getVideoTracks()[0];
+  t.add_cleanup(() => videoTrack.stop());
+
+  await doSignalingHandshake(caller, callee);
+
+  const videoSender = caller.addTrack(videoTrack);
+  assert_throws_dom("InvalidStateError", () => videoSender.createEncodedVideoStreams());
+}, 'RTCRtpSender.createEncodedVideoStream() throws if not requested in PC configuration');
+
+promise_test(async t => {
+  const caller = new RTCPeerConnection();
+  t.add_cleanup(() => caller.close());
+  const callee = new RTCPeerConnection();
+  t.add_cleanup(() => callee.close());
+  const stream = await navigator.mediaDevices.getUserMedia({video:true});
+  const videoTrack = stream.getVideoTracks()[0];
+  t.add_cleanup(() => videoTrack.stop());
+
+  const videoSender = caller.addTrack(videoTrack);
+  const ontrackPromise = new Promise(resolve => {
+    callee.ontrack = t.step_func(() => {
+        let receivers = callee.getReceivers();
+        let videoReceiver = null;
+        for (const r of receivers) {
+          if (r.track.kind == 'video')
+            videoReceiver = r;
+        }
+        assert_true(videoReceiver !== undefined);
+        assert_throws_dom("InvalidStateError", () => videoReceiver.createEncodedVideoStreams());
+        resolve();
+    });
+  });
+
+  await doSignalingHandshake(caller, callee);
+  return ontrackPromise;
+}, 'RTCRtpReceiver.createEncodedVideoStream() throws if not requested in PC configuration');
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/replaced/image-map-cursor-expected.txt b/third_party/blink/web_tests/fast/replaced/image-map-cursor-expected.txt
index 7bf223fa..c47f867 100644
--- a/third_party/blink/web_tests/fast/replaced/image-map-cursor-expected.txt
+++ b/third_party/blink/web_tests/fast/replaced/image-map-cursor-expected.txt
@@ -1,4 +1,4 @@
-Cursor Info: type=Hand hotSpot=0,0
+Cursor Info: type=Hand
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-restored-breakpoint-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-restored-breakpoint-expected.txt
deleted file mode 100644
index 1d8c5c4..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-restored-breakpoint-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-Tests breakpoint is restored.
-Set different breakpoints and dump them
-breakpoint at 5 disabled
-breakpoint at 9
-breakpoint at 10 conditional
-Reload page and add script again and dump breakpoints
-Page reloaded.
-breakpoint at 5 disabled
-breakpoint at 9
-breakpoint at 10 conditional
-Added two more iframes with script with the same url
-Show uiSourceCode and dump breakpoints
-breakpoint at 9
-breakpoint at 10 conditional
-Show uiSourceCode and dump breakpoints
-breakpoint at 9
-breakpoint at 10 conditional
-Show uiSourceCode and dump breakpoints
-breakpoint at 5 disabled
-breakpoint at 9
-breakpoint at 10 conditional
-Reload page and add script again and dump breakpoints
-Page reloaded.
-breakpoint at 5 disabled
-breakpoint at 9
-breakpoint at 10 conditional
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-restored-breakpoint.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-restored-breakpoint.js
deleted file mode 100644
index fbcde86a5..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-restored-breakpoint.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult('Tests breakpoint is restored.');
-  await TestRunner.loadModule('sources_test_runner');
-  await TestRunner.showPanel('sources');
-
-  await TestRunner.addScriptTag('resources/a.js');
-
-  // Pairs of line number plus breakpoint decoration counts.
-  // We expect line 5, 9 and 10 to have decoration each.
-  const expectedDecorations = [[5, 1], [9, 1], [10, 1]];
-
-  let originalSourceFrame = await SourcesTestRunner.showScriptSourcePromise('a.js');
-  TestRunner.addResult('Set different breakpoints and dump them');
-  await SourcesTestRunner.runActionAndWaitForExactBreakpointDecorations(originalSourceFrame, expectedDecorations, async () => {
-    await SourcesTestRunner.toggleBreakpoint(originalSourceFrame, 9, false);
-    await SourcesTestRunner.createNewBreakpoint(originalSourceFrame, 10, 'a === 3', true);
-    await SourcesTestRunner.createNewBreakpoint(originalSourceFrame, 5, '', false);
-  });
-
-  TestRunner.addResult('Reload page and add script again and dump breakpoints');
-  await TestRunner.reloadPagePromise();
-  await TestRunner.addScriptTag(TestRunner.url('resources/a.js'));
-  let sourceFrameAfterReload = await SourcesTestRunner.showScriptSourcePromise('a.js');
-  await SourcesTestRunner.runActionAndWaitForExactBreakpointDecorations(
-      sourceFrameAfterReload, expectedDecorations, () => {}, true);
-
-  // TODO(kozyatinskiy): as soon as we have script with the same url in different frames
-  // everything looks compeltely broken, we should fix it.
-  TestRunner.addResult('Added two more iframes with script with the same url');
-  TestRunner.addIframe(TestRunner.url('resources/frame-with-script.html'));
-  TestRunner.addIframe(TestRunner.url('resources/frame-with-script.html'));
-  const uiSourceCodes = await waitForNScriptSources('a.js', 3);
-
-  // The disabled breakpoint at line 5 is currently not included in the iframes that are added.
-  const expectedDecorationsArray = [[[9, 1], [10, 1]], [[9, 1], [10, 1]], expectedDecorations];
-  let index = 0;
-  for (const uiSourceCode of uiSourceCodes) {
-    TestRunner.addResult('Show uiSourceCode and dump breakpoints');
-    const sourceFrame = await SourcesTestRunner.showUISourceCodePromise(uiSourceCode);
-    await SourcesTestRunner.runActionAndWaitForExactBreakpointDecorations(
-        sourceFrame, expectedDecorationsArray[index++], () => {}, true);
-  }
-
-  TestRunner.addResult('Reload page and add script again and dump breakpoints');
-  await TestRunner.reloadPagePromise();
-  await TestRunner.addScriptTag(TestRunner.url('resources/a.js'));
-  sourceFrameAfterReload = await SourcesTestRunner.showScriptSourcePromise('a.js');
-  await SourcesTestRunner.runActionAndWaitForExactBreakpointDecorations(
-      sourceFrameAfterReload, expectedDecorations, () => {}, true);
-
-  TestRunner.completeTest();
-
-  async function waitForNScriptSources(scriptName, N) {
-    while (true) {
-      let uiSourceCodes = UI.panels.sources._workspace.uiSourceCodes();
-      uiSourceCodes = uiSourceCodes.filter(uiSourceCode => uiSourceCode.project().type() !== Workspace.projectTypes.Service && uiSourceCode.name() === scriptName);
-      if (uiSourceCodes.length === N)
-        return uiSourceCodes;
-      await TestRunner.addSnifferPromise(Sources.SourcesView.prototype, '_addUISourceCode');
-    }
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/misc/lock-renderer-for-middle-click-autoscroll.html b/third_party/blink/web_tests/http/tests/misc/lock-renderer-for-middle-click-autoscroll.html
index b260ae81..bf9f174c 100644
--- a/third_party/blink/web_tests/http/tests/misc/lock-renderer-for-middle-click-autoscroll.html
+++ b/third_party/blink/web_tests/http/tests/misc/lock-renderer-for-middle-click-autoscroll.html
@@ -43,8 +43,7 @@
        // Wait for the cursor shape to go back to normal.
        await waitFor(() => {
          var cursorInfo = internals.getCurrentCursorInfo();
-         return cursorInfo === "type=Pointer hotSpot=0,0" ||
-                cursorInfo === "type=IBeam hotSpot=0,0";
+         return cursorInfo === "type=Pointer" || cursorInfo === "type=IBeam";
        });
 
        t.step(() => { assert_greater_than(container.scrollTop, 0 ,
@@ -58,4 +57,4 @@
 <iframe id="target-iframe1"
   src="http://localhost:8080/misc/resources/cross-origin-subframe-for-scrolling.html">
 </iframe>
-</div>
\ No newline at end of file
+</div>
diff --git a/third_party/blink/web_tests/http/tests/security/isolatedWorld/bypass-main-world-trusted-types.html b/third_party/blink/web_tests/http/tests/security/isolatedWorld/bypass-main-world-trusted-types.html
index d19991a1..efebce2a 100644
--- a/third_party/blink/web_tests/http/tests/security/isolatedWorld/bypass-main-world-trusted-types.html
+++ b/third_party/blink/web_tests/http/tests/security/isolatedWorld/bypass-main-world-trusted-types.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
 <head>
-<meta http-equiv="Content-Security-Policy" content="trusted-types *; require-trusted-types-for 'script';">
+<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';">
 <script>
     if (window.testRunner) {
         testRunner.dumpAsText();
diff --git a/third_party/blink/web_tests/shadow-dom/crashes/imperative-api.html b/third_party/blink/web_tests/shadow-dom/crashes/imperative-api.html
index 3b44667..f5445793 100644
--- a/third_party/blink/web_tests/shadow-dom/crashes/imperative-api.html
+++ b/third_party/blink/web_tests/shadow-dom/crashes/imperative-api.html
@@ -12,7 +12,7 @@
 <script>
 const host = document.querySelector("#host");
 const child1 = document.querySelector("#child1");
-const shadow_root = host.attachShadow({ mode: "open", slotting: "manual" });
+const shadow_root = host.attachShadow({ mode: "open", slotAssignment: "manual" });
 const slot1 = document.createElement("slot");
 const slot2 = document.createElement("slot");
 shadow_root.appendChild(slot1);
diff --git a/third_party/blink/web_tests/shadow-dom/imperative-api.html b/third_party/blink/web_tests/shadow-dom/imperative-api.html
index 5b900fd..44f15d69 100644
--- a/third_party/blink/web_tests/shadow-dom/imperative-api.html
+++ b/third_party/blink/web_tests/shadow-dom/imperative-api.html
@@ -6,27 +6,11 @@
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
 <script src="resources/shadow-dom.js"></script>
-<div id="test1">
-  <div id="host"></div>
-  <div id="host1"></div>
-  <div id="host2"></div>
-</div>
-<script>
-test(() => {
-  let n = createTestTree(test1);
-  assert_not_equals(n.host.attachShadow({ mode: 'open', slotting: 'manual' }),
-                    null, 'slotting manual should work');
-  assert_not_equals(n.host1.attachShadow({ mode: 'open', slotting: 'auto' }),
-                    null, 'slotting auto should work');
-  assert_throws_js(TypeError, () => {
-  n.host2.attachShadow({ mode: 'open', slotting: 'exceptional' })},
-                'others should throw exception');
-}, 'attachShadow can take slotting parameter');
 
 </script>
 <div id="test2">
   <div id="host">
-    <template id="shadow_root" data-mode="open" data-slotting="manual">
+    <template id="shadow_root" data-mode="open" data-slot-assignment="manual">
       <slot id="s1"></slot>
       <slot id="s2"></slot>
       <slot id="s3"></slot>
@@ -37,7 +21,7 @@
   </div>
   <div id="c4"></div>
   <div id="host4">
-    <template id="shadow_root1" data-mode="open" data-slotting="manual">
+    <template id="shadow_root1" data-mode="open" data-slot-assignment="manual">
       <slot id="s5" name="s5"></slot>
     </template>
   </div>
diff --git a/third_party/blink/web_tests/shadow-dom/imperative-apis/custom-detail-summary.js b/third_party/blink/web_tests/shadow-dom/imperative-apis/custom-detail-summary.js
index daaee28..835b399 100644
--- a/third_party/blink/web_tests/shadow-dom/imperative-apis/custom-detail-summary.js
+++ b/third_party/blink/web_tests/shadow-dom/imperative-apis/custom-detail-summary.js
@@ -11,7 +11,7 @@
 customElements.define("my-detail", class extends HTMLElement {
   constructor() {
     super();
-    this.attachShadow({ mode: "open", slotting: "manual" });
+    this.attachShadow({ mode: "open", slotAssignment: "manual" });
   }
   connectedCallback() {
     const target = this;
diff --git a/third_party/blink/web_tests/shadow-dom/resources/shadow-dom.js b/third_party/blink/web_tests/shadow-dom/resources/shadow-dom.js
index c9f2545..7828095 100644
--- a/third_party/blink/web_tests/shadow-dom/resources/shadow-dom.js
+++ b/third_party/blink/web_tests/shadow-dom/resources/shadow-dom.js
@@ -76,10 +76,10 @@
     if (template.getAttribute('data-mode') === 'v0') {
       // For legacy Shadow DOM
       shadowRoot = parent.createShadowRoot();
-    } else if (template.getAttribute('data-slotting') === 'manual') {
+    } else if (template.getAttribute('data-slot-assignment') === 'manual') {
        shadowRoot =
           parent.attachShadow({mode: template.getAttribute('data-mode'),
-                               slotting: 'manual'});
+                               slotAssignment: 'manual'});
     } else {
       shadowRoot =
           parent.attachShadow({mode: template.getAttribute('data-mode')});
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index cf0eb14..e26fe0b 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -8030,10 +8030,12 @@
     getter mode
     getter pictureInPictureElement
     getter pointerLockElement
+    getter slotAssignment
     getter styleSheets
     method constructor
     method elementFromPoint
     method elementsFromPoint
+    method getAnimations
     method getSelection
     setter adoptedStyleSheets
     setter fullscreenElement
diff --git a/third_party/blink/web_tests/wpt_internal/portals/portals-cursor.html b/third_party/blink/web_tests/wpt_internal/portals/portals-cursor.html
index 27bc173f..2ee7fa6 100644
--- a/third_party/blink/web_tests/wpt_internal/portals/portals-cursor.html
+++ b/third_party/blink/web_tests/wpt_internal/portals/portals-cursor.html
@@ -14,7 +14,7 @@
           .pointerMove(20, 20, {origin: portal});
       actions.send();
       await new Promise(r => portal.onpointermove = r);
-      assert_equals('type=Hand hotSpot=0,0', internals.getCurrentCursorInfo());
+      assert_equals('type=Hand', internals.getCurrentCursorInfo());
     }, 'test that cursor is updated over portal');
   </script>
 </body>
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index f791828b..0b1fae7 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -220,7 +220,6 @@
     <classpathentry kind="src" path="content/shell/android/browsertests_apk/src"/>
     <classpathentry kind="src" path="content/shell/android/java/src"/>
     <classpathentry kind="src" path="content/shell/android/javatests/src"/>
-    <classpathentry kind="src" path="content/shell/android/linker_test_apk/src"/>
     <classpathentry kind="src" path="content/shell/android/shell_apk/src"/>
     <classpathentry kind="src" path="device/bluetooth/android/java/src"/>
     <classpathentry kind="src" path="device/bluetooth/test/android/java/src"/>
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index d206b50b..ff095c6 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -475,8 +475,6 @@
       '-DCOMPILER_RT_USE_LIBCXX=NO',
       # Don't run Go bindings tests; PGO makes them confused.
       '-DLLVM_INCLUDE_GO_TESTS=OFF',
-      # TODO(b/148147812) Goma client doesn't handle in-process cc1.
-      '-DCLANG_SPAWN_CC1=ON',
   ]
 
   if args.gcc_toolchain:
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 5e0643a..f1ea28d 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -41,7 +41,7 @@
 # Reverting problematic clang rolls is safe, though.
 CLANG_REVISION = '9284abd0040afecfd619dbcf1b244a8b533291c9'
 CLANG_SVN_REVISION = 'n344329'
-CLANG_SUB_REVISION = 2
+CLANG_SUB_REVISION = 4
 
 PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8],
                                 CLANG_SUB_REVISION)
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 40cf6a0..a5e4c8f 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -22560,7 +22560,16 @@
   <owner>chrome-signin-team@google.com</owner>
   <description>
     The user selected to customize sync from
-    |chrome://settings/syncSetup/advanced|.
+    chrome://settings/syncSetup/advanced.
+  </description>
+</action>
+
+<action name="Sync_NavigateToSyncAdvancedPage">
+  <owner>msalama@chromium.org</owner>
+  <owner>chrome-signin-team@google.com</owner>
+  <description>
+    The user has navigated to the sync advanced settings page
+    chrome://settings/syncSetup/advanced.
   </description>
 </action>
 
@@ -22568,8 +22577,8 @@
   <owner>msalama@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <description>
-    The user has clicked on the history usage row in
-    |chrome://settings/syncSetup| to open Activity controls page.
+    The user has clicked on the history usage row in chrome://settings/syncSetup
+    to open Activity controls page.
   </description>
 </action>
 
@@ -22577,8 +22586,7 @@
   <owner>msalama@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <description>
-    The user selected sync everything from
-    |chrome://settings/syncSetup/advanced|.
+    The user selected sync everything from chrome://settings/syncSetup/advanced.
   </description>
 </action>
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d1d707e..8a18a48 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7562,6 +7562,11 @@
   <int value="1" label="Unresponsive"/>
 </enum>
 
+<enum name="BooleanUpdateReceived">
+  <int value="0" label="No update"/>
+  <int value="1" label="Received update"/>
+</enum>
+
 <enum name="BooleanUpload">
   <int value="0" label="Download"/>
   <int value="1" label="Upload"/>
@@ -8838,6 +8843,10 @@
   <int value="32" label="AnnotateStrokeDeviceMouse"/>
   <int value="33" label="AnnotateStrokeDevicePenFirst"/>
   <int value="34" label="AnnotateStrokeDevicePen"/>
+  <int value="35" label="TwoUpViewEnableFirst"/>
+  <int value="36" label="TwoUpViewEnable"/>
+  <int value="37" label="TwoUpViewDisableFirst"/>
+  <int value="38" label="TwoUpViewDisable"/>
 </enum>
 
 <enum name="ChromePDFViewerAnnotationType">
@@ -26780,6 +26789,8 @@
   <int value="3184" label="QuicTransport"/>
   <int value="3185" label="QuicTransportStreamApis"/>
   <int value="3186" label="QuicTransportDatagramApis"/>
+  <int value="3187" label="V8Document_GetAnimations_Method"/>
+  <int value="3188" label="V8ShadowRoot_GetAnimations_Method"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8c7c42b..17d7522 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -8800,16 +8800,13 @@
 <!-- Name completed by histogram suffixes
      name="HotseatTransitionType" -->
 
-<!-- Name completed by histogram suffixes
-     name="HotseatWidgetElement" -->
-
   <owner>anasalazar@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
     Tracks the animation smoothness for the bounds animation of the hotseat
-    widget's elements during transitions of the hotseat to shown, extended, and
-    hidden hotseat states. Check Ash.HotseatTransition.AnimationSmoothness for
-    smoothness of the shelf's animating background.
+    widget during transitions of the hotseat to shown, extended, and hidden
+    hotseat states. Check Ash.HotseatTransition.AnimationSmoothness for
+    smoothness of the animating background.
   </summary>
 </histogram>
 
@@ -44023,6 +44020,23 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.DeviceRemoteCommandInvalidations"
+    enum="EnterprisePolicyInvalidations" expires_after="2021-03-01">
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting device remote commands invalidations received with and
+    without payloads. Invalidations indicate that there is a remote command to
+    execute. Payloads provide context about the remote commands update, but may
+    be absent if dropped by the invalidation service.
+
+    Metric is similar to Enterprise.DevicePolicyInvalidations2. Device local
+    account scope does not exist for remote commands so there is no metric for
+    remote commands similar to
+    Enterprise.DeviceLocalAccountPolicyInvalidations2.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.DeviceSettings.MissingPolicyMitigated"
     units="BooleanSuccess" expires_after="2020-08-01">
   <owner>poromov@chromium.org</owner>
@@ -44820,6 +44834,19 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.RemoteCommandInvalidationsRegistrationResult"
+    enum="BooleanSuccess" expires_after="2021-03-01">
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Tracks whether remote commands invalidator registered for corresponding
+    invalidations. In case of success the invalidator is able to receive
+    incoming invalidations.
+
+    Metric is similar to Enterprise.PolicyInvalidationsRegistrationResult.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.ResourceCacheTiming" units="ms"
     expires_after="2020-03-01">
   <obsolete>
@@ -45110,6 +45137,20 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.UserRemoteCommandInvalidations"
+    enum="EnterprisePolicyInvalidations" expires_after="2021-03-01">
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting user remote commands invalidations received with and
+    without payloads. Invalidations indicate that there is a remote command to
+    execute. Payloads provide context about the remote commands update, but may
+    be absent if dropped by the invalidation service.
+
+    Metric is similar to Enterprise.PolicyInvalidations.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.UserSession.Logins"
     enum="EnterpriseUserSessionLogins" expires_after="2020-08-30">
   <owner>xiyuan@chromium.org</owner>
@@ -126717,8 +126758,9 @@
 </histogram>
 
 <histogram name="Profile.Avatar" enum="ProfileAvatar"
-    expires_after="2020-03-01">
-  <owner>rogerta@chromium.org</owner>
+    expires_after="2021-03-01">
+  <owner>jkrcal@chromium.org</owner>
+  <owner>droger@chromium.org</owner>
   <summary>The frequency of selection of each avatar.</summary>
 </histogram>
 
@@ -127449,9 +127491,9 @@
 </histogram>
 
 <histogram name="Profile.TimeToOpenUserManagerUpTo1min" units="ms"
-    expires_after="M82">
+    expires_after="2021-03-01">
   <owner>msarda@chromium.org</owner>
-  <owner>tangltom@chromium.org</owner>
+  <owner>droger@chromium.org</owner>
   <summary>
     Time required to open the UserManager, from when it started to show until
     when its javascript started executing.
@@ -135366,6 +135408,30 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.V4LocalDatabaseManager.HasReceivedUpdateResponse"
+    enum="BooleanUpdateReceived" expires_after="2021-03-04">
+  <owner>ajuma@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    Records whether a network response has been received for a request to update
+    the Safe Browsing local database, since the browser was launched. This will
+    be logged whenever the database is queried.
+  </summary>
+</histogram>
+
+<histogram
+    name="SafeBrowsing.V4LocalDatabaseManager.TimeSinceLastUpdateResponse"
+    units="ms" expires_after="2021-03-04">
+  <owner>ajuma@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    Records the time since the last network response was received for a request
+    to udpate the Safe Browsing local database. This will be logged whenever the
+    database is queried, but only if at least one such network response has been
+    received since the browser was launched.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.V4MergeUpdateTime" units="ms"
     expires_after="2016-10-12">
   <obsolete>
@@ -187169,14 +187235,6 @@
   <affected-histogram name="Ash.NavigationWidget.AnimationSmoothness"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="HotseatWidgetElement" separator="."
-    ordering="prefix,2">
-  <suffix name="TranslucentBackground"
-      label="Hotseat widget's translucent background"/>
-  <suffix name="Widget" label="Hotseat widget"/>
-  <affected-histogram name="Ash.HotseatWidgetAnimation.AnimationSmoothness"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="HstsState" separator=".">
   <suffix name="HSTSNotEnabled" label="The HSTS is not enabled."/>
   <suffix name="WithHSTSEnabled" label="The HSTS is enabled."/>
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index e65edde1..cca5b60c 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -232,6 +232,7 @@
     "java/src/org/chromium/ui/base/ActivityWindowAndroid.java",
     "java/src/org/chromium/ui/base/AndroidPermissionDelegate.java",
     "java/src/org/chromium/ui/base/AndroidPermissionDelegateWithRequester.java",
+    "java/src/org/chromium/ui/base/ApplicationViewportInsetSupplier.java",
     "java/src/org/chromium/ui/base/Clipboard.java",
     "java/src/org/chromium/ui/base/DeviceFormFactor.java",
     "java/src/org/chromium/ui/base/EventForwarder.java",
@@ -377,6 +378,7 @@
   sources = [
     "junit/src/org/chromium/ui/AsyncViewProviderTest.java",
     "junit/src/org/chromium/ui/AsyncViewStubTest.java",
+    "junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java",
     "junit/src/org/chromium/ui/base/ClipboardTest.java",
     "junit/src/org/chromium/ui/base/EventOffsetHandlerTest.java",
     "junit/src/org/chromium/ui/base/LocalizationUtilsTest.java",
diff --git a/ui/android/java/src/org/chromium/ui/base/ApplicationViewportInsetSupplier.java b/ui/android/java/src/org/chromium/ui/base/ApplicationViewportInsetSupplier.java
new file mode 100644
index 0000000..5cd35c3
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/base/ApplicationViewportInsetSupplier.java
@@ -0,0 +1,88 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.ui.base;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.Callback;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.ObservableSupplierImpl;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A class responsible for managing multiple users of window viewport insets. The class that
+ * actually updates the viewport insets listens to this class via {@link #addObserver(Callback)}.
+ * Other features can also listen to this class to know if something is obscuring part of the
+ * screen. Features that wish to update the viewport's insets can attach an inset supplier via
+ * {@link #addSupplier(ObservableSupplier)}. This class will use the largest inset value to
+ * adjust the viewport inset.
+ *
+ * In general:
+ *  - Features that want to modify the inset should pass around the
+ *    {@link ApplicationViewportInsetSupplier} object.
+ *  - Features only interested in what the current inset is should pass around an
+ *    {@link ObservableSupplier<Integer>} object.
+ */
+public class ApplicationViewportInsetSupplier extends ObservableSupplierImpl<Integer> {
+    /** The list of inset providers that this class manages. */
+    private final Set<ObservableSupplier<Integer>> mInsetSuppliers = new HashSet<>();
+
+    /** The observer that gets attached to all inset providers. */
+    private final Callback<Integer> mInsetSupplierObserver = (inset) -> computeInset();
+
+    /** Default constructor. */
+    ApplicationViewportInsetSupplier() {
+        super();
+        // Make sure this is initialized to 0 since "Integer" is an object and would be null
+        // otherwise.
+        super.set(0);
+    }
+
+    @VisibleForTesting
+    public static ApplicationViewportInsetSupplier createForTests() {
+        return new ApplicationViewportInsetSupplier();
+    }
+
+    /** Clean up observers and suppliers. */
+    public void destroy() {
+        for (ObservableSupplier<Integer> os : mInsetSuppliers) {
+            os.removeObserver(mInsetSupplierObserver);
+        }
+        mInsetSuppliers.clear();
+    }
+
+    /** Compute the new inset based on the current registered providers. */
+    private void computeInset() {
+        int currentInset = 0;
+        for (ObservableSupplier<Integer> os : mInsetSuppliers) {
+            // Similarly to the constructor, check that the Integer object isn't null as the
+            // supplier may not yet have supplied the initial value.
+            currentInset = Math.max(currentInset, os.get() == null ? 0 : os.get());
+        }
+
+        super.set(currentInset);
+    }
+
+    @Override
+    public void set(Integer value) {
+        throw new IllegalStateException(
+                "#set(...) should not be called directly on ApplicationViewportInsetSupplier.");
+    }
+
+    /** @param insetSupplier A supplier of bottom insets to be added. */
+    public void addSupplier(ObservableSupplier<Integer> insetSupplier) {
+        mInsetSuppliers.add(insetSupplier);
+        insetSupplier.addObserver(mInsetSupplierObserver);
+    }
+
+    /** @param insetSupplier A supplier of bottom insets to be removed. */
+    public void removeSupplier(ObservableSupplier<Integer> insetSupplier) {
+        mInsetSuppliers.remove(insetSupplier);
+        insetSupplier.removeObserver(mInsetSupplierObserver);
+        computeInset();
+    }
+}
diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
index 99374ad..db08c9f 100644
--- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
@@ -121,6 +121,10 @@
     // System accessibility service.
     private final AccessibilityManager mAccessibilityManager;
 
+    /** A mechanism for observing and updating the application window's bottom inset. */
+    private ApplicationViewportInsetSupplier mApplicationBottomInsetProvider =
+            new ApplicationViewportInsetSupplier();
+
     // Whether touch exploration is enabled.
     private boolean mIsTouchExplorationEnabled;
 
@@ -653,6 +657,7 @@
         }
 
         TouchlessEventHandler.removeCursorObserver(mCursorObserver);
+        mApplicationBottomInsetProvider.destroy();
     }
 
     /**
@@ -733,6 +738,11 @@
         return mKeyboardVisibilityDelegate;
     }
 
+    /** @return A mechanism for updating and observing the bottom inset of the browser window. */
+    public ApplicationViewportInsetSupplier getApplicationBottomInsetProvider() {
+        return mApplicationBottomInsetProvider;
+    }
+
     @VisibleForTesting
     public void setKeyboardDelegate(KeyboardVisibilityDelegate keyboardDelegate) {
         mKeyboardVisibilityDelegate = keyboardDelegate;
diff --git a/ui/android/junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java b/ui/android/junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java
new file mode 100644
index 0000000..e2584ae
--- /dev/null
+++ b/ui/android/junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java
@@ -0,0 +1,133 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.ui.base;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.Callback;
+import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Unit tests for the ApplicationViewportInsetSupplier. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class ApplicationViewportInsetSupplierTest {
+    /** A callback with the ability to get the last value pushed to it. */
+    private static class CapturingCallback<T> implements Callback<T> {
+        private T mValue;
+
+        @Override
+        public void onResult(T result) {
+            mValue = result;
+        }
+
+        public T getCapturedValue() {
+            return mValue;
+        }
+    }
+
+    private ApplicationViewportInsetSupplier mWindowApplicationInsetSupplier;
+    private ObservableSupplierImpl<Integer> mFeatureInsetSupplier;
+    private CapturingCallback<Integer> mWindowInsetObserver;
+
+    @Before
+    public void setUp() {
+        mWindowApplicationInsetSupplier = new ApplicationViewportInsetSupplier();
+        mFeatureInsetSupplier = new ObservableSupplierImpl<>();
+        mWindowInsetObserver = new CapturingCallback<>();
+
+        mWindowApplicationInsetSupplier.addSupplier(mFeatureInsetSupplier);
+        mWindowApplicationInsetSupplier.addObserver(mWindowInsetObserver);
+    }
+
+    @Test
+    public void testSupplierDidNotSetValue() {
+        assertEquals("Observed value from supplier is incorrect.", (Integer) 0,
+                mWindowApplicationInsetSupplier.get());
+    }
+
+    @Test
+    public void testSupplierTriggersObserver() {
+        mFeatureInsetSupplier.set(5);
+        assertEquals("Observed value from supplier is incorrect.", (Integer) 5,
+                mWindowInsetObserver.getCapturedValue());
+    }
+
+    @Test
+    public void testSupplierTriggersObserver_multipleSuppliers_2() {
+        ObservableSupplierImpl<Integer> secondSupplier = new ObservableSupplierImpl<>();
+        mWindowApplicationInsetSupplier.addSupplier(secondSupplier);
+
+        mFeatureInsetSupplier.set(5);
+        secondSupplier.set(10);
+
+        assertEquals("Observed value should be the max of the two supplied.", (Integer) 10,
+                mWindowInsetObserver.getCapturedValue());
+    }
+
+    @Test
+    public void testSupplierTriggersObserver_multipleSuppliers_3() {
+        ObservableSupplierImpl<Integer> secondSupplier = new ObservableSupplierImpl<>();
+        mWindowApplicationInsetSupplier.addSupplier(secondSupplier);
+
+        ObservableSupplierImpl<Integer> thirdSupplier = new ObservableSupplierImpl<>();
+        mWindowApplicationInsetSupplier.addSupplier(thirdSupplier);
+
+        mFeatureInsetSupplier.set(5);
+        secondSupplier.set(20);
+        thirdSupplier.set(10);
+
+        assertEquals("Observed value should be the max of the three supplied.", (Integer) 20,
+                mWindowInsetObserver.getCapturedValue());
+    }
+
+    @Test
+    public void testSupplierTriggersObserver_setBeforeAdded() {
+        ObservableSupplierImpl<Integer> supplier = new ObservableSupplierImpl<>();
+        supplier.set(20);
+
+        mWindowApplicationInsetSupplier.addSupplier(supplier);
+
+        assertEquals("The observer should have been triggered after the supplier was added.",
+                (Integer) 20, mWindowInsetObserver.getCapturedValue());
+    }
+
+    @Test
+    public void testSupplierRemoveTriggersEvent() {
+        ObservableSupplierImpl<Integer> secondSupplier = new ObservableSupplierImpl<>();
+        mWindowApplicationInsetSupplier.addSupplier(secondSupplier);
+
+        ObservableSupplierImpl<Integer> thirdSupplier = new ObservableSupplierImpl<>();
+        mWindowApplicationInsetSupplier.addSupplier(thirdSupplier);
+
+        mFeatureInsetSupplier.set(5);
+        secondSupplier.set(20);
+        thirdSupplier.set(10);
+
+        assertEquals("Observed value should be the max of the three supplied.", (Integer) 20,
+                mWindowInsetObserver.getCapturedValue());
+
+        mWindowApplicationInsetSupplier.removeSupplier(secondSupplier);
+
+        assertEquals("Observed value should be the max of the two remaining.", (Integer) 10,
+                mWindowInsetObserver.getCapturedValue());
+    }
+
+    @Test
+    public void testAllSuppliersRemoved() {
+        mFeatureInsetSupplier.set(5);
+
+        assertEquals("Observed value from supplier is incorrect.", (Integer) 5,
+                mWindowInsetObserver.getCapturedValue());
+
+        mWindowApplicationInsetSupplier.removeSupplier(mFeatureInsetSupplier);
+
+        assertEquals("Observed value should be 0 with no suppliers attached.", (Integer) 0,
+                mWindowInsetObserver.getCapturedValue());
+    }
+}
diff --git a/ui/events/gesture_detection/gesture_detector.cc b/ui/events/gesture_detection/gesture_detector.cc
index 7523ad0e..5ee0e7c 100644
--- a/ui/events/gesture_detection/gesture_detector.cc
+++ b/ui/events/gesture_detection/gesture_detector.cc
@@ -11,6 +11,7 @@
 #include "base/numerics/ranges.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
+#include "ui/events/event_constants.h"
 #include "ui/events/gesture_detection/gesture_listeners.h"
 #include "ui/events/gesture_detection/motion_event.h"
 #include "ui/gfx/geometry/angle_conversions.h"
@@ -51,6 +52,11 @@
       two_finger_tap_max_separation(300),
       two_finger_tap_timeout(base::TimeDelta::FromMilliseconds(700)),
       single_tap_repeat_interval(1),
+#if defined(OS_CHROMEOS)
+      stylus_button_accelerated_longpress_enabled(true),
+#else
+      stylus_button_accelerated_longpress_enabled(false),
+#endif
       velocity_tracker_strategy(VelocityTracker::Strategy::STRATEGY_DEFAULT) {
 }
 
@@ -135,6 +141,7 @@
       last_focus_y_(0),
       down_focus_x_(0),
       down_focus_y_(0),
+      stylus_button_accelerated_longpress_enabled_(false),
       longpress_enabled_(true),
       showpress_enabled_(true),
       swipe_enabled_(false),
@@ -333,6 +340,11 @@
         last_focus_y_ = focus_y;
       }
 
+      if (stylus_button_accelerated_longpress_enabled_ &&
+          (ev.GetFlags() & ui::EF_LEFT_MOUSE_BUTTON)) {
+        OnStylusButtonPress(ev);
+      }
+
       if (!two_finger_tap_allowed_for_gesture_)
         break;
 
@@ -458,6 +470,8 @@
 
   DCHECK_GE(config.single_tap_repeat_interval, 1);
   single_tap_repeat_interval_ = config.single_tap_repeat_interval;
+  stylus_button_accelerated_longpress_enabled_ =
+      config.stylus_button_accelerated_longpress_enabled;
 }
 
 void GestureDetector::OnShowPressTimeout() {
@@ -481,6 +495,20 @@
   }
 }
 
+void GestureDetector::OnStylusButtonPress(const MotionEvent& ev) {
+  if (!timeout_handler_->HasTimeout(LONG_PRESS))
+    return;
+  timeout_handler_->StopTimeout(TAP);
+  timeout_handler_->StopTimeout(SHOW_PRESS);
+  timeout_handler_->StopTimeout(LONG_PRESS);
+  defer_confirm_single_tap_ = false;
+  // This will generate a ET_GESTURE_LONG_PRESS event with EF_LEFT_MOUSE_BUTTON,
+  // which is consumed by MetalayerMode if that feature is enabled, because
+  // MetalayerMode is also activated by a stylus button press and has precedence
+  // over this press acceleration feature.
+  listener_->OnLongPress(ev);
+}
+
 void GestureDetector::Cancel() {
   // Stop waiting for a second tap and send a GESTURE_TAP_CANCEL to keep the
   // gesture stream valid.
diff --git a/ui/events/gesture_detection/gesture_detector.h b/ui/events/gesture_detection/gesture_detector.h
index 761bc5a..e525277b 100644
--- a/ui/events/gesture_detection/gesture_detector.h
+++ b/ui/events/gesture_detection/gesture_detector.h
@@ -77,6 +77,10 @@
     // count will always be 1.
     int single_tap_repeat_interval;
 
+    // Whether a longpress should be generated immediately when a stylus button
+    // is pressed, given that the longpress timeout is still active.
+    bool stylus_button_accelerated_longpress_enabled;
+
     VelocityTracker::Strategy velocity_tracker_strategy;
   };
 
@@ -112,6 +116,7 @@
   void OnShowPressTimeout();
   void OnLongPressTimeout();
   void OnTapTimeout();
+  void OnStylusButtonPress(const MotionEvent& ev);
   void Cancel();
   void CancelTaps();
   bool IsRepeatedTap(const MotionEvent& first_down,
@@ -172,6 +177,7 @@
   float down_focus_x_;
   float down_focus_y_;
 
+  bool stylus_button_accelerated_longpress_enabled_;
   bool longpress_enabled_;
   bool showpress_enabled_;
   bool swipe_enabled_;
diff --git a/ui/events/gesture_detection/gesture_provider_unittest.cc b/ui/events/gesture_detection/gesture_provider_unittest.cc
index 5386ab3..9c0b37b 100644
--- a/ui/events/gesture_detection/gesture_provider_unittest.cc
+++ b/ui/events/gesture_detection/gesture_provider_unittest.cc
@@ -298,6 +298,13 @@
     SetUpWithConfig(config);
   }
 
+  void SetStylusButtonAcceleratedLongPress(bool enabled) {
+    GestureProvider::Config config = GetDefaultConfig();
+    config.gesture_detector_config.stylus_button_accelerated_longpress_enabled =
+        enabled;
+    SetUpWithConfig(config);
+  }
+
   bool HasDownEvent() const { return gesture_provider_->current_down_event(); }
 
  protected:
@@ -1249,6 +1256,36 @@
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
 }
 
+TEST_F(GestureProviderTest, StylusButtonCausesLongPress) {
+  SetStylusButtonAcceleratedLongPress(true);
+  base::TimeTicks event_time = base::TimeTicks::Now();
+
+  MockMotionEvent event =
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
+  EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
+
+  event = ObtainMotionEvent(event_time + kOneMicrosecond,
+                            MotionEvent::Action::MOVE);
+  event.set_flags(EF_LEFT_MOUSE_BUTTON);
+  EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
+  EXPECT_EQ(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
+}
+
+TEST_F(GestureProviderTest, DisabledStylusButtonDoesNotCauseLongPress) {
+  SetStylusButtonAcceleratedLongPress(false);
+  base::TimeTicks event_time = base::TimeTicks::Now();
+
+  MockMotionEvent event =
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
+  EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
+
+  event = ObtainMotionEvent(event_time + kOneMicrosecond,
+                            MotionEvent::Action::MOVE);
+  event.set_flags(EF_LEFT_MOUSE_BUTTON);
+  EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
+  EXPECT_NE(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
+}
+
 TEST_F(GestureProviderTest, NoGestureLongPressDuringDoubleTap) {
   base::TimeTicks event_time = base::TimeTicks::Now();
   int motion_event_id = 6;
diff --git a/ui/file_manager/integration_tests/file_manager/search.js b/ui/file_manager/integration_tests/file_manager/search.js
index 76976d4..dd2b0d8 100644
--- a/ui/file_manager/integration_tests/file_manager/search.js
+++ b/ui/file_manager/integration_tests/file_manager/search.js
@@ -163,4 +163,52 @@
         await remoteCall.waitForElement(appId, ['#search-box cr-input']);
     chrome.test.assertEq('false', textInputElement.attributes['aria-disabled']);
   };
+
+  /**
+   * Tests that the search box collapses when empty and Tab out of the box.
+   */
+  testcase.searchHidingViaTab = async () => {
+    const entry = ENTRIES.hello;
+
+    // Open Files app on Downloads.
+    const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS, [entry], []);
+
+    // Measure the width of the search box when it's collapsed.
+    const collapsedSearchBox = await remoteCall.waitForElementStyles(
+        appId, '#search-wrapper', ['width']);
+
+    // Click the toolbar search button.
+    chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+        'fakeEvent', appId, ['#search-button', 'click']));
+
+    // Wait search box to be expanded.
+    const caller = getCaller();
+    await repeatUntil(async () => {
+      const element = await remoteCall.waitForElementStyles(
+          appId, '#search-wrapper', ['width']);
+      if (collapsedSearchBox.renderedWidth > element.renderedWidth) {
+        return pending(caller, 'Waiting search box to expand');
+      }
+    });
+
+    // Verify the search input has focus.
+    const input =
+        await remoteCall.callRemoteTestUtil('deepGetActiveElement', appId, []);
+    chrome.test.assertEq(input.attributes['id'], 'input');
+    chrome.test.assertEq(input.attributes['aria-label'], 'Search');
+
+    // Send Tab key to focus the next element.
+    const result = await sendTestMessage({name: 'dispatchTabKey'});
+    chrome.test.assertEq(
+        result, 'tabKeyDispatched', 'Tab key dispatch failure');
+
+    // Check: the search box should collapse.
+    await repeatUntil(async () => {
+      const element = await remoteCall.waitForElementStyles(
+          appId, '#search-wrapper', ['width']);
+      if (collapsedSearchBox.renderedWidth < element.renderedWidth) {
+        return pending(caller, 'Waiting search box to collapse');
+      }
+    });
+  };
 })();
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
index 46eb655..cfb59c05 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
@@ -104,6 +104,8 @@
 
   void OnWidgetDestroying(Widget* widget) override {
     auto iter = std::find(widgets_.begin(), widgets_.end(), widget);
+    // Since |widget| is about to be destroyed, there is no point in removing
+    // |this| from its list of observers.
     if (iter != widgets_.end())
       widgets_.erase(iter);
   }
diff --git a/ui/views/controls/menu/menu_runner_cocoa_unittest.mm b/ui/views/controls/menu/menu_runner_cocoa_unittest.mm
index 5193079..3213086 100644
--- a/ui/views/controls/menu/menu_runner_cocoa_unittest.mm
+++ b/ui/views/controls/menu/menu_runner_cocoa_unittest.mm
@@ -334,7 +334,8 @@
 
 // Tests a potential lifetime issue using the Cocoa MenuController, which has a
 // weak reference to the model.
-TEST_P(MenuRunnerCocoaTest, RunMenuAndDeleteThenSelectItem) {
+// Disabled: crbug.com/1060063
+TEST_P(MenuRunnerCocoaTest, DISABLED_RunMenuAndDeleteThenSelectItem) {
   RunMenu(
       base::BindOnce(&MenuRunnerCocoaTest::ModelDeleteThenSelectItemCallback,
                      base::Unretained(this)));
@@ -343,7 +344,8 @@
 
 // Ensure a menu can be safely released immediately after a call to Cancel() in
 // the same run loop iteration.
-TEST_P(MenuRunnerCocoaTest, DestroyAfterCanceling) {
+// Disabled: crbug.com/1060063
+TEST_P(MenuRunnerCocoaTest, DISABLED_DestroyAfterCanceling) {
   RunMenu(base::BindOnce(&MenuRunnerCocoaTest::MenuCancelAndDeleteCallback,
                          base::Unretained(this)));
 
@@ -380,7 +382,8 @@
 }
 
 // Tests anchoring of the menus used for toolkit-views Comboboxes.
-TEST_P(MenuRunnerCocoaTest, ComboboxAnchoring) {
+// Disabled: crbug.com/1060063
+TEST_P(MenuRunnerCocoaTest, DISABLED_ComboboxAnchoring) {
   // Combobox at 20,10 in the Widget.
   const gfx::Rect combobox_rect(20, 10, 80, 50);
 
diff --git a/ui/views/layout/flex_layout_types.cc b/ui/views/layout/flex_layout_types.cc
index 2d25fd54..ed32ed8c 100644
--- a/ui/views/layout/flex_layout_types.cc
+++ b/ui/views/layout/flex_layout_types.cc
@@ -25,6 +25,12 @@
                     int minimum_size,
                     int preferred_size,
                     int available_size) {
+  // A view may (mistakenly) report a minimum size larger than its preferred
+  // size. While in principle this shouldn't happen, by the time we've gotten
+  // here it's better to simply make sure the minimum and preferred don't
+  // cross.
+  minimum_size = std::min(minimum_size, preferred_size);
+
   if (available_size < minimum_size) {
     switch (minimum_size_rule) {
       case MinimumFlexSizeRule::kScaleToZero: