diff --git a/DEPS b/DEPS
index bd785c6..9275223 100644
--- a/DEPS
+++ b/DEPS
@@ -129,11 +129,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': 'e62bf561638c60550c35e81b1606843ed74bcfef',
+  'skia_revision': '12cf258193dc2b92c19fafefde28265f1201f578',
   # 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': '8042513e57dbb4da70ff0050af4ad19400108d9f',
+  'v8_revision': '81260d20b1cfb15752913acb657113c92c6c0026',
   # 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.
@@ -141,15 +141,15 @@
   # 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': '1e064014eb2ba3f8a4aca5dec389ab1c7c58ddd7',
+  'angle_revision': '065f8dc35f9dffead39ced85a0ab1ba447ccf843',
   # 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': '7d867277f805cedc984b7d4235ee04f35d859487',
+  'swiftshader_revision': 'db4f3dfd19601c7c544f2b1eb2e98706a33f7902',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '763affaf6625e4670c172aa19fba8e1a3d23fab5',
+  'pdfium_revision': 'd69e0650a1c945933be7766a5b3a9e9c93cadd8a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -196,7 +196,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': '5b353de6daa95ccf247a9bd26add94edbbdd684b',
+  'catapult_revision': 'b614c567e8b363220b00ce5557a3ddfd8d18357c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -805,7 +805,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7017b6e58088989184bf2581c3862c1b5627f7b9',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd7ba750e8dd8eb4df11ae6a5bd01e62de590efae',
       'condition': 'checkout_linux',
   },
 
@@ -978,7 +978,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + '83f4659730b4de886afa5ea19f71f332a009d2ee',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '4ae7482a0e9e1f77a793545d803086a5ad4bcfd8',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '35f7e139f33f1ddbfdb68b65dda29aff430c3f6f',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -1027,7 +1027,7 @@
   },
 
   'src/third_party/leveldatabase/src':
-    Var('chromium_git') + '/external/leveldb.git' + '@' + '7035af5fc36657447054617759854a726d31dbe0',
+    Var('chromium_git') + '/external/leveldb.git' + '@' + 'ffabb1ae86cc4eb4516a7c0824c878c3b2d19e5d',
 
   'src/third_party/libFuzzer/src':
     Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git' + '@' +  Var('libfuzzer_revision'),
@@ -1354,7 +1354,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '3f6583d3fee4ab71866ade794504a20eb6f63f88',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3af5c4c3547b7da427b0cc87b0cdc0d56de37bcd',
+    Var('webrtc_git') + '/src.git' + '@' + '79e9f4b9c128963eef2c7031dd9311f86fca5535',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1395,7 +1395,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0d6826ff6cd92888bca24c2423013db9c65c2fc3',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2221eb4885adfdeb675107859a225d2dd56e3a52',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index f6db694..cba5aad4 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -985,9 +985,14 @@
 
 bool AppListControllerImpl::CanProcessEventsOnApplistViews() {
   // Do not allow processing events during overview or while overview is
-  // finished but still animating out.
+  // finished but still animating out. Note in clamshell mode, if overview and
+  // splitview is both active, we still allow the user to open app list and
+  // select an app. The app will be opened in snapped window state and overview
+  // will be ended after the app is opened.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
-  if (overview_controller->IsSelecting() ||
+  auto* split_view_controller = Shell::Get()->split_view_controller();
+  if ((overview_controller->IsSelecting() &&
+       !split_view_controller->InClamshellSplitViewMode()) ||
       overview_controller->IsCompletingShutdownAnimations()) {
     return false;
   }
diff --git a/ash/app_list/app_list_presenter_delegate_impl.cc b/ash/app_list/app_list_presenter_delegate_impl.cc
index 577be62..f8f8f310 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.cc
+++ b/ash/app_list/app_list_presenter_delegate_impl.cc
@@ -112,6 +112,7 @@
   DCHECK(is_visible_);
   DCHECK(view_);
   is_visible_ = false;
+  Shell::Get()->RemovePreTargetHandler(this);
   controller_->ViewClosing();
 }
 
@@ -230,7 +231,7 @@
   }
 
   aura::Window* window = view_->GetWidget()->GetNativeView()->parent();
-  if (!window->Contains(target) && !presenter_->CloseOpenedPage() &&
+  if (!window->Contains(target) && !presenter_->HandleCloseOpenFolder() &&
       !app_list::switches::ShouldNotDismissOnBlur() && !IsTabletMode()) {
     const aura::Window* status_window =
         shelf->shelf_widget()->status_area_widget()->GetNativeWindow();
@@ -242,6 +243,10 @@
     if (status_window && status_window->Contains(target))
       auto_hide_lock.emplace(shelf);
 
+    // Since event happened outside the app list, close the open search box if
+    // it is open.
+    presenter_->HandleCloseOpenSearchBox();
+
     // Keep app list opened if event happened in the shelf area.
     if (!shelf_window || !shelf_window->Contains(target))
       presenter_->Dismiss(event->time_stamp());
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 38feec3a..7020b27 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -841,8 +841,8 @@
       GetPrimaryShelf()->shelf_layout_manager()->GetShelfBackgroundType());
 }
 
-// Tests that the half app list transitions to peeking state and then closes
-// itself if the user taps outside its bounds.
+// Tests that the half app list closes itself if the user taps outside its
+// bounds.
 TEST_P(AppListPresenterDelegateTest, TapAndClickOutsideClosesHalfAppList) {
   // TODO(newcomer): Investigate mash failures crbug.com/726838
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
@@ -857,16 +857,6 @@
   gfx::Point to_point(
       0, GetAppListView()->GetWidget()->GetWindowBoundsInScreen().y() - 1);
 
-  // Clicking/tapping outside the bounds closes the search results page.
-  if (TestMouseEventParam()) {
-    generator->MoveMouseTo(to_point);
-    generator->ClickLeftButton();
-  } else {
-    generator->GestureTapAt(to_point);
-  }
-  GetAppListTestHelper()->WaitUntilIdle();
-  GetAppListTestHelper()->CheckState(ash::mojom::AppListViewState::kPeeking);
-
   // Clicking/tapping outside the bounds closes the app list.
   if (TestMouseEventParam()) {
     generator->MoveMouseTo(to_point);
diff --git a/ash/app_list/presenter/app_list_presenter_impl.cc b/ash/app_list/presenter/app_list_presenter_impl.cc
index 4dfd81d..6f1776bf 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl.cc
@@ -158,14 +158,12 @@
   base::RecordAction(base::UserMetricsAction("Launcher_Dismiss"));
 }
 
-bool AppListPresenterImpl::CloseOpenedPage() {
-  if (!is_visible_)
-    return false;
+bool AppListPresenterImpl::HandleCloseOpenFolder() {
+  return is_visible_ && view_ && view_->HandleCloseOpenFolder();
+}
 
-  // If the app list is currently visible, there should be an existing view.
-  DCHECK(view_);
-
-  return view_->CloseOpenedPage();
+bool AppListPresenterImpl::HandleCloseOpenSearchBox() {
+  return is_visible_ && view_ && view_->HandleCloseOpenSearchBox();
 }
 
 ash::ShelfAction AppListPresenterImpl::ToggleAppList(
diff --git a/ash/app_list/presenter/app_list_presenter_impl.h b/ash/app_list/presenter/app_list_presenter_impl.h
index e577f591..ad54a25 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.h
+++ b/ash/app_list/presenter/app_list_presenter_impl.h
@@ -81,9 +81,13 @@
   // one AppListShowSource or focusing out side of the launcher.
   void Dismiss(base::TimeTicks event_time_stamp);
 
-  // Closes opened folder or search result page if they are opened. Returns
-  // whether the action was handled.
-  bool CloseOpenedPage();
+  // If app list has an opened folder, close it. Returns whether an opened
+  // folder was closed.
+  bool HandleCloseOpenFolder();
+
+  // If app list has an open search box, close it. Returns whether an open
+  // search box was closed.
+  bool HandleCloseOpenSearchBox();
 
   // Show the app list if it is visible, hide it if it is hidden. If
   // |event_time_stamp| is not 0, it means |ToggleAppList()| was triggered by
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 2dc0bad..05d2da1 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -421,19 +421,31 @@
   GetWidget()->Deactivate();
 }
 
-bool AppListView::CloseOpenedPage() {
-  if (!app_list_main_view_)
-    return false;
+void AppListView::CloseOpenedPage() {
+  if (HandleCloseOpenFolder())
+    return;
 
-  if (app_list_main_view_->contents_view()->IsShowingSearchResults() ||
-      GetAppsContainerView()->IsInFolderView()) {
-    return app_list_main_view_->contents_view()->Back();
+  HandleCloseOpenSearchBox();
+}
+
+bool AppListView::HandleCloseOpenFolder() {
+  if (GetAppsContainerView()->IsInFolderView()) {
+    GetAppsContainerView()->app_list_folder_view()->CloseFolderPage();
+    return true;
   }
   return false;
 }
 
-void AppListView::Back() {
-  app_list_main_view_->contents_view()->Back();
+bool AppListView::HandleCloseOpenSearchBox() {
+  if (app_list_main_view_ &&
+      app_list_main_view_->contents_view()->IsShowingSearchResults()) {
+    return Back();
+  }
+  return false;
+}
+
+bool AppListView::Back() {
+  return app_list_main_view_->contents_view()->Back();
 }
 
 void AppListView::OnPaint(gfx::Canvas* canvas) {
@@ -461,9 +473,8 @@
     case ui::VKEY_BROWSER_BACK:
       // If the ContentsView does not handle the back action, then this is the
       // top level, so we close the app list.
-      if (!app_list_main_view_->contents_view()->Back() && !is_tablet_mode()) {
+      if (!Back() && !is_tablet_mode())
         Dismiss();
-      }
       break;
     default:
       NOTREACHED();
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index c94310b..922a947 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -147,12 +147,18 @@
   // Dismisses the UI, cleans up and sets the state to CLOSED.
   void Dismiss();
 
-  // Closes opened folder or search result page if they are opened. Returns
-  // whether the action was handled.
-  bool CloseOpenedPage();
+  // Closes opened folder or search result page if they are opened.
+  void CloseOpenedPage();
+
+  // If a folder is open, close it. Returns whether an opened folder was closed.
+  bool HandleCloseOpenFolder();
+
+  // If a search box is open, close it. Returns whether an open search box was
+  // closed.
+  bool HandleCloseOpenSearchBox();
 
   // Performs the 'back' action for the active page.
-  void Back();
+  bool Back();
 
   // views::View:
   void OnPaint(gfx::Canvas* canvas) override;
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index cd6f9c84..3a53c68 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -13,6 +13,9 @@
 const base::Feature kDockedMagnifier{"DockedMagnifier",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kDragToSnapInClamshellMode{
+    "DragToSnapInClamshellMode", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kEnableOverviewRoundedCorners{
     "EnableOverviewRoundedCorners", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 740cc81d..c0b5b8d 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -16,6 +16,10 @@
 // https://crbug.com/709824.
 ASH_PUBLIC_EXPORT extern const base::Feature kDockedMagnifier;
 
+// Enables dragging and snapping an overview window in clamshell mode.
+// TODO(crbug.com/890029): Remove this when the feature is fully launched.
+ASH_PUBLIC_EXPORT extern const base::Feature kDragToSnapInClamshellMode;
+
 // Enables rounded corners in overview mode for testing.
 // TODO(crbug.com/903486): Remove this when new rounded corners implementation
 // has landed.
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index 19348b5..6e72052 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -326,8 +326,9 @@
 
   if (IsSelecting()) {
     // Do not allow ending overview if we're in single split mode unless swiping
-    // up from the shelf.
-    if (windows.empty() && Shell::Get()->IsSplitViewModeActive() &&
+    // up from the shelf in tablet mode.
+    if (windows.empty() &&
+        Shell::Get()->split_view_controller()->InTabletSplitViewMode() &&
         type != OverviewSession::EnterExitOverviewType::kSwipeFromShelf) {
       return true;
     }
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 849f793..c8622b9 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "ash/accessibility/accessibility_controller.h"
+#include "ash/app_list/app_list_controller_impl.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/screen_util.h"
@@ -350,7 +351,7 @@
     // not active. Set |index| to -1 so that it does not attempt to select any
     // items.
     index = -1;
-    if (!Shell::Get()->IsSplitViewModeActive()) {
+    if (!Shell::Get()->split_view_controller()->InTabletSplitViewMode()) {
       for (const auto& grid : grid_list_)
         grid->Shutdown();
       grid_list_.clear();
@@ -760,6 +761,15 @@
 }
 
 void OverviewSession::OnKeyEvent(ui::KeyEvent* event) {
+  // If app list is open when overview is active (it can happen in clamshell
+  // mode, when we snap an overview window to one side of the screen and then
+  // open the app list to select an app to snap to the other side), in this case
+  // we let the app list to handle the key event.
+  // TODO(crbug.com/952315): Explore better ways to handle this splitview +
+  // overview + applist case.
+  if (Shell::Get()->app_list_controller()->IsVisible())
+    return;
+
   if (event->type() != ui::ET_KEY_PRESSED)
     return;
 
@@ -831,6 +841,10 @@
 void OverviewSession::OnSplitViewStateChanged(
     SplitViewController::State previous_state,
     SplitViewController::State state) {
+  // Do nothing if overview is being shutdown.
+  if (!Shell::Get()->overview_controller()->IsSelecting())
+    return;
+
   const bool unsnappable_window_activated =
       state == SplitViewController::NO_SNAP &&
       Shell::Get()->split_view_controller()->end_reason() ==
@@ -842,9 +856,12 @@
     ResetFocusRestoreWindow(false);
 
   // If two windows were snapped to both sides of the screen or an unsnappable
-  // window was just activated, end overview mode and bail out.
+  // window was just activated, or we're in single split mode in clamshell mode
+  // and there is no window in overview, end overview mode and bail out.
   if (state == SplitViewController::BOTH_SNAPPED ||
-      unsnappable_window_activated) {
+      unsnappable_window_activated ||
+      (Shell::Get()->split_view_controller()->InClamshellSplitViewMode() &&
+       IsEmpty())) {
     CancelSelection();
     return;
   }
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 42a0c1d..d99dbf3 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -2840,6 +2840,7 @@
   EXPECT_FALSE(IsSelecting());
 }
 
+// Test the split view and overview functionalities in tablet mode.
 class SplitViewOverviewSessionTest : public OverviewSessionTest {
  public:
   SplitViewOverviewSessionTest() = default;
@@ -4191,4 +4192,292 @@
   EXPECT_FALSE(IsSelecting());
 }
 
+// Test the split view and overview functionalities in clamshell mode. Split
+// view is only active when overview is active in clamshell mode.
+class SplitViewOverviewSessionInClamshellTest
+    : public SplitViewOverviewSessionTest {
+ public:
+  SplitViewOverviewSessionInClamshellTest() = default;
+  ~SplitViewOverviewSessionInClamshellTest() override = default;
+
+  // AshTestBase:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        ash::features::kDragToSnapInClamshellMode);
+    OverviewSessionTest::SetUp();
+    Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(
+        false);
+    DCHECK(ShouldAllowSplitView());
+  }
+
+  aura::Window* CreateWindowWithHitTestComponent(int hit_test_component,
+                                                 const gfx::Rect& bounds) {
+    return CreateTestWindowInShellWithDelegate(
+        new TestWindowHitTestDelegate(hit_test_component), 0, bounds);
+  }
+
+ private:
+  class TestWindowHitTestDelegate : public aura::test::TestWindowDelegate {
+   public:
+    explicit TestWindowHitTestDelegate(int hit_test_component) {
+      set_window_component(hit_test_component);
+    }
+    ~TestWindowHitTestDelegate() override = default;
+
+   private:
+    // aura::Test::TestWindowDelegate:
+    void OnWindowDestroyed(aura::Window* window) override { delete this; }
+
+    DISALLOW_COPY_AND_ASSIGN(TestWindowHitTestDelegate);
+  };
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+  DISALLOW_COPY_AND_ASSIGN(SplitViewOverviewSessionInClamshellTest);
+};
+
+// Test some basic functionalities in clamshell splitview mode.
+TEST_F(SplitViewOverviewSessionInClamshellTest, BasicFunctionalitiesTest) {
+  UpdateDisplay("600x400");
+  EXPECT_FALSE(Shell::Get()
+                   ->tablet_mode_controller()
+                   ->IsTabletModeWindowManagerEnabled());
+
+  // 1. Test the 1 window scenario.
+  const gfx::Rect bounds(400, 400);
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+  wm::WindowState* window_state1 = wm::GetWindowState(window1.get());
+  EXPECT_FALSE(window_state1->IsSnapped());
+  ToggleOverview();
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+  // Drag |window1| selector item to snap to left.
+  const int grid_index = 0;
+  OverviewItem* overview_item1 =
+      GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(overview_item1, gfx::PointF(0, 0));
+  // Since the only window is snapped, overview and splitview should be both
+  // ended.
+  EXPECT_EQ(window_state1->GetStateType(),
+            mojom::WindowStateType::LEFT_SNAPPED);
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+
+  // 2. Test if one window is snapped, the other windows are showing in
+  // overview, close all windows in overview will end overview and also
+  // splitview.
+  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
+  ToggleOverview();
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+  overview_item1 = GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(overview_item1, gfx::PointF(600, 300));
+  // SplitView and overview are both active at the moment.
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  EXPECT_TRUE(split_view_controller()->IsWindowInSplitView(window1.get()));
+  EXPECT_TRUE(overview_controller()->overview_session()->IsWindowInOverview(
+      window2.get()));
+  EXPECT_EQ(window_state1->GetStateType(),
+            mojom::WindowStateType::RIGHT_SNAPPED);
+  // Close |window2| will end overview and splitview.
+  window2.reset();
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+
+  // 3. Test that snap 2 windows will end overview and splitview.
+  std::unique_ptr<aura::Window> window3(CreateWindow(bounds));
+  ToggleOverview();
+  overview_item1 = GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(overview_item1, gfx::PointF(0, 0));
+  OverviewItem* overview_item3 =
+      GetWindowItemForWindow(grid_index, window3.get());
+  DragWindowTo(overview_item3, gfx::PointF(600, 300));
+  EXPECT_EQ(window_state1->GetStateType(),
+            mojom::WindowStateType::LEFT_SNAPPED);
+  EXPECT_EQ(wm::GetWindowState(window3.get())->GetStateType(),
+            mojom::WindowStateType::RIGHT_SNAPPED);
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+
+  // 4. Test if one window is snapped, the other windows are showing in
+  // overview, we can drag another window in overview to snap in splitview, and
+  // the previous snapped window will not be put back into overview.
+  std::unique_ptr<aura::Window> window4(CreateWindow(bounds));
+  ToggleOverview();
+  overview_item1 = GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(overview_item1, gfx::PointF(0, 0));
+  EXPECT_FALSE(overview_controller()->overview_session()->IsWindowInOverview(
+      window1.get()));
+  overview_item3 = GetWindowItemForWindow(grid_index, window3.get());
+  DragWindowTo(overview_item3, gfx::PointF(0, 0));
+  EXPECT_FALSE(overview_controller()->overview_session()->IsWindowInOverview(
+      window3.get()));
+  EXPECT_FALSE(overview_controller()->overview_session()->IsWindowInOverview(
+      window1.get()));
+  EXPECT_EQ(window_state1->GetStateType(),
+            mojom::WindowStateType::LEFT_SNAPPED);
+  EXPECT_EQ(wm::GetWindowState(window3.get())->GetStateType(),
+            mojom::WindowStateType::LEFT_SNAPPED);
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  ToggleOverview();
+  EXPECT_EQ(wm::GetWindowState(window4.get())->GetStateType(),
+            mojom::WindowStateType::RIGHT_SNAPPED);
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+
+  // 5. Test if one window is snapped, the other window are showing in overview,
+  // activating an new window will open in splitview, which ends splitview and
+  // overview.
+  ToggleOverview();
+  overview_item1 = GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(overview_item1, gfx::PointF(0, 0));
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  std::unique_ptr<aura::Window> window5(CreateWindow(bounds));
+  wm::ActivateWindow(window5.get());
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+
+  // 6. Test if one window is snapped, the other window is showing in overview,
+  // close the snapped window will end split view, but overview is still active.
+  ToggleOverview();
+  const gfx::Rect overview_bounds = GetGridBounds();
+  overview_item1 = GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(overview_item1, gfx::PointF(0, 0));
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  EXPECT_NE(GetGridBounds(), overview_bounds);
+  EXPECT_EQ(GetGridBounds(), GetSplitViewRightWindowBounds(window1.get()));
+  window1.reset();
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+  // Overview bounds will adjust from snapped bounds to fullscreen bounds.
+  EXPECT_EQ(GetGridBounds(), overview_bounds);
+}
+
+// Test that if app list is visible when overview is open, overview should
+// ignore all key events.
+TEST_F(SplitViewOverviewSessionInClamshellTest, IgnoreEventsIfApplistVisible) {
+  const gfx::Rect bounds(400, 400);
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
+
+  // If splitview is not active, open app list when overview is active will
+  // just end overview.
+  ToggleOverview();
+  // Open app list.
+  AppListControllerImpl* app_list_controller =
+      Shell::Get()->app_list_controller();
+  app_list_controller->ToggleAppList(
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window1.get()).id(),
+      app_list::AppListShowSource::kSearchKey, base::TimeTicks());
+  base::RunLoop().RunUntilIdle();
+  // Test that app list is open and overview is ended.
+  EXPECT_TRUE(app_list_controller->IsVisible());
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+
+  ToggleOverview();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(app_list_controller->IsVisible());
+  const int grid_index = 0;
+  OverviewItem* overview_item1 =
+      GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(overview_item1, gfx::PointF(0, 0));
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+
+  // Open app list.
+  app_list_controller->ToggleAppList(
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window1.get()).id(),
+      app_list::AppListShowSource::kSearchKey, base::TimeTicks());
+  base::RunLoop().RunUntilIdle();
+  // Test that app list is open, splitview and overview are both active.
+  EXPECT_TRUE(app_list_controller->IsVisible());
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+
+  // Send ui::VKEY_ESCAPE should dismiss app list. But splitview and overview
+  // should still be active.
+  SendKey(ui::VKEY_ESCAPE);
+  EXPECT_FALSE(app_list_controller->IsVisible());
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  // Send ui::VKEY_ESCAPE should then end overview.
+  SendKey(ui::VKEY_ESCAPE);
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+}
+
+// Test that when overview and splitview are both active, only resize that
+// happens on eligible window components will change snapped window bounds and
+// overview bounds at the same time.
+TEST_F(SplitViewOverviewSessionInClamshellTest, ResizeWindowTest) {
+  UpdateDisplay("600x400");
+  const gfx::Rect bounds(400, 400);
+  std::unique_ptr<aura::Window> window1(
+      CreateWindowWithHitTestComponent(HTRIGHT, bounds));
+  std::unique_ptr<aura::Window> window2(
+      CreateWindowWithHitTestComponent(HTLEFT, bounds));
+  std::unique_ptr<aura::Window> window3(
+      CreateWindowWithHitTestComponent(HTTOP, bounds));
+  std::unique_ptr<aura::Window> window4(
+      CreateWindowWithHitTestComponent(HTBOTTOM, bounds));
+
+  ToggleOverview();
+  const gfx::Rect overview_full_bounds = GetGridBounds();
+  const int grid_index = 0;
+  OverviewItem* overview_item1 =
+      GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(overview_item1, gfx::PointF(0, 0));
+  EXPECT_NE(GetGridBounds(), overview_full_bounds);
+  EXPECT_EQ(GetGridBounds(), GetSplitViewRightWindowBounds(window1.get()));
+  const gfx::Rect overview_snapped_bounds = GetGridBounds();
+
+  // Resize that happens on the right edge of the left snapped window will
+  // resize the window and overview at the same time.
+  ui::test::EventGenerator generator1(Shell::GetPrimaryRootWindow(),
+                                      window1.get());
+  generator1.DragMouseBy(50, 50);
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  EXPECT_NE(GetGridBounds(), overview_full_bounds);
+  EXPECT_NE(GetGridBounds(), overview_snapped_bounds);
+  EXPECT_EQ(GetGridBounds(), GetSplitViewRightWindowBounds(window1.get()));
+
+  // Resize that happens on the left edge of the left snapped window will end
+  // overview. The same for the resize that happens on the top or bottom edge of
+  // the left snapped window.
+  OverviewItem* overview_item2 =
+      GetWindowItemForWindow(grid_index, window2.get());
+  DragWindowTo(overview_item2, gfx::PointF(0, 0));
+  EXPECT_TRUE(overview_controller()->IsSelecting());
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  ui::test::EventGenerator generator2(Shell::GetPrimaryRootWindow(),
+                                      window2.get());
+  generator2.DragMouseBy(50, 50);
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+
+  ToggleOverview();
+  OverviewItem* overview_item3 =
+      GetWindowItemForWindow(grid_index, window3.get());
+  DragWindowTo(overview_item3, gfx::PointF(0, 0));
+  ui::test::EventGenerator generator3(Shell::GetPrimaryRootWindow(),
+                                      window3.get());
+  generator3.DragMouseBy(50, 50);
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+
+  ToggleOverview();
+  OverviewItem* overview_item4 =
+      GetWindowItemForWindow(grid_index, window4.get());
+  DragWindowTo(overview_item4, gfx::PointF(0, 0));
+  ui::test::EventGenerator generator4(Shell::GetPrimaryRootWindow(),
+                                      window4.get());
+  generator4.DragMouseBy(50, 50);
+  EXPECT_FALSE(overview_controller()->IsSelecting());
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+}
+
 }  // namespace ash
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index fd252d81..32193e0 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -290,6 +290,14 @@
 SplitViewController::SplitViewController() {
   Shell::Get()->accessibility_controller()->AddObserver(this);
   display::Screen::GetScreen()->AddObserver(this);
+  Shell::Get()->tablet_mode_controller()->AddObserver(this);
+  if (IsClamshellSplitViewModeEnabled()) {
+    split_view_type_ = Shell::Get()
+                               ->tablet_mode_controller()
+                               ->IsTabletModeWindowManagerEnabled()
+                           ? SplitViewType::kTabletType
+                           : SplitViewType::kClamshellType;
+  }
 }
 
 SplitViewController::~SplitViewController() {
@@ -304,7 +312,15 @@
 }
 
 bool SplitViewController::IsSplitViewModeActive() const {
-  return state_ != NO_SNAP;
+  return InClamshellSplitViewMode() || InTabletSplitViewMode();
+}
+
+bool SplitViewController::InClamshellSplitViewMode() const {
+  return state_ != NO_SNAP && split_view_type_ == SplitViewType::kClamshellType;
+}
+
+bool SplitViewController::InTabletSplitViewMode() const {
+  return state_ != NO_SNAP && split_view_type_ == SplitViewType::kTabletType;
 }
 
 void SplitViewController::SnapWindow(aura::Window* window,
@@ -328,13 +344,16 @@
     Shell::Get()->AddShellObserver(this);
     Shell::Get()->overview_controller()->AddObserver(this);
     Shell::Get()->activation_client()->AddObserver(this);
-    Shell::Get()->tablet_mode_controller()->AddObserver(this);
     Shell::Get()->NotifySplitViewModeStarting();
 
     divider_position_ = GetDefaultDividerPosition(window);
     default_snap_position_ = snap_position;
-    split_view_divider_ =
-        std::make_unique<SplitViewDivider>(this, window->GetRootWindow());
+
+    // There is no divider bar in clamshell splitview mode.
+    if (split_view_type_ == SplitViewType::kTabletType) {
+      split_view_divider_ =
+          std::make_unique<SplitViewDivider>(this, window->GetRootWindow());
+    }
     splitview_start_time_ = base::Time::Now();
   }
 
@@ -362,20 +381,28 @@
   }
   StartObserving(window);
 
-  // Insert the previous snapped window to overview if overview is active.
-  if (previous_snapped_window)
-    InsertWindowToOverview(previous_snapped_window);
+  if (split_view_type_ == SplitViewType::kTabletType) {
+    // Insert the previous snapped window to overview if overview is active.
+    if (previous_snapped_window)
+      InsertWindowToOverview(previous_snapped_window);
 
-  // Update the divider position and window bounds before snapping a new window.
-  // Since the minimum size of |window| maybe larger than currently bounds in
-  // |snap_position|.
-  if (state_ != NO_SNAP) {
-    divider_position_ = GetClosestFixedDividerPosition();
-    UpdateSnappedWindowsAndDividerBounds();
+    // Update the divider position and window bounds before snapping a new
+    // window. Since the minimum size of |window| maybe larger than currently
+    // bounds in |snap_position|.
+    if (state_ != NO_SNAP) {
+      divider_position_ = GetClosestFixedDividerPosition();
+      UpdateSnappedWindowsAndDividerBounds();
+    }
   }
 
   if (wm::GetWindowState(window)->GetStateType() ==
       GetStateTypeFromSnapPosition(snap_position)) {
+    // Update its snapped bounds as its bounds may not be the expected snapped
+    // bounds here.
+    const wm::WMEvent event((snap_position == LEFT) ? wm::WM_EVENT_SNAP_LEFT
+                                                    : wm::WM_EVENT_SNAP_RIGHT);
+    wm::GetWindowState(window)->OnWMEvent(&event);
+
     OnWindowSnapped(window);
   } else {
     // Otherwise, try to snap it first. It will be activated later after the
@@ -483,9 +510,13 @@
   const gfx::Rect work_area_bounds_in_screen =
       screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           window);
-  const gfx::Size divider_size = SplitViewDivider::GetDividerSize(
-      work_area_bounds_in_screen, GetCurrentScreenOrientation(),
-      false /* is_dragging */);
+  gfx::Size divider_size;
+  // In clamshell mode, there is no divider bar, thus divider_size is empty.
+  if (split_view_type_ == SplitViewType::kTabletType) {
+    divider_size = SplitViewDivider::GetDividerSize(
+        work_area_bounds_in_screen, GetCurrentScreenOrientation(),
+        false /* is_dragging */);
+  }
   if (IsCurrentScreenOrientationLandscape())
     return (work_area_bounds_in_screen.width() - divider_size.width()) * 0.5f;
   else
@@ -633,7 +664,6 @@
   Shell::Get()->RemoveShellObserver(this);
   Shell::Get()->overview_controller()->RemoveObserver(this);
   Shell::Get()->activation_client()->RemoveObserver(this);
-  Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
 
   StopObserving(LEFT);
   StopObserving(RIGHT);
@@ -661,7 +691,7 @@
     OnSnappedWindowDetached(dragged_window, /*window_drag=*/true);
 
   // OnSnappedWindowDetached() may end split view mode.
-  if (IsSplitViewModeActive())
+  if (split_view_divider_)
     split_view_divider_->OnWindowDragStarted(dragged_window);
 }
 
@@ -722,6 +752,97 @@
   }
 }
 
+void SplitViewController::OnWindowBoundsChanged(
+    aura::Window* window,
+    const gfx::Rect& old_bounds,
+    const gfx::Rect& new_bounds,
+    ui::PropertyChangeReason reason) {
+  if (!IsSplitViewModeActive())
+    return;
+
+  if (split_view_type_ == SplitViewType::kClamshellType &&
+      reason == ui::PropertyChangeReason::NOT_FROM_ANIMATION) {
+    const gfx::Rect work_area =
+        screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
+            window);
+    const bool is_left_or_top_window =
+        IsCurrentScreenOrientationPrimary()
+            ? (window == left_window_ ? true : false)
+            : (window == left_window_ ? false : true);
+    if (IsCurrentScreenOrientationLandscape()) {
+      divider_position_ = is_left_or_top_window
+                              ? new_bounds.width()
+                              : work_area.width() - new_bounds.width();
+    } else {
+      divider_position_ = is_left_or_top_window
+                              ? new_bounds.height()
+                              : work_area.height() - new_bounds.height();
+    }
+    NotifyDividerPositionChanged();
+  }
+}
+
+void SplitViewController::OnResizeLoopStarted(aura::Window* window) {
+  if (split_view_type_ != SplitViewType::kClamshellType ||
+      !IsSplitViewModeActive()) {
+    return;
+  }
+
+  const int resize_component =
+      wm::GetWindowState(window)->drag_details()->window_component;
+  const OrientationLockType orientation_type = GetCurrentScreenOrientation();
+
+  // In clamshell mode, if splitview is active (which means overview is active
+  // at the same time), only the resize that happens on the window edge that's
+  // next to the overview grid will resize the window and overview grid at the
+  // same time. For the resize that happens on the other part of the window,
+  // we'll just end splitview and overview mode.
+  bool should_end_splitview = true;
+  switch (orientation_type) {
+    case OrientationLockType::kLandscapePrimary:
+      should_end_splitview =
+          !(window == left_window_ && resize_component == HTRIGHT) &&
+          !(window == right_window_ && resize_component == HTLEFT);
+      break;
+    case OrientationLockType::kLandscapeSecondary:
+      should_end_splitview =
+          !(window == left_window_ && resize_component == HTLEFT) &&
+          !(window == right_window_ && resize_component == HTRIGHT);
+      break;
+    case OrientationLockType::kPortraitPrimary:
+      should_end_splitview =
+          !(window == left_window_ && resize_component == HTBOTTOM) &&
+          !(window == right_window_ && resize_component == HTTOP);
+      break;
+    case OrientationLockType::kPortraitSecondary:
+      should_end_splitview =
+          !(window == left_window_ && resize_component == HTTOP) &&
+          !(window == right_window_ && resize_component == HTBOTTOM);
+      break;
+    default:
+      break;
+  }
+
+  if (should_end_splitview) {
+    EndSplitView();
+    EndOverview();
+  }
+}
+
+void SplitViewController::OnResizeLoopEnded(aura::Window* window) {
+  if (split_view_type_ != SplitViewType::kClamshellType ||
+      !IsSplitViewModeActive()) {
+    return;
+  }
+
+  if (divider_position_ < GetDividerEndPosition() * kOneThirdPositionRatio ||
+      divider_position_ > GetDividerEndPosition() * kTwoThirdPositionRatio) {
+    EndSplitView();
+    EndOverview();
+    wm::GetWindowState(window)->Maximize();
+  }
+}
+
 void SplitViewController::OnPostWindowStateTypeChange(
     ash::wm::WindowState* window_state,
     ash::mojom::WindowStateType old_type) {
@@ -844,8 +965,13 @@
 
   OverviewGrid* current_grid =
       overview_session->GetGridWithRootWindow(root_window);
-  if (!current_grid)
+  if (!current_grid || current_grid->empty()) {
+    // If overview is ended with an empty overview grid, end split view as well
+    // if we're in clamshell splitview mode.
+    if (InClamshellSplitViewMode())
+      EndSplitView();
     return;
+  }
 
   // If split view mode is active but only has one snapped window when overview
   // mode is ending, retrieve the first snappable window in the overview window
@@ -949,10 +1075,20 @@
   UpdateSnappedWindowsAndDividerBounds();
 }
 
+void SplitViewController::OnTabletModeStarted() {
+  split_view_type_ = SplitViewType::kTabletType;
+}
+
 void SplitViewController::OnTabletModeEnding() {
+  if (IsClamshellSplitViewModeEnabled())
+    split_view_type_ = SplitViewType::kClamshellType;
   EndSplitView();
 }
 
+void SplitViewController::OnTabletControllerDestroyed() {
+  tablet_mode_observer_.RemoveAll();
+}
+
 void SplitViewController::OnAccessibilityStatusChanged() {
   // TODO(crubg.com/853588): Exit split screen if ChromeVox is turned on until
   // they are compatible.
@@ -965,7 +1101,8 @@
     Shell::Get()->shadow_controller()->UpdateShadowForWindow(window);
     window->AddObserver(this);
     wm::GetWindowState(window)->AddObserver(this);
-    split_view_divider_->AddObservedWindow(window);
+    if (split_view_divider_)
+      split_view_divider_->AddObservedWindow(window);
   }
 }
 
@@ -979,7 +1116,8 @@
   if (window && window->HasObserver(this)) {
     window->RemoveObserver(this);
     wm::GetWindowState(window)->RemoveObserver(this);
-    split_view_divider_->RemoveObservedWindow(window);
+    if (split_view_divider_)
+      split_view_divider_->RemoveObservedWindow(window);
     Shell::Get()->shadow_controller()->UpdateShadowForWindow(window);
   }
 }
@@ -1076,7 +1214,8 @@
   }
 
   // Update divider's bounds.
-  split_view_divider_->UpdateDividerBounds();
+  if (split_view_divider_)
+    split_view_divider_->UpdateDividerBounds();
 }
 
 SplitViewController::SnapPosition SplitViewController::GetBlackScrimPosition(
@@ -1153,9 +1292,17 @@
   divider_position_ = (divider_position_ < 0)
                           ? GetDefaultDividerPosition(window)
                           : divider_position_;
-  const gfx::Rect divider_bounds = SplitViewDivider::GetDividerBoundsInScreen(
-      work_area_bounds_in_screen, GetCurrentScreenOrientation(),
-      divider_position_, false /* is_dragging */);
+  gfx::Rect divider_bounds;
+  if (split_view_type_ == SplitViewType::kTabletType) {
+    divider_bounds = SplitViewDivider::GetDividerBoundsInScreen(
+        work_area_bounds_in_screen, GetCurrentScreenOrientation(),
+        divider_position_, false /* is_dragging */);
+  } else {
+    if (IsCurrentScreenOrientationLandscape())
+      divider_bounds.set_x(work_area_bounds_in_screen.x() + divider_position_);
+    else
+      divider_bounds.set_y(work_area_bounds_in_screen.y() + divider_position_);
+  }
 
   SplitRect(work_area_bounds_in_screen, divider_bounds,
             IsCurrentScreenOrientationLandscape(), left_or_top_rect,
@@ -1285,6 +1432,12 @@
   RestoreTransformIfApplicable(window);
   UpdateSplitViewStateAndNotifyObservers();
   ActivateAndStackSnappedWindow(window);
+
+  // If there are two window snapped in clamshell mode, splitview mode is ended.
+  if (state_ == BOTH_SNAPPED &&
+      split_view_type_ == SplitViewType::kClamshellType) {
+    EndSplitView();
+  }
 }
 
 void SplitViewController::OnSnappedWindowDetached(aura::Window* window,
@@ -1653,7 +1806,7 @@
     bool is_being_destroyed,
     SnapPosition desired_snap_position,
     const gfx::Point& last_location_in_screen) {
-  if (IsSplitViewModeActive())
+  if (split_view_divider_)
     split_view_divider_->OnWindowDragEnded();
 
   // If the dragged window is to be destroyed, do not try to snap it.
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 258bb0b..9f52e418 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -13,6 +13,7 @@
 #include "ash/public/interfaces/split_view.mojom.h"
 #include "ash/shell_observer.h"
 #include "ash/wm/overview/overview_observer.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
 #include "ash/wm/window_state_observer.h"
 #include "base/containers/flat_map.h"
@@ -76,6 +77,19 @@
     kPipExpanded,
   };
 
+  // The behaviors of split view are very different when in tablet mode and in
+  // clamshell mode. In tablet mode, split view mode will stay active until the
+  // user explicitly ends it (e.g., by pressing home launcher, or long pressing
+  // the overview button, or sliding the divider bar to the edge, etc). However,
+  // in clamshell mode, there is no divider bar, and split view mode only stays
+  // active during overview snapping, i.e., it's only possible that split view
+  // is active when overview is active. Once the user has selected two windows
+  // to snap to both side of the screen, split view mode is no longer active.
+  enum class SplitViewType {
+    kTabletType = 0,
+    kClamshellType,
+  };
+
   class Observer {
    public:
     // Called when split view state changed from |previous_state| to |state|.
@@ -93,8 +107,12 @@
   // Binds the mojom::SplitViewController interface to this object.
   void BindRequest(mojom::SplitViewControllerRequest request);
 
-  // Returns true if split view mode is active.
+  // Returns true if split view mode is active. Please see SplitViewType above
+  // to see the difference between tablet mode and clamshell mode splitview
+  // mode.
   bool IsSplitViewModeActive() const;
+  bool InClamshellSplitViewMode() const;
+  bool InTabletSplitViewMode() const;
 
   // Snaps window to left/right. It will try to remove |window| from the
   // overview window grid first before snapping it if |window| is currently
@@ -151,6 +169,12 @@
   void OnWindowDragEnded(aura::Window* dragged_window,
                          SnapPosition desired_snap_position,
                          const gfx::Point& last_location_in_screen);
+  void OnWindowBoundsChanged(aura::Window* window,
+                             const gfx::Rect& old_bounds,
+                             const gfx::Rect& new_bounds,
+                             ui::PropertyChangeReason reason) override;
+  void OnResizeLoopStarted(aura::Window* window) override;
+  void OnResizeLoopEnded(aura::Window* window) override;
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
@@ -188,7 +212,9 @@
                                uint32_t metrics) override;
 
   // TabletModeObserver:
+  void OnTabletModeStarted() override;
   void OnTabletModeEnding() override;
+  void OnTabletControllerDestroyed() override;
 
   // AccessibilityObserver:
   void OnAccessibilityStatusChanged() override;
@@ -389,10 +415,10 @@
   aura::Window* left_window_ = nullptr;
   aura::Window* right_window_ = nullptr;
 
-  // Split view divider widget. It's a black bar stretching from one edge of the
-  // screen to the other, containing a small white drag bar in the middle. As
-  // the user presses on it and drag it to left or right, the left and right
-  // window will be resized accordingly.
+  // Split view divider widget. Only exist in tablet splitview mode. It's a
+  // black bar stretching from one edge of the screen to the other, containing a
+  // small white drag bar in the middle. As the user presses on it and drag it
+  // to left or right, the left and right window will be resized accordingly.
   std::unique_ptr<SplitViewDivider> split_view_divider_;
 
   // A black scrim layer that fades in over a window when it’s width drops under
@@ -400,7 +426,7 @@
   // closer to the edge of the screen.
   std::unique_ptr<ui::Layer> black_scrim_layer_;
 
-  // The window observer that obseves the tab-dragged window.
+  // The window observer that obseves the tab-dragged window in tablet mode.
   std::unique_ptr<TabDraggedWindowObserver> dragged_window_observer_;
 
   // The distance between the origin of the divider and the origin of the
@@ -443,6 +469,10 @@
   // Stores the reason which cause splitview to end.
   EndReason end_reason_ = EndReason::kNormal;
 
+  // The split view type. See SplitViewType for the differences between tablet
+  // split view and clamshell split view.
+  SplitViewType split_view_type_ = SplitViewType::kTabletType;
+
   // The time when splitview starts. Used for metric collection purpose.
   base::Time splitview_start_time_;
 
@@ -453,6 +483,9 @@
   base::ObserverList<Observer>::Unchecked observers_;
   mojo::InterfacePtrSet<mojom::SplitViewObserver> mojo_observers_;
 
+  ScopedObserver<TabletModeController, TabletModeObserver>
+      tablet_mode_observer_{this};
+
   // Records the presentation time of resize operation in split view mode.
   std::unique_ptr<PresentationTimeRecorder> presentation_time_recorder_;
 
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index 7083610..03afe6b 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -5,6 +5,7 @@
 #include "ash/wm/splitview/split_view_utils.h"
 
 #include "ash/accessibility/accessibility_controller.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
@@ -217,6 +218,11 @@
   layer->SetTransform(target_transform);
 }
 
+bool IsClamshellSplitViewModeEnabled() {
+  return base::FeatureList::IsEnabled(
+      ash::features::kDragToSnapInClamshellMode);
+}
+
 bool ShouldAllowSplitView() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kAshDisableTabletSplitView)) {
@@ -225,7 +231,8 @@
 
   if (!Shell::Get()
            ->tablet_mode_controller()
-           ->IsTabletModeWindowManagerEnabled()) {
+           ->IsTabletModeWindowManagerEnabled() &&
+      !IsClamshellSplitViewModeEnabled()) {
     return false;
   }
 
diff --git a/ash/wm/splitview/split_view_utils.h b/ash/wm/splitview/split_view_utils.h
index b64ec1aff..a7e146f 100644
--- a/ash/wm/splitview/split_view_utils.h
+++ b/ash/wm/splitview/split_view_utils.h
@@ -79,8 +79,11 @@
                                    SplitviewAnimationType type,
                                    const gfx::Transform& target_transform);
 
-// Returns true if split view mode is supported. Currently the split view
-// mode is only supported in tablet mode.
+// Returns true if we allow dragging an overview window to snap to split view in
+// clamshell mode.
+ASH_EXPORT bool IsClamshellSplitViewModeEnabled();
+
+// Returns true if split view mode is supported.
 ASH_EXPORT bool ShouldAllowSplitView();
 
 // Returns true if |window| can be activated and snapped in split screen in
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index a2d18106..5a086f7 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -363,14 +363,15 @@
 }
 
 bool ShouldExcludeForOverview(const aura::Window* window) {
-  // Remove the default snapped window from the window list. The default
-  // snapped window occupies one side of the screen, while the other windows
-  // occupy the other side of the screen in overview mode. The default snap
-  // position is the position where the window was first snapped. See
-  // |default_snap_position_| in SplitViewController for more detail.
-  if (Shell::Get()->IsSplitViewModeActive() &&
-      window ==
-          Shell::Get()->split_view_controller()->GetDefaultSnappedWindow()) {
+  // If we're currently in tablet splitview, remove the default snapped window
+  // from the window list. The default snapped window occupies one side of the
+  // screen, while the other windows occupy the other side of the screen in
+  // overview mode. The default snap position is the position where the window
+  // was first snapped. See |default_snap_position_| in SplitViewController for
+  // more detail.
+  auto* split_view_controller = Shell::Get()->split_view_controller();
+  if (split_view_controller->InTabletSplitViewMode() &&
+      window == split_view_controller->GetDefaultSnappedWindow()) {
     return true;
   }
 
diff --git a/base/fuchsia/service_directory_client.cc b/base/fuchsia/service_directory_client.cc
index 3dc964f..8b5a641a 100644
--- a/base/fuchsia/service_directory_client.cc
+++ b/base/fuchsia/service_directory_client.cc
@@ -10,23 +10,12 @@
 #include "base/fuchsia/file_utils.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
 
 namespace base {
 namespace fuchsia {
 
-namespace {
-
-// Singleton container for the process-global ServiceDirectoryClient instance.
-std::unique_ptr<ServiceDirectoryClient>* ProcessServiceDirectoryClient() {
-  static base::NoDestructor<std::unique_ptr<ServiceDirectoryClient>>
-      service_directory_client_ptr(std::make_unique<ServiceDirectoryClient>(
-          OpenDirectory(base::FilePath(kServiceDirectoryPath))));
-  return service_directory_client_ptr.get();
-}
-
-}  // namespace
-
 ServiceDirectoryClient::ServiceDirectoryClient(
     fidl::InterfaceHandle<::fuchsia::io::Directory> directory)
     : directory_(std::move(directory)) {
@@ -37,7 +26,7 @@
 
 // static
 const ServiceDirectoryClient* ServiceDirectoryClient::ForCurrentProcess() {
-  return ProcessServiceDirectoryClient()->get();
+  return ServiceDirectoryClient::ProcessInstance()->get();
 }
 
 zx_status_t ServiceDirectoryClient::ConnectToServiceUnsafe(
@@ -48,19 +37,40 @@
                                  request.release());
 }
 
+ServiceDirectoryClient::ServiceDirectoryClient() {}
+
+// static
+std::unique_ptr<ServiceDirectoryClient>
+ServiceDirectoryClient::CreateForProcess() {
+  fidl::InterfaceHandle<::fuchsia::io::Directory> directory =
+      OpenDirectory(base::FilePath(kServiceDirectoryPath));
+  if (directory)
+    return std::make_unique<ServiceDirectoryClient>(std::move(directory));
+  LOG(WARNING) << "/svc is not available.";
+  return base::WrapUnique(new ServiceDirectoryClient);
+}
+
+// static
+std::unique_ptr<ServiceDirectoryClient>*
+ServiceDirectoryClient::ProcessInstance() {
+  static base::NoDestructor<std::unique_ptr<ServiceDirectoryClient>>
+      service_directory_client_ptr(CreateForProcess());
+  return service_directory_client_ptr.get();
+}
+
 ScopedServiceDirectoryClientForCurrentProcessForTest::
     ScopedServiceDirectoryClientForCurrentProcessForTest(
         fidl::InterfaceHandle<::fuchsia::io::Directory> directory)
-    : old_client_(std::move(*ProcessServiceDirectoryClient())) {
-  *ProcessServiceDirectoryClient() =
+    : old_client_(std::move(*ServiceDirectoryClient::ProcessInstance())) {
+  *ServiceDirectoryClient::ProcessInstance() =
       std::make_unique<ServiceDirectoryClient>(std::move(directory));
-  client_ = ProcessServiceDirectoryClient()->get();
+  client_ = ServiceDirectoryClient::ProcessInstance()->get();
 }
 
 ScopedServiceDirectoryClientForCurrentProcessForTest::
     ~ScopedServiceDirectoryClientForCurrentProcessForTest() {
-  DCHECK_EQ(ProcessServiceDirectoryClient()->get(), client_);
-  *ProcessServiceDirectoryClient() = std::move(old_client_);
+  DCHECK_EQ(ServiceDirectoryClient::ProcessInstance()->get(), client_);
+  *ServiceDirectoryClient::ProcessInstance() = std::move(old_client_);
 }
 
 }  // namespace fuchsia
diff --git a/base/fuchsia/service_directory_client.h b/base/fuchsia/service_directory_client.h
index c2f9603..8342b772 100644
--- a/base/fuchsia/service_directory_client.h
+++ b/base/fuchsia/service_directory_client.h
@@ -25,6 +25,8 @@
 namespace base {
 namespace fuchsia {
 
+class ScopedServiceDirectoryClientForCurrentProcessForTest;
+
 // Helper for connecting to services from a supplied fuchsia.io.Directory.
 class BASE_EXPORT ServiceDirectoryClient {
  public:
@@ -69,6 +71,18 @@
                                      zx::channel request) const;
 
  private:
+  friend class ScopedServiceDirectoryClientForCurrentProcessForTest;
+  ServiceDirectoryClient();
+
+  // Creates a ServiceDirectoryClient connected to the process' "/svc"
+  // directory, or a dummy instance if the "/svc" directory is not available.
+  static std::unique_ptr<ServiceDirectoryClient> CreateForProcess();
+
+  // Returns the container holding the ForCurrentProcess() instance. The
+  // default ServiceDirectoryClient is created the first time this function is
+  // called.
+  static std::unique_ptr<ServiceDirectoryClient>* ProcessInstance();
+
   const fidl::InterfaceHandle<::fuchsia::io::Directory> directory_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceDirectoryClient);
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index a81a031b..9b7573f 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -575,16 +575,20 @@
       // Adding another element here? Remeber to increase the argument to
       // reserve(), above.
 
-      for (const auto& i : fd_shuffle1)
-        fd_shuffle2.push_back(i);
+      // Cannot use STL iterators here, since debug iterators use locks.
+      // NOLINTNEXTLINE(modernize-loop-convert)
+      for (size_t i = 0; i < fd_shuffle1.size(); ++i)
+        fd_shuffle2.push_back(fd_shuffle1[i]);
 
       if (!ShuffleFileDescriptors(&fd_shuffle1))
         _exit(127);
 
       CloseSuperfluousFds(fd_shuffle2);
 
-      for (const auto& arg : argv)
-        argv_cstr.push_back(const_cast<char*>(arg.c_str()));
+      // Cannot use STL iterators here, since debug iterators use locks.
+      // NOLINTNEXTLINE(modernize-loop-convert)
+      for (size_t i = 0; i < argv.size(); ++i)
+        argv_cstr.push_back(const_cast<char*>(argv[i].c_str()));
       argv_cstr.push_back(nullptr);
 
       if (do_search_path)
diff --git a/build/android/binary_size/apk_downloader.py b/build/android/binary_size/apk_downloader.py
index 83f7918c..aa7d12f 100755
--- a/build/android/binary_size/apk_downloader.py
+++ b/build/android/binary_size/apk_downloader.py
@@ -15,8 +15,8 @@
 sys.path.append(os.path.join(_BUILD_ANDROID, 'gyp'))
 from util import build_utils
 
-sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'build'))
-import find_depot_tools  # pylint: disable=import-error,unused-import
+sys.path.append(
+    os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party', 'depot_tools'))
 import download_from_google_storage
 import upload_to_google_storage
 
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py
index 9ccf5ac..f2bcec5 100755
--- a/build/android/gradle/generate_gradle.py
+++ b/build/android/gradle/generate_gradle.py
@@ -29,9 +29,8 @@
 import jinja_template
 from util import build_utils
 
-sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'build'))
-import find_depot_tools  # pylint: disable=import-error
-
+_DEPOT_TOOLS_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party',
+                                 'depot_tools')
 _DEFAULT_ANDROID_MANIFEST_PATH = os.path.join(
     host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'gradle',
     'AndroidManifest.xml')
@@ -114,9 +113,9 @@
 
 def _RunGnGen(output_dir, args=None):
   cmd = [
-    os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'gn'),
-    'gen',
-    output_dir,
+      os.path.join(_DEPOT_TOOLS_PATH, 'gn'),
+      'gen',
+      output_dir,
   ]
   if args:
     cmd.extend(args)
@@ -126,10 +125,10 @@
 
 def _RunNinja(output_dir, args, j):
   cmd = [
-    os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'ninja'),
-    '-C',
-    output_dir,
-    '-j{}'.format(j),
+      os.path.join(_DEPOT_TOOLS_PATH, 'ninja'),
+      '-C',
+      output_dir,
+      '-j{}'.format(j),
   ]
   cmd.extend(args)
   logging.info('Running: %r', cmd)
@@ -139,11 +138,11 @@
 def _QueryForAllGnTargets(output_dir):
   # Query ninja rather than GN since it's faster.
   cmd = [
-    os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'ninja'),
-    '-C',
-    output_dir,
-    '-t',
-    'targets',
+      os.path.join(_DEPOT_TOOLS_PATH, 'ninja'),
+      '-C',
+      output_dir,
+      '-t',
+      'targets',
   ]
   logging.info('Running: %r', cmd)
   ninja_output = build_utils.CheckOutput(cmd)
diff --git a/build/android/resource_sizes.pydeps b/build/android/resource_sizes.pydeps
index d071d12..7c075c2 100644
--- a/build/android/resource_sizes.pydeps
+++ b/build/android/resource_sizes.pydeps
@@ -44,11 +44,9 @@
 ../../third_party/catapult/tracing/tracing/value/__init__.py
 ../../third_party/catapult/tracing/tracing/value/convert_chart_json.py
 ../../third_party/catapult/tracing/tracing_project.py
-../../third_party/depot_tools/breakpad.py
 ../../third_party/depot_tools/download_from_google_storage.py
 ../../third_party/depot_tools/subprocess2.py
 ../../third_party/depot_tools/upload_to_google_storage.py
-../find_depot_tools.py
 ../gn_helpers.py
 ../util/lib/common/perf_result_data_type.py
 ../util/lib/common/perf_tests_results_helper.py
diff --git a/build/android/update_deps/update_third_party_deps.py b/build/android/update_deps/update_third_party_deps.py
index 5351d5c..3a869c4 100755
--- a/build/android/update_deps/update_third_party_deps.py
+++ b/build/android/update_deps/update_third_party_deps.py
@@ -20,9 +20,9 @@
 from pylib import constants
 from pylib.constants import host_paths
 
-sys.path.append(os.path.abspath(
-    os.path.join(host_paths.DIR_SOURCE_ROOT, 'build')))
-import find_depot_tools  # pylint: disable=import-error,unused-import
+sys.path.append(
+    os.path.abspath(
+        os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party', 'depot_tools')))
 import download_from_google_storage
 import upload_to_google_storage
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 546b0272..0f89ce3 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8916443031887960288
\ No newline at end of file
+8916269455106018432
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index cc92c7a8..80b9e33a 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8916474687955919136
\ No newline at end of file
+8916272757229952512
\ No newline at end of file
diff --git a/chrome/VERSION b/chrome/VERSION
index 98b5f48..7e1fa72 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=75
 MINOR=0
-BUILD=3764
+BUILD=3766
 PATCH=0
diff --git a/chrome/android/java/res/color/item_chooser_row_icon_color.xml b/chrome/android/java/res/color/item_chooser_row_icon_color.xml
index 4d608a16..fe24d39 100644
--- a/chrome/android/java/res/color/item_chooser_row_icon_color.xml
+++ b/chrome/android/java/res/color/item_chooser_row_icon_color.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@android:color/white" android:state_selected="true"/>
-    <item android:color="@color/primary_text_disabled_material_light" android:state_enabled="false"/>
+    <item android:color="@color/default_icon_color_inverse" android:state_selected="true"/>
+    <item android:color="@color/default_icon_color_disabled" android:state_enabled="false"/>
     <item android:color="@color/default_icon_color"/>
 </selector>
diff --git a/chrome/android/java/res/color/item_chooser_row_text_color.xml b/chrome/android/java/res/color/item_chooser_row_text_color.xml
index bb8c05c..b625ef51 100644
--- a/chrome/android/java/res/color/item_chooser_row_text_color.xml
+++ b/chrome/android/java/res/color/item_chooser_row_text_color.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@android:color/white" android:state_selected="true"/>
-    <item android:color="@color/primary_text_disabled_material_light" android:state_enabled="false"/>
+    <item android:color="@color/default_text_color_inverse" android:state_selected="true"/>
+    <item android:color="@color/disabled_text_color" android:state_enabled="false"/>
     <item android:color="@color/default_text_color"/>
 </selector>
diff --git a/chrome/android/java/res/layout/item_chooser_dialog.xml b/chrome/android/java/res/layout/item_chooser_dialog.xml
index 080b40a..118a82c2 100644
--- a/chrome/android/java/res/layout/item_chooser_dialog.xml
+++ b/chrome/android/java/res/layout/item_chooser_dialog.xml
@@ -9,7 +9,8 @@
     android:layout_height="fill_parent"
     android:orientation="vertical"
     android:paddingBottom="12dp"
-    android:paddingTop="20dp" >
+    android:paddingTop="20dp"
+    android:background="@color/sheet_bg_color">
 
     <!-- The title at the top. -->
     <org.chromium.ui.widget.TextViewWithClickableSpans
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java
index 9a08e1b..913a37a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java
@@ -185,8 +185,14 @@
         // Emphasize the origin.
         Profile profile = Profile.getLastUsedProfile();
         SpannableString origin = new SpannableString(mOrigin);
-        OmniboxUrlEmphasizer.emphasizeUrl(
-                origin, mActivity.getResources(), profile, mSecurityLevel, false, true, true);
+
+        assert mActivity instanceof ChromeBaseAppCompatActivity;
+        final boolean useDarkColors = !((ChromeBaseAppCompatActivity) mActivity)
+                                               .getNightModeStateProvider()
+                                               .isInNightMode();
+
+        OmniboxUrlEmphasizer.emphasizeUrl(origin, mActivity.getResources(), profile, mSecurityLevel,
+                false, useDarkColors, true);
         // Construct a full string and replace the origin text with emphasized version.
         SpannableString title =
                 new SpannableString(mActivity.getString(R.string.bluetooth_dialog_title, mOrigin));
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index c17c0a12..0bb9306 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9557,6 +9557,9 @@
       <message name="IDS_WEBAUTHN_SELECT_ACCOUNT_DESC" desc="The description on a dialog where the user is expected to select an account from a list. For example, the list may include several identities, e.g. joe@gmail.com, mary@gmail.com, and the user has to select one to login as.">
         Select an account to sign in:
       </message>
+      <message name="IDS_WEBAUTHN_RESIDENT_KEY_PRIVACY" desc="A message included in dialogs when activating a security key (an external physical device for user authentication) may cause information about a site visit to be recorded on that security key. Since this is a privacy concern, a message is shown to the user.">
+        This will leave a record of your visit to <ph name="WEBSITE"><ex>accounts.google.com</ex>$1</ph> on your security key.
+      </message>
       <message name="IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_TITLE" desc="Title of the dialog informing the user that the security key (an external physical device for user authentication) that they selected does not support some capability that the site requested.">
         Your security key can't be used with this site
       </message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b429d77..a745611 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2126,12 +2126,6 @@
      SINGLE_VALUE_TYPE(
          ::switches::kEnableExperimentalAccessibilityLanguageDetection)},
 #if defined(OS_CHROMEOS)
-    {"disable-system-timezone-automatic-detection",
-     flag_descriptions::kDisableSystemTimezoneAutomaticDetectionName,
-     flag_descriptions::kDisableSystemTimezoneAutomaticDetectionDescription,
-     kOsCrOS,
-     SINGLE_VALUE_TYPE(
-         chromeos::switches::kDisableSystemTimezoneAutomaticDetectionPolicy)},
     {"enable-bulk-printers", flag_descriptions::kBulkPrintersName,
      flag_descriptions::kBulkPrintersDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kBulkPrinters)},
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3a6ab133..30d0b980 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -5107,9 +5107,10 @@
 
 std::unique_ptr<content::AuthenticatorRequestClientDelegate>
 ChromeContentBrowserClient::GetWebAuthenticationRequestDelegate(
-    content::RenderFrameHost* render_frame_host) {
-  return AuthenticatorRequestScheduler::CreateRequestDelegate(
-      render_frame_host);
+    content::RenderFrameHost* render_frame_host,
+    const std::string& relying_party_id) {
+  return AuthenticatorRequestScheduler::CreateRequestDelegate(render_frame_host,
+                                                              relying_party_id);
 }
 
 #if defined(OS_MACOSX)
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 319bd17..138a10e 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -516,7 +516,8 @@
       base::OnceCallback<void(bool, int, int)> callback) override;
   std::unique_ptr<content::AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      content::RenderFrameHost* render_frame_host) override;
+      content::RenderFrameHost* render_frame_host,
+      const std::string& relying_party_id) override;
 #if defined(OS_MACOSX)
   bool IsWebAuthenticationTouchIdAuthenticatorSupported() override;
 #endif
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index b505f19..1eeec1f 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1827,6 +1827,8 @@
     "printing/enterprise_printers_provider.h",
     "printing/ppd_provider_factory.cc",
     "printing/ppd_provider_factory.h",
+    "printing/ppd_resolution_state.cc",
+    "printing/ppd_resolution_state.h",
     "printing/printer_configurer.cc",
     "printing/printer_configurer.h",
     "printing/printer_detector.h",
@@ -2537,6 +2539,7 @@
     "preferences_unittest.cc",
     "printing/bulk_printers_calculator_unittest.cc",
     "printing/cups_printers_manager_unittest.cc",
+    "printing/ppd_resolution_state_unittest.cc",
     "printing/printer_detector_test_util.h",
     "printing/printer_event_tracker_unittest.cc",
     "printing/printers_sync_bridge_unittest.cc",
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
index a5e6d62..ac69064 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
@@ -466,6 +466,7 @@
 
   feature_state_ = it->second;
   LoadRemoteDevices();
+  UpdateAppState();
 }
 
 void EasyUnlockServiceRegular::ShowChromebookAddedNotification() {
diff --git a/chrome/browser/chromeos/login/reset_browsertest.cc b/chrome/browser/chromeos/login/reset_browsertest.cc
index abd9b25..c7a387e4 100644
--- a/chrome/browser/chromeos/login/reset_browsertest.cc
+++ b/chrome/browser/chromeos/login/reset_browsertest.cc
@@ -272,8 +272,7 @@
   RegisterSomeUser();
 }
 
-// Disabled due to flakiness (crbug.com/870284)
-IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, DISABLED_RollbackUnavailable) {
+IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, RollbackUnavailable) {
   InvokeResetScreen();
   EXPECT_EQ(0, FakePowerManagerClient::Get()->num_request_restart_calls());
   EXPECT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
diff --git a/chrome/browser/chromeos/printing/ppd_resolution_state.cc b/chrome/browser/chromeos/printing/ppd_resolution_state.cc
new file mode 100644
index 0000000..c1731f7
--- /dev/null
+++ b/chrome/browser/chromeos/printing/ppd_resolution_state.cc
@@ -0,0 +1,61 @@
+// 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/chromeos/printing/ppd_resolution_state.h"
+
+#include <string>
+
+#include "base/logging.h"
+
+namespace chromeos {
+
+PpdResolutionState::PpdResolutionState()
+    : is_inflight_(true), is_ppd_resolution_successful_(false) {}
+
+PpdResolutionState::~PpdResolutionState() = default;
+
+void PpdResolutionState::MarkResolutionSuccessful(
+    const Printer::PpdReference& ppd_reference) {
+  DCHECK(is_inflight_);
+
+  ppd_reference_ = ppd_reference;
+  is_inflight_ = false;
+  is_ppd_resolution_successful_ = true;
+}
+
+void PpdResolutionState::MarkResolutionFailed() {
+  DCHECK(is_inflight_);
+
+  is_inflight_ = false;
+  is_ppd_resolution_successful_ = false;
+}
+
+void PpdResolutionState::SetUsbManufacturer(
+    const std::string& usb_manufacturer) {
+  DCHECK(!is_inflight_);
+  DCHECK(!is_ppd_resolution_successful_);
+
+  usb_manufacturer_ = usb_manufacturer;
+}
+
+const Printer::PpdReference& PpdResolutionState::GetPpdReference() const {
+  DCHECK(!is_inflight_);
+  DCHECK(is_ppd_resolution_successful_);
+  return ppd_reference_;
+}
+
+const std::string& PpdResolutionState::GetUsbManufacturer() const {
+  DCHECK(!is_inflight_);
+  return usb_manufacturer_;
+}
+
+bool PpdResolutionState::IsInflight() const {
+  return is_inflight_;
+}
+
+bool PpdResolutionState::WasResolutionSuccessful() const {
+  return is_ppd_resolution_successful_;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/ppd_resolution_state.h b/chrome/browser/chromeos/printing/ppd_resolution_state.h
new file mode 100644
index 0000000..675c050
--- /dev/null
+++ b/chrome/browser/chromeos/printing/ppd_resolution_state.h
@@ -0,0 +1,51 @@
+// 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_CHROMEOS_PRINTING_PPD_RESOLUTION_STATE_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_PPD_RESOLUTION_STATE_H_
+
+#include <string>
+
+#include "chromeos/printing/printer_configuration.h"
+
+namespace chromeos {
+
+class PpdResolutionState {
+ public:
+  PpdResolutionState();
+  ~PpdResolutionState();
+
+  // Marks PPD resolution was successful and stores |ppd_reference|.
+  void MarkResolutionSuccessful(const Printer::PpdReference& ppd_reference);
+
+  // Marks PPD resolution was unsuccessful.
+  void MarkResolutionFailed();
+
+  // Store |usb_manufacturer|.
+  void SetUsbManufacturer(const std::string& usb_manufacturer);
+
+  // Getter function for |ppd_reference_|.
+  const Printer::PpdReference& GetPpdReference() const;
+
+  // Getter function for |usb_manufacturer_|.
+  const std::string& GetUsbManufacturer() const;
+
+  // Returns true if the PPD resolution is inflight.
+  bool IsInflight() const;
+
+  // Returns true if a PpdReference was retrieved.
+  bool WasResolutionSuccessful() const;
+
+ private:
+  bool is_inflight_;
+  bool is_ppd_resolution_successful_;
+  Printer::PpdReference ppd_reference_;
+  std::string usb_manufacturer_;
+
+  DISALLOW_COPY_AND_ASSIGN(PpdResolutionState);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_PPD_RESOLUTION_STATE_H_
diff --git a/chrome/browser/chromeos/printing/ppd_resolution_state_unittest.cc b/chrome/browser/chromeos/printing/ppd_resolution_state_unittest.cc
new file mode 100644
index 0000000..394131f9
--- /dev/null
+++ b/chrome/browser/chromeos/printing/ppd_resolution_state_unittest.cc
@@ -0,0 +1,65 @@
+// 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/chromeos/printing/ppd_resolution_state.h"
+
+#include "base/macros.h"
+#include "chromeos/printing/printer_configuration.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace {
+
+class PpdResolutionStateTest : public testing::Test {
+ public:
+  PpdResolutionStateTest() = default;
+  ~PpdResolutionStateTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PpdResolutionStateTest);
+};
+
+TEST_F(PpdResolutionStateTest, TestDefaultState) {
+  PpdResolutionState ppd_resolution_state_;
+
+  EXPECT_TRUE(ppd_resolution_state_.IsInflight());
+  EXPECT_FALSE(ppd_resolution_state_.WasResolutionSuccessful());
+}
+
+TEST_F(PpdResolutionStateTest, TestMarkPpdResolutionSucessful) {
+  PpdResolutionState ppd_resolution_state_;
+
+  const std::string expected_make_and_model = "printer_make_model";
+  Printer::PpdReference ppd_ref;
+  ppd_ref.effective_make_and_model = expected_make_and_model;
+
+  ppd_resolution_state_.MarkResolutionSuccessful(ppd_ref);
+
+  EXPECT_FALSE(ppd_resolution_state_.IsInflight());
+  EXPECT_TRUE(ppd_resolution_state_.WasResolutionSuccessful());
+
+  const Printer::PpdReference ref = ppd_resolution_state_.GetPpdReference();
+
+  EXPECT_EQ(expected_make_and_model, ref.effective_make_and_model);
+  EXPECT_TRUE(ref.user_supplied_ppd_url.empty());
+  EXPECT_FALSE(ref.autoconf);
+}
+
+TEST_F(PpdResolutionStateTest, TestMarkResolutionFailedAndSetUsbManufacturer) {
+  PpdResolutionState ppd_resolution_state_;
+
+  ppd_resolution_state_.MarkResolutionFailed();
+
+  EXPECT_FALSE(ppd_resolution_state_.IsInflight());
+  EXPECT_FALSE(ppd_resolution_state_.WasResolutionSuccessful());
+
+  const std::string expected_usb_manufacturer = "Hewlett-Packard";
+  ppd_resolution_state_.SetUsbManufacturer(expected_usb_manufacturer);
+
+  EXPECT_EQ(expected_usb_manufacturer,
+            ppd_resolution_state_.GetUsbManufacturer());
+}
+
+}  // namespace
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/system/timezone_resolver_manager.cc b/chrome/browser/chromeos/system/timezone_resolver_manager.cc
index 1430c3f..c01ff5e 100644
--- a/chrome/browser/chromeos/system/timezone_resolver_manager.cc
+++ b/chrome/browser/chromeos/system/timezone_resolver_manager.cc
@@ -33,11 +33,6 @@
 // SystemTimezoneAutomaticDetectionPolicy.
 // Returns SHOULD_* if timezone resolver status is controlled by this policy.
 ServiceConfiguration GetServiceConfigurationFromAutomaticDetectionPolicy() {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisableSystemTimezoneAutomaticDetectionPolicy)) {
-    return UNSPECIFIED;
-  }
-
   PrefService* local_state = g_browser_process->local_state();
   const bool is_managed = local_state->IsManagedPreference(
       prefs::kSystemTimezoneAutomaticDetectionPolicy);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 2cbdfb6..577e0bf 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -572,11 +572,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "disable-system-timezone-automatic-detection",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "disable-tablet-splitview",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d5264ed..7bcbc99 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3072,11 +3072,6 @@
     "Always rely on implicit syncrhonization between GPU and display "
     "controller instead of using dma-fences explcitily when available.";
 
-const char kDisableSystemTimezoneAutomaticDetectionName[] =
-    "SystemTimezoneAutomaticDetection policy support";
-const char kDisableSystemTimezoneAutomaticDetectionDescription[] =
-    "Disable system timezone automatic detection device policy.";
-
 const char kDisableTabletAutohideTitlebarsName[] =
     "Disable autohide titlebars in tablet mode";
 const char kDisableTabletAutohideTitlebarsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index b9c2c1a..006d4e7 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1848,9 +1848,6 @@
 extern const char kDisableExplicitDmaFencesName[];
 extern const char kDisableExplicitDmaFencesDescription[];
 
-extern const char kDisableSystemTimezoneAutomaticDetectionName[];
-extern const char kDisableSystemTimezoneAutomaticDetectionDescription[];
-
 extern const char kDisableTabletAutohideTitlebarsName[];
 extern const char kDisableTabletAutohideTitlebarsDescription[];
 
diff --git a/chrome/browser/notifications/notification_permission_context.h b/chrome/browser/notifications/notification_permission_context.h
index bdec582..23200bdc 100644
--- a/chrome/browser/notifications/notification_permission_context.h
+++ b/chrome/browser/notifications/notification_permission_context.h
@@ -36,6 +36,11 @@
 // CONTENT_SETTING_BLOCK will be returned to reflect the fact that permission
 // cannot be requested.
 //
+// When the user rejects a notification permission request, the WebContents will
+// be prevented from requesting the permission again (regardless of origin)
+// until a user-initiated navigation occurs. This stops users from being locked
+// in to cross-origin request loops that may be hard to escape from.
+//
 // ANDROID O+
 //
 //     On Android O and beyond, notification channels will be used for storing
diff --git a/chrome/browser/permissions/permission_request_manager.cc b/chrome/browser/permissions/permission_request_manager.cc
index 3345fe7..d08b280 100644
--- a/chrome/browser/permissions/permission_request_manager.cc
+++ b/chrome/browser/permissions/permission_request_manager.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/containers/circular_deque.h"
+#include "base/feature_list.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/string16.h"
@@ -19,6 +20,7 @@
 #include "chrome/browser/permissions/permission_uma_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/permission_bubble/permission_prompt.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -95,6 +97,17 @@
     return;
   }
 
+  if (is_notification_prompt_cooldown_active_ &&
+      request->GetContentSettingsType() ==
+          CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
+    // Short-circuit by canceling rather than denying to avoid creating a large
+    // number of content setting exceptions on Desktop / disabled notification
+    // channels on Android.
+    request->Cancelled();
+    request->RequestFinished();
+    return;
+  }
+
   // TODO(tsergeant): change the UMA to no longer mention bubbles.
   base::RecordAction(base::UserMetricsAction("PermissionBubbleRequest"));
 
@@ -155,6 +168,25 @@
   return nullptr;
 }
 
+void PermissionRequestManager::DidStartNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsInMainFrame() ||
+      navigation_handle->IsSameDocument()) {
+    return;
+  }
+
+  // Cooldown lasts until the next user-initiated navigation, which is defined
+  // as either a renderer-initiated navigation with a user gesture, or a
+  // browser-initiated navigation.
+  //
+  // TODO(crbug.com/952347): This check has to be done at DidStartNavigation
+  // time, the HasUserGesture state is lost by the time the navigation commits.
+  if (!navigation_handle->IsRendererInitiated() ||
+      navigation_handle->HasUserGesture()) {
+    is_notification_prompt_cooldown_active_ = false;
+  }
+}
+
 void PermissionRequestManager::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   if (!navigation_handle->IsInMainFrame() ||
@@ -262,6 +294,20 @@
 
 void PermissionRequestManager::Deny() {
   DCHECK(view_);
+
+  // Suppress any further prompts in this WebContents, from any origin, until
+  // there is a user-initiated navigation. This stops users from getting trapped
+  // in request loops where the website automatically navigates cross-origin
+  // (e.g. to another subdomain) to be able to prompt again after a rejection.
+  if (base::FeatureList::IsEnabled(
+          features::kBlockRepeatedNotificationPermissionPrompts) &&
+      std::any_of(requests_.begin(), requests_.end(), [](const auto* request) {
+        return request->GetContentSettingsType() ==
+               CONTENT_SETTINGS_TYPE_NOTIFICATIONS;
+      })) {
+    is_notification_prompt_cooldown_active_ = true;
+  }
+
   std::vector<PermissionRequest*>::iterator requests_iter;
   for (requests_iter = requests_.begin();
        requests_iter != requests_.end();
diff --git a/chrome/browser/permissions/permission_request_manager.h b/chrome/browser/permissions/permission_request_manager.h
index 1c5171cc..31a29ba 100644
--- a/chrome/browser/permissions/permission_request_manager.h
+++ b/chrome/browser/permissions/permission_request_manager.h
@@ -104,6 +104,8 @@
   explicit PermissionRequestManager(content::WebContents* web_contents);
 
   // WebContentsObserver:
+  void DidStartNavigation(
+      content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
   void DocumentOnLoadCompletedInMainFrame() override;
@@ -183,6 +185,10 @@
   base::ObserverList<Observer>::Unchecked observer_list_;
   AutoResponseType auto_response_for_test_;
 
+  // Suppress notification permission prompts in this tab, regardless of the
+  // origin requesting the permission.
+  bool is_notification_prompt_cooldown_active_ = false;
+
   base::WeakPtrFactory<PermissionRequestManager> weak_factory_;
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
diff --git a/chrome/browser/permissions/permission_request_manager_browsertest.cc b/chrome/browser/permissions/permission_request_manager_browsertest.cc
index 4ffc322..9abe958 100644
--- a/chrome/browser/permissions/permission_request_manager_browsertest.cc
+++ b/chrome/browser/permissions/permission_request_manager_browsertest.cc
@@ -38,6 +38,7 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "media/base/media_switches.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
 namespace {
@@ -57,6 +58,10 @@
     PermissionRequestManager* manager = GetPermissionRequestManager();
     mock_permission_prompt_factory_.reset(
         new MockPermissionPromptFactory(manager));
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kBlockRepeatedNotificationPermissionPrompts);
+
+    host_resolver()->AddRule("*", "127.0.0.1");
   }
 
   void TearDownOnMainThread() override {
@@ -89,7 +94,73 @@
                                            kPermissionsKillSwitchTestGroup);
   }
 
+  void TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
+      void navigation_action(content::WebContents*, const GURL&),
+      bool expect_cooldown) {
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    const GURL kInitialURL = embedded_test_server()->GetURL(
+        "a.localhost", "/permissions/killswitch_tester.html");
+    const GURL kSecondURL = embedded_test_server()->GetURL(
+        "b.localhost", "/permissions/killswitch_tester.html");
+    const GURL kThirdURL = embedded_test_server()->GetURL(
+        "c.localhost", "/permissions/killswitch_tester.html");
+
+    ui_test_utils::NavigateToURL(browser(), kInitialURL);
+    bubble_factory()->ResetCounts();
+    bubble_factory()->set_response_type(
+        PermissionRequestManager::AutoResponseType::DENY_ALL);
+
+    // Simulate a notification permission request that is denied by the user.
+    std::string result;
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+        web_contents, "requestNotification();", &result));
+    ASSERT_EQ(1, bubble_factory()->show_count());
+    ASSERT_EQ(1, bubble_factory()->TotalRequestCount());
+    ASSERT_EQ("denied", result);
+
+    // In response, simulate the website automatically triggering a
+    // renderer-initiated cross-origin navigation without user gesture.
+    content::TestNavigationObserver navigation_observer(web_contents);
+    ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(
+        web_contents, "window.location = \"" + kSecondURL.spec() + "\";"));
+    navigation_observer.Wait();
+
+    bubble_factory()->ResetCounts();
+    bubble_factory()->set_response_type(
+        PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
+
+    // Request the notification permission again from a different origin.
+    // Cross-origin permission prompt cool-down should be in effect.
+    ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+        web_contents, "requestNotification();", &result));
+    ASSERT_EQ(0, bubble_factory()->show_count());
+    ASSERT_EQ(0, bubble_factory()->TotalRequestCount());
+    ASSERT_EQ("default", result);
+
+    // Now try one of a number other kinds of navigations, and request the
+    // notification permission again.
+    navigation_action(web_contents, kThirdURL);
+    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+        web_contents, "requestNotification();", &result));
+
+    // Cross-origin prompt cool-down may or may not be in effect anymore
+    // depending on the type of navigation.
+    if (expect_cooldown) {
+      EXPECT_EQ(0, bubble_factory()->show_count());
+      EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
+      EXPECT_EQ("default", result);
+    } else {
+      EXPECT_EQ(1, bubble_factory()->show_count());
+      EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
+      EXPECT_EQ("granted", result);
+    }
+  }
+
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<MockPermissionPromptFactory> mock_permission_prompt_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PermissionRequestManagerBrowserTest);
@@ -451,6 +522,112 @@
   EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
 }
 
+// Regression test for crbug.com/900997.
+IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
+                       CrossOriginPromptCooldown) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  const GURL kInitialURL = embedded_test_server()->GetURL(
+      "a.localhost", "/permissions/killswitch_tester.html");
+  const GURL kSecondURL = embedded_test_server()->GetURL(
+      "b.localhost", "/permissions/killswitch_tester.html");
+
+  ui_test_utils::NavigateToURL(browser(), kInitialURL);
+  bubble_factory()->set_response_type(
+      PermissionRequestManager::AutoResponseType::DENY_ALL);
+
+  // Simulate a notification permission request that is denied by the user.
+  std::string result;
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      web_contents, "requestNotification();", &result));
+  ASSERT_EQ(1, bubble_factory()->show_count());
+  ASSERT_EQ(1, bubble_factory()->TotalRequestCount());
+  ASSERT_EQ("denied", result);
+
+  // In response, simulate the website automatically triggering a
+  // renderer-initiated cross-origin navigation without user gesture.
+  content::TestNavigationObserver navigation_observer(web_contents);
+  ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(
+      web_contents, "window.location = \"" + kSecondURL.spec() + "\";"));
+  navigation_observer.Wait();
+
+  // Request the notification permission again from a different origin.
+  // Cross-origin permission prompt cool-down should be in effect.
+  bubble_factory()->ResetCounts();
+  bubble_factory()->set_response_type(
+      PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
+  ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      web_contents, "requestNotification();", &result));
+  EXPECT_EQ(0, bubble_factory()->show_count());
+  EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
+  EXPECT_EQ("default", result);
+}
+
+// Regression test for crbug.com/900997.
+IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
+                       CooldownEndsOnUserInitiatedReload) {
+  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
+      [](content::WebContents* web_contents, const GURL& unused_url) {
+        content::NavigationController& controller =
+            web_contents->GetController();
+        controller.Reload(content::ReloadType::NORMAL, false);
+        EXPECT_TRUE(content::WaitForLoadStop(web_contents));
+      },
+      false /* expect_cooldown */);
+}
+
+// Regression test for crbug.com/900997.
+IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
+                       CooldownEndsOnBrowserInitiateNavigation) {
+  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
+      [](content::WebContents* web_contents, const GURL& url) {
+        EXPECT_TRUE(content::NavigateToURL(web_contents, url));
+      },
+      false /* expect_cooldown */);
+}
+
+// Regression test for crbug.com/900997.
+IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
+                       CooldownEndsOnRendererInitiateNavigationWithGesture) {
+  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
+      [](content::WebContents* web_contents, const GURL& url) {
+        content::TestNavigationObserver navigation_observer(web_contents);
+        EXPECT_TRUE(content::ExecuteScript(
+            web_contents, "window.location = \"" + url.spec() + "\";"));
+        navigation_observer.Wait();
+      },
+      false /* expect_cooldown */);
+}
+
+// Regression test for crbug.com/900997.
+IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
+                       CooldownOutlastsRendererInitiatedReload) {
+  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
+      [](content::WebContents* web_contents, const GURL& unused_url) {
+        content::TestNavigationObserver navigation_observer(web_contents);
+        EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
+            web_contents, "window.location.reload();"));
+        navigation_observer.Wait();
+      },
+      true /* expect_cooldown */);
+}
+
+// Regression test for crbug.com/900997.
+IN_PROC_BROWSER_TEST_F(
+    PermissionRequestManagerBrowserTest,
+    CooldownOutlastsRendererInitiateNavigationWithoutGesture) {
+  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
+      [](content::WebContents* web_contents, const GURL& url) {
+        content::TestNavigationObserver navigation_observer(web_contents);
+        EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
+            web_contents, "window.location = \"" + url.spec() + "\";"));
+        navigation_observer.Wait();
+      },
+      true /* expect_cooldown */);
+}
+
 // Bubble requests should not be shown when the killswitch is on.
 IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                        KillSwitchNotifications) {
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index 0c51bfdb..3daa254 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -39,6 +39,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
@@ -1413,6 +1415,77 @@
   WaitForPingback();
 }
 
+class TestDataReductionProxyPingbackClient
+    : public data_reduction_proxy::DataReductionProxyPingbackClient {
+ public:
+  void WaitForPingback() {
+    base::RunLoop run_loop;
+    wait_for_pingback_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
+  data_reduction_proxy::DataReductionProxyData* data() { return data_.get(); }
+
+ private:
+  void SendPingback(
+      const data_reduction_proxy::DataReductionProxyData& data,
+      const data_reduction_proxy::DataReductionProxyPageLoadTiming& timing)
+      override {
+    data_ = data.DeepCopy();
+    if (wait_for_pingback_closure_)
+      std::move(wait_for_pingback_closure_).Run();
+  }
+
+  void SetPingbackReportingFraction(
+      float pingback_reporting_fraction) override {}
+
+  base::OnceClosure wait_for_pingback_closure_;
+  std::unique_ptr<data_reduction_proxy::DataReductionProxyData> data_;
+};
+
+IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
+                       DISABLE_ON_WIN_MAC_CHROMESOS(PingbackContent)) {
+  TestDataReductionProxyPingbackClient* pingback_client =
+      new TestDataReductionProxyPingbackClient();
+  DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+      browser()->profile())
+      ->data_reduction_proxy_service()
+      ->SetPingbackClientForTesting(pingback_client);
+
+  ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
+  VerifyPreviewLoaded();
+
+  // Starting a new page load will send a pingback for the previous page load.
+  GetWebContents()->GetController().Reload(content::ReloadType::NORMAL, false);
+  pingback_client->WaitForPingback();
+
+  data_reduction_proxy::DataReductionProxyData* data = pingback_client->data();
+  EXPECT_TRUE(data->used_data_reduction_proxy());
+  EXPECT_TRUE(data->lite_page_received());
+  EXPECT_FALSE(data->lofi_policy_received());
+  EXPECT_FALSE(data->lofi_received());
+  EXPECT_FALSE(data->was_cached_data_reduction_proxy_response());
+
+  // TODO(crbug.com/952523): Fix and remove this early return.
+  if (GetParam())
+    return;
+
+  PreviewsUITabHelper* ui_tab_helper =
+      PreviewsUITabHelper::FromWebContents(GetWebContents());
+  previews::PreviewsUserData* previews_data =
+      ui_tab_helper->previews_user_data();
+
+  EXPECT_EQ(data->session_key(),
+            previews_data->server_lite_page_info()->drp_session_key);
+
+  // TODO(crbug.com/952523): The page id is being incremented for every
+  // restarted navigation. Fix and remove the early return.
+  return;
+
+  EXPECT_EQ(data->page_id().value(),
+            previews_data->server_lite_page_info()->page_id);
+}
+
 class PreviewsLitePageServerTimeoutBrowserTest
     : public PreviewsLitePageServerBrowserTest {
  public:
diff --git a/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc b/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
index c0e90ab..b4990a7 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
@@ -62,6 +62,10 @@
         "Line Because Life Would Be Just Too Simple That Way");
   }
 
+  base::Optional<base::string16> GetAdditionalDescription() const override {
+    return base::ASCIIToUTF16("More description text.");
+  }
+
   ui::MenuModel* GetOtherTransportsMenuModel() override { return nullptr; }
 
   void OnBack() override {}
@@ -105,7 +109,8 @@
     content::WebContents* const web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
 
-    auto dialog_model = std::make_unique<AuthenticatorRequestDialogModel>();
+    auto dialog_model = std::make_unique<AuthenticatorRequestDialogModel>(
+        /*relying_party_id=*/"example.com");
     dialog_model->SetCurrentStep(
         AuthenticatorRequestDialogModel::Step::kTimedOut);
     AuthenticatorRequestDialogView* dialog =
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
index 697959c..1f6fd9a 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
@@ -168,6 +168,18 @@
   description_label->SetMultiLine(true);
   description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   label_container->AddChildView(description_label.release());
+
+  base::Optional<base::string16> additional_desciption =
+      model()->GetAdditionalDescription();
+  if (additional_desciption) {
+    auto label = std::make_unique<views::Label>(
+        std::move(*additional_desciption),
+        views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT);
+    label->SetMultiLine(true);
+    label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    label_container->AddChildView(label.release());
+  }
+
   contents->AddChildView(label_container.release());
 
   std::unique_ptr<views::View> step_specific_content =
diff --git a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
index 2a63174..0bfa486 100644
--- a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
+++ b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
@@ -28,10 +28,10 @@
     // https://crbug.com/893292.
     set_should_verify_dialog_bounds(false);
 
-    auto model = std::make_unique<AuthenticatorRequestDialogModel>();
+    auto model = std::make_unique<AuthenticatorRequestDialogModel>(
+        /*relying_party_id=*/"example.com");
     ::device::FidoRequestHandlerBase::TransportAvailabilityInfo
         transport_availability;
-    transport_availability.rp_id = "example.com";
     transport_availability.available_transports = {
         AuthenticatorTransport::kBluetoothLowEnergy,
         AuthenticatorTransport::kUsbHumanInterfaceDevice,
diff --git a/chrome/browser/ui/webauthn/authenticator_request_sheet_model.h b/chrome/browser/ui/webauthn/authenticator_request_sheet_model.h
index d7aac0e..a67c81b 100644
--- a/chrome/browser/ui/webauthn/authenticator_request_sheet_model.h
+++ b/chrome/browser/ui/webauthn/authenticator_request_sheet_model.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/optional.h"
 #include "base/strings/string16.h"
 
 namespace gfx {
@@ -57,6 +58,7 @@
       ImageColorScheme color_scheme) const = 0;
   virtual base::string16 GetStepTitle() const = 0;
   virtual base::string16 GetStepDescription() const = 0;
+  virtual base::Optional<base::string16> GetAdditionalDescription() const = 0;
 
   virtual ui::MenuModel* GetOtherTransportsMenuModel() = 0;
 
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index d52834f..a8ccee5 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -24,13 +24,14 @@
 #include "url/gurl.h"
 
 namespace {
+
 base::string16 GetRelyingPartyIdString(
     AuthenticatorRequestDialogModel* dialog_model) {
   static constexpr char kRpIdUrlPrefix[] = "https://";
   // The preferred width of medium snap point modal dialog view is 448 dp, but
   // we leave some room for padding between the text and the modal views.
   static constexpr int kDialogWidth = 300;
-  const auto& rp_id = dialog_model->transport_availability()->rp_id;
+  const auto& rp_id = dialog_model->relying_party_id();
   DCHECK(!rp_id.empty());
   GURL rp_id_url(kRpIdUrlPrefix + rp_id);
   auto max_static_string_length = gfx::GetStringWidthF(
@@ -39,6 +40,18 @@
   return url_formatter::ElideHost(rp_id_url, gfx::FontList(),
                                   kDialogWidth - max_static_string_length);
 }
+
+// Possibly returns a resident key warning if the model indicates that it's
+// needed.
+base::Optional<base::string16> PossibleResidentKeyWarning(
+    AuthenticatorRequestDialogModel* dialog_model) {
+  if (dialog_model->might_create_resident_credential()) {
+    return l10n_util::GetStringFUTF16(IDS_WEBAUTHN_RESIDENT_KEY_PRIVACY,
+                                      GetRelyingPartyIdString(dialog_model));
+  }
+  return base::nullopt;
+}
+
 }  // namespace
 
 // AuthenticatorSheetModelBase ------------------------------------------------
@@ -92,6 +105,11 @@
   return base::string16();
 }
 
+base::Optional<base::string16>
+AuthenticatorSheetModelBase::GetAdditionalDescription() const {
+  return base::nullopt;
+}
+
 ui::MenuModel* AuthenticatorSheetModelBase::GetOtherTransportsMenuModel() {
   return nullptr;
 }
@@ -215,6 +233,11 @@
   return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USB_ACTIVATE_DESCRIPTION);
 }
 
+base::Optional<base::string16>
+AuthenticatorInsertAndActivateUsbSheetModel::GetAdditionalDescription() const {
+  return PossibleResidentKeyWarning(dialog_model());
+}
+
 ui::MenuModel*
 AuthenticatorInsertAndActivateUsbSheetModel::GetOtherTransportsMenuModel() {
   return other_transports_menu_model_.get();
@@ -619,6 +642,11 @@
   return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLE_ACTIVATE_DESCRIPTION);
 }
 
+base::Optional<base::string16>
+AuthenticatorBleActivateSheetModel::GetAdditionalDescription() const {
+  return PossibleResidentKeyWarning(dialog_model());
+}
+
 ui::MenuModel*
 AuthenticatorBleActivateSheetModel::GetOtherTransportsMenuModel() {
   return other_transports_menu_model_.get();
@@ -887,6 +915,11 @@
   return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_TAP_AGAIN_DESCRIPTION);
 }
 
+base::Optional<base::string16>
+AuthenticatorClientPinTapAgainSheetModel::GetAdditionalDescription() const {
+  return PossibleResidentKeyWarning(dialog_model());
+}
+
 // AuthenticatorGenericErrorSheetModel -----------------------------------
 
 // static
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index b05c7fe..d69247d 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -46,6 +46,7 @@
   bool IsAcceptButtonVisible() const override;
   bool IsAcceptButtonEnabled() const override;
   base::string16 GetAcceptButtonLabel() const override;
+  base::Optional<base::string16> GetAdditionalDescription() const override;
   ui::MenuModel* GetOtherTransportsMenuModel() override;
   void OnBack() override;
   void OnAccept() override;
@@ -111,6 +112,7 @@
       ImageColorScheme color_scheme) const override;
   base::string16 GetStepTitle() const override;
   base::string16 GetStepDescription() const override;
+  base::Optional<base::string16> GetAdditionalDescription() const override;
   ui::MenuModel* GetOtherTransportsMenuModel() override;
 
   std::unique_ptr<OtherTransportsMenuModel> other_transports_menu_model_;
@@ -318,6 +320,7 @@
       ImageColorScheme color_scheme) const override;
   base::string16 GetStepTitle() const override;
   base::string16 GetStepDescription() const override;
+  base::Optional<base::string16> GetAdditionalDescription() const override;
   ui::MenuModel* GetOtherTransportsMenuModel() override;
 
   std::unique_ptr<OtherTransportsMenuModel> other_transports_menu_model_;
@@ -418,6 +421,7 @@
       ImageColorScheme color_scheme) const override;
   base::string16 GetStepTitle() const override;
   base::string16 GetStepDescription() const override;
+  base::Optional<base::string16> GetAdditionalDescription() const override;
 };
 
 // Generic error dialog that can only be dismissed. Backwards navigation is
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index f2ea054..228d315 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -231,9 +231,7 @@
 #endif
 
 #if defined(FULL_SAFE_BROWSING)
-#include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
 #include "chrome/browser/ui/webui/reset_password/reset_password_ui.h"
-#include "components/safe_browsing/features.h"
 #endif
 
 using content::WebUI;
@@ -702,13 +700,7 @@
   }
 
 #if defined(FULL_SAFE_BROWSING)
-  bool enable_reset_password =
-      base::FeatureList::IsEnabled(
-          safe_browsing::kForceEnableResetPasswordWebUI) ||
-      safe_browsing::ChromePasswordProtectionService::
-          IsPasswordReuseProtectionConfigured(profile);
-  if (url.host_piece() == chrome::kChromeUIResetPasswordHost &&
-      enable_reset_password) {
+  if (url.host_piece() == chrome::kChromeUIResetPasswordHost) {
     return &NewWebUI<ResetPasswordUI>;
   }
 #endif
diff --git a/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc b/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
index 2423ebcd..c3a3202 100644
--- a/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
@@ -26,19 +26,9 @@
 
 namespace {
 
-// Returns whether the system time zone automatic detection policy is disabled
-// by a flag.
-bool IsSystemTimezoneAutomaticDetectionPolicyFlagDisabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableSystemTimezoneAutomaticDetectionPolicy);
-}
-
 // Returns whether the system's automatic time zone detection setting is
 // managed, which may override the user's setting.
 bool IsSystemTimezoneAutomaticDetectionManaged() {
-  if (IsSystemTimezoneAutomaticDetectionPolicyFlagDisabled())
-    return false;
-
   return g_browser_process->local_state()->IsManagedPreference(
       prefs::kSystemTimezoneAutomaticDetectionPolicy);
 }
@@ -112,9 +102,6 @@
           base::Bind(&DateTimeHandler::NotifyTimezoneAutomaticDetectionPolicy,
                      weak_ptr_factory_.GetWeakPtr()));
 
-  if (IsSystemTimezoneAutomaticDetectionPolicyFlagDisabled())
-    return;
-
   // The auto-detection policy can force auto-detection on or off.
   local_state_pref_change_registrar_.Init(g_browser_process->local_state());
   local_state_pref_change_registrar_.Add(
@@ -126,9 +113,7 @@
 void DateTimeHandler::OnJavascriptDisallowed() {
   scoped_observer_.RemoveAll();
   system_timezone_policy_subscription_.reset();
-
-  if (!IsSystemTimezoneAutomaticDetectionPolicyFlagDisabled())
-    local_state_pref_change_registrar_.RemoveAll();
+  local_state_pref_change_registrar_.RemoveAll();
 }
 
 void DateTimeHandler::HandleDateTimePageReady(const base::ListValue* args) {
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index ae9e468..f9aaedf9a 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -81,8 +81,9 @@
 
 }  // namespace
 
-AuthenticatorRequestDialogModel::AuthenticatorRequestDialogModel()
-    : weak_factory_(this) {}
+AuthenticatorRequestDialogModel::AuthenticatorRequestDialogModel(
+    const std::string& relying_party_id)
+    : relying_party_id_(relying_party_id), weak_factory_(this) {}
 
 AuthenticatorRequestDialogModel::~AuthenticatorRequestDialogModel() {
   for (auto& observer : observers_)
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index 3915e28..aa45f94 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -124,7 +124,7 @@
     virtual void OnCancelRequest() {}
   };
 
-  AuthenticatorRequestDialogModel();
+  AuthenticatorRequestDialogModel(const std::string& relying_party_id);
   ~AuthenticatorRequestDialogModel();
 
   void SetCurrentStep(Step step);
@@ -384,10 +384,23 @@
     incognito_mode_ = incognito_mode;
   }
 
+  bool might_create_resident_credential() const {
+    return might_create_resident_credential_;
+  }
+
+  void set_might_create_resident_credential(bool v) {
+    might_create_resident_credential_ = v;
+  }
+
+  const std::string& relying_party_id() const { return relying_party_id_; }
+
  private:
   void DispatchRequestAsync(AuthenticatorReference* authenticator);
   void DispatchRequestAsyncInternal(const std::string& authenticator_id);
 
+  // relying_party_id is the RP ID from Webauthn, essentially a domain name.
+  const std::string relying_party_id_;
+
   // The current step of the request UX flow that is currently shown.
   Step current_step_ = Step::kNotStarted;
 
@@ -428,6 +441,12 @@
 
   base::OnceCallback<void(bool)> attestation_callback_;
 
+  // might_create_resident_credential_ records whether activating an
+  // authenticator may cause a resident credential to be created. A resident
+  // credential may be discovered by someone with physical access to the
+  // authenticator and thus has privacy implications.
+  bool might_create_resident_credential_ = false;
+
   // responses_ contains possible accounts to select between.
   std::vector<device::AuthenticatorGetAssertionResponse> responses_;
   base::OnceCallback<void(device::AuthenticatorGetAssertionResponse)>
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
index f10df0b..b348daf 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
@@ -314,7 +314,7 @@
       transports_info.win_native_api_authenticator_id = "some_authenticator_id";
     }
 
-    AuthenticatorRequestDialogModel model;
+    AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
     model.StartFlow(std::move(transports_info), test_case.last_used_transport,
                     &test_paired_device_list_);
     EXPECT_EQ(test_case.expected_first_step, model.current_step());
@@ -331,7 +331,7 @@
   TransportAvailabilityInfo transports_info;
   transports_info.available_transports = kAllTransports;
 
-  AuthenticatorRequestDialogModel model;
+  AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
   model.StartFlow(std::move(transports_info), base::nullopt,
                   &test_paired_device_list_);
   EXPECT_THAT(model.available_transports(),
@@ -345,7 +345,7 @@
 
 TEST_F(AuthenticatorRequestDialogModelTest, NoAvailableTransports) {
   testing::StrictMock<MockDialogModelObserver> mock_observer;
-  AuthenticatorRequestDialogModel model;
+  AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
   model.AddObserver(&mock_observer);
 
   EXPECT_CALL(mock_observer, OnStepTransition());
@@ -380,7 +380,7 @@
 
   for (const auto& test_case : kTestCases) {
     testing::StrictMock<MockDialogModelObserver> mock_observer;
-    AuthenticatorRequestDialogModel model;
+    AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
     model.AddObserver(&mock_observer);
 
     TransportAvailabilityInfo transports_info;
@@ -426,7 +426,7 @@
     transports_info.is_ble_powered = true;
 
     BluetoothAdapterPowerOnCallbackReceiver power_receiver;
-    AuthenticatorRequestDialogModel model;
+    AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
     model.SetBluetoothAdapterPowerOnCallback(power_receiver.GetCallback());
     model.StartFlow(std::move(transports_info), base::nullopt,
                     test_case.paired_device_address_list);
@@ -453,7 +453,7 @@
     transports_info.is_ble_powered = true;
 
     BluetoothAdapterPowerOnCallbackReceiver power_receiver;
-    AuthenticatorRequestDialogModel model;
+    AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
     model.SetBluetoothAdapterPowerOnCallback(power_receiver.GetCallback());
     model.StartFlow(std::move(transports_info), base::nullopt,
                     &test_paired_device_list_);
@@ -482,7 +482,7 @@
 
     testing::NiceMock<MockDialogModelObserver> mock_observer;
     BluetoothAdapterPowerOnCallbackReceiver power_receiver;
-    AuthenticatorRequestDialogModel model;
+    AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
     model.AddObserver(&mock_observer);
     model.SetBluetoothAdapterPowerOnCallback(power_receiver.GetCallback());
     model.StartFlow(std::move(transports_info), base::nullopt,
@@ -523,7 +523,7 @@
     transports_info.is_ble_powered = false;
 
     BluetoothAdapterPowerOnCallbackReceiver power_receiver;
-    AuthenticatorRequestDialogModel model;
+    AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
     model.SetBluetoothAdapterPowerOnCallback(power_receiver.GetCallback());
     model.StartFlow(std::move(transports_info), base::nullopt,
                     &test_paired_device_list_);
@@ -553,7 +553,7 @@
       AuthenticatorTransport::kUsbHumanInterfaceDevice};
 
   int num_called = 0;
-  AuthenticatorRequestDialogModel model;
+  AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
   model.SetRequestCallback(base::BindRepeating(
       [](int* i, const std::string& authenticator_id) { ++(*i); },
       &num_called));
@@ -599,7 +599,7 @@
   transports_info.win_native_api_authenticator_id = kWinAuthenticatorId;
 
   std::vector<std::string> dispatched_authenticator_ids;
-  AuthenticatorRequestDialogModel model;
+  AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
   model.SetRequestCallback(base::BindRepeating(
       [](std::vector<std::string>* ids, const std::string& authenticator_id) {
         ids->push_back(authenticator_id);
diff --git a/chrome/browser/webauthn/authenticator_request_scheduler.cc b/chrome/browser/webauthn/authenticator_request_scheduler.cc
index fb49996..0bee466 100644
--- a/chrome/browser/webauthn/authenticator_request_scheduler.cc
+++ b/chrome/browser/webauthn/authenticator_request_scheduler.cc
@@ -44,7 +44,8 @@
 // static
 std::unique_ptr<ChromeAuthenticatorRequestDelegate>
 AuthenticatorRequestScheduler::CreateRequestDelegate(
-    content::RenderFrameHost* render_frame_host) {
+    content::RenderFrameHost* render_frame_host,
+    const std::string& relying_party_id) {
   auto* const web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host);
   auto* const active_request_holder =
@@ -53,8 +54,8 @@
   if (active_request_holder->request())
     return nullptr;
 
-  auto request =
-      std::make_unique<ChromeAuthenticatorRequestDelegate>(render_frame_host);
+  auto request = std::make_unique<ChromeAuthenticatorRequestDelegate>(
+      render_frame_host, relying_party_id);
   active_request_holder->request() = request->AsWeakPtr();
   return request;
 }
diff --git a/chrome/browser/webauthn/authenticator_request_scheduler.h b/chrome/browser/webauthn/authenticator_request_scheduler.h
index f70ed3e..8b3d1d9 100644
--- a/chrome/browser/webauthn/authenticator_request_scheduler.h
+++ b/chrome/browser/webauthn/authenticator_request_scheduler.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_WEBAUTHN_AUTHENTICATOR_REQUEST_SCHEDULER_H_
 
 #include <memory>
+#include <string>
 
 #include "base/macros.h"
 
@@ -30,7 +31,8 @@
   // Returns a nullptr delegate if there is already an ongoing request in the
   // same WebContents.
   static std::unique_ptr<ChromeAuthenticatorRequestDelegate>
-  CreateRequestDelegate(content::RenderFrameHost* render_frame_host);
+  CreateRequestDelegate(content::RenderFrameHost* render_frame_host,
+                        const std::string& relying_party_id);
 
   // Returns the current request delegate associated to the |web_contents| or
   // nullptr if there is none.
diff --git a/chrome/browser/webauthn/authenticator_request_scheduler_unittest.cc b/chrome/browser/webauthn/authenticator_request_scheduler_unittest.cc
index 072ebf98..796a2f1 100644
--- a/chrome/browser/webauthn/authenticator_request_scheduler_unittest.cc
+++ b/chrome/browser/webauthn/authenticator_request_scheduler_unittest.cc
@@ -20,26 +20,28 @@
 
 TEST_F(AuthenticatorRequestSchedulerTest,
        SingleWebContents_AtMostOneSimultaneousRequest) {
+  const std::string rp_id = "example.com";
   auto first_request = AuthenticatorRequestScheduler::CreateRequestDelegate(
-      web_contents()->GetMainFrame());
+      web_contents()->GetMainFrame(), rp_id);
   ASSERT_TRUE(first_request);
 
   ASSERT_FALSE(AuthenticatorRequestScheduler::CreateRequestDelegate(
-      web_contents()->GetMainFrame()));
+      web_contents()->GetMainFrame(), rp_id));
 
   first_request.reset();
   ASSERT_TRUE(AuthenticatorRequestScheduler::CreateRequestDelegate(
-      web_contents()->GetMainFrame()));
+      web_contents()->GetMainFrame(), rp_id));
 }
 
 TEST_F(AuthenticatorRequestSchedulerTest,
        TwoWebContents_TwoSimultaneousRequests) {
+  const std::string rp_id = "example.com";
   auto first_request = AuthenticatorRequestScheduler::CreateRequestDelegate(
-      web_contents()->GetMainFrame());
+      web_contents()->GetMainFrame(), rp_id);
 
   auto second_web_contents = CreateTestWebContents();
   auto second_request = AuthenticatorRequestScheduler::CreateRequestDelegate(
-      second_web_contents->GetMainFrame());
+      second_web_contents->GetMainFrame(), rp_id);
 
   ASSERT_TRUE(first_request);
   ASSERT_TRUE(second_request);
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index f35e0f0..54ea6d3e 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -87,8 +87,11 @@
 }
 
 ChromeAuthenticatorRequestDelegate::ChromeAuthenticatorRequestDelegate(
-    content::RenderFrameHost* render_frame_host)
-    : render_frame_host_(render_frame_host), weak_ptr_factory_(this) {}
+    content::RenderFrameHost* render_frame_host,
+    const std::string& relying_party_id)
+    : render_frame_host_(render_frame_host),
+      relying_party_id_(relying_party_id),
+      weak_ptr_factory_(this) {}
 
 ChromeAuthenticatorRequestDelegate::~ChromeAuthenticatorRequestDelegate() {
   // Currently, completion of the request is indicated by //content destroying
@@ -165,7 +168,7 @@
   cancel_callback_ = std::move(cancel_callback);
 
   transient_dialog_model_holder_ =
-      std::make_unique<AuthenticatorRequestDialogModel>();
+      std::make_unique<AuthenticatorRequestDialogModel>(relying_party_id_);
   transient_dialog_model_holder_->SetRequestCallback(request_callback);
   transient_dialog_model_holder_->SetBluetoothAdapterPowerOnCallback(
       bluetooth_adapter_power_on_callback);
@@ -441,6 +444,14 @@
       AuthenticatorRequestDialogModel::Step::kClientPinTapAgain);
 }
 
+void ChromeAuthenticatorRequestDelegate::SetMightCreateResidentCredential(
+    bool v) {
+  if (!weak_dialog_model_) {
+    return;
+  }
+  weak_dialog_model_->set_might_create_resident_credential(v);
+}
+
 void ChromeAuthenticatorRequestDelegate::OnModelDestroyed() {
   DCHECK(weak_dialog_model_);
   weak_dialog_model_ = nullptr;
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index 95d7b362..292d8c98 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -47,7 +47,8 @@
 
   // The |render_frame_host| must outlive this instance.
   explicit ChromeAuthenticatorRequestDelegate(
-      content::RenderFrameHost* render_frame_host);
+      content::RenderFrameHost* render_frame_host,
+      const std::string& relying_party_id);
   ~ChromeAuthenticatorRequestDelegate() override;
 
 #if defined(OS_MACOSX)
@@ -113,6 +114,7 @@
       base::Optional<int> attempts,
       base::OnceCallback<void(std::string)> provide_pin_cb) override;
   void FinishCollectPIN() override;
+  void SetMightCreateResidentCredential(bool v) override;
 
   // AuthenticatorRequestDialogModel::Observer:
   void OnModelDestroyed() override;
@@ -123,6 +125,7 @@
   const base::ListValue* GetPreviouslyPairedFidoBleDeviceIds() const;
 
   content::RenderFrameHost* const render_frame_host_;
+  const std::string relying_party_id_;
   AuthenticatorRequestDialogModel* weak_dialog_model_ = nullptr;
   // Holds ownership of AuthenticatorRequestDialogModel until
   // OnTransportAvailabilityEnumerated() is invoked, at which point the
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
index c724749..64e8c1b 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
@@ -15,8 +15,10 @@
 class ChromeAuthenticatorRequestDelegateTest
     : public ChromeRenderViewHostTestHarness {};
 
+static constexpr char kRelyingPartyID[] = "example.com";
+
 TEST_F(ChromeAuthenticatorRequestDelegateTest, TestTransportPrefType) {
-  ChromeAuthenticatorRequestDelegate delegate(main_rfh());
+  ChromeAuthenticatorRequestDelegate delegate(main_rfh(), kRelyingPartyID);
   EXPECT_FALSE(delegate.GetLastTransportUsed());
   delegate.UpdateLastTransportUsed(device::FidoTransportProtocol::kInternal);
   const auto transport = delegate.GetLastTransportUsed();
@@ -29,7 +31,7 @@
   static constexpr char kTestPairedDeviceAddress[] = "paired_device_address";
   static constexpr char kTestPairedDeviceAddress2[] = "paired_device_address2";
 
-  ChromeAuthenticatorRequestDelegate delegate(main_rfh());
+  ChromeAuthenticatorRequestDelegate delegate(main_rfh(), kRelyingPartyID);
 
   auto* const address_list = delegate.GetPreviouslyPairedFidoBleDeviceIds();
   ASSERT_TRUE(address_list);
@@ -73,7 +75,7 @@
 }
 
 TEST_F(ChromeAuthenticatorRequestDelegateTest, TouchIdMetadataSecret) {
-  ChromeAuthenticatorRequestDelegate delegate(main_rfh());
+  ChromeAuthenticatorRequestDelegate delegate(main_rfh(), kRelyingPartyID);
   std::string secret = TouchIdMetadataSecret(delegate);
   EXPECT_EQ(secret.size(), 32u);
   EXPECT_EQ(secret, TouchIdMetadataSecret(delegate));
@@ -83,9 +85,10 @@
        TouchIdMetadataSecret_EqualForSameProfile) {
   // Different delegates on the same BrowserContext (Profile) should return the
   // same secret.
-  EXPECT_EQ(
-      TouchIdMetadataSecret(ChromeAuthenticatorRequestDelegate(main_rfh())),
-      TouchIdMetadataSecret(ChromeAuthenticatorRequestDelegate(main_rfh())));
+  EXPECT_EQ(TouchIdMetadataSecret(ChromeAuthenticatorRequestDelegate(
+                main_rfh(), kRelyingPartyID)),
+            TouchIdMetadataSecret(ChromeAuthenticatorRequestDelegate(
+                main_rfh(), kRelyingPartyID)));
 }
 
 TEST_F(ChromeAuthenticatorRequestDelegateTest,
@@ -95,13 +98,14 @@
   auto browser_context = base::WrapUnique(CreateBrowserContext());
   auto web_contents = content::WebContentsTester::CreateTestWebContents(
       browser_context.get(), nullptr);
-  EXPECT_NE(
-      TouchIdMetadataSecret(ChromeAuthenticatorRequestDelegate(main_rfh())),
-      TouchIdMetadataSecret(
-          ChromeAuthenticatorRequestDelegate(web_contents->GetMainFrame())));
+  EXPECT_NE(TouchIdMetadataSecret(ChromeAuthenticatorRequestDelegate(
+                main_rfh(), kRelyingPartyID)),
+            TouchIdMetadataSecret(ChromeAuthenticatorRequestDelegate(
+                web_contents->GetMainFrame(), kRelyingPartyID)));
   // Ensure this second secret is actually valid.
-  EXPECT_EQ(32u, TouchIdMetadataSecret(ChromeAuthenticatorRequestDelegate(
-                                           web_contents->GetMainFrame()))
+  EXPECT_EQ(32u, TouchIdMetadataSecret(
+                     ChromeAuthenticatorRequestDelegate(
+                         web_contents->GetMainFrame(), kRelyingPartyID))
                      .size());
 }
 #endif
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 6d4a53ec..b510875 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -110,6 +110,13 @@
 const base::Feature kBlockPromptsIfIgnoredOften{
     "BlockPromptsIfIgnoredOften", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Once the user declines a notification permission prompt in a WebContents,
+// automatically dismiss subsequent prompts in the same WebContents, from any
+// origin, until the next user-initiated navigation.
+const base::Feature kBlockRepeatedNotificationPermissionPrompts{
+    "BlockRepeatedNotificationPermissionPrompts",
+    base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Fixes for browser hang bugs are deployed in a field trial in order to measure
 // their impact. See crbug.com/478209.
 const base::Feature kBrowserHangFixesExperiment{
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index c9f3776e..cc3ba13 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -71,6 +71,9 @@
 extern const base::Feature kBlockPromptsIfIgnoredOften;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kBlockRepeatedNotificationPermissionPrompts;
+
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kBrowserHangFixesExperiment;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/extensions/api/extension_action/action_info.cc b/chrome/common/extensions/api/extension_action/action_info.cc
index c4c14f3..a47cccfd 100644
--- a/chrome/common/extensions/api/extension_action/action_info.cc
+++ b/chrome/common/extensions/api/extension_action/action_info.cc
@@ -27,15 +27,15 @@
 // The manifest data container for the ActionInfos for BrowserActions,
 // PageActions and SystemIndicators.
 struct ActionInfoData : public Extension::ManifestData {
-  explicit ActionInfoData(ActionInfo* action_info);
+  explicit ActionInfoData(std::unique_ptr<ActionInfo> action_info);
   ~ActionInfoData() override;
 
   // The action associated with the BrowserAction.
   std::unique_ptr<ActionInfo> action_info;
 };
 
-ActionInfoData::ActionInfoData(ActionInfo* info) : action_info(info) {
-}
+ActionInfoData::ActionInfoData(std::unique_ptr<ActionInfo> info)
+    : action_info(std::move(info)) {}
 
 ActionInfoData::~ActionInfoData() {
 }
@@ -157,28 +157,30 @@
 
 // static
 void ActionInfo::SetExtensionActionInfo(Extension* extension,
-                                        ActionInfo* info) {
+                                        std::unique_ptr<ActionInfo> info) {
   extension->SetManifestData(keys::kAction,
-                             std::make_unique<ActionInfoData>(info));
+                             std::make_unique<ActionInfoData>(std::move(info)));
 }
 
 // static
-void ActionInfo::SetBrowserActionInfo(Extension* extension, ActionInfo* info) {
+void ActionInfo::SetBrowserActionInfo(Extension* extension,
+                                      std::unique_ptr<ActionInfo> info) {
   extension->SetManifestData(keys::kBrowserAction,
-                             std::make_unique<ActionInfoData>(info));
+                             std::make_unique<ActionInfoData>(std::move(info)));
 }
 
 // static
-void ActionInfo::SetPageActionInfo(Extension* extension, ActionInfo* info) {
+void ActionInfo::SetPageActionInfo(Extension* extension,
+                                   std::unique_ptr<ActionInfo> info) {
   extension->SetManifestData(keys::kPageAction,
-                             std::make_unique<ActionInfoData>(info));
+                             std::make_unique<ActionInfoData>(std::move(info)));
 }
 
 // static
 void ActionInfo::SetSystemIndicatorInfo(Extension* extension,
-                                        ActionInfo* info) {
+                                        std::unique_ptr<ActionInfo> info) {
   extension->SetManifestData(keys::kSystemIndicator,
-                             std::make_unique<ActionInfoData>(info));
+                             std::make_unique<ActionInfoData>(std::move(info)));
 }
 
 // static
diff --git a/chrome/common/extensions/api/extension_action/action_info.h b/chrome/common/extensions/api/extension_action/action_info.h
index 54d1412..eaa0ed0b 100644
--- a/chrome/common/extensions/api/extension_action/action_info.h
+++ b/chrome/common/extensions/api/extension_action/action_info.h
@@ -55,18 +55,21 @@
   // Returns the extension's system indicator, if any.
   static const ActionInfo* GetSystemIndicatorInfo(const Extension* extension);
 
-  // Sets the extension's action. |extension| takes ownership of |info|.
-  static void SetExtensionActionInfo(Extension* extension, ActionInfo* info);
+  // Sets the extension's action.
+  static void SetExtensionActionInfo(Extension* extension,
+                                     std::unique_ptr<ActionInfo> info);
 
-  // Sets the extension's browser action. |extension| takes ownership of |info|.
-  static void SetBrowserActionInfo(Extension* extension, ActionInfo* info);
+  // Sets the extension's browser action.
+  static void SetBrowserActionInfo(Extension* extension,
+                                   std::unique_ptr<ActionInfo> info);
 
-  // Sets the extension's page action. |extension| takes ownership of |info|.
-  static void SetPageActionInfo(Extension* extension, ActionInfo* info);
+  // Sets the extension's page action.
+  static void SetPageActionInfo(Extension* extension,
+                                std::unique_ptr<ActionInfo> info);
 
-  // Sets the extension's system indicator. |extension| takes ownership of
-  // |info|.
-  static void SetSystemIndicatorInfo(Extension* extension, ActionInfo* info);
+  // Sets the extension's system indicator.
+  static void SetSystemIndicatorInfo(Extension* extension,
+                                     std::unique_ptr<ActionInfo> info);
 
   // Returns true if the extension needs a verbose install message because
   // of its page action.
diff --git a/chrome/common/extensions/api/system_indicator/system_indicator_handler.cc b/chrome/common/extensions/api/system_indicator/system_indicator_handler.cc
index 761b9dd..e96dcb5 100644
--- a/chrome/common/extensions/api/system_indicator/system_indicator_handler.cc
+++ b/chrome/common/extensions/api/system_indicator/system_indicator_handler.cc
@@ -35,7 +35,7 @@
   if (!action_info.get())
     return false;
 
-  ActionInfo::SetSystemIndicatorInfo(extension, action_info.release());
+  ActionInfo::SetSystemIndicatorInfo(extension, std::move(action_info));
   return true;
 }
 
diff --git a/chrome/common/extensions/manifest_handlers/extension_action_handler.cc b/chrome/common/extensions/manifest_handlers/extension_action_handler.cc
index b3744ff..7e79d548 100644
--- a/chrome/common/extensions/manifest_handlers/extension_action_handler.cc
+++ b/chrome/common/extensions/manifest_handlers/extension_action_handler.cc
@@ -67,7 +67,7 @@
       return false;  // Failed to parse extension action definition.
 
     if (key == manifest_keys::kAction) {
-      ActionInfo::SetExtensionActionInfo(extension, action_info.release());
+      ActionInfo::SetExtensionActionInfo(extension, std::move(action_info));
     } else {
       if (dict->HasKey(manifest_keys::kActionDefaultState)) {
         *error =
@@ -76,9 +76,9 @@
       }
 
       if (key == manifest_keys::kPageAction)
-        ActionInfo::SetPageActionInfo(extension, action_info.release());
+        ActionInfo::SetPageActionInfo(extension, std::move(action_info));
       else
-        ActionInfo::SetBrowserActionInfo(extension, action_info.release());
+        ActionInfo::SetBrowserActionInfo(extension, std::move(action_info));
     }
   } else {  // No key, used for synthesizing an action for extensions with none.
     if (Manifest::IsComponentLocation(extension->location()))
@@ -96,7 +96,7 @@
     // action) because the action should not be seen as enabled on every page.
     std::unique_ptr<ActionInfo> action_info(new ActionInfo());
     action_info->synthesized = true;
-    ActionInfo::SetPageActionInfo(extension, action_info.release());
+    ActionInfo::SetPageActionInfo(extension, std::move(action_info));
   }
 
   return true;
diff --git a/chrome/common/media_router/providers/cast/cast_media_source.cc b/chrome/common/media_router/providers/cast/cast_media_source.cc
index 7d0fe5b5..af79bff 100644
--- a/chrome/common/media_router/providers/cast/cast_media_source.cc
+++ b/chrome/common/media_router/providers/cast/cast_media_source.cc
@@ -4,7 +4,11 @@
 
 #include "chrome/common/media_router/providers/cast/cast_media_source.h"
 
+#include <utility>
+
+#include "base/containers/flat_map.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/media_router/media_source_helper.h"
@@ -14,38 +18,107 @@
 #include "url/url_util.h"
 
 using cast_channel::BroadcastRequest;
+using cast_channel::CastDeviceCapability;
 
 namespace cast_util {
+
+using media_router::AutoJoinPolicy;
+using media_router::DefaultActionPolicy;
+
 template <>
-const EnumTable<media_router::AutoJoinPolicy>
-    EnumTable<media_router::AutoJoinPolicy>::instance({
-        {media_router::AutoJoinPolicy::kTabAndOriginScoped,
-         "tab_and_origin_scoped"},
-        {media_router::AutoJoinPolicy::kOriginScoped, "origin_scoped"},
-        {media_router::AutoJoinPolicy::kPageScoped, "page_scoped"},
-        // kNone deliberately omitted.
-    });
+const EnumTable<AutoJoinPolicy> EnumTable<AutoJoinPolicy>::instance({
+    {AutoJoinPolicy::kPageScoped, "page_scoped"},
+    {AutoJoinPolicy::kTabAndOriginScoped, "tab_and_origin_scoped"},
+    {AutoJoinPolicy::kOriginScoped, "origin_scoped"},
+});
+
+template <>
+const EnumTable<DefaultActionPolicy> EnumTable<DefaultActionPolicy>::instance({
+    {DefaultActionPolicy::kCreateSession, "create_session"},
+    {DefaultActionPolicy::kCastThisTab, "cast_this_tab"},
+});
+
+template <>
+const EnumTable<CastDeviceCapability> EnumTable<CastDeviceCapability>::instance(
+    {
+        {CastDeviceCapability::MULTIZONE_GROUP, "multizone_group"},
+        {CastDeviceCapability::DEV_MODE, "dev_mode"},
+        {CastDeviceCapability::AUDIO_IN, "audio_in"},
+        {CastDeviceCapability::AUDIO_OUT, "audio_out"},
+        {CastDeviceCapability::VIDEO_IN, "video_in"},
+        {CastDeviceCapability::VIDEO_OUT, "video_out"},
+        // NONE deliberately omitted
+    },
+    UnsortedEnumTable);
+
 }  // namespace cast_util
 
 namespace media_router {
 
 namespace {
 
-// Parameter keys used by new Cast URLs.
-constexpr char kCapabilitiesKey[] = "capabilities";
-constexpr char kBroadcastNamespaceKey[] = "broadcastNamespace";
-constexpr char kBroadcastMessageKey[] = "broadcastMessage";
-constexpr char kClientIdKey[] = "clientId";
-constexpr char kLaunchTimeoutKey[] = "launchTimeout";
-constexpr char kAutoJoinPolicyKey[] = "autoJoinPolicy";
+// A nonmember version of base::Optional::value_or that works on pointers as
+// well as instance of base::Optional.
+template <typename T>
+inline auto value_or(const T& optional,
+                     const std::decay_t<decltype(*optional)>& default_value)
+    -> std::decay_t<decltype(*optional)> {
+  return optional ? *optional : default_value;
+}
 
-// Parameter keys used by legacy Cast URLs.
-constexpr char kLegacyAppIdKey[] = "__castAppId__";
-constexpr char kLegacyBroadcastNamespaceKey[] = "__castBroadcastNamespace__";
-constexpr char kLegacyBroadcastMessageKey[] = "__castBroadcastMessage__";
-constexpr char kLegacyClientIdKey[] = "__castClientId__";
-constexpr char kLegacyLaunchTimeoutKey[] = "__castLaunchTimeout__";
-constexpr char kLegacyAutoJoinPolicyKey[] = "__castAutoJoinPolicy__";
+// FindValue() looks up the value associated with a key |key| in a map-like
+// object |map| and returns a pointer to the value if |key| is found, or nullptr
+// otherwise.
+//
+// The type of |map| can be anything that supports a find() method like
+// std::map::find, or any iterable object whose values are key/value pairs.
+//
+// See also FindValueOr().
+
+// Overload for types with a find() method.
+template <typename Map, typename = typename Map::key_type>
+inline const typename Map::mapped_type* FindValue(
+    const Map& map,
+    const typename Map::key_type& key) {
+  auto it = map.find(key);
+  if (it == map.end())
+    return nullptr;
+  return &it->second;
+}
+
+// Overload for types without a find() method.
+template <typename Map, typename Key>
+auto FindValue(const Map& map, const Key& key) -> const
+    decltype(begin(map)->second)* {
+  for (const auto& item : map) {
+    if (item.first == key)
+      return &item.second;
+  }
+  return nullptr;
+}
+
+// Looks up the value associated with a key |key| in a map-like object |map| and
+// returns a reference to the value if |key| is found, or |default_value|
+// otherwise.
+//
+// The type of |map| can be anything that supports a find() method like
+// std::map::find, or any iterable object whose values are key/value pairs.
+template <typename Map, typename Key, typename T>
+inline auto FindValueOr(const Map& map, const Key& key, const T& default_value)
+    -> std::decay_t<decltype(*FindValue(map, key))> {
+  return value_or(FindValue(map, key), default_value);
+}
+
+// Creates a map from the query parameters of |url|.  If |url| contains multiple
+// values for the same parameter, the last value is used.
+base::flat_map<std::string, std::string> MakeQueryMap(const GURL& url) {
+  base::flat_map<std::string, std::string> result;
+  for (net::QueryIterator query_it(url); !query_it.IsAtEnd();
+       query_it.Advance()) {
+    result[query_it.GetKey()] = query_it.GetValue();
+  }
+  return result;
+}
 
 // TODO(imcheng): Move to common utils?
 std::string DecodeURLComponent(const std::string& encoded) {
@@ -60,20 +133,22 @@
   return std::string();
 }
 
-cast_channel::CastDeviceCapability CastDeviceCapabilityFromString(
+// Converts a string containing a comma-separated list of capabilities into a
+// bitwise OR of CastDeviceCapability values.
+BitwiseOr<CastDeviceCapability> CastDeviceCapabilitiesFromString(
     const base::StringPiece& s) {
-  if (s == "video_out")
-    return cast_channel::CastDeviceCapability::VIDEO_OUT;
-  if (s == "video_in")
-    return cast_channel::CastDeviceCapability::VIDEO_IN;
-  if (s == "audio_out")
-    return cast_channel::CastDeviceCapability::AUDIO_OUT;
-  if (s == "audio_in")
-    return cast_channel::CastDeviceCapability::AUDIO_IN;
-  if (s == "multizone_group")
-    return cast_channel::CastDeviceCapability::MULTIZONE_GROUP;
-
-  return cast_channel::CastDeviceCapability::NONE;
+  BitwiseOr<CastDeviceCapability> result{};
+  for (const auto& capability_str : base::SplitStringPiece(
+           s, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+    const auto capability =
+        cast_util::StringToEnum<CastDeviceCapability>(capability_str);
+    if (capability) {
+      result.Add(*capability);
+    } else {
+      DLOG(ERROR) << "Unkown capability name: " << capability_str;
+    }
+  }
+  return result;
 }
 
 std::unique_ptr<CastMediaSource> CastMediaSourceForTabMirroring(
@@ -92,28 +167,38 @@
       source_id, std::vector<CastAppInfo>({CastAppInfo(kCastStreamingAppId)}));
 }
 
+// The logic shared by ParseCastUrl() and ParseLegacyCastUrl().
 std::unique_ptr<CastMediaSource> CreateFromURLParams(
     const MediaSource::Id& source_id,
     const std::vector<CastAppInfo>& app_infos,
     const std::string& auto_join_policy_str,
+    const std::string& default_action_policy_str,
     const std::string& client_id,
     const std::string& broadcast_namespace,
-    const std::string& broadcast_message,
-    base::TimeDelta launch_timeout) {
+    const std::string& encoded_broadcast_message,
+    const std::string& launch_timeout_str) {
   if (app_infos.empty())
     return nullptr;
 
   auto cast_source = std::make_unique<CastMediaSource>(
       source_id, app_infos,
       cast_util::StringToEnum<AutoJoinPolicy>(auto_join_policy_str)
-          .value_or(AutoJoinPolicy::kNone));
+          .value_or(AutoJoinPolicy::kPageScoped),
+      cast_util::StringToEnum<DefaultActionPolicy>(default_action_policy_str)
+          .value_or(DefaultActionPolicy::kCreateSession));
   cast_source->set_client_id(client_id);
-  if (!broadcast_namespace.empty() && !broadcast_message.empty()) {
-    cast_source->set_broadcast_request(
-        BroadcastRequest(broadcast_namespace, broadcast_message));
+  if (!broadcast_namespace.empty() && !encoded_broadcast_message.empty()) {
+    cast_source->set_broadcast_request(BroadcastRequest(
+        broadcast_namespace, DecodeURLComponent(encoded_broadcast_message)));
   }
-  if (launch_timeout > base::TimeDelta())
-    cast_source->set_launch_timeout(launch_timeout);
+
+  int launch_timeout_millis;
+  if (base::StringToInt(launch_timeout_str, &launch_timeout_millis) &&
+      launch_timeout_millis > 0) {
+    cast_source->set_launch_timeout(
+        base::TimeDelta::FromMilliseconds(launch_timeout_millis));
+  }
+
   return cast_source;
 }
 
@@ -124,78 +209,30 @@
   if (app_id.empty())
     return nullptr;
 
-  std::string broadcast_namespace, broadcast_message, capabilities;
-  std::string client_id, auto_join_policy;
-  int launch_timeout_millis = 0;
-  for (net::QueryIterator query_it(url); !query_it.IsAtEnd();
-       query_it.Advance()) {
-    std::string key = query_it.GetKey();
-    std::string value = query_it.GetValue();
-    if (key.empty() || value.empty())
-      continue;
-    if (key == kBroadcastNamespaceKey) {
-      broadcast_namespace = value;
-    } else if (key == kBroadcastMessageKey) {
-      // The broadcast message is URL-encoded.
-      broadcast_message = DecodeURLComponent(value);
-    } else if (key == kCapabilitiesKey) {
-      capabilities = value;
-    } else if (key == kClientIdKey) {
-      client_id = value;
-    } else if (key == kLaunchTimeoutKey) {
-      if (!base::StringToInt(value, &launch_timeout_millis) ||
-          launch_timeout_millis < 0)
-        launch_timeout_millis = 0;
-    } else if (key == kAutoJoinPolicyKey) {
-      auto_join_policy = value;
-    }
-  }
-
-  CastAppInfo app_info(app_id);
-  if (!capabilities.empty()) {
-    for (const auto& capability :
-         base::SplitStringPiece(capabilities, ",", base::KEEP_WHITESPACE,
-                                base::SPLIT_WANT_NONEMPTY)) {
-      app_info.required_capabilities |=
-          CastDeviceCapabilityFromString(capability);
-    }
-  }
-
+  auto params{MakeQueryMap(url)};
   return CreateFromURLParams(
-      source_id, {app_info}, auto_join_policy, client_id, broadcast_namespace,
-      broadcast_message,
-      base::TimeDelta::FromMilliseconds(launch_timeout_millis));
+      source_id,
+      {CastAppInfo(app_id, CastDeviceCapabilitiesFromString(
+                               FindValueOr(params, "capabilities", "")))},
+      FindValueOr(params, "autoJoinPolicy", ""),
+      FindValueOr(params, "defaultActionPolicy", ""),
+      FindValueOr(params, "clientId", ""),
+      FindValueOr(params, "broadcastNamespace", ""),
+      FindValueOr(params, "broadcastMessage", ""),
+      FindValueOr(params, "launchTimeout", ""));
 }
 
 std::unique_ptr<CastMediaSource> ParseLegacyCastUrl(
     const MediaSource::Id& source_id,
     const GURL& url) {
-  base::StringPairs parameters;
-  base::SplitStringIntoKeyValuePairs(url.ref(), '=', '/', &parameters);
+  base::StringPairs params;
+  base::SplitStringIntoKeyValuePairs(url.ref(), '=', '/', &params);
+
   // Legacy URLs can specify multiple apps.
   std::vector<std::string> app_id_params;
-  std::string broadcast_namespace, broadcast_message;
-  std::string client_id, auto_join_policy;
-  int launch_timeout_millis = 0;
-  for (const auto& key_value : parameters) {
-    const auto& key = key_value.first;
-    const auto& value = key_value.second;
-    if (key == kLegacyAppIdKey) {
-      app_id_params.push_back(value);
-    } else if (key == kLegacyBroadcastNamespaceKey) {
-      broadcast_namespace = value;
-    } else if (key == kLegacyBroadcastMessageKey) {
-      // The broadcast message is URL-encoded.
-      broadcast_message = DecodeURLComponent(value);
-    } else if (key == kLegacyClientIdKey) {
-      client_id = value;
-    } else if (key == kLegacyLaunchTimeoutKey) {
-      if (!base::StringToInt(value, &launch_timeout_millis) ||
-          launch_timeout_millis < 0)
-        launch_timeout_millis = 0;
-    } else if (key == kLegacyAutoJoinPolicyKey) {
-      auto_join_policy = value;
-    }
+  for (const auto& param : params) {
+    if (param.first == "__castAppId__")
+      app_id_params.push_back(param.second);
   }
 
   std::vector<CastAppInfo> app_infos;
@@ -217,13 +254,8 @@
     if (app_id.empty())
       continue;
 
-    CastAppInfo app_info(app_id);
-    for (const auto& capability :
-         base::SplitStringPiece(capabilities, ",", base::KEEP_WHITESPACE,
-                                base::SPLIT_WANT_NONEMPTY)) {
-      app_info.required_capabilities |=
-          CastDeviceCapabilityFromString(capability);
-    }
+    CastAppInfo app_info(app_id,
+                         CastDeviceCapabilitiesFromString(capabilities));
 
     app_infos.push_back(app_info);
   }
@@ -232,14 +264,20 @@
     return nullptr;
 
   return CreateFromURLParams(
-      source_id, app_infos, auto_join_policy, client_id, broadcast_namespace,
-      broadcast_message,
-      base::TimeDelta::FromMilliseconds(launch_timeout_millis));
+      source_id, app_infos, FindValueOr(params, "__castAutoJoinPolicy__", ""),
+      FindValueOr(params, "__castDefaultActionPolicy__", ""),
+      FindValueOr(params, "__castClientId__", ""),
+      FindValueOr(params, "__castBroadcastNamespace__", ""),
+      FindValueOr(params, "__castBroadcastMessage__", ""),
+      FindValueOr(params, "__castLaunchTimeout__", ""));
 }
 
 }  // namespace
 
-CastAppInfo::CastAppInfo(const std::string& app_id) : app_id(app_id) {}
+CastAppInfo::CastAppInfo(
+    const std::string& app_id,
+    BitwiseOr<cast_channel::CastDeviceCapability> required_capabilities)
+    : app_id(app_id), required_capabilities(required_capabilities) {}
 CastAppInfo::~CastAppInfo() = default;
 
 CastAppInfo::CastAppInfo(const CastAppInfo& other) = default;
@@ -279,10 +317,12 @@
 
 CastMediaSource::CastMediaSource(const MediaSource::Id& source_id,
                                  const std::vector<CastAppInfo>& app_infos,
-                                 AutoJoinPolicy auto_join_policy)
+                                 AutoJoinPolicy auto_join_policy,
+                                 DefaultActionPolicy default_action_policy)
     : source_id_(source_id),
       app_infos_(app_infos),
-      auto_join_policy_(auto_join_policy) {}
+      auto_join_policy_(auto_join_policy),
+      default_action_policy_(default_action_policy) {}
 CastMediaSource::CastMediaSource(const CastMediaSource& other) = default;
 CastMediaSource::~CastMediaSource() = default;
 
diff --git a/chrome/common/media_router/providers/cast/cast_media_source.h b/chrome/common/media_router/providers/cast/cast_media_source.h
index bda28af..c5441df 100644
--- a/chrome/common/media_router/providers/cast/cast_media_source.h
+++ b/chrome/common/media_router/providers/cast/cast_media_source.h
@@ -5,10 +5,13 @@
 #ifndef CHROME_COMMON_MEDIA_ROUTER_PROVIDERS_CAST_CAST_MEDIA_SOURCE_H_
 #define CHROME_COMMON_MEDIA_ROUTER_PROVIDERS_CAST_CAST_MEDIA_SOURCE_H_
 
+#include <initializer_list>
 #include <memory>
 #include <string>
+#include <type_traits>
 #include <vector>
 
+#include "base/logging.h"
 #include "base/optional.h"
 #include "chrome/common/media_router/media_source.h"
 #include "components/cast_channel/cast_message_util.h"
@@ -26,9 +29,37 @@
 static constexpr base::TimeDelta kDefaultLaunchTimeout =
     base::TimeDelta::FromSeconds(60);
 
+// Class for storing a bitwise OR of enum values.
+//
+// TODO(jrw): Make values of cast_channel::CastDeviceCapability consecutive and
+// store sets of values using a class like v8::base::EnumSet instead of this
+// monstrosity.
+template <typename E, typename T = std::underlying_type_t<E>>
+class BitwiseOr {
+ public:
+  constexpr BitwiseOr() : bits_(0) {}
+  constexpr BitwiseOr(std::initializer_list<E> values) : bits_(0) {
+    for (E e : values)
+      Add(e);
+  }
+  bool empty() const { return bits_ == 0; }
+  void Add(E value) { bits_ |= Mask(value); }
+  bool operator==(const BitwiseOr& other) const { return bits_ == other.bits_; }
+  bool operator!=(const BitwiseOr& other) const { return *this != other; }
+
+ private:
+  static T Mask(E value) {
+    const T result = static_cast<T>(value);
+    DCHECK(static_cast<E>(result) == value);
+    return result;
+  }
+  T bits_;
+};
+
 // Represents a Cast app and its capabilitity requirements.
 struct CastAppInfo {
-  explicit CastAppInfo(const std::string& app_id);
+  explicit CastAppInfo(const std::string& app_id,
+                       BitwiseOr<cast_channel::CastDeviceCapability> = {});
   ~CastAppInfo();
 
   CastAppInfo(const CastAppInfo& other);
@@ -36,22 +67,35 @@
   std::string app_id;
 
   // A bitset of capabilities required by the app.
-  int required_capabilities = cast_channel::CastDeviceCapability::NONE;
+  BitwiseOr<cast_channel::CastDeviceCapability> required_capabilities;
 };
 
 // Auto-join policy determines when the SDK will automatically connect a sender
 // application to an existing session after API initialization.
 enum class AutoJoinPolicy {
+  // No automatic connection.
+  kPageScoped,
   // Automatically connects when the session was started with the same app ID,
   // in the same tab and page origin.
   kTabAndOriginScoped,
   // Automatically connects when the session was started with the same app ID
   // and the same page origin (regardless of tab).
   kOriginScoped,
-  // No automatic connection.
-  kPageScoped,
-  // No policy was specified.  Generally treated the same as kPageScoped.
-  kNone,
+};
+
+// Default action policy determines when the SDK will automatically create a
+// session after initializing the API.  This also controls the default action
+// for the tab in the Cast dialog.
+enum class DefaultActionPolicy {
+  // If the tab containing the app is being cast when the API initializes, the
+  // SDK stops tab casting and automatically launches the app.  The Cast dialog
+  // prompts the user to cast the app.
+  kCreateSession,
+
+  // No automatic launch is done after initializing the API, even if the tab is
+  // being cast.  The Cast dialog prompts the user to mirror the tab (mirror,
+  // not cast, despite the name).
+  kCastThisTab,
 };
 
 // Represents a MediaSource parsed into structured, Cast specific data. The
@@ -69,7 +113,9 @@
 
   CastMediaSource(const MediaSource::Id& source_id,
                   const std::vector<CastAppInfo>& app_infos,
-                  AutoJoinPolicy auto_join_policy = AutoJoinPolicy::kNone);
+                  AutoJoinPolicy auto_join_policy = AutoJoinPolicy::kPageScoped,
+                  DefaultActionPolicy default_action_policy =
+                      DefaultActionPolicy::kCreateSession);
   CastMediaSource(const CastMediaSource& other);
   ~CastMediaSource();
 
@@ -92,17 +138,17 @@
       const {
     return broadcast_request_;
   }
-
   void set_broadcast_request(const cast_channel::BroadcastRequest& request) {
     broadcast_request_ = request;
   }
-
   AutoJoinPolicy auto_join_policy() const { return auto_join_policy_; }
+  DefaultActionPolicy default_action_policy() { return default_action_policy_; }
 
  private:
   MediaSource::Id source_id_;
   std::vector<CastAppInfo> app_infos_;
   AutoJoinPolicy auto_join_policy_;
+  DefaultActionPolicy default_action_policy_;
   base::TimeDelta launch_timeout_ = kDefaultLaunchTimeout;
   // Empty if not set.
   std::string client_id_;
diff --git a/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc b/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc
index a9f9747..7073e8e2 100644
--- a/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc
+++ b/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc
@@ -7,16 +7,38 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using cast_channel::CastDeviceCapability;
+
 namespace media_router {
 
+TEST(CastMediaSourceTest, FromCastURLWithDefaults) {
+  MediaSource::Id source_id("cast:ABCDEFAB");
+  std::unique_ptr<CastMediaSource> source =
+      CastMediaSource::FromMediaSourceId(source_id);
+  ASSERT_TRUE(source);
+  EXPECT_EQ(source_id, source->source_id());
+  ASSERT_EQ(1u, source->app_infos().size());
+  const CastAppInfo& app_info = source->app_infos()[0];
+  EXPECT_EQ("ABCDEFAB", app_info.app_id);
+  EXPECT_TRUE(app_info.required_capabilities.empty());
+  const auto& broadcast_request = source->broadcast_request();
+  EXPECT_FALSE(broadcast_request);
+  EXPECT_EQ("", source->client_id());
+  EXPECT_EQ(kDefaultLaunchTimeout, source->launch_timeout());
+  EXPECT_EQ(AutoJoinPolicy::kPageScoped, source->auto_join_policy());
+  EXPECT_EQ(DefaultActionPolicy::kCreateSession,
+            source->default_action_policy());
+}
+
 TEST(CastMediaSourceTest, FromCastURL) {
   MediaSource::Id source_id(
       "cast:ABCDEFAB?capabilities=video_out,audio_out"
       "&broadcastNamespace=namespace"
-      "&broadcastMessage=message"
+      "&broadcastMessage=message%25"
       "&clientId=12345"
       "&launchTimeout=30000"
-      "&autoJoinPolicy=tab_and_origin_scoped");
+      "&autoJoinPolicy=tab_and_origin_scoped"
+      "&defaultActionPolicy=cast_this_tab");
   std::unique_ptr<CastMediaSource> source =
       CastMediaSource::FromMediaSourceId(source_id);
   ASSERT_TRUE(source);
@@ -24,43 +46,49 @@
   ASSERT_EQ(1u, source->app_infos().size());
   const CastAppInfo& app_info = source->app_infos()[0];
   EXPECT_EQ("ABCDEFAB", app_info.app_id);
-  EXPECT_EQ(cast_channel::CastDeviceCapability::VIDEO_OUT |
-                cast_channel::CastDeviceCapability::AUDIO_OUT,
+  EXPECT_EQ((BitwiseOr<CastDeviceCapability>{CastDeviceCapability::VIDEO_OUT,
+                                             CastDeviceCapability::AUDIO_OUT}),
             app_info.required_capabilities);
   const auto& broadcast_request = source->broadcast_request();
   ASSERT_TRUE(broadcast_request);
   EXPECT_EQ("namespace", broadcast_request->broadcast_namespace);
-  EXPECT_EQ("message", broadcast_request->message);
+  EXPECT_EQ("message%", broadcast_request->message);
   EXPECT_EQ("12345", source->client_id());
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(30000), source->launch_timeout());
   EXPECT_EQ(AutoJoinPolicy::kTabAndOriginScoped, source->auto_join_policy());
+  EXPECT_EQ(DefaultActionPolicy::kCastThisTab, source->default_action_policy());
 }
 
 TEST(CastMediaSourceTest, FromLegacyCastURL) {
   MediaSource::Id source_id(
-      "https://google.com/cast#__castAppId__=ABCDEFAB(video_out,audio_out)"
+      "https://google.com/cast"
+      "#__castAppId__=ABCDEFAB(video_out,audio_out)"
+      "/__castAppId__=otherAppId"
       "/__castBroadcastNamespace__=namespace"
-      "/__castBroadcastMessage__=message"
+      "/__castBroadcastMessage__=message%25"
       "/__castClientId__=12345"
       "/__castLaunchTimeout__=30000"
-      "/__castAutoJoinPolicy__=origin_scoped");
+      "/__castAutoJoinPolicy__=origin_scoped"
+      "/__castDefaultActionPolicy__=cast_this_tab");
   std::unique_ptr<CastMediaSource> source =
       CastMediaSource::FromMediaSourceId(source_id);
   ASSERT_TRUE(source);
   EXPECT_EQ(source_id, source->source_id());
-  ASSERT_EQ(1u, source->app_infos().size());
+  ASSERT_EQ(2u, source->app_infos().size());
   const CastAppInfo& app_info = source->app_infos()[0];
   EXPECT_EQ("ABCDEFAB", app_info.app_id);
-  EXPECT_EQ(cast_channel::CastDeviceCapability::VIDEO_OUT |
-                cast_channel::CastDeviceCapability::AUDIO_OUT,
+  EXPECT_EQ((BitwiseOr<CastDeviceCapability>{CastDeviceCapability::VIDEO_OUT,
+                                             CastDeviceCapability::AUDIO_OUT}),
             app_info.required_capabilities);
+  EXPECT_EQ("otherAppId", source->app_infos()[1].app_id);
   const auto& broadcast_request = source->broadcast_request();
   ASSERT_TRUE(broadcast_request);
   EXPECT_EQ("namespace", broadcast_request->broadcast_namespace);
-  EXPECT_EQ("message", broadcast_request->message);
+  EXPECT_EQ("message%", broadcast_request->message);
   EXPECT_EQ("12345", source->client_id());
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(30000), source->launch_timeout());
   EXPECT_EQ(AutoJoinPolicy::kOriginScoped, source->auto_join_policy());
+  EXPECT_EQ(DefaultActionPolicy::kCastThisTab, source->default_action_policy());
 }
 
 TEST(CastMediaSourceTest, FromPresentationURL) {
@@ -74,7 +102,9 @@
   EXPECT_EQ(kCastStreamingAudioAppId, source->app_infos()[1].app_id);
   EXPECT_TRUE(source->client_id().empty());
   EXPECT_EQ(kDefaultLaunchTimeout, source->launch_timeout());
-  EXPECT_EQ(AutoJoinPolicy::kNone, source->auto_join_policy());
+  EXPECT_EQ(AutoJoinPolicy::kPageScoped, source->auto_join_policy());
+  EXPECT_EQ(DefaultActionPolicy::kCreateSession,
+            source->default_action_policy());
 }
 
 TEST(CastMediaSourceTest, FromMirroringURN) {
@@ -88,7 +118,9 @@
   EXPECT_EQ(kCastStreamingAudioAppId, source->app_infos()[1].app_id);
   EXPECT_TRUE(source->client_id().empty());
   EXPECT_EQ(kDefaultLaunchTimeout, source->launch_timeout());
-  EXPECT_EQ(AutoJoinPolicy::kNone, source->auto_join_policy());
+  EXPECT_EQ(AutoJoinPolicy::kPageScoped, source->auto_join_policy());
+  EXPECT_EQ(DefaultActionPolicy::kCreateSession,
+            source->default_action_policy());
 }
 
 TEST(CastMediaSourceTest, FromDesktopUrn) {
@@ -101,7 +133,9 @@
   EXPECT_EQ(kCastStreamingAppId, source->app_infos()[0].app_id);
   EXPECT_TRUE(source->client_id().empty());
   EXPECT_EQ(kDefaultLaunchTimeout, source->launch_timeout());
-  EXPECT_EQ(AutoJoinPolicy::kNone, source->auto_join_policy());
+  EXPECT_EQ(AutoJoinPolicy::kPageScoped, source->auto_join_policy());
+  EXPECT_EQ(DefaultActionPolicy::kCreateSession,
+            source->default_action_policy());
 }
 
 TEST(CastMediaSourceTest, FromInvalidSource) {
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 86b94e9..0da8799 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -278,10 +278,6 @@
 const char kDisableSigninFrameClientCertUserSelection[] =
     "disable-signin-frame-client-cert-user-selection";
 
-// Disables SystemTimezoneAutomaticDetection policy.
-const char kDisableSystemTimezoneAutomaticDetectionPolicy[] =
-    "disable-system-timezone-automatic-detection";
-
 // Disables volume adjust sound.
 const char kDisableVolumeAdjustSound[] = "disable-volume-adjust-sound";
 
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index aed69b8..4dc3698 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -103,8 +103,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kDisableSigninFrameClientCertUserSelection[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const char kDisableSystemTimezoneAutomaticDetectionPolicy[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kDisableVolumeAdjustSound[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableWakeOnWifi[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kEnableArc[];
diff --git a/components/content_capture/browser/content_capture_receiver.cc b/components/content_capture/browser/content_capture_receiver.cc
index 327b209..b0573e9 100644
--- a/components/content_capture/browser/content_capture_receiver.cc
+++ b/components/content_capture/browser/content_capture_receiver.cc
@@ -97,18 +97,22 @@
 
 const ContentCaptureData& ContentCaptureReceiver::GetFrameContentCaptureData() {
   base::string16 url = base::UTF8ToUTF16(rfh_->GetLastCommittedURL().spec());
-  if (url != frame_content_capture_data_.value) {
-    if (frame_content_capture_data_.id != 0) {
-      auto* manager = ContentCaptureReceiverManager::FromWebContents(
-          content::WebContents::FromRenderFrameHost(rfh_));
-      manager->DidRemoveSession(this);
-    }
+  if (url == frame_content_capture_data_.value)
+    return frame_content_capture_data_;
 
-    frame_content_capture_data_.id = id_;
-    frame_content_capture_data_.value = url;
-    const base::Optional<gfx::Size>& size = rfh_->GetFrameSize();
-    if (size.has_value())
-      frame_content_capture_data_.bounds = gfx::Rect(size.value());
+  bool should_remove_session = frame_content_capture_data_.id != 0;
+  frame_content_capture_data_.id = id_;
+  frame_content_capture_data_.value = url;
+  const base::Optional<gfx::Size>& size = rfh_->GetFrameSize();
+  if (size.has_value())
+    frame_content_capture_data_.bounds = gfx::Rect(size.value());
+
+  // frame_content_capture_data_ must be set to new value before removing
+  // sesesion, otherwises, it causes infinite loop.
+  if (should_remove_session) {
+    auto* manager = ContentCaptureReceiverManager::FromWebContents(
+        content::WebContents::FromRenderFrameHost(rfh_));
+    manager->DidRemoveSession(this);
   }
   return frame_content_capture_data_;
 }
diff --git a/components/content_capture/browser/content_capture_receiver_test.cc b/components/content_capture/browser/content_capture_receiver_test.cc
index 1a0b35f..7c411e0 100644
--- a/components/content_capture/browser/content_capture_receiver_test.cc
+++ b/components/content_capture/browser/content_capture_receiver_test.cc
@@ -19,6 +19,7 @@
 namespace {
 
 static const char kMainFrameUrl[] = "http://foo.com/main.html";
+static const char kMainFrameUrl2[] = "http://foo.com/2.html";
 static const char kChildFrameUrl[] = "http://foo.org/child.html";
 
 // Fake ContentCaptureSender to call ContentCaptureReceiver mojom interface.
@@ -378,7 +379,34 @@
   EXPECT_EQ(GetExpectedTestData2(false /* main_frame */),
             content_capture_receiver_manager_helper()->captured_data());
 
-  // When main frame navigates to another URL, the parent session will change.
+  // When main frame navigates to same url, the parent session will not change.
+  NavigateMainFrame(GURL(kMainFrameUrl));
+  SetupChildFrame();
+  DidCaptureContentForChildFrame(test_data2(), true /* first_data */);
+  VerifySession(expected,
+                content_capture_receiver_manager_helper()->parent_session());
+  EXPECT_TRUE(
+      content_capture_receiver_manager_helper()->removed_session().empty());
+
+  // When main frame navigates to same domain, the parent session will change.
+  NavigateMainFrame(GURL(kMainFrameUrl2));
+  SetupChildFrame();
+  DidCaptureContentForChildFrame(test_data2(), true /* first_data */);
+
+  // Intentionally reuse the data.id from previous result, so we know navigating
+  // to same domain didn't create new ContentCaptureReceiver when call
+  // VerifySession(), otherwise, we can't test the code to handle the navigation
+  // in ContentCaptureReceiver.
+  data.value = base::ASCIIToUTF16(kMainFrameUrl2);
+  // Currently, there is no way to fake frame size, set it to 0.
+  data.bounds = gfx::Rect();
+  expected.clear();
+  expected.push_back(data);
+  VerifySession(expected,
+                content_capture_receiver_manager_helper()->parent_session());
+
+  // When main frame navigates to different domain, the parent session will
+  // change.
   NavigateMainFrame(GURL(kChildFrameUrl));
   SetupChildFrame();
   DidCaptureContentForChildFrame(test_data2(), true /* first_data */);
diff --git a/components/gwp_asan/client/guarded_page_allocator.cc b/components/gwp_asan/client/guarded_page_allocator.cc
index 66146a7..fa627e59 100644
--- a/components/gwp_asan/client/guarded_page_allocator.cc
+++ b/components/gwp_asan/client/guarded_page_allocator.cc
@@ -269,8 +269,8 @@
       base::debug::CollectStackTrace(trace, AllocatorState::kMaxStackFrames);
   metadata_[metadata_idx].alloc.trace_len =
       Pack(reinterpret_cast<uintptr_t*>(trace), len,
-           metadata_[metadata_idx].alloc.packed_trace,
-           sizeof(metadata_[metadata_idx].alloc.packed_trace));
+           metadata_[metadata_idx].stack_trace_pool,
+           sizeof(metadata_[metadata_idx].stack_trace_pool) / 2);
   metadata_[metadata_idx].alloc.tid = ReportTid();
   metadata_[metadata_idx].alloc.trace_collected = true;
 
@@ -287,8 +287,10 @@
       base::debug::CollectStackTrace(trace, AllocatorState::kMaxStackFrames);
   metadata_[metadata_idx].dealloc.trace_len =
       Pack(reinterpret_cast<uintptr_t*>(trace), len,
-           metadata_[metadata_idx].dealloc.packed_trace,
-           sizeof(metadata_[metadata_idx].dealloc.packed_trace));
+           metadata_[metadata_idx].stack_trace_pool +
+               metadata_[metadata_idx].alloc.trace_len,
+           sizeof(metadata_[metadata_idx].stack_trace_pool) -
+               metadata_[metadata_idx].alloc.trace_len);
   metadata_[metadata_idx].dealloc.tid = ReportTid();
   metadata_[metadata_idx].dealloc.trace_collected = true;
 }
diff --git a/components/gwp_asan/common/allocator_state.h b/components/gwp_asan/common/allocator_state.h
index 4e3d9240..fff558e 100644
--- a/components/gwp_asan/common/allocator_state.h
+++ b/components/gwp_asan/common/allocator_state.h
@@ -49,11 +49,12 @@
   // Invalid metadata index.
   static constexpr MetadataIdx kInvalidMetadataIdx = kMaxMetadata;
 
-  // Maximum number of stack trace frames to collect.
-  static constexpr size_t kMaxStackFrames = 60;
-  // Number of bytes to allocate for packed stack traces. This can hold
-  // approximately kMaxStackFrames under normal conditions.
-  static constexpr size_t kMaxPackedTraceLength = 200;
+  // Maximum number of stack trace frames to collect for an allocation or
+  // deallocation.
+  static constexpr size_t kMaxStackFrames = 100;
+  // Number of bytes to allocate for both allocation and deallocation packed
+  // stack traces. (Stack trace entries take ~3.5 bytes on average.)
+  static constexpr size_t kMaxPackedTraceLength = 400;
 
   static_assert(std::numeric_limits<SlotIdx>::max() >= kMaxSlots - 1,
                 "SlotIdx can hold all possible slot index values");
@@ -88,12 +89,14 @@
       // (De)allocation thread id or base::kInvalidThreadId if no (de)allocation
       // occurred.
       uint64_t tid = base::kInvalidThreadId;
-      // Packed stack trace.
-      uint8_t packed_trace[kMaxPackedTraceLength];
       // Length used to encode the packed stack trace.
-      size_t trace_len = 0;
+      uint16_t trace_len = 0;
       // Whether a stack trace has been collected for this (de)allocation.
       bool trace_collected = false;
+
+      static_assert(std::numeric_limits<decltype(trace_len)>::max() >=
+                        kMaxPackedTraceLength - 1,
+                    "trace_len can hold all possible length values.");
     };
 
     // Size of the allocation
@@ -103,6 +106,10 @@
     // Used to synchronize whether a deallocation has occurred (e.g. whether a
     // double free has occurred) between threads.
     std::atomic<bool> deallocation_occurred{false};
+    // Holds the combined allocation/deallocation stack traces. The deallocation
+    // stack trace is stored immediately after the allocation stack trace to
+    // optimize on space.
+    uint8_t stack_trace_pool[kMaxPackedTraceLength];
 
     AllocationInfo alloc;
     AllocationInfo dealloc;
diff --git a/components/gwp_asan/crash_handler/crash_analyzer.cc b/components/gwp_asan/crash_handler/crash_analyzer.cc
index 78f3213..a4458753 100644
--- a/components/gwp_asan/crash_handler/crash_analyzer.cc
+++ b/components/gwp_asan/crash_handler/crash_analyzer.cc
@@ -169,10 +169,12 @@
     proto->set_allocation_size(metadata.alloc_size);
     if (metadata.alloc.tid != base::kInvalidThreadId ||
         metadata.alloc.trace_len)
-      ReadAllocationInfo(metadata.alloc, proto->mutable_allocation());
+      ReadAllocationInfo(metadata.stack_trace_pool, 0, metadata.alloc,
+                         proto->mutable_allocation());
     if (metadata.dealloc.tid != base::kInvalidThreadId ||
         metadata.dealloc.trace_len)
-      ReadAllocationInfo(metadata.dealloc, proto->mutable_deallocation());
+      ReadAllocationInfo(metadata.stack_trace_pool, metadata.alloc.trace_len,
+                         metadata.dealloc, proto->mutable_deallocation());
   }
 
   proto->set_region_start(valid_state.pages_base_addr);
@@ -185,6 +187,8 @@
 }
 
 void CrashAnalyzer::ReadAllocationInfo(
+    const uint8_t* stack_trace,
+    size_t stack_trace_offset,
     const SlotMetadata::AllocationInfo& slot_info,
     gwp_asan::Crash_AllocationInfo* proto_info) {
   if (slot_info.tid != base::kInvalidThreadId)
@@ -193,15 +197,17 @@
   if (!slot_info.trace_len || !slot_info.trace_collected)
     return;
 
-  if (slot_info.trace_len > AllocatorState::kMaxPackedTraceLength) {
+  if (slot_info.trace_len > AllocatorState::kMaxPackedTraceLength ||
+      stack_trace_offset + slot_info.trace_len >
+          AllocatorState::kMaxPackedTraceLength) {
     DLOG(ERROR) << "Stack trace length is corrupted: " << slot_info.trace_len;
     return;
   }
 
   uintptr_t unpacked_stack_trace[AllocatorState::kMaxPackedTraceLength];
   size_t unpacked_len =
-      Unpack(slot_info.packed_trace, slot_info.trace_len, unpacked_stack_trace,
-             AllocatorState::kMaxPackedTraceLength);
+      Unpack(stack_trace + stack_trace_offset, slot_info.trace_len,
+             unpacked_stack_trace, AllocatorState::kMaxPackedTraceLength);
   if (!unpacked_len) {
     DLOG(ERROR) << "Failed to unpack stack trace.";
     return;
diff --git a/components/gwp_asan/crash_handler/crash_analyzer.h b/components/gwp_asan/crash_handler/crash_analyzer.h
index d38077c..961814d 100644
--- a/components/gwp_asan/crash_handler/crash_analyzer.h
+++ b/components/gwp_asan/crash_handler/crash_analyzer.h
@@ -91,9 +91,11 @@
       crashpad::VMAddress gpa_addr,
       gwp_asan::Crash* proto);
 
-  // This method fills out an AllocationInfo protobuf from a
-  // SlotMetadata::AllocationInfo struct.
-  static void ReadAllocationInfo(const SlotMetadata::AllocationInfo& slot_info,
+  // This method fills out an AllocationInfo protobuf from a stack trace
+  // and a SlotMetadata::AllocationInfo struct.
+  static void ReadAllocationInfo(const uint8_t* stack_trace,
+                                 size_t stack_trace_offset,
+                                 const SlotMetadata::AllocationInfo& slot_info,
                                  gwp_asan::Crash_AllocationInfo* proto_info);
 
   FRIEND_TEST_ALL_PREFIXES(CrashAnalyzerTest, StackTraceCollection);
diff --git a/components/safe_browsing/features.cc b/components/safe_browsing/features.cc
index 62e7d58..286f3d7b 100644
--- a/components/safe_browsing/features.cc
+++ b/components/safe_browsing/features.cc
@@ -35,9 +35,6 @@
 const base::Feature kCommittedSBInterstitials{
     "SafeBrowsingCommittedInterstitials", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kForceEnableResetPasswordWebUI{
-    "ForceEnableResetPasswordWebUI", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kPasswordProtectionForSignedInUsers{
     "SafeBrowsingPasswordProtectionForSignedInUsers",
     base::FEATURE_DISABLED_BY_DEFAULT};
@@ -79,7 +76,6 @@
     {&kAdSamplerTriggerFeature, false},
     {&kCheckByURLLoaderThrottle, true},
     {&kCommittedSBInterstitials, true},
-    {&kForceEnableResetPasswordWebUI, true},
     {&kPasswordProtectionForSignedInUsers, true},
     {&kSuspiciousSiteTriggerQuotaFeature, true},
     {&kTelemetryForApkDownloads, true},
diff --git a/components/safe_browsing/features.h b/components/safe_browsing/features.h
index 87392b4..c0e311e 100644
--- a/components/safe_browsing/features.h
+++ b/components/safe_browsing/features.h
@@ -27,10 +27,6 @@
 // navigations instead of overlays.
 extern const base::Feature kCommittedSBInterstitials;
 
-// Forces the chrome://reset-password page to be shown for review or testing
-// purpose.
-extern const base::Feature kForceEnableResetPasswordWebUI;
-
 // Enable GAIA password protection for signed-in users.
 extern const base::Feature kPasswordProtectionForSignedInUsers;
 
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 18867d2..abb70db8 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1970,6 +1970,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("nestedlist.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityButtonWithListboxPopup) {
+  RunHtmlTest(FILE_PATH_LITERAL("button-with-listbox-popup.html"));
+}
+
 //
 // Regression tests. These don't test a specific web platform feature,
 // they test a specific web page that crashed or had some bad behavior
diff --git a/content/browser/frame_host/navigation_handle_impl_browsertest.cc b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
index 7db1917..8817210a 100644
--- a/content/browser/frame_host/navigation_handle_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
@@ -1410,6 +1410,71 @@
   EXPECT_FALSE(NavigateToURL(shell(), kUrl2));
 }
 
+// Verify that a cross-process navigation in a frame for which the current
+// renderer process is not live will not result in leaking a
+// RenderProcessHost. See https://crbug.com/949977.
+IN_PROC_BROWSER_TEST_F(NavigationHandleImplBrowserTest,
+                       NoLeakFromStartingSiteInstance) {
+  GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+
+  // Kill the a.com process, to test what happens with the next navigation.
+  scoped_refptr<SiteInstance> site_instance_a =
+      shell()->web_contents()->GetMainFrame()->GetSiteInstance();
+  EXPECT_TRUE(site_instance_a->HasProcess());
+  RenderProcessHost* process_1 = site_instance_a->GetProcess();
+  RenderProcessHostWatcher process_exit_observer_1(
+      process_1, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  RenderProcessHostWatcher rph_gone_observer_1(
+      process_1, content::RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
+  process_1->Shutdown(RESULT_CODE_KILLED);
+  process_exit_observer_1.Wait();
+
+  // Start to navigate the sad tab to another site.
+  GURL url_b = embedded_test_server()->GetURL("b.com", "/title2.html");
+  TestNavigationManager navigation_b(shell()->web_contents(), url_b);
+  shell()->web_contents()->GetController().LoadURL(
+      url_b, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
+  EXPECT_TRUE(navigation_b.WaitForRequestStart());
+
+  // The starting SiteInstance should be the SiteInstance of the current
+  // RenderFrameHost.
+  scoped_refptr<SiteInstance> starting_site_instance =
+      navigation_b.GetNavigationHandle()->GetStartingSiteInstance();
+  EXPECT_EQ(shell()->web_contents()->GetMainFrame()->GetSiteInstance(),
+            starting_site_instance);
+  // Because of the sad tab, this is actually the b.com SiteInstance, which
+  // commits immediately after starting the navigation and has a process.
+  EXPECT_EQ(GURL("http://b.com"), starting_site_instance->GetSiteURL());
+  EXPECT_TRUE(starting_site_instance->HasProcess());
+
+  // In https://crbug.com/949977, we used the a.com SiteInstance here and didn't
+  // have a process, and an observer called GetProcess, creating a process. This
+  // process never went away, even after the SiteInstance was gone.
+  RenderProcessHost* rph_2 = starting_site_instance->GetProcess();
+  RenderProcessHostWatcher process_exit_observer_2(
+      rph_2, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  navigation_b.WaitForNavigationFinished();
+
+  // Ensure RPH 1 is destroyed, which happens at commit time even before the fix
+  // for the bug.
+  rph_gone_observer_1.Wait();
+
+  // Navigate to another process. This isn't necessary to trigger the original
+  // leak (when the starting SiteInstance was a.com), but it lets the test
+  // finish in the case that the starting SiteInstance is b.com, since b.com's
+  // process goes away with this navigation.
+  // TODO(creis): There's still a slight risk that other buggy code could find
+  // site_instance_a and call GetProcess() on it, causing a leak. We'll add a
+  // backup fix and test for that in a followup CL.
+  GURL url_c = embedded_test_server()->GetURL("c.com", "/title1.html");
+  EXPECT_TRUE(NavigateToURL(shell()->web_contents(), url_c));
+
+  // Wait for rph_2 to exit when it's not used. This wouldn't happen when the
+  // bug was present.
+  process_exit_observer_2.Wait();
+}
+
 // Specialized test that verifies the NavigationHandle gets the HTTPS upgraded
 // URL from the very beginning of the navigation.
 class NavigationHandleImplHttpsUpgradeBrowserTest
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index d55cdbd..41b3e41 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -563,17 +563,6 @@
     bindings_ = entry->bindings();
   }
 
-  // This is needed to get site URLs and assign the expected RenderProcessHost.
-  // This is not always the same as |source_site_instance|, as it only depends
-  // on the current frame host, and does not depend on |entry|.
-  starting_site_instance_ =
-      frame_tree_node->current_frame_host()->GetSiteInstance();
-
-  // TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
-  // be correct for cross-BrowsingInstance redirects.
-  site_url_ = SiteInstanceImpl::GetSiteForURL(
-      starting_site_instance_->GetIsolationContext(), common_params_.url);
-
   // Update the load flags with cache information.
   UpdateLoadFlagsWithCacheFlags(&begin_params_->load_flags,
                                 common_params_.navigation_type,
@@ -811,6 +800,21 @@
   DCHECK(frame_tree_node_->navigation_request() == this || is_for_commit);
   FrameTreeNode* frame_tree_node = frame_tree_node_;
 
+  // This is needed to get site URLs and assign the expected RenderProcessHost.
+  // This is not always the same as |source_site_instance_|, as it only depends
+  // on the current frame host, and does not depend on |entry|.
+  // The |starting_site_instance_| needs to be set here instead of the
+  // constructor since a navigation can be started after the constructor and
+  // before here, which can set a different RenderFrameHost and a different
+  // starting SiteInstance.
+  starting_site_instance_ =
+      frame_tree_node->current_frame_host()->GetSiteInstance();
+
+  // TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
+  // be correct for cross-BrowsingInstance redirects.
+  site_url_ = SiteInstanceImpl::GetSiteForURL(
+      starting_site_instance_->GetIsolationContext(), common_params_.url);
+
   // Compute the redirect chain.
   // TODO(clamy): Try to simplify this and have the redirects be part of
   // CommonNavigationParams.
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 4eeaaeb3..862bb76 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -455,17 +455,35 @@
   // There are two cases:
   // 1. Startup duration is not reached.
   // 2. Or if the trace should be saved to file for --trace-config-file flag.
+  base::Optional<base::FilePath> startup_trace_file;
   if (startup_trace_timer_.IsRunning()) {
     startup_trace_timer_.Stop();
     if (startup_trace_file_ != base::FilePath().AppendASCII("none")) {
-      return std::make_unique<BrowserShutdownProfileDumper>(
-          startup_trace_file_);
+      startup_trace_file = startup_trace_file_;
     }
   } else if (tracing::TraceStartupConfig::GetInstance()
                  ->ShouldTraceToResultFile()) {
-    return std::make_unique<BrowserShutdownProfileDumper>(
-        GetStartupTraceFileName());
+    startup_trace_file = GetStartupTraceFileName();
   }
+  if (!startup_trace_file)
+    return nullptr;
+  if (!tracing::TracingUsesPerfettoBackend()) {
+    return std::make_unique<BrowserShutdownProfileDumper>(
+        startup_trace_file.value());
+  }
+  // Perfetto doesn't support shutdown profiling due to complications
+  // around service shutdown timings.
+  // TODO(eseckler): Do something about it.
+  base::RunLoop run_loop;
+  StopTracing(CreateFileEndpoint(
+      startup_trace_file.value(),
+      base::BindRepeating(
+          [](base::FilePath trace_file, base::OnceClosure quit_closure) {
+            OnStoppedStartupTracing(trace_file);
+            std::move(quit_closure).Run();
+          },
+          startup_trace_file.value(), run_loop.QuitClosure())));
+  run_loop.Run();
   return nullptr;
 }
 
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index 028b6d5..a2bddd60 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -539,9 +539,10 @@
 
 void AuthenticatorCommon::UpdateRequestDelegate() {
   DCHECK(!request_delegate_);
+  DCHECK(!relying_party_id_.empty());
   request_delegate_ =
       GetContentClient()->browser()->GetWebAuthenticationRequestDelegate(
-          render_frame_host_);
+          render_frame_host_, relying_party_id_);
 }
 
 bool AuthenticatorCommon::IsFocused() const {
@@ -599,6 +600,31 @@
   }
   DCHECK(!request_);
 
+  if (!HasValidEffectiveDomain(caller_origin)) {
+    ReportSecurityCheckFailure(
+        RelyingPartySecurityCheckFailure::kOpaqueOrNonSecureOrigin);
+    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
+                                    bad_message::AUTH_INVALID_EFFECTIVE_DOMAIN);
+    InvokeCallbackAndCleanup(std::move(callback),
+                             blink::mojom::AuthenticatorStatus::INVALID_DOMAIN,
+                             nullptr, Focus::kDontCheck);
+    return;
+  }
+
+  if (!IsRelyingPartyIdValid(options->relying_party->id, caller_origin)) {
+    ReportSecurityCheckFailure(
+        RelyingPartySecurityCheckFailure::kRelyingPartyIdInvalid);
+    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
+                                    bad_message::AUTH_INVALID_RELYING_PARTY);
+    InvokeCallbackAndCleanup(std::move(callback),
+                             blink::mojom::AuthenticatorStatus::INVALID_DOMAIN,
+                             nullptr, Focus::kDontCheck);
+    return;
+  }
+
+  caller_origin_ = caller_origin;
+  relying_party_id_ = options->relying_party->id;
+
   UpdateRequestDelegate();
   if (!request_delegate_) {
     InvokeCallbackAndCleanup(std::move(callback),
@@ -613,31 +639,6 @@
     return;
   }
 
-  caller_origin_ = caller_origin;
-  relying_party_id_ = options->relying_party->id;
-
-  if (!HasValidEffectiveDomain(caller_origin_)) {
-    ReportSecurityCheckFailure(
-        RelyingPartySecurityCheckFailure::kOpaqueOrNonSecureOrigin);
-    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
-                                    bad_message::AUTH_INVALID_EFFECTIVE_DOMAIN);
-    InvokeCallbackAndCleanup(std::move(callback),
-                             blink::mojom::AuthenticatorStatus::INVALID_DOMAIN,
-                             nullptr, Focus::kDontCheck);
-    return;
-  }
-
-  if (!IsRelyingPartyIdValid(relying_party_id_, caller_origin_)) {
-    ReportSecurityCheckFailure(
-        RelyingPartySecurityCheckFailure::kRelyingPartyIdInvalid);
-    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
-                                    bad_message::AUTH_INVALID_RELYING_PARTY);
-    InvokeCallbackAndCleanup(std::move(callback),
-                             blink::mojom::AuthenticatorStatus::INVALID_DOMAIN,
-                             nullptr, Focus::kDontCheck);
-    return;
-  }
-
   if (options->authenticator_selection &&
       options->authenticator_selection->require_resident_key &&
       (!base::FeatureList::IsEnabled(device::kWebAuthResidentKeys) ||
@@ -731,6 +732,10 @@
       base::BindRepeating(
           &device::FidoRequestHandlerBase::InitiatePairingWithDevice,
           request_->GetWeakPtr()) /* ble_pairing_callback */);
+  if (options->authenticator_selection &&
+      options->authenticator_selection->require_resident_key) {
+    request_delegate_->SetMightCreateResidentCredential(true);
+  }
   request_->set_observer(request_delegate_.get());
 
   request_->SetPlatformAuthenticatorOrMarkUnavailable(
@@ -758,6 +763,31 @@
   }
   DCHECK(!request_);
 
+  if (!HasValidEffectiveDomain(caller_origin)) {
+    ReportSecurityCheckFailure(
+        RelyingPartySecurityCheckFailure::kOpaqueOrNonSecureOrigin);
+    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
+                                    bad_message::AUTH_INVALID_EFFECTIVE_DOMAIN);
+    InvokeCallbackAndCleanup(std::move(callback),
+                             blink::mojom::AuthenticatorStatus::INVALID_DOMAIN,
+                             nullptr);
+    return;
+  }
+
+  if (!IsRelyingPartyIdValid(options->relying_party_id, caller_origin)) {
+    ReportSecurityCheckFailure(
+        RelyingPartySecurityCheckFailure::kRelyingPartyIdInvalid);
+    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
+                                    bad_message::AUTH_INVALID_RELYING_PARTY);
+    InvokeCallbackAndCleanup(std::move(callback),
+                             blink::mojom::AuthenticatorStatus::INVALID_DOMAIN,
+                             nullptr);
+    return;
+  }
+
+  caller_origin_ = caller_origin;
+  relying_party_id_ = options->relying_party_id;
+
   UpdateRequestDelegate();
   if (!request_delegate_) {
     InvokeCallbackAndCleanup(std::move(callback),
@@ -766,8 +796,6 @@
     return;
   }
 
-  caller_origin_ = caller_origin;
-
   // Save client data to return with the authenticator response.
   // TODO(kpaulhamus): Fetch and add the Channel ID/Token Binding ID public key
   // used to communicate with the origin.
@@ -785,35 +813,13 @@
         std::move(options->challenge));
   }
 
-  if (!HasValidEffectiveDomain(caller_origin_)) {
-    ReportSecurityCheckFailure(
-        RelyingPartySecurityCheckFailure::kOpaqueOrNonSecureOrigin);
-    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
-                                    bad_message::AUTH_INVALID_EFFECTIVE_DOMAIN);
-    InvokeCallbackAndCleanup(std::move(callback),
-                             blink::mojom::AuthenticatorStatus::INVALID_DOMAIN,
-                             nullptr);
-    return;
-  }
-
-  if (!IsRelyingPartyIdValid(options->relying_party_id, caller_origin_)) {
-    ReportSecurityCheckFailure(
-        RelyingPartySecurityCheckFailure::kRelyingPartyIdInvalid);
-    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
-                                    bad_message::AUTH_INVALID_RELYING_PARTY);
-    InvokeCallbackAndCleanup(std::move(callback),
-                             blink::mojom::AuthenticatorStatus::INVALID_DOMAIN,
-                             nullptr);
-    return;
-  }
-
   if (options->allow_credentials.empty() &&
       (!base::FeatureList::IsEnabled(device::kWebAuthResidentKeys) ||
-        !request_delegate_->SupportsResidentKeys())) {
-      InvokeCallbackAndCleanup(
-          std::move(callback),
-          blink::mojom::AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED);
-      return;
+       !request_delegate_->SupportsResidentKeys())) {
+    InvokeCallbackAndCleanup(
+        std::move(callback),
+        blink::mojom::AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED);
+    return;
   }
 
   if (options->appid) {
@@ -879,6 +885,8 @@
 void AuthenticatorCommon::IsUserVerifyingPlatformAuthenticatorAvailable(
     blink::mojom::Authenticator::
         IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
+  const std::string relying_party_id =
+      render_frame_host_->GetLastCommittedOrigin().host();
   // Use |request_delegate_| if a request is currently in progress; or create a
   // temporary request delegate otherwise.
   //
@@ -888,7 +896,7 @@
       request_delegate_
           ? nullptr
           : GetContentClient()->browser()->GetWebAuthenticationRequestDelegate(
-                render_frame_host_);
+                render_frame_host_, relying_party_id);
   AuthenticatorRequestClientDelegate* request_delegate_ptr =
       request_delegate_ ? request_delegate_.get()
                         : maybe_request_delegate.get();
@@ -1331,6 +1339,8 @@
   get_assertion_response_callback_.Reset();
   client_data_json_.clear();
   app_id_.reset();
+  caller_origin_ = url::Origin();
+  relying_party_id_.clear();
   attestation_requested_ = false;
   error_awaiting_user_acknowledgement_ =
       blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR;
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index d6337e6..788e95b 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -1397,7 +1397,8 @@
  public:
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host) override {
+      RenderFrameHost* render_frame_host,
+      const std::string& relying_party_id) override {
     if (return_null_delegate)
       return nullptr;
     return std::make_unique<TestAuthenticatorRequestDelegate>(
@@ -2682,7 +2683,8 @@
  public:
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host) override {
+      RenderFrameHost* render_frame_host,
+      const std::string& relying_party_id) override {
     return std::make_unique<PINTestAuthenticatorRequestDelegate>(
         supports_pin, expected, &failure_reason);
   }
@@ -3216,9 +3218,11 @@
  public:
   ResidentKeyTestAuthenticatorRequestDelegate(
       std::string expected_accounts,
-      std::vector<uint8_t> selected_user_id)
+      std::vector<uint8_t> selected_user_id,
+      bool* might_create_resident_credential)
       : expected_accounts_(expected_accounts),
-        selected_user_id_(selected_user_id) {}
+        selected_user_id_(selected_user_id),
+        might_create_resident_credential_(might_create_resident_credential) {}
 
   bool SupportsPIN() const override { return true; }
 
@@ -3267,9 +3271,14 @@
         FROM_HERE, base::BindOnce(std::move(callback), std::move(*selected)));
   }
 
+  void SetMightCreateResidentCredential(bool v) override {
+    *might_create_resident_credential_ = v;
+  }
+
  private:
   const std::string expected_accounts_;
   const std::vector<uint8_t> selected_user_id_;
+  bool* const might_create_resident_credential_;
   DISALLOW_COPY_AND_ASSIGN(ResidentKeyTestAuthenticatorRequestDelegate);
 };
 
@@ -3278,13 +3287,15 @@
  public:
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host) override {
+      RenderFrameHost* render_frame_host,
+      const std::string& relying_party_id) override {
     return std::make_unique<ResidentKeyTestAuthenticatorRequestDelegate>(
-        expected_accounts, selected_user_id);
+        expected_accounts, selected_user_id, &might_create_resident_credential);
   }
 
   std::string expected_accounts;
   std::vector<uint8_t> selected_user_id;
+  bool might_create_resident_credential = false;
 };
 
 class ResidentKeyAuthenticatorImplTest : public UVAuthenticatorImplTest {
@@ -3339,24 +3350,39 @@
 TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredential) {
   TestServiceManagerContext smc;
   AuthenticatorPtr authenticator = ConnectToAuthenticator();
-  TestMakeCredentialCallback callback_receiver;
-  authenticator->MakeCredential(make_credential_options(),
-                                callback_receiver.callback());
-  callback_receiver.WaitForCallback();
-  EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
 
-  EXPECT_TRUE(HasUV(callback_receiver));
-  ASSERT_EQ(1u, virtual_device_.mutable_state()->registrations.size());
-  const device::VirtualFidoDevice::RegistrationData& registration =
-      virtual_device_.mutable_state()->registrations.begin()->second;
-  EXPECT_TRUE(registration.is_resident);
-  ASSERT_TRUE(registration.user.has_value());
-  const auto options = make_credential_options();
-  EXPECT_EQ(options->user->name, registration.user->user_name());
-  EXPECT_EQ(options->user->display_name,
-            registration.user->user_display_name());
-  EXPECT_EQ(options->user->id, registration.user->user_id());
-  EXPECT_EQ(options->user->icon, registration.user->user_icon_url());
+  for (const bool internal_uv : {false, true}) {
+    SCOPED_TRACE(::testing::Message() << "internal_uv=" << internal_uv);
+    test_client_.might_create_resident_credential = false;
+
+    if (internal_uv) {
+      device::VirtualCtap2Device::Config config;
+      config.resident_key_support = true;
+      config.internal_uv_support = true;
+      virtual_device_.SetCtap2Config(config);
+      virtual_device_.mutable_state()->fingerprints_enrolled = true;
+    }
+
+    TestMakeCredentialCallback callback_receiver;
+    authenticator->MakeCredential(make_credential_options(),
+                                  callback_receiver.callback());
+    callback_receiver.WaitForCallback();
+    EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
+
+    EXPECT_TRUE(test_client_.might_create_resident_credential);
+    EXPECT_TRUE(HasUV(callback_receiver));
+    ASSERT_EQ(1u, virtual_device_.mutable_state()->registrations.size());
+    const device::VirtualFidoDevice::RegistrationData& registration =
+        virtual_device_.mutable_state()->registrations.begin()->second;
+    EXPECT_TRUE(registration.is_resident);
+    ASSERT_TRUE(registration.user.has_value());
+    const auto options = make_credential_options();
+    EXPECT_EQ(options->user->name, registration.user->user_name());
+    EXPECT_EQ(options->user->display_name,
+              registration.user->user_display_name());
+    EXPECT_EQ(options->user->id, registration.user->user_id());
+    EXPECT_EQ(options->user->icon, registration.user->user_icon_url());
+  }
 }
 
 TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionSingle) {
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc
index ac765d0..8c00e09 100644
--- a/content/browser/webauth/webauth_browsertest.cc
+++ b/content/browser/webauth/webauth_browsertest.cc
@@ -289,7 +289,8 @@
 
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host) override {
+      RenderFrameHost* render_frame_host,
+      const std::string& relying_party_id) override {
     test_state_->delegate_create_count++;
     return std::make_unique<WebAuthBrowserTestClientDelegate>(test_state_);
   }
diff --git a/content/public/browser/authenticator_request_client_delegate.cc b/content/public/browser/authenticator_request_client_delegate.cc
index dc2a910..c789d428 100644
--- a/content/public/browser/authenticator_request_client_delegate.cc
+++ b/content/public/browser/authenticator_request_client_delegate.cc
@@ -42,6 +42,9 @@
   return false;
 }
 
+void AuthenticatorRequestClientDelegate::SetMightCreateResidentCredential(
+    bool v) {}
+
 void AuthenticatorRequestClientDelegate::SelectAccount(
     std::vector<device::AuthenticatorGetAssertionResponse> responses,
     base::OnceCallback<void(device::AuthenticatorGetAssertionResponse)>
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index d4ef4b9..6d34ff72 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -92,6 +92,12 @@
   // |SelectAccount| will never be called.
   virtual bool SupportsResidentKeys();
 
+  // SetMightCreateResidentCredential indicates whether activating an
+  // authenticator may cause a resident credential to be created. A resident
+  // credential may be discovered by someone with physical access to the
+  // authenticator and thus has privacy implications.
+  void SetMightCreateResidentCredential(bool v) override;
+
   // SelectAccount is called to allow the embedder to select between one or more
   // accounts. This is triggered when the web page requests an unspecified
   // credential (by passing an empty allow-list). In this case, any accounts
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index d4cc92d3..13cad74 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -862,7 +862,8 @@
 
 std::unique_ptr<AuthenticatorRequestClientDelegate>
 ContentBrowserClient::GetWebAuthenticationRequestDelegate(
-    RenderFrameHost* render_frame_host) {
+    RenderFrameHost* render_frame_host,
+    const std::string& relying_party_id) {
   return std::make_unique<AuthenticatorRequestClientDelegate>();
 }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index b7eb0de..fc58b2d 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1386,9 +1386,11 @@
   // being serviced in a given RenderFrame. The instance is guaranteed to be
   // destroyed before the RenderFrame goes out of scope. The embedder may choose
   // to return nullptr to indicate that the request cannot be serviced right
-  // now.
+  // now. |relying_party_id| is the RP ID from Webauthn, essentially a domain
+  // name.
   virtual std::unique_ptr<AuthenticatorRequestClientDelegate>
-  GetWebAuthenticationRequestDelegate(RenderFrameHost* render_frame_host);
+  GetWebAuthenticationRequestDelegate(RenderFrameHost* render_frame_host,
+                                      const std::string& relying_party_id);
 
 #if defined(OS_MACOSX)
   // Returns whether WebAuthn supports the built-in Touch ID platform
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-android.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-android.txt
new file mode 100644
index 0000000..a67ef11e
--- /dev/null
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-android.txt
@@ -0,0 +1,8 @@
+android.webkit.WebView focusable focused scrollable
+++android.view.View
+++++android.view.View name='Choose one:'
+++++android.widget.Spinner role_description='pop up button' clickable focusable name='Choose one: Foo'
+++++android.widget.ListView role_description='list box' clickable collection focusable name='Choose one:' item_count=3 row_count=3
+++++++android.view.View clickable collection_item focusable name='Baz'
+++++++android.view.View clickable collection_item focusable name='Bar' item_index=1 row_index=1
+++++++android.view.View clickable collection_item focusable name='Foo' item_index=2 row_index=2
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-auralinux.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-auralinux.txt
new file mode 100644
index 0000000..c0387aab
--- /dev/null
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-auralinux.txt
@@ -0,0 +1,10 @@
+[document web]
+++[section]
+++++[section] label-for
+++++++[text] name='Choose one:'
+++++[push button] name='Choose one: Foo' labelled-by
+++++++[text] name='Foo'
+++++[list box] name='Choose one:' labelled-by
+++++++[list item] name='Baz' selectable
+++++++[list item] name='Bar' selectable
+++++++[list item] name='Foo' selectable
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-blink.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-blink.txt
new file mode 100644
index 0000000..6096db6
--- /dev/null
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-blink.txt
@@ -0,0 +1,12 @@
+rootWebArea
+++genericContainer
+++++genericContainer
+++++++staticText name='Choose one:'
+++++++++inlineTextBox name='Choose one:'
+++++popUpButton name='Choose one: Foo'
+++++++staticText name='Foo'
+++++++++inlineTextBox name='Foo'
+++++listBox name='Choose one:'
+++++++listBoxOption name='Baz' selected=false
+++++++listBoxOption name='Bar' selected=false
+++++++listBoxOption name='Foo' selected=false
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-mac.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-mac.txt
new file mode 100644
index 0000000..734aa9b
--- /dev/null
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-mac.txt
@@ -0,0 +1,10 @@
+AXWebArea
+++AXGroup
+++++AXGroup
+++++++AXStaticText AXValue='Choose one:'
+++++AXPopUpButton AXTitle='Choose one: Foo'
+++++++AXStaticText AXValue='Foo'
+++++AXList AXTitle='Choose one:'
+++++++AXStaticText AXValue='Baz'
+++++++AXStaticText AXValue='Bar'
+++++++AXStaticText AXValue='Foo'
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-uia-win.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-uia-win.txt
new file mode 100644
index 0000000..96676848
--- /dev/null
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-uia-win.txt
@@ -0,0 +1,11 @@
+region
+++document
+++++group
+++++++group
+++++++++description Name='Choose one:'
+++++++menu Name='Choose one: Foo' ExpandCollapse.ExpandCollapseState='LeafNode'
+++++++++description Name='Foo'
+++++++listbox Name='Choose one:' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++++++option Name='Baz' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
+++++++++option Name='Bar' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
+++++++++option Name='Foo' SelectionItem.IsSelected=false SelectionItem.SelectionContainer='Choose one:'
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-win.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-win.txt
new file mode 100644
index 0000000..ece56cb
--- /dev/null
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-win.txt
@@ -0,0 +1,10 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++IA2_ROLE_SECTION
+++++IA2_ROLE_SECTION
+++++++ROLE_SYSTEM_STATICTEXT name='Choose one:'
+++++ROLE_SYSTEM_BUTTONMENU name='Choose one: Foo' FOCUSABLE HASPOPUP
+++++++ROLE_SYSTEM_STATICTEXT name='Foo'
+++++ROLE_SYSTEM_LIST name='Choose one:' FOCUSABLE
+++++++ROLE_SYSTEM_LISTITEM name='Baz' FOCUSABLE
+++++++ROLE_SYSTEM_LISTITEM name='Bar' FOCUSABLE
+++++++ROLE_SYSTEM_LISTITEM name='Foo' FOCUSABLE
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup.html b/content/test/data/accessibility/html/button-with-listbox-popup.html
new file mode 100644
index 0000000..263529d5
--- /dev/null
+++ b/content/test/data/accessibility/html/button-with-listbox-popup.html
@@ -0,0 +1,13 @@
+<html>
+<body>
+<div id="test1">
+  <span id="span">Choose one:</span>
+  <button aria-haspopup="listbox" aria-labelledby="span test" id="test">Foo</button>
+  <ul id="options" tabindex="-1" role="listbox" aria-labelledby="span">
+    <li id="option1" role="option">Baz</li>
+    <li id="option2" role="option">Bar</li>
+    <li id="option3" role="option">Foo</li>
+  </ul>
+</div>
+</body>
+</html>
diff --git a/device/fido/ble_adapter_manager_unittest.cc b/device/fido/ble_adapter_manager_unittest.cc
index 7358421..42980cfb 100644
--- a/device/fido/ble_adapter_manager_unittest.cc
+++ b/device/fido/ble_adapter_manager_unittest.cc
@@ -54,6 +54,7 @@
                void(base::Optional<int>,
                     base::OnceCallback<void(std::string)>));
   MOCK_METHOD0(FinishCollectPIN, void());
+  MOCK_METHOD1(SetMightCreateResidentCredential, void(bool));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockObserver);
diff --git a/device/fido/ctap2_device_operation.h b/device/fido/ctap2_device_operation.h
index cc194b6..4bda200 100644
--- a/device/fido/ctap2_device_operation.h
+++ b/device/fido/ctap2_device_operation.h
@@ -90,6 +90,18 @@
                        weak_factory_.GetWeakPtr()));
   }
 
+  // Cancel requests that the operation be canceled. This is safe to call at any
+  // time but may not be effective because the operation may have already
+  // completed or the device may not support cancelation. Even if canceled, the
+  // callback will still be invoked, albeit perhaps with a status of
+  // |kCtap2ErrKeepAliveCancel|.
+  void Cancel() override {
+    if (this->token_) {
+      this->device()->Cancel(*this->token_);
+      this->token_.reset();
+    }
+  }
+
   void OnResponseReceived(
       base::Optional<std::vector<uint8_t>> device_response) {
     this->token_.reset();
diff --git a/device/fido/device_operation.h b/device/fido/device_operation.h
index 59793db..debcb4f 100644
--- a/device/fido/device_operation.h
+++ b/device/fido/device_operation.h
@@ -50,18 +50,6 @@
 
   virtual ~DeviceOperation() = default;
 
-  // Cancel requests that the operation be canceled. This is safe to call at any
-  // time but may not be effective because the operation may have already
-  // completed or the device may not support cancelation. Even if canceled, the
-  // callback will still be invoked, albeit perhaps with a status of
-  // |kCtap2ErrKeepAliveCancel|.
-  void Cancel() override {
-    if (token_) {
-      device_->Cancel(*token_);
-      token_.reset();
-    }
-  }
-
  protected:
   // TODO(hongjunchoi): Refactor so that |command| is never base::nullopt.
   void DispatchDeviceRequest(base::Optional<std::vector<uint8_t>> command,
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h
index f76f20cdf..e100d32 100644
--- a/device/fido/fido_request_handler_base.h
+++ b/device/fido/fido_request_handler_base.h
@@ -74,10 +74,8 @@
         const TransportAvailabilityInfo& other);
     ~TransportAvailabilityInfo();
 
-    // TODO(hongjunchoi): Factor |rp_id| and |request_type| from
-    // TransportAvailabilityInfo.
+    // TODO(hongjunchoi): Factor |request_type| from TransportAvailabilityInfo.
     // See: https://crbug.com/875011
-    std::string rp_id;
     RequestType request_type = RequestType::kMakeCredential;
 
     // The intersection of transports supported by the client and allowed by the
@@ -149,6 +147,13 @@
 
     // CollectClientPin is guaranteed to have been called previously.
     virtual void FinishCollectPIN() = 0;
+
+    // SetMightCreateResidentCredential indicates whether the activation of an
+    // authenticator may cause a resident credential to be created. A resident
+    // credential may be discovered by someone with physical access to the
+    // authenticator and thus has privacy implications. Initially, this is
+    // assumed to be false.
+    virtual void SetMightCreateResidentCredential(bool v) = 0;
   };
 
   // TODO(https://crbug.com/769631): Remove the dependency on Connector once
diff --git a/device/fido/fido_request_handler_unittest.cc b/device/fido/fido_request_handler_unittest.cc
index 1d196b5..1cdd155 100644
--- a/device/fido/fido_request_handler_unittest.cc
+++ b/device/fido/fido_request_handler_unittest.cc
@@ -135,6 +135,8 @@
     NOTREACHED();
   }
 
+  void SetMightCreateResidentCredential(bool v) override {}
+
   void FinishCollectPIN() override { NOTREACHED(); }
 
  private:
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index 1a3d33b..c138ed83 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -49,42 +49,6 @@
 
 }  // namespace
 
-class TestObserver : public FidoRequestHandlerBase::Observer {
- public:
-  TestObserver() {}
-  ~TestObserver() override {}
-
-  void set_controls_dispatch(bool controls_dispatch) {
-    controls_dispatch_ = controls_dispatch;
-  }
-
- private:
-  // FidoRequestHandlerBase::Observer:
-  void OnTransportAvailabilityEnumerated(
-      FidoRequestHandlerBase::TransportAvailabilityInfo data) override {}
-  bool EmbedderControlsAuthenticatorDispatch(
-      const FidoAuthenticator&) override {
-    return controls_dispatch_;
-  }
-  void BluetoothAdapterPowerChanged(bool is_powered_on) override {}
-  void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator) override {
-  }
-  void FidoAuthenticatorRemoved(base::StringPiece device_id) override {}
-  void FidoAuthenticatorIdChanged(base::StringPiece old_authenticator_id,
-                                  std::string new_authenticator_id) override {}
-  void FidoAuthenticatorPairingModeChanged(base::StringPiece authenticator_id,
-                                           bool is_in_pairing_mode) override {}
-  bool SupportsPIN() const override { return false; }
-  void CollectPIN(
-      base::Optional<int> attempts,
-      base::OnceCallback<void(std::string)> provide_pin_cb) override {
-    NOTREACHED();
-  }
-  void FinishCollectPIN() override { NOTREACHED(); }
-
-  bool controls_dispatch_ = false;
-};
-
 // FidoGetAssertionHandlerTest allows testing GetAssertionRequestHandler against
 // MockFidoDevices injected via a ScopedFakeFidoDiscoveryFactory.
 class FidoGetAssertionHandlerTest : public ::testing::Test {
@@ -230,8 +194,6 @@
 
   EXPECT_EQ(FidoRequestHandlerBase::RequestType::kGetAssertion,
             request_handler->transport_availability_info().request_type);
-  EXPECT_EQ(test_data::kRelyingPartyId,
-            request_handler->transport_availability_info().rp_id);
 }
 
 TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
@@ -740,6 +702,44 @@
 }
 
 #if defined(OS_WIN)
+
+class TestObserver : public FidoRequestHandlerBase::Observer {
+ public:
+  TestObserver() {}
+  ~TestObserver() override {}
+
+  void set_controls_dispatch(bool controls_dispatch) {
+    controls_dispatch_ = controls_dispatch;
+  }
+
+ private:
+  // FidoRequestHandlerBase::Observer:
+  void OnTransportAvailabilityEnumerated(
+      FidoRequestHandlerBase::TransportAvailabilityInfo data) override {}
+  bool EmbedderControlsAuthenticatorDispatch(
+      const FidoAuthenticator&) override {
+    return controls_dispatch_;
+  }
+  void BluetoothAdapterPowerChanged(bool is_powered_on) override {}
+  void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator) override {
+  }
+  void FidoAuthenticatorRemoved(base::StringPiece device_id) override {}
+  void FidoAuthenticatorIdChanged(base::StringPiece old_authenticator_id,
+                                  std::string new_authenticator_id) override {}
+  void FidoAuthenticatorPairingModeChanged(base::StringPiece authenticator_id,
+                                           bool is_in_pairing_mode) override {}
+  bool SupportsPIN() const override { return false; }
+  void CollectPIN(
+      base::Optional<int> attempts,
+      base::OnceCallback<void(std::string)> provide_pin_cb) override {
+    NOTREACHED();
+  }
+  void FinishCollectPIN() override { NOTREACHED(); }
+  void SetMightCreateResidentCredential(bool v) override {}
+
+  bool controls_dispatch_ = false;
+};
+
 // Verify that the request handler instantiates a HID device backed
 // FidoDeviceAuthenticator or a WinNativeCrossPlatformAuthenticator, depending
 // on API availability.
@@ -779,6 +779,7 @@
         handler->AuthenticatorsForTesting().begin()->second->GetId());
   }
 }
+
 #endif  // defined(OS_WIN)
 
 }  // namespace device
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index 4cb6003..c3e700e 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -189,7 +189,6 @@
           std::move(completion_callback)),
       request_(std::move(request)),
       weak_factory_(this) {
-  transport_availability_info().rp_id = request_.rp_id();
   transport_availability_info().request_type =
       FidoRequestHandlerBase::RequestType::kGetAssertion;
 
@@ -318,9 +317,9 @@
          authenticator->WillNeedPINToGetAssertion(request_, observer()) ==
              FidoAuthenticator::GetAssertionPINDisposition::kNoPIN);
 
+  state_ = State::kFinished;
   CancelActiveAuthenticators(authenticator->GetId());
 
-  state_ = State::kFinished;
   if (response_code != CtapDeviceResponseCode::kSuccess) {
     FIDO_LOG(ERROR) << "Failing assertion request due to status "
                     << static_cast<int>(response_code) << " from "
@@ -407,8 +406,8 @@
              FidoAuthenticator::GetAssertionPINDisposition::kNoPIN);
 
   DCHECK(observer());
-  CancelActiveAuthenticators(authenticator->GetId());
   state_ = State::kGettingRetries;
+  CancelActiveAuthenticators(authenticator->GetId());
   authenticator_ = authenticator;
   authenticator_->GetRetries(
       base::BindOnce(&GetAssertionRequestHandler::OnRetriesResponse,
@@ -418,6 +417,7 @@
 void GetAssertionRequestHandler::HandleInapplicableAuthenticator(
     FidoAuthenticator* authenticator) {
   // User touched an authenticator that cannot handle this request.
+  state_ = State::kFinished;
   CancelActiveAuthenticators(authenticator->GetId());
   std::move(completion_callback_)
       .Run(FidoReturnCode::kUserConsentButCredentialNotRecognized,
diff --git a/device/fido/get_assertion_task.cc b/device/fido/get_assertion_task.cc
index b531c9b..39e3031b 100644
--- a/device/fido/get_assertion_task.cc
+++ b/device/fido/get_assertion_task.cc
@@ -50,6 +50,8 @@
 GetAssertionTask::~GetAssertionTask() = default;
 
 void GetAssertionTask::Cancel() {
+  canceled_ = true;
+
   if (sign_operation_) {
     sign_operation_->Cancel();
   }
@@ -114,6 +116,10 @@
 void GetAssertionTask::HandleResponse(
     CtapDeviceResponseCode response_code,
     base::Optional<AuthenticatorGetAssertionResponse> response_data) {
+  if (canceled_) {
+    return;
+  }
+
   // Some authenticators will return this error before waiting for a touch if
   // they don't recognise a credential. In other cases the result can be
   // returned immediately.
@@ -138,6 +144,10 @@
     base::Optional<AuthenticatorGetAssertionResponse> response_data) {
   DCHECK(request_.allow_list() && request_.allow_list()->size() > 0);
 
+  if (canceled_) {
+    return;
+  }
+
   // Credential was recognized by the device. As this authentication was a
   // silent authentication (i.e. user touch was not provided), try again with
   // only the matching credential, user presence enforced and with the original
diff --git a/device/fido/get_assertion_task.h b/device/fido/get_assertion_task.h
index f91d89dc..195d59a 100644
--- a/device/fido/get_assertion_task.h
+++ b/device/fido/get_assertion_task.h
@@ -79,6 +79,7 @@
   std::unique_ptr<RegisterOperation> dummy_register_operation_;
   GetAssertionTaskCallback callback_;
   size_t current_credential_ = 0;
+  bool canceled_ = false;
 
   base::WeakPtrFactory<GetAssertionTask> weak_factory_;
 
diff --git a/device/fido/hid/fido_hid_device.cc b/device/fido/hid/fido_hid_device.cc
index 89e03b8..caafe5f 100644
--- a/device/fido/hid/fido_hid_device.cc
+++ b/device/fido/hid/fido_hid_device.cc
@@ -98,8 +98,11 @@
   }
 }
 
-// TODO(agl): maybe Transition should take the next step to move to?
-void FidoHidDevice::Transition() {
+void FidoHidDevice::Transition(base::Optional<State> next_state) {
+  if (next_state) {
+    state_ = *next_state;
+  }
+
   switch (state_) {
     case State::kInit:
       state_ = State::kConnecting;
@@ -164,8 +167,7 @@
   timeout_callback_.Cancel();
 
   if (!connection) {
-    state_ = State::kDeviceError;
-    Transition();
+    Transition(State::kDeviceError);
     return;
   }
 
@@ -194,8 +196,7 @@
   }
 
   if (!success) {
-    state_ = State::kDeviceError;
-    Transition();
+    Transition(State::kDeviceError);
   }
 
   connection_->Read(base::BindOnce(&FidoHidDevice::OnPotentialInitReply,
@@ -248,8 +249,7 @@
   }
 
   if (!success) {
-    state_ = State::kDeviceError;
-    Transition();
+    Transition(State::kDeviceError);
     return;
   }
   DCHECK(buf);
@@ -268,8 +268,7 @@
 
   timeout_callback_.Cancel();
   channel_id_ = *maybe_channel_id;
-  state_ = State::kReady;
-  Transition();
+  Transition(State::kReady);
 }
 
 void FidoHidDevice::WriteMessage(FidoHidMessage message) {
@@ -292,8 +291,7 @@
 
   DCHECK_EQ(State::kBusy, state_);
   if (!success) {
-    state_ = State::kDeviceError;
-    Transition();
+    Transition(State::kDeviceError);
     return;
   }
 
@@ -332,16 +330,14 @@
   DCHECK_EQ(State::kBusy, state_);
 
   if (!success) {
-    state_ = State::kDeviceError;
-    Transition();
+    Transition(State::kDeviceError);
     return;
   }
   DCHECK(buf);
 
   auto message = FidoHidMessage::CreateFromSerializedData(*buf);
   if (!message) {
-    state_ = State::kDeviceError;
-    Transition();
+    Transition(State::kDeviceError);
     return;
   }
 
@@ -392,8 +388,7 @@
   }
 
   if (!success) {
-    state_ = State::kDeviceError;
-    Transition();
+    Transition(State::kDeviceError);
     return;
   }
   DCHECK(buf);
@@ -415,19 +410,47 @@
   const auto cmd = message.cmd();
   auto response = message.GetMessagePayload();
   if (cmd != FidoHidDeviceCommand::kMsg && cmd != FidoHidDeviceCommand::kCbor) {
-    // TODO(agl): inline |ProcessHidError|, or maybe have it call |Transition|.
-    ProcessHidError(cmd, response);
-    Transition();
+    if (cmd != FidoHidDeviceCommand::kError || response.size() != 1) {
+      FIDO_LOG(ERROR) << "Unknown HID message received: "
+                      << static_cast<int>(cmd) << " "
+                      << base::HexEncode(response.data(), response.size());
+      Transition(State::kDeviceError);
+      return;
+    }
+
+    // HID transport layer error constants that are returned to the client.
+    // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#ctaphid-commands
+    enum class HidErrorConstant : uint8_t {
+      kInvalidCommand = 0x01,
+      kInvalidParameter = 0x02,
+      kInvalidLength = 0x03,
+      // (Other errors omitted.)
+    };
+
+    switch (static_cast<HidErrorConstant>(response[0])) {
+      case HidErrorConstant::kInvalidCommand:
+      case HidErrorConstant::kInvalidParameter:
+      case HidErrorConstant::kInvalidLength:
+        Transition(State::kMsgError);
+        break;
+      default:
+        FIDO_LOG(ERROR) << "HID error received: "
+                        << static_cast<int>(response[0]);
+        Transition(State::kDeviceError);
+    }
+
     return;
   }
 
-  state_ = State::kReady;
   DCHECK(!pending_transactions_.empty());
   auto callback = std::move(pending_transactions_.front().callback);
   pending_transactions_.pop_front();
   current_token_ = FidoDevice::kInvalidCancelToken;
 
   base::WeakPtr<FidoHidDevice> self = weak_factory_.GetWeakPtr();
+  // The callback may call back into this object thus |state_| is set ahead of
+  // time.
+  state_ = State::kReady;
   std::move(callback).Run(std::move(response));
 
   // Executing |callback| may have freed |this|. Check |self| first.
@@ -446,44 +469,7 @@
 }
 
 void FidoHidDevice::OnTimeout() {
-  state_ = State::kDeviceError;
-  Transition();
-}
-
-void FidoHidDevice::ProcessHidError(FidoHidDeviceCommand cmd,
-                                    base::span<const uint8_t> payload) {
-  if (cmd != FidoHidDeviceCommand::kError || payload.size() != 1) {
-    FIDO_LOG(ERROR) << "Unknown HID message received: " << static_cast<int>(cmd)
-                    << " " << base::HexEncode(payload.data(), payload.size());
-    state_ = State::kDeviceError;
-    return;
-  }
-
-  // HID transport layer error constants that are returned to the client.
-  // Carried in the payload section of the Error command.
-  // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#ctaphid-commands
-  enum class HidErrorConstant : uint8_t {
-    kInvalidCommand = 0x01,
-    kInvalidParameter = 0x02,
-    kInvalidLength = 0x03,
-    kInvalidSequence = 0x04,
-    kTimeout = 0x05,
-    kBusy = 0x06,
-    kLockRequired = 0x0a,
-    kInvalidChannel = 0x0b,
-    kOther = 0x7f,
-  };
-
-  switch (static_cast<HidErrorConstant>(payload[0])) {
-    case HidErrorConstant::kInvalidCommand:
-    case HidErrorConstant::kInvalidParameter:
-    case HidErrorConstant::kInvalidLength:
-      state_ = State::kMsgError;
-      break;
-    default:
-      FIDO_LOG(ERROR) << "HID error received: " << static_cast<int>(payload[0]);
-      state_ = State::kDeviceError;
-  }
+  Transition(State::kDeviceError);
 }
 
 void FidoHidDevice::WriteCancel() {
diff --git a/device/fido/hid/fido_hid_device.h b/device/fido/hid/fido_hid_device.h
index 82cef29..89db992 100644
--- a/device/fido/hid/fido_hid_device.h
+++ b/device/fido/hid/fido_hid_device.h
@@ -89,7 +89,7 @@
     CancelToken token;
   };
 
-  void Transition();
+  void Transition(base::Optional<State> next_state = base::nullopt);
 
   // Open a connection to this device.
   void Connect(device::mojom::HidManager::ConnectCallback callback);
@@ -118,8 +118,6 @@
   void ArmTimeout();
   void OnTimeout();
   void WriteCancel();
-  void ProcessHidError(FidoHidDeviceCommand cmd,
-                       base::span<const uint8_t> payload);
 
   base::WeakPtr<FidoDevice> GetWeakPtr() override;
 
diff --git a/device/fido/make_credential_handler_unittest.cc b/device/fido/make_credential_handler_unittest.cc
index dd9971b..39f7f5e8 100644
--- a/device/fido/make_credential_handler_unittest.cc
+++ b/device/fido/make_credential_handler_unittest.cc
@@ -158,8 +158,6 @@
 
   EXPECT_EQ(FidoRequestHandlerBase::RequestType::kMakeCredential,
             request_handler->transport_availability_info().request_type);
-  EXPECT_EQ(test_data::kRelyingPartyId,
-            request_handler->transport_availability_info().rp_id);
 }
 
 TEST_F(FidoMakeCredentialHandlerTest, TestCtap2MakeCredential) {
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index 1e20ec10..4388977 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -125,7 +125,6 @@
       authenticator_selection_criteria_(
           std::move(authenticator_selection_criteria)),
       weak_factory_(this) {
-  transport_availability_info().rp_id = request_.rp().rp_id();
   transport_availability_info().request_type =
       FidoRequestHandlerBase::RequestType::kMakeCredential;
 
@@ -260,9 +259,9 @@
          authenticator->WillNeedPINToMakeCredential(request_, observer()) ==
              MakeCredentialPINDisposition::kNoPIN);
 
+  state_ = State::kFinished;
   CancelActiveAuthenticators(authenticator->GetId());
 
-  state_ = State::kFinished;
   if (response_code != CtapDeviceResponseCode::kSuccess) {
     OnAuthenticatorResponse(authenticator, response_code, base::nullopt);
     return;
@@ -292,8 +291,8 @@
     case MakeCredentialPINDisposition::kUsePIN:
       // Will need to get PIN to handle this request.
       DCHECK(observer());
-      CancelActiveAuthenticators(authenticator->GetId());
       state_ = State::kGettingRetries;
+      CancelActiveAuthenticators(authenticator->GetId());
       authenticator_ = authenticator;
       authenticator_->GetRetries(
           base::BindOnce(&MakeCredentialRequestHandler::OnRetriesResponse,
@@ -303,8 +302,8 @@
     case MakeCredentialPINDisposition::kSetPIN:
       // Will need to set a PIN to handle this request.
       DCHECK(observer());
-      CancelActiveAuthenticators(authenticator->GetId());
       state_ = State::kWaitingForNewPIN;
+      CancelActiveAuthenticators(authenticator->GetId());
       authenticator_ = authenticator;
       observer()->CollectPIN(
           base::nullopt,
@@ -323,6 +322,7 @@
 void MakeCredentialRequestHandler::HandleInapplicableAuthenticator(
     FidoAuthenticator* authenticator) {
   // User touched an authenticator that cannot handle this request.
+  state_ = State::kFinished;
   CancelActiveAuthenticators(authenticator->GetId());
   const FidoReturnCode capability_error = IsCandidateAuthenticatorPostTouch(
       request_, authenticator, authenticator_selection_criteria_, observer());
diff --git a/device/fido/make_credential_task.cc b/device/fido/make_credential_task.cc
index b0c3092..cc9bbe77 100644
--- a/device/fido/make_credential_task.cc
+++ b/device/fido/make_credential_task.cc
@@ -96,7 +96,8 @@
 }
 
 void MakeCredentialTask::Cancel() {
-  // TODO: should set a flag on this object to stop any more operations.
+  canceled_ = true;
+
   if (register_operation_) {
     register_operation_->Cancel();
   }
@@ -159,6 +160,10 @@
     base::Optional<AuthenticatorGetAssertionResponse> response_data) {
   DCHECK(request_.exclude_list() && request_.exclude_list()->size() > 0);
 
+  if (canceled_) {
+    return;
+  }
+
   // The authenticator recognized a credential from the exclude list. Send the
   // actual request with only that credential in the exclude list to collect a
   // touch and and the CTAP2_ERR_CREDENTIAL_EXCLUDED error code.
diff --git a/device/fido/make_credential_task.h b/device/fido/make_credential_task.h
index 4a53cd1a..6c846ab 100644
--- a/device/fido/make_credential_task.h
+++ b/device/fido/make_credential_task.h
@@ -73,6 +73,7 @@
   std::unique_ptr<SignOperation> silent_sign_operation_;
   MakeCredentialTaskCallback callback_;
   size_t current_credential_ = 0;
+  bool canceled_ = false;
 
   base::WeakPtrFactory<MakeCredentialTask> weak_factory_;
 
diff --git a/device/fido/reset_request_handler.cc b/device/fido/reset_request_handler.cc
index aecad53..873b9232 100644
--- a/device/fido/reset_request_handler.cc
+++ b/device/fido/reset_request_handler.cc
@@ -27,7 +27,6 @@
 
 ResetRequestHandler::~ResetRequestHandler() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
-  CancelActiveAuthenticators();
 }
 
 void ResetRequestHandler::DispatchRequest(FidoAuthenticator* authenticator) {
@@ -46,7 +45,7 @@
   }
 
   processed_touch_ = true;
-  CancelActiveAuthenticators();
+  CancelActiveAuthenticators(authenticator->GetId());
 
   if (authenticator->SupportedProtocol() != ProtocolVersion::kCtap) {
     std::move(finished_callback_)
diff --git a/device/fido/set_pin_request_handler.cc b/device/fido/set_pin_request_handler.cc
index dfe179f..734e9e7 100644
--- a/device/fido/set_pin_request_handler.cc
+++ b/device/fido/set_pin_request_handler.cc
@@ -28,7 +28,6 @@
 
 SetPINRequestHandler::~SetPINRequestHandler() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
-  CancelActiveAuthenticators();
 }
 
 void SetPINRequestHandler::ProvidePIN(const std::string& old_pin,
@@ -78,17 +77,18 @@
   }
 
   authenticator_ = authenticator;
-  CancelActiveAuthenticators();
 
   switch (authenticator_->Options()->client_pin_availability) {
     case AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported:
       state_ = State::kFinished;
+      CancelActiveAuthenticators(authenticator->GetId());
       finished_callback_.Run(CtapDeviceResponseCode::kCtap1ErrInvalidCommand);
       return;
 
     case AuthenticatorSupportedOptions::ClientPinAvailability::
         kSupportedAndPinSet:
       state_ = State::kGettingRetries;
+      CancelActiveAuthenticators(authenticator->GetId());
       authenticator_->GetRetries(
           base::BindOnce(&SetPINRequestHandler::OnRetriesResponse,
                          weak_factory_.GetWeakPtr()));
@@ -97,6 +97,7 @@
     case AuthenticatorSupportedOptions::ClientPinAvailability::
         kSupportedButPinNotSet:
       state_ = State::kWaitingForPIN;
+      CancelActiveAuthenticators(authenticator->GetId());
       std::move(get_pin_callback_).Run(base::nullopt);
       break;
   }
diff --git a/device/fido/u2f_register_operation.cc b/device/fido/u2f_register_operation.cc
index 056edd07..55d5e7f 100644
--- a/device/fido/u2f_register_operation.cc
+++ b/device/fido/u2f_register_operation.cc
@@ -42,6 +42,10 @@
   }
 }
 
+void U2fRegisterOperation::Cancel() {
+  canceled_ = true;
+}
+
 void U2fRegisterOperation::TrySign() {
   DispatchDeviceRequest(
       ConvertToU2fSignCommand(request(), excluded_key_handle()),
@@ -51,6 +55,10 @@
 
 void U2fRegisterOperation::OnCheckForExcludedKeyHandle(
     base::Optional<std::vector<uint8_t>> device_response) {
+  if (canceled_) {
+    return;
+  }
+
   auto result = apdu::ApduResponse::Status::SW_WRONG_DATA;
   if (device_response) {
     const auto apdu_response =
@@ -115,6 +123,10 @@
 
 void U2fRegisterOperation::OnRegisterResponseReceived(
     base::Optional<std::vector<uint8_t>> device_response) {
+  if (canceled_) {
+    return;
+  }
+
   auto result = apdu::ApduResponse::Status::SW_WRONG_DATA;
   const auto apdu_response =
       device_response
diff --git a/device/fido/u2f_register_operation.h b/device/fido/u2f_register_operation.h
index cf423f9..e9ee470 100644
--- a/device/fido/u2f_register_operation.h
+++ b/device/fido/u2f_register_operation.h
@@ -38,6 +38,7 @@
 
   // DeviceOperation:
   void Start() override;
+  void Cancel() override;
 
  private:
   using ExcludeListIterator =
@@ -52,6 +53,7 @@
   const std::vector<uint8_t>& excluded_key_handle() const;
 
   size_t current_key_handle_index_ = 0;
+  bool canceled_ = false;
   base::WeakPtrFactory<U2fRegisterOperation> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(U2fRegisterOperation);
diff --git a/device/fido/u2f_sign_operation.cc b/device/fido/u2f_sign_operation.cc
index 102b361..c0499486 100644
--- a/device/fido/u2f_sign_operation.cc
+++ b/device/fido/u2f_sign_operation.cc
@@ -43,6 +43,10 @@
   }
 }
 
+void U2fSignOperation::Cancel() {
+  canceled_ = true;
+}
+
 void U2fSignOperation::TrySign() {
   DispatchDeviceRequest(
       ConvertToU2fSignCommand(request(), app_param_type_, key_handle()),
@@ -52,6 +56,10 @@
 
 void U2fSignOperation::OnSignResponseReceived(
     base::Optional<std::vector<uint8_t>> device_response) {
+  if (canceled_) {
+    return;
+  }
+
   auto result = apdu::ApduResponse::Status::SW_WRONG_DATA;
   const auto apdu_response =
       device_response
@@ -134,6 +142,10 @@
 
 void U2fSignOperation::OnEnrollmentResponseReceived(
     base::Optional<std::vector<uint8_t>> device_response) {
+  if (canceled_) {
+    return;
+  }
+
   auto result = apdu::ApduResponse::Status::SW_WRONG_DATA;
   if (device_response) {
     const auto apdu_response =
diff --git a/device/fido/u2f_sign_operation.h b/device/fido/u2f_sign_operation.h
index 99e10b9b..9c6d7a8 100644
--- a/device/fido/u2f_sign_operation.h
+++ b/device/fido/u2f_sign_operation.h
@@ -38,6 +38,7 @@
 
   // DeviceOperation:
   void Start() override;
+  void Cancel() override;
 
  private:
   void TrySign();
@@ -52,6 +53,7 @@
   // app_param_type_ identifies whether we're currently trying the RP ID (the
   // primary value) or an RP-provided U2F AppID.
   ApplicationParameterType app_param_type_ = ApplicationParameterType::kPrimary;
+  bool canceled_ = false;
   base::WeakPtrFactory<U2fSignOperation> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(U2fSignOperation);
diff --git a/device/vr/windows/compositor_base.cc b/device/vr/windows/compositor_base.cc
index 992804a..3f1fd64e 100644
--- a/device/vr/windows/compositor_base.cc
+++ b/device/vr/windows/compositor_base.cc
@@ -168,6 +168,13 @@
   // merge with vr_shell_gl eventually.
   left_webxr_bounds_ = left_bounds;
   right_webxr_bounds_ = right_bounds;
+
+  // Swap top/bottom to account for differences between D3D and GL coordinates.
+  left_webxr_bounds_.set_y(
+      1 - (left_webxr_bounds_.y() + left_webxr_bounds_.height()));
+  right_webxr_bounds_.set_y(
+      1 - (right_webxr_bounds_.y() + right_webxr_bounds_.height()));
+
   source_size_ = source_size;
 
   OnLayerBoundsChanged();
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
index cfbe920..fc7bcb9 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
@@ -29,6 +29,8 @@
     : GuestViewContainer(render_frame),
       MimeHandlerViewContainerBase(render_frame, info, mime_type, original_url),
       guest_proxy_routing_id_(-1) {
+  RecordInteraction(
+      MimeHandlerViewUMATypes::Type::kDidCreateMimeHandlerViewContainerBase);
   is_embedded_ = !render_frame->GetWebFrame()->GetDocument().IsPluginDocument();
 }
 
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
index 1ddc5f04..1feafce7 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
@@ -200,7 +200,6 @@
   DCHECK(!mime_type_.empty());
   g_mime_handler_view_container_base_map.Get()[embedder_render_frame].insert(
       this);
-  RecordInteraction(UMAType::kDidCreateMimeHandlerViewContainerBase);
 }
 
 MimeHandlerViewContainerBase::~MimeHandlerViewContainerBase() {
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_frame_container.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_frame_container.cc
index b80d88f..466ce206 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_frame_container.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_frame_container.cc
@@ -84,6 +84,11 @@
     const GURL& resource_url,
     const std::string& mime_type,
     const std::string& view_id) {
+  // TODO(ekaramad): Eventually MHVFC should not request the resource which
+  // means this mode of MHVFC is no longer necessary. After intercepting the
+  // response we should just proceed with creating the MimeHandlerViewGuest
+  // and attaching to the inserted <iframe> on the browser side. The current
+  // renderer state for it seems unnecessary.
   new MimeHandlerViewFrameContainer(web_frame, resource_url, mime_type,
                                     view_id);
 }
@@ -102,6 +107,8 @@
       element_instance_id_(content::RenderThread::Get()->GenerateRoutingID()),
       render_frame_lifetime_observer_(
           new RenderFrameLifetimeObserver(this, GetEmbedderRenderFrame())) {
+  RecordInteraction(
+      MimeHandlerViewUMATypes::Type::kDidCreateMimeHandlerViewContainerBase);
   is_embedded_ = true;
   SendResourceRequest();
 }
diff --git a/net/android/network_change_notifier_android.cc b/net/android/network_change_notifier_android.cc
index 02cf01e..7094724 100644
--- a/net/android/network_change_notifier_android.cc
+++ b/net/android/network_change_notifier_android.cc
@@ -67,9 +67,12 @@
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
 #include "base/threading/thread.h"
 #include "net/base/address_tracker_linux.h"
-#include "net/dns/dns_config_service.h"
+#include "net/dns/dns_config_service_posix.h"
 
 namespace net {
 
@@ -86,41 +89,32 @@
 
 // Thread on which we can run DnsConfigService, which requires a TYPE_IO
 // message loop to monitor /system/etc/hosts.
-class NetworkChangeNotifierAndroid::DnsConfigServiceThread
-    : public base::Thread {
+class NetworkChangeNotifierAndroid::BlockingThreadObjects {
  public:
-  DnsConfigServiceThread()
-      : base::Thread("DnsConfigService"),
-        address_tracker_(base::DoNothing(),
+  BlockingThreadObjects()
+      : address_tracker_(base::DoNothing(),
                          base::DoNothing(),
                          // We're only interested in tunnel interface changes.
                          base::Bind(NotifyNetworkChangeNotifierObservers),
                          std::unordered_set<std::string>()) {}
 
-  ~DnsConfigServiceThread() override {
-    Stop();
-  }
-
-  void Init() override {
+  void Init() {
     address_tracker_.Init();
-    dns_config_service_ = DnsConfigService::CreateSystemService();
-    dns_config_service_->WatchConfig(
+    dns_config_service_.WatchConfig(
         base::Bind(&NetworkChangeNotifier::SetDnsConfig));
   }
 
-  void CleanUp() override { dns_config_service_.reset(); }
-
   static void NotifyNetworkChangeNotifierObservers() {
     NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
     NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
   }
 
  private:
-  std::unique_ptr<DnsConfigService> dns_config_service_;
+  internal::DnsConfigServicePosix dns_config_service_;
   // Used to detect tunnel state changes.
   internal::AddressTrackerLinux address_tracker_;
 
-  DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread);
+  DISALLOW_COPY_AND_ASSIGN(BlockingThreadObjects);
 };
 
 NetworkChangeNotifierAndroid::~NetworkChangeNotifierAndroid() {
@@ -175,7 +169,7 @@
 }
 
 void NetworkChangeNotifierAndroid::OnConnectionTypeChanged() {
-  DnsConfigServiceThread::NotifyNetworkChangeNotifierObservers();
+  BlockingThreadObjects::NotifyNetworkChangeNotifierObservers();
 }
 
 void NetworkChangeNotifierAndroid::OnMaxBandwidthChanged(
@@ -211,13 +205,25 @@
     NetworkChangeNotifierDelegateAndroid* delegate)
     : NetworkChangeNotifier(NetworkChangeCalculatorParamsAndroid()),
       delegate_(delegate),
-      dns_config_service_thread_(new DnsConfigServiceThread()),
+      blocking_thread_runner_(
+          base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
+      blocking_thread_objects_(
+          new BlockingThreadObjects(),
+          // Ensure |blocking_thread_objects_| lives on
+          // |blocking_thread_runner_| to prevent races where
+          // NetworkChangeNotifierAndroid outlives
+          // ScopedTaskEnvironment. https://crbug.com/938126
+          base::OnTaskRunnerDeleter(blocking_thread_runner_)),
       force_network_handles_supported_for_testing_(false) {
   CHECK_EQ(NetId::INVALID, NetworkChangeNotifier::kInvalidNetworkHandle)
       << "kInvalidNetworkHandle doesn't match NetId::INVALID";
   delegate_->AddObserver(this);
-  dns_config_service_thread_->StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+  blocking_thread_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&BlockingThreadObjects::Init,
+                     // The Unretained pointer is safe here because it's
+                     // posted before the deleter can post.
+                     base::Unretained(blocking_thread_objects_.get())));
 }
 
 // static
diff --git a/net/android/network_change_notifier_android.h b/net/android/network_change_notifier_android.h
index 003e36b..dab9fd3 100644
--- a/net/android/network_change_notifier_android.h
+++ b/net/android/network_change_notifier_android.h
@@ -10,10 +10,16 @@
 #include "base/android/jni_android.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "net/android/network_change_notifier_delegate_android.h"
 #include "net/base/net_export.h"
 #include "net/base/network_change_notifier.h"
 
+namespace base {
+class SequencedTaskRunner;
+struct OnTaskRunnerDeleter;
+}  // namespace base
+
 namespace net {
 
 class NetworkChangeNotifierAndroidTest;
@@ -81,7 +87,7 @@
   friend class NetworkChangeNotifierAndroidTest;
   friend class NetworkChangeNotifierFactoryAndroid;
 
-  class DnsConfigServiceThread;
+  class BlockingThreadObjects;
 
   // Enable NetworkHandles support for tests.
   void ForceNetworkHandlesSupportedForTesting();
@@ -90,7 +96,14 @@
       NetworkChangeNotifierDelegateAndroid* delegate);
 
   NetworkChangeNotifierDelegateAndroid* const delegate_;
-  std::unique_ptr<DnsConfigServiceThread> dns_config_service_thread_;
+  // |blocking_thread_objects_| will live on this runner.
+  scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner_;
+  // A collection of objects that must live on blocking sequences. These objects
+  // listen for notifications and relay the notifications to the registered
+  // observers without posting back to the thread the object was created on.
+  // Also used for DnsConfigService which also must live on blocking sequences.
+  std::unique_ptr<BlockingThreadObjects, base::OnTaskRunnerDeleter>
+      blocking_thread_objects_;
   bool force_network_handles_supported_for_testing_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierAndroid);
diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc
index 7eecd01e..9820b6d0 100644
--- a/net/base/address_tracker_linux.cc
+++ b/net/base/address_tracker_linux.cc
@@ -118,7 +118,6 @@
       address_callback_(base::DoNothing()),
       link_callback_(base::DoNothing()),
       tunnel_callback_(base::DoNothing()),
-      watcher_(FROM_HERE),
       ignored_interfaces_(),
       connection_type_initialized_(false),
       connection_type_initialized_cv_(&connection_type_lock_),
@@ -135,7 +134,6 @@
       address_callback_(address_callback),
       link_callback_(link_callback),
       tunnel_callback_(tunnel_callback),
-      watcher_(FROM_HERE),
       ignored_interfaces_(ignored_interfaces),
       connection_type_initialized_(false),
       connection_type_initialized_cv_(&connection_type_lock_),
@@ -146,9 +144,7 @@
   DCHECK(!link_callback.is_null());
 }
 
-AddressTrackerLinux::~AddressTrackerLinux() {
-  watcher_.StopWatchingFileDescriptor();
-}
+AddressTrackerLinux::~AddressTrackerLinux() = default;
 
 void AddressTrackerLinux::Init() {
   netlink_fd_.reset(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
@@ -230,19 +226,15 @@
   }
 
   if (tracking_) {
-    rv = base::MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
-        netlink_fd_.get(), true, base::MessagePumpForIO::WATCH_READ, &watcher_,
-        this);
-    if (rv < 0) {
-      PLOG(ERROR) << "Could not watch NETLINK socket";
-      AbortAndForceOnline();
-      return;
-    }
+    watcher_ = base::FileDescriptorWatcher::WatchReadable(
+        netlink_fd_.get(),
+        base::BindRepeating(&AddressTrackerLinux::OnFileCanReadWithoutBlocking,
+                            base::Unretained(this)));
   }
 }
 
 void AddressTrackerLinux::AbortAndForceOnline() {
-  watcher_.StopWatchingFileDescriptor();
+  watcher_.reset();
   netlink_fd_.reset();
   AddressTrackerAutoLock lock(*this, connection_type_lock_);
   current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
@@ -424,8 +416,7 @@
   }
 }
 
-void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
-  DCHECK_EQ(netlink_fd_.get(), fd);
+void AddressTrackerLinux::OnFileCanReadWithoutBlocking() {
   bool address_changed;
   bool link_changed;
   bool tunnel_changed;
@@ -438,8 +429,6 @@
     tunnel_callback_.Run();
 }
 
-void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
-
 bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const {
   char buf[IFNAMSIZ] = {0};
   return IsTunnelInterfaceName(get_interface_name_(interface_index, buf));
diff --git a/net/base/address_tracker_linux.h b/net/base/address_tracker_linux.h
index 0fd3f09..f937eabf 100644
--- a/net/base/address_tracker_linux.h
+++ b/net/base/address_tracker_linux.h
@@ -17,9 +17,9 @@
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/files/file_descriptor_watcher_posix.h"
 #include "base/files/scoped_file.h"
 #include "base/macros.h"
-#include "base/message_loop/message_pump_for_io.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
@@ -32,8 +32,7 @@
 
 // Keeps track of network interface addresses using rtnetlink. Used by
 // NetworkChangeNotifier to provide signals to registered IPAddressObservers.
-class NET_EXPORT_PRIVATE AddressTrackerLinux
-    : public base::MessagePumpForIO::FdWatcher {
+class NET_EXPORT_PRIVATE AddressTrackerLinux {
  public:
   typedef std::map<IPAddress, struct ifaddrmsg> AddressMap;
 
@@ -58,7 +57,7 @@
       const base::Closure& link_callback,
       const base::Closure& tunnel_callback,
       const std::unordered_set<std::string>& ignored_interfaces);
-  ~AddressTrackerLinux() override;
+  virtual ~AddressTrackerLinux();
 
   // In tracking mode, it starts watching the system configuration for
   // changes. The current thread must have a MessageLoopForIO. In
@@ -129,9 +128,8 @@
   // Call when some part of initialization failed; forces online and unblocks.
   void AbortAndForceOnline();
 
-  // MessagePumpForIO::FdWatcher:
-  void OnFileCanReadWithoutBlocking(int fd) override;
-  void OnFileCanWriteWithoutBlocking(int /* fd */) override;
+  // Called by |watcher_| when |netlink_fd_| can be read without blocking.
+  void OnFileCanReadWithoutBlocking();
 
   // Does |interface_index| refer to a tunnel interface?
   bool IsTunnelInterface(int interface_index) const;
@@ -157,7 +155,7 @@
 
   // Note that |watcher_| must be inactive when |netlink_fd_| is closed.
   base::ScopedFD netlink_fd_;
-  base::MessagePumpForIO::FdWatchController watcher_;
+  std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_;
 
   mutable base::Lock address_map_lock_;
   AddressMap address_map_;
diff --git a/net/base/network_change_notifier_linux.cc b/net/base/network_change_notifier_linux.cc
index 9621a74..ff0b277 100644
--- a/net/base/network_change_notifier_linux.cc
+++ b/net/base/network_change_notifier_linux.cc
@@ -9,83 +9,73 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
 #include "base/threading/thread.h"
 #include "net/base/address_tracker_linux.h"
-#include "net/dns/dns_config_service.h"
+#include "net/dns/dns_config_service_posix.h"
 
 namespace net {
 
-class NetworkChangeNotifierLinux::Thread : public base::Thread {
+// A collection of objects that live on blocking threads.
+class NetworkChangeNotifierLinux::BlockingThreadObjects {
  public:
-  explicit Thread(const std::unordered_set<std::string>& ignored_interfaces);
-  ~Thread() override;
+  explicit BlockingThreadObjects(
+      const std::unordered_set<std::string>& ignored_interfaces);
 
   // Plumbing for NetworkChangeNotifier::GetCurrentConnectionType.
   // Safe to call from any thread.
   NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() {
-    return address_tracker_->GetCurrentConnectionType();
+    return address_tracker_.GetCurrentConnectionType();
   }
 
   const internal::AddressTrackerLinux* address_tracker() const {
-    return address_tracker_.get();
+    return &address_tracker_;
   }
 
- protected:
-  // base::Thread
-  void Init() override;
-  void CleanUp() override;
+  // Begin watching for DNS and netlink changes.
+  void Init();
 
  private:
   void OnIPAddressChanged();
   void OnLinkChanged();
-  std::unique_ptr<DnsConfigService> dns_config_service_;
+  internal::DnsConfigServicePosix dns_config_service_;
   // Used to detect online/offline state and IP address changes.
-  std::unique_ptr<internal::AddressTrackerLinux> address_tracker_;
+  internal::AddressTrackerLinux address_tracker_;
   NetworkChangeNotifier::ConnectionType last_type_;
 
-  DISALLOW_COPY_AND_ASSIGN(Thread);
+  DISALLOW_COPY_AND_ASSIGN(BlockingThreadObjects);
 };
 
-NetworkChangeNotifierLinux::Thread::Thread(
+NetworkChangeNotifierLinux::BlockingThreadObjects::BlockingThreadObjects(
     const std::unordered_set<std::string>& ignored_interfaces)
-    : base::Thread("NetworkChangeNotifier"),
-      address_tracker_(new internal::AddressTrackerLinux(
-          base::Bind(&NetworkChangeNotifierLinux::Thread::OnIPAddressChanged,
-                     base::Unretained(this)),
-          base::Bind(&NetworkChangeNotifierLinux::Thread::OnLinkChanged,
-                     base::Unretained(this)),
+    : address_tracker_(
+          base::BindRepeating(&NetworkChangeNotifierLinux::
+                                  BlockingThreadObjects::OnIPAddressChanged,
+                              base::Unretained(this)),
+          base::BindRepeating(
+              &NetworkChangeNotifierLinux::BlockingThreadObjects::OnLinkChanged,
+              base::Unretained(this)),
           base::DoNothing(),
-          ignored_interfaces)),
+          ignored_interfaces),
       last_type_(NetworkChangeNotifier::CONNECTION_NONE) {}
 
-NetworkChangeNotifierLinux::Thread::~Thread() {
-  DCHECK(!Thread::IsRunning());
-}
-
-void NetworkChangeNotifierLinux::Thread::Init() {
-  address_tracker_->Init();
+void NetworkChangeNotifierLinux::BlockingThreadObjects::Init() {
+  address_tracker_.Init();
   last_type_ = GetCurrentConnectionType();
-  dns_config_service_ = DnsConfigService::CreateSystemService();
-  dns_config_service_->WatchConfig(
+  dns_config_service_.WatchConfig(
       base::Bind(&NetworkChangeNotifier::SetDnsConfig));
 }
 
-void NetworkChangeNotifierLinux::Thread::CleanUp() {
-  // Delete AddressTrackerLinux before MessageLoop gets deleted as
-  // AddressTrackerLinux's FileDescriptorWatcher holds a pointer to the
-  // MessageLoop.
-  address_tracker_.reset();
-  dns_config_service_.reset();
-}
-
-void NetworkChangeNotifierLinux::Thread::OnIPAddressChanged() {
+void NetworkChangeNotifierLinux::BlockingThreadObjects::OnIPAddressChanged() {
   NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
   // When the IP address of a network interface is added/deleted, the
   // connection type may have changed.
   OnLinkChanged();
 }
 
-void NetworkChangeNotifierLinux::Thread::OnLinkChanged() {
+void NetworkChangeNotifierLinux::BlockingThreadObjects::OnLinkChanged() {
   if (last_type_ != GetCurrentConnectionType()) {
     NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
     last_type_ = GetCurrentConnectionType();
@@ -100,19 +90,24 @@
 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(
     const std::unordered_set<std::string>& ignored_interfaces)
     : NetworkChangeNotifier(NetworkChangeCalculatorParamsLinux()),
-      notifier_thread_(new Thread(ignored_interfaces)) {
-  // We create this notifier thread because the notification implementation
-  // needs a MessageLoopForIO, and there's no guarantee that
-  // MessageLoopCurrent::Get() meets that criterion.
-  base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
-  notifier_thread_->StartWithOptions(thread_options);
+      blocking_thread_runner_(
+          base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
+      blocking_thread_objects_(
+          new BlockingThreadObjects(ignored_interfaces),
+          // Ensure |blocking_thread_objects_| lives on
+          // |blocking_thread_runner_| to prevent races where
+          // NetworkChangeNotifierLinux outlives
+          // ScopedTaskEnvironment. https://crbug.com/938126
+          base::OnTaskRunnerDeleter(blocking_thread_runner_)) {
+  blocking_thread_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&NetworkChangeNotifierLinux::BlockingThreadObjects::Init,
+                     // The Unretained pointer is safe here because it's
+                     // posted before the deleter can post.
+                     base::Unretained(blocking_thread_objects_.get())));
 }
 
-NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
-  // Stopping from here allows us to sanity- check that the notifier
-  // thread shut down properly.
-  notifier_thread_->Stop();
-}
+NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() = default;
 
 // static
 NetworkChangeNotifier::NetworkChangeCalculatorParams
@@ -130,12 +125,12 @@
 
 NetworkChangeNotifier::ConnectionType
 NetworkChangeNotifierLinux::GetCurrentConnectionType() const {
-  return notifier_thread_->GetCurrentConnectionType();
+  return blocking_thread_objects_->GetCurrentConnectionType();
 }
 
 const internal::AddressTrackerLinux*
 NetworkChangeNotifierLinux::GetAddressTrackerInternal() const {
-  return notifier_thread_->address_tracker();
+  return blocking_thread_objects_->address_tracker();
 }
 
 }  // namespace net
diff --git a/net/base/network_change_notifier_linux.h b/net/base/network_change_notifier_linux.h
index 3439270b..fa14f30 100644
--- a/net/base/network_change_notifier_linux.h
+++ b/net/base/network_change_notifier_linux.h
@@ -10,9 +10,15 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "net/base/net_export.h"
 #include "net/base/network_change_notifier.h"
 
+namespace base {
+class SequencedTaskRunner;
+struct OnTaskRunnerDeleter;
+}  // namespace base
+
 namespace net {
 
 class NET_EXPORT_PRIVATE NetworkChangeNotifierLinux
@@ -28,7 +34,7 @@
       const std::unordered_set<std::string>& ignored_interfaces);
 
  private:
-  class Thread;
+  class BlockingThreadObjects;
 
   ~NetworkChangeNotifierLinux() override;
   static NetworkChangeCalculatorParams NetworkChangeCalculatorParamsLinux();
@@ -39,11 +45,14 @@
   const internal::AddressTrackerLinux* GetAddressTrackerInternal()
       const override;
 
-  // The thread used to listen for notifications.  This relays the notification
-  // to the registered observers without posting back to the thread the object
-  // was created on.
-  // Also used for DnsConfigService which requires TYPE_IO message loop.
-  std::unique_ptr<Thread> notifier_thread_;
+  // |blocking_thread_objects_| will live on this runner.
+  scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner_;
+  // A collection of objects that must live on blocking sequences. These objects
+  // listen for notifications and relay the notifications to the registered
+  // observers without posting back to the thread the object was created on.
+  // Also used for DnsConfigService which also must live on blocking sequences.
+  std::unique_ptr<BlockingThreadObjects, base::OnTaskRunnerDeleter>
+      blocking_thread_objects_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierLinux);
 };
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index 3da2eb5..48ee925 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -18,7 +18,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "net/base/ip_address.h"
@@ -273,7 +273,7 @@
   void OnConfigChanged(bool succeeded) {
     // Ignore transient flutter of resolv.conf by delaying the signal a bit.
     const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(50);
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
         base::BindOnce(&Watcher::OnConfigChangedDelayed,
                        weak_factory_.GetWeakPtr(), succeeded),
diff --git a/remoting/base/service_urls.cc b/remoting/base/service_urls.cc
index 4500599..684db13c6 100644
--- a/remoting/base/service_urls.cc
+++ b/remoting/base/service_urls.cc
@@ -10,26 +10,28 @@
 #include "remoting/base/remoting_bot.h"
 
 // Configurable service data.
-const char kDirectoryBaseUrl[] = "https://www.googleapis.com/chromoting/v1";
-const char kGcdBaseUrl[] = "https://www.googleapis.com/clouddevices/v1";
-const char kXmppServerAddress[] = "talk.google.com:443";
-const char kXmppServerAddressForMe2MeHost[] = "talk.google.com:5222";
-const bool kXmppServerUseTls = true;
-const char kGcdJid[] = "clouddevices.gserviceaccount.com";
+constexpr char kDirectoryBaseUrl[] = "https://www.googleapis.com/chromoting/v1";
+constexpr char kGcdBaseUrl[] = "https://www.googleapis.com/clouddevices/v1";
+constexpr char kXmppServerAddress[] = "talk.google.com:443";
+constexpr char kXmppServerAddressForMe2MeHost[] = "talk.google.com:5222";
+constexpr bool kXmppServerUseTls = true;
+constexpr char kGcdJid[] = "clouddevices.gserviceaccount.com";
+constexpr char kFtlServerEndpoint[] = "instantmessaging-pa.googleapis.com";
 
 // Command line switches.
 #if !defined(NDEBUG)
-const char kDirectoryBaseUrlSwitch[] = "directory-base-url";
-const char kGcdBaseUrlSwitch[] = "gcd-base-url";
-const char kXmppServerAddressSwitch[] = "xmpp-server-address";
-const char kXmppServerDisableTlsSwitch[] = "disable-xmpp-server-tls";
-const char kDirectoryBotJidSwitch[] = "directory-bot-jid";
-const char kGcdJidSwitch[] = "gcd-jid";
+constexpr char kDirectoryBaseUrlSwitch[] = "directory-base-url";
+constexpr char kGcdBaseUrlSwitch[] = "gcd-base-url";
+constexpr char kXmppServerAddressSwitch[] = "xmpp-server-address";
+constexpr char kXmppServerDisableTlsSwitch[] = "disable-xmpp-server-tls";
+constexpr char kDirectoryBotJidSwitch[] = "directory-bot-jid";
+constexpr char kGcdJidSwitch[] = "gcd-jid";
+constexpr char kFtlServerEndpointSwitch[] = "ftl-server-endpoint";
 #endif  // !defined(NDEBUG)
 
 // Non-configurable service paths.
-const char kDirectoryHostsSuffix[] = "/@me/hosts/";
-const char kDirectoryIceConfigSuffix[] = "/@me/iceconfig";
+constexpr char kDirectoryHostsSuffix[] = "/@me/hosts/";
+constexpr char kDirectoryIceConfigSuffix[] = "/@me/iceconfig";
 
 namespace remoting {
 
@@ -40,7 +42,8 @@
       xmpp_server_address_for_me2me_host_(kXmppServerAddressForMe2MeHost),
       xmpp_server_use_tls_(kXmppServerUseTls),
       directory_bot_jid_(kRemotingBotJid),
-      gcd_jid_(kGcdJid) {
+      gcd_jid_(kGcdJid),
+      ftl_server_endpoint_(kFtlServerEndpoint) {
 #if !defined(NDEBUG)
   // The command line may not be initialized when running as a PNaCl plugin.
   if (base::CommandLine::InitializedForCurrentProcess()) {
@@ -69,6 +72,10 @@
     if (command_line->HasSwitch(kGcdJidSwitch)) {
       gcd_jid_ = command_line->GetSwitchValueASCII(kGcdJidSwitch);
     }
+    if (command_line->HasSwitch(kFtlServerEndpointSwitch)) {
+      ftl_server_endpoint_ =
+          command_line->GetSwitchValueASCII(kFtlServerEndpointSwitch);
+    }
   }
 #endif  // !defined(NDEBUG)
 
diff --git a/remoting/base/service_urls.h b/remoting/base/service_urls.h
index 12c4d9c0..4f29b7c 100644
--- a/remoting/base/service_urls.h
+++ b/remoting/base/service_urls.h
@@ -45,6 +45,10 @@
   // ICE config URL.
   const std::string& ice_config_url() const { return ice_config_url_; }
 
+  const std::string& ftl_server_endpoint() const {
+    return ftl_server_endpoint_;
+  }
+
 #if !defined(NDEBUG)
   // Override the directory bot JID for testing.
   void set_directory_bot_jid(const std::string& bot_jid) {
@@ -67,6 +71,7 @@
   std::string directory_bot_jid_;
   std::string gcd_jid_;
   std::string ice_config_url_;
+  std::string ftl_server_endpoint_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceUrls);
 };
diff --git a/remoting/protocol/webrtc_transport.h b/remoting/protocol/webrtc_transport.h
index ebd97c7a..b58c668 100644
--- a/remoting/protocol/webrtc_transport.h
+++ b/remoting/protocol/webrtc_transport.h
@@ -34,6 +34,8 @@
  public:
   class EventHandler {
    public:
+    virtual ~EventHandler() = default;
+
     // Called after |peer_connection| has been created but before handshake. The
     // handler should create data channels and media streams. Renegotiation will
     // be required in two cases after this method returns:
@@ -58,9 +60,6 @@
         scoped_refptr<webrtc::MediaStreamInterface> stream) = 0;
     virtual void OnWebrtcTransportMediaStreamRemoved(
         scoped_refptr<webrtc::MediaStreamInterface> stream) = 0;
-
-   protected:
-    virtual ~EventHandler() {}
   };
 
   WebrtcTransport(rtc::Thread* worker_thread,
diff --git a/remoting/signaling/ftl_grpc_context.cc b/remoting/signaling/ftl_grpc_context.cc
index 0ecbc8f..2c132844 100644
--- a/remoting/signaling/ftl_grpc_context.cc
+++ b/remoting/signaling/ftl_grpc_context.cc
@@ -10,6 +10,7 @@
 #include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "google_apis/google_api_keys.h"
+#include "remoting/base/service_urls.h"
 #include "third_party/grpc/src/include/grpcpp/channel.h"
 #include "third_party/grpc/src/include/grpcpp/client_context.h"
 #include "third_party/grpc/src/include/grpcpp/grpcpp.h"
@@ -20,9 +21,6 @@
 
 constexpr char kChromotingAppIdentifier[] = "CRD";
 
-// TODO(yuweih): We should target different service environments.
-constexpr char kFtlServerEndpoint[] = "instantmessaging-pa.googleapis.com";
-
 static base::NoDestructor<GrpcChannelSharedPtr> g_channel_for_testing;
 
 const net::BackoffEntry::Policy kBackoffPolicy = {
@@ -81,7 +79,8 @@
   if (*g_channel_for_testing) {
     return *g_channel_for_testing;
   }
-  return CreateSslChannelForEndpoint(kFtlServerEndpoint);
+  return CreateSslChannelForEndpoint(
+      ServiceUrls::GetInstance()->ftl_server_endpoint());
 }
 
 // static
diff --git a/remoting/test/ftl_signaling_playground.cc b/remoting/test/ftl_signaling_playground.cc
index da1bc43..b92d04c 100644
--- a/remoting/test/ftl_signaling_playground.cc
+++ b/remoting/test/ftl_signaling_playground.cc
@@ -5,6 +5,7 @@
 #include "remoting/test/ftl_signaling_playground.h"
 
 #include <inttypes.h>
+
 #include <string>
 #include <utility>
 #include <vector>
@@ -20,15 +21,21 @@
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
 #include "jingle/glue/thread_wrapper.h"
+#include "remoting/base/chromium_url_request.h"
 #include "remoting/base/logging.h"
 #include "remoting/base/oauth_token_getter_impl.h"
 #include "remoting/base/rsa_key_pair.h"
+#include "remoting/base/service_urls.h"
+#include "remoting/base/url_request_context_getter.h"
 #include "remoting/protocol/auth_util.h"
+#include "remoting/protocol/chromium_port_allocator_factory.h"
 #include "remoting/protocol/jingle_session_manager.h"
 #include "remoting/protocol/me2me_host_authenticator_factory.h"
 #include "remoting/protocol/negotiating_client_authenticator.h"
+#include "remoting/protocol/network_settings.h"
 #include "remoting/protocol/pairing_registry.h"
 #include "remoting/protocol/transport.h"
+#include "remoting/protocol/transport_context.h"
 #include "remoting/signaling/ftl_grpc_context.h"
 #include "remoting/signaling/ftl_services.grpc.pb.h"
 #include "remoting/signaling/ftl_signal_strategy.h"
@@ -37,6 +44,8 @@
 #include "remoting/test/test_device_id_provider.h"
 #include "remoting/test/test_oauth_token_getter.h"
 #include "remoting/test/test_token_storage.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/transitional_url_loader_factory_owner.h"
 
 namespace remoting {
 
@@ -62,16 +71,43 @@
   return "";
 }
 
-class FakeTransport : public protocol::Transport {
+class FakeTransportEventHandler final
+    : public protocol::WebrtcTransport::EventHandler {
  public:
-  FakeTransport() = default;
-  ~FakeTransport() override = default;
-
-  void Start(protocol::Authenticator* authenticator,
-             SendTransportInfoCallback send_transport_info_callback) override {}
-  bool ProcessTransportInfo(jingle_xmpp::XmlElement* transport_info) override {
-    return false;
+  explicit FakeTransportEventHandler(base::OnceClosure on_closed) {
+    on_closed_ = std::move(on_closed);
   }
+
+  ~FakeTransportEventHandler() override = default;
+
+  // protocol::WebrtcTransport::EventHandler interface.
+  void OnWebrtcTransportConnecting() override {
+    HOST_LOG << "Webrtc transport is connecting...";
+  }
+
+  void OnWebrtcTransportConnected() override {
+    HOST_LOG << "Webrtc transport is connected!!!";
+    std::move(on_closed_).Run();
+  }
+
+  void OnWebrtcTransportError(protocol::ErrorCode error) override {
+    LOG(ERROR) << "Webrtc transport error: " << error;
+    std::move(on_closed_).Run();
+  }
+
+  void OnWebrtcTransportIncomingDataChannel(
+      const std::string& name,
+      std::unique_ptr<protocol::MessagePipe> pipe) override {}
+
+  void OnWebrtcTransportMediaStreamAdded(
+      scoped_refptr<webrtc::MediaStreamInterface> stream) override {}
+
+  void OnWebrtcTransportMediaStreamRemoved(
+      scoped_refptr<webrtc::MediaStreamInterface> stream) override {}
+
+ private:
+  base::OnceClosure on_closed_;
+  DISALLOW_COPY_AND_ASSIGN(FakeTransportEventHandler);
 };
 
 }  // namespace
@@ -105,6 +141,13 @@
   storage_ = test::TestTokenStorage::OnDisk(username, storage_path);
   token_getter_ = std::make_unique<test::TestOAuthTokenGetter>(storage_.get());
 
+  auto url_request_context_getter =
+      base::MakeRefCounted<URLRequestContextGetter>(
+          base::ThreadTaskRunnerHandle::Get());
+  url_loader_factory_owner_ =
+      std::make_unique<network::TransitionalURLLoaderFactoryOwner>(
+          url_request_context_getter);
+
   base::RunLoop initialize_token_getter_loop;
   token_getter_->Initialize(initialize_token_getter_loop.QuitClosure());
   initialize_token_getter_loop.Run();
@@ -154,7 +197,8 @@
     protocol::Session* owned_session,
     protocol::SessionManager::IncomingSessionResponse* response) {
   HOST_LOG << "Received incoming session!\n";
-  RegisterSession(base::WrapUnique(owned_session));
+  RegisterSession(base::WrapUnique(owned_session),
+                  protocol::TransportRole::CLIENT);
   *response = protocol::SessionManager::ACCEPT;
 }
 
@@ -182,7 +226,7 @@
       std::make_unique<protocol::NegotiatingClientAuthenticator>(
           signal_strategy_->GetLocalAddress().id(), host_jid,
           client_auth_config));
-  RegisterSession(std::move(session));
+  RegisterSession(std::move(session), protocol::TransportRole::SERVER);
 }
 
 void FtlSignalingPlayground::FetchSecret(
@@ -202,26 +246,47 @@
 
   session_manager_ =
       std::make_unique<protocol::JingleSessionManager>(signal_strategy_.get());
-  session_manager_->set_protocol_config(
-      protocol::CandidateSessionConfig::CreateDefault());
+  auto protocol_config = protocol::CandidateSessionConfig::CreateDefault();
+  protocol_config->set_webrtc_supported(true);
+  session_manager_->set_protocol_config(std::move(protocol_config));
 
   signal_strategy_->Connect();
 }
 
 void FtlSignalingPlayground::TearDownSignaling() {
+  on_signaling_connected_callback_.Reset();
   session_.reset();
-  fake_transport_.reset();
+  transport_.reset();
+  transport_event_handler_.reset();
   signal_strategy_->RemoveListener(this);
   session_manager_.reset();
   signal_strategy_.reset();
 }
 
 void FtlSignalingPlayground::RegisterSession(
-    std::unique_ptr<protocol::Session> session) {
+    std::unique_ptr<protocol::Session> session,
+    protocol::TransportRole transport_role) {
   session_ = std::move(session);
-  fake_transport_ = std::make_unique<FakeTransport>();
+  protocol::NetworkSettings network_settings(
+      protocol::NetworkSettings::NAT_TRAVERSAL_FULL);
+  auto transport_context = base::MakeRefCounted<protocol::TransportContext>(
+      signal_strategy_.get(),
+      std::make_unique<protocol::ChromiumPortAllocatorFactory>(),
+      std::make_unique<ChromiumUrlRequestFactory>(
+          url_loader_factory_owner_->GetURLLoaderFactory()),
+      network_settings, transport_role);
+  transport_context->set_ice_config_url(
+      ServiceUrls::GetInstance()->ice_config_url(), token_getter_.get());
+  std::unique_ptr<protocol::SessionManager> session_manager(
+      new protocol::JingleSessionManager(signal_strategy_.get()));
+  transport_event_handler_ = std::make_unique<FakeTransportEventHandler>(
+      base::BindOnce(&FtlSignalingPlayground::AsyncTearDownAndRunCallback,
+                     base::Unretained(this)));
+  transport_ = std::make_unique<protocol::WebrtcTransport>(
+      jingle_glue::JingleThreadWrapper::current(), transport_context,
+      transport_event_handler_.get());
   session_->SetEventHandler(this);
-  session_->SetTransport(fake_transport_.get());
+  session_->SetTransport(transport_.get());
 }
 
 void FtlSignalingPlayground::OnSignalStrategyStateChange(
@@ -281,7 +346,7 @@
       return;
     case protocol::Session::AUTHENTICATED:
       HOST_LOG << "Session is successfully authenticated!!!";
-      break;
+      return;
 
     case protocol::Session::CLOSED:
     case protocol::Session::FAILED:
@@ -289,6 +354,15 @@
       break;
   }
 
+  TearDownAndRunCallback();
+}
+
+void FtlSignalingPlayground::AsyncTearDownAndRunCallback() {
+  tear_down_timer_.Start(FROM_HERE, base::TimeDelta(), this,
+                         &FtlSignalingPlayground::TearDownAndRunCallback);
+}
+
+void FtlSignalingPlayground::TearDownAndRunCallback() {
   TearDownSignaling();
   if (current_callback_) {
     std::move(current_callback_).Run();
diff --git a/remoting/test/ftl_signaling_playground.h b/remoting/test/ftl_signaling_playground.h
index 21b5932..9256623 100644
--- a/remoting/test/ftl_signaling_playground.h
+++ b/remoting/test/ftl_signaling_playground.h
@@ -10,19 +10,21 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
+#include "base/timer/timer.h"
 #include "remoting/base/oauth_token_getter.h"
 #include "remoting/protocol/client_authentication_config.h"
 #include "remoting/protocol/session.h"
 #include "remoting/protocol/session_manager.h"
+#include "remoting/protocol/webrtc_transport.h"
 #include "remoting/signaling/ftl_messaging_client.h"
 #include "remoting/signaling/ftl_registration_manager.h"
 #include "remoting/signaling/signal_strategy.h"
 
-namespace remoting {
+namespace network {
+class TransitionalURLLoaderFactoryOwner;
+}  // namespace network
 
-namespace protocol {
-class Transport;
-}  // namespace protocol
+namespace remoting {
 
 namespace test {
 class TestOAuthTokenGetter;
@@ -53,7 +55,8 @@
 
   void SetUpSignaling();
   void TearDownSignaling();
-  void RegisterSession(std::unique_ptr<protocol::Session> session);
+  void RegisterSession(std::unique_ptr<protocol::Session> session,
+                       protocol::TransportRole transport_role);
 
   // SignalStrategy::Listener interface.
   void OnSignalStrategyStateChange(SignalStrategy::State state) override;
@@ -63,17 +66,26 @@
   // Session::EventHandler interface.
   void OnSessionStateChange(protocol::Session::State state) override;
 
+  void AsyncTearDownAndRunCallback();
+  void TearDownAndRunCallback();
+
   std::unique_ptr<test::TestTokenStorage> storage_;
   std::unique_ptr<test::TestOAuthTokenGetter> token_getter_;
+  std::unique_ptr<network::TransitionalURLLoaderFactoryOwner>
+      url_loader_factory_owner_;
 
   std::unique_ptr<SignalStrategy> signal_strategy_;
   std::unique_ptr<protocol::SessionManager> session_manager_;
-  std::unique_ptr<protocol::Transport> fake_transport_;
+  std::unique_ptr<protocol::WebrtcTransport::EventHandler>
+      transport_event_handler_;
+  std::unique_ptr<protocol::WebrtcTransport> transport_;
   std::unique_ptr<protocol::Session> session_;
 
   base::OnceClosure current_callback_;
   base::OnceClosure on_signaling_connected_callback_;
 
+  base::OneShotTimer tear_down_timer_;
+
   DISALLOW_COPY_AND_ASSIGN(FtlSignalingPlayground);
 };
 
diff --git a/remoting/test/test_oauth_token_getter.cc b/remoting/test/test_oauth_token_getter.cc
index 05ce6a3..74fdb24 100644
--- a/remoting/test/test_oauth_token_getter.cc
+++ b/remoting/test/test_oauth_token_getter.cc
@@ -87,6 +87,21 @@
 
 void TestOAuthTokenGetter::ResetWithAuthenticationFlow(
     base::OnceClosure on_done) {
+  on_authentication_done_.push(std::move(on_done));
+  InvalidateCache();
+}
+
+void TestOAuthTokenGetter::CallWithToken(TokenCallback on_access_token) {
+  token_getter_->CallWithToken(std::move(on_access_token));
+}
+
+void TestOAuthTokenGetter::InvalidateCache() {
+  if (is_authenticating_) {
+    return;
+  }
+
+  is_authenticating_ = true;
+
   static const std::string read_auth_code_prompt = base::StringPrintf(
       "Please authenticate at:\n\n"
       "  %s\n\n"
@@ -105,17 +120,8 @@
       base::DoNothing::Repeatedly<const std::string&, const std::string&>());
 
   // Get the access token so that we can reuse it for next time.
-  token_getter_->CallWithToken(
-      base::BindOnce(&TestOAuthTokenGetter::OnAccessToken,
-                     weak_factory_.GetWeakPtr(), std::move(on_done)));
-}
-
-void TestOAuthTokenGetter::CallWithToken(TokenCallback on_access_token) {
-  token_getter_->CallWithToken(std::move(on_access_token));
-}
-
-void TestOAuthTokenGetter::InvalidateCache() {
-  ResetWithAuthenticationFlow(base::DoNothing());
+  token_getter_->CallWithToken(base::BindOnce(
+      &TestOAuthTokenGetter::OnAccessToken, weak_factory_.GetWeakPtr()));
 }
 
 std::unique_ptr<OAuthTokenGetter>
@@ -133,21 +139,24 @@
       /* auto_refresh */ true);
 }
 
-void TestOAuthTokenGetter::OnAccessToken(base::OnceClosure on_done,
-                                         OAuthTokenGetter::Status status,
+void TestOAuthTokenGetter::OnAccessToken(OAuthTokenGetter::Status status,
                                          const std::string& user_email,
                                          const std::string& access_token) {
+  is_authenticating_ = false;
   if (status != OAuthTokenGetter::Status::SUCCESS) {
     fprintf(stderr,
             "Failed to authenticate. Please check if your access  token is "
             "correct.\n");
-    ResetWithAuthenticationFlow(std::move(on_done));
+    InvalidateCache();
     return;
   }
   VLOG(0) << "Received access_token: " << access_token;
   token_storage_->StoreUserEmail(user_email);
   token_storage_->StoreAccessToken(access_token);
-  std::move(on_done).Run();
+  while (!on_authentication_done_.empty()) {
+    std::move(on_authentication_done_.front()).Run();
+    on_authentication_done_.pop();
+  }
 }
 
 }  // namespace test
diff --git a/remoting/test/test_oauth_token_getter.h b/remoting/test/test_oauth_token_getter.h
index 4bbbfb0..16ca4e1 100644
--- a/remoting/test/test_oauth_token_getter.h
+++ b/remoting/test/test_oauth_token_getter.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback_forward.h"
+#include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "remoting/base/oauth_token_getter.h"
@@ -49,8 +50,7 @@
       const OAuthTokenGetter::CredentialsUpdatedCallback&
           on_credentials_update);
 
-  void OnAccessToken(base::OnceClosure on_done,
-                     OAuthTokenGetter::Status status,
+  void OnAccessToken(OAuthTokenGetter::Status status,
                      const std::string& user_email,
                      const std::string& access_token);
 
@@ -58,6 +58,8 @@
       url_loader_factory_owner_;
   TestTokenStorage* token_storage_ = nullptr;
   std::unique_ptr<OAuthTokenGetter> token_getter_;
+  bool is_authenticating_ = false;
+  base::queue<base::OnceClosure> on_authentication_done_;
 
   base::WeakPtrFactory<TestOAuthTokenGetter> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(TestOAuthTokenGetter);
diff --git a/services/network/resource_scheduler.cc b/services/network/resource_scheduler.cc
index a44ebe4..bfc7818b 100644
--- a/services/network/resource_scheduler.cc
+++ b/services/network/resource_scheduler.cc
@@ -671,9 +671,28 @@
 
   void RecordMetricsOnStartRequest(const ScheduledResourceRequestImpl& request,
                                    base::TimeTicks ticks_now) const {
+    const size_t non_delayable_requests_in_flight_count =
+        in_flight_requests_.size() - in_flight_delayable_count_;
+
     // Record the number of delayable requests in-flight when a non-delayable
     // request starts.
     if (!RequestAttributesAreSet(request.attributes(), kAttributeDelayable)) {
+      if (non_delayable_requests_in_flight_count > 0) {
+        if (last_non_delayable_request_start_) {
+          UMA_HISTOGRAM_MEDIUM_TIMES(
+              "ResourceScheduler.NonDelayableLastStartToNonDelayableStart."
+              "NonDelayableInFlight",
+              ticks_now - last_non_delayable_request_start_.value());
+        }
+      } else {
+        if (last_non_delayable_request_end_) {
+          UMA_HISTOGRAM_MEDIUM_TIMES(
+              "ResourceScheduler.NonDelayableLastEndToNonDelayableStart."
+              "NonDelayableNotInFlight",
+              ticks_now - last_non_delayable_request_end_.value());
+        }
+      }
+
       UMA_HISTOGRAM_COUNTS_100(
           "ResourceScheduler.NumDelayableRequestsInFlightAtStart.NonDelayable",
           in_flight_delayable_count_);
diff --git a/services/network/resource_scheduler_unittest.cc b/services/network/resource_scheduler_unittest.cc
index e3e2e4a..e2a06e6 100644
--- a/services/network/resource_scheduler_unittest.cc
+++ b/services/network/resource_scheduler_unittest.cc
@@ -1833,6 +1833,12 @@
 
   ExpectSampleIsAtLeastSpecifiedValue(
       histogram_tester_1,
+      "ResourceScheduler.NonDelayableLastStartToNonDelayableStart."
+      "NonDelayableInFlight",
+      high1_start_to_high2_start.InMilliseconds());
+
+  ExpectSampleIsAtLeastSpecifiedValue(
+      histogram_tester_1,
       "ResourceScheduler.NonDelayableLastStartOrEndToNonDelayableStart",
       high1_start_to_high2_start.InMilliseconds());
 
@@ -1868,6 +1874,12 @@
 
   ExpectSampleIsAtLeastSpecifiedValue(
       histogram_tester_2,
+      "ResourceScheduler.NonDelayableLastEndToNonDelayableStart."
+      "NonDelayableNotInFlight",
+      high2_end_to_high3_start.InMilliseconds());
+
+  ExpectSampleIsAtLeastSpecifiedValue(
+      histogram_tester_2,
       "ResourceScheduler.NonDelayableLastStartOrEndToNonDelayableStart",
       high2_end_to_high3_start.InMilliseconds());
 }
diff --git a/third_party/blink/renderer/bindings/scripts/utilities.py b/third_party/blink/renderer/bindings/scripts/utilities.py
index cbd7579..45df0332 100644
--- a/third_party/blink/renderer/bindings/scripts/utilities.py
+++ b/third_party/blink/renderer/bindings/scripts/utilities.py
@@ -445,9 +445,9 @@
         'CSSImageValueOrHTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrImageBitmapOrOffscreenCanvas': 'CanvasImageSource',
         # modules/canvas/htmlcanvas/html_canvas_element_module_support_webgl2_compute.idl
         # Due to html_canvas_element_module_support_webgl2_compute.idl and html_canvas_element_module.idl are exclusive in modules_idl_files.gni, they have same shorten name.
-        'CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrWebGL2ComputeRenderingContextOrImageBitmapRenderingContextOrXRPresentationContext': 'RenderingContext',
+        'CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrWebGL2ComputeRenderingContextOrImageBitmapRenderingContextOrXRPresentationContextOrGPUCanvasContext': 'RenderingContext',
         # modules/canvas/htmlcanvas/html_canvas_element_module.idl
-        'CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrImageBitmapRenderingContextOrXRPresentationContext': 'RenderingContext',
+        'CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrImageBitmapRenderingContextOrXRPresentationContextOrGPUCanvasContext': 'RenderingContext',
         # core/frame/window_or_worker_global_scope.idl
         'HTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas': 'ImageBitmapSource',
         # bindings/tests/idls/core/TestTypedefs.idl
diff --git a/third_party/blink/renderer/core/execution_context/security_context.h b/third_party/blink/renderer/core/execution_context/security_context.h
index 02bfd5a5..1c79dfe 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.h
+++ b/third_party/blink/renderer/core/execution_context/security_context.h
@@ -129,9 +129,6 @@
   bool GetMixedAutoUpgradeOptOut() { return mixed_autoupgrade_opt_out_; }
 
   FeaturePolicy* GetFeaturePolicy() const { return feature_policy_.get(); }
-  FeaturePolicy* GetReportOnlyFeaturePolicy() const {
-    return report_only_feature_policy_.get();
-  }
   void SetFeaturePolicy(std::unique_ptr<FeaturePolicy> feature_policy);
   // Constructs the enforcement FeaturePolicy struct for this security context.
   // The resulted FeaturePolicy is a combination of:
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index cb0a8ae6..71c3d053 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -961,11 +961,11 @@
   if (nested_layout_count_)
     return;
 
-  // TODO(crbug.com/946004): Move the following line into a DCHECK_IS_ON() block
-  // when we no longer see missing layout reports in LayoutNG.
+#if DCHECK_IS_ON()
   // Post-layout assert that nobody was re-marked as needing layout during
   // layout.
   GetLayoutView()->AssertSubtreeIsLaidOut();
+#endif
 
   if (frame_->IsMainFrame()) {
     // Scrollbars changing state can cause a visual viewport size change.
@@ -2885,9 +2885,9 @@
   // These asserts ensure that parent frames are clean, when child frames
   // finished updating layout and style.
   CheckDoesNotNeedLayout();
-  // TODO(crbug.com/946004): Move the following line into a DCHECK_IS_ON() block
-  // when we no longer see missing layout reports in LayoutNG.
+#if DCHECK_IS_ON()
   frame_->GetDocument()->GetLayoutView()->AssertLaidOut();
+#endif
 
   UpdateGeometriesIfNeeded();
 
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
index 9bc6da4..0ab64d5 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
@@ -148,6 +148,8 @@
     return kContextImageBitmap;
   if (id == "xrpresent")
     return kContextXRPresent;
+  if (id == "gpupresent" && RuntimeEnabledFeatures::WebGPUEnabled())
+    return kContextGPUPresent;
   return kContextTypeUnknown;
 }
 
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
index faca212..858c10a 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -71,7 +71,8 @@
     kContextImageBitmap = 5,
     kContextXRPresent = 6,
     kContextWebgl2Compute = 7,
-    kContextTypeUnknown = 8,
+    kContextGPUPresent = 8,
+    kContextTypeUnknown = 9,
     kMaxValue = kContextTypeUnknown,
   };
 
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
index 7afd096..eace8c95 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -75,13 +75,13 @@
 
 #if defined(SUPPORT_WEBGL2_COMPUTE_CONTEXT)
 class
-    CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrWebGL2ComputeRenderingContextOrImageBitmapRenderingContextOrXRPresentationContext;
-typedef CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrWebGL2ComputeRenderingContextOrImageBitmapRenderingContextOrXRPresentationContext
+    CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrWebGL2ComputeRenderingContextOrImageBitmapRenderingContextOrXRPresentationContextOrGPUCanvasContext;
+typedef CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrWebGL2ComputeRenderingContextOrImageBitmapRenderingContextOrXRPresentationContextOrGPUCanvasContext
     RenderingContext;
 #else
 class
-    CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrImageBitmapRenderingContextOrXRPresentationContext;
-typedef CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrImageBitmapRenderingContextOrXRPresentationContext
+    CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrImageBitmapRenderingContextOrXRPresentationContextOrGPUCanvasContext;
+typedef CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrImageBitmapRenderingContextOrXRPresentationContextOrGPUCanvasContext
     RenderingContext;
 #endif
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 04a5b56..7844e84 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -359,7 +359,8 @@
   // elements like images should use their intristic height and expand the
   // linebox  as needed. To get an appropriate quads we descend
   // into the children and have them add their boxes.
-  if (layout_object->IsLayoutInline()) {
+  if (layout_object->IsLayoutInline() &&
+      LayoutTreeBuilderTraversal::FirstChild(*node)) {
     for (Node* child = LayoutTreeBuilderTraversal::FirstChild(*node); child;
          child = LayoutTreeBuilderTraversal::NextSibling(*child))
       CollectQuadsRecursive(child, out_quads);
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 9d85143..0554a29 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -392,11 +392,6 @@
     LayoutObject& layout_object_;
     bool preexisting_forbidden_;
   };
-#endif  // DCHECK_IS_ON()
-
-  // TODO(crbug.com/946004): When we no longer see missing layouts in LayoutNG,
-  // move the following two functions back to the DCHECK_IS_ON() block and
-  // remove relevant branches and CHECK.
 
   void AssertLaidOut() const {
 #ifndef NDEBUG
@@ -404,16 +399,9 @@
       ShowLayoutTreeForThis();
 #endif
     SECURITY_DCHECK(!NeedsLayout() || LayoutBlockedByDisplayLock());
-    if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-      return;
-    CHECK(!NeedsLayout() || LayoutBlockedByDisplayLock()) << this;
   }
 
   void AssertSubtreeIsLaidOut() const {
-#if !DCHECK_IS_ON()
-    if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-      return;
-#endif
     for (const LayoutObject* layout_object = this; layout_object;
          layout_object = layout_object->LayoutBlockedByDisplayLock()
                              ? layout_object->NextInPreOrderAfterChildren()
@@ -422,7 +410,6 @@
     }
   }
 
-#if DCHECK_IS_ON()
   void AssertClearedPaintInvalidationFlags() const {
 #ifndef NDEBUG
     if (PaintInvalidationStateIsDirty() && !PrePaintBlockedByDisplayLock()) {
@@ -441,7 +428,7 @@
     }
   }
 
-#endif  // DCHECK_IS_ON()
+#endif
 
   // LayoutObject tree manipulation
   //////////////////////////////////////////
diff --git a/third_party/blink/renderer/core/layout/layout_table_section.cc b/third_party/blink/renderer/core/layout/layout_table_section.cc
index 9cd5981..eb0b1bd 100644
--- a/third_party/blink/renderer/core/layout/layout_table_section.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_section.cc
@@ -1394,19 +1394,6 @@
     for (auto* cell = row->FirstCell(); cell; cell = cell->NextCell()) {
       if (cell->HasSelfPaintingLayer())
         continue;
-      // Let the section's self visual overflow cover the cell's whole collapsed
-      // borders. This ensures correct raster invalidation on section border
-      // style change.
-      // TODO(wangxianzhu): When implementing row as DisplayItemClient of
-      // collapsed borders, the following logic should be replaced by
-      // invalidation of rows on section border style change. crbug.com/663208.
-      if (const auto* collapsed_borders = cell->GetCollapsedBorderValues()) {
-        LayoutRect rect = cell->RectForOverflowPropagation(
-            collapsed_borders->LocalVisualRect());
-        rect.MoveBy(cell->Location());
-        AddSelfVisualOverflow(rect);
-      }
-
       if (force_full_paint_ || !cell->HasVisualOverflow())
         continue;
 
diff --git a/third_party/blink/renderer/core/layout/layout_table_section_test.cc b/third_party/blink/renderer/core/layout/layout_table_section_test.cc
index 9b40a63..f26085d5 100644
--- a/third_party/blink/renderer/core/layout/layout_table_section_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_section_test.cc
@@ -301,11 +301,8 @@
 
   auto* section = GetSectionByElementId("section");
 
-  // The section's self visual overflow covers the collapsed borders.
-  LayoutRect expected_self_visual_overflow = section->BorderBoxRect();
-  expected_self_visual_overflow.ExpandEdges(LayoutUnit(1), LayoutUnit(8),
-                                            LayoutUnit(0), LayoutUnit(0));
-  EXPECT_EQ(expected_self_visual_overflow, section->SelfVisualOverflowRect());
+  // The section's self visual overflow doesn't cover the collapsed borders.
+  EXPECT_EQ(section->BorderBoxRect(), section->SelfVisualOverflowRect());
 
   // The section's visual overflow covers self visual overflow and visual
   // overflows rows.
diff --git a/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-dt-bundle.js b/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-dt-bundle.js
index 5c086c44..f48bc4c 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-dt-bundle.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-dt-bundle.js
@@ -1,4 +1,4 @@
-// lighthouse, browserified. 4.2.0 (9790337f9873710c3fd38bafdec819337c8367be)
+// lighthouse, browserified. 4.3.0 (01b217be64ddff7ca500ad0f787f914fa9900b73)
 require=function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a;}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r);},p,p.exports,r,e,n,t);}return n[i].exports;}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o;}return r;}()({"../audits/accessibility/accesskeys":[function(require,module,exports){
 (function(__filename){
 
@@ -3138,7 +3138,7 @@
 title:str_(UIStrings.title),
 description:str_(UIStrings.description),
 scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-requiredArtifacts:['Scripts','devtoolsLogs','traces']};
+requiredArtifacts:['ScriptElements','devtoolsLogs','traces']};
 
 }
 
@@ -3174,10 +3174,11 @@
 
 const items=[];
 const warnings=[];
-for(const{requestId,inline,content}of artifacts.Scripts){
+for(const{requestId,src,content}of artifacts.ScriptElements){
 if(!content)continue;
+
 const networkRecord=networkRecords.find(record=>record.requestId===requestId);
-const displayUrl=inline||!networkRecord?
+const displayUrl=!src||!networkRecord?
 `inline: ${content.substr(0,40)}...`:
 networkRecord.url;
 try{
@@ -6031,7 +6032,7 @@
 
 for(const stylesheet of artifacts.CSSUsage.stylesheets){
 
-const newlinesStripped=stylesheet.content.replace(/\n/g,' ');
+const newlinesStripped=stylesheet.content.replace(/(\r|\n)+/g,' ');
 
 const fontFaceDeclarations=newlinesStripped.match(/@font-face\s*{(.*?)}/g)||[];
 
@@ -6566,10 +6567,10 @@
 const tasks=await MainThreadTasksComputed.request(trace,context);
 
 const results=tasks.
-filter(task=>task.duration>5&&task.parent).
+
+filter(task=>task.duration>5&&!task.parent).
 map(task=>{
 return{
-type:task.group.id,
 duration:task.duration,
 startTime:task.startTime};
 
@@ -6577,7 +6578,6 @@
 
 
 const headings=[
-{key:'type',itemType:'text',text:'Task Type'},
 {key:'startTime',itemType:'ms',granularity:1,text:'Start Time'},
 {key:'duration',itemType:'ms',granularity:1,text:'End Time'}];
 
@@ -7821,6 +7821,11 @@
 undefined:(time-earliestStartTime)*1000;
 
 const results=records.map(record=>{
+const endTimeDeltaMs=record.lrStatistics&&record.lrStatistics.endTimeDeltaMs;
+const TCPMs=record.lrStatistics&&record.lrStatistics.TCPMs;
+const requestMs=record.lrStatistics&&record.lrStatistics.requestMs;
+const responseMs=record.lrStatistics&&record.lrStatistics.responseMs;
+
 return{
 url:URL.elideDataURI(record.url),
 startTime:timeToMs(record.startTime),
@@ -7829,7 +7834,11 @@
 resourceSize:record.resourceSize,
 statusCode:record.statusCode,
 mimeType:record.mimeType,
-resourceType:record.resourceType};
+resourceType:record.resourceType,
+lrEndTimeDeltaMs:endTimeDeltaMs,
+lrTCPMs:TCPMs,
+lrRequestMs:requestMs,
+lrResponseMs:responseMs};
 
 });
 
@@ -12281,7 +12290,8 @@
 
 const seenUrls=new Set();
 return networkRecords.reduce((prev,record)=>{
-if(seenUrls.has(record.url)||!record.finished){
+
+if(seenUrls.has(record.url)||!record.finished||record.sessionId){
 return prev;
 }
 
@@ -12493,6 +12503,9 @@
 const unoptimizedResponses=[];
 
 networkRecords.forEach(record=>{
+
+if(record.sessionId)return;
+
 const mimeType=record.mimeType;
 const resourceType=record.resourceType||NetworkRequest.TYPES.Other;
 const resourceSize=record.resourceSize;
@@ -13483,7 +13496,7 @@
 
 module.exports=RuntimeExceptions;
 
-},{"./gatherer":48}],"../gather/gatherers/scripts":[function(require,module,exports){
+},{"./gatherer":48}],"../gather/gatherers/script-elements":[function(require,module,exports){
 
 
 
@@ -13491,16 +13504,37 @@
 
 'use strict';
 
-const log=require('lighthouse-logger');
-const Gatherer=require('./gatherer');
-const NetworkRequest=require('../../lib/network-request');
+const Gatherer=require('./gatherer.js');
+const NetworkAnalyzer=require('../../lib/dependency-graph/simulator/network-analyzer.js');
+const NetworkRequest=require('../../lib/network-request.js');
 const getElementsInDocumentString=require('../../lib/page-functions.js').getElementsInDocumentString;
-const URL=require('../../lib/url-shim.js');
 
 
 
 
-class Scripts extends Gatherer{
+
+function collectAllScriptElements(){
+
+
+const scripts=getElementsInDocument('script');
+
+return scripts.map(script=>{
+return{
+type:script.type||null,
+src:script.src||null,
+async:script.async,
+defer:script.defer,
+source:script.closest('head')?'head':'body',
+content:script.src?null:script.text,
+requestId:null};
+
+});
+}
+
+
+
+
+class ScriptElements extends Gatherer{
 
 
 
@@ -13508,52 +13542,42 @@
 
 async afterPass(passContext,loadData){
 const driver=passContext.driver;
+const mainResource=NetworkAnalyzer.findMainDocument(loadData.networkRecords,passContext.url);
 
 
-const scripts=[];
-
-
-const inlineScripts=await driver.evaluateAsync(`(() => {
-      ${getElementsInDocumentString};
-
-      return getElementsInDocument('script')
-        .filter(script => !script.src && script.text.trim())
-        .map(script => script.text);
+const scripts=await driver.evaluateAsync(`(() => {
+      ${getElementsInDocumentString}
+      return (${collectAllScriptElements.toString()})();
     })()`,{useIsolation:true});
 
-if(inlineScripts.length){
-
-
-
-const mainResource=loadData.networkRecords.find(request=>
-passContext.url.startsWith(request.url)&&
-URL.equalWithExcludedFragments(request.url,passContext.url));
-if(!mainResource){
-log.warn('Scripts','could not locate mainResource');
-}
-const requestId=mainResource?mainResource.requestId:undefined;
-scripts.push(
-...inlineScripts.map(content=>{
-return{
-content,
-inline:true,
-requestId};
-
-}));
-
+for(const script of scripts){
+if(script.content)script.requestId=mainResource.requestId;
 }
 
 const scriptRecords=loadData.networkRecords.
+
+filter(record=>!record.sessionId).
+
 filter(record=>record.resourceType===NetworkRequest.TYPES.Script);
 
 for(const record of scriptRecords){
 try{
 const content=await driver.getRequestContent(record.requestId);
-if(content){
+if(!content)continue;
+
+const matchedScriptElement=scripts.find(script=>script.src===record.url);
+if(matchedScriptElement){
+matchedScriptElement.requestId=record.requestId;
+matchedScriptElement.content=content;
+}else{
 scripts.push({
-content,
-inline:false,
-requestId:record.requestId});
+type:null,
+src:record.url,
+async:false,
+defer:false,
+source:'network',
+requestId:record.requestId,
+content});
 
 }
 }catch(e){}
@@ -13563,9 +13587,9 @@
 }}
 
 
-module.exports=Scripts;
+module.exports=ScriptElements;
 
-},{"../../lib/network-request":71,"../../lib/page-functions.js":72,"../../lib/url-shim.js":"url","./gatherer":48,"lighthouse-logger":120}],"../gather/gatherers/seo/embedded-content":[function(require,module,exports){
+},{"../../lib/dependency-graph/simulator/network-analyzer.js":57,"../../lib/network-request.js":71,"../../lib/page-functions.js":72,"./gatherer.js":48}],"../gather/gatherers/seo/embedded-content":[function(require,module,exports){
 
 
 
@@ -15801,7 +15825,8 @@
 
 
 
-static _createTasksFromEvents(mainThreadEvents,priorTaskData){
+
+static _createTasksFromEvents(mainThreadEvents,priorTaskData,traceEndTs){
 
 const tasks=[];
 
@@ -15833,7 +15858,7 @@
 if(!currentTask){
 
 if(event.ph==='E'){
-throw new Error('Fatal trace logic error');
+throw new Error('Fatal trace logic error - unexpected end event');
 }
 
 currentTask=MainThreadTasks._createNewTaskNode(event);
@@ -15849,7 +15874,8 @@
 currentTask=newTask;
 }else{
 if(currentTask.event.ph!=='B'){
-throw new Error('Fatal trace logic error');
+throw new Error(
+`Fatal trace logic error - expected start event, got ${currentTask.event.ph}`);
 }
 
 
@@ -15858,6 +15884,14 @@
 }
 }
 
+
+while(currentTask&&!Number.isFinite(currentTask.endTime)){
+
+currentTask.endTime=traceEndTs;
+currentTask=currentTask.parent;
+}
+
+
 return tasks;
 }
 
@@ -15865,9 +15899,14 @@
 
 
 
-static _computeRecursiveSelfTime(task){
+
+static _computeRecursiveSelfTime(task,parent){
+if(parent&&task.endTime>parent.endTime){
+throw new Error('Fatal trace logic error - child cannot end after parent');
+}
+
 const childTime=task.children.
-map(MainThreadTasks._computeRecursiveSelfTime).
+map(child=>MainThreadTasks._computeRecursiveSelfTime(child,task)).
 reduce((sum,child)=>sum+child,0);
 task.duration=task.endTime-task.startTime;
 task.selfTime=task.duration-childTime;
@@ -15942,16 +15981,17 @@
 
 
 
-static getMainThreadTasks(traceEvents){
+
+static getMainThreadTasks(traceEvents,traceEndTs){
 const timers=new Map();
 const priorTaskData={timers};
-const tasks=MainThreadTasks._createTasksFromEvents(traceEvents,priorTaskData);
+const tasks=MainThreadTasks._createTasksFromEvents(traceEvents,priorTaskData,traceEndTs);
 
 
 for(const task of tasks){
 if(task.parent)continue;
 
-MainThreadTasks._computeRecursiveSelfTime(task);
+MainThreadTasks._computeRecursiveSelfTime(task,undefined);
 MainThreadTasks._computeRecursiveAttributableURLs(task,[],priorTaskData);
 MainThreadTasks._computeRecursiveTaskGroup(task);
 }
@@ -15979,8 +16019,8 @@
 
 
 static async compute_(trace,context){
-const{mainThreadEvents}=await TraceOfTab.request(trace,context);
-return MainThreadTasks.getMainThreadTasks(mainThreadEvents);
+const{mainThreadEvents,timestamps}=await TraceOfTab.request(trace,context);
+return MainThreadTasks.getMainThreadTasks(mainThreadEvents,timestamps.traceEnd);
 }}
 
 
@@ -17742,7 +17782,7 @@
 const records=await NetworkRecords.request(devtoolsLog,context);
 const throughput=NetworkAnalyzer.estimateThroughput(records);
 const rttAndServerResponseTime=NetworkAnalysis.computeRTTAndServerResponseTime(records);
-return{records,throughput,...rttAndServerResponseTime};
+return{throughput,...rttAndServerResponseTime};
 }}
 
 
@@ -17907,9 +17947,9 @@
 rootNode.addDependent(node);
 }
 
-const redirects=Array.from(node.record.redirects||[]);
-redirects.push(node.record);
+if(!node.record.redirects)return;
 
+const redirects=[...node.record.redirects,node.record];
 for(let i=1;i<redirects.length;i++){
 const redirectNode=networkNodeOutput.idToNodeMap.get(redirects[i-1].requestId);
 const actualNode=networkNodeOutput.idToNodeMap.get(redirects[i].requestId);
@@ -19944,7 +19984,6 @@
 networkQuietThresholdMs:1000,
 cpuQuietThresholdMs:1000,
 gatherers:[
-'scripts',
 'css-usage',
 'viewport-dimensions',
 'runtime-exceptions',
@@ -19954,6 +19993,7 @@
 'image-elements',
 'link-elements',
 'meta-elements',
+'script-elements',
 'dobetterweb/appcache',
 'dobetterweb/doctype',
 'dobetterweb/domstats',
@@ -20972,7 +21012,7 @@
 
 
 async _handleReceivedMessageFromTarget(event,parentSessionIds){
-const{sessionId,message}=event;
+const{targetId,sessionId,message}=event;
 
 const protocolMessage=JSON.parse(message);
 
@@ -20994,7 +21034,7 @@
 }
 
 if(protocolMessage.method.startsWith('Network')){
-this._handleProtocolEvent(protocolMessage);
+this._handleProtocolEvent({...protocolMessage,source:{targetId,sessionId}});
 }
 }
 
@@ -23474,40 +23514,43 @@
 const rootNode=this.getRootNode();
 
 
-let shouldIncludeNode=()=>true;
-if(predicate){
-const idsToInclude=new Set();
+const idsToIncludedClones=new Map();
+
+
 rootNode.traverse(node=>{
+if(idsToIncludedClones.has(node.id))return;
+
+if(predicate===undefined){
+
+idsToIncludedClones.set(node.id,node.cloneWithoutRelationships());
+return;
+}
+
 if(predicate(node)){
+
 node.traverse(
-node=>idsToInclude.add(node.id),
-node=>node._dependencies.filter(parent=>!idsToInclude.has(parent)));
+node=>idsToIncludedClones.set(node.id,node.cloneWithoutRelationships()),
+
+node=>node._dependencies.filter(parent=>!idsToIncludedClones.has(parent.id)));
 
 }
 });
 
-shouldIncludeNode=node=>idsToInclude.has(node.id);
-}
-
-const idToNodeMap=new Map();
-rootNode.traverse(originalNode=>{
-if(!shouldIncludeNode(originalNode))return;
-const clonedNode=originalNode.cloneWithoutRelationships();
-idToNodeMap.set(clonedNode.id,clonedNode);
-});
 
 rootNode.traverse(originalNode=>{
-if(!shouldIncludeNode(originalNode))return;
-const clonedNode=idToNodeMap.get(originalNode.id);
+const clonedNode=idsToIncludedClones.get(originalNode.id);
+if(!clonedNode)return;
 
 for(const dependency of originalNode._dependencies){
-const clonedDependency=idToNodeMap.get(dependency.id);
+const clonedDependency=idsToIncludedClones.get(dependency.id);
+if(!clonedDependency)throw new Error('Dependency somehow not cloned');
 clonedNode.addDependency(clonedDependency);
 }
 });
 
-if(!idToNodeMap.has(this.id))throw new Error('Cloned graph missing node');
-return idToNodeMap.get(this.id);
+const clonedThisNode=idsToIncludedClones.get(this.id);
+if(!clonedThisNode)throw new Error('Cloned graph missing node');
+return clonedThisNode;
 }
 
 
@@ -23516,45 +23559,34 @@
 
 
 
-_traversePaths(iterator,getNext){
-const stack=[[this]];
-while(stack.length){
 
 
-const path=stack.shift();
-const node=path[0];
-iterator(node,path);
 
-const nodesToAdd=getNext(node);
-for(const nextNode of nodesToAdd){
-stack.push([nextNode].concat(path));
-}
-}
+
+traverse(callback,getNextNodes){
+if(!getNextNodes){
+getNextNodes=node=>node.getDependents();
 }
 
 
 
+const queue=[[this]];
+const visited=new Set([this.id]);
+
+while(queue.length){
 
 
+const traversalPath=queue.shift();
+const node=traversalPath[0];
+callback(node,traversalPath);
 
+for(const nextNode of getNextNodes(node)){
+if(visited.has(nextNode.id))continue;
+visited.add(nextNode.id);
 
-traverse(iterator,getNext){
-if(!getNext){
-getNext=node=>node.getDependents();
+queue.push([nextNode,...traversalPath]);
 }
-
-const visited=new Set();
-const originalGetNext=getNext;
-
-getNext=node=>{
-visited.add(node.id);
-const allNodesToVisit=originalGetNext(node);
-const nodesToVisit=allNodesToVisit.filter(nextNode=>!visited.has(nextNode.id));
-nodesToVisit.forEach(nextNode=>visited.add(nextNode.id));
-return nodesToVisit;
-};
-
-this._traversePaths(iterator,getNext);
+}
 }
 
 
@@ -23811,19 +23843,7 @@
 
 
 constructor(records,options){
-this._options=Object.assign(
-{
-rtt:undefined,
-throughput:undefined,
-additionalRttByOrigin:new Map(),
-serverResponseTimeByOrigin:new Map()},
-
-options);
-
-
-if(!this._options.rtt||!this._options.throughput){
-throw new Error('Cannot create pool with no rtt or throughput');
-}
+this._options=options;
 
 this._records=records;
 
@@ -23929,14 +23949,10 @@
 
 
 acquire(record,options={}){
-if(this._connectionsByRecord.has(record)){
+if(this._connectionsByRecord.has(record))throw new Error('Record already has a connection');
 
-return this._connectionsByRecord.get(record);
-}
-
-const origin=String(record.parsedURL.securityOrigin);
+const origin=record.parsedURL.securityOrigin;
 const observedConnectionWasReused=!!this._connectionReusedByRequestId.get(record.requestId);
-
 const connections=this._connectionsByOrigin.get(origin)||[];
 const connectionToUse=this._findAvailableConnectionWithLargestCongestionWindow(connections,{
 ignoreConnectionReused:options.ignoreConnectionReused,
@@ -23953,6 +23969,20 @@
 
 
 
+
+
+
+
+acquireActiveConnectionFromRecord(record){
+const activeConnection=this._connectionsByRecord.get(record);
+if(!activeConnection)throw new Error('Could not find an active connection for record');
+
+return activeConnection;
+}
+
+
+
+
 release(record){
 const connection=this._connectionsByRecord.get(record);
 this._connectionsByRecord.delete(record);
@@ -23979,19 +24009,9 @@
 
 
 
-constructor(options){
-this._options=Object.assign(
-{
-rtt:undefined},
+constructor({rtt}){
+this._rtt=rtt;
 
-options);
-
-
-if(!this._options.rtt){
-throw new Error('Cannot create DNS cache with no rtt');
-}
-
-this._rtt=this._options.rtt;
 
 this._resolvedDomainNames=new Map();
 }
@@ -24312,10 +24332,10 @@
 
 
 static estimateIfConnectionWasReused(records,options){
-options=Object.assign({forceCoarseEstimates:false},options);
+const{forceCoarseEstimates=false}=options||{};
 
 
-if(!options.forceCoarseEstimates&&NetworkAnalyzer.canTrustConnectionInformation(records)){
+if(!forceCoarseEstimates&&NetworkAnalyzer.canTrustConnectionInformation(records)){
 
 return new Map(records.map(record=>[record.requestId,!!record.connectionReused]));
 }
@@ -24355,48 +24375,42 @@
 
 
 static estimateRTTByOrigin(records,options){
-options=Object.assign(
-{
+const{
+forceCoarseEstimates=false,
 
 
-forceCoarseEstimates:false,
-
-
-coarseEstimateMultiplier:0.3,
-
-useDownloadEstimates:true,
-useSendStartEstimates:true,
-useHeadersEndEstimates:true},
-
-options);
-
+coarseEstimateMultiplier=0.3,
+useDownloadEstimates=true,
+useSendStartEstimates=true,
+useHeadersEndEstimates=true}=
+options||{};
 
 let estimatesByOrigin=NetworkAnalyzer._estimateRTTByOriginViaTCPTiming(records);
-if(!estimatesByOrigin.size||options.forceCoarseEstimates){
+if(!estimatesByOrigin.size||forceCoarseEstimates){
 estimatesByOrigin=new Map();
 const estimatesViaDownload=NetworkAnalyzer._estimateRTTByOriginViaDownloadTiming(records);
 const estimatesViaSendStart=NetworkAnalyzer._estimateRTTByOriginViaSendStartTiming(records);
 const estimatesViaTTFB=NetworkAnalyzer._estimateRTTByOriginViaHeadersEndTiming(records);
 
 for(const[origin,estimates]of estimatesViaDownload.entries()){
-if(!options.useDownloadEstimates)continue;
+if(!useDownloadEstimates)continue;
 estimatesByOrigin.set(origin,estimates);
 }
 
 for(const[origin,estimates]of estimatesViaSendStart.entries()){
-if(!options.useSendStartEstimates)continue;
+if(!useSendStartEstimates)continue;
 const existing=estimatesByOrigin.get(origin)||[];
 estimatesByOrigin.set(origin,existing.concat(estimates));
 }
 
 for(const[origin,estimates]of estimatesViaTTFB.entries()){
-if(!options.useHeadersEndEstimates)continue;
+if(!useHeadersEndEstimates)continue;
 const existing=estimatesByOrigin.get(origin)||[];
 estimatesByOrigin.set(origin,existing.concat(estimates));
 }
 
 for(const estimates of estimatesByOrigin.values()){
-estimates.forEach((x,i)=>estimates[i]=x*options.coarseEstimateMultiplier);
+estimates.forEach((x,i)=>estimates[i]=x*coarseEstimateMultiplier);
 }
 }
 
@@ -24413,17 +24427,13 @@
 
 
 static estimateServerResponseTimeByOrigin(records,options){
-options=Object.assign(
-{
-rttByOrigin:null},
-
-options);
-
-
-let rttByOrigin=options.rttByOrigin;
+let rttByOrigin=(options||{}).rttByOrigin;
 if(!rttByOrigin){
-rttByOrigin=NetworkAnalyzer.estimateRTTByOrigin(records,options);
-for(const[origin,summary]of rttByOrigin.entries()){
+
+rttByOrigin=new Map();
+
+const rttSummaryByOrigin=NetworkAnalyzer.estimateRTTByOrigin(records,options);
+for(const[origin,summary]of rttSummaryByOrigin.entries()){
 rttByOrigin.set(origin,summary.min);
 }
 }
@@ -24523,6 +24533,15 @@
 
 
 
+
+
+
+
+
+
+
+
+
 },{"../../network-request.js":71,"../../url-shim.js":"url"}],58:[function(require,module,exports){
 
 
@@ -24826,8 +24845,7 @@
 const sizeInMb=(record.resourceSize||0)/1024/1024;
 timeElapsed=8+20*sizeInMb-timingData.timeElapsed;
 }else{
-
-const connection=this._acquireConnection(record);
+const connection=this._connectionPool.acquireActiveConnectionFromRecord(record);
 const dnsResolutionTime=this._dns.getTimeUntilResolution(record,{
 requestedAt:timingData.startTime,
 shouldUpdateCache:true});
@@ -24878,8 +24896,7 @@
 if(node.type!==BaseNode.TYPES.NETWORK)throw new Error('Unsupported');
 
 const record=node.record;
-
-const connection=this._acquireConnection(record);
+const connection=this._connectionPool.acquireActiveConnectionFromRecord(record);
 const dnsResolutionTime=this._dns.getTimeUntilResolution(record,{
 requestedAt:timingData.startTime,
 shouldUpdateCache:true});
@@ -28612,8 +28629,9 @@
 
 
 
-onRequestWillBeSent(data){
-const originalRequest=this._findRealRequest(data.requestId);
+onRequestWillBeSent(event){
+const data=event.params;
+const originalRequest=this._findRealRequestAndSetSource(data.requestId,event.source);
 
 if(!originalRequest){
 const request=new NetworkRequest();
@@ -28651,8 +28669,9 @@
 
 
 
-onRequestServedFromCache(data){
-const request=this._findRealRequest(data.requestId);
+onRequestServedFromCache(event){
+const data=event.params;
+const request=this._findRealRequestAndSetSource(data.requestId,event.source);
 if(!request)return;
 request.onRequestServedFromCache();
 }
@@ -28660,8 +28679,9 @@
 
 
 
-onResponseReceived(data){
-const request=this._findRealRequest(data.requestId);
+onResponseReceived(event){
+const data=event.params;
+const request=this._findRealRequestAndSetSource(data.requestId,event.source);
 if(!request)return;
 request.onResponseReceived(data);
 }
@@ -28669,8 +28689,9 @@
 
 
 
-onDataReceived(data){
-const request=this._findRealRequest(data.requestId);
+onDataReceived(event){
+const data=event.params;
+const request=this._findRealRequestAndSetSource(data.requestId,event.source);
 if(!request)return;
 request.onDataReceived(data);
 }
@@ -28678,8 +28699,9 @@
 
 
 
-onLoadingFinished(data){
-const request=this._findRealRequest(data.requestId);
+onLoadingFinished(event){
+const data=event.params;
+const request=this._findRealRequestAndSetSource(data.requestId,event.source);
 if(!request)return;
 request.onLoadingFinished(data);
 this.onRequestFinished(request);
@@ -28688,8 +28710,9 @@
 
 
 
-onLoadingFailed(data){
-const request=this._findRealRequest(data.requestId);
+onLoadingFailed(event){
+const data=event.params;
+const request=this._findRealRequestAndSetSource(data.requestId,event.source);
 if(!request)return;
 request.onLoadingFailed(data);
 this.onRequestFinished(request);
@@ -28698,8 +28721,9 @@
 
 
 
-onResourceChangedPriority(data){
-const request=this._findRealRequest(data.requestId);
+onResourceChangedPriority(event){
+const data=event.params;
+const request=this._findRealRequestAndSetSource(data.requestId,event.source);
 if(!request)return;
 request.onResourceChangedPriority(data);
 }
@@ -28710,13 +28734,13 @@
 
 dispatch(event){
 switch(event.method){
-case'Network.requestWillBeSent':return this.onRequestWillBeSent(event.params);
-case'Network.requestServedFromCache':return this.onRequestServedFromCache(event.params);
-case'Network.responseReceived':return this.onResponseReceived(event.params);
-case'Network.dataReceived':return this.onDataReceived(event.params);
-case'Network.loadingFinished':return this.onLoadingFinished(event.params);
-case'Network.loadingFailed':return this.onLoadingFailed(event.params);
-case'Network.resourceChangedPriority':return this.onResourceChangedPriority(event.params);
+case'Network.requestWillBeSent':return this.onRequestWillBeSent(event);
+case'Network.requestServedFromCache':return this.onRequestServedFromCache(event);
+case'Network.responseReceived':return this.onResponseReceived(event);
+case'Network.dataReceived':return this.onDataReceived(event);
+case'Network.loadingFinished':return this.onLoadingFinished(event);
+case'Network.loadingFailed':return this.onLoadingFailed(event);
+case'Network.resourceChangedPriority':return this.onResourceChangedPriority(event);
 default:return;}
 
 }
@@ -28730,7 +28754,8 @@
 
 
 
-_findRealRequest(requestId){
+
+_findRealRequestAndSetSource(requestId,source){
 let request=this._recordsById.get(requestId);
 if(!request||!request.isValid)return undefined;
 
@@ -28738,6 +28763,7 @@
 request=request.redirectDestination;
 }
 
+request.setSource(source);
 return request;
 }
 
@@ -28815,6 +28841,27 @@
 
 
 
+const HEADER_TCP='X-TCPMs';
+const HEADER_SSL='X-SSLMs';
+const HEADER_REQ='X-RequestMs';
+const HEADER_RES='X-ResponseMs';
+const HEADER_TOTAL='X-TotalMs';
+const HEADER_FETCHED_SIZE='X-TotalFetchedSize';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 
 
 
@@ -28846,7 +28893,7 @@
 CSPViolationReport:'CSPViolationReport'};
 
 
-module.exports=class NetworkRequest{
+class NetworkRequest{
 constructor(){
 this.requestId='';
 this.connectionId='0';
@@ -28871,6 +28918,9 @@
 this.fromDiskCache=false;
 this.fromMemoryCache=false;
 
+
+this.lrStatistics=undefined;
+
 this.finished=false;
 this.requestMethod='';
 this.statusCode=-1;
@@ -28901,6 +28951,18 @@
 this.fetchedViaServiceWorker=false;
 
 this.frameId='';
+
+
+
+
+
+this.targetId=undefined;
+
+
+
+
+
+this.sessionId=undefined;
 this.isLinkPreload=false;
 }
 
@@ -28991,6 +29053,7 @@
 
 this._updateResponseReceivedTimeIfNecessary();
 this._updateTransferSizeForLightrider();
+this._updateTimingsForLightrider();
 }
 
 
@@ -29009,6 +29072,7 @@
 
 this._updateResponseReceivedTimeIfNecessary();
 this._updateTransferSizeForLightrider();
+this._updateTimingsForLightrider();
 }
 
 
@@ -29034,6 +29098,19 @@
 
 
 
+setSource(source){
+if(source){
+this.targetId=source.targetId;
+this.sessionId=source.sessionId;
+}else{
+this.targetId=undefined;
+this.sessionId=undefined;
+}
+}
+
+
+
+
 
 
 _onResponse(response,timestamp,resourceType){
@@ -29082,6 +29159,8 @@
 
 this.responseReceivedTime=Math.min(this.responseReceivedTime,headersReceivedTime);
 this.responseReceivedTime=Math.max(this.responseReceivedTime,this.startTime);
+
+
 this.endTime=Math.max(this.endTime,this.responseReceivedTime);
 }
 
@@ -29114,10 +29193,71 @@
 
 if(!global.isLightrider)return;
 
-const totalFetchedSize=this.responseHeaders.find(item=>item.name==='X-TotalFetchedSize');
+const totalFetchedSize=this.responseHeaders.find(item=>item.name===HEADER_FETCHED_SIZE);
 
 if(!totalFetchedSize)return;
-this.transferSize=parseFloat(totalFetchedSize.value);
+const floatValue=parseFloat(totalFetchedSize.value);
+
+if(isNaN(floatValue))return;
+this.transferSize=floatValue;
+}
+
+
+
+
+
+_updateTimingsForLightrider(){
+
+if(!global.isLightrider)return;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+const totalHeader=this.responseHeaders.find(item=>item.name===HEADER_TOTAL);
+
+if(!totalHeader)return;
+
+const totalMs=parseInt(totalHeader.value);
+const TCPMsHeader=this.responseHeaders.find(item=>item.name===HEADER_TCP);
+const SSLMsHeader=this.responseHeaders.find(item=>item.name===HEADER_SSL);
+const requestMsHeader=this.responseHeaders.find(item=>item.name===HEADER_REQ);
+const responseMsHeader=this.responseHeaders.find(item=>item.name===HEADER_RES);
+
+
+const TCPMs=TCPMsHeader?Math.max(0,parseInt(TCPMsHeader.value)):0;
+const SSLMs=SSLMsHeader?Math.max(0,parseInt(SSLMsHeader.value)):0;
+const requestMs=requestMsHeader?Math.max(0,parseInt(requestMsHeader.value)):0;
+const responseMs=responseMsHeader?Math.max(0,parseInt(responseMsHeader.value)):0;
+
+
+if(TCPMs+requestMs+responseMs!==totalMs){
+return;
+}
+
+
+if(SSLMs>TCPMs){
+return;
+}
+
+this.lrStatistics={
+endTimeDeltaMs:(this.endTime-(this.startTime+totalMs/1000))*1000,
+TCPMs:TCPMs,
+requestMs:requestMs,
+responseMs:responseMs};
+
 }
 
 
@@ -29149,9 +29289,18 @@
 
 static get TYPES(){
 return RESOURCE_TYPES;
-}};
+}}
 
 
+NetworkRequest.HEADER_TCP=HEADER_TCP;
+NetworkRequest.HEADER_SSL=HEADER_SSL;
+NetworkRequest.HEADER_REQ=HEADER_REQ;
+NetworkRequest.HEADER_RES=HEADER_RES;
+NetworkRequest.HEADER_TOTAL=HEADER_TOTAL;
+NetworkRequest.HEADER_FETCHED_SIZE=HEADER_FETCHED_SIZE;
+
+module.exports=NetworkRequest;
+
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
 },{"./url-shim":"url"}],72:[function(require,module,exports){
 
@@ -31370,7 +31519,16 @@
 ...timingEntriesFromRunner].
 
 map(entry=>[entry.startTime,entry]);
-const timingEntries=Array.from(new Map(timingEntriesKeyValues).values());
+const timingEntries=Array.from(new Map(timingEntriesKeyValues).values()).
+
+
+map(entry=>{
+return{
+...entry,
+duration:parseFloat(entry.duration.toFixed(2)),
+startTime:parseFloat(entry.startTime.toFixed(2))};
+
+});
 const runnerEntry=timingEntries.find(e=>e.name==='lh:runner:run');
 return{entries:timingEntries,total:runnerEntry&&runnerEntry.duration||0};
 }
@@ -31578,7 +31736,7 @@
 
 static getGathererList(){
 const fileList=[
-...["accessibility.js","anchor-elements.js","cache-contents.js","chrome-console-messages.js","css-usage.js","dobetterweb","gatherer.js","html-without-javascript.js","http-redirect.js","image-elements.js","js-usage.js","link-elements.js","meta-elements.js","mixed-content.js","offline.js","runtime-exceptions.js","scripts.js","seo","service-worker.js","start-url.js","viewport-dimensions.js"],
+...["accessibility.js","anchor-elements.js","cache-contents.js","chrome-console-messages.js","css-usage.js","dobetterweb","gatherer.js","html-without-javascript.js","http-redirect.js","image-elements.js","js-usage.js","link-elements.js","meta-elements.js","mixed-content.js","offline.js","runtime-exceptions.js","script-elements.js","seo","service-worker.js","start-url.js","viewport-dimensions.js"],
 ...["embedded-content.js","font-size.js","robots-txt.js","tap-targets.js"].map(f=>`seo/${f}`),
 ...["appcache.js","doctype.js","domstats.js","js-libraries.js","optimized-images.js","password-inputs-with-prevented-paste.js","response-compression.js","tags-blocking-first-paint.js"].
 map(f=>`dobetterweb/${f}`)];
@@ -62174,7 +62332,7 @@
 arguments[4][87][0].apply(exports,arguments);
 },{"./support/isBuffer":165,"_process":137,"dup":87,"inherits":105}],167:[function(require,module,exports){
 module.exports={
-"version":"4.2.0"};
+"version":"4.3.0"};
 
 },{}],168:[function(require,module,exports){
 module.exports={
@@ -62236,10 +62394,11 @@
 {"id":"npm:highcharts:20180225","severity":"high","semver":{"vulnerable":["<6.1.0"]}}],
 
 "jquery":[
+{"id":"SNYK-JS-JQUERY-174006","severity":"medium","semver":{"vulnerable":["*"]}},
 {"id":"npm:jquery:20160529","severity":"low","semver":{"vulnerable":["=3.0.0-rc1"]}},
 {"id":"npm:jquery:20150627","severity":"medium","semver":{"vulnerable":["<1.12.2",">=1.12.3 <2.2.2",">=2.2.3 <3.0.0"]}},
 {"id":"npm:jquery:20140902","severity":"medium","semver":{"vulnerable":[">=1.4.2 <1.6.2"]}},
-{"id":"npm:jquery:20120206","severity":"medium","semver":{"vulnerable":["<1.9.0 >=1.7.1"]}},
+{"id":"npm:jquery:20120206","severity":"medium","semver":{"vulnerable":[">=1.7.1 <1.9.0"]}},
 {"id":"npm:jquery:20110606","severity":"medium","semver":{"vulnerable":["<1.6.3"]}}],
 
 "jquery-mobile":[
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 1f8b70a..5603092d 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
 #include "third_party/blink/renderer/core/html/html_dialog_element.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
@@ -3087,8 +3088,11 @@
     case ax::mojom::Role::kComboBoxGrouping:
     case ax::mojom::Role::kComboBoxMenuButton:
     case ax::mojom::Role::kListBox:
-    case ax::mojom::Role::kPopUpButton:
       return recursive;
+    // This can be either a button widget with a non-false value of
+    // aria-haspopup or a select element with size of 1.
+    case ax::mojom::Role::kPopUpButton:
+      return ToHTMLSelectElementOrNull(*GetNode()) ? recursive : false;
     default:
       return false;
   }
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl
index 8aa25fc..fa8072f 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl
@@ -8,7 +8,8 @@
          WebGLRenderingContext or
          WebGL2RenderingContext or
          ImageBitmapRenderingContext or
-         XRPresentationContext) RenderingContext;
+         XRPresentationContext or
+         GPUCanvasContext) RenderingContext;
 
 [
     ImplementedAs=HTMLCanvasElementModule
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_support_webgl2_compute.idl b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_support_webgl2_compute.idl
index aee0f57f..6f930b1 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_support_webgl2_compute.idl
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_support_webgl2_compute.idl
@@ -9,7 +9,8 @@
          WebGL2RenderingContext or
          WebGL2ComputeRenderingContext or
          ImageBitmapRenderingContext or
-         XRPresentationContext) RenderingContext;
+         XRPresentationContext or
+         GPUCanvasContext) RenderingContext;
 
 [
     ImplementedAs=HTMLCanvasElementModule
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad.cc b/third_party/blink/renderer/modules/gamepad/gamepad.cc
index dc4b9ee..e8c5d87 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad.cc
@@ -31,9 +31,9 @@
 
 namespace blink {
 
-Gamepad::Gamepad(NavigatorGamepad* navigator_gamepad)
+Gamepad::Gamepad(NavigatorGamepad* navigator_gamepad, unsigned index)
     : navigator_gamepad_(navigator_gamepad),
-      index_(0),
+      index_(index),
       timestamp_(0.0),
       has_vibration_actuator_(false),
       vibration_actuator_type_(device::GamepadHapticActuatorType::kDualRumble),
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad.h b/third_party/blink/renderer/modules/gamepad/gamepad.h
index 5a7ee0f6..d98a65a 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad.h
@@ -45,7 +45,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit Gamepad(NavigatorGamepad* navigator_gamepad);
+  explicit Gamepad(NavigatorGamepad* navigator_gamepad, unsigned index);
   ~Gamepad() override;
 
   typedef Vector<double> DoubleVector;
@@ -54,7 +54,6 @@
   void SetId(const String& id) { id_ = id; }
 
   unsigned index() const { return index_; }
-  void SetIndex(unsigned val) { index_ = val; }
 
   bool connected() const { return connected_; }
   void SetConnected(bool val) { connected_ = val; }
@@ -99,7 +98,7 @@
   String id_;
 
   // The index of this gamepad within the GamepadList.
-  unsigned index_;
+  const unsigned index_;
 
   // True if this gamepad was still connected when gamepad state was captured.
   bool connected_;
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_button.cc b/third_party/blink/renderer/modules/gamepad/gamepad_button.cc
index 4d9a2b5..0e45883 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_button.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_button.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/gamepad/gamepad.h"
 
+#include "device/gamepad/public/cpp/gamepad.h"
+
 namespace blink {
 
 GamepadButton::GamepadButton() : value_(0.), pressed_(false), touched_(false) {}
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_button.h b/third_party/blink/renderer/modules/gamepad/gamepad_button.h
index 35099bcb..f53621c 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_button.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_button.h
@@ -5,10 +5,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_BUTTON_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_BUTTON_H_
 
-#include "device/gamepad/public/cpp/gamepad.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+
+namespace device {
+class GamepadButton;
+}
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc b/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
index 96877dea..b747f1d 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
@@ -53,9 +53,8 @@
     device::GamepadButton buttons[1] = {{false, false, 0.0}};
     device::GamepadPose null_pose;
     auto* list = MakeGarbageCollected<GamepadList>();
-    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr, 0);
     gamepad->SetId("gamepad");
-    gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
     gamepad->SetButtons(1, buttons);
     gamepad->SetConnected(true);
@@ -69,9 +68,8 @@
     device::GamepadButton buttons[1] = {{false, false, 0.0}};
 
     auto* list = MakeGarbageCollected<GamepadList>();
-    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr, 0);
     gamepad->SetId("gamepad");
-    gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
     gamepad->SetButtons(1, buttons);
     gamepad->SetConnected(true);
@@ -84,9 +82,8 @@
     device::GamepadButton buttons[1] = {{true, true, 1.0}};
 
     auto* list = MakeGarbageCollected<GamepadList>();
-    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr, 0);
     gamepad->SetId("gamepad");
-    gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
     gamepad->SetButtons(1, buttons);
     gamepad->SetConnected(true);
@@ -104,9 +101,8 @@
     }};
 
     auto* list = MakeGarbageCollected<GamepadList>();
-    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr, 0);
     gamepad->SetId("gamepad");
-    gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
     gamepad->SetButtons(1, buttons);
     gamepad->SetConnected(true);
@@ -124,9 +120,8 @@
     }};
 
     auto* list = MakeGarbageCollected<GamepadList>();
-    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr, 0);
     gamepad->SetId("gamepad");
-    gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
     gamepad->SetButtons(1, buttons);
     gamepad->SetConnected(true);
@@ -140,9 +135,8 @@
     device::GamepadPose pose;
     InitGamepadPose(pose);
     auto* list = MakeGarbageCollected<GamepadList>();
-    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr, 0);
     gamepad->SetId("gamepad");
-    gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
     gamepad->SetButtons(1, buttons);
     gamepad->SetConnected(true);
@@ -159,9 +153,8 @@
     // Modify the linear velocity.
     pose.linear_velocity.x = 100.f;
     auto* list = MakeGarbageCollected<GamepadList>();
-    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr, 0);
     gamepad->SetId("gamepad");
-    gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
     gamepad->SetButtons(1, buttons);
     gamepad->SetConnected(true);
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.cc b/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.cc
index b8f5c6e..b02f10ae 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h"
 
+#include "device/gamepad/public/cpp/gamepads.h"
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h"
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h b/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h
index 2fe8e15..6cdbd1e 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h
@@ -5,11 +5,17 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_DISPATCHER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_DISPATCHER_H_
 
-#include "device/gamepad/public/cpp/gamepads.h"
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
 #include "device/gamepad/public/mojom/gamepad.mojom-blink.h"
 #include "third_party/blink/public/platform/web_gamepad_listener.h"
 #include "third_party/blink/renderer/core/frame/platform_event_dispatcher.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace device {
+class Gamepad;
+class Gamepads;
+}  // namespace device
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.cc b/third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.cc
index d7a955a..aaac240 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.cc
@@ -5,9 +5,13 @@
 #include "third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.h"
 
 #include "base/bind_helpers.h"
+#include "device/gamepad/public/cpp/gamepad.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h"
+#include "third_party/blink/renderer/modules/gamepad/gamepad_effect_parameters.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace {
 
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.h b/third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.h
index 32d5b42..0a15ef4 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_haptic_actuator.h
@@ -5,19 +5,20 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_HAPTIC_ACTUATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_HAPTIC_ACTUATOR_H_
 
-#include "device/gamepad/public/cpp/gamepad.h"
 #include "device/gamepad/public/mojom/gamepad.mojom-blink.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/modules/gamepad/gamepad_effect_parameters.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
 class GamepadDispatcher;
+class GamepadEffectParameters;
+enum class GamepadHapticActuatorType;
+class ScriptState;
+class ScriptPromise;
+class ScriptPromiseResolver;
 
 class GamepadHapticActuator final : public ScriptWrappable,
                                     public ContextClient {
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_pose.cc b/third_party/blink/renderer/modules/gamepad/gamepad_pose.cc
index b44ffe9..6fce407 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_pose.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_pose.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/gamepad/gamepad_pose.h"
 
+#include "device/gamepad/public/cpp/gamepad.h"
+
 namespace blink {
 
 namespace {
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_pose.h b/third_party/blink/renderer/modules/gamepad/gamepad_pose.h
index 0b4b2ef22..d7580717 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_pose.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_pose.h
@@ -5,11 +5,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_POSE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_POSE_H_
 
-#include "device/gamepad/public/cpp/gamepad.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+
+namespace device {
+class GamepadPose;
+}
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.cc b/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.cc
index b659128c9..2a595cd1 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.cc
@@ -3,9 +3,13 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h"
+
 #include "base/metrics/histogram_macros.h"
+#include "device/gamepad/public/cpp/gamepads.h"
+#include "device/gamepad/public/mojom/gamepad_hardware_buffer.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/web_gamepad_listener.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h b/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h
index 091f5ff..abfb0d7 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h
@@ -8,17 +8,24 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "device/base/synchronization/shared_memory_seqlock_buffer.h"
-#include "device/gamepad/public/cpp/gamepads.h"
 #include "device/gamepad/public/mojom/gamepad.mojom-blink.h"
 #include "device/gamepad/public/mojom/gamepad_hardware_buffer.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/buffer.h"
-#include "third_party/blink/public/platform/web_gamepad_listener.h"
+
+namespace base {
+class ReadOnlySharedMemoryRegion;
+}
+
+namespace device {
+class Gamepad;
+class Gamepads;
+}  // namespace device
 
 namespace blink {
 
 class LocalFrame;
+class WebGamepadListener;
 
 class GamepadSharedMemoryReader : public device::mojom::blink::GamepadObserver {
  public:
diff --git a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
index 16db3e3..2b825836 100644
--- a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
+++ b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
@@ -36,11 +36,13 @@
 #include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/timing/performance.h"
+#include "third_party/blink/renderer/modules/gamepad/gamepad.h"
 #include "third_party/blink/renderer/modules/gamepad/gamepad_comparisons.h"
 #include "third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h"
 #include "third_party/blink/renderer/modules/gamepad/gamepad_event.h"
 #include "third_party/blink/renderer/modules/gamepad/gamepad_list.h"
 #include "third_party/blink/renderer/modules/vr/navigator_vr.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_view.h"
 
 namespace blink {
@@ -132,8 +134,8 @@
     } else if (device_gamepad.connected) {
       Gamepad* gamepad = gamepads_back_->item(i);
       if (!gamepad)
-        gamepad = MakeGarbageCollected<Gamepad>(this);
-      SampleGamepad(i, *gamepad, device_gamepad);
+        gamepad = MakeGarbageCollected<Gamepad>(this, i);
+      SampleGamepad(device_gamepad, *gamepad);
       gamepads_back_->Set(i, gamepad);
     } else {
       gamepads_back_->Set(i, nullptr);
@@ -141,9 +143,8 @@
   }
 }
 
-void NavigatorGamepad::SampleGamepad(uint32_t index,
-                                     Gamepad& gamepad,
-                                     const device::Gamepad& device_gamepad) {
+void NavigatorGamepad::SampleGamepad(const device::Gamepad& device_gamepad,
+                                     Gamepad& gamepad) {
   bool newly_connected;
   GamepadComparisons::HasGamepadConnectionChanged(
       gamepad.connected(),                            // Old connected.
@@ -182,7 +183,6 @@
   // gamepad is newly connected.
   if (newly_connected) {
     gamepad.SetId(device_gamepad.id);
-    gamepad.SetIndex(index);
     gamepad.SetMapping(device_gamepad.mapping);
 
     if (device_gamepad.is_xr && device_gamepad.display_id) {
diff --git a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.h b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.h
index 385dcde..6422223 100644
--- a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.h
+++ b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.h
@@ -31,14 +31,19 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/navigator.h"
 #include "third_party/blink/renderer/core/frame/platform_event_controller.h"
-#include "third_party/blink/renderer/modules/gamepad/gamepad.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace device {
+class Gamepad;
+}
 
 namespace blink {
 
 class Document;
+class Gamepad;
 class GamepadDispatcher;
 class GamepadHapticActuator;
 class GamepadList;
@@ -70,9 +75,7 @@
 
  private:
   void SampleGamepads();
-  void SampleGamepad(uint32_t index,
-                     Gamepad& gamepad,
-                     const device::Gamepad& device_gamepad);
+  void SampleGamepad(const device::Gamepad& device_gamepad, Gamepad& gamepad);
 
   void DidRemoveGamepadEventListeners();
   bool StartUpdatingIfAttached();
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index 362c72f..6df2d5ceb 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -87,6 +87,7 @@
 #include "third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.h"
 #include "third_party/blink/renderer/modules/webgl/webgl2_rendering_context.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_rendering_context.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h"
 #include "third_party/blink/renderer/modules/xr/xr_presentation_context.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
@@ -140,6 +141,8 @@
       std::make_unique<ImageBitmapRenderingContext::Factory>());
   HTMLCanvasElement::RegisterRenderingContextFactory(
       std::make_unique<XRPresentationContext::Factory>());
+  HTMLCanvasElement::RegisterRenderingContextFactory(
+      std::make_unique<GPUCanvasContext::Factory>());
 
   // OffscreenCanvas context types must be registered with the OffscreenCanvas.
   OffscreenCanvas::RegisterRenderingContextFactory(
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
index f1c82d15b..c9fa73f4 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
@@ -4,4 +4,37 @@
 
 #include "third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h"
 
-namespace blink {}  // namespace blink
+#include "third_party/blink/renderer/bindings/modules/v8/rendering_context.h"
+
+namespace blink {
+
+GPUCanvasContext::Factory::Factory() {}
+GPUCanvasContext::Factory::~Factory() {}
+
+CanvasRenderingContext* GPUCanvasContext::Factory::Create(
+    CanvasRenderingContextHost* host,
+    const CanvasContextCreationAttributesCore& attrs) {
+  return MakeGarbageCollected<GPUCanvasContext>(host, attrs);
+}
+
+CanvasRenderingContext::ContextType GPUCanvasContext::Factory::GetContextType()
+    const {
+  return CanvasRenderingContext::kContextGPUPresent;
+}
+
+GPUCanvasContext::GPUCanvasContext(
+    CanvasRenderingContextHost* host,
+    const CanvasContextCreationAttributesCore& attrs)
+    : CanvasRenderingContext(host, attrs) {}
+
+GPUCanvasContext::~GPUCanvasContext() {}
+
+CanvasRenderingContext::ContextType GPUCanvasContext::GetContextType() const {
+  return CanvasRenderingContext::kContextGPUPresent;
+}
+
+void GPUCanvasContext::SetCanvasGetContextResult(RenderingContext& result) {
+  result.SetGPUCanvasContext(this);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
index 3f1a42e9..aa4b69d 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
@@ -5,14 +5,55 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_CANVAS_CONTEXT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_CANVAS_CONTEXT_H_
 
+#include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
+#include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 
 namespace blink {
 
-class GPUCanvasContext : public ScriptWrappable {
+// A GPUCanvasContext does little by itself and basically just binds a canvas
+// and a GPUSwapChain together and forwards calls from one to the other. The
+// logic that are in other CanvasRenderingContext is in GPUSwapChain instead.
+class GPUCanvasContext : public CanvasRenderingContext {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  class Factory : public CanvasRenderingContextFactory {
+    DISALLOW_COPY_AND_ASSIGN(Factory);
+
+   public:
+    Factory();
+    ~Factory() override;
+
+    CanvasRenderingContext* Create(
+        CanvasRenderingContextHost*,
+        const CanvasContextCreationAttributesCore&) override;
+    CanvasRenderingContext::ContextType GetContextType() const override;
+  };
+
+  GPUCanvasContext(CanvasRenderingContextHost*,
+                   const CanvasContextCreationAttributesCore&);
+  ~GPUCanvasContext() override;
+
+  // CanvasRenderingContext implementation
+  ContextType GetContextType() const override;
+  void SetCanvasGetContextResult(RenderingContext&) final;
+  scoped_refptr<StaticBitmapImage> GetImage(AccelerationHint) const final {
+    return nullptr;
+  }
+  void SetIsHidden(bool) override {}
+  bool isContextLost() const override { return false; }
+  bool IsComposited() const final { return true; }
+  bool IsAccelerated() const final { return true; }
+  bool IsOriginTopLeft() const final { return true; }
+  bool Is3d() const final { return true; }
+  void SetFilterQuality(SkFilterQuality) final {}
+  bool IsPaintable() const final { return true; }
+  int ExternallyAllocatedBufferCountPerPixel() final { return 1; }
+  void Stop() override {}
+  cc::Layer* CcLayer() const final { return nullptr; }
+
   // gpu_canvas_context.idl
   // TODO(crbug.com/877147): implement GPUCanvasContext.
 
@@ -20,6 +61,14 @@
   DISALLOW_COPY_AND_ASSIGN(GPUCanvasContext);
 };
 
+DEFINE_TYPE_CASTS(GPUCanvasContext,
+                  CanvasRenderingContext,
+                  context,
+                  context->GetContextType() ==
+                      CanvasRenderingContext::kContextGPUPresent,
+                  context.GetContextType() ==
+                      CanvasRenderingContext::kContextGPUPresent);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_CANVAS_CONTEXT_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
index 8bfcba0..4cde7cd6 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -227,12 +227,10 @@
     // When mirroring make sure to also update the mirrored canvas UVs so it
     // only shows a single eye's data, cropped to display proportionally.
     if (session()->outputContext()) {
-      float left = 0;
-      float top = 0;
-      float right = static_cast<float>(left_viewport_->width()) /
-                    static_cast<float>(framebuffer_width);
-      float bottom = static_cast<float>(left_viewport_->height()) /
-                     static_cast<float>(framebuffer_height);
+      float source_pixels_left = left_viewport_->x();
+      float source_pixels_right = left_viewport_->x() + left_viewport_->width();
+      float source_pixels_bottom = left_viewport_->y();
+      float source_pixels_top = left_viewport_->y() + left_viewport_->height();
 
       // Adjust the UVs so that the mirrored content always fills the canvas
       // and is centered while staying proportional.
@@ -242,23 +240,32 @@
                                static_cast<float>(left_viewport_->height());
 
       if (output_aspect > viewport_aspect) {
-        float viewport_scale = bottom;
-        output_aspect = viewport_aspect / output_aspect;
-        top = 0.5 - (output_aspect * 0.5);
-        bottom = top + output_aspect;
-        top *= viewport_scale;
-        bottom *= viewport_scale;
+        // Output is wider than rendered image, scale to height and chop off top
+        // and bottom.
+        float cropped_image_height = left_viewport_->width() / output_aspect;
+        float crop_amount = (left_viewport_->height() - cropped_image_height);
+        source_pixels_top -= crop_amount / 2;
+        source_pixels_bottom += crop_amount / 2;
+
       } else {
-        float viewport_scale = right;
-        output_aspect = output_aspect / viewport_aspect;
-        left = 0.5 - (output_aspect * 0.5);
-        right = left + output_aspect;
-        left *= viewport_scale;
-        right *= viewport_scale;
+        // Output is taller relatively than rendered image, scale to width and
+        // chop of left and right.
+        float cropped_image_width = left_viewport_->height() * output_aspect;
+        float crop_amount = (left_viewport_->width() - cropped_image_width);
+        source_pixels_left += crop_amount / 2;
+        source_pixels_right -= crop_amount / 2;
       }
 
-      session()->outputContext()->SetUV(FloatPoint(left, top),
-                                        FloatPoint(right, bottom));
+      float uv_left = source_pixels_left / framebuffer_width;
+      float uv_right = source_pixels_right / framebuffer_width;
+      float uv_top = source_pixels_top / framebuffer_height;
+      float uv_bottom = source_pixels_bottom / framebuffer_height;
+
+      // Finally, in UV space (0, 0) is top-left corner, so we need to flip.
+      uv_top = 1 - uv_top;
+      uv_bottom = 1 - uv_bottom;
+      session()->outputContext()->SetUV(FloatPoint(uv_left, uv_top),
+                                        FloatPoint(uv_right, uv_bottom));
     }
   } else {
     left_viewport_ = MakeGarbageCollected<XRViewport>(
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index bd96d88..2805c25 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -1036,6 +1036,8 @@
       blink_effects[effect.id] &&
       blink_effects[effect.id]->HasActiveOpacityAnimation())
     return true;
+  if (effect.is_fast_rounded_corner)
+    return true;
   return false;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 8d9a9f5..818c388 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -504,6 +504,9 @@
 }
 
 void PropertyTreeManager::EmitClipMaskLayer() {
+  if (RuntimeEnabledFeatures::FastBorderRadiusEnabled())
+    return;
+
   cc::EffectNode& mask_isolation = *GetEffectTree().Node(current_.effect_id);
   if (pending_synthetic_mask_layers_.Contains(mask_isolation.id))
     return;
@@ -755,8 +758,15 @@
     const auto& transform = pending_clip.clip->LocalTransformSpace();
     synthetic_effect.transform_id = EnsureCompositorTransformNode(transform);
     synthetic_effect.double_sided = !transform.IsBackfaceHidden();
-    synthetic_effect.has_render_surface = true;
-    pending_synthetic_mask_layers_.insert(synthetic_effect.id);
+
+    if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) {
+      synthetic_effect.rounded_corner_bounds =
+          gfx::RRectF(pending_clip.clip->ClipRect());
+      synthetic_effect.is_fast_rounded_corner = true;
+    } else {
+      synthetic_effect.has_render_surface = true;
+      pending_synthetic_mask_layers_.insert(synthetic_effect.id);
+    }
 
     // Clip and kDstIn do not commute. This shall never be reached because
     // kDstIn is only used internally to implement CSS clip-path and mask,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 1880fa5..b8a2018 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -525,6 +525,9 @@
       name: "FallbackCursorMode",
     },
     {
+      name: "FastBorderRadius",
+    },
+    {
       name: "FastFlatTreeTraversal",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/transforms/transformation_matrix.cc b/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
index 1bea01b..ea51d53 100644
--- a/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
+++ b/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
@@ -2080,26 +2080,11 @@
 
 SkMatrix44 TransformationMatrix::ToSkMatrix44(
     const TransformationMatrix& matrix) {
-  // TODO(masonfreed): Replace this with an explicit 16-element constructor
-  // on SkMatrix44, once that's available. This code does a *lot* of extra
-  // work, because each call to setDouble re-calculates the matrix type.
-  SkMatrix44 ret(SkMatrix44::kIdentity_Constructor);
-  ret.setDouble(0, 0, matrix.M11());
-  ret.setDouble(0, 1, matrix.M21());
-  ret.setDouble(0, 2, matrix.M31());
-  ret.setDouble(0, 3, matrix.M41());
-  ret.setDouble(1, 0, matrix.M12());
-  ret.setDouble(1, 1, matrix.M22());
-  ret.setDouble(1, 2, matrix.M32());
-  ret.setDouble(1, 3, matrix.M42());
-  ret.setDouble(2, 0, matrix.M13());
-  ret.setDouble(2, 1, matrix.M23());
-  ret.setDouble(2, 2, matrix.M33());
-  ret.setDouble(2, 3, matrix.M43());
-  ret.setDouble(3, 0, matrix.M14());
-  ret.setDouble(3, 1, matrix.M24());
-  ret.setDouble(3, 2, matrix.M34());
-  ret.setDouble(3, 3, matrix.M44());
+  SkMatrix44 ret(SkMatrix44::kUninitialized_Constructor);
+  ret.set4x4(matrix.M11(), matrix.M12(), matrix.M13(), matrix.M14(),
+             matrix.M21(), matrix.M22(), matrix.M23(), matrix.M24(),
+             matrix.M31(), matrix.M32(), matrix.M33(), matrix.M34(),
+             matrix.M41(), matrix.M42(), matrix.M43(), matrix.M44());
   return ret;
 }
 
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-unsafe-webgpu b/third_party/blink/web_tests/FlagExpectations/enable-unsafe-webgpu
index 85b70dd..55a55dd0 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-unsafe-webgpu
+++ b/third_party/blink/web_tests/FlagExpectations/enable-unsafe-webgpu
@@ -1,2 +1,3 @@
 # WebGPU tests are only run with --enable-unsafe-webgpu
+Bug(none) webgpu/canvas_context.html [ Pass ]
 Bug(none) webgpu/fence.html [ Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 09b6e08..888a01d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -12,6 +12,7 @@
 Bug(intentional) external/wpt/infrastructure/expected-fail/timeout.html [ Timeout ]
 
 # WebGPU tests are only run with --enable-unsafe-webgpu
+Bug(none) webgpu/canvas_context.html [ Skip ]
 Bug(none) webgpu/fence.html [ Skip ]
 
 crbug.com/807686 crbug.com/24182 jquery/manipulation.html [ Timeout Pass ]
@@ -92,6 +93,7 @@
 crbug.com/678482 virtual/nobinary-for-devtools/http/tests/devtools/debugger/fetch-breakpoints.js [ Timeout Pass ]
 crbug.com/678491 http/tests/misc/webtiming-no-origin.html [ Crash Pass ]
 crbug.com/765779 http/tests/loading/bad-server-subframe.html [ Failure ]
+crbug.com/793127 external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html [ Crash ]
 crbug.com/793127 http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Crash ]
 crbug.com/793127 virtual/outofblink-cors/http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Crash ]
 crbug.com/801992 http/tests/misc/iframe-script-modify-attr.html [ Pass Crash ]
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/table/invisible-col-visible-td-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/table/invisible-col-visible-td-expected.txt
index f7877fc..4bd25cb 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/table/invisible-col-visible-td-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/table/invisible-col-visible-td-expected.txt
@@ -8,7 +8,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutTableSection TBODY",
-          "rect": [8, 8, 200, 200],
+          "rect": [9, 9, 198, 198],
           "reason": "style change"
         }
       ]
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/table/invisible-tbody-visible-td-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/table/invisible-tbody-visible-td-expected.txt
index 4ee94a75..1db1e66 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/table/invisible-tbody-visible-td-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/table/invisible-tbody-visible-td-expected.txt
@@ -8,7 +8,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutTableSection TBODY id='tbody'",
-          "rect": [8, 8, 200, 200],
+          "rect": [9, 9, 198, 198],
           "reason": "background"
         }
       ]
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits2/audits2-successful-run-expected.txt b/third_party/blink/web_tests/http/tests/devtools/audits2/audits2-successful-run-expected.txt
index f293516..184a3e8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/audits2/audits2-successful-run-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/audits2/audits2-successful-run-expected.txt
@@ -22,7 +22,6 @@
 Benchmarking machine
 Initializing…
 Running beforePass methods
-Retrieving setup: Scripts
 Retrieving setup: CSSUsage
 Retrieving setup: ViewportDimensions
 Retrieving setup: RuntimeExceptions
@@ -32,6 +31,7 @@
 Retrieving setup: ImageElements
 Retrieving setup: LinkElements
 Retrieving setup: MetaElements
+Retrieving setup: ScriptElements
 Retrieving setup: AppCacheManifest
 Retrieving setup: Doctype
 Retrieving setup: DOMStats
@@ -41,15 +41,12 @@
 Retrieving setup: ResponseCompression
 Retrieving setup: TagsBlockingFirstPaint
 Retrieving setup: FontSize
-Retrieving setup: Hreflang
 Retrieving setup: EmbeddedContent
-Retrieving setup: Canonical
 Retrieving setup: RobotsTxt
 Retrieving setup: TapTargets
 Loading page & waiting for onload
 Getting browser version
 Running pass methods
-Retrieving in-page: Scripts
 Retrieving in-page: CSSUsage
 Retrieving in-page: ViewportDimensions
 Retrieving in-page: RuntimeExceptions
@@ -59,6 +56,7 @@
 Retrieving in-page: ImageElements
 Retrieving in-page: LinkElements
 Retrieving in-page: MetaElements
+Retrieving in-page: ScriptElements
 Retrieving in-page: AppCacheManifest
 Retrieving in-page: Doctype
 Retrieving in-page: DOMStats
@@ -68,15 +66,12 @@
 Retrieving in-page: ResponseCompression
 Retrieving in-page: TagsBlockingFirstPaint
 Retrieving in-page: FontSize
-Retrieving in-page: Hreflang
 Retrieving in-page: EmbeddedContent
-Retrieving in-page: Canonical
 Retrieving in-page: RobotsTxt
 Retrieving in-page: TapTargets
 Retrieving trace
 Retrieving devtoolsLog & network records
 Running afterPass methods
-Retrieving: Scripts
 Retrieving: CSSUsage
 Retrieving: ViewportDimensions
 Retrieving: RuntimeExceptions
@@ -86,6 +81,7 @@
 Retrieving: ImageElements
 Retrieving: LinkElements
 Retrieving: MetaElements
+Retrieving: ScriptElements
 Retrieving: AppCacheManifest
 Retrieving: Doctype
 Retrieving: DOMStats
@@ -95,9 +91,7 @@
 Retrieving: ResponseCompression
 Retrieving: TagsBlockingFirstPaint
 Retrieving: FontSize
-Retrieving: Hreflang
 Retrieving: EmbeddedContent
-Retrieving: Canonical
 Retrieving: RobotsTxt
 Retrieving: TapTargets
 Resetting state with about:blank
@@ -277,7 +271,7 @@
 
 =============== Lighthouse Results ===============
 URL: http://127.0.0.1:8000/devtools/resources/inspected-page.html
-Version: 4.2.0
+Version: 4.3.0
 
 
 accesskeys: notApplicable
diff --git a/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-before-element-expected.txt b/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-before-element-expected.txt
new file mode 100644
index 0000000..e8b0a05
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-before-element-expected.txt
@@ -0,0 +1,4 @@
+Tests DOM.getContentQuads method with single before element.
+Returned quads amount: 1
+window.CLICKED =  42
+
diff --git a/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-before-element.js b/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-before-element.js
new file mode 100644
index 0000000..ca1fc46a
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-before-element.js
@@ -0,0 +1,48 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startHTML(`
+    <style>
+    span::before {
+      content: '\e003';
+    }
+    </style>
+    <span onclick='javascript:window.CLICKED = 42;'></span>
+  `, 'Tests DOM.getContentQuads method with single before element.');
+
+  const document = (await dp.DOM.getDocument()).result.root;
+  const node = (await dp.DOM.querySelector({nodeId: document.nodeId, selector: 'span'})).result;
+  const quads = (await dp.DOM.getContentQuads({nodeId: node.nodeId})).result.quads;
+  testRunner.log('Returned quads amount: ' + quads.length);
+  const center = middlePoint(quads[0]);
+  await dp.Input.dispatchMouseEvent({
+    type: 'mousePressed',
+    button: 'left',
+    buttons: 1,
+    clickCount: 1,
+    x: center.x,
+    y: center.y,
+  });
+  await dp.Input.dispatchMouseEvent({
+    type: 'mouseReleased',
+    button: 'left',
+    buttons: 1,
+    clickCount: 1,
+    x: center.x,
+    y: center.y,
+  });
+  testRunner.log('window.CLICKED =  ' + (await session.evaluate(`window.CLICKED`)));
+
+  testRunner.completeTest();
+
+  function middlePoint(quad) {
+    let x = 0, y = 0;
+    for (let i = 0; i < 8; i += 2) {
+      x += quad[i];
+      y += quad[i + 1];
+    }
+    return {
+      x: Math.round(x / 4),
+      y: Math.round(y / 4)
+    };
+  }
+})
+
diff --git a/third_party/blink/web_tests/paint/invalidation/table/invisible-col-visible-td-expected.txt b/third_party/blink/web_tests/paint/invalidation/table/invisible-col-visible-td-expected.txt
index cd3d1ca..0f96792 100644
--- a/third_party/blink/web_tests/paint/invalidation/table/invisible-col-visible-td-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/table/invisible-col-visible-td-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutTableSection TBODY",
-          "rect": [8, 8, 200, 200],
+          "rect": [9, 9, 198, 198],
           "reason": "style change"
         }
       ]
diff --git a/third_party/blink/web_tests/paint/invalidation/table/invisible-tbody-visible-td-expected.txt b/third_party/blink/web_tests/paint/invalidation/table/invisible-tbody-visible-td-expected.txt
index ebdbe091..537612d 100644
--- a/third_party/blink/web_tests/paint/invalidation/table/invisible-tbody-visible-td-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/table/invisible-tbody-visible-td-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutTableSection TBODY id='tbody'",
-          "rect": [8, 8, 200, 200],
+          "rect": [9, 9, 198, 198],
           "reason": "background"
         }
       ]
diff --git a/third_party/blink/web_tests/webgpu/canvas_context.html b/third_party/blink/web_tests/webgpu/canvas_context.html
new file mode 100644
index 0000000..7075ea1
--- /dev/null
+++ b/third_party/blink/web_tests/webgpu/canvas_context.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+let width = 10;
+let height = 10;
+
+test(function() {
+  let canvas = document.createElement("canvas");
+  canvas.width = width;
+  canvas.height = height;
+
+  let ctx = canvas.getContext('gpupresent');
+  assert_true(ctx instanceof GPUCanvasContext);
+}, "Test that canvas.getContext('gpupresent') returns an instance of GPUCanvasContext");
+
+</script>
diff --git a/third_party/leveldatabase/README.chromium b/third_party/leveldatabase/README.chromium
index c948db4..4355952 100644
--- a/third_party/leveldatabase/README.chromium
+++ b/third_party/leveldatabase/README.chromium
@@ -1,7 +1,7 @@
 Name: LevelDB: A Fast Persistent Key-Value Store
 Short Name: leveldb
 URL: https://github.com/google/leveldb.git
-Version: 1.20.git.7035af5fc36657447054617759854a726d31dbe0
+Version: 1.21.git.ffabb1ae86cc4eb4516a7c0824c878c3b2d19e5d
 License: New BSD
 License File: src/LICENSE
 Security Critical: yes
diff --git a/third_party/sqlite/amalgamation/sqlite3.c b/third_party/sqlite/amalgamation/sqlite3.c
index a1529a6f..a33c21a 100644
--- a/third_party/sqlite/amalgamation/sqlite3.c
+++ b/third_party/sqlite/amalgamation/sqlite3.c
@@ -69202,7 +69202,7 @@
   MemPage *pPage1 = pBt->pPage1;      /* Local reference to page 1 */
   MemPage *pPage;                     /* Page being freed. May be NULL. */
   int rc;                             /* Return Code */
-  int nFree;                          /* Initial number of pages on free-list */
+  u32 nFree;                          /* Initial number of pages on free-list */
 
   assert( sqlite3_mutex_held(pBt->mutex) );
   assert( CORRUPT_DB || iPage>1 );
@@ -118449,6 +118449,9 @@
     if( pSrcIdx==0 ){
       return 0;    /* pDestIdx has no corresponding index in pSrc */
     }
+    if( pSrcIdx->tnum==pDestIdx->tnum && pSrc->pSchema==pDest->pSchema ){
+      return 0;   /* Corrupt schema - two indexes on the same btree */
+    }
   }
 #ifndef SQLITE_OMIT_CHECK
   if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
@@ -221431,7 +221434,7 @@
 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
 
 /************** End of stmt.c ************************************************/
-#if __LINE__!=221434
+#if __LINE__!=221437
 #undef SQLITE_SOURCE_ID
 #define SQLITE_SOURCE_ID      "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0alt2"
 #endif
diff --git a/third_party/sqlite/patched/src/btree.c b/third_party/sqlite/patched/src/btree.c
index c528bb1..269cd419 100644
--- a/third_party/sqlite/patched/src/btree.c
+++ b/third_party/sqlite/patched/src/btree.c
@@ -6142,7 +6142,7 @@
   MemPage *pPage1 = pBt->pPage1;      /* Local reference to page 1 */
   MemPage *pPage;                     /* Page being freed. May be NULL. */
   int rc;                             /* Return Code */
-  int nFree;                          /* Initial number of pages on free-list */
+  u32 nFree;                          /* Initial number of pages on free-list */
 
   assert( sqlite3_mutex_held(pBt->mutex) );
   assert( CORRUPT_DB || iPage>1 );
diff --git a/third_party/sqlite/patched/src/insert.c b/third_party/sqlite/patched/src/insert.c
index 624ce298..e0007af 100644
--- a/third_party/sqlite/patched/src/insert.c
+++ b/third_party/sqlite/patched/src/insert.c
@@ -2273,6 +2273,9 @@
     if( pSrcIdx==0 ){
       return 0;    /* pDestIdx has no corresponding index in pSrc */
     }
+    if( pSrcIdx->tnum==pDestIdx->tnum && pSrc->pSchema==pDest->pSchema ){
+      return 0;   /* Corrupt schema - two indexes on the same btree */
+    }
   }
 #ifndef SQLITE_OMIT_CHECK
   if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
diff --git a/third_party/sqlite/patches/0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch b/third_party/sqlite/patches/0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch
index c5c57d4..087fc90 100644
--- a/third_party/sqlite/patches/0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch
+++ b/third_party/sqlite/patches/0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Scott Hess <shess@chromium.org>
 Date: Sat, 20 Jul 2013 11:42:21 -0700
-Subject: [PATCH 01/11] Virtual table supporting recovery of corrupted
+Subject: [PATCH 01/13] Virtual table supporting recovery of corrupted
  databases.
 
 "recover" implements a virtual table which uses the SQLite pager layer
@@ -3901,5 +3901,5 @@
 +
 +finish_test
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0002-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch b/third_party/sqlite/patches/0002-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch
index 38b19e1..f62a60c 100644
--- a/third_party/sqlite/patches/0002-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch
+++ b/third_party/sqlite/patches/0002-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: "tc@google.com" <tc@google.com>
 Date: Tue, 6 Jan 2009 22:39:41 +0000
-Subject: [PATCH 02/11] Custom shell.c helpers to load Chromium's ICU data.
+Subject: [PATCH 02/13] Custom shell.c helpers to load Chromium's ICU data.
 
 History uses fts3 with an icu-based segmenter.  These changes allow building a
 sqlite3 binary for Linux or Windows which can read those files.
@@ -141,5 +141,5 @@
 +  return 1;
 +}
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0003-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch b/third_party/sqlite/patches/0003-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch
index 6cd9acc..8253553 100644
--- a/third_party/sqlite/patches/0003-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch
+++ b/third_party/sqlite/patches/0003-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sun, 10 Feb 2019 13:12:57 -0800
-Subject: [PATCH 03/11] Fix compilation with SQLITE_OMIT_WINDOWFUNC.
+Subject: [PATCH 03/13] Fix compilation with SQLITE_OMIT_WINDOWFUNC.
 
 ---
  third_party/sqlite/patched/src/resolve.c | 2 ++
@@ -28,5 +28,5 @@
      /* If this is part of a compound SELECT, check that it has the right
      ** number of expressions in the select list. */
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0004-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch b/third_party/sqlite/patches/0004-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch
index d2ee401..274d62e 100644
--- a/third_party/sqlite/patches/0004-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch
+++ b/third_party/sqlite/patches/0004-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sun, 10 Feb 2019 15:18:43 -0800
-Subject: [PATCH 04/11] Fix dbfuzz2.c compilation errors on Windows.
+Subject: [PATCH 04/13] Fix dbfuzz2.c compilation errors on Windows.
 
 ---
  third_party/sqlite/patched/test/dbfuzz2.c | 4 ++++
@@ -39,5 +39,5 @@
      argv[j++] = argv[i];
    }
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0005-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch b/third_party/sqlite/patches/0005-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch
index f10b0df..4de7809 100644
--- a/third_party/sqlite/patches/0005-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch
+++ b/third_party/sqlite/patches/0005-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Darwin Huang <huangdarwin@chromium.org>
 Date: Tue, 5 Mar 2019 13:49:51 -0800
-Subject: [PATCH 05/11] Fix Heap-buffer-overflow in vdbeRecordCompareInt
+Subject: [PATCH 05/13] Fix Heap-buffer-overflow in vdbeRecordCompareInt
 
 This backports https://www.sqlite.org/src/info/c1ac00706bae45fe
 
@@ -24,5 +24,5 @@
          }
          assert(
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0006-fix-heap-buffer-overflow-in-cellsizeptr.patch b/third_party/sqlite/patches/0006-fix-heap-buffer-overflow-in-cellsizeptr.patch
index b265b86..bf065ee 100644
--- a/third_party/sqlite/patches/0006-fix-heap-buffer-overflow-in-cellsizeptr.patch
+++ b/third_party/sqlite/patches/0006-fix-heap-buffer-overflow-in-cellsizeptr.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Darwin Huang <huangdarwin@chromium.org>
 Date: Tue, 5 Mar 2019 14:13:19 -0800
-Subject: [PATCH 06/11] fix heap-buffer-overflow in cellsizeptr
+Subject: [PATCH 06/13] fix heap-buffer-overflow in cellsizeptr
 
 This backports https://www.sqlite.org/src/info/e7aca0714bc475e0
 
@@ -32,5 +32,5 @@
  
      if( rc==SQLITE_OK ){
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0007-fix-integer-overflow-in-checkList.patch b/third_party/sqlite/patches/0007-fix-integer-overflow-in-checkList.patch
index 2c67373..bf12915 100644
--- a/third_party/sqlite/patches/0007-fix-integer-overflow-in-checkList.patch
+++ b/third_party/sqlite/patches/0007-fix-integer-overflow-in-checkList.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Darwin Huang <huangdarwin@chromium.org>
 Date: Tue, 5 Mar 2019 14:17:05 -0800
-Subject: [PATCH 07/11] fix integer overflow in checkList
+Subject: [PATCH 07/13] fix integer overflow in checkList
 
 This backports https://www.sqlite.org/src/info/05b87e0755638d31
 
@@ -37,5 +37,5 @@
        assert( pc + info.nSize - 4 <= usableSize );
        nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4);
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0008-Fix-Heap-use-after-free-in-releasePageNotNull.patch b/third_party/sqlite/patches/0008-Fix-Heap-use-after-free-in-releasePageNotNull.patch
index cce0e58..f06de28 100644
--- a/third_party/sqlite/patches/0008-Fix-Heap-use-after-free-in-releasePageNotNull.patch
+++ b/third_party/sqlite/patches/0008-Fix-Heap-use-after-free-in-releasePageNotNull.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Darwin Huang <huangdarwin@chromium.org>
 Date: Tue, 12 Mar 2019 17:30:33 -0700
-Subject: [PATCH 08/11] Fix Heap-use-after-free in releasePageNotNull
+Subject: [PATCH 08/13] Fix Heap-use-after-free in releasePageNotNull
 
 This backports https://www.sqlite.org/src/info/b0d5cf40bba34e45
 
@@ -29,5 +29,5 @@
      if( pPager->tempFile ){
        /* Do not discard pages from an in-memory database since we might
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0009-Fix-dangling-pointer-dereference.patch b/third_party/sqlite/patches/0009-Fix-dangling-pointer-dereference.patch
index d9b34b8..cc5d07a 100644
--- a/third_party/sqlite/patches/0009-Fix-dangling-pointer-dereference.patch
+++ b/third_party/sqlite/patches/0009-Fix-dangling-pointer-dereference.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Darwin Huang <huangdarwin@chromium.org>
 Date: Thu, 21 Mar 2019 13:19:11 -0700
-Subject: [PATCH 09/11] Fix dangling pointer dereference
+Subject: [PATCH 09/13] Fix dangling pointer dereference
 
 This backports https://www.sqlite.org/src/info/b9e2393cf201e3fc
 
@@ -62,5 +62,5 @@
  finish_test
  
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0010-Fix-faulty-assert-statement.patch b/third_party/sqlite/patches/0010-Fix-faulty-assert-statement.patch
index a0c2235..bbfe184 100644
--- a/third_party/sqlite/patches/0010-Fix-faulty-assert-statement.patch
+++ b/third_party/sqlite/patches/0010-Fix-faulty-assert-statement.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Darwin Huang <huangdarwin@chromium.org>
 Date: Wed, 27 Mar 2019 12:05:31 -0700
-Subject: [PATCH 10/11] Fix faulty assert statement
+Subject: [PATCH 10/13] Fix faulty assert statement
 
 This backports https://www.sqlite.org/src/info/bcbe7d96df3c9515
 
@@ -25,5 +25,5 @@
      );
      copyNodeContent(apNew[0], pParent, &rc);
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0011-Add-dbfuzz2-progress-handler-patch.patch b/third_party/sqlite/patches/0011-Add-dbfuzz2-progress-handler-patch.patch
index 32fb274..02f8b620 100644
--- a/third_party/sqlite/patches/0011-Add-dbfuzz2-progress-handler-patch.patch
+++ b/third_party/sqlite/patches/0011-Add-dbfuzz2-progress-handler-patch.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Darwin Huang <huangdarwin@chromium.org>
 Date: Wed, 27 Mar 2019 12:10:17 -0700
-Subject: [PATCH 11/11] Add dbfuzz2 progress handler patch
+Subject: [PATCH 11/13] Add dbfuzz2 progress handler patch
 
 This backports https://www.sqlite.org/src/info/b99f8512c06b9d47
 
@@ -80,5 +80,5 @@
          sqlite3MemTraceActivate(stdout);
          continue;
 -- 
-2.20.1
+2.21.0.392.gf8f6787159e-goog
 
diff --git a/third_party/sqlite/patches/0012-Use-fixed-width-integer-type.patch b/third_party/sqlite/patches/0012-Use-fixed-width-integer-type.patch
new file mode 100644
index 0000000..9b6c97e0
--- /dev/null
+++ b/third_party/sqlite/patches/0012-Use-fixed-width-integer-type.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Darwin Huang <huangdarwin@chromium.org>
+Date: Fri, 12 Apr 2019 18:07:16 -0700
+Subject: [PATCH 12/13] Use fixed-width integer type
+
+This backports https://www.sqlite.org/src/info/8820408597341344
+
+Bug: 950296
+---
+ third_party/sqlite/patched/src/btree.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/third_party/sqlite/patched/src/btree.c b/third_party/sqlite/patched/src/btree.c
+index c528bb1815fd..269cd4198d36 100644
+--- a/third_party/sqlite/patched/src/btree.c
++++ b/third_party/sqlite/patched/src/btree.c
+@@ -6142,7 +6142,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
+   MemPage *pPage1 = pBt->pPage1;      /* Local reference to page 1 */
+   MemPage *pPage;                     /* Page being freed. May be NULL. */
+   int rc;                             /* Return Code */
+-  int nFree;                          /* Initial number of pages on free-list */
++  u32 nFree;                          /* Initial number of pages on free-list */
+ 
+   assert( sqlite3_mutex_held(pBt->mutex) );
+   assert( CORRUPT_DB || iPage>1 );
+-- 
+2.21.0.392.gf8f6787159e-goog
+
diff --git a/third_party/sqlite/patches/0013-Do-early-detection-for-corrupt-schema.patch b/third_party/sqlite/patches/0013-Do-early-detection-for-corrupt-schema.patch
new file mode 100644
index 0000000..dc03fc4
--- /dev/null
+++ b/third_party/sqlite/patches/0013-Do-early-detection-for-corrupt-schema.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Darwin Huang <huangdarwin@chromium.org>
+Date: Fri, 12 Apr 2019 18:09:06 -0700
+Subject: [PATCH 13/13] Do early detection for corrupt schema
+
+This backports https://www.sqlite.org/src/info/af1e5873d44d3146
+
+Bug: 949453
+---
+ third_party/sqlite/patched/src/insert.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/third_party/sqlite/patched/src/insert.c b/third_party/sqlite/patched/src/insert.c
+index 624ce29870f3..e0007afa15d7 100644
+--- a/third_party/sqlite/patched/src/insert.c
++++ b/third_party/sqlite/patched/src/insert.c
+@@ -2273,6 +2273,9 @@ static int xferOptimization(
+     if( pSrcIdx==0 ){
+       return 0;    /* pDestIdx has no corresponding index in pSrc */
+     }
++    if( pSrcIdx->tnum==pDestIdx->tnum && pSrc->pSchema==pDest->pSchema ){
++      return 0;   /* Corrupt schema - two indexes on the same btree */
++    }
+   }
+ #ifndef SQLITE_OMIT_CHECK
+   if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
+-- 
+2.21.0.392.gf8f6787159e-goog
+
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2df779b..fd78724 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -11879,7 +11879,7 @@
 </histogram>
 
 <histogram name="Blink.Fonts.WindowsUniqueLocalFontInstantiationResult"
-    enum="WindowsUniqueLocalFontInstantiationResult" expires_after="M75">
+    enum="WindowsUniqueLocalFontInstantiationResult" expires_after="M78">
   <owner>drott@chromium.org</owner>
   <summary>
     Tracks whether a locally uniquely matched font can be instantiated inside
@@ -102681,6 +102681,20 @@
 </histogram>
 
 <histogram
+    name="ResourceScheduler.NonDelayableLastEndToNonDelayableStart.NonDelayableNotInFlight"
+    units="ms" expires_after="M77">
+  <owner>tbansal@chromium.org</owner>
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    The duration of time between the end of a non-delayable resource request to
+    the start of the next non-delayable resource request. Recorded at the start
+    of all non-delayable requests that are preceeded by at least one completed
+    non-delayable request in the same resource scheduler client. Recorded only
+    when a non-delayable request is NOT in-flight.
+  </summary>
+</histogram>
+
+<histogram
     name="ResourceScheduler.NonDelayableLastStartOrEndToNonDelayableStart"
     units="ms" expires_after="M77">
   <owner>tbansal@chromium.org</owner>
@@ -102707,6 +102721,20 @@
 </histogram>
 
 <histogram
+    name="ResourceScheduler.NonDelayableLastStartToNonDelayableStart.NonDelayableInFlight"
+    units="ms" expires_after="M77">
+  <owner>tbansal@chromium.org</owner>
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    The duration of time between the start of a non-delayable resource request
+    to the start of the next non-delayable resource request. Recorded at the
+    start of all non-delayable requests that are preceeded by at least one
+    started non-delayable request in the same resource scheduler client.
+    Recorded only when a delayable request is in-flight.
+  </summary>
+</histogram>
+
+<histogram
     name="ResourceScheduler.NumDelayableRequestsInFlightAtStart.NonDelayable"
     units="requests">
   <owner>tbansal@chromium.org</owner>
diff --git a/ui/accessibility/platform/atk_util_auralinux.cc b/ui/accessibility/platform/atk_util_auralinux.cc
index 945bc2c..bc83396 100644
--- a/ui/accessibility/platform/atk_util_auralinux.cc
+++ b/ui/accessibility/platform/atk_util_auralinux.cc
@@ -4,6 +4,8 @@
 
 #include <atk/atk.h>
 #include <map>
+#include <memory>
+#include <string>
 #include <utility>
 
 #include "base/environment.h"
@@ -45,13 +47,11 @@
 typedef struct _AtkUtilAuraLinux        AtkUtilAuraLinux;
 typedef struct _AtkUtilAuraLinuxClass   AtkUtilAuraLinuxClass;
 
-struct _AtkUtilAuraLinux
-{
+struct _AtkUtilAuraLinux {
   AtkUtil parent;
 };
 
-struct _AtkUtilAuraLinuxClass
-{
+struct _AtkUtilAuraLinuxClass {
   AtkUtilClass parent_class;
 };
 
@@ -99,7 +99,7 @@
 }
 
 static void atk_util_auralinux_class_init(AtkUtilAuraLinuxClass *klass) {
-  AtkUtilClass *atk_class;
+  AtkUtilClass* atk_class;
   gpointer data;
 
   data = g_type_class_peek(ATK_TYPE_UTIL);
diff --git a/ui/accessibility/platform/atk_util_auralinux.h b/ui/accessibility/platform/atk_util_auralinux.h
index b846d5f..9f6ad91 100644
--- a/ui/accessibility/platform/atk_util_auralinux.h
+++ b/ui/accessibility/platform/atk_util_auralinux.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 UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_
-#define UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_
+#ifndef UI_ACCESSIBILITY_PLATFORM_ATK_UTIL_AURALINUX_H_
+#define UI_ACCESSIBILITY_PLATFORM_ATK_UTIL_AURALINUX_H_
 
 #include <atk/atk.h>
 
@@ -65,4 +65,4 @@
 
 }  // namespace ui
 
-#endif  // UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_
+#endif  // UI_ACCESSIBILITY_PLATFORM_ATK_UTIL_AURALINUX_H_
diff --git a/ui/accessibility/platform/ax_fragment_root_win.cc b/ui/accessibility/platform/ax_fragment_root_win.cc
index adcffb4e..a029658 100644
--- a/ui/accessibility/platform/ax_fragment_root_win.cc
+++ b/ui/accessibility/platform/ax_fragment_root_win.cc
@@ -4,6 +4,8 @@
 
 #include "ui/accessibility/platform/ax_fragment_root_win.h"
 
+#include <unordered_map>
+
 #include "base/no_destructor.h"
 #include "ui/accessibility/platform/ax_platform_node_win.h"
 #include "ui/base/win/atl_module.h"
diff --git a/ui/accessibility/platform/ax_platform_node.h b/ui/accessibility/platform/ax_platform_node.h
index 1e9b8be6..aeab1f7 100644
--- a/ui/accessibility/platform/ax_platform_node.h
+++ b/ui/accessibility/platform/ax_platform_node.h
@@ -96,7 +96,7 @@
 
 #if defined(OS_MACOSX)
   // Fire a platform-specific notification to announce |text|.
-  virtual void AnnounceText(base::string16& text) = 0;
+  virtual void AnnounceText(const base::string16& text) = 0;
 #endif
 
   // Return this object's delegate.
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 9796e55..d6291a8 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -235,7 +235,7 @@
 }
 
 void IdsToGPtrArray(AXPlatformNodeDelegate* delegate,
-                    std::vector<int32_t>& ids,
+                    const std::vector<int32_t>& ids,
                     GPtrArray* array) {
   for (const auto& node_id : ids) {
     if (AXPlatformNode* header = delegate->GetFromNodeID(node_id)) {
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 9019c920..5195d07 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -123,7 +123,7 @@
 }
 
 #if defined(OS_MACOSX)
-void AXPlatformNodeBase::AnnounceText(base::string16& text) {}
+void AXPlatformNodeBase::AnnounceText(const base::string16& text) {}
 #endif
 
 AXPlatformNodeDelegate* AXPlatformNodeBase::GetDelegate() const {
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
index 0ef8d74..3cbca68 100644
--- a/ui/accessibility/platform/ax_platform_node_base.h
+++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -68,7 +68,7 @@
   void NotifyAccessibilityEvent(ax::mojom::Event event_type) override;
 
 #if defined(OS_MACOSX)
-  void AnnounceText(base::string16& text) override;
+  void AnnounceText(const base::string16& text) override;
 #endif
 
   AXPlatformNodeDelegate* GetDelegate() const override;
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.h b/ui/accessibility/platform/ax_platform_node_delegate_base.h
index f5b243b..1db5ffb 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.h
@@ -8,6 +8,7 @@
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 
 #include <set>
+#include <vector>
 
 namespace ui {
 
diff --git a/ui/accessibility/platform/ax_platform_node_mac.h b/ui/accessibility/platform/ax_platform_node_mac.h
index a58408b..a5624cb7 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.h
+++ b/ui/accessibility/platform/ax_platform_node_mac.h
@@ -23,7 +23,7 @@
   // AXPlatformNode.
   gfx::NativeViewAccessible GetNativeViewAccessible() override;
   void NotifyAccessibilityEvent(ax::mojom::Event event_type) override;
-  void AnnounceText(base::string16& text) override;
+  void AnnounceText(const base::string16& text) override;
 
   // AXPlatformNodeBase.
   void Destroy() override;
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index f7f0f3e..3e767ce 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -992,7 +992,7 @@
   NotifyMacEvent(native_node_, event_type);
 }
 
-void AXPlatformNodeMac::AnnounceText(base::string16& text) {
+void AXPlatformNodeMac::AnnounceText(const base::string16& text) {
   PostAnnouncementNotification(base::SysUTF16ToNSString(text));
 }
 
diff --git a/ui/accessibility/platform/ax_platform_node_test_helper.h b/ui/accessibility/platform/ax_platform_node_test_helper.h
index 14994ea..dd5ffb2e 100644
--- a/ui/accessibility/platform/ax_platform_node_test_helper.h
+++ b/ui/accessibility/platform/ax_platform_node_test_helper.h
@@ -5,6 +5,8 @@
 #ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEST_HELPER_H_
 #define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEST_HELPER_H_
 
+#include <string>
+
 #include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 
diff --git a/ui/accessibility/platform/ax_platform_node_textprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textprovider_win.cc
index 959f2e6..5e068d0 100644
--- a/ui/accessibility/platform/ax_platform_node_textprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textprovider_win.cc
@@ -109,7 +109,7 @@
   if (!selections_to_return.Get())
     return E_OUTOFMEMORY;
 
-  long index = 0;
+  LONG index = 0;
   hr = SafeArrayPutElement(selections_to_return.Get(), &index,
                            text_range_provider);
   DCHECK(SUCCEEDED(hr));
diff --git a/ui/accessibility/platform/ax_platform_node_textprovider_win.h b/ui/accessibility/platform/ax_platform_node_textprovider_win.h
index 65c5713..4a7567d 100644
--- a/ui/accessibility/platform/ax_platform_node_textprovider_win.h
+++ b/ui/accessibility/platform/ax_platform_node_textprovider_win.h
@@ -5,6 +5,8 @@
 #ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEXTPROVIDER_WIN_H_
 #define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEXTPROVIDER_WIN_H_
 
+#include <string>
+
 #include "ui/accessibility/platform/ax_platform_node_win.h"
 
 namespace ui {
diff --git a/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
index 8bfc52a4..239bc450 100644
--- a/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
@@ -316,14 +316,14 @@
   root_text_provider->GetSelection(selections.Receive());
   ASSERT_NE(nullptr, selections.Get());
 
-  long ubound;
+  LONG ubound;
   EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selections.Get(), 1, &ubound));
   EXPECT_EQ(0, ubound);
-  long lbound;
+  LONG lbound;
   EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selections.Get(), 1, &lbound));
   EXPECT_EQ(0, lbound);
 
-  long index = 0;
+  LONG index = 0;
   CComPtr<ITextRangeProvider> text_range_provider;
   EXPECT_HRESULT_SUCCEEDED(
       SafeArrayGetElement(selections.Get(), &index, &text_range_provider));
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
index 8456cc7..5e03a81 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
@@ -5,6 +5,7 @@
 #ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEXTRANGEPROVIDER_WIN_H_
 #define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEXTRANGEPROVIDER_WIN_H_
 
+#include <string>
 #include <tuple>
 #include <vector>
 
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 3d98c46..b79fac73 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -1501,9 +1501,9 @@
 
   CComPtr<ITextRangeProvider> selected_text_range_provider;
   base::win::ScopedSafearray selection;
-  long index = 0;
-  long ubound;
-  long lbound;
+  LONG index = 0;
+  LONG ubound;
+  LONG lbound;
 
   // Text range "some text" performs select.
   {
diff --git a/ui/accessibility/platform/ax_platform_node_unittest.h b/ui/accessibility/platform/ax_platform_node_unittest.h
index c9218a4..9bd6ea1 100644
--- a/ui/accessibility/platform/ax_platform_node_unittest.h
+++ b/ui/accessibility/platform/ax_platform_node_unittest.h
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_ACCESSIBILITY_AX_PLATFORM_NODE_UNITTEST_H_
-#define UI_ACCESSIBILITY_AX_PLATFORM_NODE_UNITTEST_H_
+#ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_UNITTEST_H_
+#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_UNITTEST_H_
+
+#include <memory>
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/ax_node.h"
@@ -63,4 +65,4 @@
 
 }  // namespace ui
 
-#endif  // UI_ACCESSIBILITY_AX_PLATFORM_NODE_UNITTEST_H_
+#endif  // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_UNITTEST_H_
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index de4e39c..cdf0a9058 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -3575,8 +3575,8 @@
                                                    VARIANT* result) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PROPERTY_VALUE);
 
-  constexpr long kFirstKnownUiaPropertyId = UIA_RuntimeIdPropertyId;
-  constexpr long kLastKnownUiaPropertyId = UIA_HeadingLevelPropertyId;
+  constexpr LONG kFirstKnownUiaPropertyId = UIA_RuntimeIdPropertyId;
+  constexpr LONG kLastKnownUiaPropertyId = UIA_HeadingLevelPropertyId;
   if (property_id >= kFirstKnownUiaPropertyId &&
       property_id <= kLastKnownUiaPropertyId) {
     base::UmaHistogramSparse("Accessibility.WinAPIs.GetPropertyValue",
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index caed8f9..ee9e2a9 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -1,3 +1,4 @@
+//
 // Copyright 2015 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/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index aa2f127..d803a82 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -1,3 +1,4 @@
+//
 // Copyright 2015 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.
@@ -308,7 +309,7 @@
   return result;
 }
 
-void AXPlatformNodeWinTest::CheckVariantHasName(ScopedVariant& variant,
+void AXPlatformNodeWinTest::CheckVariantHasName(const ScopedVariant& variant,
                                                 const wchar_t* expected_name) {
   ASSERT_NE(nullptr, variant.ptr());
   ComPtr<IAccessible> accessible;
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.h b/ui/accessibility/platform/ax_platform_node_win_unittest.h
index 4ed3cef..88a871a 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.h
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.h
@@ -7,6 +7,7 @@
 
 #include "ui/accessibility/platform/ax_platform_node_unittest.h"
 
+#include <memory>
 #include <unordered_set>
 
 #include "ui/base/win/accessibility_misc_utils.h"
@@ -58,7 +59,7 @@
       Microsoft::WRL::ComPtr<IAccessible> accessible);
   Microsoft::WRL::ComPtr<IAccessible2_2> ToIAccessible2_2(
       Microsoft::WRL::ComPtr<IAccessible> accessible);
-  void CheckVariantHasName(base::win::ScopedVariant& variant,
+  void CheckVariantHasName(const base::win::ScopedVariant& variant,
                            const wchar_t* expected_name);
   void CheckIUnknownHasName(Microsoft::WRL::ComPtr<IUnknown> unknown,
                             const wchar_t* expected_name);
@@ -67,7 +68,7 @@
   void InitFragmentRoot();
   Microsoft::WRL::ComPtr<IRawElementProviderFragmentRoot> GetFragmentRoot();
 
-  using PatternSet = std::unordered_set<long>;
+  using PatternSet = std::unordered_set<LONG>;
   PatternSet GetSupportedPatternsFromNodeId(int32_t id);
   std::unique_ptr<AXFragmentRootWin> ax_fragment_root_;
 };
diff --git a/ui/accessibility/platform/ax_platform_relation_win.h b/ui/accessibility/platform/ax_platform_relation_win.h
index 2abfe23..dd8426f 100644
--- a/ui/accessibility/platform/ax_platform_relation_win.h
+++ b/ui/accessibility/platform/ax_platform_relation_win.h
@@ -79,4 +79,4 @@
 
 }  // namespace ui
 
-#endif  // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_WIN_H_
+#endif  // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_RELATION_WIN_H_
diff --git a/ui/accessibility/platform/ax_system_caret_win.h b/ui/accessibility/platform/ax_system_caret_win.h
index a18ed98..0d017c6 100644
--- a/ui/accessibility/platform/ax_system_caret_win.h
+++ b/ui/accessibility/platform/ax_system_caret_win.h
@@ -47,12 +47,11 @@
   AXPlatformNodeWin* caret_;
   gfx::AcceleratedWidget event_target_;
   AXNodeData data_;
+  ui::AXUniqueId unique_id_;
 
   friend class AXPlatformNodeWin;
-  DISALLOW_COPY_AND_ASSIGN(AXSystemCaretWin);
 
- private:
-  ui::AXUniqueId unique_id_;
+  DISALLOW_COPY_AND_ASSIGN(AXSystemCaretWin);
 };
 
 }  // namespace ui
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index 2fcb1ba8..551395dd 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -5,6 +5,7 @@
 #include "ui/accessibility/platform/test_ax_node_wrapper.h"
 
 #include <unordered_map>
+#include <utility>
 
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -600,13 +601,13 @@
 // descendants for a given node within the descendants vector.
 void TestAXNodeWrapper::Descendants(
     const AXNode* node,
-    std::vector<gfx::NativeViewAccessible>& descendants) const {
+    std::vector<gfx::NativeViewAccessible>* descendants) const {
   std::vector<AXNode*> child_nodes = node->children();
   for (AXNode* child : child_nodes) {
-    descendants.emplace_back(ax_platform_node()
-                                 ->GetDelegate()
-                                 ->GetFromNodeID(child->id())
-                                 ->GetNativeViewAccessible());
+    descendants->emplace_back(ax_platform_node()
+                                  ->GetDelegate()
+                                  ->GetFromNodeID(child->id())
+                                  ->GetNativeViewAccessible());
     Descendants(child, descendants);
   }
 }
@@ -614,7 +615,7 @@
 const std::vector<gfx::NativeViewAccessible> TestAXNodeWrapper::GetDescendants()
     const {
   std::vector<gfx::NativeViewAccessible> descendants;
-  Descendants(node_, descendants);
+  Descendants(node_, &descendants);
   return descendants;
 }
 
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.h b/ui/accessibility/platform/test_ax_node_wrapper.h
index e3cfad2..3136e0d 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.h
+++ b/ui/accessibility/platform/test_ax_node_wrapper.h
@@ -6,6 +6,7 @@
 #define UI_ACCESSIBILITY_PLATFORM_TEST_AX_NODE_WRAPPER_H_
 
 #include <set>
+#include <string>
 #include <vector>
 
 #include "build/build_config.h"
@@ -113,7 +114,7 @@
   int32_t GetSetSize() const override;
   const std::vector<gfx::NativeViewAccessible> GetDescendants() const override;
   void Descendants(const AXNode* node,
-                   std::vector<gfx::NativeViewAccessible>& descendants) const;
+                   std::vector<gfx::NativeViewAccessible>* descendants) const;
 
  private:
   TestAXNodeWrapper(AXTree* tree, AXNode* node);
diff --git a/ui/base/mpris/mpris_service_impl.cc b/ui/base/mpris/mpris_service_impl.cc
index 64a2fabbd..240b7c6 100644
--- a/ui/base/mpris/mpris_service_impl.cc
+++ b/ui/base/mpris/mpris_service_impl.cc
@@ -227,8 +227,12 @@
 void MprisServiceImpl::OnExported(const std::string& interface_name,
                                   const std::string& method_name,
                                   bool success) {
-  if (success)
-    num_methods_exported_++;
+  if (!success) {
+    service_failed_to_start_ = true;
+    return;
+  }
+
+  num_methods_exported_++;
 
   // Still waiting for more methods to finish exporting.
   if (num_methods_exported_ < kNumMethodsToExport)
@@ -242,8 +246,10 @@
 
 void MprisServiceImpl::OnOwnership(const std::string& service_name,
                                    bool success) {
-  if (!success)
+  if (!success) {
+    service_failed_to_start_ = true;
     return;
+  }
 
   service_ready_ = true;
 
@@ -441,6 +447,12 @@
 }
 
 void MprisServiceImpl::EmitPropertiesChangedSignal() {
+  // If we're still trying to start the service, delay emitting.
+  if (!service_ready_ && !service_failed_to_start_) {
+    EmitPropertiesChangedSignalDebounced();
+    return;
+  }
+
   if (!bus_ || !exported_object_ || !service_ready_)
     return;
 
diff --git a/ui/base/mpris/mpris_service_impl.h b/ui/base/mpris/mpris_service_impl.h
index 0fc9e640f..801ac56 100644
--- a/ui/base/mpris/mpris_service_impl.h
+++ b/ui/base/mpris/mpris_service_impl.h
@@ -132,6 +132,9 @@
   // True if we have finished creating the DBus service and received ownership.
   bool service_ready_ = false;
 
+  // True if we failed to start the MPRIS DBus service.
+  bool service_failed_to_start_ = false;
+
   // Used to only send 1 PropertiesChanged signal when many properties are
   // changed at once.
   base::OneShotTimer properties_changed_debounce_timer_;
diff --git a/ui/file_manager/image_loader/piex/tests.html b/ui/file_manager/image_loader/piex/tests.html
index 6394b97c..f9cd1c35 100644
--- a/ui/file_manager/image_loader/piex/tests.html
+++ b/ui/file_manager/image_loader/piex/tests.html
@@ -86,6 +86,53 @@
     }
   }
 
+  function createFileSystem(images) {
+    return new Promise((resolve, reject) => {
+      document.title = 'createFileSystem';
+
+      function failed(error) {
+        reject(new Error('Creating file system: ' + error.name));
+      }
+
+      function createdFileSystem(fileSystem) {
+        console.log('test: created file system', fileSystem.name);
+        window.fileSystem = fileSystem;
+        resolve();
+      }
+
+      const bytes = images * 30 * 1024 * 1024;  // 30M per image.
+      window.webkitRequestFileSystem(
+          window.TEMPORARY, bytes, createdFileSystem, failed);
+    });
+  }
+
+  function writeToFileSystem(image) {
+    return new Promise(async (resolve, reject) => {
+      document.title = image;
+
+      const buffer = await fetch(image).then((response) => {
+        if (!response.ok)
+          throw new Error('Failed to fetch image: ' + image);
+        return response.arrayBuffer();
+      }).catch(reject);
+
+      function failure(error) {
+        reject(new Error('Writing file system: ' + error.name));
+      }
+
+      function writeEntry(fileEntry) {
+        fileEntry.createWriter((writer) => {
+          writer.onerror = failure;
+          writer.onwrite = resolve;
+          writer.write(new Blob([buffer]));
+        }, failure);
+      }
+
+      window.fileSystem.root.getFile(
+          image.replace('images/', ''), {create: true}, writeEntry, failure);
+    });
+  }
+
   function hashUint8Array(data, hash = ~0) {
     for (let i = 0; i < data.byteLength; ++i)
       hash = (hash << 5) - hash + data[i];
diff --git a/ui/gfx/transform.cc b/ui/gfx/transform.cc
index 0d5e04b..b42890ec3 100644
--- a/ui/gfx/transform.cc
+++ b/ui/gfx/transform.cc
@@ -53,29 +53,10 @@
                      SkMScalar col2row4,
                      SkMScalar col3row4,
                      SkMScalar col4row4)
-    : matrix_(SkMatrix44::kIdentity_Constructor) {
-  // TODO(masonfreed): Replace this with an explicit 16-element constructor
-  // on SkMatrix44, once that's available. This code does a *lot* of extra
-  // work, because each call to setDouble re-calculates the matrix type.
-  matrix_.set(0, 0, col1row1);
-  matrix_.set(1, 0, col1row2);
-  matrix_.set(2, 0, col1row3);
-  matrix_.set(3, 0, col1row4);
-
-  matrix_.set(0, 1, col2row1);
-  matrix_.set(1, 1, col2row2);
-  matrix_.set(2, 1, col2row3);
-  matrix_.set(3, 1, col2row4);
-
-  matrix_.set(0, 2, col3row1);
-  matrix_.set(1, 2, col3row2);
-  matrix_.set(2, 2, col3row3);
-  matrix_.set(3, 2, col3row4);
-
-  matrix_.set(0, 3, col4row1);
-  matrix_.set(1, 3, col4row2);
-  matrix_.set(2, 3, col4row3);
-  matrix_.set(3, 3, col4row4);
+    : matrix_(SkMatrix44::kUninitialized_Constructor) {
+  matrix_.set4x4(col1row1, col1row2, col1row3, col1row4, col2row1, col2row2,
+                 col2row3, col2row4, col3row1, col3row2, col3row3, col3row4,
+                 col4row1, col4row2, col4row3, col4row4);
 }
 
 Transform::Transform(SkMScalar col1row1,
diff --git a/ui/login/screen.js b/ui/login/screen.js
index 8fcddc9..6d54840 100644
--- a/ui/login/screen.js
+++ b/ui/login/screen.js
@@ -60,56 +60,6 @@
     },
 
     /**
-     * Creates and returns new button element with given identifier
-     * and on-click event listener, which sends notification about
-     * user action to the C++ side.
-     *
-     * @param {string} id Identifier of a button.
-     * @param {string} opt_action_id Identifier of user action.
-     * @final
-     */
-    declareButton: function(id, opt_action_id) {
-      var button = this.ownerDocument.createElement('button');
-      button.id = id;
-      this.declareUserAction(button,
-                             { action_id: opt_action_id,
-                               event: 'click'
-                             });
-      return button;
-    },
-
-    /**
-      * Adds event listener to an element which sends notification
-      * about event to the C++ side.
-      *
-      * @param {Element} element An DOM element
-      * @param {Object} options A dictionary of optional arguments:
-      *   {string} event: name of event that will be listened,
-      *            default: 'click'.
-      *   {string} action_id: name of an action which will be sent to
-      *                       the C++ side.
-      *   {function} condition: a one-argument function which takes
-      *              event as an argument, notification is sent to the
-      *              C++ side iff condition is true, default: constant
-      *              true function.
-      * @final
-      */
-    declareUserAction: function(element, options) {
-      var self = this;
-      options = options || {};
-
-      var event = options.event || 'click';
-      var action_id = options.action_id || element.id;
-      var condition = options.condition || alwaysTruePredicate;
-
-      element.addEventListener(event, function(e) {
-        if (condition(e))
-          self.sendImpl_(CALLBACK_USER_ACTED, action_id);
-        e.stopPropagation();
-      });
-    },
-
-    /**
      * @override
      * @final
      */
@@ -118,34 +68,10 @@
     },
 
     /**
-     * Does the following things:
-     *  * Looks for elements having "alias" property and adds them as the
-     *    proprties of the screen with name equal to value of "alias", i.e. HTML
-     *    element <div alias="myDiv"></div> will be stored in this.myDiv.
-     *  * Looks for buttons having "action" properties and adds click handlers
-     *    to them. These handlers send |CALLBACK_USER_ACTED| messages to
-     *    C++ with "action" property's value as payload.
      * @private
      */
     initializeImpl_: function() {
       this.decorate();
-
-      this.querySelectorAllImpl_('[alias]').forEach(function(element) {
-        var alias = element.getAttribute('alias');
-        if (alias in this)
-          throw Error('Alias "' + alias + '" of "' + this.name() + '" screen ' +
-              'shadows or redefines property that is already defined.');
-        this[alias] = element;
-        this[element.getAttribute('alias')] = element;
-      }, this);
-      var self = this;
-      this.querySelectorAllImpl_('button[action]').forEach(function(button) {
-        button.addEventListener('click', function(e) {
-          var action = this.getAttribute('action');
-          self.send(CALLBACK_USER_ACTED, action);
-          e.stopPropagation();
-        });
-      });
     },
 
     /**