diff --git a/DEPS b/DEPS
index 85ce8fb0..4988f70 100644
--- a/DEPS
+++ b/DEPS
@@ -116,11 +116,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': 'e7b1a13a1e2ae07cd53144bc979184118f1a852e',
+  'skia_revision': 'a1211832046de9730dac317afaa8491e1f9019d8',
   # 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': '558ce607b922e7ac06ac3147a90a38b5266f5241',
+  'v8_revision': '655a9bc2d20b6879e1a644613899ca05aa6121ad',
   # 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.
@@ -128,7 +128,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'fde74c078f77f434963f763a4715136b4ed6baaf',
+  'angle_revision': '8a0fb48a56d668d9473dfead811e1e0d1bbc5f0c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -176,7 +176,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': '352a0e0997b945a08a0c0b25e600fec59ed9ce22',
+  'catapult_revision': '5913160a7d40a5d0edb2578f3bb769e8667d41a9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -224,7 +224,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': 'cd22b3155708d7922524e7f52551e0794b3c1d62',
+  'spv_tools_revision': 'd652ed3029eb89a4ba0b51e6604dbf3472299647',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -655,7 +655,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a58355c49753c0a79b39c1a9891e1a0d3e0f2908',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'dac70cc74650f16c7a57b6059df0ae83ae326d72',
       'condition': 'checkout_linux',
   },
 
@@ -680,7 +680,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'af3328fc7a75c5462e2d0078b9d59b0a5e5e90c1',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ae6836ecee576f30a16050b84e5e97f69d584851',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1014,7 +1014,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'e867072a2353e9609710c7ee0af14a954d21e397',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '96e14159c92b4b47817ae224193553e6771ead0b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1166,7 +1166,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '5b6cbd789b9b91b4e46dde883c9f2ecb31eddade',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '72bba625d5f79f26ca75984d7acbc7ece0468a84',
+    Var('webrtc_git') + '/src.git' + '@' + '85340ce5166c880be8c863be6949874b675238f1',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1197,7 +1197,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e86231b9da1a598d910364855eb070457bebf606',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@baafd0234bc7db77b39ac9d55926cde57fa86e80',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 6ab7089..b5c428a 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -130,6 +130,10 @@
     'arc_fileapi': {
       'filepath': 'chrome/browser/chromeos/arc/fileapi'
     },
+    'arc_ime': {
+      'filepath': 'chrome/browser/chromeos/arc/input_method_manager/'\
+                  '|components/arc/ime/'
+    },
     'arc_kiosk': {
       'filepath': 'chrome/browser/chromeos/app_mode/arc/'\
                   '|components/arc/kiosk/'\
@@ -1813,6 +1817,7 @@
     'arc_auth': ['khmel+watch@chromium.org'],
     'arc_common': ['hashimoto+watch@chromium.org'],
     'arc_fileapi': ['nya+watch@chromium.org'],
+    'arc_ime': ['yhanada+watch@chromium.org'],
     'arc_kiosk': ['poromov+watch@chromium.org'],
     'arc_net': ['abhishekbh@chromium.org',
                 'cernekee@chromium.org',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ec018be..dde21b4d 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1264,6 +1264,8 @@
     "ws/ash_gpu_interface_provider.h",
     "ws/ash_window_manager.cc",
     "ws/ash_window_manager.h",
+    "ws/multi_user_window_manager_bridge.cc",
+    "ws/multi_user_window_manager_bridge.h",
     "ws/window_lookup.cc",
     "ws/window_service_delegate_impl.cc",
     "ws/window_service_delegate_impl.h",
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 3617781..8487488 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -1088,6 +1088,13 @@
   EXPECT_LE(0, view->GetWidget()->GetNativeView()->bounds().y());
 }
 
+// Tests that no crash occurs after an attempt to show app list in an invalid
+// display.
+TEST_F(AppListPresenterDelegateTest, ShowInInvalidDisplay) {
+  GetAppListTestHelper()->ShowAndRunLoop(display::kInvalidDisplayId);
+  GetAppListTestHelper()->CheckState(app_list::AppListViewState::CLOSED);
+}
+
 // Test a variety of behaviors for home launcher (app list in tablet mode).
 class AppListPresenterDelegateHomeLauncherTest
     : public AppListPresenterDelegateTest {
diff --git a/ash/app_list/presenter/app_list_presenter_impl.cc b/ash/app_list/presenter/app_list_presenter_impl.cc
index e012041..eb06530 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl.cc
@@ -126,6 +126,11 @@
     return;
   }
 
+  if (!delegate_->GetRootWindowForDisplayId(display_id)) {
+    LOG(ERROR) << "Root window does not exist for display: " << display_id;
+    return;
+  }
+
   is_visible_ = true;
   RequestPresentationTime(display_id, event_time_stamp);
 
diff --git a/ash/app_list/presenter/app_list_presenter_impl_unittest.cc b/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
index fba8e6a8..2e181ab 100644
--- a/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl_unittest.cc
@@ -70,7 +70,7 @@
   bool IsHomeLauncherEnabledInTabletMode() override { return false; }
   bool GetOnScreenKeyboardShown() override { return false; }
   aura::Window* GetRootWindowForDisplayId(int64_t display_id) override {
-    return nullptr;
+    return container_->GetRootWindow();
   }
   void OnVisibilityChanged(bool visible, aura::Window* root_window) override {}
   void OnTargetVisibilityChanged(bool visible) override {}
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 3fad61f1..26037c9d 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -20,7 +20,6 @@
 #include "build/build_config.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/animation/throb_animation.h"
@@ -462,24 +461,19 @@
 
   if (!apps_grid_view_->IsSelectedView(this))
     apps_grid_view_->ClearAnySelectedView();
-  int run_types = views::MenuRunner::HAS_MNEMONICS;
+
+  int run_types = views::MenuRunner::HAS_MNEMONICS |
+                  views::MenuRunner::USE_TOUCHABLE_LAYOUT |
+                  views::MenuRunner::FIXED_ANCHOR |
+                  views::MenuRunner::CONTEXT_MENU;
 
   if (source_type == ui::MENU_SOURCE_TOUCH)
     run_types |= views::MenuRunner::SEND_GESTURE_EVENTS_TO_OWNER;
 
-  views::MenuAnchorPosition anchor_position = views::MENU_ANCHOR_TOPLEFT;
-  gfx::Rect anchor_rect = gfx::Rect(point, gfx::Size());
-
-  if (::features::IsTouchableAppContextMenuEnabled()) {
-    run_types |= views::MenuRunner::USE_TOUCHABLE_LAYOUT |
-                 views::MenuRunner::FIXED_ANCHOR |
-                 views::MenuRunner::CONTEXT_MENU;
-    anchor_position = views::MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT;
-    anchor_rect = apps_grid_view_->GetIdealBounds(this);
-    // Anchor the menu to the same rect that is used for selection highlight.
-    AdaptBoundsForSelectionHighlight(&anchor_rect);
-    views::View::ConvertRectToScreen(apps_grid_view_, &anchor_rect);
-  }
+  gfx::Rect anchor_rect = apps_grid_view_->GetIdealBounds(this);
+  // Anchor the menu to the same rect that is used for selection highlight.
+  AdaptBoundsForSelectionHighlight(&anchor_rect);
+  views::View::ConvertRectToScreen(apps_grid_view_, &anchor_rect);
 
   context_menu_ = std::make_unique<AppListMenuModelAdapter>(
       item_weak_->GetMetadata()->id, this, source_type, this,
@@ -487,7 +481,8 @@
       base::BindOnce(&AppListItemView::OnMenuClosed,
                      weak_ptr_factory_.GetWeakPtr()));
   context_menu_->Build(std::move(menu));
-  context_menu_->Run(anchor_rect, anchor_position, run_types);
+  context_menu_->Run(anchor_rect, views::MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT,
+                     run_types);
   apps_grid_view_->SetSelectedView(this);
 }
 
diff --git a/ash/app_list/views/search_result_tile_item_view.cc b/ash/app_list/views/search_result_tile_item_view.cc
index c6ee795b..4fb773c 100644
--- a/ash/app_list/views/search_result_tile_item_view.cc
+++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -24,7 +24,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -367,27 +366,20 @@
   if (menu.empty() || (context_menu_ && context_menu_->IsShowingMenu()))
     return;
 
-  int run_types = views::MenuRunner::HAS_MNEMONICS;
-  views::MenuAnchorPosition anchor_position = views::MENU_ANCHOR_TOPLEFT;
-  gfx::Rect anchor_rect = gfx::Rect(point, gfx::Size());
-
-  if (::features::IsTouchableAppContextMenuEnabled()) {
-    anchor_position = views::MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT;
-    run_types |= views::MenuRunner::USE_TOUCHABLE_LAYOUT |
-                 views::MenuRunner::CONTEXT_MENU |
-                 views::MenuRunner::FIXED_ANCHOR;
-    anchor_rect = source->GetBoundsInScreen();
-    // Anchor the menu to the same rect that is used for selection highlight.
-    anchor_rect.ClampToCenteredSize(
-        AppListConfig::instance().grid_focus_size());
-  }
+  gfx::Rect anchor_rect = source->GetBoundsInScreen();
+  // Anchor the menu to the same rect that is used for selection highlight.
+  anchor_rect.ClampToCenteredSize(AppListConfig::instance().grid_focus_size());
 
   context_menu_ = std::make_unique<AppListMenuModelAdapter>(
       item_->id(), this, source_type, this, GetAppType(),
       base::BindOnce(&SearchResultTileItemView::OnMenuClosed,
                      weak_ptr_factory_.GetWeakPtr()));
   context_menu_->Build(std::move(menu));
-  context_menu_->Run(anchor_rect, anchor_position, run_types);
+  context_menu_->Run(anchor_rect, views::MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT,
+                     views::MenuRunner::HAS_MNEMONICS |
+                         views::MenuRunner::USE_TOUCHABLE_LAYOUT |
+                         views::MenuRunner::CONTEXT_MENU |
+                         views::MenuRunner::FIXED_ANCHOR);
   source->RequestFocus();
 }
 
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 16c33721..63304f4 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -697,7 +697,7 @@
 
     auth_error_bubble_->ShowErrorBubble(
         container, big_view->auth_user()->password_view() /*anchor_view*/,
-        LoginBubble::kFlagPersistent);
+        true /*show_persistently*/);
   }
 }
 
@@ -816,7 +816,7 @@
   label->SetEnabledColor(SK_ColorWHITE);
   warning_banner_bubble_->ShowErrorBubble(
       label, CurrentBigUserView()->auth_user()->password_view() /*anchor_view*/,
-      LoginBubble::kFlagPersistent);
+      true /*show_persistently*/);
 }
 
 void LockContentsView::OnHideWarningBanner() {
@@ -975,7 +975,7 @@
 
   detachable_base_error_bubble_->ShowErrorBubble(
       label, CurrentBigUserView()->auth_user()->password_view() /*anchor_view*/,
-      LoginBubble::kFlagPersistent);
+      true /*show_persistently*/);
 
   // Remove the focus from the password field, to make user less likely to enter
   // the password without seeing the warning about detachable base change.
@@ -1489,7 +1489,7 @@
     supervised_user_deprecation_bubble_->ShowErrorBubble(
         label,
         CurrentBigUserView()->auth_user()->password_view() /*anchor_view*/,
-        LoginBubble::kFlagPersistent);
+        true /*show_persistently*/);
   }
 
   // The new auth user might have different last used detachable base - make
@@ -1595,7 +1595,7 @@
 
   auth_error_bubble_->ShowErrorBubble(
       container, big_view->auth_user()->password_view() /*anchor_view*/,
-      LoginBubble::kFlagsNone);
+      false /*show_persistently*/);
 }
 
 void LockContentsView::OnEasyUnlockIconHovered() {
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index d35d155a..3914642 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -377,7 +377,7 @@
   LockContentsView* contents =
       LockScreen::TestApi(LockScreen::Get()).contents_view();
   ASSERT_NE(nullptr, contents);
-  LoadUsers(9);
+  SetUserCount(9);
 
   // Users list in extra small layout should adjust its height to parent.
   ScrollableUsersListView* users_list =
@@ -399,7 +399,7 @@
   LockContentsView* contents =
       LockScreen::TestApi(LockScreen::Get()).contents_view();
   ASSERT_NE(nullptr, contents);
-  LoadUsers(4);
+  SetUserCount(4);
   ScrollableUsersListView* users_list =
       LockContentsView::TestApi(contents).users_list();
 
@@ -1231,7 +1231,7 @@
 
   // Add user who can use pin authentication.
   const std::string email = "user@domain.com";
-  LoadUser(email);
+  AddUserByEmail(email);
   contents->OnPinEnabledForUserChanged(AccountId::FromUserEmail(email), true);
   LoginBigUserView* big_view =
       LockContentsView::TestApi(contents).primary_big_view();
@@ -1259,7 +1259,7 @@
       LockScreen::TestApi(LockScreen::Get()).contents_view();
   ASSERT_NE(nullptr, contents);
 
-  LoadUsers(2);
+  SetUserCount(2);
 
   LoginAuthUserView::TestApi primary_user(
       LockContentsView::TestApi(contents).primary_big_view()->auth_user());
@@ -1305,7 +1305,7 @@
 
   // Add user who can use pin authentication.
   const std::string email = "user@domain.com";
-  LoadUser(email);
+  AddUserByEmail(email);
   contents->OnPinEnabledForUserChanged(AccountId::FromUserEmail(email), true);
   LoginBigUserView* big_view =
       LockContentsView::TestApi(contents).primary_big_view();
@@ -1799,14 +1799,13 @@
   EXPECT_TRUE(HasFocusInAnyChildView(status_area));
 }
 
-class LockContentsViewPowerManagerUnitTest
-    : public LockContentsViewKeyboardUnitTest {
+class LockContentsViewPowerManagerUnitTest : public LockContentsViewUnitTest {
  public:
   void SetUp() override {
     chromeos::DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient(
         std::make_unique<chromeos::FakePowerManagerClient>());
 
-    LockContentsViewKeyboardUnitTest::SetUp();
+    LockContentsViewUnitTest::SetUp();
   }
 };
 
@@ -1831,9 +1830,9 @@
 
 // Verifies that the password box for the active user is cleared if a suspend
 // event is received.
-TEST_F(LockContentsViewKeyboardUnitTest, PasswordClearedOnSuspend) {
+TEST_F(LockContentsViewUnitTest, PasswordClearedOnSuspend) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(1);
+  AddUsers(1);
 
   LockScreen::TestApi lock_screen = LockScreen::TestApi(LockScreen::Get());
   LockContentsView* contents = lock_screen.contents_view();
@@ -1851,9 +1850,9 @@
   EXPECT_TRUE(textfield->text().empty());
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, ArrowNavSingleUser) {
+TEST_F(LockContentsViewUnitTest, ArrowNavSingleUser) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(1);
+  SetUserCount(1);
   LockContentsView* lock_contents =
       LockScreen::TestApi(LockScreen::Get()).contents_view();
 
@@ -1869,10 +1868,10 @@
   EXPECT_TRUE(login_views_utils::HasFocusInAnyChildView(primary_big_view));
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, ArrowNavTwoUsers) {
+TEST_F(LockContentsViewUnitTest, ArrowNavTwoUsers) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(1);
-  LoadPublicAccountUsers(1);
+  AddUsers(1);
+  AddPublicAccountUsers(1);
   LockContentsView::TestApi lock_contents = LockContentsView::TestApi(
       LockScreen::TestApi(LockScreen::Get()).contents_view());
 
@@ -1899,9 +1898,9 @@
   EXPECT_TRUE(login_views_utils::HasFocusInAnyChildView(primary_password_view));
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, ArrowNavThreeUsers) {
+TEST_F(LockContentsViewUnitTest, ArrowNavThreeUsers) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(3);
+  SetUserCount(3);
   LockContentsView::TestApi lock_contents = LockContentsView::TestApi(
       LockScreen::TestApi(LockScreen::Get()).contents_view());
 
@@ -1932,9 +1931,9 @@
   EXPECT_TRUE(login_views_utils::HasFocusInAnyChildView(primary_password_view));
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, UserSwapFocusesBigView) {
+TEST_F(LockContentsViewUnitTest, UserSwapFocusesBigView) {
   ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  LoadUsers(3);
+  SetUserCount(3);
   LockContentsView::TestApi lock_contents = LockContentsView::TestApi(
       LockScreen::TestApi(LockScreen::Get()).contents_view());
 
diff --git a/ash/login/ui/lock_window_unittest.cc b/ash/login/ui/lock_window_unittest.cc
index 5296834..667d4e8 100644
--- a/ash/login/ui/lock_window_unittest.cc
+++ b/ash/login/ui/lock_window_unittest.cc
@@ -21,7 +21,7 @@
       LockScreen::TestApi(LockScreen::Get()).contents_view();
   ASSERT_NE(nullptr, lock_contents);
 
-  LoadUsers(1);
+  SetUserCount(1);
 
   LoginBigUserView* auth_view =
       MakeLockContentsViewTestApi(lock_contents).primary_big_view();
diff --git a/ash/login/ui/login_base_bubble_view.cc b/ash/login/ui/login_base_bubble_view.cc
index efe6305..69813f7 100644
--- a/ash/login/ui/login_base_bubble_view.cc
+++ b/ash/login/ui/login_base_bubble_view.cc
@@ -50,6 +50,10 @@
   return nullptr;
 }
 
+bool LoginBaseBubbleView::IsPersistent() const {
+  return false;
+}
+
 void LoginBaseBubbleView::OnBeforeBubbleWidgetInit(
     views::Widget::InitParams* params,
     views::Widget* widget) const {
diff --git a/ash/login/ui/login_base_bubble_view.h b/ash/login/ui/login_base_bubble_view.h
index 4f89b1d..f8aa76e4 100644
--- a/ash/login/ui/login_base_bubble_view.h
+++ b/ash/login/ui/login_base_bubble_view.h
@@ -25,6 +25,9 @@
   // Returns the button responsible for opening this bubble.
   virtual LoginButton* GetBubbleOpener() const;
 
+  // Returns whether or not this bubble should show persistently.
+  virtual bool IsPersistent() const;
+
   // views::BubbleDialogDelegateView:
   void OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
                                 views::Widget* widget) const override;
diff --git a/ash/login/ui/login_bubble.cc b/ash/login/ui/login_bubble.cc
index aae18d70..8d2a7346 100644
--- a/ash/login/ui/login_bubble.cc
+++ b/ash/login/ui/login_bubble.cc
@@ -96,8 +96,10 @@
  public:
   LoginErrorBubbleView(views::View* content,
                        views::View* anchor_view,
-                       aura::Window* container)
-      : LoginBaseBubbleView(anchor_view, container) {
+                       aura::Window* container,
+                       bool show_persistently)
+      : LoginBaseBubbleView(anchor_view, container),
+        show_persistently_(show_persistently) {
     set_anchor_view_insets(
         gfx::Insets(kAnchorViewErrorBubbleVerticalSpacingDp, 0));
 
@@ -125,6 +127,9 @@
 
   ~LoginErrorBubbleView() override = default;
 
+  // LoginBaseBubbleView:
+  bool IsPersistent() const override { return show_persistently_; }
+
   // views::View:
   const char* GetClassName() const override { return "LoginErrorBubbleView"; }
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
@@ -132,6 +137,8 @@
   }
 
  private:
+  bool show_persistently_;
+
   DISALLOW_COPY_AND_ASSIGN(LoginErrorBubbleView);
 };
 
@@ -453,14 +460,14 @@
 
 void LoginBubble::ShowErrorBubble(views::View* content,
                                   views::View* anchor_view,
-                                  uint32_t flags) {
+                                  bool show_persistently) {
   if (bubble_view_)
     CloseImmediately();
 
-  flags_ = flags;
   aura::Window* menu_container = Shell::GetContainer(
       Shell::GetPrimaryRootWindow(), kShellWindowId_MenuContainer);
-  bubble_view_ = new LoginErrorBubbleView(content, anchor_view, menu_container);
+  bubble_view_ = new LoginErrorBubbleView(content, anchor_view, menu_container,
+                                          show_persistently);
 
   Show();
 }
@@ -477,7 +484,6 @@
   if (bubble_view_)
     CloseImmediately();
 
-  flags_ = kFlagsNone;
   bubble_view_ = new LoginUserMenuView(
       this, username, email, type, is_owner, anchor_view, bubble_opener,
       show_remove_user, std::move(on_remove_user_warning_shown),
@@ -496,7 +502,6 @@
   if (bubble_view_)
     CloseImmediately();
 
-  flags_ = kFlagsNone;
   bubble_view_ = new LoginTooltipView(message, anchor_view);
   Show();
 }
@@ -505,7 +510,6 @@
   if (bubble_view_)
     CloseImmediately();
 
-  flags_ = kFlagsNone;
   const bool had_focus =
       menu->GetBubbleOpener() && menu->GetBubbleOpener()->HasFocus();
 
@@ -579,7 +583,7 @@
   if (bubble_view_->GetWidget()->IsActive())
     return;
 
-  if (!(flags_ & kFlagPersistent)) {
+  if (!bubble_view_->IsPersistent()) {
     Close();
   }
 }
@@ -603,7 +607,7 @@
   if (gained_focus && bubble_window->Contains(gained_focus))
     return;
 
-  if (!(flags_ & kFlagPersistent))
+  if (!bubble_view_->IsPersistent())
     Close();
 }
 
@@ -645,7 +649,7 @@
       return;
   }
 
-  if (!(flags_ & kFlagPersistent))
+  if (!bubble_view_->IsPersistent())
     Close();
 }
 
@@ -694,7 +698,6 @@
     bubble_view_->GetWidget()->Close();
   is_visible_ = false;
   bubble_view_ = nullptr;
-  flags_ = kFlagsNone;
 }
 
 void LoginBubble::EnsureBubbleInScreen() {
diff --git a/ash/login/ui/login_bubble.h b/ash/login/ui/login_bubble.h
index 56b6b9c..db3619c 100644
--- a/ash/login/ui/login_bubble.h
+++ b/ash/login/ui/login_bubble.h
@@ -40,12 +40,6 @@
 
   static const int kUserMenuRemoveUserButtonIdForTest;
 
-  // Flags passed to ShowErrorBubble().
-  static constexpr uint32_t kFlagsNone = 0;
-  // If set, the shown error bubble will not be closed due to an unrelated user
-  // action - e.g. the bubble will not be closed if the user starts typing.
-  static constexpr uint32_t kFlagPersistent = 1 << 0;
-
   LoginBubble();
   ~LoginBubble() override;
 
@@ -53,7 +47,7 @@
   // |anchor_view| is the anchor for placing the bubble view.
   void ShowErrorBubble(views::View* content,
                        views::View* anchor_view,
-                       uint32_t flags);
+                       bool show_persistently);
 
   // Shows a user menu bubble.
   // |anchor_view| is the anchor for placing the bubble view.
@@ -128,9 +122,6 @@
   // Repositions the bubble view if it extends too far right or down.
   void EnsureBubbleInScreen();
 
-  // Flags passed to ShowErrorBubble().
-  uint32_t flags_ = kFlagsNone;
-
   LoginBaseBubbleView* bubble_view_ = nullptr;
 
   // The status of bubble after animation ends.
diff --git a/ash/login/ui/login_bubble_unittest.cc b/ash/login/ui/login_bubble_unittest.cc
index 07fee4bd..0e51846 100644
--- a/ash/login/ui/login_bubble_unittest.cc
+++ b/ash/login/ui/login_bubble_unittest.cc
@@ -288,7 +288,7 @@
 
   EXPECT_FALSE(bubble_->IsVisible());
   views::Label* error_text = new views::Label(base::ASCIIToUTF16("Error text"));
-  bubble_->ShowErrorBubble(error_text, container_, LoginBubble::kFlagsNone);
+  bubble_->ShowErrorBubble(error_text, container_, false /*show_persistently*/);
   EXPECT_TRUE(bubble_->IsVisible());
 
   // Verifies that key event on a view other than error closes the error bubble.
@@ -302,7 +302,7 @@
 
   EXPECT_FALSE(bubble_->IsVisible());
   views::Label* error_text = new views::Label(base::ASCIIToUTF16("Error text"));
-  bubble_->ShowErrorBubble(error_text, container_, LoginBubble::kFlagsNone);
+  bubble_->ShowErrorBubble(error_text, container_, false /*show_persistently*/);
   EXPECT_TRUE(bubble_->IsVisible());
 
   // Verifies that mouse event on the bubble itself won't close the bubble.
@@ -322,7 +322,7 @@
 
   EXPECT_FALSE(bubble_->IsVisible());
   views::Label* error_text = new views::Label(base::ASCIIToUTF16("Error text"));
-  bubble_->ShowErrorBubble(error_text, container_, LoginBubble::kFlagsNone);
+  bubble_->ShowErrorBubble(error_text, container_, false /*show_persistently*/);
   EXPECT_TRUE(bubble_->IsVisible());
 
   // Verifies that gesture event on the bubble itself won't close the bubble.
@@ -340,8 +340,7 @@
 
   EXPECT_FALSE(bubble_->IsVisible());
   views::Label* error_text = new views::Label(base::ASCIIToUTF16("Error text"));
-  bubble_->ShowErrorBubble(error_text, container_,
-                           LoginBubble::kFlagPersistent);
+  bubble_->ShowErrorBubble(error_text, container_, true /*show_persistently*/);
   EXPECT_TRUE(bubble_->IsVisible());
 
   // Verifies that mouse event on the bubble itself won't close the bubble.
diff --git a/ash/login/ui/login_keyboard_test_base.cc b/ash/login/ui/login_keyboard_test_base.cc
index a2ef470..fed9442 100644
--- a/ash/login/ui/login_keyboard_test_base.cc
+++ b/ash/login/ui/login_keyboard_test_base.cc
@@ -5,7 +5,6 @@
 #include "ash/login/ui/login_keyboard_test_base.h"
 
 #include "ash/keyboard/ash_keyboard_controller.h"
-#include "ash/login/login_screen_controller.h"
 #include "ash/login/mock_login_screen_client.h"
 #include "ash/login/ui/lock_screen.h"
 #include "ash/login/ui/login_test_utils.h"
@@ -29,19 +28,15 @@
 void LoginKeyboardTestBase::SetUp() {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       keyboard::switches::kEnableVirtualKeyboard);
-  AshTestBase::SetUp();
-
-  login_controller_ = Shell::Get()->login_screen_controller();
-  ASSERT_NE(nullptr, login_controller_);
+  LoginTestBase::SetUp();
 
   Shell::Get()->ash_keyboard_controller()->ActivateKeyboard();
 }
 
 void LoginKeyboardTestBase::TearDown() {
   Shell::Get()->ash_keyboard_controller()->DeactivateKeyboard();
-  if (ash::LockScreen::HasInstance())
-    ash::LockScreen::Get()->Destroy();
-  AshTestBase::TearDown();
+
+  LoginTestBase::TearDown();
 }
 
 void LoginKeyboardTestBase::ShowKeyboard() {
@@ -68,57 +63,4 @@
       ->GetBoundsInScreen();
 }
 
-void LoginKeyboardTestBase::ShowLockScreen() {
-  GetSessionControllerClient()->SetSessionState(
-      session_manager::SessionState::LOCKED);
-  // The lock screen can't be shown without a wallpaper.
-  Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
-
-  base::Optional<bool> result;
-  login_controller_->ShowLockScreen(base::BindOnce(
-      [](base::Optional<bool>* result, bool did_show) { *result = did_show; },
-      &result));
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(result.has_value());
-  ASSERT_EQ(*result, true);
-}
-
-void LoginKeyboardTestBase::ShowLoginScreen() {
-  GetSessionControllerClient()->SetSessionState(
-      session_manager::SessionState::LOGIN_PRIMARY);
-  // The login screen can't be shown without a wallpaper.
-  Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
-
-  base::Optional<bool> result;
-  login_controller_->ShowLoginScreen(base::BindOnce(
-      [](base::Optional<bool>* result, bool did_show) { *result = did_show; },
-      &result));
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(result.has_value());
-  ASSERT_EQ(*result, true);
-}
-
-void LoginKeyboardTestBase::LoadUsers(int count) {
-  for (int i = 0; i < count; ++i) {
-    std::string email =
-        base::StrCat({"user", std::to_string(i), "@domain.com "});
-    users_.push_back(CreateUser(email));
-  }
-  ash::LockScreen::Get()->data_dispatcher()->NotifyUsers(users_);
-}
-
-void LoginKeyboardTestBase::LoadPublicAccountUsers(int count) {
-  for (int i = 0; i < count; ++i) {
-    std::string email =
-        base::StrCat({"publicuser", std::to_string(i), "@domain.com"});
-    users_.push_back(CreatePublicAccountUser(email));
-  }
-  ash::LockScreen::Get()->data_dispatcher()->NotifyUsers(users_);
-}
-
-void LoginKeyboardTestBase::LoadUser(const std::string& email) {
-  users_.push_back(CreateUser(email));
-  ash::LockScreen::Get()->data_dispatcher()->NotifyUsers(users_);
-}
-
 }  // namespace ash
diff --git a/ash/login/ui/login_keyboard_test_base.h b/ash/login/ui/login_keyboard_test_base.h
index 36801a9..75a31e1 100644
--- a/ash/login/ui/login_keyboard_test_base.h
+++ b/ash/login/ui/login_keyboard_test_base.h
@@ -7,16 +7,15 @@
 
 #include <memory>
 
+#include "ash/login/ui/login_test_base.h"
 #include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ash/test/ash_test_base.h"
 
 namespace ash {
 
-class LoginScreenController;
-
 // Base test fixture for testing the views-based login and lock screens with
 // virtual keyboard.
-class LoginKeyboardTestBase : public AshTestBase {
+class LoginKeyboardTestBase : public LoginTestBase {
  public:
   LoginKeyboardTestBase();
   ~LoginKeyboardTestBase() override;
@@ -33,29 +32,11 @@
   // Returns bounds of the keyboard in screen coordinate space.
   gfx::Rect GetKeyboardBoundsInScreen() const;
 
-  // Shows lock screen. Asserts that lock screen is shown. To stop execution of
-  // the test on failed assertion use ASSERT_NO_FATAL_FAILURE macro.
-  void ShowLockScreen();
-
-  // Shows login screen. Asserts that login screen is shown. To stop execution
-  // of the test on failed assertion use ASSERT_NO_FATAL_FAILURE macro.
-  void ShowLoginScreen();
-
-  // Loads the number of test users specified by |count|.
-  void LoadUsers(int count);
-
-  // Loads the number of test public account users specified by |count|.
-  void LoadPublicAccountUsers(int count);
-
-  // Loads user with the specified |email|.
-  void LoadUser(const std::string& email);
-
   // AshTestBase:
   void SetUp() override;
   void TearDown() override;
 
  private:
-  LoginScreenController* login_controller_ = nullptr;
   std::vector<mojom::LoginUserInfoPtr> users_;
 
   DISALLOW_COPY_AND_ASSIGN(LoginKeyboardTestBase);
diff --git a/ash/login/ui/login_test_base.cc b/ash/login/ui/login_test_base.cc
index e246616d..bfba837 100644
--- a/ash/login/ui/login_test_base.cc
+++ b/ash/login/ui/login_test_base.cc
@@ -6,10 +6,14 @@
 
 #include <string>
 
+#include "ash/login/login_screen_controller.h"
+#include "ash/login/ui/lock_screen.h"
 #include "ash/login/ui/login_test_utils.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/interfaces/tray_action.mojom.h"
+#include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/strings/strcat.h"
 #include "services/ws/public/cpp/property_type_converters.h"
 #include "services/ws/public/mojom/window_manager.mojom.h"
@@ -42,6 +46,36 @@
 
 LoginTestBase::~LoginTestBase() = default;
 
+void LoginTestBase::ShowLockScreen() {
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOCKED);
+  // The lock screen can't be shown without a wallpaper.
+  Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
+
+  base::Optional<bool> result;
+  Shell::Get()->login_screen_controller()->ShowLockScreen(base::BindOnce(
+      [](base::Optional<bool>* result, bool did_show) { *result = did_show; },
+      &result));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(result.has_value());
+  ASSERT_EQ(*result, true);
+}
+
+void LoginTestBase::ShowLoginScreen() {
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
+  // The login screen can't be shown without a wallpaper.
+  Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
+
+  base::Optional<bool> result;
+  Shell::Get()->login_screen_controller()->ShowLoginScreen(base::BindOnce(
+      [](base::Optional<bool>* result, bool did_show) { *result = did_show; },
+      &result));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(result.has_value());
+  ASSERT_EQ(*result, true);
+}
+
 void LoginTestBase::SetWidget(std::unique_ptr<views::Widget> widget) {
   EXPECT_FALSE(widget_) << "SetWidget can only be called once.";
   widget_ = std::move(widget);
@@ -76,7 +110,7 @@
 
   users_.erase(users_.begin() + count, users_.end());
   // Notify any listeners that the user count has changed.
-  data_dispatcher_.NotifyUsers(users_);
+  data_dispatcher()->NotifyUsers(users_);
 }
 
 void LoginTestBase::AddUsers(size_t num_users) {
@@ -87,7 +121,12 @@
   }
 
   // Notify any listeners that the user count has changed.
-  data_dispatcher_.NotifyUsers(users_);
+  data_dispatcher()->NotifyUsers(users_);
+}
+
+void LoginTestBase::AddUserByEmail(const std::string& email) {
+  users_.push_back(CreateUser(email));
+  data_dispatcher()->NotifyUsers(users_);
 }
 
 void LoginTestBase::AddPublicAccountUsers(size_t num_public_accounts) {
@@ -98,12 +137,20 @@
   }
 
   // Notify any listeners that the user count has changed.
-  data_dispatcher_.NotifyUsers(users_);
+  data_dispatcher()->NotifyUsers(users_);
+}
+
+LoginDataDispatcher* LoginTestBase::data_dispatcher() {
+  return LockScreen::HasInstance() ? LockScreen::Get()->data_dispatcher()
+                                   : &data_dispatcher_;
 }
 
 void LoginTestBase::TearDown() {
   widget_.reset();
 
+  if (LockScreen::HasInstance())
+    LockScreen::Get()->Destroy();
+
   AshTestBase::TearDown();
 }
 
diff --git a/ash/login/ui/login_test_base.h b/ash/login/ui/login_test_base.h
index 298d32a..b914ae97 100644
--- a/ash/login/ui/login_test_base.h
+++ b/ash/login/ui/login_test_base.h
@@ -26,6 +26,13 @@
   LoginTestBase();
   ~LoginTestBase() override;
 
+  // Shows a full Lock/Login screen. These methods are useful for when we want
+  // to test interactions between multiple lock screen components, or when some
+  // component needs to be able to talk directly to the lockscreen (e.g. getting
+  // the ScreenType).
+  void ShowLockScreen();
+  void ShowLoginScreen();
+
   // Sets the primary test widget. The widget can be retrieved using |widget()|.
   // This can be used to make a widget scoped to the whole test, e.g. if the
   // widget is created in a SetUp override.
@@ -45,6 +52,9 @@
   // |data_dispatcher()|.
   void AddUsers(size_t num_users);
 
+  // Add a single user with the specified |email|.
+  void AddUserByEmail(const std::string& email);
+
   // Append number of |num_public_accounts| public account users.
   // Changes the active number of users. Fires an event on
   // |data_dispatcher()|.
@@ -54,7 +64,10 @@
 
   const std::vector<mojom::LoginUserInfoPtr>& users() const { return users_; }
 
-  LoginDataDispatcher* data_dispatcher() { return &data_dispatcher_; }
+  // If the LockScreen is instantiated, returns its data dispatcher. Otherwise,
+  // returns a standalone instance.
+  // TODO(crbug/906676): rename this method to DataDispatcher.
+  LoginDataDispatcher* data_dispatcher();
 
   // AshTestBase:
   void TearDown() override;
diff --git a/ash/multi_user/multi_user_window_manager.cc b/ash/multi_user/multi_user_window_manager.cc
index 214b028..bafe7ee 100644
--- a/ash/multi_user/multi_user_window_manager.cc
+++ b/ash/multi_user/multi_user_window_manager.cc
@@ -15,12 +15,13 @@
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/ws/window_service_owner.h"
 #include "base/auto_reset.h"
 #include "base/macros.h"
-#include "ui/aura/client/aura_constants.h"
+#include "services/ws/window_service.h"
+#include "ui/aura/mus/window_mus.h"
+#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_event_dispatcher.h"
-#include "ui/aura/window_tree_host.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/events/event.h"
 #include "ui/views/mus/mus_client.h"
@@ -82,6 +83,35 @@
   return wallpaper_user_info;
 }
 
+// If |window| has a remote client, this converts it to the remote window used
+// by the delegate. This effectively undoes the mapping that
+// MultiUserWindowManagerBridge does.
+// TODO: remove this and instead notify about changes to these windows over a
+// mojom. https://crbug.com/875111.
+aura::Window* MapWindowIfNecessary(aura::Window* window) {
+  if (!ws::WindowService::HasRemoteClient(window) ||
+      !views::MusClient::Exists()) {
+    return window;
+  }
+
+  const ws::Id window_id = Shell::Get()
+                               ->window_service_owner()
+                               ->window_service()
+                               ->GetTopLevelWindowId(window);
+  if (window_id == ws::kInvalidTransportId)
+    return window;
+
+  // children[0] is used to deal with DesktopNativeWidgetAura. In particular,
+  // client code generally expects to see the first child, which corresponds to
+  // Widget::GetNativeWindow(), when using DesktopNativeWidgetAura.
+  aura::WindowMus* window_mus =
+      views::MusClient::Get()->window_tree_client()->GetWindowByServerId(
+          window_id);
+  return window_mus && !window_mus->GetWindow()->children().empty()
+             ? window_mus->GetWindow()->children()[0]
+             : window;
+}
+
 }  // namespace
 
 // A class to temporarily change the animation properties for a window.
@@ -134,13 +164,12 @@
     animation_->CancelAnimation();
 
   // Remove all window observers.
-  WindowToEntryMap::iterator window = window_to_entry_.begin();
-  while (window != window_to_entry_.end()) {
+  while (!window_to_entry_.empty()) {
     // Explicitly remove this from window observer list since OnWindowDestroyed
     // no longer does that.
-    window->first->RemoveObserver(this);
-    OnWindowDestroyed(window->first);
-    window = window_to_entry_.begin();
+    aura::Window* window = window_to_entry_.begin()->first;
+    window->RemoveObserver(this);
+    OnWindowDestroyed(window);
   }
 
   Shell::Get()->session_controller()->RemoveObserver(this);
@@ -154,7 +183,8 @@
 }
 
 void MultiUserWindowManager::SetWindowOwner(aura::Window* window,
-                                            const AccountId& account_id) {
+                                            const AccountId& account_id,
+                                            bool show_for_current_user) {
   // Make sure the window is valid and there was no owner yet.
   DCHECK(window);
   DCHECK(account_id.is_valid());
@@ -162,10 +192,13 @@
   if (GetWindowOwner(window) == account_id)
     return;
   DCHECK(GetWindowOwner(window).empty());
-  window_to_entry_[window] = new WindowEntry(account_id);
+  std::unique_ptr<WindowEntry> window_entry_ptr =
+      std::make_unique<WindowEntry>(account_id);
+  WindowEntry* window_entry = window_entry_ptr.get();
+  window_to_entry_[window] = std::move(window_entry_ptr);
 
   // Remember the initial visibility of the window.
-  window_to_entry_[window]->set_show(window->IsVisible());
+  window_entry->set_show(window->IsVisible());
 
   // Add observers to track state changes.
   window->AddObserver(this);
@@ -173,8 +206,8 @@
 
   // Check if this window was created due to a user interaction. If it was,
   // transfer it to the current user.
-  if (window->GetProperty(aura::client::kCreatedByUserGesture))
-    window_to_entry_[window]->set_show_for_user(current_account_id_);
+  if (show_for_current_user)
+    window_entry->set_show_for_user(current_account_id_);
 
   // Add all transient children to our set of windows. Note that the function
   // will add the children but not the owner to the transient children map.
@@ -206,9 +239,8 @@
 }
 
 bool MultiUserWindowManager::AreWindowsSharedAmongUsers() const {
-  WindowToEntryMap::const_iterator it = window_to_entry_.begin();
-  for (; it != window_to_entry_.end(); ++it) {
-    if (it->second->owner() != it->second->show_for_user())
+  for (auto& window_pair : window_to_entry_) {
+    if (window_pair.second->owner() != window_pair.second->show_for_user())
       return true;
   }
   return false;
@@ -269,8 +301,6 @@
     return;
   }
   ::wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
-  // Remove the window from the owners list.
-  delete window_to_entry_[window];
   window_to_entry_.erase(window);
 }
 
@@ -340,7 +370,7 @@
 }
 
 void MultiUserWindowManager::OnTabletModeStarted() {
-  for (auto entry : window_to_entry_)
+  for (auto& entry : window_to_entry_)
     Shell::Get()->tablet_mode_controller()->AddWindow(entry.first);
 }
 
@@ -372,23 +402,25 @@
   if (account_id != owner && minimized)
     return false;
 
-  WindowToEntryMap::iterator it = window_to_entry_.find(window);
-  it->second->set_show_for_user(account_id);
+  WindowEntry* window_entry = window_to_entry_[window].get();
+  window_entry->set_show_for_user(account_id);
 
   const bool teleported = !IsWindowOnDesktopOfUser(window, owner);
 
   // Show the window if the added user is the current one.
   if (account_id == current_account_id_) {
     // Only show the window if it should be shown according to its state.
-    if (it->second->show())
+    if (window_entry->show())
       SetWindowVisibility(window, true, kTeleportAnimationTime);
   } else {
     SetWindowVisibility(window, false, kTeleportAnimationTime);
   }
 
   // Notify entry change.
-  if (delegate_)
-    delegate_->OnOwnerEntryChanged(window, account_id, minimized, teleported);
+  if (delegate_) {
+    delegate_->OnOwnerEntryChanged(MapWindowIfNecessary(window), account_id,
+                                   minimized, teleported);
+  }
   return true;
 }
 
diff --git a/ash/multi_user/multi_user_window_manager.h b/ash/multi_user/multi_user_window_manager.h
index e336b5d..8420d59 100644
--- a/ash/multi_user/multi_user_window_manager.h
+++ b/ash/multi_user/multi_user_window_manager.h
@@ -7,8 +7,6 @@
 
 #include <map>
 #include <memory>
-#include <set>
-#include <string>
 
 #include "ash/ash_export.h"
 #include "ash/session/session_observer.h"
@@ -64,8 +62,11 @@
 
   // Associates a window with a particular account. This may result in hiding
   // |window|. This should *not* be called more than once with a different
-  // account.
-  void SetWindowOwner(aura::Window* window, const AccountId& account_id);
+  // account. If |show_for_current_user| is true, this sets the 'shown'
+  // account to the current account.
+  void SetWindowOwner(aura::Window* window,
+                      const AccountId& account_id,
+                      bool show_for_current_user);
 
   // Sets the 'shown' account for a window. See class description for details on
   // what the 'shown' account is. This function may trigger changing the active
@@ -137,8 +138,8 @@
     DISALLOW_COPY_AND_ASSIGN(WindowEntry);
   };
 
-  // TODO: make map to std::unique_ptr<WindowEntry>.
-  using WindowToEntryMap = std::map<aura::Window*, WindowEntry*>;
+  using WindowToEntryMap =
+      std::map<aura::Window*, std::unique_ptr<WindowEntry>>;
 
   const AccountId& GetWindowOwner(aura::Window* window) const;
 
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index d9c4b81..d44aa88 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -42,6 +42,7 @@
     "login_user_info.mojom",
     "media.mojom",
     "menu.mojom",
+    "multi_user_window_manager.mojom",
     "new_window.mojom",
     "night_light_controller.mojom",
     "note_taking_controller.mojom",
diff --git a/ash/public/interfaces/multi_user_window_manager.mojom b/ash/public/interfaces/multi_user_window_manager.mojom
new file mode 100644
index 0000000..43060e2
--- /dev/null
+++ b/ash/public/interfaces/multi_user_window_manager.mojom
@@ -0,0 +1,22 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.mojom;
+
+import "components/account_id/interfaces/account_id.mojom";
+
+// Used to assign windows to user accounts so that ash shows the appropriate set
+// of windows based on the active user.
+interface MultiUserWindowManager {
+  // Associates a window with an account. If |show_for_current_user| is true,
+  // the window is associated with |account_id|, but is shown for the currently
+  // active user.
+  SetWindowOwner(uint64 window_id,
+                 signin.mojom.AccountId account_id,
+                 bool show_for_current_user);
+
+  // Shows a previously registered window for the specified account.
+  ShowWindowForUser(uint64 window_id,
+                    signin.mojom.AccountId account_id);
+};
diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h
index d1cb05526..d20aece2 100644
--- a/ash/wm/overview/window_selector.h
+++ b/ash/wm/overview/window_selector.h
@@ -236,6 +236,8 @@
     return split_view_drag_indicators_.get();
   }
 
+  views::Widget* text_filter_widget() { return text_filter_widget_.get(); }
+
   int text_filter_bottom() const { return text_filter_bottom_; }
 
   EnterExitOverviewType enter_exit_overview_type() const {
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 4a0bc25..c5254a27 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -697,7 +697,15 @@
 void SplitViewController::OnWindowActivated(ActivationReason reason,
                                             aura::Window* gained_active,
                                             aura::Window* lost_active) {
-  DCHECK(IsSplitViewModeActive());
+  // This may be called while SnapWindow is still underway because SnapWindow
+  // will end the overview start animations which will cause the overview text
+  // filter to be activated.
+  aura::Window* text_filter_widget =
+      GetWindowSelector()
+          ? GetWindowSelector()->text_filter_widget()->GetNativeWindow()
+          : nullptr;
+  DCHECK(IsSplitViewModeActive() ||
+         (text_filter_widget && text_filter_widget == gained_active));
 
   // If |gained_active| was activated as a side effect of a window disposition
   // change, do nothing. For example, when a snapped window is closed, another
diff --git a/ash/ws/multi_user_window_manager_bridge.cc b/ash/ws/multi_user_window_manager_bridge.cc
new file mode 100644
index 0000000..fb295855
--- /dev/null
+++ b/ash/ws/multi_user_window_manager_bridge.cc
@@ -0,0 +1,53 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/ws/multi_user_window_manager_bridge.h"
+
+#include "ash/multi_user/multi_user_window_manager.h"
+#include "services/ws/window_tree.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+
+MultiUserWindowManagerBridge::MultiUserWindowManagerBridge(
+    ws::WindowTree* window_tree,
+    mojo::ScopedInterfaceEndpointHandle handle)
+    : window_tree_(window_tree),
+      binding_(this,
+               mojo::AssociatedInterfaceRequest<mojom::MultiUserWindowManager>(
+                   std::move(handle))) {}
+
+MultiUserWindowManagerBridge::~MultiUserWindowManagerBridge() = default;
+
+void MultiUserWindowManagerBridge::SetWindowOwner(ws::Id window_id,
+                                                  const AccountId& account_id,
+                                                  bool show_for_current_user) {
+  // At this time this is only called once MultiUserWindowManager has been
+  // created. This needs to be fixed for the multi-process case.
+  // http://crbug.com/875111.
+  DCHECK(ash::MultiUserWindowManager::Get());
+  aura::Window* window = window_tree_->GetWindowByTransportId(window_id);
+  if (window && window_tree_->IsTopLevel(window)) {
+    ash::MultiUserWindowManager::Get()->SetWindowOwner(window, account_id,
+                                                       show_for_current_user);
+  } else {
+    DVLOG(1) << "SetWindowOwner passed invalid window, id=" << window_id;
+  }
+}
+
+void MultiUserWindowManagerBridge::ShowWindowForUser(
+    ws::Id window_id,
+    const AccountId& account_id) {
+  // At this time this is only called once MultiUserWindowManager has been
+  // created. This needs to be fixed for the multi-process case.
+  // http://crbug.com/875111.
+  DCHECK(ash::MultiUserWindowManager::Get());
+  aura::Window* window = window_tree_->GetWindowByTransportId(window_id);
+  if (window && window_tree_->IsTopLevel(window))
+    ash::MultiUserWindowManager::Get()->ShowWindowForUser(window, account_id);
+  else
+    DVLOG(1) << "ShowWindowForUser passed invalid window, id=" << window_id;
+}
+
+}  // namespace ash
diff --git a/ash/ws/multi_user_window_manager_bridge.h b/ash/ws/multi_user_window_manager_bridge.h
new file mode 100644
index 0000000..d74ece57
--- /dev/null
+++ b/ash/ws/multi_user_window_manager_bridge.h
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WS_MULTI_USER_WINDOW_MANAGER_BRIDGE_H_
+#define ASH_WS_MULTI_USER_WINDOW_MANAGER_BRIDGE_H_
+
+#include "ash/public/interfaces/multi_user_window_manager.mojom.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "services/ws/common/types.h"
+#include "services/ws/window_manager_interface.h"
+
+namespace mojo {
+class ScopedInterfaceEndpointHandle;
+}
+
+namespace ws {
+class WindowTree;
+}
+
+namespace ash {
+
+// Trivially forwards calls to MultiUserWindowManager.
+class MultiUserWindowManagerBridge : public mojom::MultiUserWindowManager,
+                                     public ws::WindowManagerInterface {
+ public:
+  MultiUserWindowManagerBridge(ws::WindowTree* window_tree,
+                               mojo::ScopedInterfaceEndpointHandle handle);
+  ~MultiUserWindowManagerBridge() override;
+
+  // mojom::MultiUserWindowManager overrides:
+  void SetWindowOwner(ws::Id window_id,
+                      const AccountId& account_id,
+                      bool show_for_current_user) override;
+  void ShowWindowForUser(ws::Id window_id,
+                         const AccountId& account_id) override;
+
+ private:
+  ws::WindowTree* window_tree_;
+  mojo::AssociatedBinding<mojom::MultiUserWindowManager> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerBridge);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WS_MULTI_USER_WINDOW_MANAGER_BRIDGE_H_
diff --git a/ash/ws/window_lookup.cc b/ash/ws/window_lookup.cc
index 4b3444e..9387bbd5c 100644
--- a/ash/ws/window_lookup.cc
+++ b/ash/ws/window_lookup.cc
@@ -18,12 +18,5 @@
       ->GetWindowByClientId(transport_id);
 }
 
-ws::ClientSpecificId GetFirstWindowTreeClientId() {
-  return Shell::Get()
-      ->window_service_owner()
-      ->window_service()
-      ->GetFirstWindowTreeClientId();
-}
-
 }  // namespace window_lookup
 }  // namespace ash
diff --git a/ash/ws/window_lookup.h b/ash/ws/window_lookup.h
index 2523a1a..0c1c2c187 100644
--- a/ash/ws/window_lookup.h
+++ b/ash/ws/window_lookup.h
@@ -20,10 +20,6 @@
 // Returns the aura::Window by transport id.
 ASH_EXPORT aura::Window* GetWindowByClientId(ws::Id transport_id);
 
-// Returns the id of the first WindowTreeClient. That is, the id assigned to
-// the first client that connects to the WindowService.
-ASH_EXPORT ws::ClientSpecificId GetFirstWindowTreeClientId();
-
 }  // namespace window_lookup
 }  // namespace ash
 
diff --git a/ash/ws/window_service_delegate_impl.cc b/ash/ws/window_service_delegate_impl.cc
index 3231993a..072c3bf 100644
--- a/ash/ws/window_service_delegate_impl.cc
+++ b/ash/ws/window_service_delegate_impl.cc
@@ -16,6 +16,7 @@
 #include "ash/wm/window_finder.h"
 #include "ash/wm/window_util.h"
 #include "ash/ws/ash_window_manager.h"
+#include "ash/ws/multi_user_window_manager_bridge.h"
 #include "base/bind.h"
 #include "mojo/public/cpp/bindings/map.h"
 #include "services/ws/public/mojom/window_manager.mojom.h"
@@ -230,10 +231,14 @@
     ws::WindowTree* tree,
     const std::string& name,
     mojo::ScopedInterfaceEndpointHandle handle) {
-  if (name != mojom::AshWindowManager::Name_)
-    return nullptr;
+  if (name == mojom::AshWindowManager::Name_)
+    return std::make_unique<AshWindowManager>(tree, std::move(handle));
 
-  return std::make_unique<AshWindowManager>(tree, std::move(handle));
+  if (name == mojom::MultiUserWindowManager::Name_) {
+    return std::make_unique<MultiUserWindowManagerBridge>(tree,
+                                                          std::move(handle));
+  }
+  return nullptr;
 }
 
 }  // namespace ash
diff --git a/base/android/jni_generator/BUILD.gn b/base/android/jni_generator/BUILD.gn
index a37726b..6d92eed 100644
--- a/base/android/jni_generator/BUILD.gn
+++ b/base/android/jni_generator/BUILD.gn
@@ -80,6 +80,14 @@
   ]
 }
 
+java_cpp_template("processor_args_java") {
+  package_path = "org/chromium/jni_generator"
+  sources = [
+    "ProcessorArgs.template",
+  ]
+  defines = [ "IS_JAVA_DEBUG_VALUE=$is_java_debug" ]
+}
+
 java_annotation_processor("jni_processor") {
   java_files = [ "java/src/org/chromium/jni_generator/JniProcessor.java" ]
 
@@ -93,4 +101,6 @@
     "//third_party/auto:auto_service_java",
     "//third_party/guava:guava_java",
   ]
+
+  srcjar_deps = [ ":processor_args_java" ]
 }
diff --git a/base/android/jni_generator/ProcessorArgs.template b/base/android/jni_generator/ProcessorArgs.template
new file mode 100644
index 0000000..bbd4c4b
--- /dev/null
+++ b/base/android/jni_generator/ProcessorArgs.template
@@ -0,0 +1,9 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.jni_generator;
+
+class ProcessorArgs {
+    public static final boolean IS_JAVA_DEBUG = IS_JAVA_DEBUG_VALUE;
+}
diff --git a/base/android/jni_generator/java/src/org/chromium/jni_generator/JniProcessor.java b/base/android/jni_generator/java/src/org/chromium/jni_generator/JniProcessor.java
index 48edf19..4956c65 100644
--- a/base/android/jni_generator/java/src/org/chromium/jni_generator/JniProcessor.java
+++ b/base/android/jni_generator/java/src/org/chromium/jni_generator/JniProcessor.java
@@ -75,7 +75,7 @@
     private static MessageDigest sNativeMethodHashFunction;
 
     // If true, native methods in GEN_JNI will be named as a hash of their descriptor.
-    private static final boolean USE_HASH_FOR_METHODS = false;
+    private static final boolean USE_HASH_FOR_METHODS = !ProcessorArgs.IS_JAVA_DEBUG;
 
     // Limits the number characters of the Base64 encoded hash
     // of the method descriptor used as name of the generated
diff --git a/base/android/jni_generator/jni_registration_generator.py b/base/android/jni_generator/jni_registration_generator.py
index 45abb511..19e85e4 100755
--- a/base/android/jni_generator/jni_registration_generator.py
+++ b/base/android/jni_generator/jni_registration_generator.py
@@ -10,6 +10,7 @@
 to register all native methods that exist within an application."""
 
 import argparse
+import functools
 import multiprocessing
 import os
 import string
@@ -34,7 +35,11 @@
 ]
 
 
-def _Generate(java_file_paths, srcjar_path, header_path=None, namespace=''):
+def _Generate(java_file_paths,
+              srcjar_path,
+              use_proxy_hash=False,
+              header_path=None,
+              namespace=''):
   """Generates files required to perform JNI registration.
 
   Generates a srcjar containing a single class, GEN_JNI, that contains all
@@ -53,34 +58,39 @@
   # Without multiprocessing, script takes ~13 seconds for chrome_public_apk
   # on a z620. With multiprocessing, takes ~2 seconds.
   pool = multiprocessing.Pool()
-  results = [d for d in pool.imap_unordered(_DictForPath, java_file_paths) if d]
+
+  results = []
+  for d in pool.imap_unordered(
+      functools.partial(_DictForPath, use_proxy_hash=use_proxy_hash),
+      java_file_paths):
+    if d:
+      results.append(d)
   pool.close()
 
   # Sort to make output deterministic.
   results.sort(key=lambda d: d['FULL_CLASS_NAME'])
 
-  if header_path:
-    combined_dict = {}
-    for key in MERGEABLE_KEYS:
-      combined_dict[key] = ''.join(d.get(key, '') for d in results)
+  combined_dict = {}
+  for key in MERGEABLE_KEYS:
+    combined_dict[key] = ''.join(d.get(key, '') for d in results)
 
+  if header_path:
     combined_dict['HEADER_GUARD'] = \
         os.path.splitext(header_path)[0].replace('/', '_').upper() + '_'
     combined_dict['NAMESPACE'] = namespace
-
     header_content = CreateFromDict(combined_dict)
     with build_utils.AtomicOutput(header_path) as f:
       f.write(header_content)
 
   with build_utils.AtomicOutput(srcjar_path) as f:
     with zipfile.ZipFile(f, 'w') as srcjar:
-      # TODO(abenner): Write GEN_JNI.java here.
-      # build_utils.AddToZipHermetic(srcjar, 'org/chromium/base/GEN_JNI.java',
-      #     data='$CONTENT')
-      pass
+      build_utils.AddToZipHermetic(
+          srcjar,
+          'org/chromium/base/natives/GEN_JNI.java',
+          data=CreateProxyJavaFromDict(combined_dict))
 
 
-def _DictForPath(path):
+def _DictForPath(path, use_proxy_hash=False):
   with open(path) as f:
     contents = jni_generator.RemoveComments(f.read())
     if '@JniIgnoreNatives' in contents:
@@ -93,7 +103,8 @@
   natives += jni_generator.NativeProxyHelpers.ExtractStaticProxyNatives(
       fully_qualified_class=fully_qualified_class,
       contents=contents,
-      ptr_type='long')
+      ptr_type='long',
+      use_hash=use_proxy_hash)
   if len(natives) == 0:
     return None
   namespace = jni_generator.ExtractJNINamespace(contents)
@@ -516,6 +527,11 @@
                           default='',
                           help='Namespace to wrap the registration functions '
                           'into.')
+  arg_parser.add_argument(
+      '--use_proxy_hash',
+      action='store_true',
+      help='Enables hashing of the native declaration '
+      'for methods in an @JniNatives interface')
   args = arg_parser.parse_args(build_utils.ExpandFileArgs(argv[1:]))
   args.sources_files = build_utils.ParseGnList(args.sources_files)
 
@@ -529,6 +545,7 @@
   _Generate(
       java_file_paths,
       args.srcjar_path,
+      use_proxy_hash=args.use_proxy_hash,
       header_path=args.header_path,
       namespace=args.namespace)
 
diff --git a/base/profiler/native_stack_sampler_win.cc b/base/profiler/native_stack_sampler_win.cc
index d7d6c31..4fbf4027f 100644
--- a/base/profiler/native_stack_sampler_win.cc
+++ b/base/profiler/native_stack_sampler_win.cc
@@ -355,7 +355,8 @@
   uintptr_t bottom = 0u;
 
   {
-    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"), "SuspendThread");
+    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
+                 "SuspendThread");
     {
       ScopedSuspendThread suspend_thread(thread_handle);
 
@@ -393,7 +394,8 @@
     test_delegate->OnPreStackWalk();
 
   {
-    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"), "RecordStack");
+    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
+                 "RecordStack");
 
     RewritePointersToStackMemory(top, bottom, &thread_context,
                                  stack_copy_buffer);
@@ -452,7 +454,7 @@
 std::vector<Frame> NativeStackSamplerWin::RecordStackFrames(
     StackBuffer* stack_buffer,
     ProfileBuilder* profile_builder) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
                "NativeStackSamplerWin::RecordStackFrames");
   DCHECK(stack_buffer);
 
@@ -476,7 +478,7 @@
 
 std::vector<Frame> NativeStackSamplerWin::CreateFrames(
     const std::vector<RecordedFrame>& stack) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
                "NativeStackSamplerWin::CreateFrames");
 
   std::vector<Frame> frames;
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
index 4cb1748..5572409 100644
--- a/base/trace_event/builtin_categories.h
+++ b/base/trace_event/builtin_categories.h
@@ -161,6 +161,7 @@
   X(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"))              \
   X(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.now"))                 \
   X(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"))                           \
+  X(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"))                     \
   X(TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"))                    \
   X(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"))                      \
   X(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.frame"))                \
diff --git a/base/trace_event/category_registry.cc b/base/trace_event/category_registry.cc
index 2e4ef47..3002f49f 100644
--- a/base/trace_event/category_registry.cc
+++ b/base/trace_event/category_registry.cc
@@ -122,9 +122,9 @@
 }
 
 // static
-bool CategoryRegistry::IsBuiltinCategory(const TraceCategory* category) {
+bool CategoryRegistry::IsMetaCategory(const TraceCategory* category) {
   DCHECK(IsValidCategoryPtr(category));
-  return category < &categories_[BuiltinCategories::Size()];
+  return category <= kCategoryMetadata;
 }
 
 // static
diff --git a/base/trace_event/category_registry.h b/base/trace_event/category_registry.h
index ddf346c98..b820303 100644
--- a/base/trace_event/category_registry.h
+++ b/base/trace_event/category_registry.h
@@ -83,7 +83,9 @@
 #endif
   }
 
-  static bool IsBuiltinCategory(const TraceCategory*);
+  // Returns whether |category| points at one of the meta categories that
+  // shouldn't be displayed in the tracing UI.
+  static bool IsMetaCategory(const TraceCategory* category);
 
  private:
   friend class TraceCategoryTest;
diff --git a/base/trace_event/trace_category_unittest.cc b/base/trace_event/trace_category_unittest.cc
index ed6fda9..eff06f7 100644
--- a/base/trace_event/trace_category_unittest.cc
+++ b/base/trace_event/trace_category_unittest.cc
@@ -112,10 +112,10 @@
   int num_test_categories_seen = 0;
   for (const TraceCategory& cat : GetAllCategories()) {
     if (strcmp(cat.name(), kMetadataName) == 0)
-      ASSERT_TRUE(CategoryRegistry::IsBuiltinCategory(&cat));
+      ASSERT_TRUE(CategoryRegistry::IsMetaCategory(&cat));
 
     if (strncmp(cat.name(), "__test_basic_", 13) == 0) {
-      ASSERT_FALSE(CategoryRegistry::IsBuiltinCategory(&cat));
+      ASSERT_FALSE(CategoryRegistry::IsMetaCategory(&cat));
       num_test_categories_seen++;
     }
   }
diff --git a/base/trace_event/trace_event_argument.h b/base/trace_event/trace_event_argument.h
deleted file mode 100644
index 2d2eb54..0000000
--- a/base/trace_event/trace_event_argument.h
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
-#define BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
-
-// TODO(crbug/898787): Update callers to use traced_value.h instead.
-#include "base/trace_event/traced_value.h"
-
-#endif  // BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 998d78e..210b5e6 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -562,7 +562,7 @@
 void TraceLog::GetKnownCategoryGroups(
     std::vector<std::string>* category_groups) {
   for (const auto& category : CategoryRegistry::GetAllCategories()) {
-    if (!CategoryRegistry::IsBuiltinCategory(&category))
+    if (!CategoryRegistry::IsMetaCategory(&category))
       category_groups->push_back(category.name());
   }
 }
diff --git a/build/android/gyp/create_app_bundle.py b/build/android/gyp/create_app_bundle.py
index f3224ba..c787bbd 100755
--- a/build/android/gyp/create_app_bundle.py
+++ b/build/android/gyp/create_app_bundle.py
@@ -172,8 +172,7 @@
   locales#<language>/<locale>.pak, where <language> is the language code
   from the locale.
 
-  Returns a list of paths. The language split should be duplicated at all the
-  returned paths.
+  Returns new path.
   """
   if not src_path.startswith(_LOCALES_SUBDIR) or not src_path.endswith('.pak'):
     return [src_path]
@@ -189,11 +188,16 @@
   else:
     android_language = android_locale
 
-  result_paths = ['assets/locales#lang_%s/%s.pak' % (android_language, locale)]
   if android_language == _FALLBACK_LANGUAGE:
-    result_paths.append('assets/locales/%s.pak' % locale)
+    # Fallback language .pak files must be placed in a different directory
+    # to ensure they are always stored in the base module.
+    result_path = 'assets/fallback-locales/%s.pak' % locale
+  else:
+    # Other language .pak files go into a language-specific asset directory
+    # that bundletool will store in separate split APKs.
+    result_path = 'assets/locales#lang_%s/%s.pak' % (android_language, locale)
 
-  return result_paths
+  return result_path
 
 
 def _SplitModuleForAssetTargeting(src_module_zip, tmp_dir, split_dimensions):
@@ -230,16 +234,15 @@
         src_path = info.filename
         is_compressed = info.compress_type != zipfile.ZIP_STORED
 
-        dst_paths = [src_path]
+        dst_path = src_path
         if src_path in language_files:
-          dst_paths = _RewriteLanguageAssetPath(src_path)
+          dst_path = _RewriteLanguageAssetPath(src_path)
 
-        for dst_path in dst_paths:
-          build_utils.AddToZipHermetic(
-              dst_zip,
-              dst_path,
-              data=src_zip.read(src_path),
-              compress=is_compressed)
+        build_utils.AddToZipHermetic(
+            dst_zip,
+            dst_path,
+            data=src_zip.read(src_path),
+            compress=is_compressed)
 
     return tmp_zip
 
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 38c2c50..701e77e 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -271,10 +271,6 @@
   <issue id="ResourceType" severity="Error">
     <ignore regexp="/javatests/"/>
   </issue>
-  <issue id="RedundantNamespace">
-    <!-- Please do not add any additional suppressions here with the exception of the 1 file below -->
-    <ignore regexp="chrome/android/java/res_autofill_assistant/layout/init_screen.xml"/>
-  </issue>
   <!-- TODO(crbug.com/831774): Play Services starts complaining about RestrictedApi. Needs investigation -->
   <issue id="RestrictedApi" severity="ignore"/>
   <issue id="RtlCompat" severity="ignore"/>
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 8417916..ea79cb1 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -249,6 +249,10 @@
         rebase_path(_jni_generator_include, _jni_output_dir),
       ]
 
+      if (!is_java_debug) {
+        args += [ "--use_proxy_hash" ]
+      }
+
       if (enable_profiling) {
         args += [ "--enable_profiling" ]
       }
@@ -435,6 +439,17 @@
         "--depfile",
         rebase_path(depfile, root_build_dir),
       ]
+
+      if (!is_java_debug) {
+        args += [ "--use_proxy_hash" ]
+      }
+
+      if (defined(invoker.exception_files)) {
+        _rebase_exception_java_files =
+            rebase_path(invoker.exception_files, root_build_dir)
+        args += [ "--no_register_java=$_rebase_exception_java_files" ]
+      }
+
       if (defined(invoker.header_output)) {
         outputs += [ invoker.header_output ]
         args += [
@@ -442,11 +457,13 @@
           rebase_path(invoker.header_output, root_build_dir),
         ]
       }
+
       if (defined(invoker.sources_blacklist)) {
         _rebase_sources_blacklist =
             rebase_path(invoker.sources_blacklist, root_build_dir)
         args += [ "--sources-blacklist=$_rebase_sources_blacklist" ]
       }
+
       if (defined(invoker.namespace)) {
         args += [ "--namespace=${invoker.namespace}" ]
       }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 3abf2ce8..b7373bd 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-1cea35ffc58495fc5a3f9913bf8f18109965f873
\ No newline at end of file
+426769662771726c2926af9b909376fdd91f79b4
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 31c871c..34ff0e6 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-20ae813ac8b9d9aedc7e41502db2c83701be633f
\ No newline at end of file
+af68fe84f0c46a943568c3ac9045c33e53bf5fba
\ No newline at end of file
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 0d349e6..1227c14f 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -11,19 +11,13 @@
 #include <vector>
 
 #include "base/numerics/safe_conversions.h"
-#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
 #include "cc/debug/debug_colors.h"
-#include "cc/paint/display_item_list.h"
-#include "cc/paint/paint_canvas.h"
-#include "cc/paint/paint_flags.h"
-#include "cc/paint/paint_shader.h"
-#include "cc/paint/record_paint_canvas.h"
-#include "cc/paint/skia_paint_canvas.h"
 #include "cc/raster/scoped_gpu_raster.h"
 #include "cc/resources/memory_history.h"
 #include "cc/trees/frame_rate_counter.h"
@@ -39,30 +33,28 @@
 #include "components/viz/common/resources/platform_color.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/shared_image_trace_utils.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
-#include "gpu/config/gpu_feature_info.h"
-#include "third_party/skia/include/core/SkFont.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/size_conversions.h"
-#include "ui/gfx/skia_util.h"
 #include "ui/gl/trace_util.h"
 
 namespace cc {
 
-namespace {
-
-PaintFlags CreatePaintFlags() {
-  PaintFlags flags;
+static inline SkPaint CreatePaint() {
+  SkPaint paint;
 #if (SK_R32_SHIFT || SK_B32_SHIFT != 16)
-  // The PaintCanvas is in RGBA but the shader is expecting BGRA, so we need to
-  // swizzle our colors when drawing to the PaintCanvas.
+  // The SkCanvas is in RGBA but the shader is expecting BGRA, so we need to
+  // swizzle our colors when drawing to the SkCanvas.
   SkScalar color_matrix[20];
   for (int i = 0; i < 20; ++i)
     color_matrix[i] = 0;
@@ -71,39 +63,12 @@
   color_matrix[2 + 5 * 0] = 1;
   color_matrix[3 + 5 * 3] = 1;
 
-  flags.setColorFilter(
+  paint.setColorFilter(
       SkColorFilter::MakeMatrixFilterRowMajor255(color_matrix));
 #endif
-  return flags;
+  return paint;
 }
 
-void DrawArc(PaintCanvas* canvas,
-             const SkRect& oval,
-             SkScalar start_angle,
-             SkScalar sweep_angle,
-             const PaintFlags& flags) {
-  DCHECK_GT(sweep_angle, 0.f);
-  DCHECK_LT(sweep_angle, 360.f);
-  SkPath path;
-  path.moveTo(oval.centerX(), oval.centerY());
-  path.arcTo(oval, start_angle, sweep_angle, false /* forceMoveTo */);
-  path.close();
-  canvas->drawPath(path, flags);
-}
-
-class DummyImageProvider : public ImageProvider {
- public:
-  DummyImageProvider() = default;
-  ~DummyImageProvider() override = default;
-  ScopedDecodedDrawImage GetDecodedDrawImage(
-      const DrawImage& draw_image) override {
-    NOTREACHED();
-    return ScopedDecodedDrawImage();
-  }
-};
-
-}  // namespace
-
 HeadsUpDisplayLayerImpl::Graph::Graph(double indicator_value,
                                       double start_upper_bound)
     : value(0.0),
@@ -125,9 +90,7 @@
       internal_contents_scale_(1.f),
       fps_graph_(60.0, 80.0),
       paint_time_graph_(16.0, 48.0),
-      fade_step_(0),
-      raster_color_space_(gfx::ColorSpace::CreateSRGB(),
-                          gfx::ColorSpace::GetNextId()) {}
+      fade_step_(0) {}
 
 HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl() {
   ReleaseResources();
@@ -143,10 +106,11 @@
   ~HudGpuBacking() override {
     if (mailbox.IsZero())
       return;
+    auto* sii = compositor_context_provider->SharedImageInterface();
     if (returned_sync_token.HasData())
-      shared_image_interface->DestroySharedImage(returned_sync_token, mailbox);
+      sii->DestroySharedImage(returned_sync_token, mailbox);
     else if (mailbox_sync_token.HasData())
-      shared_image_interface->DestroySharedImage(mailbox_sync_token, mailbox);
+      sii->DestroySharedImage(mailbox_sync_token, mailbox);
   }
 
   void OnMemoryDump(
@@ -162,7 +126,7 @@
     pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance);
   }
 
-  gpu::SharedImageInterface* shared_image_interface = nullptr;
+  viz::ContextProvider* compositor_context_provider;
 };
 
 class HudSoftwareBacking : public ResourcePool::SoftwareBacking {
@@ -234,33 +198,14 @@
   // Update state that will be drawn.
   UpdateHudContents();
 
-  // TODO(penghuang): Do not use worker_context_provider() when context_provider
-  // is changed to RasterContextProvider.
-  // https://crbug.com/c/1286950
-  auto* raster_context_provider =
-      gpu_raster ? layer_tree_frame_sink->worker_context_provider() : nullptr;
-  base::Optional<viz::RasterContextProvider::ScopedRasterContextLock> lock;
-  bool use_oopr = false;
-  if (raster_context_provider) {
-    lock.emplace(raster_context_provider);
-    use_oopr = raster_context_provider->GetGpuFeatureInfo()
-                   .status_values[gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION] ==
-               gpu::kGpuFeatureStatusEnabled;
-    if (!use_oopr) {
-      raster_context_provider = nullptr;
-      lock.reset();
-    }
-  }
-
-  auto* context_provider = layer_tree_frame_sink->context_provider();
   if (!pool_) {
     scoped_refptr<base::SingleThreadTaskRunner> task_runner =
         layer_tree_impl()->task_runner_provider()->HasImplThread()
             ? layer_tree_impl()->task_runner_provider()->ImplThreadTaskRunner()
             : layer_tree_impl()->task_runner_provider()->MainThreadTaskRunner();
     pool_ = std::make_unique<ResourcePool>(
-        resource_provider, context_provider, std::move(task_runner),
-        ResourcePool::kDefaultExpirationDelay,
+        resource_provider, layer_tree_frame_sink->context_provider(),
+        std::move(task_runner), ResourcePool::kDefaultExpirationDelay,
         layer_tree_impl()->settings().disallow_non_exact_resource_reuse);
   }
 
@@ -275,58 +220,39 @@
   // compositing.
   ResourcePool::InUsePoolResource pool_resource;
   if (draw_mode == DRAW_MODE_HARDWARE) {
-    DCHECK(raster_context_provider || context_provider);
-    const auto& caps = raster_context_provider
-                           ? raster_context_provider->ContextCapabilities()
-                           : context_provider->ContextCapabilities();
+    viz::ContextProvider* context_provider =
+        layer_tree_impl()->context_provider();
+    DCHECK(context_provider);
+
     viz::ResourceFormat format =
-        viz::PlatformColor::BestSupportedRenderBufferFormat(caps);
+        viz::PlatformColor::BestSupportedRenderBufferFormat(
+            context_provider->ContextCapabilities());
     pool_resource = pool_->AcquireResource(internal_content_bounds_, format,
                                            gfx::ColorSpace());
 
     if (!pool_resource.gpu_backing()) {
       auto backing = std::make_unique<HudGpuBacking>();
-      auto* sii = raster_context_provider
-                      ? raster_context_provider->SharedImageInterface()
-                      : context_provider->SharedImageInterface();
-      backing->shared_image_interface = sii;
+      backing->compositor_context_provider = context_provider;
       backing->InitOverlayCandidateAndTextureTarget(
-          pool_resource.format(), caps,
+          pool_resource.format(), context_provider->ContextCapabilities(),
           layer_tree_impl()
               ->settings()
               .resource_settings.use_gpu_memory_buffer_resources);
-
-      uint32_t flags = 0;
-      if (use_oopr) {
-        flags = gpu::SHARED_IMAGE_USAGE_RASTER |
-                gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
-      } else if (gpu_raster) {
-        flags = gpu::SHARED_IMAGE_USAGE_GLES2 |
-                gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
-      }
+      auto* sii = context_provider->SharedImageInterface();
+      uint32_t flags = gpu::SHARED_IMAGE_USAGE_GLES2;
+      if (gpu_raster)
+        flags |= gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
       if (backing->overlay_candidate)
         flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
       backing->mailbox =
           sii->CreateSharedImage(pool_resource.format(), pool_resource.size(),
                                  pool_resource.color_space(), flags);
-      if (raster_context_provider) {
-        auto* ri = raster_context_provider->RasterInterface();
-        ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
-      } else {
-        auto* gl = context_provider->ContextGL();
-        gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
-      }
+      gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
+      gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
       pool_resource.set_gpu_backing(std::move(backing));
     } else if (pool_resource.gpu_backing()->returned_sync_token.HasData()) {
-      if (raster_context_provider) {
-        auto* ri = raster_context_provider->RasterInterface();
-        ri->WaitSyncTokenCHROMIUM(
-            pool_resource.gpu_backing()->returned_sync_token.GetConstData());
-      } else {
-        auto* gl = context_provider->ContextGL();
-        gl->WaitSyncTokenCHROMIUM(
-            pool_resource.gpu_backing()->returned_sync_token.GetConstData());
-      }
+      context_provider->ContextGL()->WaitSyncTokenCHROMIUM(
+          pool_resource.gpu_backing()->returned_sync_token.GetConstData());
       pool_resource.gpu_backing()->returned_sync_token = gpu::SyncToken();
     }
   } else {
@@ -359,60 +285,29 @@
     DCHECK_EQ(draw_mode, DRAW_MODE_HARDWARE);
     DCHECK(pool_resource.gpu_backing());
     auto* backing = static_cast<HudGpuBacking*>(pool_resource.gpu_backing());
+    viz::ContextProvider* context_provider =
+        layer_tree_impl()->context_provider();
+    gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
+    GLuint mailbox_texture_id =
+        gl->CreateAndConsumeTextureCHROMIUM(backing->mailbox.name);
 
-    if (use_oopr) {
-      const auto& size = pool_resource.size();
-      auto display_item_list = base::MakeRefCounted<DisplayItemList>(
-          DisplayItemList::kTopLevelDisplayItemList);
-      RecordPaintCanvas canvas(display_item_list.get(),
-                               SkRect::MakeIWH(size.width(), size.height()));
-      display_item_list->StartPaint();
-      DrawHudContents(&canvas);
-      display_item_list->EndPaintOfUnpaired(gfx::Rect(size));
-      display_item_list->Finalize();
-
-      auto* ri = raster_context_provider->RasterInterface();
-      constexpr GLuint background_color = SkColorSetARGB(0, 0, 0, 0);
-      constexpr GLuint msaa_sample_count = -1;
-      constexpr bool can_use_lcd_text = true;
-      const auto pixel_config = viz::ResourceFormatToClosestSkColorType(
-          true /* gpu_compositing */, pool_resource.format());
-      ri->BeginRasterCHROMIUM(background_color, msaa_sample_count,
-                              can_use_lcd_text, pixel_config,
-                              raster_color_space_, backing->mailbox.name);
-      gfx::Vector2dF post_translate(0.f, 0.f);
-      DummyImageProvider image_provider;
-      ri->RasterCHROMIUM(display_item_list.get(), &image_provider, size,
-                         gfx::Rect(size), gfx::Rect(size), post_translate,
-                         1.f /* post_scale */, false /* requires_clear */);
-      ri->EndRasterCHROMIUM();
-      backing->mailbox_sync_token =
-          viz::ClientResourceProvider::GenerateSyncTokenHelper(ri);
-    } else {
-      auto* gl = context_provider->ContextGL();
-      GLuint mailbox_texture_id =
-          gl->CreateAndConsumeTextureCHROMIUM(backing->mailbox.name);
-
-      {
-        ScopedGpuRaster gpu_raster(context_provider);
-        viz::ClientResourceProvider::ScopedSkSurface scoped_surface(
-            context_provider->GrContext(), mailbox_texture_id,
-            backing->texture_target, pool_resource.size(),
-            pool_resource.format(), false /* can_use_lcd_text */,
-            0 /* msaa_sample_count */);
-        SkSurface* surface = scoped_surface.surface();
-        if (!surface) {
-          pool_->ReleaseResource(std::move(pool_resource));
-          return;
-        }
-        SkiaPaintCanvas canvas(surface->getCanvas());
-        DrawHudContents(&canvas);
+    {
+      ScopedGpuRaster gpu_raster(context_provider);
+      viz::ClientResourceProvider::ScopedSkSurface scoped_surface(
+          context_provider->GrContext(), mailbox_texture_id,
+          backing->texture_target, pool_resource.size(), pool_resource.format(),
+          false /* can_use_lcd_text */, 0 /* msaa_sample_count */);
+      SkSurface* surface = scoped_surface.surface();
+      if (!surface) {
+        pool_->ReleaseResource(std::move(pool_resource));
+        return;
       }
-
-      gl->DeleteTextures(1, &mailbox_texture_id);
-      backing->mailbox_sync_token =
-          viz::ClientResourceProvider::GenerateSyncTokenHelper(gl);
+      DrawHudContents(surface->getCanvas());
     }
+
+    gl->DeleteTextures(1, &mailbox_texture_id);
+    backing->mailbox_sync_token =
+        viz::ClientResourceProvider::GenerateSyncTokenHelper(gl);
   } else if (draw_mode == DRAW_MODE_HARDWARE) {
     // If not using |gpu_raster| but using gpu compositing, we DrawHudContents()
     // into a software bitmap and upload it to a texture for compositing.
@@ -429,8 +324,7 @@
           pool_resource.size().width(), pool_resource.size().height());
     }
 
-    SkiaPaintCanvas canvas(staging_surface_->getCanvas());
-    DrawHudContents(&canvas);
+    DrawHudContents(staging_surface_->getCanvas());
 
     TRACE_EVENT0("cc", "UploadHudTexture");
     SkPixmap pixmap;
@@ -460,8 +354,7 @@
     sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(
         info, backing->shared_memory->memory(), info.minRowBytes());
 
-    SkiaPaintCanvas canvas(surface->getCanvas());
-    DrawHudContents(&canvas);
+    DrawHudContents(surface->getCanvas());
   }
 
   // Exports the backing to the ResourceProvider, giving it a ResourceId that
@@ -564,7 +457,7 @@
   paint_time_graph_.UpdateUpperBound();
 }
 
-void HeadsUpDisplayLayerImpl::DrawHudContents(PaintCanvas* canvas) {
+void HeadsUpDisplayLayerImpl::DrawHudContents(SkCanvas* canvas) {
   const LayerTreeDebugState& debug_state = layer_tree_impl()->debug_state();
 
   TRACE_EVENT0("cc", "DrawHudContents");
@@ -594,74 +487,94 @@
 
   canvas->restore();
 }
+int HeadsUpDisplayLayerImpl::MeasureText(SkPaint* paint,
+                                         const std::string& text,
+                                         int size) const {
+  DCHECK(typeface_.get());
+  const bool anti_alias = paint->isAntiAlias();
+  paint->setAntiAlias(true);
+  paint->setTextSize(size);
+  paint->setTypeface(typeface_);
+  SkScalar text_width = paint->measureText(text.c_str(), text.length());
 
-void HeadsUpDisplayLayerImpl::DrawText(PaintCanvas* canvas,
-                                       PaintFlags* flags,
+  paint->setAntiAlias(anti_alias);
+  return SkScalarCeilToInt(text_width);
+}
+void HeadsUpDisplayLayerImpl::DrawText(SkCanvas* canvas,
+                                       SkPaint* paint,
                                        const std::string& text,
                                        TextAlign align,
                                        int size,
                                        int x,
                                        int y) const {
   DCHECK(typeface_.get());
-  flags->setAntiAlias(true);
-  flags->setTextSize(size);
-  flags->setTypeface(typeface_);
-  if (align == TextAlign::kCenter) {
-    auto width = flags->ToSkPaint().measureText(text.c_str(), text.length());
-    x -= width * 0.5f;
-  } else if (align == TextAlign::kRight) {
-    auto width = flags->ToSkPaint().measureText(text.c_str(), text.length());
-    x -= width;
-  }
-  auto sk_paint = flags->ToSkPaint();
+  const bool anti_alias = paint->isAntiAlias();
+  paint->setAntiAlias(true);
 
-  auto text_blob = SkTextBlob::MakeFromText(
-      text.c_str(), text.length(),
-      SkFont(sk_paint.refTypeface(), sk_paint.getTextSize()));
-  canvas->drawTextBlob(std::move(text_blob), x, y, *flags);
+  paint->setTextSize(size);
+  paint->setTypeface(typeface_);
+
+  if (align != TextAlign::kLeft) {
+    SkScalar width = paint->measureText(text.c_str(), text.length(), nullptr);
+    if (align == TextAlign::kCenter) {
+      x -= width * 0.5f;
+    } else {
+      DCHECK_EQ(align, TextAlign::kRight);
+      x -= width;
+    }
+  }
+  canvas->drawText(text.c_str(), text.length(), x, y, *paint);
+
+  paint->setAntiAlias(anti_alias);
 }
 
-void HeadsUpDisplayLayerImpl::DrawText(PaintCanvas* canvas,
-                                       PaintFlags* flags,
+void HeadsUpDisplayLayerImpl::DrawText(SkCanvas* canvas,
+                                       SkPaint* paint,
                                        const std::string& text,
                                        TextAlign align,
                                        int size,
                                        const SkPoint& pos) const {
-  DrawText(canvas, flags, text, align, size, pos.x(), pos.y());
+  DrawText(canvas, paint, text, align, size, pos.x(), pos.y());
 }
 
-void HeadsUpDisplayLayerImpl::DrawGraphBackground(PaintCanvas* canvas,
-                                                  PaintFlags* flags,
+void HeadsUpDisplayLayerImpl::DrawGraphBackground(SkCanvas* canvas,
+                                                  SkPaint* paint,
                                                   const SkRect& bounds) const {
-  flags->setColor(DebugColors::HUDBackgroundColor());
-  canvas->drawRect(bounds, *flags);
+  paint->setColor(DebugColors::HUDBackgroundColor());
+  canvas->drawRect(bounds, *paint);
 }
 
-void HeadsUpDisplayLayerImpl::DrawGraphLines(PaintCanvas* canvas,
-                                             PaintFlags* flags,
+void HeadsUpDisplayLayerImpl::DrawGraphLines(SkCanvas* canvas,
+                                             SkPaint* paint,
                                              const SkRect& bounds,
                                              const Graph& graph) const {
   // Draw top and bottom line.
-  flags->setColor(DebugColors::HUDSeparatorLineColor());
-  canvas->drawLine(bounds.left(), bounds.top() - 1, bounds.right(),
-                   bounds.top() - 1, *flags);
-  canvas->drawLine(bounds.left(), bounds.bottom(), bounds.right(),
-                   bounds.bottom(), *flags);
+  paint->setColor(DebugColors::HUDSeparatorLineColor());
+  canvas->drawLine(bounds.left(),
+                   bounds.top() - 1,
+                   bounds.right(),
+                   bounds.top() - 1,
+                   *paint);
+  canvas->drawLine(
+      bounds.left(), bounds.bottom(), bounds.right(), bounds.bottom(), *paint);
 
   // Draw indicator line (additive blend mode to increase contrast when drawn on
   // top of graph).
-  flags->setColor(DebugColors::HUDIndicatorLineColor());
-  flags->setBlendMode(SkBlendMode::kPlus);
+  paint->setColor(DebugColors::HUDIndicatorLineColor());
+  paint->setBlendMode(SkBlendMode::kPlus);
   const double indicator_top =
       bounds.height() * (1.0 - graph.indicator / graph.current_upper_bound) -
       1.0;
-  canvas->drawLine(bounds.left(), bounds.top() + indicator_top, bounds.right(),
-                   bounds.top() + indicator_top, *flags);
-  flags->setBlendMode(SkBlendMode::kSrcOver);
+  canvas->drawLine(bounds.left(),
+                   bounds.top() + indicator_top,
+                   bounds.right(),
+                   bounds.top() + indicator_top,
+                   *paint);
+  paint->setBlendMode(SkBlendMode::kSrcOver);
 }
 
 SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay(
-    PaintCanvas* canvas,
+    SkCanvas* canvas,
     const FrameRateCounter* fps_counter,
     int right,
     int top) const {
@@ -682,8 +595,8 @@
   int left = bounds().width() - width - right;
   SkRect area = SkRect::MakeXYWH(left, top, width, height);
 
-  PaintFlags flags = CreatePaintFlags();
-  DrawGraphBackground(canvas, &flags, area);
+  SkPaint paint = CreatePaint();
+  DrawGraphBackground(canvas, &paint, area);
 
   SkRect title_bounds = SkRect::MakeXYWH(
       left + kPadding, top + kPadding, kGraphWidth + kHistogramWidth + kGap + 2,
@@ -708,17 +621,17 @@
 
   VLOG(1) << value_text;
 
-  flags.setColor(DebugColors::HUDTitleColor());
-  DrawText(canvas, &flags, title, TextAlign::kLeft, kTitleFontHeight,
+  paint.setColor(DebugColors::HUDTitleColor());
+  DrawText(canvas, &paint, title, TextAlign::kLeft, kTitleFontHeight,
            title_bounds.left(), title_bounds.bottom());
 
-  flags.setColor(DebugColors::FPSDisplayTextAndGraphColor());
-  DrawText(canvas, &flags, value_text, TextAlign::kLeft, kFontHeight,
+  paint.setColor(DebugColors::FPSDisplayTextAndGraphColor());
+  DrawText(canvas, &paint, value_text, TextAlign::kLeft, kFontHeight,
            text_bounds.left(), text_bounds.bottom());
-  DrawText(canvas, &flags, min_max_text, TextAlign::kRight, kFontHeight,
+  DrawText(canvas, &paint, min_max_text, TextAlign::kRight, kFontHeight,
            text_bounds.right(), text_bounds.bottom());
 
-  DrawGraphLines(canvas, &flags, graph_bounds, fps_graph_);
+  DrawGraphLines(canvas, &paint, graph_bounds, fps_graph_);
 
   // Collect graph and histogram data.
   SkPath path;
@@ -761,15 +674,19 @@
   }
 
   // Draw FPS histogram.
-  flags.setColor(DebugColors::HUDSeparatorLineColor());
-  canvas->drawLine(histogram_bounds.left() - 1, histogram_bounds.top() - 1,
-                   histogram_bounds.left() - 1, histogram_bounds.bottom() + 1,
-                   flags);
-  canvas->drawLine(histogram_bounds.right() + 1, histogram_bounds.top() - 1,
-                   histogram_bounds.right() + 1, histogram_bounds.bottom() + 1,
-                   flags);
+  paint.setColor(DebugColors::HUDSeparatorLineColor());
+  canvas->drawLine(histogram_bounds.left() - 1,
+                   histogram_bounds.top() - 1,
+                   histogram_bounds.left() - 1,
+                   histogram_bounds.bottom() + 1,
+                   paint);
+  canvas->drawLine(histogram_bounds.right() + 1,
+                   histogram_bounds.top() - 1,
+                   histogram_bounds.right() + 1,
+                   histogram_bounds.bottom() + 1,
+                   paint);
 
-  flags.setColor(DebugColors::FPSDisplayTextAndGraphColor());
+  paint.setColor(DebugColors::FPSDisplayTextAndGraphColor());
   const double bar_height = histogram_bounds.height() / kHistogramSize;
 
   for (int i = kHistogramSize - 1; i >= 0; --i) {
@@ -779,21 +696,22 @@
       canvas->drawRect(
           SkRect::MakeXYWH(histogram_bounds.left(),
                            histogram_bounds.bottom() - (i + 1) * bar_height,
-                           bar_width, 1),
-          flags);
+                           bar_width,
+                           1),
+          paint);
     }
   }
 
   // Draw FPS graph.
-  flags.setAntiAlias(true);
-  flags.setStyle(PaintFlags::kStroke_Style);
-  flags.setStrokeWidth(1);
-  canvas->drawPath(path, flags);
+  paint.setAntiAlias(true);
+  paint.setStyle(SkPaint::kStroke_Style);
+  paint.setStrokeWidth(1);
+  canvas->drawPath(path, paint);
 
   return area;
 }
 
-SkRect HeadsUpDisplayLayerImpl::DrawMemoryDisplay(PaintCanvas* canvas,
+SkRect HeadsUpDisplayLayerImpl::DrawMemoryDisplay(SkCanvas* canvas,
                                                   int right,
                                                   int top,
                                                   int width) const {
@@ -807,8 +725,8 @@
 
   const double kMegabyte = 1024.0 * 1024.0;
 
-  PaintFlags flags = CreatePaintFlags();
-  DrawGraphBackground(canvas, &flags, area);
+  SkPaint paint = CreatePaint();
+  DrawGraphBackground(canvas, &paint, area);
 
   SkPoint title_pos =
       SkPoint::Make(left + kPadding, top + kFontHeight + kPadding);
@@ -817,32 +735,31 @@
   SkPoint stat2_pos = SkPoint::Make(left + width - kPadding - 1,
                                     top + 2 * kPadding + 3 * kFontHeight);
 
-  flags.setColor(DebugColors::HUDTitleColor());
-  DrawText(canvas, &flags, "GPU Memory", TextAlign::kLeft, kTitleFontHeight,
+  paint.setColor(DebugColors::HUDTitleColor());
+  DrawText(canvas, &paint, "GPU Memory", TextAlign::kLeft, kTitleFontHeight,
            title_pos);
 
-  flags.setColor(DebugColors::MemoryDisplayTextColor());
+  paint.setColor(DebugColors::MemoryDisplayTextColor());
   std::string text = base::StringPrintf(
       "%6.1f MB used", memory_entry_.total_bytes_used / kMegabyte);
-  DrawText(canvas, &flags, text, TextAlign::kRight, kFontHeight, stat1_pos);
+  DrawText(canvas, &paint, text, TextAlign::kRight, kFontHeight, stat1_pos);
 
   if (!memory_entry_.had_enough_memory)
-    flags.setColor(SK_ColorRED);
+    paint.setColor(SK_ColorRED);
   text = base::StringPrintf("%6.1f MB max ",
                             memory_entry_.total_budget_in_bytes / kMegabyte);
-
-  DrawText(canvas, &flags, text, TextAlign::kRight, kFontHeight, stat2_pos);
+  DrawText(canvas, &paint, text, TextAlign::kRight, kFontHeight, stat2_pos);
 
   // Draw memory graph.
   int length = 2 * kFontHeight + kPadding + 12;
   SkRect oval =
       SkRect::MakeXYWH(left + kPadding * 6,
                        top + kTitleFontHeight + kPadding * 3, length, length);
-  flags.setAntiAlias(true);
-  flags.setStyle(PaintFlags::kFill_Style);
+  paint.setAntiAlias(true);
+  paint.setStyle(SkPaint::kFill_Style);
 
-  flags.setColor(SkColorSetARGB(64, 255, 255, 0));
-  DrawArc(canvas, oval, 180, 180, flags);
+  paint.setColor(SkColorSetARGB(64, 255, 255, 0));
+  canvas->drawArc(oval, 180, 180, true, paint);
 
   int radius = length / 2;
   int cx = oval.left() + radius;
@@ -856,25 +773,24 @@
   const SkScalar pos[] = {SkFloatToScalar(0.2f), SkFloatToScalar(0.4f),
                           SkFloatToScalar(0.6f), SkFloatToScalar(0.8f),
                           SkFloatToScalar(1.0f)};
-  flags.setShader(PaintShader::MakeSweepGradient(
-      cx, cy, colors, pos, 5, SkShader::kClamp_TileMode, 0, 360));
-  flags.setAntiAlias(true);
+  paint.setShader(SkGradientShader::MakeSweep(cx, cy, colors, pos, 5));
+  paint.setFlags(SkPaint::kAntiAlias_Flag);
 
   // Draw current status.
-  flags.setStyle(PaintFlags::kStroke_Style);
-  flags.setAlpha(32);
-  flags.setStrokeWidth(4);
-  DrawArc(canvas, oval, 180, angle, flags);
+  paint.setStyle(SkPaint::kStroke_Style);
+  paint.setAlpha(32);
+  paint.setStrokeWidth(4);
+  canvas->drawArc(oval, 180, angle, true, paint);
 
-  flags.setStyle(PaintFlags::kFill_Style);
-  flags.setColor(SkColorSetARGB(255, 0, 255, 0));
-  DrawArc(canvas, oval, 180, angle, flags);
-  flags.setShader(nullptr);
+  paint.setStyle(SkPaint::kFill_Style);
+  paint.setColor(SkColorSetARGB(255, 0, 255, 0));
+  canvas->drawArc(oval, 180, angle, true, paint);
+  paint.setShader(nullptr);
 
   return area;
 }
 
-SkRect HeadsUpDisplayLayerImpl::DrawGpuRasterizationStatus(PaintCanvas* canvas,
+SkRect HeadsUpDisplayLayerImpl::DrawGpuRasterizationStatus(SkCanvas* canvas,
                                                            int right,
                                                            int top,
                                                            int width) const {
@@ -914,24 +830,24 @@
   const int left = bounds().width() - width - right;
   const SkRect area = SkRect::MakeXYWH(left, top, width, height);
 
-  PaintFlags flags = CreatePaintFlags();
-  DrawGraphBackground(canvas, &flags, area);
+  SkPaint paint = CreatePaint();
+  DrawGraphBackground(canvas, &paint, area);
 
   SkPoint gpu_status_pos = SkPoint::Make(left + width - kPadding,
                                          top + 2 * kFontHeight + 2 * kPadding);
-  flags.setColor(DebugColors::HUDTitleColor());
-  DrawText(canvas, &flags, "GPU Raster", TextAlign::kLeft, kTitleFontHeight,
+  paint.setColor(DebugColors::HUDTitleColor());
+  DrawText(canvas, &paint, "GPU Raster", TextAlign::kLeft, kTitleFontHeight,
            left + kPadding, top + kFontHeight + kPadding);
-  flags.setColor(color);
-  DrawText(canvas, &flags, status, TextAlign::kRight, kFontHeight,
+  paint.setColor(color);
+  DrawText(canvas, &paint, status, TextAlign::kRight, kFontHeight,
            gpu_status_pos);
 
   return area;
 }
 
 void HeadsUpDisplayLayerImpl::DrawDebugRect(
-    PaintCanvas* canvas,
-    PaintFlags* flags,
+    SkCanvas* canvas,
+    SkPaint* paint,
     const DebugRect& rect,
     SkColor stroke_color,
     SkColor fill_color,
@@ -942,14 +858,14 @@
       gfx::ScaleToEnclosingRect(rect.rect, 1.0 / internal_contents_scale_,
                                 1.0 / internal_contents_scale_);
   SkIRect sk_rect = RectToSkIRect(debug_layer_rect);
-  flags->setColor(fill_color);
-  flags->setStyle(PaintFlags::kFill_Style);
-  canvas->drawIRect(sk_rect, *flags);
+  paint->setColor(fill_color);
+  paint->setStyle(SkPaint::kFill_Style);
+  canvas->drawIRect(sk_rect, *paint);
 
-  flags->setColor(stroke_color);
-  flags->setStyle(PaintFlags::kStroke_Style);
-  flags->setStrokeWidth(SkFloatToScalar(stroke_width));
-  canvas->drawIRect(sk_rect, *flags);
+  paint->setColor(stroke_color);
+  paint->setStyle(SkPaint::kStroke_Style);
+  paint->setStrokeWidth(SkFloatToScalar(stroke_width));
+  canvas->drawIRect(sk_rect, *paint);
 
   if (label_text.length()) {
     const int kFontHeight = 12;
@@ -965,29 +881,33 @@
     canvas->clipRect(sk_clip_rect);
     canvas->translate(sk_clip_rect.x(), sk_clip_rect.y());
 
-    PaintFlags label_flags = CreatePaintFlags();
-    label_flags.setTextSize(kFontHeight);
-    label_flags.setTypeface(typeface_);
-    label_flags.setColor(stroke_color);
+    SkPaint label_paint = CreatePaint();
+    label_paint.setTextSize(kFontHeight);
+    label_paint.setTypeface(typeface_);
+    label_paint.setColor(stroke_color);
 
-    const SkScalar label_text_width = label_flags.ToSkPaint().measureText(
-        label_text.c_str(), label_text.length());
+    const SkScalar label_text_width =
+        label_paint.measureText(label_text.c_str(), label_text.length());
     canvas->drawRect(SkRect::MakeWH(label_text_width + 2 * kPadding,
                                     kFontHeight + 2 * kPadding),
-                     label_flags);
+                     label_paint);
 
-    label_flags.setAntiAlias(true);
-    label_flags.setColor(SkColorSetARGB(255, 50, 50, 50));
-    DrawText(canvas, &label_flags, label_text, TextAlign::kLeft, kFontHeight,
-             kPadding, kFontHeight * 0.8f + kPadding);
+    label_paint.setAntiAlias(true);
+    label_paint.setColor(SkColorSetARGB(255, 50, 50, 50));
+    canvas->drawText(label_text.c_str(),
+                     label_text.length(),
+                     kPadding,
+                     kFontHeight * 0.8f + kPadding,
+                     label_paint);
+
     canvas->restore();
   }
 }
 
 void HeadsUpDisplayLayerImpl::DrawDebugRects(
-    PaintCanvas* canvas,
+    SkCanvas* canvas,
     DebugRectHistory* debug_rect_history) {
-  PaintFlags flags = CreatePaintFlags();
+  SkPaint paint = CreatePaint();
 
   const std::vector<DebugRect>& debug_rects = debug_rect_history->debug_rects();
   std::vector<DebugRect> new_paint_rects;
@@ -1050,8 +970,13 @@
         break;
     }
 
-    DrawDebugRect(canvas, &flags, debug_rects[i], stroke_color, fill_color,
-                  stroke_width, label_text);
+    DrawDebugRect(canvas,
+                  &paint,
+                  debug_rects[i],
+                  stroke_color,
+                  fill_color,
+                  stroke_width,
+                  label_text);
   }
 
   if (new_paint_rects.size()) {
@@ -1061,10 +986,13 @@
   if (fade_step_ > 0) {
     fade_step_--;
     for (size_t i = 0; i < paint_rects_.size(); ++i) {
-      DrawDebugRect(canvas, &flags, paint_rects_[i],
+      DrawDebugRect(canvas,
+                    &paint,
+                    paint_rects_[i],
                     DebugColors::PaintRectBorderColor(fade_step_),
                     DebugColors::PaintRectFillColor(fade_step_),
-                    DebugColors::PaintRectBorderWidth(), "");
+                    DebugColors::PaintRectBorderWidth(),
+                    "");
     }
   }
 }
diff --git a/cc/layers/heads_up_display_layer_impl.h b/cc/layers/heads_up_display_layer_impl.h
index 9a97dae..a7e3c1a9 100644
--- a/cc/layers/heads_up_display_layer_impl.h
+++ b/cc/layers/heads_up_display_layer_impl.h
@@ -14,12 +14,13 @@
 #include "base/time/time.h"
 #include "cc/cc_export.h"
 #include "cc/layers/layer_impl.h"
-#include "cc/paint/color_space_transfer_cache_entry.h"
 #include "cc/resources/memory_history.h"
 #include "cc/resources/resource_pool.h"
 #include "cc/trees/debug_rect_history.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
+class SkCanvas;
+class SkPaint;
 class SkTypeface;
 struct SkRect;
 
@@ -30,8 +31,6 @@
 namespace cc {
 class FrameRateCounter;
 class LayerTreeFrameSink;
-class PaintCanvas;
-class PaintFlags;
 
 enum class TextAlign { kLeft, kCenter, kRight };
 
@@ -96,49 +95,50 @@
   void AsValueInto(base::trace_event::TracedValue* dict) const override;
 
   void UpdateHudContents();
-  void DrawHudContents(PaintCanvas* canvas);
-  void DrawText(PaintCanvas* canvas,
-                PaintFlags* flags,
+  void DrawHudContents(SkCanvas* canvas);
+
+  int MeasureText(SkPaint* paint, const std::string& text, int size) const;
+  void DrawText(SkCanvas* canvas,
+                SkPaint* paint,
                 const std::string& text,
                 TextAlign align,
                 int size,
                 int x,
                 int y) const;
-  void DrawText(PaintCanvas* canvas,
-                PaintFlags* flags,
+  void DrawText(SkCanvas* canvas,
+                SkPaint* paint,
                 const std::string& text,
                 TextAlign align,
                 int size,
                 const SkPoint& pos) const;
-  void DrawGraphBackground(PaintCanvas* canvas,
-                           PaintFlags* flags,
+  void DrawGraphBackground(SkCanvas* canvas,
+                           SkPaint* paint,
                            const SkRect& bounds) const;
-  void DrawGraphLines(PaintCanvas* canvas,
-                      PaintFlags* flags,
+  void DrawGraphLines(SkCanvas* canvas,
+                      SkPaint* paint,
                       const SkRect& bounds,
                       const Graph& graph) const;
 
-  SkRect DrawFPSDisplay(PaintCanvas* canvas,
+  SkRect DrawFPSDisplay(SkCanvas* canvas,
                         const FrameRateCounter* fps_counter,
                         int right,
                         int top) const;
-  SkRect DrawMemoryDisplay(PaintCanvas* canvas,
+  SkRect DrawMemoryDisplay(SkCanvas* canvas,
                            int top,
                            int right,
                            int width) const;
-  SkRect DrawGpuRasterizationStatus(PaintCanvas* canvas,
+  SkRect DrawGpuRasterizationStatus(SkCanvas* canvas,
                                     int right,
                                     int top,
                                     int width) const;
-  void DrawDebugRect(PaintCanvas* canvas,
-                     PaintFlags* flags,
+  void DrawDebugRect(SkCanvas* canvas,
+                     SkPaint* paint,
                      const DebugRect& rect,
                      SkColor stroke_color,
                      SkColor fill_color,
                      float stroke_width,
                      const std::string& label_text) const;
-  void DrawDebugRects(PaintCanvas* canvas,
-                      DebugRectHistory* debug_rect_history);
+  void DrawDebugRects(SkCanvas* canvas, DebugRectHistory* debug_rect_history);
 
   ResourcePool::InUsePoolResource in_flight_resource_;
   std::unique_ptr<ResourcePool> pool_;
@@ -159,9 +159,6 @@
 
   base::TimeTicks time_of_last_graph_update_;
 
-  // color space for OOPR
-  const RasterColorSpace raster_color_space_;
-
   DISALLOW_COPY_AND_ASSIGN(HeadsUpDisplayLayerImpl);
 };
 
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 18b4a171..01c213bc 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -1107,6 +1107,9 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver"
+            android:exported="false"/>
+
         <receiver android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$OpenUrlReceiver"
             android:exported="false"/>
         <receiver android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$DeleteReceiver"
diff --git a/chrome/android/java/res/drawable-sw600dp/bg_white_dialog.xml b/chrome/android/java/res/drawable-sw600dp/bg_white_dialog.xml
index f384c95..112698f 100644
--- a/chrome/android/java/res/drawable-sw600dp/bg_white_dialog.xml
+++ b/chrome/android/java/res/drawable-sw600dp/bg_white_dialog.xml
@@ -3,10 +3,5 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <corners
-        android:radius="2dp" />
-    <solid
-        android:color="@android:color/white" />
-</shape>
\ No newline at end of file
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/popup_bg" />
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
index 1821242..0c6fe13 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
@@ -456,12 +456,12 @@
 
     /** Called to show overlay. */
     public void showOverlay() {
-        mTouchEventFilter.setEnableFiltering(true);
+        mTouchEventFilter.setFullOverlay(true);
     }
 
     /** Called to hide overlay. */
     public void hideOverlay() {
-        mTouchEventFilter.setEnableFiltering(false);
+        mTouchEventFilter.setFullOverlay(false);
     }
 
     public void hideDetails() {
@@ -657,7 +657,7 @@
     }
 
     public void updateTouchableArea(boolean enabled, List<RectF> boxes) {
-        mTouchEventFilter.updateTouchableArea(enabled, boxes);
+        mTouchEventFilter.setPartialOverlay(enabled, boxes);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/ui/TouchEventFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/ui/TouchEventFilter.java
index 0ac6981..21301cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/ui/TouchEventFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/ui/TouchEventFilter.java
@@ -70,8 +70,13 @@
     private final Paint mGrayOut;
     private final Paint mClear;
 
-    /** Whether filtering is enabled. */
-    private boolean mEnabled;
+    /** Whether a partial-screen overlay is enabled or not. Has precedence over {@link
+     * @mFullOverlayEnabled}. */
+    private boolean mPartialOverlayEnabled;
+
+    /** Whether a full-screen overlay is enabled or not. Is overridden by {@link
+     * @mPartialOverlayEnabled}.*/
+    private boolean mFullOverlayEnabled;
 
     /** Padding added between the element area and the grayed-out area. */
     private final float mPaddingPx;
@@ -153,12 +158,14 @@
             @Override
             public boolean onScroll(MotionEvent downEvent, MotionEvent moveEvent, float distanceX,
                     float distanceY) {
-                mClient.scrollBy(distanceX / getWidth(), distanceY / getVisualViewportHeight());
+                if (mPartialOverlayEnabled)
+                    mClient.scrollBy(distanceX / getWidth(), distanceY / getVisualViewportHeight());
                 return true;
             }
         });
 
-        updateTouchableArea(false, Collections.emptyList());
+        setFullOverlay(false);
+        setPartialOverlay(false, Collections.emptyList());
     }
 
     /** Initializes dependencies. */
@@ -175,49 +182,71 @@
         mGrayOut.setColor(color);
     }
 
-    /** Enables/disables the filter. Clears all currently set touchable areas. */
-    public void setEnableFiltering(boolean enabled) {
-        if (mEnabled != enabled) {
-            clearTouchableArea();
-            mEnabled = enabled;
-            setAlpha(mEnabled ? 1.0f : 0.0f);
+    /**
+     * Enables/disables a full screen overlay.
+     *
+     * If both a full and a partial screen overlay are set, the partial overlay has precedence.
+     *
+     * @param enabled if {@code false}, the full screen overlay is disabled
+     */
+    public void setFullOverlay(boolean enabled) {
+        if (mFullOverlayEnabled != enabled) {
+            mFullOverlayEnabled = enabled;
 
-            // reset tap counter each time the filter is disabled.
-            if (!mEnabled) mUnexpectedTapTimes.clear();
-
+            // reset tap counter each time the full screen overlay is disabled.
+            if (!mFullOverlayEnabled) mUnexpectedTapTimes.clear();
+            updateVisibility();
             invalidate();
         }
     }
 
     /**
-     * Defines the area of the visible viewport that can be used.
+     * Enables/disables a partial screen overlay.
      *
-     * @param enabled if {@code false}, the filter is fully disabled and invisible
+     * If both a full and a partial screen overlay are set, the partial overlay has precedence.
+     *
+     * @param enabled if {@code false}, the partial overlay is disabled
      * @param rectangles rectangles defining the area that can be used, may be empty
      */
-    public void updateTouchableArea(boolean enabled, List<RectF> rectangles) {
-        setEnableFiltering(enabled);
-        if (!mTouchableArea.equals(rectangles)) {
+    public void setPartialOverlay(boolean enabled, List<RectF> rectangles) {
+        if (mPartialOverlayEnabled != enabled || (enabled && !mTouchableArea.equals(rectangles))) {
+            mPartialOverlayEnabled = enabled;
+
             clearTouchableArea();
             mTouchableArea.addAll(rectangles);
+            updateVisibility();
+            invalidate();
         }
     }
 
+    private boolean isOverlayShown() {
+        return mFullOverlayEnabled || mPartialOverlayEnabled;
+    }
+
+    private void updateVisibility() {
+        setAlpha(isOverlayShown() ? 1.0f : 0.0f);
+    }
+
     private void clearTouchableArea() {
         mTouchableArea.clear();
         mOffsetY = 0;
         mInitialBrowserScrollOffsetY += mBrowserScrollOffsetY;
         mBrowserScrollOffsetY = 0;
-        invalidate();
     }
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent event) {
+        // Note that partial overlays have precedence over full overlays
+        if (mPartialOverlayEnabled) return dispatchTouchEventWithPartialOverlay(event);
+        if (mFullOverlayEnabled) return dispatchTouchEventWithFullOverlay(event);
+        return false;
+    }
+
+    private boolean dispatchTouchEventWithPartialOverlay(MotionEvent event) {
         switch (event.getAction()) {
             case MotionEvent.ACTION_SCROLL:
                 // Scrolling is always safe. Let it through.
                 return false;
-
             case MotionEvent.ACTION_MOVE:
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
@@ -236,8 +265,6 @@
             // fallthrough
 
             case MotionEvent.ACTION_DOWN:
-                if (!mEnabled) return false; // let everything through
-
                 // Only let through events if they're meant for the touchable area of the screen.
                 int yTop = getVisualViewportTop();
                 int yBottom = getVisualViewportBottom();
@@ -259,12 +286,17 @@
         }
     }
 
+    private boolean dispatchTouchEventWithFullOverlay(MotionEvent event) {
+        mNonBrowserGesture.onTouchEvent(event);
+        return true;
+    }
+
     /** Returns the origin of the visual viewport in this view. */
     @Override
     @SuppressLint("CanvasSize")
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        if (!mEnabled) {
+        if (!isOverlayShown()) {
             return;
         }
         canvas.drawPaint(mGrayOut);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index e0cff14..ac39128 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -110,7 +110,9 @@
 import org.chromium.chrome.browser.webapps.WebappCustomTabTimeSpentLogger;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.content_public.browser.NavigationHistory;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.PageTransition;
 
@@ -370,6 +372,32 @@
         }
     }
 
+    /**
+     * @return The index of the previous navigation history entry managed by a dynamic module
+     * or -1 if there is no such entry.
+     */
+    private boolean goToModuleManagedNavigationIndex() {
+        if (mModuleActivityDelegate == null && mModuleCallback == null) return false;
+        NavigationController navigationController = getNavigationController();
+        if (navigationController == null) return false;
+
+        NavigationHistory history = navigationController.getNavigationHistory();
+        for (int i = history.getCurrentEntryIndex() - 1; i >= 0; i--) {
+            if (isModuleManagedUrl(history.getEntryAtIndex(i).getUrl())) {
+                navigationController.goToNavigationIndex(i);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Nullable
+    private NavigationController getNavigationController() {
+        WebContents webContents = getActivityTab().getWebContents();
+        return webContents == null ? null : webContents.getNavigationController();
+    }
+
     @VisibleForTesting
     void maybeInitialiseDynamicModulePostMessageHandler(PostMessageBackend backend) {
         // Only initialise the handler if the feature is enabled.
@@ -592,6 +620,11 @@
                         if (mIntentDataProvider.shouldEnableEmbeddedMediaExperience()) {
                             RecordUserAction.record("CustomTabs.CloseButtonClicked.DownloadsUI");
                         }
+                        if (goToModuleManagedNavigationIndex()) {
+                            RecordUserAction.record(
+                                    "CustomTabs.CloseButtonClicked.GoToModuleManagedUrl");
+                            return;
+                        }
                         recordClientConnectionStatus();
                         finishAndClose(false);
                     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index 29a9db42..97d4b0d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -134,10 +134,12 @@
             "org.chromium.chrome.browser.customtabs.EXTRA_MODULE_MANAGED_URLS_REGEX";
 
     /** The APK package to load the module from. */
+    @VisibleForTesting
     /* package */ static final String EXTRA_MODULE_PACKAGE_NAME =
             "org.chromium.chrome.browser.customtabs.EXTRA_MODULE_PACKAGE_NAME";
 
     /** The class name of the module entry point. */
+    @VisibleForTesting
     /* package */ static final String EXTRA_MODULE_CLASS_NAME =
             "org.chromium.chrome.browser.customtabs.EXTRA_MODULE_CLASS_NAME";
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java
new file mode 100644
index 0000000..6af94f2
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java
@@ -0,0 +1,107 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.notifications;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.support.annotation.IntDef;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Random;
+
+/**
+ * Class to intercept {@link PendingIntent}s from notifications, including
+ * {@link Notification#contentIntent}, {@link Notification.Action#actionIntent} and
+ * {@link Notification#deleteIntent} with broadcast receivers.
+ */
+public class NotificationIntentInterceptor {
+    private static final String TAG = "IntentInterceptor";
+    private static final String EXTRA_PENDING_INTENT =
+            "notifications.NotificationIntentInterceptor.EXTRA_PENDING_INTENT";
+    private static final String EXTRA_INTENT_TYPE =
+            "notifications.NotificationIntentInterceptor.EXTRA_INTENT_TYPE";
+
+    /**
+     * Enum that defines type of notification intent.
+     */
+    @IntDef({IntentType.CONTENT_INTENT, IntentType.ACTION_INTENT, IntentType.DELETE_INTENT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IntentType {
+        int CONTENT_INTENT = 0;
+        int ACTION_INTENT = 1;
+        int DELETE_INTENT = 2;
+    }
+
+    /**
+     * Receives the event when the user taps on the notification body, notification action, or
+     * dismiss notification.
+     * {@link Notification#contentIntent}, {@link Notification#deleteIntent}
+     * {@link Notification.Action#actionIntent} will be delivered to this broadcast receiver.
+     */
+    public static final class Receiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // TODO(xingliu): Record notification CTR UMA.
+            forwardPendingIntent(intent);
+        }
+    }
+
+    private NotificationIntentInterceptor() {}
+
+    /**
+     * Wraps the notification {@link PendingIntent }into another PendingIntent, to intercept clicks
+     * and dismiss events for metrics purpose.
+     * @param intentType The type of the pending intent to intercept.
+     * @param pendingIntent The {@link PendingIntent} of the notification that performs actual task.
+     */
+    public static PendingIntent createInterceptPendingIntent(
+            @IntentType int intentType, PendingIntent pendingIntent) {
+        Context applicationContext = ContextUtils.getApplicationContext();
+        Intent intent = new Intent(applicationContext, Receiver.class);
+        intent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
+        intent.putExtra(EXTRA_INTENT_TYPE, intentType);
+        // TODO(xingliu): Add more extras to track notification type and action type. Use the
+        // combination of notification type and id as the request code.
+        int requestCode = new Random().nextInt(Integer.MAX_VALUE);
+
+        // This flag ensures the broadcast is delivered with foreground priority to speed up the
+        // broadcast delivery.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        }
+        return PendingIntent.getBroadcast(
+                applicationContext, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
+    // Launches the notification's pending intent, which will perform Chrome feature related tasks.
+    private static void forwardPendingIntent(Intent intent) {
+        if (intent == null) {
+            Log.e(TAG, "Intent to forward is null.");
+            return;
+        }
+
+        PendingIntent pendingIntent =
+                (PendingIntent) (intent.getParcelableExtra(EXTRA_PENDING_INTENT));
+        if (pendingIntent == null) {
+            Log.d(TAG, "The notification's PendingIntent is null.");
+            return;
+        }
+
+        try {
+            pendingIntent.send();
+        } catch (PendingIntent.CanceledException e) {
+            Log.e(TAG, "The PendingIntent to fire is canceled.");
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index c41b9f9a..6b3bced1 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -967,6 +967,7 @@
   "java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java",
   "java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java",
   "java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java",
+  "java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java",
   "java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java",
   "java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java",
   "java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java",
@@ -2011,6 +2012,7 @@
   "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilderTest.java",
   "javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java",
+  "javatests/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java",
   "javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java",
   "javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java",
   "javatests/src/org/chromium/chrome/browser/notifications/NotificationTestRule.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index cbd6bd50e..5ce67cd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -42,6 +42,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
+import android.support.test.uiautomator.UiDevice;
 import android.support.v7.content.res.AppCompatResources;
 import android.text.TextUtils;
 import android.view.Menu;
@@ -77,6 +78,7 @@
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.AppHooksModule;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -88,6 +90,7 @@
 import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils;
 import org.chromium.chrome.browser.browserservices.Origin;
 import org.chromium.chrome.browser.browserservices.OriginVerifier;
+import org.chromium.chrome.browser.dependency_injection.ModuleFactoryOverrides;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.browser.history.BrowsingHistoryBridge;
@@ -117,6 +120,7 @@
 import org.chromium.chrome.test.util.browser.contextmenu.ContextMenuUtils;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContentsObserver;
+import org.chromium.content_public.browser.test.util.ClickUtils;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.DOMUtils;
@@ -124,12 +128,14 @@
 import org.chromium.content_public.browser.test.util.WebContentsUtils;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.net.test.util.TestWebServer;
+import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -220,6 +226,9 @@
         PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
         LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
         mWebServer = TestWebServer.start();
+
+        ModuleFactoryOverrides.setOverride(AppHooksModule.Factory.class,
+                CustomTabsDynamicModuleTestUtils.AppHooksModuleForTest::new);
     }
 
     @After
@@ -236,6 +245,8 @@
             if (handler != null) handler.hideAppMenu();
         });
         mWebServer.shutdown();
+
+        ModuleFactoryOverrides.clearOverrides();
     }
 
     private CustomTabActivity getActivity() {
@@ -1110,6 +1121,119 @@
         }
     }
 
+    private void runAndWaitForActivityStopped(Runnable runnable)
+            throws TimeoutException, InterruptedException {
+        CallbackHelper cctHiddenCallback = new CallbackHelper();
+        ActivityStateListener listener = (activity, newState) -> {
+            if (activity == mCustomTabActivityTestRule.getActivity()
+                    && (newState == ActivityState.STOPPED || newState == ActivityState.DESTROYED)) {
+                cctHiddenCallback.notifyCalled();
+            }
+        };
+        ApplicationStatus.registerStateListenerForAllActivities(listener);
+
+        runnable.run();
+        cctHiddenCallback.waitForCallback("Hide cct", 0);
+        ApplicationStatus.unregisterActivityStateListener(listener);
+    }
+
+    /**
+     This test executes the following workflow assuming dynamic module has been loaded succesfully:
+      - moduleManagedUrl1 -> nav1.1 -> nav1.2 -> modulemanagedUrl2 -> nav2.1 -> nav2.2
+      - User hits the "close button", therefore goes back to modulemanagedUrl2
+      - User hits the Android back button, going returning to nav1.2
+      - User hits the "close button" again, going return to moduleManagedUrl1
+      - User hits the Android back button thereby closes CCT.
+     */
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.CCT_MODULE)
+    public void testCloseButtonBehaviourWithDynamicModule()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        String moduleManagedUrl1 = "https://www.google.com/search?q=cat";
+        String moduleManagedUrl2 = "https://www.google.com/search?q=dog";
+
+        Intent intent = CustomTabsDynamicModuleTestUtils.makeDynamicModuleIntent(
+                moduleManagedUrl1, "^https://www.google.com/search.*");
+
+        // Open CCT with moduleManagedUrl1 and navigate
+        // moduleManagedUrl1 -> nav1.1 - nav1.2 -> modulemanagedUrl2 -> nav2.1 -> nav2.2
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        CustomTabActivity cctActivity = mCustomTabActivityTestRule.getActivity();
+
+        mCustomTabActivityTestRule.loadUrlInTab(mTestPage, PageTransition.LINK,
+                cctActivity.getActivityTab());
+        mCustomTabActivityTestRule.loadUrlInTab(mTestPage2, PageTransition.LINK,
+                cctActivity.getActivityTab());
+        mCustomTabActivityTestRule.loadUrlInTab(moduleManagedUrl2, PageTransition.TYPED,
+                cctActivity.getActivityTab());
+        mCustomTabActivityTestRule.loadUrlInTab(mTestPage, PageTransition.LINK,
+                cctActivity.getActivityTab());
+        mCustomTabActivityTestRule.loadUrlInTab(mTestPage2, PageTransition.LINK,
+                cctActivity.getActivityTab());
+
+        // click the close button and wait while tab page loaded
+        ClickUtils.clickButton(cctActivity.findViewById(R.id.close_button));
+        ChromeTabUtils.waitForTabPageLoaded(cctActivity.getActivityTab(), (String) null);
+
+        // close button returns back to moduleManagedUrl2
+        Assert.assertEquals(moduleManagedUrl2, cctActivity.getActivityTab().getUrl());
+
+        // press the back button and wait while tab page loaded
+        UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mDevice.pressBack();
+        ChromeTabUtils.waitForTabPageLoaded(cctActivity.getActivityTab(), (String) null);
+
+        // the back button returns to nav1.2
+        Assert.assertEquals(mTestPage2, cctActivity.getActivityTab().getUrl());
+
+        // click the close button and wait while tab page loaded
+        ClickUtils.clickButton(cctActivity.findViewById(R.id.close_button));
+        ChromeTabUtils.waitForTabPageLoaded(cctActivity.getActivityTab(), (String) null);
+
+        // close button returns back to moduleManagedUrl1
+        Assert.assertEquals(moduleManagedUrl1, cctActivity.getActivityTab().getUrl());
+
+        // press back button and while cct is hidden
+        runAndWaitForActivityStopped(mDevice::pressBack);
+    }
+
+    /**
+     This test executes the following workflow assuming dynamic module has not been loaded:
+     - moduleManagedUrl1 -> nav1.1 - nav1.2 -> modulemanagedUrl2 -> nav2.1 -> nav2.2
+     - User hits the close button, thereby closes CCT
+     */
+    @Test
+    @SmallTest
+    public void testCloseButtonBehaviourWithoutDynamicModule()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        String moduleManagedUrl1 = "https://www.google.com/search?q=cat";
+        String moduleManagedUrl2 = "https://www.google.com/search?q=dog";
+
+        // Open CCT with moduleManagedUrl1 and navigate
+        // moduleManagedUrl1 -> nav1.1 - nav1.2 -> modulemanagedUrl2 -> nav2.1 -> nav2.2
+
+        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(
+                InstrumentationRegistry.getTargetContext(), moduleManagedUrl1);
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        CustomTabActivity cctActivity = mCustomTabActivityTestRule.getActivity();
+
+        mCustomTabActivityTestRule.loadUrlInTab(mTestPage, PageTransition.LINK,
+                cctActivity.getActivityTab());
+        mCustomTabActivityTestRule.loadUrlInTab(mTestPage2, PageTransition.LINK,
+                cctActivity.getActivityTab());
+        mCustomTabActivityTestRule.loadUrlInTab(moduleManagedUrl2, PageTransition.LINK,
+                cctActivity.getActivityTab());
+        mCustomTabActivityTestRule.loadUrlInTab(mTestPage, PageTransition.LINK,
+                cctActivity.getActivityTab());
+        mCustomTabActivityTestRule.loadUrlInTab(mTestPage2, PageTransition.LINK,
+                cctActivity.getActivityTab());
+
+        // click close button and wait while cct is hidden
+        runAndWaitForActivityStopped(() ->
+                ClickUtils.clickButton(cctActivity.findViewById(R.id.close_button)));
+    }
+
     @Test
     @SmallTest
     @RetryOnFailure
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
new file mode 100644
index 0000000..0de0c5b
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
@@ -0,0 +1,120 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.notifications;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+
+/**
+ * Test to verify intercepting notification pending intents with broadcast receiver.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class NotificationIntentInterceptorTest {
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    private static final String TEST_NOTIFICATION_TITLE = "Test notification title.";
+    private static final long WAIT_FOR_NOTIFICATION_SHOWN_TIMEOUT_MILLISECONDS = 3000;
+
+    @Before
+    public void setUp() throws Exception {
+        mActivityTestRule.startMainActivityOnBlankPage();
+    }
+
+    // Builds a simple notification used in tests.
+    private Notification buildSimpleNotification(String title) {
+        ChromeNotificationBuilder builder =
+                NotificationBuilderFactory.createChromeNotificationBuilder(
+                        true /* preferCompat */, ChannelDefinitions.ChannelId.DOWNLOADS);
+
+        // Set content intent. UI automator may tap the notification and expand the action buttons,
+        // in order to reduce flakiness, don't add action button.
+        Context context = ContextUtils.getApplicationContext();
+        Intent contentIntent = new Intent(context, ChromeTabbedActivity.class);
+        Uri uri = Uri.parse("www.example.com");
+        contentIntent.setData(uri);
+        contentIntent.setAction(Intent.ACTION_VIEW);
+        int flags = PendingIntent.FLAG_ONE_SHOT;
+        PendingIntent contentPendingIntent =
+                PendingIntent.getActivity(context, 0, contentIntent, flags);
+        assert contentPendingIntent != null;
+        builder.setContentIntent(NotificationIntentInterceptor.createInterceptPendingIntent(
+                NotificationIntentInterceptor.IntentType.CONTENT_INTENT, contentPendingIntent));
+        builder.setContentTitle(title);
+        builder.setSmallIcon(R.drawable.offline_pin);
+        return builder.build();
+    }
+
+    /**
+     * Clicks the notification with UI automator. Notice the notification bar is not part of the
+     * app, so we have to use UI automator.
+     * @param text The text of notification UI.
+     */
+    private void clickNotification(String text) {
+        UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        device.openNotification();
+        device.wait(
+                Until.hasObject(By.text(text)), WAIT_FOR_NOTIFICATION_SHOWN_TIMEOUT_MILLISECONDS);
+        UiObject2 textObject = device.findObject(By.text(text));
+        Assert.assertEquals(text, textObject.getText());
+        textObject.click();
+    }
+
+    /**
+     * Verifies {@link Notification#contentIntent} can be intercepted by broadcast receiver.
+     * Action button and dismiss have no test coverage due to difficulty in simulation with UI
+     * automator. On different Android version, the way to dismiss or find the action button can be
+     * different.
+     */
+    @Test
+    @MediumTest
+    public void testContentIntentInterception() {
+        // Send notification.
+        NotificationManager notificationManager =
+                (NotificationManager) ContextUtils.getApplicationContext().getSystemService(
+                        Context.NOTIFICATION_SERVICE);
+        notificationManager.notify(0, buildSimpleNotification(TEST_NOTIFICATION_TITLE));
+
+        // Click notification body.
+        clickNotification(TEST_NOTIFICATION_TITLE);
+
+        // Wait for another tab to load.
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mActivityTestRule.tabsCount(false) > 1;
+            }
+        });
+        notificationManager.cancelAll();
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerDisableIncognitoModeIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerDisableIncognitoModeIntegrationTest.java
index 82872e3..53ae1ba 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerDisableIncognitoModeIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerDisableIncognitoModeIntegrationTest.java
@@ -155,7 +155,7 @@
             waitForParentalControlsEnabledState(true);
 
             CriteriaHelper.pollInstrumentationThread(
-                    Criteria.equals(0, () -> mActivityTestRule.incognitoTabsCount()));
+                    Criteria.equals(0, () -> mActivityTestRule.tabsCount(true /* incognito */)));
         } finally {
             testServer.stopAndDestroyServer();
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
index d55f8e58..0421051 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
@@ -462,12 +462,12 @@
      * @throws InterruptedException
      */
     @Test
-    @MediumTest
-    // TODO(jbudorick): Replace with DisableIf when it supports filtering by device type.
-    // Flaky on tablets, crbug.com/620014.
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @RetryOnFailure
-    public void testTwoTabs() throws InterruptedException, TimeoutException {
+    // @MediumTest
+    // @RetryOnFailure
+    @DisabledTest(
+            message = "Flaky on all Android configurations except Swarming.  See crbug.com/620014.")
+    public void
+    testTwoTabs() throws InterruptedException, TimeoutException {
         TabModel model = mActivityTestRule.getActivity().getTabModelSelector().getModel(false);
         ChromeTabCreator tabCreator = mActivityTestRule.getActivity().getTabCreator(false);
         createTabOnUiThread(tabCreator);
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 6d72834d..d484067 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5080,6 +5080,9 @@
       <message name="IDS_NEWTAB_PROMO_2" desc="Variations option 3. Text shown on promotional UI appearing next to the New Tab button, which encourages users to use it.">
         Open a new tab to browse two sites at once
       </message>
+      <message name="IDS_REOPEN_TAB_PROMO" desc="Text shown on promotional UI appearing next to the app menu button">
+        Reopen a tab if you accidentally closed it
+      </message>
 
       <!-- Browser Hung Plugin Detector -->
       <if expr="is_win">
diff --git a/chrome/app/generated_resources_grd/IDS_REOPEN_TAB_PROMO.png.sha1 b/chrome/app/generated_resources_grd/IDS_REOPEN_TAB_PROMO.png.sha1
new file mode 100644
index 0000000..03fb3fa
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_REOPEN_TAB_PROMO.png.sha1
@@ -0,0 +1 @@
+6e3827c2634ca8ef00f34a4df80367296252542b
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9dc3120..ee72a15 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -461,20 +461,6 @@
     {flag_descriptions::kNumRasterThreadsFour, switches::kNumRasterThreads,
      "4"}};
 
-const FeatureEntry::Choice kGpuRasterizationMSAASampleCountChoices[] = {
-    {flags_ui::kGenericExperimentChoiceDefault, "", ""},
-    {flag_descriptions::kGpuRasterizationMsaaSampleCountZero,
-     switches::kGpuRasterizationMSAASampleCount, "0"},
-    {flag_descriptions::kGpuRasterizationMsaaSampleCountTwo,
-     switches::kGpuRasterizationMSAASampleCount, "2"},
-    {flag_descriptions::kGpuRasterizationMsaaSampleCountFour,
-     switches::kGpuRasterizationMSAASampleCount, "4"},
-    {flag_descriptions::kGpuRasterizationMsaaSampleCountEight,
-     switches::kGpuRasterizationMSAASampleCount, "8"},
-    {flag_descriptions::kGpuRasterizationMsaaSampleCountSixteen,
-     switches::kGpuRasterizationMSAASampleCount, "16"},
-};
-
 const FeatureEntry::Choice kMHTMLGeneratorOptionChoices[] = {
     {flags_ui::kGenericExperimentChoiceDefault, "", ""},
     {flag_descriptions::kMhtmlSkipNostoreMain, switches::kMHTMLGeneratorOption,
@@ -1571,10 +1557,6 @@
     {"enable-oop-rasterization", flag_descriptions::kOopRasterizationName,
      flag_descriptions::kOopRasterizationDescription, kOsAll,
      MULTI_VALUE_TYPE(kEnableOopRasterizationChoices)},
-    {"gpu-rasterization-msaa-sample-count",
-     flag_descriptions::kGpuRasterizationMsaaSampleCountName,
-     flag_descriptions::kGpuRasterizationMsaaSampleCountDescription, kOsAll,
-     MULTI_VALUE_TYPE(kGpuRasterizationMSAASampleCountChoices)},
     {"enable-experimental-web-platform-features",
      flag_descriptions::kExperimentalWebPlatformFeaturesName,
      flag_descriptions::kExperimentalWebPlatformFeaturesDescription, kOsAll,
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 946b85d2..16d6dda 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -2961,6 +2961,70 @@
 }
 
 // Test that we can autofill forms that dynamically change the element that
+// has been clicked on, even though there are multiple forms with identical
+// names.
+IN_PROC_BROWSER_TEST_P(
+    AutofillDynamicFormInteractiveTest,
+    DynamicFormFill_FirstElementDisappearsMultipleBadNameForms) {
+  CreateTestProfile();
+
+  GURL url = embedded_test_server()->GetURL(
+      "a.com",
+      "/autofill/dynamic_form_element_invalid_multiple_badname_forms.html");
+
+  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(), url));
+
+  TriggerFormFill("firstname_5");
+
+  // Wait for the re-fill to happen.
+  bool has_refilled = false;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      GetWebContents(), "hasRefilled()", &has_refilled));
+
+  ASSERT_TRUE(has_refilled);
+  // Make sure the second form was filled correctly, and the first form was left
+  // unfilled.
+  ExpectFieldValue("firstname_1", "");
+  ExpectFieldValue("firstname_2", "");
+  ExpectFieldValue("address1_3", "");
+  ExpectFieldValue("country_4", "CA");  // default
+  ExpectFieldValue("firstname_6", "Milton");
+  ExpectFieldValue("address1_7", "4120 Freidrich Lane");
+  ExpectFieldValue("country_8", "US");
+}
+
+// Test that we can autofill forms that dynamically change the element that
+// has been clicked on, even though there are multiple forms with identical
+// names.
+IN_PROC_BROWSER_TEST_P(AutofillDynamicFormInteractiveTest,
+                       DynamicFormFill_FirstElementDisappearsBadnameUnowned) {
+  CreateTestProfile();
+
+  GURL url = embedded_test_server()->GetURL(
+      "a.com", "/autofill/dynamic_form_element_invalid_unowned_badnames.html");
+
+  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(), url));
+
+  TriggerFormFill("firstname_5");
+
+  // Wait for the re-fill to happen.
+  bool has_refilled = false;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      GetWebContents(), "hasRefilled()", &has_refilled));
+
+  ASSERT_TRUE(has_refilled);
+  // Make sure the second form was filled correctly, and the first form was left
+  // unfilled.
+  ExpectFieldValue("firstname_1", "");
+  ExpectFieldValue("firstname_2", "");
+  ExpectFieldValue("address1_3", "");
+  ExpectFieldValue("country_4", "CA");  // default
+  ExpectFieldValue("firstname_6", "Milton");
+  ExpectFieldValue("address1_7", "4120 Freidrich Lane");
+  ExpectFieldValue("country_8", "US");
+}
+
+// Test that we can autofill forms that dynamically change the element that
 // has been clicked on, even though the elements are unowned.
 IN_PROC_BROWSER_TEST_P(AutofillDynamicFormInteractiveTest,
                        DynamicFormFill_FirstElementDisappearsUnowned) {
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index b16c80a..c2d663c 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -45,7 +45,6 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/browsing_data/core/browsing_data_utils.h"
-#include "components/browsing_data/core/features.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_reconcilor.h"
@@ -284,8 +283,7 @@
  public:
   BrowsingDataRemoverBrowserTest() {
     feature_list_.InitWithFeatures(
-        {browsing_data::features::kRemoveNavigationHistory,
-         leveldb::kLevelDBRewriteFeature,
+        {leveldb::kLevelDBRewriteFeature,
          // Ensure that kOnionSoupDOMStorage is enabled because the old
          // SessionStorage implementation causes flaky tests.
          blink::features::kOnionSoupDOMStorage},
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 7f77886..61fcb1c 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -53,7 +53,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h"
@@ -64,7 +63,6 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/bookmarks/browser/bookmark_model.h"
-#include "components/browsing_data/core/features.h"
 #include "components/content_settings/core/browser/content_settings_registry.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
@@ -87,13 +85,13 @@
 #include "components/prefs/pref_service.h"
 #include "components/previews/content/previews_ui_service.h"
 #include "components/search_engines/template_url_service.h"
-#include "components/sessions/core/tab_restore_service.h"
 #include "components/web_cache/browser/web_cache_manager.h"
 #include "components/webrtc_logging/browser/log_cleanup.h"
 #include "components/webrtc_logging/browser/text_log_list.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browsing_data_filter_builder.h"
+#include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/plugin_data_remover.h"
 #include "content/public/browser/ssl_host_state_delegate.h"
 #include "content/public/browser/storage_partition.h"
@@ -122,11 +120,6 @@
 #include "extensions/common/constants.h"
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-#if BUILDFLAG(ENABLE_SESSION_SERVICE)
-#include "chrome/browser/sessions/session_service.h"
-#include "chrome/browser/sessions/session_service_factory.h"
-#endif  // BUILDFLAG(ENABLE_SESSION_SERVICE)
-
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
@@ -500,33 +493,6 @@
           prerender::PrerenderManager::CLEAR_PRERENDER_HISTORY);
     }
 
-    // When this feature is enabled, recent tabs and sessions will be deleted
-    // by NavigationEntryRemover and not here.
-    bool is_navigation_entry_remover_enabled = base::FeatureList::IsEnabled(
-        browsing_data::features::kRemoveNavigationHistory);
-
-    // If the caller is removing history for all hosts, then clear ancillary
-    // historical information.
-    if (!is_navigation_entry_remover_enabled &&
-        filter_builder.GetMode() == BrowsingDataFilterBuilder::BLACKLIST) {
-      // We also delete the list of recently closed tabs. Since these expire,
-      // they can't be more than a day old, so we can simply clear them all.
-      sessions::TabRestoreService* tab_service =
-          TabRestoreServiceFactory::GetForProfile(profile_);
-      if (tab_service) {
-        tab_service->ClearEntries();
-        tab_service->DeleteLastSession();
-      }
-
-#if BUILDFLAG(ENABLE_SESSION_SERVICE)
-      // We also delete the last session when we delete the history.
-      SessionService* session_service =
-          SessionServiceFactory::GetForProfile(profile_);
-      if (session_service)
-        session_service->DeleteLastSession();
-#endif
-    }
-
     // The saved Autofill profiles and credit cards can include the origin from
     // which these profiles and credit cards were learned.  These are a form of
     // history, so clear them as well.
diff --git a/chrome/browser/browsing_data/navigation_entry_remover.cc b/chrome/browser/browsing_data/navigation_entry_remover.cc
index 0896c266..41bedc1 100644
--- a/chrome/browser/browsing_data/navigation_entry_remover.cc
+++ b/chrome/browser/browsing_data/navigation_entry_remover.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/common/buildflags.h"
-#include "components/browsing_data/core/features.h"
 #include "components/sessions/core/serialized_navigation_entry.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/sessions/core/tab_restore_service_observer.h"
@@ -216,10 +215,6 @@
                              const history::DeletionInfo& deletion_info) {
   DCHECK(profile->GetProfileType() == Profile::ProfileType::REGULAR_PROFILE);
   DCHECK(!deletion_info.is_from_expiration());
-  if (!base::FeatureList::IsEnabled(
-          browsing_data::features::kRemoveNavigationHistory)) {
-    return;
-  }
 
   base::flat_set<GURL> url_set;
   if (!deletion_info.time_range().IsValid())
diff --git a/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc b/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
index a11c7d7..6a201c23 100644
--- a/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "base/files/file_path.h"
-#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browsing_data/navigation_entry_remover.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
@@ -13,7 +12,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/browsing_data/core/features.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -28,8 +26,6 @@
 class NavigationEntryRemoverTest : public InProcessBrowserTest {
  protected:
   void SetUpOnMainThread() override {
-    feature_list_.InitWithFeatures(
-        {browsing_data::features::kRemoveNavigationHistory}, {});
     auto path = base::FilePath(FILE_PATH_LITERAL("browsing_data"));
     url_a_ = ui_test_utils::GetTestUrl(
         path, base::FilePath(FILE_PATH_LITERAL("a.html")));
@@ -107,8 +103,6 @@
   GURL url_d_;
   GURL about_blank_;
 
- private:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 // === Tests for helper functions ===
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index eaa2f776..d9a9d11 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -610,33 +610,6 @@
 #endif
 }
 
-bool WaitUntilMachineLevelUserCloudPolicyEnrollmentFinished(
-    policy::ChromeBrowserPolicyConnector* connector) {
-#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
-  using RegisterResult =
-      policy::MachineLevelUserCloudPolicyController::RegisterResult;
-  switch (connector->machine_level_user_cloud_policy_controller()
-              ->WaitUntilPolicyEnrollmentFinished()) {
-    case RegisterResult::kNoEnrollmentNeeded:
-    case RegisterResult::kEnrollmentSuccessBeforeDialogDisplayed:
-      return true;
-    case RegisterResult::kEnrollmentSuccess:
-#if defined(OS_MACOSX)
-      app_controller_mac::EnterpriseStartupDialogClosed();
-#endif
-      return true;
-    case RegisterResult::kRestartDueToFailure:
-      chrome::AttemptRestart();
-      return false;
-    case RegisterResult::kQuitDueToFailure:
-      chrome::AttemptExit();
-      return false;
-  }
-#else
-  return true;
-#endif
-}
-
 // Sets up the ThreadProfiler for the browser process, runs it, and returns the
 // profiler.
 std::unique_ptr<ThreadProfiler> CreateAndStartBrowserMainThreadProfiler() {
@@ -1373,13 +1346,16 @@
   // running.
   browser_process_->PreMainMessageLoopRun();
 
+#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
   // Wait for the result of machine level user cloud policy enrollment after
   // we start the enrollment process in browser_porcess_->PreMainMessageLoopRun.
   // Abort the launch process if the enrollment is failed.
-  if (!WaitUntilMachineLevelUserCloudPolicyEnrollmentFinished(
-          browser_process_->browser_policy_connector())) {
+  if (!browser_process_->browser_policy_connector()
+           ->machine_level_user_cloud_policy_controller()
+           ->WaitUntilPolicyEnrollmentFinished()) {
     return chrome::RESULT_CODE_CLOUD_POLICY_ENROLLMENT_FAILED;
   }
+#endif
 
   // Record last shutdown time into a histogram.
   browser_shutdown::ReadLastShutdownInfo();
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 772da05..4996d130 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1573,8 +1573,6 @@
     "policy/status_uploader.h",
     "policy/system_log_uploader.cc",
     "policy/system_log_uploader.h",
-    "policy/temp_certs_cache_nss.cc",
-    "policy/temp_certs_cache_nss.h",
     "policy/ticl_device_settings_provider.cc",
     "policy/ticl_device_settings_provider.h",
     "policy/upload_job.h",
@@ -2303,7 +2301,6 @@
     "policy/single_app_install_event_log_unittest.cc",
     "policy/status_uploader_unittest.cc",
     "policy/system_log_uploader_unittest.cc",
-    "policy/temp_certs_cache_nss_unittest.cc",
     "policy/upload_job_unittest.cc",
     "policy/user_cloud_policy_manager_chromeos_unittest.cc",
     "policy/user_cloud_policy_store_chromeos_unittest.cc",
diff --git a/chrome/browser/chromeos/DEPS b/chrome/browser/chromeos/DEPS
index 2345595e7e..b9346a7 100644
--- a/chrome/browser/chromeos/DEPS
+++ b/chrome/browser/chromeos/DEPS
@@ -15,6 +15,7 @@
   "+remoting/host/it2me",  # For CRD host in remote command
   "+services/device/public",
   "+services/metrics/public",
+  "+services/network",
   "+services/tracing/public",
   "+services/viz/public/interfaces",
   # Chromeos should not use ozone directly, it must go through mojo as ozone
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
index c4d934da..9058bc0 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
@@ -139,19 +139,18 @@
     // need additional information contained only in the CollectionInfo. The
     // CollectionInfo should be an ancestor of this node.
     AXCollectionInfoData* collection_info = nullptr;
-    for (AXNodeInfoData* container = node_ptr_; container;) {
-      if (!container)
+    for (const ArcAccessibilityInfoData* container =
+             static_cast<const ArcAccessibilityInfoData*>(this);
+         container;) {
+      if (!container || !container->IsNode())
         break;
-      if (container->collection_info.get()) {
-        collection_info = container->collection_info.get();
+      if (container->IsNode() && container->GetNode()->collection_info.get()) {
+        collection_info = container->GetNode()->collection_info.get();
         break;
       }
 
-      ArcAccessibilityInfoData* container_data =
-          tree_source_->GetParent(tree_source_->GetFromId(container->id));
-      if (!container_data->IsNode())
-        break;
-      container = container_data->GetNode();
+      container =
+          tree_source_->GetParent(tree_source_->GetFromId(container->GetId()));
     }
 
     if (collection_info) {
@@ -387,6 +386,10 @@
                                              local_bounds.width(),
                                              local_bounds.height());
   }
+  // TODO(katie): Try using offset_container_id to make bounds calculations
+  // more efficient. If this is the child of the root, set the
+  // offset_container_id to be the root. Otherwise, set it to the first node
+  // child of the root.
 
   // Integer properties.
   int32_t val;
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
index ab1d070..d6bf312 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
@@ -239,6 +239,7 @@
   // Same task id, different package name.
   event2->node_data.clear();
   event2->node_data.push_back(arc::mojom::AccessibilityNodeInfoData::New());
+  event2->source_id = 3;
   event2->node_data[0]->id = 3;
   event2->node_data[0]->string_properties =
       base::flat_map<arc::mojom::AccessibilityStringProperty, std::string>();
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
index e01e61a..3859243 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
@@ -31,7 +31,7 @@
     : current_tree_serializer_(new AXTreeArcSerializer(this)),
       root_id_(-1),
       window_id_(-1),
-      focused_node_id_(-1),
+      focused_id_(-1),
       is_notification_(false),
       delegate_(delegate) {}
 
@@ -61,10 +61,30 @@
       continue;
     auto it = event_data->node_data[i]->int_list_properties->find(
         AXIntListProperty::CHILD_NODE_IDS);
-    if (it != event_data->node_data[i]->int_list_properties->end()) {
-      all_children_map[event_data->node_data[i]->id] = it->second;
+    if (it == event_data->node_data[i]->int_list_properties->end())
+      continue;
+    all_children_map[event_data->node_data[i]->id] = it->second;
+    for (size_t j = 0; j < it->second.size(); ++j)
+      all_parent_map[it->second[j]] = event_data->node_data[i]->id;
+  }
+  if (event_data->window_data) {
+    for (size_t i = 0; i < event_data->window_data->size(); ++i) {
+      int32_t window_id = event_data->window_data->at(i)->window_id;
+      int32_t root_node_id = event_data->window_data->at(i)->root_node_id;
+      if (root_node_id) {
+        all_parent_map[root_node_id] = window_id;
+        all_children_map[window_id] = {root_node_id};
+      }
+      if (!event_data->window_data->at(i)->int_list_properties)
+        continue;
+      auto it = event_data->window_data->at(i)->int_list_properties->find(
+          mojom::AccessibilityWindowIntListProperty::CHILD_WINDOW_IDS);
+      if (it == event_data->window_data->at(i)->int_list_properties->end())
+        continue;
+      all_children_map[window_id].insert(all_children_map[window_id].begin(),
+                                         it->second.begin(), it->second.end());
       for (size_t j = 0; j < it->second.size(); ++j)
-        all_parent_map[it->second[j]] = event_data->node_data[i]->id;
+        all_parent_map[it->second[j]] = window_id;
     }
   }
 
@@ -103,8 +123,19 @@
     tree_map_[id] =
         std::make_unique<AccessibilityNodeInfoDataWrapper>(this, node);
 
-    if (tree_map_[id]->IsFocused()) {
-      focused_node_id_ = id;
+    if (tree_map_[id]->IsFocused())
+      focused_id_ = id;
+  }
+  if (event_data->window_data) {
+    for (size_t i = 0; i < event_data->window_data->size(); ++i) {
+      int32_t id = event_data->window_data->at(i)->window_id;
+      // Only map nodes in the parent_map and the root.
+      // This avoids adding other subtrees that are not interesting.
+      if (parent_map_.find(id) == parent_map_.end() && id != root_id_)
+        continue;
+      AXWindowInfoData* window = event_data->window_data->at(i).get();
+      tree_map_[id] =
+          std::make_unique<AccessibilityWindowInfoDataWrapper>(this, window);
     }
   }
 
@@ -117,6 +148,14 @@
       continue;
     cached_computed_bounds_[id] = ComputeEnclosingBounds(tree_map_[id].get());
   }
+  if (event_data->window_data) {
+    for (int i = event_data->window_data->size() - 1; i >= 0; --i) {
+      int32_t id = event_data->window_data->at(i)->window_id;
+      if (parent_map_.find(id) == parent_map_.end() && id != root_id_)
+        continue;
+      cached_computed_bounds_[id] = ComputeEnclosingBounds(tree_map_[id].get());
+    }
+  }
 
   ExtensionMsg_AccessibilityEventBundleParams event_bundle;
   event_bundle.tree_id = tree_id();
@@ -147,10 +186,25 @@
 
 bool AXTreeSourceArc::GetTreeData(ui::AXTreeData* data) const {
   data->tree_id = tree_id();
-  if (focused_node_id_ >= 0)
-    data->focus_id = focused_node_id_;
-  else if (root_id_ >= 0)
-    data->focus_id = root_id_;
+  if (focused_id_ >= 0) {
+    data->focus_id = focused_id_;
+  } else if (root_id_ >= 0) {
+    ArcAccessibilityInfoData* root = GetRoot();
+    if (root->IsNode()) {
+      data->focus_id = root_id_;
+    } else {
+      std::vector<ArcAccessibilityInfoData*> children;
+      root->GetChildren(&children);
+      if (!children.empty()) {
+        for (size_t i = 0; i < children.size(); ++i) {
+          if (children[i]->IsNode()) {
+            data->focus_id = children[i]->GetId();
+            break;
+          }
+        }
+      }
+    }
+  }
   return true;
 }
 
@@ -261,9 +315,11 @@
 
   int32_t id = info_data->GetId();
   out_data->id = id;
-  // TODO(katie): this may not hold true with Windows. it's probably the root
-  // window's root node which is a kRootWebArea.
-  if (id == root_id_)
+  // If the node is the root, or if the node's parent is the root window,
+  // the role should be rootWebArea.
+  if (info_data->IsNode() && id == root_id_)
+    out_data->role = ax::mojom::Role::kRootWebArea;
+  else if (info_data->IsNode() && parent_map_.at(id) == root_id_)
     out_data->role = ax::mojom::Role::kRootWebArea;
   else
     info_data->PopulateAXRole(out_data);
@@ -275,46 +331,44 @@
                                            aura::Window* active_window) const {
   DCHECK_NE(root_id_, -1);
 
-  gfx::Rect node_bounds = info_data->GetBounds();
+  gfx::Rect info_data_bounds = info_data->GetBounds();
 
-  if (active_window && info_data->GetId() == root_id_) {
-    // Top level window returns its bounds in dip.
-    aura::Window* toplevel_window = active_window->GetToplevelWindow();
-    float scale = toplevel_window->layer()->device_scale_factor();
-
-    views::Widget* widget =
-        views::Widget::GetWidgetForNativeView(active_window);
-    DCHECK(widget);
-    DCHECK(widget->widget_delegate());
-    DCHECK(widget->widget_delegate()->GetContentsView());
-    const gfx::Rect bounds =
-        widget->widget_delegate()->GetContentsView()->GetBoundsInScreen();
-
-    // Bounds of root node is relative to its container, i.e. contents view
-    // (ShellSurfaceBase).
-    node_bounds.Offset(
-        static_cast<int>(-1.0f * scale * static_cast<float>(bounds.x())),
-        static_cast<int>(-1.0f * scale * static_cast<float>(bounds.y())));
-
-    // On Android side, content is rendered without considering height of
-    // caption bar, e.g. content is rendered at y:0 instead of y:32 where 32 is
-    // height of caption bar. Add back height of caption bar here.
-    if (widget->IsMaximized()) {
-      node_bounds.Offset(
-          0, static_cast<int>(scale *
-                              static_cast<float>(widget->non_client_view()
-                                                     ->frame_view()
-                                                     ->GetBoundsForClientView()
-                                                     .y())));
-    }
-
-    return node_bounds;
+  if (!active_window) {
+    gfx::Rect root_bounds = GetRoot()->GetBounds();
+    info_data_bounds.Offset(-1 * root_bounds.x(), -1 * root_bounds.y());
+    return info_data_bounds;
   }
 
-  // Bounds of non-root node is relative to its tree's root.
-  gfx::Rect root_bounds = GetFromId(root_id_)->GetBounds();
-  node_bounds.Offset(-1 * root_bounds.x(), -1 * root_bounds.y());
-  return node_bounds;
+  // TODO(katie): offset_container_id should work and we shouldn't have to
+  // go into this code path for each node.
+  aura::Window* toplevel_window = active_window->GetToplevelWindow();
+  float scale = toplevel_window->layer()->device_scale_factor();
+
+  views::Widget* widget = views::Widget::GetWidgetForNativeView(active_window);
+  DCHECK(widget);
+  DCHECK(widget->widget_delegate());
+  DCHECK(widget->widget_delegate()->GetContentsView());
+  const gfx::Rect bounds =
+      widget->widget_delegate()->GetContentsView()->GetBoundsInScreen();
+
+  // Bounds of root node is relative to its container, i.e. contents view
+  // (ShellSurfaceBase).
+  info_data_bounds.Offset(
+      static_cast<int>(-1.0f * scale * static_cast<float>(bounds.x())),
+      static_cast<int>(-1.0f * scale * static_cast<float>(bounds.y())));
+
+  // On Android side, content is rendered without considering height of
+  // caption bar, e.g. content is rendered at y:0 instead of y:32 where 32 is
+  // height of caption bar. Add back height of caption bar here.
+  if (widget->IsMaximized()) {
+    info_data_bounds.Offset(
+        0, static_cast<int>(scale *
+                            static_cast<float>(widget->non_client_view()
+                                                   ->frame_view()
+                                                   ->GetBoundsForClientView()
+                                                   .y())));
+  }
+  return info_data_bounds;
 }
 
 gfx::Rect AXTreeSourceArc::ComputeEnclosingBounds(
@@ -363,7 +417,7 @@
   cached_computed_bounds_.clear();
   current_tree_serializer_.reset(new AXTreeArcSerializer(this));
   root_id_ = -1;
-  focused_node_id_ = -1;
+  focused_id_ = -1;
   extensions::AutomationEventRouter* router =
       extensions::AutomationEventRouter::GetInstance();
   if (!router)
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
index c1aeaba..c3a96558 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
@@ -62,10 +62,10 @@
   // TODO(katie): should these be "friended" or "protected" instead?
   ArcAccessibilityInfoData* GetRoot() const override;
   ArcAccessibilityInfoData* GetFromId(int32_t id) const override;
-  void SerializeNode(ArcAccessibilityInfoData* node,
+  void SerializeNode(ArcAccessibilityInfoData* info_data,
                      ui::AXNodeData* out_data) const override;
   ArcAccessibilityInfoData* GetParent(
-      ArcAccessibilityInfoData* node) const override;
+      ArcAccessibilityInfoData* info_data) const override;
 
   // Returns bounds of a node which can be passed to AXNodeData.location. Bounds
   // are returned in the following coordinates depending on whether it's root or
@@ -74,7 +74,7 @@
   // - Non-root node is relative to the root node of this tree.
   //
   // focused_window is nullptr for notification.
-  const gfx::Rect GetBounds(ArcAccessibilityInfoData* node,
+  const gfx::Rect GetBounds(ArcAccessibilityInfoData* info_data,
                             aura::Window* focused_window) const;
 
   bool is_notification() { return is_notification_; }
@@ -84,21 +84,22 @@
   class FocusStealer;
 
   // AXTreeSource overrides.
-  int32_t GetId(ArcAccessibilityInfoData* node) const override;
+  int32_t GetId(ArcAccessibilityInfoData* info_data) const override;
   void GetChildren(
-      ArcAccessibilityInfoData* node,
+      ArcAccessibilityInfoData* info_data,
       std::vector<ArcAccessibilityInfoData*>* out_children) const override;
-  bool IsValid(ArcAccessibilityInfoData* node) const override;
-  bool IsEqual(ArcAccessibilityInfoData* node1,
-               ArcAccessibilityInfoData* node2) const override;
+  bool IsValid(ArcAccessibilityInfoData* info_data) const override;
+  bool IsEqual(ArcAccessibilityInfoData* info_data1,
+               ArcAccessibilityInfoData* info_data2) const override;
   ArcAccessibilityInfoData* GetNull() const override;
 
-  // Computes the smallest rect that encloses all of the descendants of |node|.
-  gfx::Rect ComputeEnclosingBounds(ArcAccessibilityInfoData* node) const;
+  // Computes the smallest rect that encloses all of the descendants of
+  // |info_data|.
+  gfx::Rect ComputeEnclosingBounds(ArcAccessibilityInfoData* info_data) const;
 
-  // Helper to recursively compute bounds for |node|. Returns true if non-empty
-  // bounds were encountered.
-  void ComputeEnclosingBoundsInternal(ArcAccessibilityInfoData* node,
+  // Helper to recursively compute bounds for |info_data|. Returns true if
+  // non-empty bounds were encountered.
+  void ComputeEnclosingBoundsInternal(ArcAccessibilityInfoData* info_data,
                                       gfx::Rect& computed_bounds) const;
 
   // AXHostDelegate overrides.
@@ -115,7 +116,7 @@
   std::unique_ptr<AXTreeArcSerializer> current_tree_serializer_;
   int32_t root_id_;
   int32_t window_id_;
-  int32_t focused_node_id_;
+  int32_t focused_id_;
   bool is_notification_;
 
   // A delegate that handles accessibility actions on behalf of this tree. The
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
index a5372dd..53f3ccd 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
 
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h"
+#include "chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h"
 #include "components/arc/common/accessibility_helper.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/platform/ax_android_constants.h"
@@ -22,6 +23,9 @@
 using AXRangeInfoData = mojom::AccessibilityRangeInfoData;
 using AXStringListProperty = mojom::AccessibilityStringListProperty;
 using AXStringProperty = mojom::AccessibilityStringProperty;
+using AXWindowInfoData = mojom::AccessibilityWindowInfoData;
+using AXWindowIntListProperty = mojom::AccessibilityWindowIntListProperty;
+using AXWindowStringProperty = mojom::AccessibilityWindowStringProperty;
 
 void SetProperty(AXNodeInfoData* node, AXBooleanProperty prop, bool value) {
   if (!node->boolean_properties) {
@@ -46,6 +50,16 @@
   node->int_properties.value().insert(std::make_pair(prop, value));
 }
 
+void SetProperty(AXWindowInfoData* window,
+                 AXWindowStringProperty prop,
+                 const std::string& value) {
+  if (!window->string_properties) {
+    window->string_properties =
+        base::flat_map<AXWindowStringProperty, std::string>();
+  }
+  window->string_properties.value().insert(std::make_pair(prop, value));
+}
+
 void SetProperty(AXNodeInfoData* node,
                  AXIntListProperty prop,
                  const std::vector<int>& value) {
@@ -56,6 +70,16 @@
   node->int_list_properties.value().insert(std::make_pair(prop, value));
 }
 
+void SetProperty(AXWindowInfoData* window,
+                 AXWindowIntListProperty prop,
+                 const std::vector<int>& value) {
+  if (!window->int_list_properties) {
+    window->int_list_properties =
+        base::flat_map<AXWindowIntListProperty, std::vector<int>>();
+  }
+  window->int_list_properties.value().insert(std::make_pair(prop, value));
+}
+
 class AXTreeSourceArcTest : public testing::Test,
                             public AXTreeSourceArc::Delegate {
  public:
@@ -67,13 +91,13 @@
   }
 
   void CallGetChildren(
-      mojom::AccessibilityNodeInfoData* node,
+      AXNodeInfoData* node,
       std::vector<ArcAccessibilityInfoData*>* out_children) const {
     AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node);
     tree_->GetChildren(&node_data, out_children);
   }
 
-  void CallSerializeNode(mojom::AccessibilityNodeInfoData* node,
+  void CallSerializeNode(AXNodeInfoData* node,
                          std::unique_ptr<ui::AXNodeData>* out_data) const {
     ASSERT_TRUE(out_data);
     AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node);
@@ -81,10 +105,22 @@
     tree_->SerializeNode(&node_data, out_data->get());
   }
 
+  void CallSerializeWindow(AXWindowInfoData* window,
+                           std::unique_ptr<ui::AXNodeData>* out_data) const {
+    ASSERT_TRUE(out_data);
+    AccessibilityWindowInfoDataWrapper window_data(tree_.get(), window);
+    *out_data = std::make_unique<ui::AXNodeData>();
+    tree_->SerializeNode(&window_data, out_data->get());
+  }
+
   ArcAccessibilityInfoData* CallGetFromId(int32_t id) const {
     return tree_->GetFromId(id);
   }
 
+  bool CallGetTreeData(ui::AXTreeData* data) {
+    return tree_->GetTreeData(data);
+  }
+
  private:
   void OnAction(const ui::AXActionData& data) const override {}
 
@@ -99,6 +135,12 @@
   event->task_id = 1;
   event->event_type = AXEventType::VIEW_FOCUSED;
 
+  event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
+  event->window_data->push_back(AXWindowInfoData::New());
+  AXWindowInfoData* root_window = event->window_data->back().get();
+  root_window->window_id = 100;
+  root_window->root_node_id = 0;
+
   event->node_data.push_back(AXNodeInfoData::New());
   AXNodeInfoData* root = event->node_data.back().get();
   root->id = 0;
@@ -247,8 +289,6 @@
   // Populate the tree source with the data.
   CallNotifyAccessibilityEvent(event.get());
 
-  // Live edit name related attributes.
-
   // No attributes.
   std::unique_ptr<ui::AXNodeData> data;
   CallSerializeNode(root, &data);
@@ -262,7 +302,7 @@
   CallSerializeNode(root, &data);
   ASSERT_TRUE(
       data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
-  ASSERT_EQ("", name);
+  EXPECT_EQ("", name);
 
   // Text (non-empty).
   root->string_properties->clear();
@@ -271,7 +311,7 @@
   CallSerializeNode(root, &data);
   ASSERT_TRUE(
       data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
-  ASSERT_EQ("label text", name);
+  EXPECT_EQ("label text", name);
 
   // Content description (empty), text (non-empty).
   SetProperty(root, AXStringProperty::CONTENT_DESCRIPTION, "");
@@ -279,7 +319,7 @@
   CallSerializeNode(root, &data);
   ASSERT_TRUE(
       data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
-  ASSERT_EQ("label text", name);
+  EXPECT_EQ("label text", name);
 
   // Content description (non-empty), text (non-empty).
   root->string_properties.value()[AXStringProperty::CONTENT_DESCRIPTION] =
@@ -288,7 +328,7 @@
   CallSerializeNode(root, &data);
   ASSERT_TRUE(
       data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
-  ASSERT_EQ("label content description", name);
+  EXPECT_EQ("label content description", name);
 
   // Name from contents.
 
@@ -321,8 +361,111 @@
       data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
 }
 
+TEST_F(AXTreeSourceArcTest, AccessibleNameComputationWindow) {
+  auto event = AXEventData::New();
+  event->source_id = 0;
+  event->task_id = 1;
+  event->event_type = AXEventType::VIEW_FOCUSED;
+  event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
+  event->window_data->push_back(AXWindowInfoData::New());
+  AXWindowInfoData* root = event->window_data->back().get();
+  root->window_id = 0;
+
+  CallNotifyAccessibilityEvent(event.get());
+
+  // Live edit name related attributes.
+
+  // No attributes.
+  std::unique_ptr<ui::AXNodeData> data;
+  CallSerializeWindow(root, &data);
+  std::string name;
+  ASSERT_FALSE(
+      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+
+  // Title attribute
+  SetProperty(root, AXWindowStringProperty::TITLE, "window title");
+  CallSerializeWindow(root, &data);
+  ASSERT_TRUE(
+      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+  EXPECT_EQ("window title", name);
+}
+
+TEST_F(AXTreeSourceArcTest, AccessibleNameComputationWindowWithChildren) {
+  auto event = AXEventData::New();
+  event->source_id = 3;
+  event->task_id = 1;
+  event->event_type = AXEventType::VIEW_FOCUSED;
+  event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
+  event->window_data->push_back(AXWindowInfoData::New());
+  AXWindowInfoData* root = event->window_data->back().get();
+  root->window_id = 0;
+  root->root_node_id = 3;
+  SetProperty(root, AXWindowIntListProperty::CHILD_WINDOW_IDS, {2, 5});
+  SetProperty(root, AXWindowStringProperty::TITLE, "window title");
+
+  // Add a child window.
+  event->window_data->push_back(AXWindowInfoData::New());
+  AXWindowInfoData* child = event->window_data->back().get();
+  child->window_id = 2;
+  child->root_node_id = 4;
+  SetProperty(child, AXWindowStringProperty::TITLE, "child window title");
+
+  // Add a child node.
+  event->node_data.push_back(AXNodeInfoData::New());
+  AXNodeInfoData* node = event->node_data.back().get();
+  node->id = 3;
+  SetProperty(node, AXStringProperty::TEXT, "node text");
+
+  // Add a child node to the child window as well.
+  event->node_data.push_back(AXNodeInfoData::New());
+  AXNodeInfoData* child_node = event->node_data.back().get();
+  child_node->id = 4;
+  SetProperty(child_node, AXStringProperty::TEXT, "child node text");
+
+  // Add a child window with no children as well.
+  event->window_data->push_back(AXWindowInfoData::New());
+  AXWindowInfoData* child2 = event->window_data->back().get();
+  child2->window_id = 5;
+  SetProperty(child2, AXWindowStringProperty::TITLE, "child2 window title");
+
+  CallNotifyAccessibilityEvent(event.get());
+  std::unique_ptr<ui::AXNodeData> data;
+  std::string name;
+
+  CallSerializeWindow(root, &data);
+  ASSERT_TRUE(
+      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+  EXPECT_EQ("window title", name);
+  EXPECT_NE(ax::mojom::Role::kRootWebArea, data->role);
+
+  CallSerializeWindow(child, &data);
+  ASSERT_TRUE(
+      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+  EXPECT_EQ("child window title", name);
+  EXPECT_NE(ax::mojom::Role::kRootWebArea, data->role);
+
+  CallSerializeNode(node, &data);
+  ASSERT_TRUE(
+      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+  EXPECT_EQ("node text", name);
+  EXPECT_EQ(ax::mojom::Role::kRootWebArea, data->role);
+
+  CallSerializeNode(child_node, &data);
+  ASSERT_TRUE(
+      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+  EXPECT_EQ("child node text", name);
+  EXPECT_NE(ax::mojom::Role::kRootWebArea, data->role);
+
+  CallSerializeWindow(child2, &data);
+  ASSERT_TRUE(
+      data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+  EXPECT_EQ("child2 window title", name);
+  EXPECT_NE(ax::mojom::Role::kRootWebArea, data->role);
+}
+
 // TODO(katie): Maybe remove this test when adding AccessibilityWindowInfoData
-// support per go/a11y-arc++-window-mapping if it is no longer needed.
+// support per go/a11y-arc++-window-mapping if it is no longer needed. This
+// depends on if we can assume that each tree has exactly one root.
 TEST_F(AXTreeSourceArcTest, MultipleNodeSubtrees) {
   // Run several times to try source_id from root, middle, or leaf of the tree.
   int tree_size = 4;
@@ -383,4 +526,103 @@
   }
 }
 
+TEST_F(AXTreeSourceArcTest, ComplexTreeStructure) {
+  int tree_size = 4;
+  int num_trees = 3;
+
+  auto event = AXEventData::New();
+  event->source_id = 4;
+  event->task_id = 1;
+  event->event_type = AXEventType::VIEW_FOCUSED;
+  event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
+  event->window_data->push_back(AXWindowInfoData::New());
+  AXWindowInfoData* root_window = event->window_data->back().get();
+  // Pick large numbers for the IDs so as not to overlap.
+  root_window->window_id = 1000;
+  SetProperty(root_window, AXWindowIntListProperty::CHILD_WINDOW_IDS,
+              {100, 200, 300});
+
+  // Make three non-overlapping trees rooted at the same window. One tree has
+  // the source_id of interest. Each subtree has a root window, which has a
+  // root node with one child, and that child has two leaf children.
+  for (int i = 0; i < num_trees; i++) {
+    event->window_data->push_back(AXWindowInfoData::New());
+    AXWindowInfoData* child_window = event->window_data->back().get();
+    child_window->window_id = (i + 1) * 100;
+    child_window->root_node_id = i * tree_size + 1;
+
+    event->node_data.push_back(AXNodeInfoData::New());
+    AXNodeInfoData* root = event->node_data.back().get();
+    root->id = i * tree_size + 1;
+    root->window_id = (i + 1) * 100;
+    SetProperty(root, AXIntListProperty::CHILD_NODE_IDS,
+                std::vector<int>({i * tree_size + 2}));
+
+    event->node_data.push_back(AXNodeInfoData::New());
+    AXNodeInfoData* child1 = event->node_data.back().get();
+    child1->id = i * tree_size + 2;
+    SetProperty(child1, AXIntListProperty::CHILD_NODE_IDS,
+                std::vector<int>({i * tree_size + 3, i * tree_size + 4}));
+
+    event->node_data.push_back(AXNodeInfoData::New());
+    AXNodeInfoData* child2 = event->node_data.back().get();
+    child2->id = i * tree_size + 3;
+
+    event->node_data.push_back(AXNodeInfoData::New());
+    AXNodeInfoData* child3 = event->node_data.back().get();
+    child3->id = i * tree_size + 4;
+  }
+
+  CallNotifyAccessibilityEvent(event.get());
+
+  // Check that each node subtree tree was added, and that it is correct.
+  std::vector<ArcAccessibilityInfoData*> children;
+  for (int i = 0; i < num_trees; i++) {
+    CallGetChildren(event->node_data.at(i * tree_size).get(), &children);
+    ASSERT_EQ(1U, children.size());
+    EXPECT_EQ(i * tree_size + 2, children[0]->GetId());
+    children.clear();
+    CallGetChildren(event->node_data.at(i * tree_size + 1).get(), &children);
+    ASSERT_EQ(2U, children.size());
+    EXPECT_EQ(i * tree_size + 3, children[0]->GetId());
+    EXPECT_EQ(i * tree_size + 4, children[1]->GetId());
+    children.clear();
+  }
+}
+
+TEST_F(AXTreeSourceArcTest, GetTreeDataAppliesFocus) {
+  auto event = AXEventData::New();
+  event->source_id = 5;
+  event->task_id = 1;
+  event->event_type = AXEventType::VIEW_FOCUSED;
+  event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
+  event->window_data->push_back(AXWindowInfoData::New());
+  AXWindowInfoData* root = event->window_data->back().get();
+  root->window_id = 5;
+  SetProperty(root, AXWindowIntListProperty::CHILD_WINDOW_IDS, {1});
+
+  // Add a child window.
+  event->window_data->push_back(AXWindowInfoData::New());
+  AXWindowInfoData* child = event->window_data->back().get();
+  child->window_id = 1;
+
+  CallNotifyAccessibilityEvent(event.get());
+  ui::AXTreeData data;
+
+  // Nothing should be focused when there are no nodes.
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(-1, data.focus_id);
+
+  // Add a child node.
+  root->root_node_id = 2;
+  event->node_data.push_back(AXNodeInfoData::New());
+  AXNodeInfoData* node = event->node_data.back().get();
+  node->id = 2;
+
+  CallNotifyAccessibilityEvent(event.get());
+
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(2, data.focus_id);
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 0e46ef39..44a16b2 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -1393,13 +1393,17 @@
 
 ui::InputMethodKeyboardController*
 InputMethodManagerImpl::GetInputMethodKeyboardController() {
+  // TODO(stevenjb/shuchen): Fix this for Mash. https://crbug.com/756059
+  if (features::IsMultiProcessMash())
+    return nullptr;
   // Callers expect a nullptr when the keyboard is disabled. See
-  // https://crbug.com/850020. TODO(stevenjb/shuchen): Fix this for Mash.
-  // https://crbug.com/756059
-  return keyboard::KeyboardController::HasInstance() &&
-                 keyboard::KeyboardController::Get()->IsEnabled()
-             ? keyboard::KeyboardController::Get()
-             : nullptr;
+  // https://crbug.com/850020.
+  if (!keyboard::KeyboardController::HasInstance() ||
+      !keyboard::KeyboardController::Get()->IsEnabled()) {
+    return nullptr;
+  }
+  return keyboard::KeyboardController::Get()
+      ->input_method_keyboard_controller();
 }
 
 void InputMethodManagerImpl::ReloadKeyboard() {
diff --git a/chrome/browser/chromeos/policy/policy_cert_service.cc b/chrome/browser/chromeos/policy/policy_cert_service.cc
index e7468ac..08a1669 100644
--- a/chrome/browser/chromeos/policy/policy_cert_service.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_service.cc
@@ -10,7 +10,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
-#include "chrome/browser/chromeos/policy/temp_certs_cache_nss.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -19,6 +18,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "net/cert/x509_certificate.h"
 #include "services/network/cert_verifier_with_trust_anchors.h"
+#include "services/network/nss_temp_certs_cache_chromeos.h"
 #include "services/network/public/cpp/features.h"
 
 namespace policy {
@@ -110,7 +110,9 @@
   // expecting that the operation of creating in-memory NSS certs is cheap in
   // that case.
   temp_policy_provided_certs_ =
-      std::make_unique<TempCertsCacheNSS>(all_server_and_authority_certs);
+      std::make_unique<network::NSSTempCertsCacheChromeOS>(
+          all_server_and_authority_certs);
+  all_server_and_authority_certs_ = all_server_and_authority_certs;
 
   // Do not use certificates installed via ONC policy if the current session has
   // multiple profiles. This is important to make sure that any possibly tainted
@@ -119,17 +121,18 @@
   if (!trust_anchors.empty() && user_manager_->GetLoggedInUsers().size() > 1u) {
     LOG(ERROR) << "Ignoring ONC-pushed certificates update because multiple "
                << "users are logged in.";
-    return;
+    trust_anchors_.clear();
+  } else {
+    trust_anchors_ = trust_anchors;
   }
 
-  trust_anchors_ = trust_anchors;
-
   if (!notify)
     return;
 
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     ProfileNetworkContextServiceFactory::GetForContext(profile_)
-        ->UpdateTrustAnchors(trust_anchors_);
+        ->UpdateAdditionalCertificates(all_server_and_authority_certs_,
+                                       trust_anchors_);
     return;
   }
 
diff --git a/chrome/browser/chromeos/policy/policy_cert_service.h b/chrome/browser/chromeos/policy/policy_cert_service.h
index 22b9d595..0fdae41 100644
--- a/chrome/browser/chromeos/policy/policy_cert_service.h
+++ b/chrome/browser/chromeos/policy/policy_cert_service.h
@@ -30,10 +30,10 @@
 
 namespace network {
 class CertVerifierWithTrustAnchors;
+class NSSTempCertsCacheChromeOS;
 }
 
 namespace policy {
-class TempCertsCacheNSS;
 
 // This service is the counterpart of PolicyCertVerifier on the UI thread. It's
 // responsible for pushing the current list of trust anchors to the CertVerifier
@@ -66,6 +66,9 @@
 
   bool has_policy_certificates() const { return !trust_anchors_.empty(); }
 
+  const net::CertificateList& all_server_and_authority_certs() const {
+    return all_server_and_authority_certs_;
+  }
   const net::CertificateList& trust_anchors() const { return trust_anchors_; }
 
   // UserNetworkConfigurationUpdater::PolicyProvidedCertsObserver:
@@ -97,12 +100,14 @@
   std::string user_id_;
   UserNetworkConfigurationUpdater* net_conf_updater_;
   user_manager::UserManager* user_manager_;
+  net::CertificateList all_server_and_authority_certs_;
   net::CertificateList trust_anchors_;
 
   // Holds all policy-provided server and authority certificates and makes them
   // available to NSS as temp certificates. This is needed so they can be used
   // as intermediates when NSS verifies a certificate.
-  std::unique_ptr<TempCertsCacheNSS> temp_policy_provided_certs_;
+  std::unique_ptr<network::NSSTempCertsCacheChromeOS>
+      temp_policy_provided_certs_;
 
   // Weak pointers to handle callbacks from PolicyCertVerifier on the IO thread.
   // The factory and the created WeakPtrs must only be used on the UI thread.
diff --git a/chrome/browser/chromeos/policy/temp_certs_cache_nss.cc b/chrome/browser/chromeos/policy/temp_certs_cache_nss.cc
deleted file mode 100644
index efafe06..0000000
--- a/chrome/browser/chromeos/policy/temp_certs_cache_nss.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/policy/temp_certs_cache_nss.h"
-
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/policy/device_network_configuration_updater.h"
-#include "chromeos/network/onc/onc_utils.h"
-#include "net/cert/x509_util_nss.h"
-
-namespace policy {
-
-TempCertsCacheNSS::TempCertsCacheNSS(const net::CertificateList& certificates) {
-  for (const auto& certificate : certificates) {
-    net::ScopedCERTCertificate x509_cert =
-        net::x509_util::CreateCERTCertificateFromX509Certificate(
-            certificate.get());
-    if (!x509_cert) {
-      LOG(ERROR) << "Unable to create CERTCertificate";
-      continue;
-    }
-
-    temp_certs_.push_back(std::move(x509_cert));
-  }
-}
-
-TempCertsCacheNSS::~TempCertsCacheNSS() {}
-
-}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
index 7c6ae90..c70e2ce 100644
--- a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
@@ -68,15 +68,20 @@
 namespace {
 
 // Test data file storing an ONC blob with an Authority certificate.
-constexpr char kRootCertOnc[] = "root-ca-cert.onc";
+constexpr char kRootCaCertOnc[] = "root-ca-cert.onc";
 constexpr char kClientCertOnc[] = "certificate-client.onc";
+constexpr char kRootAndIntermediateCaCertsOnc[] =
+    "root-and-intermediate-ca-certs.onc";
 constexpr char kClientCertSubjectCommonName[] = "lmao";
 constexpr char kNetworkComponentDirectory[] = "network";
 // A PEM-encoded certificate which was signed by the Authority specified in
-// |kRootCertOnc|.
-constexpr char kGoodCert[] = "ok_cert.pem";
-// The PEM-encoded Authority certificate specified by |kRootCertOnc|.
-constexpr char kRootCert[] = "root_ca_cert.pem";
+// |kRootCaCertOnc|.
+constexpr char kServerCert[] = "ok_cert.pem";
+// A PEM-encoded certificate which was signed by the intermediate Authority
+// specified in |kRootAndIntermediateCaCertsOnc|.
+constexpr char kServerCertByIntermediate[] = "ok_cert_by_intermediate.pem";
+// The PEM-encoded Authority certificate specified by |kRootCaCertOnc|.
+constexpr char kRootCaCert[] = "root_ca_cert.pem";
 constexpr char kDeviceLocalAccountId[] = "dla1@example.com";
 
 // Allows waiting until the list of policy-pushed web-trusted certificates
@@ -147,23 +152,29 @@
 };
 
 // Allows setting user policy to assign trust to the CA certificate specified by
-// |kRootCert|.
+// |kRootCaCert|.
 class UserPolicyCertsHelper {
  public:
   UserPolicyCertsHelper() {
-    base::FilePath server_cert_pem_file_path;
-    chromeos::test_utils::GetTestDataPath(kNetworkComponentDirectory, kGoodCert,
-                                          &server_cert_pem_file_path);
-    test_server_cert_ =
-        net::ImportCertFromFile(server_cert_pem_file_path.DirName(),
-                                server_cert_pem_file_path.BaseName().value());
+    base::FilePath server_cert_path;
+    chromeos::test_utils::GetTestDataPath(kNetworkComponentDirectory,
+                                          kServerCert, &server_cert_path);
+    server_cert_ = net::ImportCertFromFile(server_cert_path.DirName(),
+                                           server_cert_path.BaseName().value());
 
-    base::FilePath root_cert_pem_file_path;
-    chromeos::test_utils::GetTestDataPath(kNetworkComponentDirectory, kRootCert,
-                                          &root_cert_pem_file_path);
-    test_root_cert_ =
-        net::ImportCertFromFile(root_cert_pem_file_path.DirName(),
-                                root_cert_pem_file_path.BaseName().value());
+    base::FilePath root_cert_path;
+    chromeos::test_utils::GetTestDataPath(kNetworkComponentDirectory,
+                                          kRootCaCert, &root_cert_path);
+    root_cert_ = net::ImportCertFromFile(root_cert_path.DirName(),
+                                         root_cert_path.BaseName().value());
+
+    base::FilePath server_cert_by_intermediate_path;
+    chromeos::test_utils::GetTestDataPath(kNetworkComponentDirectory,
+                                          kServerCertByIntermediate,
+                                          &server_cert_by_intermediate_path);
+    server_cert_by_intermediate_ = net::ImportCertFromFile(
+        server_cert_path.DirName(),
+        server_cert_by_intermediate_path.BaseName().value());
   }
 
   // Installs the BrowserPolicyConnector to set user policy.
@@ -176,10 +187,36 @@
     BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
   }
 
-  // Sets the ONC-policy to the blob defined by |kRootCertOnc| and waits until
+  // Sets the ONC-policy to the blob defined by |kRootCaCertOnc| and waits until
   // the notification that policy-provided trust roots have changed is sent from
   // |profile|'s UserNetworkConfigurationUpdater.
   void SetRootCertONCUserPolicy(Profile* profile) {
+    std::string onc_policy_data =
+        chromeos::onc::test_utils::ReadTestData(kRootCaCertOnc);
+    SetONCUserPolicy(profile, onc_policy_data);
+  }
+
+  // Sets the ONC-policy to the blob defined by |kRootCaCertOnc| and waits until
+  // the notification that policy-provided trust roots have changed is sent from
+  // |profile|'s UserNetworkConfigurationUpdater.
+  void SetRootAndIntermediateCertsONCUserPolicy(Profile* profile) {
+    std::string onc_policy_data =
+        chromeos::onc::test_utils::ReadTestData(kRootAndIntermediateCaCertsOnc);
+    SetONCUserPolicy(profile, onc_policy_data);
+  }
+
+  const scoped_refptr<net::X509Certificate>& server_cert() {
+    return server_cert_;
+  }
+
+  const scoped_refptr<net::X509Certificate>& root_cert() { return root_cert_; }
+
+  const scoped_refptr<net::X509Certificate>& server_cert_by_intermediate() {
+    return server_cert_by_intermediate_;
+  }
+
+ private:
+  void SetONCUserPolicy(Profile* profile, const std::string& onc_policy_data) {
     ASSERT_TRUE(is_set_up_);
     UserNetworkConfigurationUpdater* user_network_configuration_updater =
         UserNetworkConfigurationUpdaterFactory::GetForBrowserContext(profile);
@@ -188,12 +225,10 @@
     user_network_configuration_updater->AddPolicyProvidedCertsObserver(
         &trust_roots_changed_observer);
 
-    const std::string& user_policy_blob =
-        chromeos::onc::test_utils::ReadTestData(kRootCertOnc);
     policy::PolicyMap policy;
     policy.Set(key::kOpenNetworkConfiguration, policy::POLICY_LEVEL_MANDATORY,
                policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
-               std::make_unique<base::Value>(user_policy_blob), nullptr);
+               std::make_unique<base::Value>(onc_policy_data), nullptr);
     provider_.UpdateChromePolicy(policy);
     // Note that this relies on the implementation detail that the notification
     // is sent even if the trust roots effectively remain the same.
@@ -202,25 +237,23 @@
         &trust_roots_changed_observer);
   }
 
-  const scoped_refptr<net::X509Certificate>& test_server_cert() {
-    return test_server_cert_;
-  }
-
-  const scoped_refptr<net::X509Certificate>& test_root_cert() {
-    return test_root_cert_;
-  }
-
- private:
   // This is set to true when |SetUpInProcessBrowserTestFixture| has been
   // called.
   bool is_set_up_ = false;
 
   MockConfigurationPolicyProvider provider_;
 
-  // Server Certificate which is signed by authority specified in |kRootCert|.
-  scoped_refptr<net::X509Certificate> test_server_cert_;
-  // Authority Certificate specified in |kRootCert|.
-  scoped_refptr<net::X509Certificate> test_root_cert_;
+  // Server Certificate which is signed by authority specified in |kRootCaCert|.
+  scoped_refptr<net::X509Certificate> server_cert_;
+  // Authority Certificate specified in |kRootCaCert|.
+  scoped_refptr<net::X509Certificate> root_cert_;
+  // Server Certificate which is signed by an intermediate authority, which
+  // itself is signed by the authority specified in |kRootCaCert|.
+  // |kRootCaCertOnc| does not know this intermediate authority.
+  // |kRootCaAndIntermediateCertsOnc| does know this intermediate authority, but
+  // does not request web trust for it. Instead, trust should be delegate from
+  // the root authrotiy.
+  scoped_refptr<net::X509Certificate> server_cert_by_intermediate_;
 };
 
 // Verifies |certificate| with |profile|'s CertVerifier and returns the result.
@@ -338,11 +371,31 @@
 };
 
 IN_PROC_BROWSER_TEST_F(PolicyProvidedTrustAnchorsRegularUserTest,
-                       AllowedForRegularUser) {
+                       TrustAnchorApplied) {
   user_policy_certs_helper_.SetRootCertONCUserPolicy(browser()->profile());
   EXPECT_EQ(net::OK,
             VerifyTestServerCert(browser()->profile(),
-                                 user_policy_certs_helper_.test_server_cert()));
+                                 user_policy_certs_helper_.server_cert()));
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyProvidedTrustAnchorsRegularUserTest,
+                       UntrustedIntermediateAuthorityApplied) {
+  // Sanity check: Apply ONC policy which does not mention the intermediate
+  // authority.
+  user_policy_certs_helper_.SetRootCertONCUserPolicy(browser()->profile());
+  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+            VerifyTestServerCert(
+                browser()->profile(),
+                user_policy_certs_helper_.server_cert_by_intermediate()));
+
+  // Now apply ONC policy which mentions the intermediate authority (but does
+  // not assign trust to it).
+  user_policy_certs_helper_.SetRootAndIntermediateCertsONCUserPolicy(
+      browser()->profile());
+  EXPECT_EQ(net::OK,
+            VerifyTestServerCert(
+                browser()->profile(),
+                user_policy_certs_helper_.server_cert_by_intermediate()));
 }
 
 IN_PROC_BROWSER_TEST_F(PolicyProvidedTrustAnchorsRegularUserTest,
@@ -355,7 +408,7 @@
   chromeos::NetworkCertLoader::Get()->SetUserNSSDB(test_nss_cert_db_.get());
 
   EXPECT_FALSE(
-      IsCertInCertificateList(user_policy_certs_helper_.test_root_cert().get(),
+      IsCertInCertificateList(user_policy_certs_helper_.root_cert().get(),
                               chromeos::NetworkCertLoader::Get()->all_certs()));
   NetworkCertLoaderTestObserver network_cert_loader_observer(
       chromeos::NetworkCertLoader::Get());
@@ -366,7 +419,7 @@
   // (Web Trust does not matter for the NetworkCertLoader, but we currently only
   // set a policy with a certificate requesting Web Trust here).
   EXPECT_TRUE(
-      IsCertInCertificateList(user_policy_certs_helper_.test_root_cert().get(),
+      IsCertInCertificateList(user_policy_certs_helper_.root_cert().get(),
                               chromeos::NetworkCertLoader::Get()->all_certs()));
 }
 
@@ -503,7 +556,7 @@
   user_policy_certs_helper_.SetRootCertONCUserPolicy(browser->profile());
   EXPECT_EQ(net::OK,
             VerifyTestServerCert(browser->profile(),
-                                 user_policy_certs_helper_.test_server_cert()));
+                                 user_policy_certs_helper_.server_cert()));
 }
 
 class PolicyProvidedTrustAnchorsOnUserSessionInitTest
@@ -530,7 +583,7 @@
 
   void GetMandatoryPoliciesValue(base::DictionaryValue* policy) const override {
     const std::string& user_policy_blob =
-        chromeos::onc::test_utils::ReadTestData(kRootCertOnc);
+        chromeos::onc::test_utils::ReadTestData(kRootCaCertOnc);
     policy->SetKey(key::kOpenNetworkConfiguration,
                    base::Value(user_policy_blob));
   }
@@ -584,12 +637,11 @@
                        TrustAnchorsAvailableImmediatelyAfterSessionStart) {
   // Load the certificate which is only OK if the policy-provided authority is
   // actually trusted.
-  base::FilePath cert_pem_file_path;
-  chromeos::test_utils::GetTestDataPath(kNetworkComponentDirectory, kGoodCert,
-                                        &cert_pem_file_path);
-  scoped_refptr<net::X509Certificate> test_server_cert =
-      net::ImportCertFromFile(cert_pem_file_path.DirName(),
-                              cert_pem_file_path.BaseName().value());
+  base::FilePath cert_path;
+  chromeos::test_utils::GetTestDataPath(kNetworkComponentDirectory, kServerCert,
+                                        &cert_path);
+  scoped_refptr<net::X509Certificate> server_cert = net::ImportCertFromFile(
+      cert_path.DirName(), cert_path.BaseName().value());
 
   SkipToLoginScreen();
   TriggerLogIn();
@@ -597,8 +649,7 @@
   EXPECT_FALSE(user_session_started());
 
   WaitSessionStart();
-  EXPECT_EQ(net::OK,
-            VerifyTestServerCert(active_user_profile(), test_server_cert));
+  EXPECT_EQ(net::OK, VerifyTestServerCert(active_user_profile(), server_cert));
 }
 
 // Testing policy-provided client cert import.
diff --git a/chrome/browser/chromeos/power/extension_event_observer_unittest.cc b/chrome/browser/chromeos/power/extension_event_observer_unittest.cc
index e47ae2d7..08e2b133 100644
--- a/chrome/browser/chromeos/power/extension_event_observer_unittest.cc
+++ b/chrome/browser/chromeos/power/extension_event_observer_unittest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/common/extensions/api/gcm.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -32,39 +33,28 @@
 #include "extensions/common/manifest_handlers/background_info.h"
 #include "extensions/common/value_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/aura/test/test_screen.h"
-#include "ui/display/screen.h"
 
 namespace chromeos {
 
-class ExtensionEventObserverTest : public ::testing::Test {
+class ExtensionEventObserverTest : public ChromeRenderViewHostTestHarness {
  public:
   ExtensionEventObserverTest()
       : power_manager_client_(new FakePowerManagerClient()),
-        test_screen_(aura::TestScreen::Create(gfx::Size())),
         fake_user_manager_(new FakeChromeUserManager()),
-        scoped_user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {
+        scoped_user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {}
+
+  ~ExtensionEventObserverTest() override = default;
+
+  // ChromeRenerViewHostTestHarness overrides:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
     DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient(
         base::WrapUnique(power_manager_client_));
-
-    profile_manager_.reset(
-        new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
-
-    extension_event_observer_.reset(new ExtensionEventObserver());
+    profile_manager_ = std::make_unique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    extension_event_observer_ = std::make_unique<ExtensionEventObserver>();
     test_api_ = extension_event_observer_->CreateTestApi();
-  }
-
-  ~ExtensionEventObserverTest() override {
-    extension_event_observer_.reset();
-    profile_manager_.reset();
-    DBusThreadManager::Shutdown();
-  }
-
-  // ::testing::Test overrides.
-  void SetUp() override {
-    ::testing::Test::SetUp();
-
-    display::Screen::SetScreenInstance(test_screen_.get());
 
     // Must be called from ::testing::Test::SetUp.
     ASSERT_TRUE(profile_manager_->SetUp());
@@ -79,10 +69,12 @@
     profile_manager_->SetLoggedIn(true);
   }
   void TearDown() override {
+    extension_event_observer_.reset();
     profile_ = NULL;
     profile_manager_->DeleteAllTestingProfiles();
-    display::Screen::SetScreenInstance(nullptr);
-    ::testing::Test::TearDown();
+    profile_manager_.reset();
+    DBusThreadManager::Shutdown();
+    ChromeRenderViewHostTestHarness::TearDown();
   }
 
  protected:
@@ -137,13 +129,6 @@
   std::unique_ptr<TestingProfileManager> profile_manager_;
 
  private:
-  std::unique_ptr<aura::TestScreen> test_screen_;
-  content::TestBrowserThreadBundle browser_thread_bundle_;
-
-  // Needed to ensure we don't end up creating actual RenderViewHosts
-  // and RenderProcessHosts.
-  content::RenderViewHostTestEnabler render_view_host_test_enabler_;
-
   // Chrome OS needs the CrosSettings test helper.
   ScopedCrosSettingsTestHelper cros_settings_test_helper_;
 
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc b/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
index 8b5a795..30a7ce2e 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
@@ -135,13 +135,13 @@
       base::i18n::ToLower(module_key.module_path.BaseName().value()));
   base::SHA1HashBytes(reinterpret_cast<const uint8_t*>(module_basename.data()),
                       module_basename.length(),
-                      packed_list_module->basename_hash);
+                      &packed_list_module->basename_hash[0]);
 
   // Hash the code id.
   const std::string module_code_id = GenerateCodeId(module_key);
   base::SHA1HashBytes(reinterpret_cast<const uint8_t*>(module_code_id.data()),
                       module_code_id.length(),
-                      packed_list_module->code_id_hash);
+                      &packed_list_module->code_id_hash[0]);
 
   packed_list_module->time_date_stamp =
       CalculateTimeDateStamp(base::Time::Now());
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc b/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
index c3dfab2..d4d7fc03 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
@@ -349,10 +349,10 @@
   const std::string module_basename = base::UTF16ToUTF8(
       base::i18n::ToLower(module_key2.module_path.BaseName().value()));
   base::SHA1HashBytes(reinterpret_cast<const uint8_t*>(module_basename.data()),
-                      module_basename.length(), expected.basename_hash);
+                      module_basename.length(), &expected.basename_hash[0]);
   const std::string module_code_id = GenerateCodeId(module_key2);
   base::SHA1HashBytes(reinterpret_cast<const uint8_t*>(module_code_id.data()),
-                      module_code_id.length(), expected.code_id_hash);
+                      module_code_id.length(), &expected.code_id_hash[0]);
 
   EXPECT_TRUE(internal::ModuleEqual()(expected, blacklisted_modules[0]));
 }
diff --git a/chrome/browser/conflicts/module_blacklist_cache_util_win.cc b/chrome/browser/conflicts/module_blacklist_cache_util_win.cc
index 2f567901..7b3d908 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_util_win.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_util_win.cc
@@ -8,6 +8,7 @@
 #include <functional>
 #include <iterator>
 #include <string>
+#include <tuple>
 #include <utility>
 
 #include "base/files/file.h"
@@ -237,30 +238,15 @@
 bool ModuleLess::operator()(
     const third_party_dlls::PackedListModule& lhs,
     const third_party_dlls::PackedListModule& rhs) const {
-  auto is_less = [](const auto& lhs, const auto& rhs) {
-    return std::lexicographical_compare(std::begin(lhs), std::end(lhs),
-                                        std::begin(rhs), std::end(rhs));
-  };
-
-  if (is_less(lhs.basename_hash, rhs.basename_hash))
-    return true;
-
-  if (is_less(rhs.basename_hash, lhs.basename_hash))
-    return false;
-
-  return is_less(lhs.code_id_hash, rhs.code_id_hash);
+  return std::tie(lhs.basename_hash, lhs.code_id_hash) <
+         std::tie(rhs.basename_hash, rhs.code_id_hash);
 }
 
 bool ModuleEqual::operator()(
     const third_party_dlls::PackedListModule& lhs,
     const third_party_dlls::PackedListModule& rhs) const {
-  auto are_equal = [](const auto& lhs, const auto& rhs) {
-    return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
-                      std::end(rhs));
-  };
-
-  return are_equal(lhs.basename_hash, rhs.basename_hash) &&
-         are_equal(lhs.code_id_hash, rhs.code_id_hash);
+  return lhs.basename_hash == rhs.basename_hash &&
+         lhs.code_id_hash == rhs.code_id_hash;
 }
 
 bool ModuleTimeDateStampGreater::operator()(
diff --git a/chrome/browser/conflicts/module_blacklist_cache_util_win_unittest.cc b/chrome/browser/conflicts/module_blacklist_cache_util_win_unittest.cc
index e29df43..dd53d2e 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_util_win_unittest.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_util_win_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/stl_util.h"
 #include "base/time/time.h"
 #include "chrome/browser/conflicts/module_list_filter_win.h"
+#include "chrome_elf/sha1/sha1.h"
 #include "chrome_elf/third_party_dlls/packed_list_format.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -42,9 +43,7 @@
 
   for (auto& entry : entries) {
     // Fill up each bytes for both SHA1 hashes.
-    for (size_t i = 0;
-         i < arraysize(third_party_dlls::PackedListModule::basename_hash);
-         ++i) {
+    for (size_t i = 0; i < elf_sha1::kSHA1Length; ++i) {
       entry.basename_hash[i] = byte_distribution(random_engine);
       entry.code_id_hash[i] = byte_distribution(random_engine);
     }
@@ -186,10 +185,12 @@
 
   void AddWhitelistedModule(const third_party_dlls::PackedListModule& module) {
     whitelisted_modules_.emplace(
-        base::StringPiece(reinterpret_cast<const char*>(module.basename_hash),
-                          base::size(module.basename_hash)),
-        base::StringPiece(reinterpret_cast<const char*>(module.code_id_hash),
-                          base::size(module.basename_hash)));
+        base::StringPiece(
+            reinterpret_cast<const char*>(&module.basename_hash[0]),
+            base::size(module.basename_hash)),
+        base::StringPiece(
+            reinterpret_cast<const char*>(&module.code_id_hash[0]),
+            base::size(module.basename_hash)));
   }
 
   // ModuleListFilter:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 71d09b0..d587a72 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -309,7 +309,7 @@
   },
   {
     "name": "background-task-component-update",
-    // "owners": [ "your-team" ],
+    "owners": [ "sorin", "waffles", "tiborg" ],
     "expiry_milestone": 76
   },
   {
@@ -2036,7 +2036,7 @@
   },
   {
     "name": "enable-webrtc-remote-event-log",
-    // "owners": [ "your-team" ],
+    "owners": [ "eladalon" ],
     "expiry_milestone": 76
   },
   {
@@ -2230,11 +2230,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "gpu-rasterization-msaa-sample-count",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "grant-notifications-to-dse",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/google/google_brand_code_map_chromeos.cc b/chrome/browser/google/google_brand_code_map_chromeos.cc
index def1e155..a5b7e0b1 100644
--- a/chrome/browser/google/google_brand_code_map_chromeos.cc
+++ b/chrome/browser/google/google_brand_code_map_chromeos.cc
@@ -34,7 +34,67 @@
                      {"GJZV", {"BUSA", "GIOS", "UYOM"}},
                      {"FSGY", {"PJQC", "RHZW", "POVI"}},
                      {"IHZG", {"MLLN", "EZTK", "GJEJ"}},
-                     {"PXDO", {"ZXCF", "TQWC", "HOAL"}}});
+                     {"PXDO", {"ZXCF", "TQWC", "HOAL"}},
+                     {"ACAC", {"CFZM", "BEUH", "GUTN"}},
+                     {"ACAG", {"KSOU", "MUHR", "YYJR"}},
+                     {"ACAH", {"KEFG", "RYNH", "HHAZ"}},
+                     {"ACAJ", {"KVPC", "UHAI", "CPNG"}},
+                     {"ACAM", {"HBCZ", "ZGSZ", "MFUO"}},
+                     {"ACAO", {"MWDF", "BNNY", "SYIY"}},
+                     {"ACAP", {"LKNW", "SVFL", "FGKR"}},
+                     {"ACAR", {"EAQE", "UHHJ", "ZYFW"}},
+                     {"ACAT", {"RJNJ", "CKCB", "VHGI"}},
+                     {"ACAV", {"TTSD", "XTQQ", "TIQC"}},
+                     {"ACAY", {"HKDC", "RYKK", "KSIY"}},
+                     {"ACBA", {"TVZD", "HLQR", "DOWV"}},
+                     {"MNZG", {"PPTP", "OFXE", "ROJJ"}},
+                     {"CYQR", {"XGJJ", "DRMC", "RUQD"}},
+                     {"ASUA", {"IEIT", "JAIV", "MURN"}},
+                     {"ASUD", {"QLMM", "CRUA", "JSID"}},
+                     {"ASUE", {"XLEN", "KECH", "HBGX"}},
+                     {"ASUF", {"IVGE", "VNTM", "XELD"}},
+                     {"ASUJ", {"HJUL", "XWWL", "WSCY"}},
+                     {"ASUK", {"RGUX", "OXBQ", "LDTL"}},
+                     {"DEAA", {"HXUG", "BJUN", "IYTV"}},
+                     {"DEAC", {"DSMM", "IXET", "KQDV"}},
+                     {"DEAF", {"TATK", "RWXF", "DQDT"}},
+                     {"DEAG", {"JFEX", "CVLN", "UFWN"}},
+                     {"DRYI", {"LWTQ", "OLEY", "NWUA"}},
+                     {"ZZAB", {"WVIK", "IUXK", "ZCIK"}},
+                     {"ZZAD", {"KSTH", "CBJY", "TSID"}},
+                     {"ZZAF", {"OTWH", "RRNB", "VNXA"}},
+                     {"XWJE", {"KDZI", "IYPJ", "ERIM"}},
+                     {"NBQS", {"KMJF", "MFWA", "UWRX"}},
+                     {"HPZY", {"RAWP", "CNRC", "TPIA"}},
+                     {"HPZV", {"WAFN", "PQVW", "MJVM"}},
+                     {"HPZT", {"IUCU", "WDAV", "LOLH"}},
+                     {"HPZS", {"QRFK", "SQGI", "VESI"}},
+                     {"HPZQ", {"XGER", "OLTF", "DVQA"}},
+                     {"HPZP", {"NQDY", "QIMT", "QKAK"}},
+                     {"JBPA", {"VUZL", "XYPI", "XOWE"}},
+                     {"LEAC", {"DMEA", "EXWD", "PBTU"}},
+                     {"LEAE", {"QFVM", "GACH", "BMXB"}},
+                     {"LEAG", {"XTLW", "WLQO", "QVKP"}},
+                     {"LEAH", {"QIDR", "XBTQ", "QYUO"}},
+                     {"LEAI", {"KCSV", "PRBF", "FVDO"}},
+                     {"LEAK", {"CGWM", "ZLOS", "JGTD"}},
+                     {"LEAL", {"EYPX", "SOCH", "PFPW"}},
+                     {"LEAO", {"MKOE", "YJSI", "QQMN"}},
+                     {"LEAP", {"AEZG", "JOYE", "JHWK"}},
+                     {"ZFCZ", {"JQUA", "SEEH", "RJVV"}},
+                     {"VEUT", {"JDFA", "ALIR", "DDJM"}},
+                     {"SMAE", {"SUUV", "QXWL", "LYKX"}},
+                     {"SMAF", {"HKPA", "NFCE", "UBOP"}},
+                     {"SMAH", {"EXLB", "YYYY", "LLLA"}},
+                     {"SMAI", {"PPDO", "ISMM", "BKNT"}},
+                     {"SMAJ", {"PVCB", "UCIK", "XVBK"}},
+                     {"SMAK", {"WOMZ", "OHAX", "JSTF"}},
+                     {"ZSKM", {"JPEZ", "FTUS", "ZFUF"}},
+                     {"SMAL", {"OWLX", "YXSA", "TXJR"}},
+                     {"TAAB", {"ZBMY", "NYDT", "CXYZ"}},
+                     {"YMMU", {"ZVIA", "CFKN", "ERLO"}},
+                     {"FSFR", {"ZDAR", "BERM", "COKX"}},
+                     {"ASCT", {"CTRF", "LBBD", "YBND"}}});
 
   const auto it = kBrandCodeMap->find(static_brand_code);
   if (it == kBrandCodeMap->end())
diff --git a/chrome/browser/mac/install.sh b/chrome/browser/mac/install.sh
index e39e729b..a7c2a3d7 100755
--- a/chrome/browser/mac/install.sh
+++ b/chrome/browser/mac/install.sh
@@ -98,12 +98,6 @@
 # permissions on any symbolic links.
 find "${DEST}" -type l -exec chmod -h "${CHMOD_MODE}" {} + >& /dev/null
 
-# Host OS version check, to be able to take advantage of features on newer
-# systems and fall back to slow ways of doing things on older systems.
-OS_VERSION=$(sw_vers -productVersion)
-OS_MAJOR=$(sed -Ene 's/^([0-9]+).*/\1/p' <<< ${OS_VERSION})
-OS_MINOR=$(sed -Ene 's/^([0-9]+)\.([0-9]+).*/\2/p' <<< ${OS_VERSION})
-
 # Because this script is launched by the application itself, the installation
 # process inherits the quarantine bit (LSFileQuarantineEnabled).  Any files or
 # directories created during the update will be quarantined in that case,
@@ -113,14 +107,7 @@
 # it can be assumed that the installed copy should not be quarantined.  Use
 # xattr to drop the quarantine attribute.
 QUARANTINE_ATTR=com.apple.quarantine
-if [ ${OS_MAJOR} -gt 10 ] ||
-   ([ ${OS_MAJOR} -eq 10 ] && [ ${OS_MINOR} -ge 6 ]) ; then
-  # On 10.6, xattr supports -r for recursive operation.
-  xattr -d -r "${QUARANTINE_ATTR}" "${DEST}" >& /dev/null
-else
-  # On earlier systems, xattr doesn't support -r, so run xattr via find.
-  find "${DEST}" -exec xattr -d "${QUARANTINE_ATTR}" {} + >& /dev/null
-fi
+xattr -d -r "${QUARANTINE_ATTR}" "${DEST}" >& /dev/null
 
 # Great success!
 exit 0
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index cf5c361..1c982f9 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -200,17 +200,24 @@
 }
 
 #if defined(OS_CHROMEOS)
-void ProfileNetworkContextService::UpdateTrustAnchors(
+void ProfileNetworkContextService::UpdateAdditionalCertificates(
+    const net::CertificateList& all_additional_certificates,
     const net::CertificateList& trust_anchors) {
   content::BrowserContext::ForEachStoragePartition(
-      profile_,
-      base::BindRepeating(
-          [](const net::CertificateList& trust_anchors,
-             content::StoragePartition* storage_partition) {
-            storage_partition->GetNetworkContext()->UpdateTrustAnchors(
-                trust_anchors);
-          },
-          trust_anchors));
+      profile_, base::BindRepeating(
+                    [](const net::CertificateList& all_additional_certificates,
+                       const net::CertificateList& trust_anchors,
+                       content::StoragePartition* storage_partition) {
+                      auto additional_certificates =
+                          network::mojom::AdditionalCertificates::New();
+                      additional_certificates->all_certificates =
+                          all_additional_certificates;
+                      additional_certificates->trust_anchors = trust_anchors;
+                      storage_partition->GetNetworkContext()
+                          ->UpdateAdditionalCertificates(
+                              std::move(additional_certificates));
+                    },
+                    all_additional_certificates, trust_anchors));
 }
 #endif
 
@@ -469,7 +476,12 @@
 
       policy::PolicyCertService* service =
           policy::PolicyCertServiceFactory::GetForProfile(profile_);
-      network_context_params->initial_trust_anchors = service->trust_anchors();
+      network_context_params->initial_additional_certificates =
+          network::mojom::AdditionalCertificates::New();
+      network_context_params->initial_additional_certificates
+          ->all_certificates = service->all_server_and_authority_certs();
+      network_context_params->initial_additional_certificates->trust_anchors =
+          service->trust_anchors();
     }
   }
 #endif
diff --git a/chrome/browser/net/profile_network_context_service.h b/chrome/browser/net/profile_network_context_service.h
index faec69e..abdae110 100644
--- a/chrome/browser/net/profile_network_context_service.h
+++ b/chrome/browser/net/profile_network_context_service.h
@@ -67,7 +67,9 @@
       network::mojom::NetworkContextParamsPtr* network_context_params);
 
 #if defined(OS_CHROMEOS)
-  void UpdateTrustAnchors(const net::CertificateList& trust_anchors);
+  void UpdateAdditionalCertificates(
+      const net::CertificateList& all_additional_certificates,
+      const net::CertificateList& trust_anchors);
 #endif
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
diff --git a/chrome/browser/notifications/notification_platform_bridge_win.cc b/chrome/browser/notifications/notification_platform_bridge_win.cc
index 103090ed..715f66d4 100644
--- a/chrome/browser/notifications/notification_platform_bridge_win.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_win.cc
@@ -868,9 +868,11 @@
 
 // static
 bool NotificationPlatformBridgeWin::NativeNotificationEnabled() {
-  // Windows 10 native notification seems to have memory leak issues on OS
-  // builds older than 17134 (i.e., VERSION_WIN10_RS4). This seems to be a
-  // Windows issue which has been fixed in 17134.
+  // There was a Microsoft bug in Windows 10 prior to build 17134 (i.e.,
+  // VERSION_WIN10_RS4), causing endless loops in displaying notifications. It
+  // significantly amplified the memory and CPU usage. Therefore, we enable
+  // Windows 10 native notification only for build 17134 and later. See
+  // crbug.com/882622 and crbug.com/878823 for more details.
   return base::win::GetVersion() >= base::win::VERSION_WIN10_RS4 &&
          base::FeatureList::IsEnabled(features::kNativeNotifications);
 }
diff --git a/chrome/browser/policy/cloud/machine_level_user_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/machine_level_user_cloud_policy_browsertest.cc
index 9deea9a0..930a808 100644
--- a/chrome/browser/policy/cloud/machine_level_user_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/machine_level_user_cloud_policy_browsertest.cc
@@ -73,7 +73,7 @@
     : public MachineLevelUserCloudPolicyController::Observer {
  public:
   void OnPolicyRegisterFinished(bool succeeded) override {
-    if (!succeeded) {
+    if (!succeeded && should_display_error_message_) {
       EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
 #if defined(OS_MACOSX)
       PostAppControllerNSNotifications();
@@ -93,11 +93,16 @@
     should_succeed_ = should_succeed;
   }
 
+  void SetShouldDisplayErrorMessage(bool should_display) {
+    should_display_error_message_ = should_display;
+  }
+
   bool IsFinished() { return is_finished_; }
 
  private:
   bool is_finished_ = false;
   bool should_succeed_ = false;
+  bool should_display_error_message_ = false;
 };
 
 class FakeBrowserDMTokenStorage : public BrowserDMTokenStorage {
@@ -116,11 +121,16 @@
     std::move(callback).Run(storage_enabled_);
   }
   std::string RetrieveDMToken() override { return dm_token_; }
-  bool ShouldDisplayErrorMessageOnFailure() override { return true; }
+  bool ShouldDisplayErrorMessageOnFailure() override {
+    return should_display_error_message_on_failure_;
+  }
 
   void SetEnrollmentToken(const std::string& enrollment_token) {
     enrollment_token_ = enrollment_token;
   }
+  void SetErrorMessageOption(bool should_displayed) {
+    should_display_error_message_on_failure_ = should_displayed;
+  }
 
   void SetClientId(std::string client_id) { client_id_ = client_id; }
 
@@ -152,6 +162,7 @@
   std::string client_id_;
   std::string dm_token_;
   bool storage_enabled_ = true;
+  bool should_display_error_message_on_failure_ = true;
 
   DISALLOW_COPY_AND_ASSIGN(FakeBrowserDMTokenStorage);
 };
@@ -383,7 +394,7 @@
 
 class MachineLevelUserCloudPolicyEnrollmentTest
     : public InProcessBrowserTest,
-      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
+      public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
  public:
   MachineLevelUserCloudPolicyEnrollmentTest() {
     BrowserDMTokenStorage::SetForTesting(&storage_);
@@ -392,8 +403,12 @@
                                     : kInvalidEnrollmentToken);
     storage_.SetClientId("client_id");
     storage_.EnableStorage(storage_enabled());
+    storage_.SetErrorMessageOption(should_display_error_message());
+
     observer_.SetShouldSucceed(is_enrollment_token_valid());
-    if (!is_enrollment_token_valid()) {
+    observer_.SetShouldDisplayErrorMessage(should_display_error_message());
+
+    if (!is_enrollment_token_valid() && should_display_error_message()) {
       set_expected_exit_code(
           chrome::RESULT_CODE_CLOUD_POLICY_ENROLLMENT_FAILED);
     }
@@ -455,6 +470,7 @@
  protected:
   bool is_enrollment_token_valid() const { return std::get<0>(GetParam()); }
   bool storage_enabled() const { return std::get<1>(GetParam()); }
+  bool should_display_error_message() const { return std::get<2>(GetParam()); }
 
   base::HistogramTester histogram_tester_;
 
@@ -467,8 +483,9 @@
 };
 
 IN_PROC_BROWSER_TEST_P(MachineLevelUserCloudPolicyEnrollmentTest, Test) {
-  // Test body is ran only if enrollment is succeeded.
-  EXPECT_TRUE(is_enrollment_token_valid());
+  // Test body is run only if enrollment is succeeded or failed without error
+  // message.
+  EXPECT_TRUE(is_enrollment_token_valid() || !should_display_error_message());
 
   EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
 
@@ -485,6 +502,7 @@
 INSTANTIATE_TEST_CASE_P(,
                         MachineLevelUserCloudPolicyEnrollmentTest,
                         ::testing::Combine(::testing::Bool(),
+                                           ::testing::Bool(),
                                            ::testing::Bool()));
 
 }  // namespace policy
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_controller.cc b/chrome/browser/policy/machine_level_user_cloud_policy_controller.cc
index 183792ec..f1a0c65 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_controller.cc
+++ b/chrome/browser/policy/machine_level_user_cloud_policy_controller.cc
@@ -16,6 +16,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/policy/browser_dm_token_storage.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/cloud/machine_level_user_cloud_policy_helper.h"
@@ -38,6 +39,10 @@
 #include "chrome/common/chrome_switches.h"
 #endif
 
+#if defined(OS_MACOSX)
+#include "chrome/browser/app_controller_mac.h"
+#endif
+
 namespace policy {
 
 namespace {
@@ -194,12 +199,30 @@
   }
 }
 
-MachineLevelUserCloudPolicyController::RegisterResult
-MachineLevelUserCloudPolicyController::WaitUntilPolicyEnrollmentFinished() {
+bool MachineLevelUserCloudPolicyController::
+    WaitUntilPolicyEnrollmentFinished() {
   if (policy_register_watcher_) {
-    return policy_register_watcher_->WaitUntilCloudPolicyEnrollmentFinished();
+    switch (
+        policy_register_watcher_->WaitUntilCloudPolicyEnrollmentFinished()) {
+      case RegisterResult::kNoEnrollmentNeeded:
+      case RegisterResult::kEnrollmentSuccessBeforeDialogDisplayed:
+      case RegisterResult::kEnrollmentFailedSilentlyBeforeDialogDisplayed:
+        return true;
+      case RegisterResult::kEnrollmentSuccess:
+      case RegisterResult::kEnrollmentFailedSilently:
+#if defined(OS_MACOSX)
+        app_controller_mac::EnterpriseStartupDialogClosed();
+#endif
+        return true;
+      case RegisterResult::kRestartDueToFailure:
+        chrome::AttemptRestart();
+        return false;
+      case RegisterResult::kQuitDueToFailure:
+        chrome::AttemptExit();
+        return false;
+    }
   }
-  return RegisterResult::kNoEnrollmentNeeded;
+  return true;
 }
 
 void MachineLevelUserCloudPolicyController::AddObserver(Observer* observer) {
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_controller.h b/chrome/browser/policy/machine_level_user_cloud_policy_controller.h
index d5ab692..7c1adb03 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_controller.h
+++ b/chrome/browser/policy/machine_level_user_cloud_policy_controller.h
@@ -40,6 +40,13 @@
     kQuitDueToFailure,   // The enrollment has failed or aborted, user choose to
                          // quit Chrome.
     kRestartDueToFailure,  // The enrollment has failed, user choose to restart
+    kEnrollmentFailedSilently,  // The enrollment has failed, admin choose to
+                                // ignore the error message.
+    kEnrollmentFailedSilentlyBeforeDialogDisplayed,  // The enrollment has
+                                                     // failed before dialog
+                                                     // displayed, admin choose
+                                                     // to ignore the error
+                                                     // message.
   };
 
   class Observer {
@@ -63,7 +70,7 @@
   void Init(PrefService* local_state,
             scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
-  RegisterResult WaitUntilPolicyEnrollmentFinished();
+  bool WaitUntilPolicyEnrollmentFinished();
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.cc b/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.cc
index 96908ec..08e1e84 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.cc
+++ b/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.cc
@@ -38,14 +38,12 @@
     WaitUntilCloudPolicyEnrollmentFinished() {
   BrowserDMTokenStorage* token_storage = BrowserDMTokenStorage::Get();
 
-  if (token_storage->RetrieveEnrollmentToken().empty()) {
+  if (token_storage->RetrieveEnrollmentToken().empty())
     return RegisterResult::kNoEnrollmentNeeded;
-  }
 
   // We are already enrolled successfully.
-  if (!token_storage->RetrieveDMToken().empty()) {
+  if (!token_storage->RetrieveDMToken().empty())
     return RegisterResult::kEnrollmentSuccessBeforeDialogDisplayed;
-  }
 
   EnterpriseStartupDialog::DialogResultCallback callback = base::BindOnce(
       &MachineLevelUserCloudPolicyRegisterWatcher::OnDialogClosed,
@@ -56,27 +54,38 @@
     dialog_ = EnterpriseStartupDialog::CreateAndShowDialog(std::move(callback));
 
   visible_start_time_ = base::Time::Now();
-  RecordEnrollmentStartDialog(EnrollmentStartupDialog::kShown);
 
   if (register_result_) {
-    // |register_result_| has been set only if the enrollment has finihsed.
+    // |register_result_| has been set only if the enrollment has finished.
     // And it must be failed if it's finished without a DM token which is
     // checked above. Show the error message directly.
     DCHECK(!register_result_.value());
+
+    if (!token_storage->ShouldDisplayErrorMessageOnFailure())
+      return RegisterResult::kEnrollmentFailedSilentlyBeforeDialogDisplayed;
+
     DisplayErrorMessage();
   } else {
     // Display the loading dialog and wait for the enrollment process.
     dialog_->DisplayLaunchingInformationWithThrobber(l10n_util::GetStringUTF16(
         IDS_ENTERPRISE_STARTUP_CLOUD_POLICY_ENROLLMENT_TOOLTIP));
   }
+  RecordEnrollmentStartDialog(EnrollmentStartupDialog::kShown);
   run_loop_.Run();
   if (register_result_.value_or(false))
     return RegisterResult::kEnrollmentSuccess;
 
+  if (!token_storage->ShouldDisplayErrorMessageOnFailure() &&
+      register_result_) {
+    SYSLOG(ERROR) << "Machine level user cloud policy enrollment has failed.";
+    return RegisterResult::kEnrollmentFailedSilently;
+  }
+
   SYSLOG(ERROR) << "Can not start Chrome as machine level user cloud policy "
                    "enrollment has failed. Please double check network "
                    "connection and the status of enrollment token then open "
                    "Chrome again.";
+
   if (is_restart_needed_)
     return RegisterResult::kRestartDueToFailure;
   return RegisterResult::kQuitDueToFailure;
@@ -105,7 +114,8 @@
   // show the error message. If dialog has been closed before enrollment
   // finished, Chrome should already be in the shutdown process.
   if (dialog_ && dialog_->IsShowing()) {
-    if (register_result_.value()) {
+    if (register_result_.value() ||
+        !BrowserDMTokenStorage::Get()->ShouldDisplayErrorMessageOnFailure()) {
       dialog_.reset();
     } else {
       DisplayErrorMessage();
@@ -118,7 +128,12 @@
     bool can_show_browser_window) {
   if (can_show_browser_window) {
     // Chrome startup can continue normally.
-    RecordEnrollmentStartDialog(EnrollmentStartupDialog::kClosedSuccess);
+    if (register_result_.value()) {
+      RecordEnrollmentStartDialog(EnrollmentStartupDialog::kClosedSuccess);
+    } else {
+      RecordEnrollmentStartDialog(
+          EnrollmentStartupDialog::kClosedFailAndIgnore);
+    }
   } else if (is_accepted) {
     // User chose to restart chrome and try re-enrolling.
     RecordEnrollmentStartDialog(EnrollmentStartupDialog::kClosedRelaunch);
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h b/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h
index 2e30c04..fa31df6c 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h
+++ b/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher.h
@@ -48,13 +48,23 @@
   FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
                            EnrollmentSucceed);
   FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+                           EnrollmentSucceedWithNoErrorMessageSetup);
+  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
                            EnrollmentFailedAndQuit);
   FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
                            EnrollmentFailedAndRestart);
   FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
                            EnrollmentCanceledBeforeFinish);
+  FRIEND_TEST_ALL_PREFIXES(
+      MachineLevelUserCloudPolicyRegisterWatcherTest,
+      EnrollmentCanceledBeforeFinishWithNoErrorMessageSetup);
   FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
                            EnrollmentFailedBeforeDialogDisplay);
+  FRIEND_TEST_ALL_PREFIXES(MachineLevelUserCloudPolicyRegisterWatcherTest,
+                           EnrollmentFailedWithoutErrorMessage);
+  FRIEND_TEST_ALL_PREFIXES(
+      MachineLevelUserCloudPolicyRegisterWatcherTest,
+      EnrollmentFailedBeforeDialogDisplayWithoutErrorMessage);
 
   // Enum used with kStartupDialogHistogramName.
   enum class EnrollmentStartupDialog {
@@ -77,7 +87,11 @@
     // before the user gave up and closed the dialog.
     kClosedAbort = 4,
 
-    kMaxValue = kClosedAbort,
+    // The dialog was closed automatically because enrollment failed but admin
+    // choose to ignore the error and show the browser window.
+    kClosedFailAndIgnore = 5,
+
+    kMaxValue = kClosedFailAndIgnore,
   };
 
   static const char kStartupDialogHistogramName[];
diff --git a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher_unittest.cc b/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher_unittest.cc
index 52af0ae8..20ec082e 100644
--- a/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher_unittest.cc
+++ b/chrome/browser/policy/machine_level_user_cloud_policy_register_watcher_unittest.cc
@@ -38,7 +38,9 @@
   std::string RetrieveDMToken() override { return dm_token_; }
   std::string RetrieveEnrollmentToken() override { return enrollment_token_; }
   std::string RetrieveClientId() override { return kClientId; }
-  bool ShouldDisplayErrorMessageOnFailure() override { return true; }
+  bool ShouldDisplayErrorMessageOnFailure() override {
+    return should_display_error_message_on_failure_;
+  }
 
   std::string InitClientId() override {
     NOTREACHED();
@@ -63,10 +65,14 @@
   void set_enrollment_token(const std::string& enrollment_token) {
     enrollment_token_ = enrollment_token;
   }
+  void set_should_display_error_message_on_failure(bool should_display) {
+    should_display_error_message_on_failure_ = should_display;
+  }
 
  private:
   std::string enrollment_token_;
   std::string dm_token_;
+  bool should_display_error_message_on_failure_ = true;
 
   DISALLOW_COPY_AND_ASSIGN(FakeDMTokenStorage);
 };
@@ -204,6 +210,32 @@
 }
 
 TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+       EnrollmentSucceedWithNoErrorMessageSetup) {
+  base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(*dialog(), DisplayLaunchingInformationWithThrobber(_));
+  EXPECT_CALL(*dialog(), IsShowing()).WillOnce(Return(true));
+  storage()->set_should_display_error_message_on_failure(false);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FakeMachineLevelUserCloudPolicyController::FireNotification,
+          base::Unretained(controller()), true));
+  EXPECT_EQ(RegisterResult::kEnrollmentSuccess,
+            watcher()->WaitUntilCloudPolicyEnrollmentFinished());
+  histogram_tester.ExpectBucketCount(
+      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
+      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+          kShown,
+      1);
+  histogram_tester.ExpectBucketCount(
+      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
+      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+          kClosedSuccess,
+      1);
+}
+
+TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
        EnrollmentFailedAndQuit) {
   base::HistogramTester histogram_tester;
 
@@ -283,6 +315,31 @@
 }
 
 TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+       EnrollmentCanceledBeforeFinishWithNoErrorMessageSetup) {
+  base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(*dialog(), DisplayLaunchingInformationWithThrobber(_));
+  storage()->set_should_display_error_message_on_failure(false);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&MockEnterpriseStartupDialog::UserClickedTheButton,
+                     base::Unretained(dialog()), false));
+  EXPECT_EQ(RegisterResult::kQuitDueToFailure,
+            watcher()->WaitUntilCloudPolicyEnrollmentFinished());
+  histogram_tester.ExpectBucketCount(
+      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
+      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+          kShown,
+      1);
+  histogram_tester.ExpectBucketCount(
+      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
+      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+          kClosedAbort,
+      1);
+}
+
+TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
        EnrollmentFailedBeforeDialogDisplay) {
   base::HistogramTester histogram_tester;
 
@@ -304,4 +361,43 @@
       1);
 }
 
+TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+       EnrollmentFailedWithoutErrorMessage) {
+  base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(*dialog(), DisplayLaunchingInformationWithThrobber(_));
+  EXPECT_CALL(*dialog(), IsShowing()).WillOnce(Return(true));
+  storage()->set_should_display_error_message_on_failure(false);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FakeMachineLevelUserCloudPolicyController::FireNotification,
+          base::Unretained(controller()), false));
+  EXPECT_EQ(RegisterResult::kEnrollmentFailedSilently,
+            watcher()->WaitUntilCloudPolicyEnrollmentFinished());
+  histogram_tester.ExpectBucketCount(
+      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
+      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+          kShown,
+      1);
+  histogram_tester.ExpectBucketCount(
+      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
+      MachineLevelUserCloudPolicyRegisterWatcher::EnrollmentStartupDialog::
+          kClosedFailAndIgnore,
+      1);
+}
+
+TEST_F(MachineLevelUserCloudPolicyRegisterWatcherTest,
+       EnrollmentFailedBeforeDialogDisplayWithoutErrorMessage) {
+  base::HistogramTester histogram_tester;
+
+  storage()->set_should_display_error_message_on_failure(false);
+  controller()->FireNotification(false);
+  EXPECT_EQ(RegisterResult::kEnrollmentFailedSilentlyBeforeDialogDisplayed,
+            watcher()->WaitUntilCloudPolicyEnrollmentFinished());
+  histogram_tester.ExpectTotalCount(
+      MachineLevelUserCloudPolicyRegisterWatcher::kStartupDialogHistogramName,
+      0);
+}
+
 }  // namespace policy
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index c9986a1..edd85e91 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -381,6 +381,7 @@
 // Deprecated 11/2018.
 const char kNetworkQualities[] = "net.network_qualities";
 const char kForceSessionSync[] = "settings.history_recorded";
+const char kOnboardDuringNUX[] = "browser.onboard_during_nux";
 
 // Register prefs used only for migration (clearing or moving to a new key).
 void RegisterProfilePrefsForMigration(
@@ -410,6 +411,7 @@
 
   registry->RegisterDictionaryPref(kNetworkQualities, PrefRegistry::LOSSY_PREF);
   registry->RegisterBooleanPref(kForceSessionSync, false);
+  registry->RegisterBooleanPref(kOnboardDuringNUX, false);
 }
 
 }  // namespace
@@ -873,4 +875,5 @@
   // Added 11/2018.
   profile_prefs->ClearPref(kNetworkQualities);
   profile_prefs->ClearPref(kForceSessionSync);
+  profile_prefs->ClearPref(kOnboardDuringNUX);
 }
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index cc62bbe..608e518 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -63,6 +63,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/browser/ui/sync/sync_promo_ui.h"
+#include "chrome/browser/ui/webui/welcome/nux_helper.h"
 #include "chrome/browser/unified_consent/unified_consent_service_factory.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_constants.h"
@@ -1078,9 +1079,10 @@
   if (profile->IsNewProfile() || first_run::IsChromeFirstRun()) {
     profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, false);
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
-    // Enterprise users should not be included in any NUX flow.
+    // Enterprise users should not be included in any NUX/Navi flow.
     if (!base::win::IsEnterpriseManaged()) {
-      profile->GetPrefs()->SetBoolean(prefs::kOnboardDuringNUX, true);
+      int group = nux::GetOnboardingGroup();
+      profile->GetPrefs()->SetInteger(prefs::kNuxOnboardGroup, group);
     }
 #endif  // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
   }
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/next_earcons.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/next_earcons.js
index 50e61154..2e861aa 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/next_earcons.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/next_earcons.js
@@ -36,11 +36,15 @@
   /** @private {boolean} */
   this.shouldPan_ = true;
 
-  chrome.audio.getDevices(
-      {isActive: true, streamTypes: [chrome.audio.StreamType.OUTPUT]},
-      this.updateShouldPanForDevices_.bind(this));
-  chrome.audio.onDeviceListChanged.addListener(
-      this.updateShouldPanForDevices_.bind(this));
+  if (chrome.audio) {
+    chrome.audio.getDevices(
+        {isActive: true, streamTypes: [chrome.audio.StreamType.OUTPUT]},
+        this.updateShouldPanForDevices_.bind(this));
+    chrome.audio.onDeviceListChanged.addListener(
+        this.updateShouldPanForDevices_.bind(this));
+  } else {
+    this.shouldPan_ = false;
+  }
 };
 
 NextEarcons.prototype = {
diff --git a/chrome/browser/sessions/tab_loader.cc b/chrome/browser/sessions/tab_loader.cc
index f07309b..a55f56cc 100644
--- a/chrome/browser/sessions/tab_loader.cc
+++ b/chrome/browser/sessions/tab_loader.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "components/favicon/content/content_favicon_driver.h"
+#include "content/public/browser/background_tracing_manager.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
@@ -27,6 +28,18 @@
 
 namespace {
 
+void BackgroundTracingTrigger() {
+  static content::BackgroundTracingManager::TriggerHandle trigger_handle_ = -1;
+  if (trigger_handle_ == -1) {
+    trigger_handle_ =
+        content::BackgroundTracingManager::GetInstance()->RegisterTriggerType(
+            "session-restore-config");
+  }
+  content::BackgroundTracingManager::GetInstance()->TriggerNamedEvent(
+      trigger_handle_,
+      content::BackgroundTracingManager::StartedFinalizingCallback());
+}
+
 const base::TickClock* GetDefaultTickClock() {
   static base::NoDestructor<base::DefaultTickClock> default_tick_clock;
   return default_tick_clock.get();
@@ -80,6 +93,10 @@
   if (tabs.empty())
     return;
 
+  // Trigger a slow-reports and collect a session restore trace if needed.
+  BackgroundTracingTrigger();
+  TRACE_EVENT0("browser", "TabLoader::RestoreTabs");
+
   if (!shared_tab_loader_)
     shared_tab_loader_ = new TabLoader(restore_started);
 
@@ -172,6 +189,7 @@
 void TabLoader::StartLoading(const std::vector<RestoredTab>& tabs) {
   DCHECK(!tabs.empty());
   ReentrancyHelper lifetime_helper(this);
+  TRACE_EVENT1("browser", "TabLoader::StartLoading", "tabs_count", tabs.size());
 
   // Create a TabLoaderDelegate which will allow OS specific behavior for tab
   // loading. This needs to be done before any calls to AddTab, as the delegate
@@ -221,6 +239,7 @@
                                      LoadingState old_loading_state,
                                      LoadingState new_loading_state) {
   ReentrancyHelper lifetime_helper(this);
+  TRACE_EVENT0("browser", "TabLoader::OnLoadingStateChange");
 
   // Calls into this can come from observers that are still running even if
   // |is_loading_enabled_| is false.
@@ -263,6 +282,8 @@
 void TabLoader::OnMemoryPressure(
     base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
   ReentrancyHelper lifetime_helper(this);
+  TRACE_EVENT0("browser", "TabLoader::OnMemoryPressure");
+
   switch (memory_pressure_level) {
     case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
       break;
@@ -362,6 +383,7 @@
 
 void TabLoader::MarkTabAsLoadInitiated(WebContents* contents) {
   DCHECK(reentry_depth_ > 0);  // This can only be called internally.
+  TRACE_EVENT0("browser", "TabLoader::MarkTabAsLoadInitiated");
 
   // This can only be called for a tab that is waiting to be loaded so this
   // should never fail.
@@ -379,6 +401,7 @@
 
 void TabLoader::MarkTabAsLoading(WebContents* contents) {
   DCHECK(reentry_depth_ > 0);  // This can only be called internally.
+  TRACE_EVENT0("browser", "TabLoader::MarkTabAsLoading");
 
   // Calls into this can come from observers that are still running even if
   // |is_loading_enabled_| is false.
@@ -413,6 +436,7 @@
 
 void TabLoader::MarkTabAsDeferred(content::WebContents* contents) {
   DCHECK(reentry_depth_ > 0);  // This can only be called internally.
+  TRACE_EVENT0("browser", "TabLoader::MarkTabAsDeferred");
 
   // This can only be called for a tab that is waiting to be loaded so this
   // should never fail.
@@ -424,6 +448,7 @@
 
 void TabLoader::MaybeLoadSomeTabs() {
   DCHECK(reentry_depth_ > 0);  // This can only be called internally.
+  TRACE_EVENT0("browser", "TabLoader::MaybeLoadSomeTabs");
 
   if (!is_loading_enabled_ || tabs_to_load_.empty())
     return;
@@ -437,6 +462,7 @@
 
 void TabLoader::ForceLoadTimerFired() {
   ReentrancyHelper lifetime_helper(this);
+  TRACE_EVENT0("browser", "TabLoader::ForceLoadTimerFired");
 
   // CheckInvariants can't be called directly as the timer is no longer
   // running at this point. However, the conditions under which the timer
@@ -468,6 +494,7 @@
 
 void TabLoader::StopLoadingTabs() {
   DCHECK(reentry_depth_ > 0);  // This can only be called internally.
+  TRACE_EVENT0("browser", "TabLoader::StopLoadingTabs");
 
   // Calls into this can come from observers that are still running even if
   // |is_loading_enabled_| is false.
@@ -508,6 +535,7 @@
 void TabLoader::LoadNextTab(bool due_to_timeout) {
   DCHECK(reentry_depth_ > 0);  // This can only be called internally.
   DCHECK(!tabs_to_load_.empty());
+  TRACE_EVENT0("browser", "TabLoader::LoadNextTab");
 
   // This is checked before loading every single tab to ensure that responses
   // to memory pressure are immediate.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a8d9d649..e830eb6 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3298,6 +3298,8 @@
       "in_product_help/reopen_tab_in_product_help_factory.h",
       "in_product_help/reopen_tab_in_product_help_trigger.cc",
       "in_product_help/reopen_tab_in_product_help_trigger.h",
+      "views/feature_promos/reopen_tab_promo_controller.cc",
+      "views/feature_promos/reopen_tab_promo_controller.h",
     ]
   }
 
diff --git a/chrome/browser/ui/ash/multi_user/DEPS b/chrome/browser/ui/ash/multi_user/DEPS
index 1a1ad789..e8f4899e 100644
--- a/chrome/browser/ui/ash/multi_user/DEPS
+++ b/chrome/browser/ui/ash/multi_user/DEPS
@@ -8,5 +8,6 @@
   "multi_user_window_manager_chromeos_unittest\.cc": [
     "+ash/multi_user/user_switch_animator.h",
     "+ash/session/session_controller.h",
+    "+ash/ws/window_lookup.h",
   ],
 }
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
index 7184725..ee49dd71 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
@@ -26,8 +26,14 @@
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
+#include "ui/aura/mus/window_mus.h"
+#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_types.h"
+#include "ui/views/mus/mus_client.h"
+#include "ui/views/widget/widget.h"
 
 namespace {
 
@@ -80,6 +86,26 @@
                             NUM_TELEPORT_WINDOW_TYPES);
 }
 
+// Returns the WindowMus to use when sending messages to the server.
+aura::WindowMus* GetWindowMus(aura::Window* window) {
+  if (!aura::WindowMus::Get(window))
+    return nullptr;
+
+  aura::Window* root_window = window->GetRootWindow();
+  if (!root_window)
+    return nullptr;
+
+  // DesktopNativeWidgetAura creates two aura Windows. GetNativeWindow() returns
+  // the child window. Get the widget for |window| and its root. If the Widgets
+  // are the same, it means |window| is the native window of a
+  // DesktopNativeWidgetAura. Use the root window to notify the server as that
+  // corresponds to the top-level window that ash knows about.
+  views::Widget* widget = views::Widget::GetWidgetForNativeView(window);
+  views::Widget* root_widget =
+      views::Widget::GetWidgetForNativeView(root_window);
+  return widget == root_widget ? aura::WindowMus::Get(root_window) : nullptr;
+}
+
 }  // namespace
 
 // This class keeps track of all applications which were started for a user.
@@ -110,20 +136,26 @@
     : current_account_id_(current_account_id),
       ash_multi_user_window_manager_(
           std::make_unique<ash::MultiUserWindowManager>(this,
-                                                        current_account_id)) {}
+                                                        current_account_id)) {
+  if (features::IsUsingWindowService()) {
+    multi_user_window_manager_mojom_ =
+        views::MusClient::Get()
+            ->window_tree_client()
+            ->BindWindowManagerInterface<ash::mojom::MultiUserWindowManager>();
+  }
+}
 
 MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() {
   // This may trigger callbacks to us, delete it early on.
   ash_multi_user_window_manager_.reset();
 
   // Remove all window observers.
-  WindowToEntryMap::iterator window = window_to_entry_.begin();
-  while (window != window_to_entry_.end()) {
+  while (!window_to_entry_.empty()) {
     // Explicitly remove this from window observer list since OnWindowDestroyed
     // no longer does that.
-    window->first->RemoveObserver(this);
-    OnWindowDestroyed(window->first);
-    window = window_to_entry_.begin();
+    aura::Window* window = window_to_entry_.begin()->first;
+    window->RemoveObserver(this);
+    OnWindowDestroyed(window);
   }
 
   // Remove all app observers.
@@ -174,17 +206,31 @@
     return;
 
   DCHECK(GetWindowOwner(window).empty());
-  window_to_entry_[window] = new WindowEntry(account_id);
+  std::unique_ptr<WindowEntry> window_entry_ptr =
+      std::make_unique<WindowEntry>(account_id);
+  WindowEntry* window_entry = window_entry_ptr.get();
+  window_to_entry_[window] = std::move(window_entry_ptr);
 
-  ash_multi_user_window_manager_->SetWindowOwner(window, account_id);
+  // Check if this window was created due to a user interaction. If it was,
+  // transfer it to the current user.
+  const bool show_for_current_user =
+      window->GetProperty(aura::client::kCreatedByUserGesture);
+  if (window->env()->mode() == aura::Env::Mode::MUS) {
+    aura::WindowMus* window_mus = GetWindowMus(window);
+    if (window_mus) {
+      multi_user_window_manager_mojom_->SetWindowOwner(
+          window_mus->server_id(), account_id, show_for_current_user);
+    }  // else case can happen during shutdown, or for child windows.
+  } else {
+    ash_multi_user_window_manager_->SetWindowOwner(window, account_id,
+                                                   show_for_current_user);
+  }
 
   // Add observers to track state changes.
   window->AddObserver(this);
 
-  // Check if this window was created due to a user interaction. If it was,
-  // transfer it to the current user.
-  if (window->GetProperty(aura::client::kCreatedByUserGesture))
-    window_to_entry_[window]->set_show_for_user(current_account_id_);
+  if (show_for_current_user)
+    window_entry->set_show_for_user(current_account_id_);
 
   // Notify entry adding.
   for (Observer& observer : observers_)
@@ -200,13 +246,23 @@
 void MultiUserWindowManagerChromeOS::ShowWindowForUser(
     aura::Window* window,
     const AccountId& account_id) {
-  ash_multi_user_window_manager_->ShowWindowForUser(window, account_id);
+  if (!window)
+    return;
+
+  if (window->env()->mode() == aura::Env::Mode::MUS) {
+    aura::WindowMus* window_mus = GetWindowMus(window);
+    if (window_mus) {
+      multi_user_window_manager_mojom_->ShowWindowForUser(
+          window_mus->server_id(), account_id);
+    }
+  } else {
+    ash_multi_user_window_manager_->ShowWindowForUser(window, account_id);
+  }
 }
 
 bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() const {
-  WindowToEntryMap::const_iterator it = window_to_entry_.begin();
-  for (; it != window_to_entry_.end(); ++it) {
-    if (it->second->owner() != it->second->show_for_user())
+  for (auto& window_pair : window_to_entry_) {
+    if (window_pair.second->owner() != window_pair.second->show_for_user())
       return true;
   }
   return false;
@@ -214,10 +270,9 @@
 
 void MultiUserWindowManagerChromeOS::GetOwnersOfVisibleWindows(
     std::set<AccountId>* account_ids) const {
-  for (WindowToEntryMap::const_iterator it = window_to_entry_.begin();
-       it != window_to_entry_.end(); ++it) {
-    if (it->first->IsVisible())
-      account_ids->insert(it->second->owner());
+  for (auto& window_pair : window_to_entry_) {
+    if (window_pair.first->IsVisible())
+      account_ids->insert(window_pair.second->owner());
   }
 }
 
@@ -278,8 +333,6 @@
 }
 
 void MultiUserWindowManagerChromeOS::OnWindowDestroyed(aura::Window* window) {
-  // Remove the window from the owners list.
-  delete window_to_entry_[window];
   window_to_entry_.erase(window);
 }
 
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
index 1db57996..ed9bd5f 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
@@ -7,9 +7,9 @@
 
 #include <map>
 #include <memory>
-#include <string>
 
 #include "ash/multi_user/multi_user_window_manager_delegate.h"
+#include "ash/public/interfaces/multi_user_window_manager.mojom.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
@@ -91,8 +91,8 @@
   // Returns the current user for unit tests.
   const AccountId& GetCurrentUserForTest() const;
 
- protected:
-  friend class UserSwitchAnimatorChromeOS;
+ private:
+  friend class ash::MultiUserWindowManagerChromeOSTest;
 
   class WindowEntry {
    public:
@@ -122,15 +122,10 @@
     DISALLOW_COPY_AND_ASSIGN(WindowEntry);
   };
 
-  // TODO(sky): make this map to unique_ptr<WindowEntry>.
-  using WindowToEntryMap = std::map<aura::Window*, WindowEntry*>;
+  using AccountIdToAppWindowObserver = std::map<AccountId, AppObserver*>;
 
-  const WindowToEntryMap& window_to_entry() { return window_to_entry_; }
-
- private:
-  friend class ash::MultiUserWindowManagerChromeOSTest;
-
-  typedef std::map<AccountId, AppObserver*> AccountIdToAppWindowObserver;
+  using WindowToEntryMap =
+      std::map<aura::Window*, std::unique_ptr<WindowEntry>>;
 
   // Add a browser window to the system so that the owner can be remembered.
   void AddBrowserWindow(Browser* browser);
@@ -153,8 +148,17 @@
   // The notification registrar to track the creation of browser windows.
   content::NotificationRegistrar registrar_;
 
+  // TODO: this won't work in the multi-process mash case. What needs to happen
+  // for the multi-process case is MultiUserWindowManagerDelegate needs to be
+  // converted to a mojom that ash uses to call this code.
+  // https://crbug.com/875111.
   std::unique_ptr<ash::MultiUserWindowManager> ash_multi_user_window_manager_;
 
+  // Only used for windows created for the window-service. For example,
+  // Browser windows when running in mash.
+  ash::mojom::MultiUserWindowManagerAssociatedPtr
+      multi_user_window_manager_mojom_;
+
   DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerChromeOS);
 };
 
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
index 1ff34c2..1400885 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
@@ -25,6 +25,7 @@
 #include "ash/wm/tablet_mode/tablet_mode_window_manager.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
+#include "ash/ws/window_lookup.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -58,13 +59,22 @@
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_info.h"
 #include "components/user_manager/user_manager.h"
+#include "services/ws/common/util.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
+#include "ui/aura/mus/window_port_mus.h"
+#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/test/env_test_helper.h"
+#include "ui/aura/test/mus/change_completion_waiter.h"
+#include "ui/aura/test/mus/window_tree_client_test_api.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/test/display_manager_test_api.h"
+#include "ui/views/mus/mus_client.h"
+#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 #include "ui/wm/core/window_modality_controller.h"
 #include "ui/wm/core/window_util.h"
 #include "ui/wm/public/activation_client.h"
@@ -78,6 +88,43 @@
 const char kArrowBAccountIdString[] =
     "->{\"account_type\":\"unknown\",\"email\":\"B\"}";
 
+aura::Window* FindWindowInAshForClientWindow(aura::Window* window) {
+  if (!aura::WindowMus::Get(window))
+    return nullptr;
+
+  // Flush all messages from the WindowTreeClient to ensure the client id has
+  // been received.
+  aura::test::WaitForAllChangesToComplete();
+
+  DCHECK_EQ(window->env()->mode(), aura::Env::Mode::MUS);
+  DCHECK(views::MusClient::Exists());
+  aura::WindowTreeClient* window_tree_client =
+      views::MusClient::Get()->window_tree_client();
+  DCHECK(window_tree_client);
+
+  aura::WindowPortMus* window_port_mus = aura::WindowPortMus::Get(window);
+  if (!window_port_mus)
+    return nullptr;  // Should only happen if we're in shutdown.
+
+  // By the time we get here the id should have been determined.
+  DCHECK(window_tree_client->id().has_value());
+  ws::ClientSpecificId client_id = *(window_tree_client->id());
+
+  // Use the top-most remote window. This should correspond to the Window of
+  // the Widget (DesktopNativeWidgetAura creates two windows).
+  const ws::Id transport_id = ws::BuildTransportId(
+      client_id,
+      static_cast<ws::ClientSpecificId>(window_port_mus->server_id()));
+  return ash::window_lookup::GetWindowByClientId(transport_id);
+}
+
+void FlushWindowTreeClientMessages() {
+  if (!views::MusClient::Exists())
+    return;
+  aura::WindowTreeClientTestApi(views::MusClient::Get()->window_tree_client())
+      .FlushForTesting();
+}
+
 const content::BrowserContext* GetActiveContext() {
   const user_manager::UserManager* user_manager =
       user_manager::UserManager::Get();
@@ -130,8 +177,14 @@
 
  protected:
   void SwitchActiveUser(const AccountId& id) {
+    // WaitForAllChangesToComplete() does nothing in classic mode.
+    // WaitForAllChangesToComplete() is called before and after to ensure all
+    // changes have been pushed to ash before a switch, and similarly after a
+    // switch.
+    FlushWindowTreeClientMessages();
     fake_user_manager_->SwitchActiveUser(id);
     ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(id);
+    FlushWindowTreeClientMessages();
   }
 
   // Set up the test environment for this many windows.
@@ -282,7 +335,6 @@
       TestingBrowserProcess::GetGlobal()->local_state());
   ash_test_helper()->set_test_shell_delegate(new TestShellDelegateChromeOS);
   AshTestBase::SetUp();
-  // This test has mixed case user ids.
   ash_test_helper()
       ->test_session_controller_client()
       ->set_use_lower_case_user_id(false);
@@ -1533,4 +1585,159 @@
   EXPECT_EQ(bounds, window(1)->bounds());
 }
 
+class MultiUserWindowManagerChromeOSMashTest
+    : public MultiUserWindowManagerChromeOSTest {
+ public:
+  MultiUserWindowManagerChromeOSMashTest() = default;
+  ~MultiUserWindowManagerChromeOSMashTest() override = default;
+
+  // MultiUserWindowManagerChromeOSTest:
+  void SetUp() override {
+    original_aura_env_mode_ =
+        aura::test::EnvTestHelper().SetMode(aura::Env::Mode::MUS);
+    feature_list_.InitWithFeatures({::features::kSingleProcessMash}, {});
+    MultiUserWindowManagerChromeOSTest::SetUp();
+    // TabletModeController calls to PowerManagerClient with a callback that is
+    // run via a posted task. Run the loop now so that we know the task is
+    // processed. Without this, the task gets processed later on, interfering
+    // with this test.
+    base::RunLoop().RunUntilIdle();
+
+    // This test configures views with mus, which means it triggers some of the
+    // DCHECKs ensuring Shell's Env is used.
+    SetRunningOutsideAsh();
+
+    // Configure views backed by mus.
+    views::MusClient::InitParams mus_client_init_params;
+    mus_client_init_params.connector =
+        ash_test_helper()->GetWindowServiceConnector();
+    mus_client_init_params.create_wm_state = false;
+    mus_client_init_params.running_in_ws_process = true;
+    mus_client_ = std::make_unique<views::MusClient>(mus_client_init_params);
+  }
+  void TearDown() override {
+    mus_client_.reset();
+    MultiUserWindowManagerChromeOSTest::TearDown();
+    aura::test::EnvTestHelper().SetMode(original_aura_env_mode_);
+  }
+
+ protected:
+  std::unique_ptr<views::Widget> CreateMusWidget() {
+    std::unique_ptr<views::Widget> widget = std::make_unique<views::Widget>();
+    views::Widget::InitParams params;
+    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    params.bounds = gfx::Rect(0, 0, 200, 200);
+    params.native_widget =
+        mus_client_->CreateNativeWidget(params, widget.get());
+    widget->Init(params);
+    widget->Show();
+    EXPECT_EQ(aura::Env::Mode::MUS, widget->GetNativeWindow()->env()->mode());
+    // Flush all messages from the WindowTreeClient to ensure ash has finished
+    // Widget creation.
+    aura::test::WaitForAllChangesToComplete();
+    return widget;
+  }
+
+ private:
+  aura::Env::Mode original_aura_env_mode_ = aura::Env::Mode::LOCAL;
+  base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<views::MusClient> mus_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerChromeOSMashTest);
+};
+
+TEST_F(MultiUserWindowManagerChromeOSMashTest,
+       SingleProcessMashWindowOrdering) {
+  SetUpForThisManyWindows(1);
+
+  std::unique_ptr<views::Widget> widget = CreateMusWidget();
+  aura::Window* widget_window_in_ash =
+      FindWindowInAshForClientWindow(widget->GetNativeWindow()->parent());
+  ASSERT_TRUE(widget_window_in_ash);
+  EXPECT_EQ(widget_window_in_ash->parent(), window(0)->parent());
+
+  const AccountId account_id_A(AccountId::FromUserEmail("A"));
+  const AccountId account_id_B(AccountId::FromUserEmail("B"));
+  AddTestUser(account_id_A);
+  AddTestUser(account_id_B);
+  SwitchActiveUser(account_id_A);
+
+  // Set the windows owner.
+  ::wm::ActivationClient* activation_client =
+      ::wm::GetActivationClient(window(0)->GetRootWindow());
+  multi_user_window_manager()->SetWindowOwner(window(0), account_id_A);
+  multi_user_window_manager()->SetWindowOwner(widget->GetNativeWindow(),
+                                              account_id_A);
+  aura::test::WaitForAllChangesToComplete();
+
+  // Activate the windows one by one.
+  activation_client->ActivateWindow(widget_window_in_ash);
+  EXPECT_EQ(activation_client->GetActiveWindow(), widget_window_in_ash);
+  activation_client->ActivateWindow(window(0));
+  EXPECT_EQ(activation_client->GetActiveWindow(), window(0));
+
+  aura::Window::Windows mru_list =
+      Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+  EXPECT_EQ(mru_list[0], window(0));
+  EXPECT_EQ(mru_list[1], widget_window_in_ash);
+
+  SwitchActiveUser(account_id_B);
+  EXPECT_EQ(activation_client->GetActiveWindow(), nullptr);
+
+  SwitchActiveUser(account_id_A);
+  EXPECT_EQ(activation_client->GetActiveWindow(), window(0));
+
+  mru_list = Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+  ASSERT_EQ(2u, mru_list.size());
+  EXPECT_EQ(mru_list[0], window(0));
+  EXPECT_TRUE(widget_window_in_ash->Contains(mru_list[1]));
+
+  // Swap the activation order, and retry.
+  activation_client->ActivateWindow(window(0));
+  EXPECT_EQ(activation_client->GetActiveWindow(), window(0));
+  activation_client->ActivateWindow(widget_window_in_ash);
+  EXPECT_EQ(activation_client->GetActiveWindow(), widget_window_in_ash);
+
+  mru_list = Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+  EXPECT_EQ(mru_list[0], widget_window_in_ash);
+  EXPECT_EQ(mru_list[1], window(0));
+
+  SwitchActiveUser(account_id_B);
+  EXPECT_EQ(activation_client->GetActiveWindow(), nullptr);
+
+  SwitchActiveUser(account_id_A);
+  EXPECT_EQ(activation_client->GetActiveWindow(), widget_window_in_ash);
+
+  mru_list = Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+  EXPECT_EQ(mru_list[0], widget_window_in_ash);
+  EXPECT_EQ(mru_list[1], window(0));
+}
+
+TEST_F(MultiUserWindowManagerChromeOSMashTest, SetWindowOwner) {
+  SetUpForThisManyWindows(1);
+
+  std::unique_ptr<views::Widget> widget = CreateMusWidget();
+  const AccountId account1(AccountId::FromUserEmail("A"));
+  const AccountId account2(AccountId::FromUserEmail("B"));
+  AddTestUser(account1);
+  AddTestUser(account2);
+  SwitchActiveUser(account1);
+
+  // Make both windows be own by |account1|.
+  multi_user_window_manager()->SetWindowOwner(window(0), account1);
+  multi_user_window_manager()->SetWindowOwner(widget->GetNativeWindow(),
+                                              account1);
+  aura::test::WaitForAllChangesToComplete();
+
+  // Switch to account2, which should hide the widget.
+  SwitchActiveUser(account2);
+  EXPECT_FALSE(widget->IsVisible());
+
+  // Show the widget for the current user, which should trigger showing it.
+  multi_user_window_manager()->ShowWindowForUser(widget->GetNativeWindow(),
+                                                 account2);
+  aura::test::WaitForAllChangesToComplete();
+  EXPECT_TRUE(widget->IsVisible());
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 2ac28dc..9ca0cf74 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -470,9 +470,9 @@
   // creation.
   registry->RegisterBooleanPref(prefs::kHasSeenWelcomePage, true);
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
-  // This  will be set to true for newly created profiles, and is used to
-  // indicate which users went through FRE after NUX is enabled.
-  registry->RegisterBooleanPref(prefs::kOnboardDuringNUX, false);
+  // This will be set to a group number for newly created profiles, and is used
+  // to indicate which users went through onboarding with the current group.
+  registry->RegisterIntegerPref(prefs::kNuxOnboardGroup, 0);
 #endif  // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
 }
 
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
new file mode 100644
index 0000000..e2549cd
--- /dev/null
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
@@ -0,0 +1,95 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h"
+
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h"
+#include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_factory.h"
+#include "chrome/browser/ui/toolbar/app_menu_model.h"
+#include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/toolbar/app_menu.h"
+#include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/views/controls/menu/menu_item_view.h"
+
+ReopenTabPromoController::ReopenTabPromoController(BrowserView* browser_view)
+    : iph_service_(ReopenTabInProductHelpFactory::GetForProfile(
+          browser_view->browser()->profile())),
+      browser_view_(browser_view) {
+  // Check that the app menu button exists. It should only not exist when there
+  // is no tab strip, in which case this shouldn't trigger in the first place.
+  BrowserAppMenuButton* app_menu_button =
+      browser_view_->toolbar()->app_menu_button();
+  DCHECK(app_menu_button);
+}
+
+void ReopenTabPromoController::ShowPromo() {
+  // Here, we start the promo display. We highlight the app menu button and open
+  // the promo bubble.
+  BrowserAppMenuButton* app_menu_button =
+      browser_view_->toolbar()->app_menu_button();
+  app_menu_button->AddMenuListener(this);
+  app_menu_button->SetHighlighted(true);
+
+  promo_bubble_ = FeaturePromoBubbleView::CreateOwned(
+      app_menu_button, views::BubbleBorder::Arrow::TOP_RIGHT,
+      IDS_REOPEN_TAB_PROMO, FeaturePromoBubbleView::ActivationAction::ACTIVATE);
+  promo_bubble_->GetWidget()->AddObserver(this);
+}
+
+void ReopenTabPromoController::OnMenuOpened() {
+  // The user followed the promo and opened the menu. Now, we highlight the
+  // history item and observe for the history submenu opening.
+  BrowserAppMenuButton* app_menu_button =
+      browser_view_->toolbar()->app_menu_button();
+  app_menu_button->RemoveMenuListener(this);
+
+  AppMenu* app_menu = app_menu_button->app_menu();
+  app_menu->AddObserver(this);
+
+  views::MenuItemView* recent_tabs_menu_item =
+      app_menu->root_menu_item()->GetMenuItemByID(IDC_RECENT_TABS_MENU);
+  recent_tabs_menu_item->SetForcedVisualSelection(true);
+}
+
+void ReopenTabPromoController::OnWidgetDestroying(views::Widget* widget) {
+  DCHECK(promo_bubble_);
+
+  // If the menu isn't showing, that means the promo bubble timed out. We should
+  // notify our IPH service that help was dismissed.
+  if (!browser_view_->toolbar()->app_menu_button()->IsMenuShowing()) {
+    BrowserAppMenuButton* app_menu_button =
+        browser_view_->toolbar()->app_menu_button();
+    app_menu_button->RemoveMenuListener(this);
+    app_menu_button->SetHighlighted(false);
+
+    iph_service_->HelpDismissed();
+  }
+}
+
+void ReopenTabPromoController::AppMenuClosed() {
+  // The menu was opened then closed, whether by clicking away or by clicking a
+  // menu item. We notify the service regardless of whether IPH succeeded.
+  // Success is determined by whether the reopen tab event was sent.
+  iph_service_->HelpDismissed();
+
+  AppMenu* app_menu = browser_view_->toolbar()->app_menu_button()->app_menu();
+  app_menu->RemoveObserver(this);
+}
+
+void ReopenTabPromoController::OnShowSubmenu() {
+  // Check if the last opened tab menu item exists (it will if the history
+  // submenu was opened). If so, highlight it.
+  views::MenuItemView* root_menu_item =
+      browser_view_->toolbar()->app_menu_button()->app_menu()->root_menu_item();
+  views::MenuItemView* last_tab_menu_item =
+      root_menu_item->GetMenuItemByID(AppMenuModel::kMinRecentTabsCommandId);
+  if (last_tab_menu_item) {
+    // The history submenu was shown. Highlight the last-closed tab item.
+    last_tab_menu_item->SetForcedVisualSelection(true);
+  }
+}
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
new file mode 100644
index 0000000..1b51c2da
--- /dev/null
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_REOPEN_TAB_PROMO_CONTROLLER_H_
+#define CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_REOPEN_TAB_PROMO_CONTROLLER_H_
+
+#include "chrome/browser/ui/views/toolbar/app_menu_observer.h"
+#include "ui/views/controls/menu/menu_listener.h"
+#include "ui/views/widget/widget_observer.h"
+
+class BrowserView;
+class FeaturePromoBubbleView;
+class ReopenTabInProductHelp;
+
+// Handles display of the reopen tab in-product help promo, including showing
+// the promo bubble and highlighting the appropriate app menu items. Notifies
+// the |ReopenTabInProductHelp| service when the promo is finished.
+class ReopenTabPromoController : public AppMenuObserver,
+                                 public views::MenuListener,
+                                 public views::WidgetObserver {
+ public:
+  explicit ReopenTabPromoController(BrowserView* browser_view);
+  ~ReopenTabPromoController() override = default;
+
+  void ShowPromo();
+
+ private:
+  // views::MenuListener:
+  void OnMenuOpened() override;
+
+  // views::WidgetObserver:
+  void OnWidgetDestroying(views::Widget* widget) override;
+
+  // AppMenuObserver:
+  void AppMenuClosed() override;
+  void OnShowSubmenu() override;
+
+  ReopenTabInProductHelp* const iph_service_;
+  BrowserView* const browser_view_;
+  FeaturePromoBubbleView* promo_bubble_ = nullptr;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_REOPEN_TAB_PROMO_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/frame/app_menu_button.h b/chrome/browser/ui/views/frame/app_menu_button.h
index e9036c6..409ce46d 100644
--- a/chrome/browser/ui/views/frame/app_menu_button.h
+++ b/chrome/browser/ui/views/frame/app_menu_button.h
@@ -43,7 +43,7 @@
   // Removes a menu listener.
   void RemoveMenuListener(views::MenuListener* listener);
 
-  AppMenu* app_menu_for_testing() { return menu_.get(); }
+  AppMenu* app_menu() { return menu_.get(); }
 
  protected:
   // Create (but don't show) the menu. |menu_model| should be a newly created
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index e4f7a82..cac0c11 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -778,8 +778,7 @@
   }
 
   AppMenu* GetAppMenu() {
-    return hosted_app_button_container_->app_menu_button_
-        ->app_menu_for_testing();
+    return hosted_app_button_container_->app_menu_button_->app_menu();
   }
 
   SkColor GetActiveColor() {
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 67ef208..b29e298 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -100,6 +100,7 @@
 #include "chrome/browser/ui/views/tabs/tab.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
+#include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
 #include "chrome/browser/ui/views/toolbar/reload_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/browser/ui/views/translate/translate_bubble_view.h"
@@ -194,6 +195,7 @@
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h"
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_factory.h"
+#include "chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h"
 #endif  // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 
 #if BUILDFLAG(ENABLE_ONE_CLICK_SIGNIN)
@@ -2958,9 +2960,11 @@
 void BrowserView::ShowInProductHelpPromo(InProductHelpFeature iph_feature) {
   switch (iph_feature) {
     case InProductHelpFeature::kReopenTab:
-      // TODO(collinbaker): start in-product help flow here.
-      ReopenTabInProductHelpFactory::GetForProfile(browser()->profile())
-          ->HelpDismissed();
+      if (!reopen_tab_promo_controller_) {
+        reopen_tab_promo_controller_ =
+            std::make_unique<ReopenTabPromoController>(this);
+      }
+      reopen_tab_promo_controller_->ShowPromo();
       break;
   }
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index a80f870..f5aa356 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -61,6 +61,7 @@
 class FullscreenControlHost;
 class InfoBarContainerView;
 class LocationBarView;
+class ReopenTabPromoController;
 class StatusBubbleViews;
 class TabStrip;
 class ToolbarButtonProvider;
@@ -806,6 +807,10 @@
 
   std::unique_ptr<FullscreenControlHost> fullscreen_control_host_;
 
+#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
+  std::unique_ptr<ReopenTabPromoController> reopen_tab_promo_controller_;
+#endif
+
   struct ResizeSession {
     // The time when user started resizing the window.
     base::TimeTicks begin_timestamp;
diff --git a/chrome/browser/ui/views/media_router/app_menu_test_api_views.cc b/chrome/browser/ui/views/media_router/app_menu_test_api_views.cc
index 80e5679..04018e5 100644
--- a/chrome/browser/ui/views/media_router/app_menu_test_api_views.cc
+++ b/chrome/browser/ui/views/media_router/app_menu_test_api_views.cc
@@ -57,7 +57,7 @@
 }
 
 AppMenu* AppMenuTestApiViews::GetAppMenu() {
-  return GetAppMenuButton()->app_menu_for_testing();
+  return GetAppMenuButton()->app_menu();
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index ef4d8ea..e83c5a5 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -1025,6 +1025,11 @@
 }
 
 void AppMenu::WillShowMenu(MenuItemView* menu) {
+  if (menu != root_) {
+    for (AppMenuObserver& observer : observer_list_)
+      observer.OnShowSubmenu();
+  }
+
   if (menu == bookmark_menu_)
     CreateBookmarkMenu();
   else if (bookmark_menu_delegate_)
@@ -1051,6 +1056,9 @@
 }
 
 void AppMenu::OnMenuClosed(views::MenuItemView* menu) {
+  for (AppMenuObserver& observer : observer_list_)
+    observer.AppMenuClosed();
+
   if (bookmark_menu_delegate_.get()) {
     BookmarkModel* model =
         BookmarkModelFactory::GetForBrowserContext(browser_->profile());
diff --git a/chrome/browser/ui/views/toolbar/app_menu.h b/chrome/browser/ui/views/toolbar/app_menu.h
index 21538b8437..1e917da2 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.h
+++ b/chrome/browser/ui/views/toolbar/app_menu.h
@@ -57,6 +57,8 @@
 
   bool for_drop() const { return (run_flags_ & FOR_DROP) != 0; }
 
+  views::MenuItemView* root_menu_item() { return root_; }
+
   void AddObserver(AppMenuObserver* observer);
   void RemoveObserver(AppMenuObserver* observer);
 
diff --git a/chrome/browser/ui/views/toolbar/app_menu_observer.h b/chrome/browser/ui/views/toolbar/app_menu_observer.h
index 1809379b..40982e0 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_observer.h
+++ b/chrome/browser/ui/views/toolbar/app_menu_observer.h
@@ -8,7 +8,11 @@
 class AppMenuObserver {
  public:
   // Invoked when the AppMenu is about to be destroyed (from its destructor).
-  virtual void AppMenuDestroyed() = 0;
+  virtual void AppMenuDestroyed() {}
+
+  virtual void AppMenuClosed() {}
+
+  virtual void OnShowSubmenu() {}
 
  protected:
   virtual ~AppMenuObserver() {}
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
index 3497911..5c4d6c7 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
@@ -51,7 +51,7 @@
   // A bunch of plumbing to safely get at the overflowed toolbar action.
   BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser);
   EXPECT_TRUE(app_menu_button->IsMenuShowing());
-  AppMenu* app_menu = app_menu_button->app_menu_for_testing();
+  AppMenu* app_menu = app_menu_button->app_menu();
   ASSERT_TRUE(app_menu);
   ExtensionToolbarMenuView* menu_view =
       app_menu->extension_toolbar_for_testing();
@@ -92,7 +92,7 @@
 
   // Get the overflow container.
   BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser);
-  AppMenu* app_menu = app_menu_button->app_menu_for_testing();
+  AppMenu* app_menu = app_menu_button->app_menu();
   ASSERT_TRUE(app_menu);
   ExtensionToolbarMenuView* menu_view =
       app_menu->extension_toolbar_for_testing();
diff --git a/chrome/browser/ui/webui/chromeos/DEPS b/chrome/browser/ui/webui/chromeos/DEPS
index ef0d6e1..c1fd05f9 100644
--- a/chrome/browser/ui/webui/chromeos/DEPS
+++ b/chrome/browser/ui/webui/chromeos/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+media/audio/sounds",
   "+services/device/public/mojom",
+  "+services/network",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 9911c73f5..8301dcc 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -35,7 +35,6 @@
 #include "chrome/browser/chromeos/net/network_portal_detector_impl.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/device_network_configuration_updater.h"
-#include "chrome/browser/chromeos/policy/temp_certs_cache_nss.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
@@ -71,6 +70,7 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "services/network/nss_temp_certs_cache_chromeos.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/chromeos/input_method_util.h"
@@ -1195,7 +1195,7 @@
     // When the WebUI is destroyed, |untrusted_authority_certs_cache_| will go
     // out of scope and the certificates will not be held in memory anymore.
     untrusted_authority_certs_cache_ =
-        std::make_unique<policy::TempCertsCacheNSS>(
+        std::make_unique<network::NSSTempCertsCacheChromeOS>(
             g_browser_process->platform_part()
                 ->browser_policy_connector_chromeos()
                 ->GetDeviceNetworkConfigurationUpdater()
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index e2bbb7c..75fced3 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -24,8 +24,8 @@
 class CanonicalCookie;
 }
 
-namespace policy {
-class TempCertsCacheNSS;
+namespace network {
+class NSSTempCertsCacheChromeOS;
 }
 
 namespace chromeos {
@@ -302,7 +302,8 @@
 
   // Makes untrusted authority certificates from device policy available for
   // client certificate discovery.
-  std::unique_ptr<policy::TempCertsCacheNSS> untrusted_authority_certs_cache_;
+  std::unique_ptr<network::NSSTempCertsCacheChromeOS>
+      untrusted_authority_certs_cache_;
 
   base::WeakPtrFactory<GaiaScreenHandler> weak_factory_;
 
diff --git a/chrome/browser/ui/webui/welcome/nux_helper.cc b/chrome/browser/ui/webui/welcome/nux_helper.cc
index d2434c4f..0c24ce3e 100644
--- a/chrome/browser/ui/webui/welcome/nux_helper.cc
+++ b/chrome/browser/ui/webui/welcome/nux_helper.cc
@@ -3,7 +3,11 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/webui/welcome/nux_helper.h"
+
+#include <string>
+
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
@@ -30,20 +34,38 @@
 const base::FeatureParam<bool> kNuxOnboardingForceEnabledShowEmailInterstitial =
     {&kNuxOnboardingForceEnabled, "show-email-interstitial", true};
 
+int GetOnboardingGroup() {
+  // Preppend a 0 to avoid issues with empty string.
+  return std::stoi("0" + base::GetFieldTrialParamValue(
+                             /* Must match finch study name */ "NaviOnboarding",
+                             "onboarding-group"));
+}
+
 bool IsNuxOnboardingEnabled(Profile* profile) {
   if (base::FeatureList::IsEnabled(nux::kNuxOnboardingForceEnabled)) {
     return true;
   } else {
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
     // To avoid diluting data collection, existing users should not be assigned
-    // an NUX group. So, the kOnboardDuringNUX flag is used to short-circuit the
-    // feature checks below.
+    // an NUX group. So, the kNuxOnboardGroup integer is used to short-circuit
+    // the feature checks below.
     PrefService* prefs = profile->GetPrefs();
-    bool onboard_during_nux =
-        prefs && prefs->GetBoolean(prefs::kOnboardDuringNUX);
+    if (!prefs)
+      return false;
 
-    return onboard_during_nux &&
-           base::FeatureList::IsEnabled(nux::kNuxOnboardingFeature);
+    int onboard_group = prefs->GetInteger(prefs::kNuxOnboardGroup);
+
+    if (onboard_group == 0)
+      return false;
+
+    int current_group = GetOnboardingGroup();
+    if (onboard_group != current_group) {
+      // Remove user from group if they're not part of the current experiment.
+      prefs->SetInteger(prefs::kNuxOnboardGroup, 0);
+      return false;
+    }
+
+    return base::FeatureList::IsEnabled(nux::kNuxOnboardingFeature);
 #else
     return false;
 #endif  // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
diff --git a/chrome/browser/ui/webui/welcome/nux_helper.h b/chrome/browser/ui/webui/welcome/nux_helper.h
index afd9493..dc60891 100644
--- a/chrome/browser/ui/webui/welcome/nux_helper.h
+++ b/chrome/browser/ui/webui/welcome/nux_helper.h
@@ -25,6 +25,15 @@
 extern const base::FeatureParam<bool>
     kNuxOnboardingForceEnabledShowEmailInterstitial;
 
+// Get the group number for users who onboard in this experiment.
+// Groups are:
+//   - Specified by finch
+//   - The same for all experiments in finch
+//   - Incremented with each new version
+//   - Not reused
+//   - Cleared out when experiment ends
+int GetOnboardingGroup();
+
 bool IsNuxOnboardingEnabled(Profile* profile);
 
 base::DictionaryValue GetNuxOnboardingModules(Profile* profile);
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index ca5b4af..db906c0a 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1283,10 +1283,10 @@
 const char kHasSeenWin10PromoPage[] = "browser.has_seen_win10_promo_page";
 
 #if defined(GOOGLE_CHROME_BUILD)
-// Whether or not this user went through the first-run experience after NUX
-// launched. This is necessary for determining which users to keep "tagging"
-// with the NUX finch experiment group, and allows a more accurate analysis.
-const char kOnboardDuringNUX[] = "browser.onboard_during_nux";
+// Put the user into an onboarding group that's decided when they go through
+// the first run onboarding experience. Only users in a group will have their
+// finch group pinged to keep track of them for the experiment.
+const char kNuxOnboardGroup[] = "browser.onboard_group";
 #endif  // defined(GOOGLE_CHROME_BUILD)
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index fe67794..fc18910 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -440,7 +440,7 @@
 #if defined(OS_WIN)
 extern const char kHasSeenWin10PromoPage[];
 #if defined(GOOGLE_CHROME_BUILD)
-extern const char kOnboardDuringNUX[];
+extern const char kNuxOnboardGroup[];
 #endif  // defined(GOOGLE_CHROME_BUILD)
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/common/thread_profiler.cc b/chrome/common/thread_profiler.cc
index b520412..3dc957a7 100644
--- a/chrome/common/thread_profiler.cc
+++ b/chrome/common/thread_profiler.cc
@@ -66,6 +66,20 @@
   return CallStackProfileParams::UNKNOWN_PROCESS;
 }
 
+std::unique_ptr<base::StackSamplingProfiler::ProfileBuilder>
+CreateProfileBuilder(
+    const CallStackProfileParams& params,
+    base::OnceClosure completed_callback = base::OnceClosure()) {
+  // Enable the new profile builder half the time.
+  if (base::RandInt(0, 99) < 50) {
+    return std::make_unique<CallStackProfileBuilder>(
+        params, std::move(completed_callback));
+  }
+
+  return std::make_unique<LegacyCallStackProfileBuilder>(
+      params, std::move(completed_callback));
+}
+
 }  // namespace
 
 // The scheduler works by splitting execution time into repeated periods such
@@ -193,29 +207,16 @@
 ThreadProfiler::ThreadProfiler(
     CallStackProfileParams::Thread thread,
     scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner)
-    : owning_thread_task_runner_(owning_thread_task_runner),
-      periodic_profile_params_(GetProcess(),
-                               thread,
-                               CallStackProfileParams::PERIODIC_COLLECTION),
+    : thread_(thread),
+      owning_thread_task_runner_(owning_thread_task_runner),
       weak_factory_(this) {
   if (!StackSamplingConfiguration::Get()->IsProfilerEnabledForCurrentProcess())
     return;
 
-  std::unique_ptr<base::StackSamplingProfiler::ProfileBuilder> profile_builder;
-  // Enable the new profile builder 1% of the time.
-  if (base::RandInt(0, 99) == 0) {
-    profile_builder =
-        std::make_unique<CallStackProfileBuilder>(CallStackProfileParams(
-            GetProcess(), thread, CallStackProfileParams::PROCESS_STARTUP));
-  } else {
-    profile_builder =
-        std::make_unique<LegacyCallStackProfileBuilder>(CallStackProfileParams(
-            GetProcess(), thread, CallStackProfileParams::PROCESS_STARTUP));
-  }
-
   startup_profiler_ = std::make_unique<StackSamplingProfiler>(
       base::PlatformThread::CurrentId(), kSamplingParams,
-      std::move(profile_builder));
+      CreateProfileBuilder(CallStackProfileParams(
+          GetProcess(), thread, CallStackProfileParams::PROCESS_STARTUP)));
 
   startup_profiler_->Start();
 
@@ -256,14 +257,14 @@
 void ThreadProfiler::StartPeriodicSamplingCollection() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // NB: Destroys the previous profiler as side effect.
-  auto profile_builder = std::make_unique<LegacyCallStackProfileBuilder>(
-      periodic_profile_params_,
-      base::BindOnce(&ThreadProfiler::OnPeriodicCollectionCompleted,
-                     owning_thread_task_runner_, weak_factory_.GetWeakPtr()));
-
   periodic_profiler_ = std::make_unique<StackSamplingProfiler>(
       base::PlatformThread::CurrentId(), kSamplingParams,
-      std::move(profile_builder));
+      CreateProfileBuilder(
+          CallStackProfileParams(GetProcess(), thread_,
+                                 CallStackProfileParams::PERIODIC_COLLECTION),
+          base::BindOnce(&ThreadProfiler::OnPeriodicCollectionCompleted,
+                         owning_thread_task_runner_,
+                         weak_factory_.GetWeakPtr())));
 
   periodic_profiler_->Start();
 }
diff --git a/chrome/common/thread_profiler.h b/chrome/common/thread_profiler.h
index f1cb7e7..76a687f 100644
--- a/chrome/common/thread_profiler.h
+++ b/chrome/common/thread_profiler.h
@@ -110,9 +110,9 @@
   // Creates a new periodic profiler and initiates a collection with it.
   void StartPeriodicSamplingCollection();
 
-  scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_;
+  metrics::CallStackProfileParams::Thread thread_;
 
-  const metrics::CallStackProfileParams periodic_profile_params_;
+  scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_;
 
   std::unique_ptr<base::StackSamplingProfiler> startup_profiler_;
 
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc
index c3475d20..cd3a4f1 100644
--- a/chrome/renderer/net/net_error_helper.cc
+++ b/chrome/renderer/net/net_error_helper.cc
@@ -118,7 +118,11 @@
 
 #if defined(OS_ANDROID)
 bool IsAutoFetchFeatureEnabled() {
-  return base::FeatureList::IsEnabled(features::kAutoFetchOnNetErrorPage);
+  // This feature is incompatible with OfflineContentOnNetError, so don't allow
+  // both.
+  return GetOfflineContentOnNetErrorFeatureState() ==
+             OfflineContentOnNetErrorFeatureState::kDisabled &&
+         base::FeatureList::IsEnabled(features::kAutoFetchOnNetErrorPage);
 }
 #else   // OS_ANDROID
 bool IsAutoFetchFeatureEnabled() {
@@ -205,6 +209,14 @@
   core_->LaunchDownloadsPage();
 }
 
+void NetErrorHelper::SavePageForLater() {
+  core_->SavePageForLater();
+}
+
+void NetErrorHelper::CancelSavePage() {
+  core_->CancelSavePage();
+}
+
 content::RenderFrame* NetErrorHelper::GetRenderFrame() {
   return render_frame();
 }
diff --git a/chrome/renderer/net/net_error_helper.h b/chrome/renderer/net/net_error_helper.h
index 23177864..186c8cc 100644
--- a/chrome/renderer/net/net_error_helper.h
+++ b/chrome/renderer/net/net_error_helper.h
@@ -68,6 +68,8 @@
   void LaunchOfflineItem(const std::string& id,
                          const std::string& name_space) override;
   void LaunchDownloadsPage() override;
+  void SavePageForLater() override;
+  void CancelSavePage() override;
 
   // SSLCertificateErrorPageController::Delegate implementation
   void SendCommand(
diff --git a/chrome/renderer/net/net_error_helper_core.cc b/chrome/renderer/net/net_error_helper_core.cc
index 750dcd1..49c5ad92 100644
--- a/chrome/renderer/net/net_error_helper_core.cc
+++ b/chrome/renderer/net/net_error_helper_core.cc
@@ -1128,3 +1128,17 @@
   available_content_helper_.LaunchDownloadsPage();
 #endif
 }
+
+void NetErrorHelperCore::SavePageForLater() {
+#if defined(OS_ANDROID)
+  page_auto_fetcher_helper_->TrySchedule(
+      /*user_requested=*/true, base::BindOnce(&Delegate::SetAutoFetchState,
+                                              base::Unretained(delegate_)));
+#endif
+}
+
+void NetErrorHelperCore::CancelSavePage() {
+#if defined(OS_ANDROID)
+  page_auto_fetcher_helper_->CancelSchedule();
+#endif
+}
diff --git a/chrome/renderer/net/net_error_helper_core.h b/chrome/renderer/net/net_error_helper_core.h
index 896a8f4..2f86f16 100644
--- a/chrome/renderer/net/net_error_helper_core.h
+++ b/chrome/renderer/net/net_error_helper_core.h
@@ -248,6 +248,9 @@
   // Shows all available offline content.
   void LaunchDownloadsPage();
 
+  void CancelSavePage();
+  void SavePageForLater();
+
  private:
   struct ErrorPageInfo;
 
diff --git a/chrome/renderer/net/net_error_page_controller.cc b/chrome/renderer/net/net_error_page_controller.cc
index e8bfd97..936809d 100644
--- a/chrome/renderer/net/net_error_page_controller.cc
+++ b/chrome/renderer/net/net_error_page_controller.cc
@@ -102,6 +102,16 @@
     delegate_->LaunchDownloadsPage();
 }
 
+void NetErrorPageController::SavePageForLater() {
+  if (delegate_)
+    delegate_->SavePageForLater();
+}
+
+void NetErrorPageController::CancelSavePage() {
+  if (delegate_)
+    delegate_->CancelSavePage();
+}
+
 NetErrorPageController::NetErrorPageController(base::WeakPtr<Delegate> delegate)
     : delegate_(delegate) {
 }
@@ -129,5 +139,7 @@
       .SetMethod("launchOfflineItem",
                  &NetErrorPageController::LaunchOfflineItem)
       .SetMethod("launchDownloadsPage",
-                 &NetErrorPageController::LaunchDownloadsPage);
+                 &NetErrorPageController::LaunchDownloadsPage)
+      .SetMethod("savePageForLater", &NetErrorPageController::SavePageForLater)
+      .SetMethod("cancelSavePage", &NetErrorPageController::CancelSavePage);
 }
diff --git a/chrome/renderer/net/net_error_page_controller.h b/chrome/renderer/net/net_error_page_controller.h
index 6c024b1..c11af32 100644
--- a/chrome/renderer/net/net_error_page_controller.h
+++ b/chrome/renderer/net/net_error_page_controller.h
@@ -38,6 +38,17 @@
     // Called to show all available offline content.
     virtual void LaunchDownloadsPage() = 0;
 
+    // Schedules a request to save the page later. This is different from the
+    // download button in that the page is only saved temporarily. This is used
+    // only for the auto-fetch-on-net-error-page feature.
+    virtual void SavePageForLater() = 0;
+
+    // Cancels the request to save the page later. This cancels a previous call
+    // to |SavePageForLater|, or the automatic request made when loading the
+    // error page. This is used only for the auto-fetch-on-net-error-page
+    // feature.
+    virtual void CancelSavePage() = 0;
+
    protected:
     Delegate();
     virtual ~Delegate();
@@ -86,6 +97,8 @@
 
   void LaunchOfflineItem(gin::Arguments* args);
   void LaunchDownloadsPage();
+  void SavePageForLater();
+  void CancelSavePage();
 
   // gin::WrappableBase
   gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
index 62ec6602..4ef5ac5 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
@@ -39,7 +39,7 @@
   return url.ReplaceComponents(replacements);
 }
 
-std::set<PhishingClassifierDelegate*> PhishingClassifierDelegates() {
+std::set<PhishingClassifierDelegate*>& PhishingClassifierDelegates() {
   static base::NoDestructor<std::set<PhishingClassifierDelegate*>> s;
   return *s;
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
index 37f354b..6ee8160 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
@@ -536,13 +536,13 @@
     }
 
     /**
-     * @return The number of incognito tabs currently open.
+     * @return The number of tabs currently open.
      */
-    public int incognitoTabsCount() {
+    public int tabsCount(boolean incognito) {
         return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Integer>() {
             @Override
             public Integer call() {
-                return getActivity().getTabModelSelector().getModel(true).getCount();
+                return getActivity().getTabModelSelector().getModel(incognito).getCount();
             }
         });
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
index cc688bc..51f7303a 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.test.util;
 
 import android.app.Instrumentation;
+import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.text.TextUtils;
 import android.view.View;
@@ -45,8 +46,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import javax.annotation.Nullable;
-
 /**
  * A utility class that contains methods generic to all Tabs tests.
  */
diff --git a/chrome/test/data/autofill/dynamic_form_element_invalid_multiple_badname_forms.html b/chrome/test/data/autofill/dynamic_form_element_invalid_multiple_badname_forms.html
new file mode 100644
index 0000000..745a5a1
--- /dev/null
+++ b/chrome/test/data/autofill/dynamic_form_element_invalid_multiple_badname_forms.html
@@ -0,0 +1,66 @@
+<!-- A page that is used to test that a dynamic form fill feature works properly. -->
+<body>
+  <form name="one" action="https://example.com/" method="post">
+    Name: <input type="text" name="firstname" id="firstname_1" autocomplete="given-name"><br>
+   <input type="text" name="firstname" id="firstname_2" autocomplete="given-name" style="display: none"><br>
+    Address: <input type="text" name="address1" id="address1_3"><br>
+    Country: <select name="country" id="country_4" onchange="CountryChanged()">
+      <option value="CA">Canada</option>
+      <option value="US">United States</option>
+    </select>
+    <input type="reset" value="Reset">
+    <input type="submit" value="Submit" id="profile_submit">
+</form>
+<br><br>
+    <form name="one" action="https://example.com/" method="post">
+    Name: <input type="text" name="firstname" id="firstname_5" autocomplete="given-name"><br>
+   <input type="text" name="firstname" id="firstname_6" autocomplete="given-name" style="display: none"><br>
+    Address: <input type="text" name="address1" id="address1_7"><br>
+    Country: <select name="country" id="country_8" onchange="CountryChanged()">
+      <option value="CA">Canada</option>
+      <option value="US">United States</option>
+    </select>
+    <input type="reset" value="Reset">
+    <input type="submit" value="Submit" id="profile_submit">
+  </form>
+</body>
+
+<script>
+
+
+var notify_on_address_input_change = false;
+var address_input_changed = false;
+
+function CountryChanged() {
+  // Reset the value of the address field.
+  var address1 = document.getElementById('address1_7');
+  address1.value = '';
+  address1.onchange = function() {
+    if (notify_on_address_input_change)
+      window.domAutomationController.send(address1.value != '');
+    else
+      address_input_changed = true;
+  }
+
+  // Change the element that triggered the autofill. Remove it, and make another
+  // field visible. This is to test if the autofill can handle the case where
+  // the clicked on element is no longer valid.
+  var first_name_input = document.getElementById("firstname_5");
+  first_name_input.setAttribute("style", "display: none");
+
+  var name_later = document.getElementById("firstname_6");
+  name_later.removeAttribute('style');
+}
+
+
+function hasRefilled() {
+  var address1 = document.getElementById('address1_7');
+  if (address1 && address_input_changed) {
+    window.domAutomationController.send(address1.value != '');
+  } else {
+    notify_on_address_input_change = true;
+  }
+}
+
+</script>
+
diff --git a/chrome/test/data/autofill/dynamic_form_element_invalid_unowned_badnames.html b/chrome/test/data/autofill/dynamic_form_element_invalid_unowned_badnames.html
new file mode 100644
index 0000000..1344ded
--- /dev/null
+++ b/chrome/test/data/autofill/dynamic_form_element_invalid_unowned_badnames.html
@@ -0,0 +1,61 @@
+<!-- A page that is used to test that a dynamic form fill feature works properly. -->
+<body>
+    Name: <input type="text" name="firstname" id="firstname_1" autocomplete="given-name"><br>
+   <input type="text" name="firstname" id="firstname_2" autocomplete="given-name" style="display: none"><br>
+    Address: <input type="text" name="address1" id="address1_3"><br>
+    Country: <select name="country" id="country_4" onchange="CountryChanged()">
+      <option value="CA">Canada</option>
+      <option value="US">United States</option>
+    </select>
+    <input type="reset" value="Reset">
+    <input type="submit" value="Submit" id="profile_submit">
+    Name: <input type="text" name="firstname" id="firstname_5" autocomplete="given-name"><br>
+   <input type="text" name="firstname" id="firstname_6" autocomplete="given-name" style="display: none"><br>
+    Address: <input type="text" name="address1" id="address1_7"><br>
+    Country: <select name="country" id="country_8" onchange="CountryChanged()">
+      <option value="CA">Canada</option>
+      <option value="US">United States</option>
+    </select>
+    <input type="reset" value="Reset">
+    <input type="submit" value="Submit" id="profile_submit">
+</body>
+
+<script>
+
+
+var notify_on_address_input_change = false;
+var address_input_changed = false;
+
+function CountryChanged() {
+  // Reset the value of the address field.
+  var address1 = document.getElementById('address1_7');
+  address1.value = '';
+  address1.onchange = function() {
+    if (notify_on_address_input_change)
+      window.domAutomationController.send(address1.value != '');
+    else
+      address_input_changed = true;
+  }
+
+  // Change the element that triggered the autofill. Remove it, and make another
+  // field visible. This is to test if the autofill can handle the case where
+  // the clicked on element is no longer valid.
+  var first_name_input = document.getElementById("firstname_5");
+  first_name_input.parentNode.removeChild(first_name_input);
+
+  var name_later = document.getElementById("firstname_6");
+  name_later.removeAttribute('style');
+}
+
+
+function hasRefilled() {
+  var address1 = document.getElementById('address1_7');
+  if (address1 && address_input_changed) {
+    window.domAutomationController.send(address1.value != '');
+  } else {
+    notify_on_address_input_change = true;
+  }
+}
+
+</script>
+
diff --git a/chrome_elf/OWNERS b/chrome_elf/OWNERS
index 62497b2..75fbb43 100644
--- a/chrome_elf/OWNERS
+++ b/chrome_elf/OWNERS
@@ -1,5 +1,6 @@
 caitkp@chromium.org
 pennymac@chromium.org
+pmonette@chromium.org
 robertshield@chromium.org
 
 # TEAM: security-dev@chromium.org
diff --git a/chrome_elf/sha1/sha1.cc b/chrome_elf/sha1/sha1.cc
index 63e5f102..3139b86 100644
--- a/chrome_elf/sha1/sha1.cc
+++ b/chrome_elf/sha1/sha1.cc
@@ -205,26 +205,11 @@
 //------------------------------------------------------------------------------
 // Public functions
 //------------------------------------------------------------------------------
-
-int CompareHashes(const uint8_t* first, const uint8_t* second) {
-  // Compare bytes, high-order to low-order.
-  for (size_t i = 0; i < kSHA1Length; ++i) {
-    if (first[i] < second[i])
-      return -1;
-    if (first[i] > second[i])
-      return 1;
-    // else they are equal, continue;
-  }
-
-  return 0;
-}
-
-std::string SHA1HashString(const std::string& str) {
-  char hash[kSHA1Length] = {};
+Digest SHA1HashString(const std::string& str) {
+  Digest digest;
   SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.c_str()),
-                str.length(), reinterpret_cast<unsigned char*>(hash));
-
-  return std::string(hash, kSHA1Length);
+                str.length(), reinterpret_cast<unsigned char*>(&digest[0]));
+  return digest;
 }
 
 }  // namespace elf_sha1
diff --git a/chrome_elf/sha1/sha1.h b/chrome_elf/sha1/sha1.h
index 5d57e470c..fdb2d57 100644
--- a/chrome_elf/sha1/sha1.h
+++ b/chrome_elf/sha1/sha1.h
@@ -11,6 +11,7 @@
 
 #include <stddef.h>
 
+#include <array>
 #include <string>
 
 namespace elf_sha1 {
@@ -18,16 +19,10 @@
 // Length in bytes of a SHA-1 hash.
 constexpr size_t kSHA1Length = 20;
 
-// Compare two hashes.
-// - Returns -1 if |first| < |second|
-// - Returns 0 if |first| == |second|
-// - Returns 1 if |first| > |second|
-// Note: Arguments should be kSHA1Length long.
-int CompareHashes(const uint8_t* first, const uint8_t* second);
+using Digest = std::array<uint8_t, kSHA1Length>;
 
-// Computes the SHA1 hash of the input string |str| and returns the full
-// hash.  The returned SHA1 will be 20 bytes in length.
-std::string SHA1HashString(const std::string& str);
+// Returns the computed SHA1 of the input string |str|.
+Digest SHA1HashString(const std::string& str);
 
 }  // namespace elf_sha1
 
diff --git a/chrome_elf/sha1/sha1_unittest.cc b/chrome_elf/sha1/sha1_unittest.cc
index 1f07ad8..8d45c99 100644
--- a/chrome_elf/sha1/sha1_unittest.cc
+++ b/chrome_elf/sha1/sha1_unittest.cc
@@ -20,12 +20,11 @@
   // Example A.1 from FIPS 180-2: one-block message.
   std::string input = "abc";
 
-  int expected[] = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
-                    0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d};
+  elf_sha1::Digest expected = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81,
+                               0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50,
+                               0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d};
 
-  std::string output = elf_sha1::SHA1HashString(input);
-  for (size_t i = 0; i < elf_sha1::kSHA1Length; i++)
-    EXPECT_EQ(expected[i], output[i] & 0xFF);
+  EXPECT_EQ(elf_sha1::SHA1HashString(input), expected);
 }
 
 TEST(SHA1Test, Test2) {
@@ -33,24 +32,22 @@
   std::string input =
       "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
 
-  int expected[] = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae,
-                    0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1};
+  elf_sha1::Digest expected = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2,
+                               0x6e, 0xba, 0xae, 0x4a, 0xa1, 0xf9, 0x51,
+                               0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1};
 
-  std::string output = elf_sha1::SHA1HashString(input);
-  for (size_t i = 0; i < elf_sha1::kSHA1Length; i++)
-    EXPECT_EQ(expected[i], output[i] & 0xFF);
+  EXPECT_EQ(elf_sha1::SHA1HashString(input), expected);
 }
 
 TEST(SHA1Test, Test3) {
   // Example A.3 from FIPS 180-2: long message.
   std::string input(1000000, 'a');
 
-  int expected[] = {0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e,
-                    0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f};
+  elf_sha1::Digest expected = {0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda,
+                               0xa4, 0xf6, 0x1e, 0xeb, 0x2b, 0xdb, 0xad,
+                               0x27, 0x31, 0x65, 0x34, 0x01, 0x6f};
 
-  std::string output = elf_sha1::SHA1HashString(input);
-  for (size_t i = 0; i < elf_sha1::kSHA1Length; i++)
-    EXPECT_EQ(expected[i], output[i] & 0xFF);
+  EXPECT_EQ(elf_sha1::SHA1HashString(input), expected);
 }
 
 }  // namespace
diff --git a/chrome_elf/third_party_dlls/hook.cc b/chrome_elf/third_party_dlls/hook.cc
index a0f25247..68db6ab2 100644
--- a/chrome_elf/third_party_dlls/hook.cc
+++ b/chrome_elf/third_party_dlls/hook.cc
@@ -294,25 +294,24 @@
   }
 
   // Note that one of either image_name or section_basename can be empty.
-  std::string image_name_hash;
+  elf_sha1::Digest image_name_hash;
   if (!image_name.empty())
     image_name_hash = elf_sha1::SHA1HashString(image_name);
-  std::string section_basename_hash;
+  elf_sha1::Digest section_basename_hash;
   if (!section_basename.empty())
     section_basename_hash = elf_sha1::SHA1HashString(section_basename);
-  std::string fingerprint_hash =
-      GetFingerprintString(time_date_stamp, image_size);
-  fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
+  elf_sha1::Digest fingerprint_hash = elf_sha1::SHA1HashString(
+      GetFingerprintString(time_date_stamp, image_size));
 
   // Check sources for blacklist decision.
   bool block = false;
 
-  if (!image_name_hash.empty() &&
+  if (!image_name.empty() &&
       IsModuleListed(image_name_hash, fingerprint_hash)) {
     // 1) Third-party DLL blacklist, check for image name from PE header.
     block = true;
-  } else if (!section_basename_hash.empty() &&
-             section_basename_hash.compare(image_name_hash) != 0 &&
+  } else if (!section_basename.empty() &&
+             section_basename_hash != image_name_hash &&
              IsModuleListed(section_basename_hash, fingerprint_hash)) {
     // 2) Third-party DLL blacklist, check for image name from the section.
     block = true;
diff --git a/chrome_elf/third_party_dlls/main_unittest.cc b/chrome_elf/third_party_dlls/main_unittest.cc
index cfca6450..dcc9406 100644
--- a/chrome_elf/third_party_dlls/main_unittest.cc
+++ b/chrome_elf/third_party_dlls/main_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <windows.h>
 
+#include <string>
+
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -98,15 +100,11 @@
   // Internally, an empty string should not be passed in here.
   assert(!image_name.empty());
 
-  // SHA1 hash the two strings, and copy them into the new struct.
-  std::string code_id = GetFingerprintString(timedatestamp, imagesize);
-  code_id = elf_sha1::SHA1HashString(code_id);
-  std::string name_hash = elf_sha1::SHA1HashString(image_name);
-
+  // SHA1 hash the two strings into the new struct.
   PackedListModule packed_module;
-  ::memcpy(packed_module.code_id_hash, code_id.data(), elf_sha1::kSHA1Length);
-  ::memcpy(packed_module.basename_hash, name_hash.data(),
-           elf_sha1::kSHA1Length);
+  packed_module.code_id_hash =
+      elf_sha1::SHA1HashString(GetFingerprintString(timedatestamp, imagesize));
+  packed_module.basename_hash = elf_sha1::SHA1HashString(image_name);
 
   return packed_module;
 }
@@ -480,15 +478,14 @@
   // Get hashes from base_sha1.
   const std::string module_basename_hash =
       base::SHA1HashString(base::UTF16ToUTF8(kChineseUnicode));
-  const std::string module_code_id_hash =
-      base::SHA1HashString(base::StringPrintf(
-          "%08lX%lx", module_data.timedatestamp, module_data.imagesize));
+  const std::string module_code_id_hash = base::SHA1HashString(
+      GetFingerprintString(module_data.timedatestamp, module_data.imagesize));
 
   // Compare the hashes.
-  EXPECT_EQ(::memcmp(elf_sha1_generated.basename_hash,
+  EXPECT_EQ(::memcmp(&elf_sha1_generated.basename_hash[0],
                      module_basename_hash.data(), elf_sha1::kSHA1Length),
             0);
-  EXPECT_EQ(::memcmp(elf_sha1_generated.code_id_hash,
+  EXPECT_EQ(::memcmp(&elf_sha1_generated.code_id_hash[0],
                      module_code_id_hash.data(), elf_sha1::kSHA1Length),
             0);
 }
diff --git a/chrome_elf/third_party_dlls/packed_list_file.cc b/chrome_elf/third_party_dlls/packed_list_file.cc
index 8d8d355..55743f1c 100644
--- a/chrome_elf/third_party_dlls/packed_list_file.cc
+++ b/chrome_elf/third_party_dlls/packed_list_file.cc
@@ -42,7 +42,7 @@
 // std::equal_range/std::is_sorted. Must return TRUE if lhs < rhs.
 bool HashBinaryPredicate(const PackedListModule& lhs,
                          const PackedListModule& rhs) {
-  return elf_sha1::CompareHashes(lhs.basename_hash, rhs.basename_hash) < 0;
+  return lhs.basename_hash < rhs.basename_hash;
 }
 
 // Given a file opened for read, pull in the packed list.
@@ -172,19 +172,16 @@
 // Public defines & functions
 //------------------------------------------------------------------------------
 
-bool IsModuleListed(const std::string& basename_hash,
-                    const std::string& fingerprint_hash) {
+bool IsModuleListed(const elf_sha1::Digest& basename_hash,
+                    const elf_sha1::Digest& fingerprint_hash) {
   assert(g_initialized);
-  assert(!basename_hash.empty() && !fingerprint_hash.empty());
-  assert(basename_hash.length() == elf_sha1::kSHA1Length &&
-         fingerprint_hash.length() == elf_sha1::kSHA1Length);
 
   if (!g_bl_module_array_size)
     return false;
 
   PackedListModule target = {};
-  ::memcpy(target.basename_hash, basename_hash.data(), elf_sha1::kSHA1Length);
-  ::memcpy(target.code_id_hash, fingerprint_hash.data(), elf_sha1::kSHA1Length);
+  target.basename_hash = basename_hash;
+  target.code_id_hash = fingerprint_hash;
 
   // Binary search for primary hash (basename).  There can be more than one
   // match.
@@ -194,7 +191,7 @@
 
   // Search for secondary hash.
   for (PackedListModule* i = pair.first; i != pair.second; ++i) {
-    if (!elf_sha1::CompareHashes(target.code_id_hash, i->code_id_hash))
+    if (target.code_id_hash == i->code_id_hash)
       return true;
   }
 
diff --git a/chrome_elf/third_party_dlls/packed_list_file.h b/chrome_elf/third_party_dlls/packed_list_file.h
index 72a6e9e..53393d4 100644
--- a/chrome_elf/third_party_dlls/packed_list_file.h
+++ b/chrome_elf/third_party_dlls/packed_list_file.h
@@ -7,14 +7,15 @@
 
 #include <string>
 
+#include "chrome_elf/sha1/sha1.h"
 #include "chrome_elf/third_party_dlls/status_codes.h"
 
 namespace third_party_dlls {
 
 // Look up a binary based on the required data points.
 // - Returns true if match found in the list.
-bool IsModuleListed(const std::string& basename_hash,
-                    const std::string& fingerprint_hash);
+bool IsModuleListed(const elf_sha1::Digest& basename_hash,
+                    const elf_sha1::Digest& fingerprint_hash);
 
 // Get the full path of the blacklist file used.
 std::wstring GetBlFilePathUsed();
diff --git a/chrome_elf/third_party_dlls/packed_list_file_unittest.cc b/chrome_elf/third_party_dlls/packed_list_file_unittest.cc
index 06fc110..3c6219a0 100644
--- a/chrome_elf/third_party_dlls/packed_list_file_unittest.cc
+++ b/chrome_elf/third_party_dlls/packed_list_file_unittest.cc
@@ -79,8 +79,6 @@
   base::FilePath path;
   char buffer[kPageSize];
   PIMAGE_NT_HEADERS nt_headers = nullptr;
-  std::string code_id;
-  std::string basename_hash;
   test_modules->clear();
   packed_modules->clear();
 
@@ -106,15 +104,11 @@
     test_modules->push_back(test_module);
 
     // SHA1 hash the two strings, and copy them into the module array.
-    code_id = base::StringPrintf("%08lX%lx", test_module.timedatestamp,
-                                 test_module.imagesize);
-    code_id = elf_sha1::SHA1HashString(code_id);
-    basename_hash = elf_sha1::SHA1HashString(test_module.basename);
-
     PackedListModule packed_module;
-    ::memcpy(packed_module.code_id_hash, code_id.data(), elf_sha1::kSHA1Length);
-    ::memcpy(packed_module.basename_hash, basename_hash.data(),
-             elf_sha1::kSHA1Length);
+    packed_module.code_id_hash = elf_sha1::SHA1HashString(
+        GetFingerprintString(test_module.timedatestamp, test_module.imagesize));
+    packed_module.basename_hash =
+        elf_sha1::SHA1HashString(test_module.basename);
     packed_modules->push_back(packed_module);
   }
 
@@ -198,22 +192,18 @@
   // Init.
   ASSERT_EQ(InitFromFile(), ThirdPartyStatus::kSuccess);
 
-  std::string fingerprint_hash;
-  std::string name_hash;
-
   // Test matching.
   for (const auto& test_module : GetTestArray()) {
-    fingerprint_hash =
-        GetFingerprintString(test_module.timedatestamp, test_module.imagesize);
-    fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
-    name_hash = elf_sha1::SHA1HashString(test_module.basename);
+    elf_sha1::Digest name_hash = elf_sha1::SHA1HashString(test_module.basename);
+    elf_sha1::Digest fingerprint_hash = elf_sha1::SHA1HashString(
+        GetFingerprintString(test_module.timedatestamp, test_module.imagesize));
     EXPECT_TRUE(IsModuleListed(name_hash, fingerprint_hash));
   }
 
   // Test a failure to match.
-  fingerprint_hash = GetFingerprintString(0x12345678, 1337);
-  fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
-  name_hash = elf_sha1::SHA1HashString("booya.dll");
+  elf_sha1::Digest name_hash = elf_sha1::SHA1HashString("booya.dll");
+  elf_sha1::Digest fingerprint_hash =
+      elf_sha1::SHA1HashString(GetFingerprintString(0x12345678, 1337));
   EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash));
 }
 
@@ -222,9 +212,9 @@
   // kFileNotFound is a non-fatal status code.
   ASSERT_EQ(InitFromFile(), ThirdPartyStatus::kFileNotFound);
 
-  std::string fingerprint_hash = GetFingerprintString(0x12345678, 1337);
-  fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
-  std::string name_hash = elf_sha1::SHA1HashString("booya.dll");
+  elf_sha1::Digest name_hash = elf_sha1::SHA1HashString("booya.dll");
+  elf_sha1::Digest fingerprint_hash =
+      elf_sha1::SHA1HashString(GetFingerprintString(0x12345678, 1337));
   EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash));
 }
 
diff --git a/chrome_elf/third_party_dlls/packed_list_format.h b/chrome_elf/third_party_dlls/packed_list_format.h
index 470de64..e6e4de1 100644
--- a/chrome_elf/third_party_dlls/packed_list_format.h
+++ b/chrome_elf/third_party_dlls/packed_list_format.h
@@ -10,6 +10,7 @@
 #ifndef CHROME_ELF_THIRD_PARTY_DLLS_PACKED_LIST_FORMAT_H_
 #define CHROME_ELF_THIRD_PARTY_DLLS_PACKED_LIST_FORMAT_H_
 
+#include <array>
 #include <string>
 
 #include <stdint.h>
@@ -53,9 +54,9 @@
 
 struct PackedListModule {
   // SHA1 of lowercase, UTF-8 basename (no path).
-  uint8_t basename_hash[elf_sha1::kSHA1Length];
+  elf_sha1::Digest basename_hash;
   // Code ID. Get the formatted string via GetFingerprintString(), then SHA1.
-  uint8_t code_id_hash[elf_sha1::kSHA1Length];
+  elf_sha1::Digest code_id_hash;
   // A timestamp used for tracking "last attempted load".  Used to manage
   // lifetime of entries in the local caches.
   uint32_t time_date_stamp;
diff --git a/chromeos/test/data/network/README b/chromeos/test/data/network/README
new file mode 100644
index 0000000..c8f5d4a
--- /dev/null
+++ b/chromeos/test/data/network/README
@@ -0,0 +1,23 @@
+This directory contains test data for ChromeOS-specific network configuration
+tests.
+
+==== Certificates ====
+The certificates in here are extracted from /net/data/ssl/certificates using the
+script setup-certificates.sh.
+The script produces:
+(*) root_ca_cert.pem: An Authority certificate that is the root of the hierarchy.
+(*) root-ca-cert.onc: ONC policy for specifying root_ca_cert.pem as an
+    additional trust anchor for Web navigations.
+(*) ok_cert.pem: A Server certificate that is signed by root_ca_cert.pem.
+(*) intermediate_ca_cert.pem: An Authority certificate that is signed by
+    root_ca_cert.pem
+(*) root-and-intermediate-ca-certs.onc: ONC policy for specifying
+    root_ca_cert.pem as an additional trust anchor for Web navigations, and
+    intermediate_ca_cert.pem as an untrusted Authority.
+(*) ok_cert_by_intermediate.pem: A Server certificate that is signed by
+    intermediate_ca_cert.pem.
+
+Run
+The script takes two arguments: Input directory, output directory.
+./setup-certificates.sh ${chromium_dir}/src/net/data/ssl/certificates .
+in this directory to sync the certificates.
diff --git a/chromeos/test/data/network/intermediate_ca_cert.pem b/chromeos/test/data/network/intermediate_ca_cert.pem
new file mode 100644
index 0000000..0a9dae5
--- /dev/null
+++ b/chromeos/test/data/network/intermediate_ca_cert.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDizCCAnOgAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE3MDYwNTE3
+MTA0NVoXDTI3MDYwMzE3MTA0NVowazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
+bGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3Qg
+Q0ExHTAbBgNVBAMMFFRlc3QgSW50ZXJtZWRpYXRlIENBMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAnem95D1KL/vC+eYiKkIVRhyMj0dM6cVXlR9mcJMi
+8JTDu7Vb76RvyMeJlXW6DDa/TmupNUcIQ54pauLD+wO3H7bhUWvtexnH+c473GXp
+ZseDlMTRTu7tZEuB8RrqWmQYG2pOk9ATbJBgytJOtyQW+LIIWJ2NpzNFFTSBrS0t
+nGDv+SuY/nnTjSxI2xKR9C76v/UmwYIFgN1MqHC/p7wQNHc520cED+1EsmVGIiCI
+WSgPxwyitJGloqrKBZ+Km26jy9Sk6CR1nSCBIltfdz7J8R6u64ozjCdbHr5tIRtC
+cpXjnhMDdadY1L5oEv5jjksRejTno2vdc64+GZrskYtzrwIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQXXEXz0KwcEEyLQ0QgxN2TxcUZOzAOBgNV
+HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAD711/2COx40jGai814Y4fGp
+dZ+1k+cKjs8KM+1Jzc7Oyl+jHWR204QW/p2IrPTR+DYrIIBSCu/gD2QnVTWje8aK
+fKXEKRex+tKJXRNruAPL1xLWziTwr0gXA1SZr+nL/UREGCNhkPa9VWUGwzaYjep9
+D3nvtrsiKAOm7NnUG0esLCXe7xoCaUwYuylI9J3hUkJkbIhGaIaD/ST1fQXXGn4i
+vEl1nIpN6POi65uDcKfW3S8FLaq+1+57jnVEO9rfhWG/6TsTQE6LUgjZ5IxU2kHb
+eywFXpkGDQKMjhmOVbA6MxgwA6ftSuT4WMwSuK/WZlUp7AEfku09TcNxO+AvcJA=
+-----END CERTIFICATE-----
diff --git a/chromeos/test/data/network/ok_cert.pem b/chromeos/test/data/network/ok_cert.pem
index 18f94ca..5384510 100644
--- a/chromeos/test/data/network/ok_cert.pem
+++ b/chromeos/test/data/network/ok_cert.pem
@@ -1,21 +1,23 @@
 -----BEGIN CERTIFICATE-----
-MIIDczCCAlugAwIBAgIBAjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxUZXN0
-IFJvb3QgQ0EwHhcNMTQwODE0MDMwNTI5WhcNMjQwODExMDMwNTI5WjBgMQswCQYD
-VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g
-VmlldzEQMA4GA1UECgwHVGVzdCBDQTESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjAN
-BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtfj0Mtj19GXK6dDL3emXoW6Q4vSy
-shbQm+KZV+17xltvScGUAKkNXbU19Dp7PBgGo3haaP+mBR99EAiuCWzc7924l55s
-zsug3DMrHpXvHfvT2vg+V+2Ljp6GTRKmDDAj7whFTyESQoiHAdilMp+3OO9grbdH
-aztLplwrVnJc0bU4h5nsO//GAu+GOO7iBcbwZuIYkVDlVyMnmbvbSSSIZqgUln4a
-bSrh/xj1ajfSiKh5yblQ9ZpoCwSeaAIdoXHgiRW6KkgGenjT0Qx3g5iD+LniYCCS
-B5vUyMD6WlqdJkDCNWUA86Di0yFNpcSRiJAUp173E7fqK6K914QYGrd7XQIDAQAB
-o4GAMH4wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUggQdvQVxg2/2mBlNTxFGiE2b
-v6gwHwYDVR0jBBgwFoAUvPcw0TzA8nn675/JbFyT84poq4MwHQYDVR0lBBYwFAYI
-KwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcNAQEL
-BQADggEBAITcEASNvT/BPvtoSz815F0C63PmDnQW5MUwawWUTpxpEF56r6R2xiin
-GsFcfh1eHF6Hl/5cWyhHMbF5Svg29rFSuNWra4bv7D3tUAtAN2ULIjq3r9QENvDw
-0poWaV2LJQP2BYdeSL0lFcQ7au1j2IdVjj4cRN7rG93Ec8emahJtSNXlEmqoVSYm
-DX68zXGFsYp25FoaxZwmv9deVxT6tlLPhZAK6H9p4bCUG6xkWuk4zFOe/cbU4V6c
-NyIuS9mBX1nhQ6d77acjIP0EkfAdTmzA3quaGStPAKMdWHTJMm7uNbYzTGSNbuyo
-jtczxzPGkorOtfZdjhJS7J0Kz0s73fM=
+MIIDvzCCAqegAwIBAgIBAzANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE3MDYwNTE3
+MTA0NloXDTI3MDYwMzE3MTA0NlowYDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
+bGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3Qg
+Q0ExEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALS/0pcz5RNbd2W9cxp1KJtHWea3MOhGM21YW9ofCv/k5C3yHfiJ6GQu
+9sPN16OO1/fN59gOEMPnVtL85ebTTuL/gk0YY4ewo97a7wo3e6y1t0PO8gc53xTp
+w6RBPn5oRzSbe2HEGOYTzrO0puC6A+7k6+eq9G2+l1uqBpdQAdB4uNaSsOTiuUOI
+ta4UZH1ScNQFHAkl1eJPyaiC20Exw75EbwvU/b/B7tlivzuPtQDI0d9dShOtceRL
+X9HZckyD2JNAv2zNL2YOBNa5QygkySX9WXD+PfKpCk7Cm8TenldeXRYl5ni2REkp
+nfa/dPuF1g3xZVjyK9aPEEnIAC2I4i0CAwEAAaOBgDB+MAwGA1UdEwEB/wQCMAAw
+HQYDVR0OBBYEFODc4C8HiHQ6n9Mwo3GK+dal5aZTMB8GA1UdIwQYMBaAFJsmC4qY
+qbsduR8c4xpAM+2OF4irMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAP
+BgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQB6FEQuUDRcC5jkX3aZ
+uuTeZEqMVL7JXgvgFqzXsPb8zIdmxr/tEDfwXx2qDf2Dpxts7Fq4vqUwimK4qV3K
+7heLnWV2+FBvV1eeSfZ7AQj+SURkdlyo42r41+t13QUf+Z0ftR9266LSWLKrukeI
+Mxk73hOkm/u8enhTd00dy/FN9dOFBFHseVMspWNxIkdRILgOmiyfQNRgxNYdOf0e
+EfELR8Hn6WjZ8wAbvO4p7RTrzu1c/RZ0M+NLkID56Brbl70GC2h5681LPwAOaZ7/
+mWQ5kekSyJjmLfF12b+h9RVAt5MrXZgk2vNujssgGf4nbWh4KZyQ6qrs778ZdDLm
+yfUn
 -----END CERTIFICATE-----
diff --git a/chromeos/test/data/network/ok_cert_by_intermediate.pem b/chromeos/test/data/network/ok_cert_by_intermediate.pem
new file mode 100644
index 0000000..3b00699
--- /dev/null
+++ b/chromeos/test/data/network/ok_cert_by_intermediate.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDxzCCAq+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHVGVzdCBDQTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwHhcN
+MTcwNjA1MTcxMDQ2WhcNMjcwNjAzMTcxMDQ2WjBgMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UE
+CgwHVGVzdCBDQTESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAtL/SlzPlE1t3Zb1zGnUom0dZ5rcw6EYzbVhb2h8K/+Tk
+LfId+InoZC72w83Xo47X983n2A4Qw+dW0vzl5tNO4v+CTRhjh7Cj3trvCjd7rLW3
+Q87yBznfFOnDpEE+fmhHNJt7YcQY5hPOs7Sm4LoD7uTr56r0bb6XW6oGl1AB0Hi4
+1pKw5OK5Q4i1rhRkfVJw1AUcCSXV4k/JqILbQTHDvkRvC9T9v8Hu2WK/O4+1AMjR
+311KE61x5Etf0dlyTIPYk0C/bM0vZg4E1rlDKCTJJf1ZcP498qkKTsKbxN6eV15d
+FiXmeLZESSmd9r90+4XWDfFlWPIr1o8QScgALYjiLQIDAQABo4GAMH4wDAYDVR0T
+AQH/BAIwADAdBgNVHQ4EFgQU4NzgLweIdDqf0zCjcYr51qXlplMwHwYDVR0jBBgw
+FoAUF1xF89CsHBBMi0NEIMTdk8XFGTswHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
+AQUFBwMCMA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcNAQELBQADggEBAEP3Yzij
+MD+r5mxCod3/jfatsuu2nku9kPzY9PBaQvy+W8zHJC+RSKahu6K3GWEhrNOB7m67
+BC0WtX7L5qMXjz5lTR2Nwdo9O62hs+j14xYTFwqjReDPhLI96DhpMUbojBBjD15r
+9BAlI5i4XNkGeIMYcAmm74/p9f+zRIU19mlhCyqN3occ7NxQx2fviCwo3sDbbiEg
+AMVbXe82xgrpBqMlPk0T+vA/vP6caEj/7BvnLDmsiCugiJYRPvAg4in8vdPTyiRj
+WBcVw6wZIcshKfxT5zvPwgpe9M7xnnc41MaQTpT+LpfLGpQqHow72zZlxhm61wsD
+IuWS4vY+35nOa0k=
+-----END CERTIFICATE-----
diff --git a/chromeos/test/data/network/root-and-intermediate-ca-certs.onc b/chromeos/test/data/network/root-and-intermediate-ca-certs.onc
new file mode 100644
index 0000000..ebc5093
--- /dev/null
+++ b/chromeos/test/data/network/root-and-intermediate-ca-certs.onc
@@ -0,0 +1,19 @@
+{
+  "Certificates": [
+    {
+      "GUID": "{b3aae353-cfa9-4093-9aff-9f8ee2bf8c29}",
+      "TrustBits": [
+        "Web"
+      ],
+      "Type": "Authority",
+      "X509": "-----BEGIN CERTIFICATE-----\nMIIDizCCAnOgAwIBAgIJAJRTRMx4iMvZMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW\naWV3MRAwDgYDVQQKDAdUZXN0IENBMRUwEwYDVQQDDAxUZXN0IFJvb3QgQ0EwHhcN\nMTcwNjA1MTcxMDQ0WhcNMjcwNjAzMTcxMDQ0WjBjMQswCQYDVQQGEwJVUzETMBEG\nA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UE\nCgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAxoEfknO2WIXZjay3IP3Hv0Cy6vrlC1IBj5rB63qA\nwfOJpD7VG2HMtc+AsRrbuyXgGL+SaSZQzec//w08tB8UEqtnN94HA2wSdII2rMPU\n02Sfke1b9ql6pJyY6GVslOHLVXOu+B1QsHjldP+xNyzLGT2kjOd2ToZcP9+z7UUj\nT1SbM8aJXhMd3X1ZpQc0KIYnH/qeU08qtkKtNxJi9XI2tgISQET+x56ViUNRXrRu\nx2eAWEO+zAcovVn/HEyNkEL0z/1UAE9IcivhZzyEF2iVv8oHe9+GnVbjMuNwh7f4\nOvfjbmUUfLt2txfxQoxvKjRkEDUUjIX2V7/zXFWdrQMQ8wIDAQABo0IwQDAPBgNV\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSbJguKmKm7HbkfHOMaQDPtjheIqzAOBgNV\nHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFtT/23VCkOlD9R9xl2I45id\nZ+sygrMP9cF4+AVKv7whBe4hCCyyFaG4svajFWHkwa2EpKdADIcJXysb+U1skn3L\nfiuwAQrtQOVOrxrxDewdnpbH1GFkOSP6XynEKjq47YpyUGqsRQR2Cag9V9fwS65G\ntIPBFFAqGVlTsk2u/C9AScitTZ3IIo2MAdsxM1r0vEyb7dfjQ9noHVOLMNiBnnKr\nns649YOT8nLb3s2wUppFTc/nIdjOFmSPQq/Bh6j51eID3bprG3x9oDgzYTm03Vxp\nF3kCOuwdb167E/umgl0HIPyG/m6LrOHCGKL+P5Vm02mKAAYsVjc0ubYx3g/2RDk=\n-----END CERTIFICATE-----\n"
+    },
+    {
+      "GUID": "{ac861420-3342-4537-a20e-3c2ec0809b7a}",
+      "TrustBits": [ ],
+      "Type": "Authority",
+      "X509": "-----BEGIN CERTIFICATE-----\nMIIDizCCAnOgAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G\nA1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE3MDYwNTE3\nMTA0NVoXDTI3MDYwMzE3MTA0NVowazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh\nbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3Qg\nQ0ExHTAbBgNVBAMMFFRlc3QgSW50ZXJtZWRpYXRlIENBMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAnem95D1KL/vC+eYiKkIVRhyMj0dM6cVXlR9mcJMi\n8JTDu7Vb76RvyMeJlXW6DDa/TmupNUcIQ54pauLD+wO3H7bhUWvtexnH+c473GXp\nZseDlMTRTu7tZEuB8RrqWmQYG2pOk9ATbJBgytJOtyQW+LIIWJ2NpzNFFTSBrS0t\nnGDv+SuY/nnTjSxI2xKR9C76v/UmwYIFgN1MqHC/p7wQNHc520cED+1EsmVGIiCI\nWSgPxwyitJGloqrKBZ+Km26jy9Sk6CR1nSCBIltfdz7J8R6u64ozjCdbHr5tIRtC\ncpXjnhMDdadY1L5oEv5jjksRejTno2vdc64+GZrskYtzrwIDAQABo0IwQDAPBgNV\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQXXEXz0KwcEEyLQ0QgxN2TxcUZOzAOBgNV\nHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAD711/2COx40jGai814Y4fGp\ndZ+1k+cKjs8KM+1Jzc7Oyl+jHWR204QW/p2IrPTR+DYrIIBSCu/gD2QnVTWje8aK\nfKXEKRex+tKJXRNruAPL1xLWziTwr0gXA1SZr+nL/UREGCNhkPa9VWUGwzaYjep9\nD3nvtrsiKAOm7NnUG0esLCXe7xoCaUwYuylI9J3hUkJkbIhGaIaD/ST1fQXXGn4i\nvEl1nIpN6POi65uDcKfW3S8FLaq+1+57jnVEO9rfhWG/6TsTQE6LUgjZ5IxU2kHb\neywFXpkGDQKMjhmOVbA6MxgwA6ftSuT4WMwSuK/WZlUp7AEfku09TcNxO+AvcJA=\n-----END CERTIFICATE-----\n"
+    }
+  ],
+  "Type": "UnencryptedConfiguration"
+}
diff --git a/chromeos/test/data/network/root-ca-cert.onc b/chromeos/test/data/network/root-ca-cert.onc
index 9a364ba..7217453 100644
--- a/chromeos/test/data/network/root-ca-cert.onc
+++ b/chromeos/test/data/network/root-ca-cert.onc
@@ -6,7 +6,7 @@
         "Web"
       ],
       "Type": "Authority",
-      "X509": "-----BEGIN CERTIFICATE-----\nMIIC8zCCAdugAwIBAgIJALF9qhLor0+aMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV\nBAMMDFRlc3QgUm9vdCBDQTAeFw0xNDA4MTQwMzA1MjlaFw0yNDA4MTEwMzA1Mjla\nMBcxFTATBgNVBAMMDFRlc3QgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP\nADCCAQoCggEBALZJQeNCAVGofzx6cdP7zZE1F4QajvY2x9FwHfqG8267dm/oMi43\n/TiSPWjkin1CMxRGG9wE9pFuVEDECgn97C1i4l7huiycwbFgTNrH+CJcgiBlQh5W\nd3VP65AsSupXDiKNbJWsEerM1+72cA0J3aY1YV3Jdm2w8h6/MIbYd1I2lZcO0UbF\n7YE9G7DyYZU8wUA4719dumGf7yucn4WJdHBj1XboNX7OAeHzERGQHA31/Y3OEGyt\nfFUaIW/XLfR4FeovOL2RnjwdB0b1Q8GCi68SU2UZimlpZgay2gv6KgChKhWESfEB\nv5swBtAVoB+dUZFH4VNf717swmF5whSfxOMCAwEAAaNCMEAwDwYDVR0TAQH/BAUw\nAwEB/zAdBgNVHQ4EFgQUvPcw0TzA8nn675/JbFyT84poq4MwDgYDVR0PAQH/BAQD\nAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBXByn7f+j/sObYWGrDkKE4HLTzaLHs6Ikj\nJNeo8iHDYOSkSVwAv9/HgniAKxj3rd3QYl6nsMzwqrTOcBJZZWd2BQAYmv/EKhfj\n8VXYvlxe68rLU4cQ1QkyNqdeQfRT2n5WYNJ+TpqlCF9ddennMMsi6e8ZSYOlI6H4\nYEzlNtU5eBjxXr/OqgtTgSx4qQpr2xMQIRR/G3A9iRpAigYsXVAZYvnHRYnyPWYF\nPX11W1UegEJyoZp8bQp09u6mIWw6mPt3gl/ya1bm3ZuOUPDGrv3qpgUHqSYGVrOy\n2bI3oCE+eQYfuVG+9LFJTZC1M+UOx15bQMVqBNFDepRqpE9h/ILg\n-----END CERTIFICATE-----"
+      "X509": "-----BEGIN CERTIFICATE-----\nMIIDizCCAnOgAwIBAgIJAJRTRMx4iMvZMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW\naWV3MRAwDgYDVQQKDAdUZXN0IENBMRUwEwYDVQQDDAxUZXN0IFJvb3QgQ0EwHhcN\nMTcwNjA1MTcxMDQ0WhcNMjcwNjAzMTcxMDQ0WjBjMQswCQYDVQQGEwJVUzETMBEG\nA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UE\nCgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAxoEfknO2WIXZjay3IP3Hv0Cy6vrlC1IBj5rB63qA\nwfOJpD7VG2HMtc+AsRrbuyXgGL+SaSZQzec//w08tB8UEqtnN94HA2wSdII2rMPU\n02Sfke1b9ql6pJyY6GVslOHLVXOu+B1QsHjldP+xNyzLGT2kjOd2ToZcP9+z7UUj\nT1SbM8aJXhMd3X1ZpQc0KIYnH/qeU08qtkKtNxJi9XI2tgISQET+x56ViUNRXrRu\nx2eAWEO+zAcovVn/HEyNkEL0z/1UAE9IcivhZzyEF2iVv8oHe9+GnVbjMuNwh7f4\nOvfjbmUUfLt2txfxQoxvKjRkEDUUjIX2V7/zXFWdrQMQ8wIDAQABo0IwQDAPBgNV\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSbJguKmKm7HbkfHOMaQDPtjheIqzAOBgNV\nHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFtT/23VCkOlD9R9xl2I45id\nZ+sygrMP9cF4+AVKv7whBe4hCCyyFaG4svajFWHkwa2EpKdADIcJXysb+U1skn3L\nfiuwAQrtQOVOrxrxDewdnpbH1GFkOSP6XynEKjq47YpyUGqsRQR2Cag9V9fwS65G\ntIPBFFAqGVlTsk2u/C9AScitTZ3IIo2MAdsxM1r0vEyb7dfjQ9noHVOLMNiBnnKr\nns649YOT8nLb3s2wUppFTc/nIdjOFmSPQq/Bh6j51eID3bprG3x9oDgzYTm03Vxp\nF3kCOuwdb167E/umgl0HIPyG/m6LrOHCGKL+P5Vm02mKAAYsVjc0ubYx3g/2RDk=\n-----END CERTIFICATE-----\n"
     }
   ],
   "Type": "UnencryptedConfiguration"
diff --git a/chromeos/test/data/network/root_ca_cert.pem b/chromeos/test/data/network/root_ca_cert.pem
index 6957b749..dac64712 100644
--- a/chromeos/test/data/network/root_ca_cert.pem
+++ b/chromeos/test/data/network/root_ca_cert.pem
@@ -1,19 +1,21 @@
 -----BEGIN CERTIFICATE-----
-MIIC8zCCAdugAwIBAgIJALF9qhLor0+aMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV
-BAMMDFRlc3QgUm9vdCBDQTAeFw0xNDA4MTQwMzA1MjlaFw0yNDA4MTEwMzA1Mjla
-MBcxFTATBgNVBAMMDFRlc3QgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBALZJQeNCAVGofzx6cdP7zZE1F4QajvY2x9FwHfqG8267dm/oMi43
-/TiSPWjkin1CMxRGG9wE9pFuVEDECgn97C1i4l7huiycwbFgTNrH+CJcgiBlQh5W
-d3VP65AsSupXDiKNbJWsEerM1+72cA0J3aY1YV3Jdm2w8h6/MIbYd1I2lZcO0UbF
-7YE9G7DyYZU8wUA4719dumGf7yucn4WJdHBj1XboNX7OAeHzERGQHA31/Y3OEGyt
-fFUaIW/XLfR4FeovOL2RnjwdB0b1Q8GCi68SU2UZimlpZgay2gv6KgChKhWESfEB
-v5swBtAVoB+dUZFH4VNf717swmF5whSfxOMCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
-AwEB/zAdBgNVHQ4EFgQUvPcw0TzA8nn675/JbFyT84poq4MwDgYDVR0PAQH/BAQD
-AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBXByn7f+j/sObYWGrDkKE4HLTzaLHs6Ikj
-JNeo8iHDYOSkSVwAv9/HgniAKxj3rd3QYl6nsMzwqrTOcBJZZWd2BQAYmv/EKhfj
-8VXYvlxe68rLU4cQ1QkyNqdeQfRT2n5WYNJ+TpqlCF9ddennMMsi6e8ZSYOlI6H4
-YEzlNtU5eBjxXr/OqgtTgSx4qQpr2xMQIRR/G3A9iRpAigYsXVAZYvnHRYnyPWYF
-PX11W1UegEJyoZp8bQp09u6mIWw6mPt3gl/ya1bm3ZuOUPDGrv3qpgUHqSYGVrOy
-2bI3oCE+eQYfuVG+9LFJTZC1M+UOx15bQMVqBNFDepRqpE9h/ILg
+MIIDizCCAnOgAwIBAgIJAJRTRMx4iMvZMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRAwDgYDVQQKDAdUZXN0IENBMRUwEwYDVQQDDAxUZXN0IFJvb3QgQ0EwHhcN
+MTcwNjA1MTcxMDQ0WhcNMjcwNjAzMTcxMDQ0WjBjMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UE
+CgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAxoEfknO2WIXZjay3IP3Hv0Cy6vrlC1IBj5rB63qA
+wfOJpD7VG2HMtc+AsRrbuyXgGL+SaSZQzec//w08tB8UEqtnN94HA2wSdII2rMPU
+02Sfke1b9ql6pJyY6GVslOHLVXOu+B1QsHjldP+xNyzLGT2kjOd2ToZcP9+z7UUj
+T1SbM8aJXhMd3X1ZpQc0KIYnH/qeU08qtkKtNxJi9XI2tgISQET+x56ViUNRXrRu
+x2eAWEO+zAcovVn/HEyNkEL0z/1UAE9IcivhZzyEF2iVv8oHe9+GnVbjMuNwh7f4
+OvfjbmUUfLt2txfxQoxvKjRkEDUUjIX2V7/zXFWdrQMQ8wIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBSbJguKmKm7HbkfHOMaQDPtjheIqzAOBgNV
+HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFtT/23VCkOlD9R9xl2I45id
+Z+sygrMP9cF4+AVKv7whBe4hCCyyFaG4svajFWHkwa2EpKdADIcJXysb+U1skn3L
+fiuwAQrtQOVOrxrxDewdnpbH1GFkOSP6XynEKjq47YpyUGqsRQR2Cag9V9fwS65G
+tIPBFFAqGVlTsk2u/C9AScitTZ3IIo2MAdsxM1r0vEyb7dfjQ9noHVOLMNiBnnKr
+ns649YOT8nLb3s2wUppFTc/nIdjOFmSPQq/Bh6j51eID3bprG3x9oDgzYTm03Vxp
+F3kCOuwdb167E/umgl0HIPyG/m6LrOHCGKL+P5Vm02mKAAYsVjc0ubYx3g/2RDk=
 -----END CERTIFICATE-----
-
diff --git a/chromeos/test/data/network/setup-certificates.sh b/chromeos/test/data/network/setup-certificates.sh
new file mode 100755
index 0000000..f849732a
--- /dev/null
+++ b/chromeos/test/data/network/setup-certificates.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+INPUT_DIR=${1?first param missing - input directory}
+OUTPUT_DIR=${2?second param missing - output directory}
+
+# This script grabs certain certificates from ${INPUT_DIR} and places them under
+# ${OUTPUT_DIR}. It uses openssl's x509 command to only take the certificate
+# sections (and not e.g. private keys).
+# Additionally, this script creates ONC files which contain some of the
+# certificates to be used by tests.
+
+openssl x509 -in "${INPUT_DIR}/root_ca_cert.pem" -inform PEM \
+  > "${OUTPUT_DIR}/root_ca_cert.pem"
+openssl x509 -in "${INPUT_DIR}/ok_cert.pem" -inform PEM \
+  > "${OUTPUT_DIR}/ok_cert.pem"
+openssl x509 -in "${INPUT_DIR}/intermediate_ca_cert.pem" -inform PEM \
+  > "${OUTPUT_DIR}/intermediate_ca_cert.pem"
+openssl x509 -in "${INPUT_DIR}/ok_cert_by_intermediate.pem" -inform PEM \
+  > "${OUTPUT_DIR}/ok_cert_by_intermediate.pem"
+
+# Read the root CA cert and interemdiate CA cert PEM files and replace newlines
+# with \n literals. This is needed because the ONC JSON does not support
+# multi-line strings. Note that replacement is done in two steps, using ',' as
+# intermediate character. PEM files will not contain commas.
+ROOT_CA_CERT_CONTENTS=$(cat root_ca_cert.pem \
+  | tr '\n' ',' | sed 's/,/\\n/g')
+INTERMEDIATE_CA_CERT_CONTENTS=$(cat intermediate_ca_cert.pem \
+  | tr '\n' ',' | sed 's/,/\\n/g')
+
+cat > "${OUTPUT_DIR}/root-ca-cert.onc" << EOL
+{
+  "Certificates": [
+    {
+      "GUID": "{b3aae353-cfa9-4093-9aff-9f8ee2bf8c29}",
+      "TrustBits": [
+        "Web"
+      ],
+      "Type": "Authority",
+      "X509": "${ROOT_CA_CERT_CONTENTS}"
+    }
+  ],
+  "Type": "UnencryptedConfiguration"
+}
+EOL
+
+cat > "${OUTPUT_DIR}/root-and-intermediate-ca-certs.onc" << EOL
+{
+  "Certificates": [
+    {
+      "GUID": "{b3aae353-cfa9-4093-9aff-9f8ee2bf8c29}",
+      "TrustBits": [
+        "Web"
+      ],
+      "Type": "Authority",
+      "X509": "${ROOT_CA_CERT_CONTENTS}"
+    },
+    {
+      "GUID": "{ac861420-3342-4537-a20e-3c2ec0809b7a}",
+      "TrustBits": [ ],
+      "Type": "Authority",
+      "X509": "${INTERMEDIATE_CA_CERT_CONTENTS}"
+    }
+  ],
+  "Type": "UnencryptedConfiguration"
+}
+EOL
diff --git a/components/browsing_data/core/BUILD.gn b/components/browsing_data/core/BUILD.gn
index 0d22030..35db894c 100644
--- a/components/browsing_data/core/BUILD.gn
+++ b/components/browsing_data/core/BUILD.gn
@@ -23,8 +23,6 @@
     "counters/passwords_counter.h",
     "counters/sync_tracker.cc",
     "counters/sync_tracker.h",
-    "features.cc",
-    "features.h",
     "history_notice_utils.cc",
     "history_notice_utils.h",
     "pref_names.cc",
diff --git a/components/browsing_data/core/features.cc b/components/browsing_data/core/features.cc
deleted file mode 100644
index 054b27e..0000000
--- a/components/browsing_data/core/features.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/browsing_data/core/features.h"
-
-namespace browsing_data {
-namespace features {
-
-const base::Feature kRemoveNavigationHistory{"RemoveNavigationHistory",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
-
-}  // namespace features
-}  // namespace browsing_data
diff --git a/components/browsing_data/core/features.h b/components/browsing_data/core/features.h
deleted file mode 100644
index 5ec27f3..0000000
--- a/components/browsing_data/core/features.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_BROWSING_DATA_FEATURES_H_
-#define COMPONENTS_BROWSING_DATA_FEATURES_H_
-
-#include "base/feature_list.h"
-
-namespace browsing_data {
-namespace features {
-
-// Enable propagation of history deletions to navigation history.
-extern const base::Feature kRemoveNavigationHistory;
-
-}  // namespace features
-}  // namespace browsing_data
-
-#endif  // COMPONENTS_BROWSING_DATA_FEATURES_H_
diff --git a/components/error_page/common/localized_error.cc b/components/error_page/common/localized_error.cc
index 8253826..8c60334 100644
--- a/components/error_page/common/localized_error.cc
+++ b/components/error_page/common/localized_error.cc
@@ -1071,23 +1071,28 @@
       !is_incognito && failed_url.is_valid() &&
       failed_url.SchemeIsHTTPOrHTTPS() &&
       IsOfflineError(error_domain, error_code)) {
-    error_strings->SetPath(
-        {"downloadButton", "msg"},
-        base::Value(l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_DOWNLOAD)));
-    error_strings->SetPath({"downloadButton", "disabledMsg"},
-                           base::Value(l10n_util::GetStringUTF16(
-                               IDS_ERRORPAGES_BUTTON_DOWNLOADING)));
+    if (!auto_fetch_feature_enabled) {
+      error_strings->SetPath({"downloadButton", "msg"},
+                             base::Value(l10n_util::GetStringUTF16(
+                                 IDS_ERRORPAGES_BUTTON_DOWNLOAD)));
+      error_strings->SetPath({"downloadButton", "disabledMsg"},
+                             base::Value(l10n_util::GetStringUTF16(
+                                 IDS_ERRORPAGES_BUTTON_DOWNLOADING)));
+    } else {
+      error_strings->SetString("attemptAutoFetch", "true");
+      error_strings->SetPath({"savePageLater", "savePageMsg"},
+                             base::Value(l10n_util::GetStringUTF16(
+                                 IDS_ERRORPAGES_SAVE_PAGE_BUTTON)));
+      error_strings->SetPath({"savePageLater", "cancelMsg"},
+                             base::Value(l10n_util::GetStringUTF16(
+                                 IDS_ERRORPAGES_CANCEL_SAVE_PAGE_BUTTON)));
+    }
   }
 
   error_strings->SetString(
       "closeDescriptionPopup",
       l10n_util::GetStringUTF16(IDS_ERRORPAGES_SUGGESTION_CLOSE_POPUP_BUTTON));
 
-  if (auto_fetch_feature_enabled && IsOfflineError(error_domain, error_code) &&
-      !is_incognito) {
-    error_strings->SetString("attemptAutoFetch", "true");
-  }
-
   if (IsOfflineError(error_domain, error_code) && !is_incognito) {
     switch (offline_content_feature_state) {
       case OfflineContentOnNetErrorFeatureState::kDisabled:
diff --git a/components/error_page_strings.grdp b/components/error_page_strings.grdp
index e83eb86..4e347059 100644
--- a/components/error_page_strings.grdp
+++ b/components/error_page_strings.grdp
@@ -48,6 +48,12 @@
     <message name="IDS_ERRORPAGES_OFFLINE_CONTENT_SUMMARY_BUTTON" desc="Label for the button that will take the user to the downloads home, attached to the offline content summary card shown in an error page">
       Take me there now
     </message>
+    <message name="IDS_ERRORPAGES_SAVE_PAGE_BUTTON" desc="Label for the button that will save the page for later. The saved page is not retained in Downloads, unlike with the DOWNLOAD button.">
+      Save page for later
+    </message>
+    <message name="IDS_ERRORPAGES_CANCEL_SAVE_PAGE_BUTTON" desc="Label for the button that will cancel the request to save the page for later. 'Cancel' is colored blue with the 'a' tag.">
+      Chrome will let you know when this page is ready. &lt;a&gt;Cancel&lt;/a&gt;
+    </message>
   </if>
 
   <message name="IDS_ERRORPAGES_SUGGESTION_VISIT_GOOGLE_CACHE" desc="When a page fails to load, sometimes we viewing a version of the page cached on Google's servers.">
diff --git a/components/exo/fullscreen_shell_surface.cc b/components/exo/fullscreen_shell_surface.cc
index 5859522..31ee9a6 100644
--- a/components/exo/fullscreen_shell_surface.cc
+++ b/components/exo/fullscreen_shell_surface.cc
@@ -5,7 +5,7 @@
 #include "components/exo/fullscreen_shell_surface.h"
 
 #include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
 #include "ui/aura/window.h"
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index 2629757c..d31d603 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -63,6 +63,8 @@
     "zcr_stylus_tools.h",
     "zwp_input_timestamps_manager.cc",
     "zwp_input_timestamps_manager.h",
+    "zwp_pointer_gestures.cc",
+    "zwp_pointer_gestures.h",
     "zwp_text_input_manager.cc",
     "zwp_text_input_manager.h",
     "zxdg_shell.cc",
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 93ee212..9f2988e4 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -109,6 +109,7 @@
 #include "components/exo/wayland/zcr_remote_shell.h"
 #include "components/exo/wayland/zcr_stylus_tools.h"
 #include "components/exo/wayland/zwp_input_timestamps_manager.h"
+#include "components/exo/wayland/zwp_pointer_gestures.h"
 #include "components/exo/wayland/zwp_text_input_manager.h"
 #include "components/exo/wayland/zxdg_shell.h"
 #include "components/exo/wm_helper.h"
@@ -2207,95 +2208,6 @@
                                  nullptr);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// pointer_gesture_swipe_v1 interface:
-
-void pointer_gestures_get_swipe_gesture(wl_client* client,
-                                        wl_resource* resource,
-                                        uint32_t id,
-                                        wl_resource* pointer_resource) {
-  NOTIMPLEMENTED();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// pointer_gesture_pinch_v1 interface:
-
-class WaylandPointerGesturePinchDelegate : public PointerGesturePinchDelegate {
- public:
-  WaylandPointerGesturePinchDelegate(wl_resource* resource, Pointer* pointer)
-      : resource_(resource), pointer_(pointer) {
-    pointer_->SetGesturePinchDelegate(this);
-  }
-
-  ~WaylandPointerGesturePinchDelegate() override {
-    if (pointer_)
-      pointer_->SetGesturePinchDelegate(nullptr);
-  }
-  void OnPointerDestroying(Pointer* pointer) override { pointer_ = nullptr; }
-  void OnPointerPinchBegin(uint32_t unique_touch_event_id,
-                           base::TimeTicks time_stamp,
-                           Surface* surface) override {
-    wl_resource* surface_resource = GetSurfaceResource(surface);
-    DCHECK(surface_resource);
-    zwp_pointer_gesture_pinch_v1_send_begin(resource_, unique_touch_event_id,
-                                            TimeTicksToMilliseconds(time_stamp),
-                                            surface_resource, 2);
-  }
-  void OnPointerPinchUpdate(base::TimeTicks time_stamp, float scale) override {
-    zwp_pointer_gesture_pinch_v1_send_update(
-        resource_, TimeTicksToMilliseconds(time_stamp), 0, 0,
-        wl_fixed_from_double(scale), 0);
-  }
-  void OnPointerPinchEnd(uint32_t unique_touch_event_id,
-                         base::TimeTicks time_stamp) override {
-    zwp_pointer_gesture_pinch_v1_send_end(resource_, unique_touch_event_id,
-                                          TimeTicksToMilliseconds(time_stamp),
-                                          0);
-  }
-
- private:
-  wl_resource* const resource_;
-  Pointer* pointer_;
-
-  DISALLOW_COPY_AND_ASSIGN(WaylandPointerGesturePinchDelegate);
-};
-
-void pointer_gesture_pinch_destroy(wl_client* client, wl_resource* resource) {
-  wl_resource_destroy(resource);
-}
-
-const struct zwp_pointer_gesture_pinch_v1_interface
-    pointer_gesture_pinch_implementation = {pointer_gesture_pinch_destroy};
-
-void pointer_gestures_get_pinch_gesture(wl_client* client,
-                                        wl_resource* resource,
-                                        uint32_t id,
-                                        wl_resource* pointer_resource) {
-  Pointer* pointer = GetUserDataAs<Pointer>(pointer_resource);
-  wl_resource* pointer_gesture_pinch_resource = wl_resource_create(
-      client, &zwp_pointer_gesture_pinch_v1_interface, 1, id);
-  SetImplementation(pointer_gesture_pinch_resource,
-                    &pointer_gesture_pinch_implementation,
-                    std::make_unique<WaylandPointerGesturePinchDelegate>(
-                        pointer_gesture_pinch_resource, pointer));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// pointer_gestures_v1 interface:
-
-const struct zwp_pointer_gestures_v1_interface pointer_gestures_implementation =
-    {pointer_gestures_get_swipe_gesture, pointer_gestures_get_pinch_gesture};
-
-void bind_pointer_gestures(wl_client* client,
-                           void* data,
-                           uint32_t version,
-                           uint32_t id) {
-  wl_resource* resource = wl_resource_create(
-      client, &zwp_pointer_gestures_v1_interface, version, id);
-  wl_resource_set_implementation(resource, &pointer_gestures_implementation,
-                                 data, nullptr);
-}
-
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/components/exo/wayland/zcr_remote_shell.cc b/components/exo/wayland/zcr_remote_shell.cc
index bd7076e..2b33ca0 100644
--- a/components/exo/wayland/zcr_remote_shell.cc
+++ b/components/exo/wayland/zcr_remote_shell.cc
@@ -664,7 +664,7 @@
 
     for (const auto& display : screen->GetAllDisplays()) {
       const gfx::Rect& bounds = display.bounds();
-      const gfx::Insets& insets = display.GetWorkAreaInsets();
+      const gfx::Insets& insets = GetAdjustedInsets(display);
 
       double device_scale_factor = display.device_scale_factor();
 
diff --git a/components/exo/wayland/zwp_pointer_gestures.cc b/components/exo/wayland/zwp_pointer_gestures.cc
new file mode 100644
index 0000000..4548a13
--- /dev/null
+++ b/components/exo/wayland/zwp_pointer_gestures.cc
@@ -0,0 +1,113 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/wayland/zwp_pointer_gestures.h"
+
+#include <pointer-gestures-unstable-v1-server-protocol.h>
+#include <wayland-server-core.h>
+#include <wayland-server-protocol-core.h>
+
+#include "components/exo/pointer.h"
+#include "components/exo/pointer_gesture_pinch_delegate.h"
+#include "components/exo/surface.h"
+#include "components/exo/wayland/server_util.h"
+
+namespace exo {
+namespace wayland {
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+// pointer_gesture_swipe_v1 interface:
+
+void pointer_gestures_get_swipe_gesture(wl_client* client,
+                                        wl_resource* resource,
+                                        uint32_t id,
+                                        wl_resource* pointer_resource) {
+  NOTIMPLEMENTED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// pointer_gesture_pinch_v1 interface:
+
+class WaylandPointerGesturePinchDelegate : public PointerGesturePinchDelegate {
+ public:
+  WaylandPointerGesturePinchDelegate(wl_resource* resource, Pointer* pointer)
+      : resource_(resource), pointer_(pointer) {
+    pointer_->SetGesturePinchDelegate(this);
+  }
+
+  ~WaylandPointerGesturePinchDelegate() override {
+    if (pointer_)
+      pointer_->SetGesturePinchDelegate(nullptr);
+  }
+  void OnPointerDestroying(Pointer* pointer) override { pointer_ = nullptr; }
+  void OnPointerPinchBegin(uint32_t unique_touch_event_id,
+                           base::TimeTicks time_stamp,
+                           Surface* surface) override {
+    wl_resource* surface_resource = GetSurfaceResource(surface);
+    DCHECK(surface_resource);
+    zwp_pointer_gesture_pinch_v1_send_begin(resource_, unique_touch_event_id,
+                                            TimeTicksToMilliseconds(time_stamp),
+                                            surface_resource, 2);
+  }
+  void OnPointerPinchUpdate(base::TimeTicks time_stamp, float scale) override {
+    zwp_pointer_gesture_pinch_v1_send_update(
+        resource_, TimeTicksToMilliseconds(time_stamp), 0, 0,
+        wl_fixed_from_double(scale), 0);
+  }
+  void OnPointerPinchEnd(uint32_t unique_touch_event_id,
+                         base::TimeTicks time_stamp) override {
+    zwp_pointer_gesture_pinch_v1_send_end(resource_, unique_touch_event_id,
+                                          TimeTicksToMilliseconds(time_stamp),
+                                          0);
+  }
+
+ private:
+  wl_resource* const resource_;
+  Pointer* pointer_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandPointerGesturePinchDelegate);
+};
+
+void pointer_gesture_pinch_destroy(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+const struct zwp_pointer_gesture_pinch_v1_interface
+    pointer_gesture_pinch_implementation = {pointer_gesture_pinch_destroy};
+
+void pointer_gestures_get_pinch_gesture(wl_client* client,
+                                        wl_resource* resource,
+                                        uint32_t id,
+                                        wl_resource* pointer_resource) {
+  Pointer* pointer = GetUserDataAs<Pointer>(pointer_resource);
+  wl_resource* pointer_gesture_pinch_resource = wl_resource_create(
+      client, &zwp_pointer_gesture_pinch_v1_interface, 1, id);
+  SetImplementation(pointer_gesture_pinch_resource,
+                    &pointer_gesture_pinch_implementation,
+                    std::make_unique<WaylandPointerGesturePinchDelegate>(
+                        pointer_gesture_pinch_resource, pointer));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// pointer_gestures_v1 interface:
+
+const struct zwp_pointer_gestures_v1_interface pointer_gestures_implementation =
+    {pointer_gestures_get_swipe_gesture, pointer_gestures_get_pinch_gesture};
+
+}  // namespace
+
+void bind_pointer_gestures(wl_client* client,
+                           void* data,
+                           uint32_t version,
+                           uint32_t id) {
+  wl_resource* resource = wl_resource_create(
+      client, &zwp_pointer_gestures_v1_interface, version, id);
+  wl_resource_set_implementation(resource, &pointer_gestures_implementation,
+                                 data, nullptr);
+}
+
+}  // namespace wayland
+}  // namespace exo
diff --git a/components/exo/wayland/zwp_pointer_gestures.h b/components/exo/wayland/zwp_pointer_gestures.h
new file mode 100644
index 0000000..f593fc1
--- /dev/null
+++ b/components/exo/wayland/zwp_pointer_gestures.h
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_WAYLAND_ZWP_POINTER_GESTURES_H_
+#define COMPONENTS_EXO_WAYLAND_ZWP_POINTER_GESTURES_H_
+
+#include <stdint.h>
+
+struct wl_client;
+
+namespace exo {
+namespace wayland {
+
+void bind_pointer_gestures(wl_client* client,
+                           void* data,
+                           uint32_t version,
+                           uint32_t id);
+
+}  // namespace wayland
+}  // namespace exo
+
+#endif  // COMPONENTS_EXO_WAYLAND_ZWP_POINTER_GESTURES_H_
diff --git a/components/gwp_asan/common/guarded_page_allocator.cc b/components/gwp_asan/common/guarded_page_allocator.cc
index 379ea59..030142b3 100644
--- a/components/gwp_asan/common/guarded_page_allocator.cc
+++ b/components/gwp_asan/common/guarded_page_allocator.cc
@@ -39,16 +39,13 @@
         (num_pages_ == kGpaMaxPages) ? ~0ULL : (1ULL << num_pages_) - 1;
   }
 
-  for (size_t i = 0; i < num_pages_; i++)
-    data_[i].Init();
+  AllocateStackTraces();
 }
 
 GuardedPageAllocator::~GuardedPageAllocator() {
   if (num_pages_) {
     UnmapPages();
-
-    for (size_t i = 0; i < num_pages_; i++)
-      data_[i].Destroy();
+    DeallocateStackTraces();
   }
 }
 
@@ -83,7 +80,7 @@
   void* alloc = reinterpret_cast<void*>(free_page + offset);
 
   // Initialize slot metadata.
-  data_[free_slot].RecordAllocation(size, alloc);
+  RecordAllocationInSlot(free_slot, size, alloc);
 
   return alloc;
 }
@@ -104,7 +101,7 @@
   }
 
   // Record deallocation stack trace/thread id.
-  data_[slot].RecordDeallocation();
+  RecordDeallocationInSlot(slot);
 
   FreeSlot(slot);
 }
@@ -218,56 +215,60 @@
   return slot;
 }
 
-void GuardedPageAllocator::SlotMetadata::Init() {
+void GuardedPageAllocator::AllocateStackTraces() {
   // new is not used so that we can explicitly call the constructor when we
   // want to collect a stack trace.
-  alloc.stacktrace =
-      static_cast<StackTrace*>(malloc(sizeof(*alloc.stacktrace)));
-  CHECK(alloc.stacktrace);
-  dealloc.stacktrace =
-      static_cast<StackTrace*>(malloc(sizeof(*dealloc.stacktrace)));
-  CHECK(dealloc.stacktrace);
+  for (size_t i = 0; i < num_pages_; i++) {
+    alloc_traces[i] =
+        static_cast<StackTrace*>(malloc(sizeof(*alloc_traces[i])));
+    CHECK(alloc_traces[i]);
+    dealloc_traces[i] =
+        static_cast<StackTrace*>(malloc(sizeof(*dealloc_traces[i])));
+    CHECK(dealloc_traces[i]);
+  }
 }
 
-void GuardedPageAllocator::SlotMetadata::Destroy() {
-  CHECK(alloc.stacktrace);
-  CHECK(dealloc.stacktrace);
+void GuardedPageAllocator::DeallocateStackTraces() {
+  for (size_t i = 0; i < num_pages_; i++) {
+    DestructStackTrace(i);
 
-  Reset();
-
-  free(alloc.stacktrace);
-  free(dealloc.stacktrace);
+    free(alloc_traces[i]);
+    alloc_traces[i] = nullptr;
+    free(dealloc_traces[i]);
+    dealloc_traces[i] = nullptr;
+  }
 }
 
-void GuardedPageAllocator::SlotMetadata::Reset() {
+void GuardedPageAllocator::DestructStackTrace(size_t slot) {
   // Destruct previous allocation/deallocation traces. The constructor was only
   // called if trace_addr is non-null.
-  if (alloc.trace_addr)
-    alloc.stacktrace->~StackTrace();
-  if (dealloc.trace_addr)
-    dealloc.stacktrace->~StackTrace();
+  if (data_[slot].alloc.trace_addr)
+    alloc_traces[slot]->~StackTrace();
+  if (data_[slot].dealloc.trace_addr)
+    dealloc_traces[slot]->~StackTrace();
 }
 
-void GuardedPageAllocator::SlotMetadata::RecordAllocation(size_t size,
-                                                          void* ptr) {
-  Reset();
+void GuardedPageAllocator::RecordAllocationInSlot(size_t slot,
+                                                  size_t size,
+                                                  void* ptr) {
+  data_[slot].alloc_size = size;
+  data_[slot].alloc_ptr = reinterpret_cast<uintptr_t>(ptr);
 
-  alloc_size = size;
-  alloc_ptr = reinterpret_cast<uintptr_t>(ptr);
+  data_[slot].alloc.tid = base::PlatformThread::CurrentId();
+  new (alloc_traces[slot]) StackTrace();
+  data_[slot].alloc.trace_addr = reinterpret_cast<uintptr_t>(
+      alloc_traces[slot]->Addresses(&data_[slot].alloc.trace_len));
 
-  alloc.tid = base::PlatformThread::CurrentId();
-  new (alloc.stacktrace) StackTrace();
-  alloc.trace_addr = alloc.stacktrace->Addresses(&alloc.trace_len);
-
-  dealloc.tid = base::kInvalidThreadId;
-  dealloc.trace_addr = nullptr;
-  dealloc.trace_len = 0;
+  data_[slot].dealloc.tid = base::kInvalidThreadId;
+  data_[slot].dealloc.trace_addr = 0;
+  data_[slot].dealloc.trace_len = 0;
 }
 
-void GuardedPageAllocator::SlotMetadata::RecordDeallocation() {
-  dealloc.tid = base::PlatformThread::CurrentId();
-  new (dealloc.stacktrace) StackTrace();
-  dealloc.trace_addr = dealloc.stacktrace->Addresses(&dealloc.trace_len);
+void GuardedPageAllocator::RecordDeallocationInSlot(size_t slot) {
+  data_[slot].dealloc.tid = base::PlatformThread::CurrentId();
+  new (dealloc_traces[slot]) StackTrace();
+  data_[slot].dealloc.trace_addr = reinterpret_cast<uintptr_t>(
+      dealloc_traces[slot]->Addresses(&data_[slot].dealloc.trace_len));
 }
 
 }  // namespace internal
diff --git a/components/gwp_asan/common/guarded_page_allocator.h b/components/gwp_asan/common/guarded_page_allocator.h
index acb9699..155f892 100644
--- a/components/gwp_asan/common/guarded_page_allocator.h
+++ b/components/gwp_asan/common/guarded_page_allocator.h
@@ -82,36 +82,12 @@
       // (De)allocation thread id or base::kInvalidThreadId if no (de)allocation
       // occurred.
       base::PlatformThreadId tid = base::kInvalidThreadId;
-
-      // Pointer to stack trace addresses or null if no (de)allocation occurred.
-      const void* const* trace_addr = nullptr;
-
+      // Address of stack trace addresses or null if no (de)allocation occurred.
+      uintptr_t trace_addr = 0;
       // Stack trace length or 0 if no (de)allocation occurred.
       size_t trace_len = 0;
-
-     private:
-      // StackTrace object for this slot, it's allocated in
-      // SlotMetadata()::Init() and only used internally, trace_addr/len should
-      // be used by external consumers of the stack trace data.
-      base::debug::StackTrace* stacktrace = nullptr;
-
-      friend struct SlotMetadata;
     };
 
-    // Allocate internal data (StackTraces) for this slot. StackTrace objects
-    // are large so we only allocate them if they're required (instead of
-    // having them be statically allocated in the SlotMetadata itself.)
-    void Init();
-
-    // Frees internal data. May only be called after Init().
-    void Destroy();
-
-    // Update slot metadata on an allocation with the given size and pointer.
-    void RecordAllocation(size_t size, void* ptr);
-
-    // Update slot metadata on a deallocation.
-    void RecordDeallocation();
-
     // Size of the allocation
     size_t alloc_size = 0;
     // The allocation address.
@@ -119,11 +95,6 @@
 
     AllocationInfo alloc;
     AllocationInfo dealloc;
-
-   private:
-    // Call destructors on (de)alloc.stacktrace if constructors for them have
-    // previously been called.
-    void Reset();
   };
 
   // Does not allocate any memory for the allocator, to finish initializing call
@@ -169,6 +140,23 @@
   uintptr_t SlotToAddr(size_t slot) const;
   size_t AddrToSlot(uintptr_t addr) const;
 
+  // Allocate num_pages_ stack traces.
+  void AllocateStackTraces();
+
+  // Deallocate stack traces. May only be called after AllocateStackTraces().
+  void DeallocateStackTraces();
+
+  // Call the destructor on the allocation and deallocation stack traces for
+  // a given slot index if the constructors for those stack traces have been
+  // called.
+  void DestructStackTrace(size_t slot);
+
+  // Record an allocation or deallocation for a given slot index. This
+  // encapsulates the logic for updating the stack traces and metadata for a
+  // given slot.
+  void RecordAllocationInSlot(size_t slot, size_t size, void* ptr);
+  void RecordDeallocationInSlot(size_t slot);
+
   // Allocator lock that protects free_pages_.
   base::Lock lock_;
 
@@ -189,6 +177,12 @@
   // Set to true if a double free has occurred.
   std::atomic<bool> double_free_detected_{false};
 
+  // StackTrace objects for every slot in AllocateStateBase::data_. We avoid
+  // statically allocating the StackTrace objects because they are large and
+  // the allocator may be initialized with num_pages_ < kGpaMaxPages.
+  base::debug::StackTrace* alloc_traces[kGpaMaxPages];
+  base::debug::StackTrace* dealloc_traces[kGpaMaxPages];
+
   // Required for a singleton to access the constructor.
   friend base::NoDestructor<GuardedPageAllocator>;
 
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index 53317fc6..2f64965 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -431,6 +431,10 @@
     sources -= [ "child_call_stack_profile_collector_unittest.cc" ]
   }
 
+  if (is_ios) {
+    deps += [ "//ios/web/public/test" ]
+  }
+
   if (!is_ios) {
     deps += [ "//content/test:test_support" ]
   }
diff --git a/components/metrics/net/DEPS b/components/metrics/net/DEPS
index 58106e5..79a65616 100644
--- a/components/metrics/net/DEPS
+++ b/components/metrics/net/DEPS
@@ -5,6 +5,7 @@
   "+components/encrypted_messages",
   "+components/variations",
   "+content/public/test/test_browser_thread_bundle.h",
+  "+ios/web/public/test/test_web_thread_bundle.h",
   "+net",
   "+services/network/public/cpp",
   "+services/network/test",
diff --git a/components/metrics/net/network_metrics_provider_unittest.cc b/components/metrics/net/network_metrics_provider_unittest.cc
index 835dfa61..ef2e586 100644
--- a/components/metrics/net/network_metrics_provider_unittest.cc
+++ b/components/metrics/net/network_metrics_provider_unittest.cc
@@ -22,9 +22,11 @@
 #endif  // OS_CHROMEOS
 
 #if defined(OS_IOS)
-#include "base/test/scoped_task_environment.h"
+#include "ios/web/public/test/test_web_thread_bundle.h"
+using TestThreadBundle = web::TestWebThreadBundle;
 #else  // !defined(OS_IOS)
 #include "content/public/test/test_browser_thread_bundle.h"
+using TestThreadBundle = content::TestBrowserThreadBundle;
 #endif  // defined(OS_IOS)
 
 namespace metrics {
@@ -33,14 +35,7 @@
  public:
  protected:
   NetworkMetricsProviderTest()
-#if defined(OS_IOS)
-      : scoped_task_environment_(
-            base::test::ScopedTaskEnvironment::MainThreadType::IO)
-#else   // !defined(OS_IOS)
-      : test_browser_thread_bundle_(
-            content::TestBrowserThreadBundle::IO_MAINLOOP)
-#endif  // defined(OS_IOS)
-  {
+      : test_thread_bundle_(TestThreadBundle::IO_MAINLOOP) {
 #if defined(OS_CHROMEOS)
     chromeos::DBusThreadManager::Initialize();
     chromeos::NetworkHandler::Initialize();
@@ -48,11 +43,7 @@
   }
 
  private:
-#if defined(OS_IOS)
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-#else   // !defined(OS_IOS)
-  content::TestBrowserThreadBundle test_browser_thread_bundle_;
-#endif  // defined(OS_IOS)
+  TestThreadBundle test_thread_bundle_;
 };
 
 // Verifies that the effective connection type is correctly set.
diff --git a/components/neterror/resources/neterror.css b/components/neterror/resources/neterror.css
index b0cfdda..c325a21 100644
--- a/components/neterror/resources/neterror.css
+++ b/components/neterror/resources/neterror.css
@@ -584,6 +584,23 @@
   padding-top: 12px;
 }
 
+#cancel-save-page-button {
+  border: 1px solid var(--google-gray-300);
+  border-radius: 5px;
+  color: var(--google-gray-700);
+  padding: 16px;
+  text-align: center;
+}
+
+#save-page-for-later-button {
+  display: flex;
+  justify-content: center;
+}
+
+.hidden#save-page-for-later-button {
+  display: none;
+}
+
 /* Don't allow overflow when in a subframe. */
 html[subframe] body {
   overflow: hidden;
diff --git a/components/neterror/resources/neterror.html b/components/neterror/resources/neterror.html
index 25c98f9..019a2892 100644
--- a/components/neterror/resources/neterror.html
+++ b/components/neterror/resources/neterror.html
@@ -117,6 +117,17 @@
             jsselect="downloadButton"
             jscontent="msg" jsvalues=".disabledText:disabledMsg">
         </button>
+        <div id="save-page-for-later-button" class="hidden">
+          <a class="link-button" onclick="savePageLaterClick()"
+            jsselect="savePageLater"
+            jscontent="savePageMsg">
+          </a>
+        </div>
+        <div id="cancel-save-page-button" class="hidden"
+          onclick="cancelSavePageClick()"
+          jsselect="savePageLater"
+          jsvalues=".innerHTML:cancelMsg">
+        </div>
       </div>
       <button id="details-button" class="secondary-button text-button small-link"
          onclick="detailsButtonClick(); toggleHelpBox()" jscontent="details"
diff --git a/components/neterror/resources/neterror.js b/components/neterror/resources/neterror.js
index df76f57..e495d10 100644
--- a/components/neterror/resources/neterror.js
+++ b/components/neterror/resources/neterror.js
@@ -159,8 +159,24 @@
 primaryControlOnLeft = false;
 // </if>
 
-// TODO(crbug.com/883486): UI not yet implemented.
 function setAutoFetchState(scheduled, can_schedule) {
+  document.getElementById('cancel-save-page-button')
+      .classList.toggle(HIDDEN_CLASS, !scheduled);
+  document.getElementById('save-page-for-later-button')
+      .classList.toggle(HIDDEN_CLASS, scheduled || !can_schedule);
+}
+
+function savePageLaterClick() {
+  errorPageController.savePageForLater();
+  // savePageForLater will eventually trigger a call to setAutoFetchState() when
+  // it completes.
+}
+
+function cancelSavePageClick() {
+  errorPageController.cancelSavePage();
+  // setAutoFetchState is not called in response to cancelSavePage(), so do it
+  // now.
+  setAutoFetchState(false, true);
 }
 
 function toggleErrorInformationPopup() {
@@ -347,9 +363,12 @@
     detailsButton.classList.add('singular');
   }
 
+  var attemptAutoFetch = loadTimeData.valueExists('attemptAutoFetch') &&
+      loadTimeData.getValue('attemptAutoFetch');
+
   // Show control buttons.
   if (reloadButtonVisible || showSavedCopyButtonVisible ||
-      downloadButtonVisible) {
+      downloadButtonVisible || attemptAutoFetch) {
     controlButtonDiv.hidden = false;
 
     // Set the secondary button state in the cases of two call to actions.
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index 49afff9..73bf1f4 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -280,7 +280,7 @@
         metrics_util::PASSWORD_USED);
   }
 
-  // TODO(https://crbug.com/831123): Implement updating Password Form Managers.
+  client_->UpdateFormManagers();
 }
 
 void NewPasswordFormManager::Update(const PasswordForm& credentials_to_update) {
@@ -308,6 +308,8 @@
       FindOtherCredentialsToUpdate();
   form_saver_->Update(pending_credentials_, best_matches_,
                       &more_credentials_to_update, nullptr);
+
+  client_->UpdateFormManagers();
 }
 
 void NewPasswordFormManager::UpdateUsername(
diff --git a/components/password_manager/core/browser/new_password_form_manager_unittest.cc b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
index d767bf94..82bb64d6 100644
--- a/components/password_manager/core/browser/new_password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
@@ -56,6 +56,14 @@
   MOCK_METHOD1(AllowPasswordGenerationForForm, void(const PasswordForm&));
 };
 
+class MockPasswordManagerClient : public StubPasswordManagerClient {
+ public:
+  MockPasswordManagerClient() = default;
+  ~MockPasswordManagerClient() override = default;
+
+  MOCK_METHOD0(UpdateFormManagers, void());
+};
+
 void CheckPendingCredentials(const PasswordForm& expected,
                              const PasswordForm& actual) {
   EXPECT_EQ(expected.signon_realm, actual.signon_realm);
@@ -279,7 +287,7 @@
   PasswordForm blacklisted_match_;
   PasswordForm parsed_observed_form_;
   PasswordForm parsed_submitted_form_;
-  StubPasswordManagerClient client_;
+  MockPasswordManagerClient client_;
   MockPasswordManagerDriver driver_;
   scoped_refptr<TestMockTimeTaskRunner> task_runner_;
   // Define |fetcher_| before |form_manager_|, because the former needs to
@@ -715,6 +723,7 @@
   std::map<base::string16, const PasswordForm*> best_matches;
   EXPECT_CALL(form_saver, Save(_, _))
       .WillOnce(DoAll(SaveArg<0>(&saved_form), SaveArg<1>(&best_matches)));
+  EXPECT_CALL(client_, UpdateFormManagers());
 
   form_manager_->Save();
 
@@ -1364,6 +1373,7 @@
   EXPECT_CALL(form_saver, Update(_, _, _, nullptr))
       .WillOnce(DoAll(SaveArg<0>(&updated_form), SaveArg<1>(&best_matches),
                       SaveArgPointee<2>(&credentials_to_update)));
+  EXPECT_CALL(client_, UpdateFormManagers());
 
   form_manager_->Update(saved_match_);
 
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 208087a..6f838eb 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -563,13 +563,11 @@
 
 void PasswordManager::UpdateFormManagers() {
   std::vector<PasswordFormManagerInterface*> form_managers;
-  if (base::FeatureList::IsEnabled(features::kNewPasswordFormParsing)) {
-    for (const auto& form_manager : form_managers_)
-      form_managers.push_back(form_manager.get());
-  } else {
-    for (const auto& form_manager : pending_login_managers_)
-      form_managers.push_back(form_manager.get());
-  }
+  for (const auto& form_manager : form_managers_)
+    form_managers.push_back(form_manager.get());
+
+  for (const auto& form_manager : pending_login_managers_)
+    form_managers.push_back(form_manager.get());
 
   // Get the fetchers and all the drivers.
   std::vector<FormFetcher*> fetchers;
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index dcdbec8..1e1f8133 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -1979,25 +1979,30 @@
 }
 
 TEST_F(PasswordManagerTest, UpdateFormManagers) {
-  // Seeing some forms should result in creating PasswordFormManagers and
-  // querying PasswordStore. Calling UpdateFormManagers should result in
-  // querying the store again.
-  EXPECT_CALL(*store_, GetLogins(_, _));
+  for (bool new_parsing_for_saving : {false, true}) {
+    SCOPED_TRACE(testing::Message()
+                 << "new_parsing_for_saving = " << new_parsing_for_saving);
+    base::test::ScopedFeatureList scoped_feature_list;
+    if (new_parsing_for_saving)
+      TurnOnNewParsingForSaving(&scoped_feature_list);
 
-  PasswordForm form;
-  std::vector<PasswordForm> observed;
-  observed.push_back(form);
-  manager()->OnPasswordFormsParsed(&driver_, observed);
-  ASSERT_EQ(1u, manager()->pending_login_managers().size());
-  PasswordFormManager* form_manager =
-      manager()->pending_login_managers().front().get();
+    // Seeing a form should result in creating PasswordFormManager and
+    // NewPasswordFormManager and querying PasswordStore. Calling
+    // UpdateFormManagers should result in querying the store again.
+    EXPECT_CALL(*store_, GetLogins(_, _))
+        .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
 
-  // The first GetLogins should have fired, but to unblock the second, we need
-  // to first send a response from the store (to be ignored).
-  static_cast<FormFetcherImpl*>(form_manager->GetFormFetcher())
-      ->OnGetPasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>>());
-  EXPECT_CALL(*store_, GetLogins(_, _));
-  manager()->UpdateFormManagers();
+    manager()->OnPasswordFormsParsed(&driver_, {PasswordForm()});
+
+    // When new parsing is on, both PasswordFormManager and
+    // NewPasswordFormManager query the password store.
+    size_t expected_calls_to_store = new_parsing_for_saving ? 2 : 1;
+
+    EXPECT_CALL(*store_, GetLogins(_, _)).Times(expected_calls_to_store);
+    manager()->UpdateFormManagers();
+
+    testing::Mock::VerifyAndClearExpectations(&store_);
+  }
 }
 
 TEST_F(PasswordManagerTest, DropFormManagers) {
diff --git a/components/rlz/rlz_tracker.cc b/components/rlz/rlz_tracker.cc
index 2b35d730..948a7798 100644
--- a/components/rlz/rlz_tracker.cc
+++ b/components/rlz/rlz_tracker.cc
@@ -303,7 +303,12 @@
       delegate_->IsBrandOrganic(reactivation_brand_)) {
     SYSLOG(INFO) << "RLZ is disabled";
   } else {
-    rlz_lib::UpdateExistingAccessPointRlz(brand_);
+    background_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(
+                       [](const std::string& brand) {
+                         rlz_lib::UpdateExistingAccessPointRlz(brand);
+                       },
+                       brand_));
   }
 #endif
 
diff --git a/components/security_interstitials/core/common/resources/interstitial_core.css b/components/security_interstitials/core/common/resources/interstitial_core.css
index 7137e0b3ad..8aa42e09a 100644
--- a/components/security_interstitials/core/common/resources/interstitial_core.css
+++ b/components/security_interstitials/core/common/resources/interstitial_core.css
@@ -10,6 +10,7 @@
   --google-blue-600: rgb(26, 115, 232);
   --google-blue-700: rgb(25, 103, 210);
   --google-gray-50: rgb(248, 249, 250);
+  --google-gray-300: rgb(218, 220, 224);
   --google-gray-500: rgb(154, 160, 166);
   --google-gray-600: rgb(128, 134, 139);
   --google-gray-700: rgb(95, 99, 105);
@@ -39,4 +40,4 @@
 .icon {
   background-repeat: no-repeat;
   background-size: 100%;
-}
\ No newline at end of file
+}
diff --git a/components/sync/engine_impl/model_type_worker_unittest.cc b/components/sync/engine_impl/model_type_worker_unittest.cc
index 8d4a582..a5fd1ee 100644
--- a/components/sync/engine_impl/model_type_worker_unittest.cc
+++ b/components/sync/engine_impl/model_type_worker_unittest.cc
@@ -1180,6 +1180,34 @@
   EXPECT_FALSE(update2.encryption_key_name.empty());
 }
 
+// Test the receipt of decryptable entities, and that the worker will keep the
+// entities until the decryption key arrives.
+TEST_F(ModelTypeWorkerTest,
+       ReceiveDecryptableEntitiesShouldWaitTillKeyArrives) {
+  NormalInitialize();
+
+  // This next update will be encrypted using the second key.
+  SetUpdateEncryptionFilter(2);
+  TriggerUpdateFromServer(10, kTag1, kValue1);
+
+  // Worker cannot decrypt it.
+  EXPECT_FALSE(processor()->HasUpdateResponse(kHash1));
+
+  // Allow the cryptographer to decrypt using the first key.
+  AddPendingKey();
+  DecryptPendingKey();
+
+  // Worker still cannot decrypt it.
+  EXPECT_FALSE(processor()->HasUpdateResponse(kHash1));
+
+  // Allow the cryptographer to decrypt using the second key.
+  AddPendingKey();
+  DecryptPendingKey();
+
+  // The worker can now decrypt the update and forward it to the processor.
+  EXPECT_TRUE(processor()->HasUpdateResponse(kHash1));
+}
+
 // Test initializing a CommitQueue with a cryptographer at startup.
 TEST_F(ModelTypeWorkerTest, InitializeWithCryptographer) {
   // Set up some encryption state.
@@ -1717,6 +1745,43 @@
       update.entity->specifics.password().has_client_only_encrypted_data());
 }
 
+// Similar to ReceiveDecryptableEntities but for PASSWORDS, which have a custom
+// encryption mechanism.
+TEST_F(ModelTypeWorkerPasswordsTest,
+       ReceiveDecryptablePasswordShouldWaitTillKeyArrives) {
+  NormalInitialize();
+
+  // Receive an encrypted password, encrypted with the second ecnryption key.
+  sync_pb::PasswordSpecificsData unencrypted_password;
+  unencrypted_password.set_password_value(kPassword);
+  sync_pb::EntitySpecifics encrypted_specifics =
+      EncryptPasswordSpecifics(GetNthKeyParams(2), unencrypted_password);
+
+  SyncEntity entity = server()->UpdateFromServer(
+      /*version_offset=*/10, kHash1, encrypted_specifics);
+  worker()->ProcessGetUpdatesResponse(server()->GetProgress(),
+                                      server()->GetContext(), {&entity},
+                                      status_controller());
+  worker()->ApplyUpdates(status_controller());
+
+  // Worker cannot decrypt it.
+  EXPECT_FALSE(processor()->HasUpdateResponse(kHash1));
+
+  // Allow the cryptographer to decrypt using the first key.
+  AddPendingKey();
+  DecryptPendingKey();
+
+  // Worker still cannot decrypt it.
+  EXPECT_FALSE(processor()->HasUpdateResponse(kHash1));
+
+  // Allow the cryptographer to decrypt using the second key.
+  AddPendingKey();
+  DecryptPendingKey();
+
+  // The worker can now decrypt the update and forward it to the processor.
+  EXPECT_TRUE(processor()->HasUpdateResponse(kHash1));
+}
+
 // Analogous to ReceiveUndecryptableEntries but for PASSWORDS, which have a
 // custom encryption mechanism.
 TEST_F(ModelTypeWorkerPasswordsTest, ReceiveUndecryptablePasswordEntries) {
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc
index 2270616..8867847 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -1406,22 +1406,29 @@
   std::string type_string = ModelTypeToString(type_);
 
   while (batch->HasNext()) {
-    KeyAndData data = batch->Next();
-    std::unique_ptr<base::DictionaryValue> node =
-        data.second->ToDictionaryValue();
-    ProcessorEntityTracker* entity = GetEntityForStorageKey(data.first);
-    // Entity could be null if there are some unapplied changes.
+    KeyAndData key_and_data = batch->Next();
+    std::unique_ptr<EntityData> data = std::move(key_and_data.second);
+
+    // There is an overlap between EntityData fields from the bridge and
+    // EntityMetadata fields from the processor's entity tracker, metadata is
+    // the authoritative source of truth.
+    ProcessorEntityTracker* entity = GetEntityForStorageKey(key_and_data.first);
+    // Tracker could be null if there are some unapplied changes.
     if (entity != nullptr) {
-      std::unique_ptr<base::DictionaryValue> metadata =
-          EntityMetadataToValue(entity->metadata());
-      base::Value* server_id = metadata->FindKey("server_id");
-      if (server_id) {
-        // Set ID value as directory, "s" means server.
-        node->SetString("ID", "s" + server_id->GetString());
-      }
-      node->Set("metadata", std::move(metadata));
+      const sync_pb::EntityMetadata& metadata = entity->metadata();
+      // Set id value as directory, "s" means server.
+      data->id = "s" + metadata.server_id();
+      data->creation_time = ProtoTimeToTime(metadata.creation_time());
+      data->modification_time = ProtoTimeToTime(metadata.modification_time());
+      data->client_tag_hash = metadata.client_tag_hash();
     }
+
+    std::unique_ptr<base::DictionaryValue> node = data->ToDictionaryValue();
     node->SetString("modelType", type_string);
+    // Copy the whole metadata message into the dictionary (if existing).
+    if (entity != nullptr) {
+      node->Set("metadata", EntityMetadataToValue(entity->metadata()));
+    }
     all_nodes->Append(std::move(node));
   }
 
diff --git a/components/viz/common/surfaces/parent_local_surface_id_allocator.cc b/components/viz/common/surfaces/parent_local_surface_id_allocator.cc
index 02deaf492..f9bf6e38 100644
--- a/components/viz/common/surfaces/parent_local_surface_id_allocator.cc
+++ b/components/viz/common/surfaces/parent_local_surface_id_allocator.cc
@@ -124,7 +124,7 @@
 }
 
 bool ParentLocalSurfaceIdAllocator::HasValidLocalSurfaceIdAllocation() const {
-  return current_local_surface_id_allocation_.IsValid();
+  return !is_invalid_ && current_local_surface_id_allocation_.IsValid();
 }
 
 // static
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index cb384e2..a5bee4e 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -541,7 +541,7 @@
   PrepareCanvasForDrawQuads(quad->shared_quad_state, draw_region, scissor_rect,
                             &auto_canvas_restore);
 
-  current_paint_.reset();
+  SkPaint paint;
   if (settings_->force_antialiasing ||
       !IsScaleAndIntegerTranslate(current_canvas_->getTotalMatrix())) {
     // TODO(danakj): Until we can enable AA only on exterior edges of the
@@ -551,30 +551,29 @@
         quad->IsRightEdge();
     if (settings_->allow_antialiasing &&
         (settings_->force_antialiasing || all_four_edges_are_exterior))
-      current_paint_.setAntiAlias(true);
-    current_paint_.setFilterQuality(kLow_SkFilterQuality);
+      paint.setAntiAlias(true);
+    paint.setFilterQuality(kLow_SkFilterQuality);
   }
 
-  current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
-  current_paint_.setBlendMode(
+  paint.setAlpha(quad->shared_quad_state->opacity * 255);
+  paint.setBlendMode(
       static_cast<SkBlendMode>(quad->shared_quad_state->blend_mode));
 
-
   switch (quad->material) {
     case DrawQuad::DEBUG_BORDER:
-      DrawDebugBorderQuad(DebugBorderDrawQuad::MaterialCast(quad));
+      DrawDebugBorderQuad(DebugBorderDrawQuad::MaterialCast(quad), &paint);
       break;
     case DrawQuad::PICTURE_CONTENT:
-      DrawPictureQuad(PictureDrawQuad::MaterialCast(quad));
+      DrawPictureQuad(PictureDrawQuad::MaterialCast(quad), &paint);
       break;
     case DrawQuad::RENDER_PASS:
-      DrawRenderPassQuad(RenderPassDrawQuad::MaterialCast(quad));
+      DrawRenderPassQuad(RenderPassDrawQuad::MaterialCast(quad), &paint);
       break;
     case DrawQuad::SOLID_COLOR:
-      DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad));
+      DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), &paint);
       break;
     case DrawQuad::TEXTURE_CONTENT:
-      DrawTextureQuad(TextureDrawQuad::MaterialCast(quad));
+      DrawTextureQuad(TextureDrawQuad::MaterialCast(quad), &paint);
       break;
     case DrawQuad::TILED_CONTENT:
       NOTREACHED();
@@ -585,11 +584,11 @@
       NOTREACHED();
       break;
     case DrawQuad::YUV_VIDEO_CONTENT:
-      DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad));
+      DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad), &paint);
       break;
     case DrawQuad::INVALID:
     case DrawQuad::STREAM_VIDEO_CONTENT:
-      DrawUnsupportedQuad(quad);
+      DrawUnsupportedQuad(quad, &paint);
       NOTREACHED();
       break;
   }
@@ -655,7 +654,9 @@
   }
 }
 
-void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
+void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad,
+                                       SkPaint* paint) {
+  DCHECK(paint);
   // We need to apply the matrix manually to have pixel-sized stroke width.
   SkPoint vertices[4];
   gfx::RectToSkRect(quad->rect).toQuad(vertices);
@@ -664,16 +665,17 @@
                                               4);
   current_canvas_->resetMatrix();
 
-  current_paint_.setColor(quad->color);
-  current_paint_.setAlpha(quad->shared_quad_state->opacity *
-                          SkColorGetA(quad->color));
-  current_paint_.setStyle(SkPaint::kStroke_Style);
-  current_paint_.setStrokeWidth(quad->width);
+  paint->setColor(quad->color);
+  paint->setAlpha(quad->shared_quad_state->opacity * SkColorGetA(quad->color));
+  paint->setStyle(SkPaint::kStroke_Style);
+  paint->setStrokeWidth(quad->width);
   current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode, 4,
-                              transformed_vertices, current_paint_);
+                              transformed_vertices, *paint);
 }
 
-void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
+void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad,
+                                   SkPaint* paint) {
+  DCHECK(paint);
   SkMatrix content_matrix;
   content_matrix.setRectToRect(gfx::RectFToSkRect(quad->tex_coord_rect),
                                gfx::RectToSkRect(quad->rect),
@@ -718,15 +720,17 @@
   quad->display_item_list->Raster(raster_canvas);
 }
 
-void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad) {
-  current_paint_.setColor(quad->color);
-  current_paint_.setAlpha(quad->shared_quad_state->opacity *
-                          SkColorGetA(quad->color));
-  current_canvas_->drawRect(gfx::RectToSkRect(quad->visible_rect),
-                            current_paint_);
+void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad,
+                                      SkPaint* paint) {
+  DCHECK(paint);
+  paint->setColor(quad->color);
+  paint->setAlpha(quad->shared_quad_state->opacity * SkColorGetA(quad->color));
+  current_canvas_->drawRect(gfx::RectToSkRect(quad->visible_rect), *paint);
 }
 
-void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad) {
+void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad,
+                                   SkPaint* paint) {
+  DCHECK(paint);
   ScopedSkImageBuilder builder(this, quad->resource_id());
   const SkImage* image = builder.sk_image();
   if (!image)
@@ -746,21 +750,21 @@
 
   bool blend_background =
       quad->background_color != SK_ColorTRANSPARENT && !image->isOpaque();
-  bool needs_layer = blend_background && (current_paint_.getAlpha() != 0xFF);
+  bool needs_layer = blend_background && (paint->getAlpha() != 0xFF);
   base::Optional<SkAutoCanvasRestore> auto_canvas_restore;
   if (needs_layer) {
     auto_canvas_restore.emplace(current_canvas_, false /* do_save */);
-    current_canvas_->saveLayerAlpha(&quad_rect, current_paint_.getAlpha());
-    current_paint_.setAlpha(0xFF);
+    current_canvas_->saveLayerAlpha(&quad_rect, paint->getAlpha());
+    paint->setAlpha(0xFF);
   }
   if (blend_background) {
     SkPaint background_paint;
     background_paint.setColor(quad->background_color);
     current_canvas_->drawRect(quad_rect, background_paint);
   }
-  current_paint_.setFilterQuality(
-      quad->nearest_neighbor ? kNone_SkFilterQuality : kLow_SkFilterQuality);
-  current_canvas_->drawImageRect(image, sk_uv_rect, quad_rect, &current_paint_);
+  paint->setFilterQuality(quad->nearest_neighbor ? kNone_SkFilterQuality
+                                                 : kLow_SkFilterQuality);
+  current_canvas_->drawImageRect(image, sk_uv_rect, quad_rect, paint);
 }
 
 void SkiaRenderer::AddTileQuadToBatch(const TileDrawQuad* quad,
@@ -827,7 +831,9 @@
   batched_tiles_.clear();
 }
 
-void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad) {
+void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
+                                    SkPaint* paint) {
+  DCHECK(paint);
   if (draw_mode_ != DrawMode::DDL) {
     NOTIMPLEMENTED();
     return;
@@ -844,9 +850,9 @@
 
   SkRect uv_rect = gfx::RectFToSkRect(visible_tex_coord_rect);
   // TODO(penghuang): figure out how to set correct filter quality.
-  current_paint_.setFilterQuality(kLow_SkFilterQuality);
-  current_canvas_->drawImageRect(
-      image, uv_rect, gfx::RectToSkRect(quad->visible_rect), &current_paint_);
+  paint->setFilterQuality(kLow_SkFilterQuality);
+  current_canvas_->drawImageRect(image, uv_rect,
+                                 gfx::RectToSkRect(quad->visible_rect), paint);
 }
 
 bool SkiaRenderer::CalculateRPDQParams(sk_sp<SkImage> content,
@@ -920,14 +926,16 @@
                                                 resource_provider_);
 }
 
-void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
+void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad,
+                                      SkPaint* paint) {
+  DCHECK(paint);
   auto bypass = render_pass_bypass_quads_.find(quad->render_pass_id);
   // When Render Pass has a single quad inside we would draw that directly.
   if (bypass != render_pass_bypass_quads_.end()) {
     TileDrawQuad* tile_quad = &bypass->second;
     ScopedSkImageBuilder builder(this, tile_quad->resource_id());
     sk_sp<SkImage> content_image = sk_ref_sp(builder.sk_image());
-    DrawRenderPassQuadInternal(quad, content_image);
+    DrawRenderPassQuadInternal(quad, content_image, paint);
   } else {
     auto iter = render_pass_backings_.find(quad->render_pass_id);
     DCHECK(render_pass_backings_.end() != iter);
@@ -957,12 +965,14 @@
       }
     }
 
-    DrawRenderPassQuadInternal(quad, content_image);
+    DrawRenderPassQuadInternal(quad, content_image, paint);
   }
 }
 
 void SkiaRenderer::DrawRenderPassQuadInternal(const RenderPassDrawQuad* quad,
-                                              sk_sp<SkImage> content_image) {
+                                              sk_sp<SkImage> content_image,
+                                              SkPaint* paint) {
+  DCHECK(paint);
   DrawRenderPassDrawQuadParams params;
   params.filters = FiltersForPass(quad->render_pass_id);
   bool can_draw = CalculateRPDQParams(content_image, quad, &params);
@@ -1006,13 +1016,13 @@
     if (!mask_filter) {
       // Not mask, so we just draw the context_image directly.
       current_canvas_->drawImageRect(content_image, content_rect,
-                                     dest_visible_rect, &current_paint_);
+                                     dest_visible_rect, paint);
       return;
     }
 
     // With mask, we need convert the content_image to a shader, and use
     // drawRect() with the shader and the mask.
-    current_paint_.setMaskFilter(mask_filter);
+    paint->setMaskFilter(mask_filter);
     // Convert the content_image to a shader, and use drawRect() with the
     // shader.
     SkMatrix content_to_dest_matrix;
@@ -1020,8 +1030,8 @@
                                          gfx::RectToSkRect(quad->rect),
                                          SkMatrix::kFill_ScaleToFit);
     auto shader = content_image->makeShader(&content_to_dest_matrix);
-    current_paint_.setShader(std::move(shader));
-    current_canvas_->drawRect(dest_visible_rect, current_paint_);
+    paint->setShader(std::move(shader));
+    current_canvas_->drawRect(dest_visible_rect, *paint);
     return;
   }
 
@@ -1046,9 +1056,9 @@
   SkAutoCanvasRestore auto_canvas_restore(current_canvas_, true /* do_save */);
   current_canvas_->clipRect(gfx::RectToSkRect(quad->rect));
 
-  SkPaint paint;
-  paint.setMaskFilter(mask_filter);
-  SkCanvas::SaveLayerRec rec(&dest_visible_rect, &paint,
+  SkPaint tmp_paint;
+  tmp_paint.setMaskFilter(mask_filter);
+  SkCanvas::SaveLayerRec rec(&dest_visible_rect, &tmp_paint,
                              background_image_filter.get(),
                              SkCanvas::kInitWithPrevious_SaveLayerFlag);
   // Lift content in the current_canvas_ into a new layer with
@@ -1059,19 +1069,20 @@
                                                          false /* do_save */);
   current_canvas_->saveLayer(rec);
   current_canvas_->drawImageRect(content_image, content_rect, dest_visible_rect,
-                                 &current_paint_);
+                                 paint);
 }
 
-void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad) {
+void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad, SkPaint* paint) {
+  DCHECK(paint);
   // TODO(weiliangc): Make sure unsupported quads work. (crbug.com/644851)
   NOTIMPLEMENTED();
 #ifdef NDEBUG
-  current_paint_.setColor(SK_ColorWHITE);
+  paint->setColor(SK_ColorWHITE);
 #else
-  current_paint_.setColor(SK_ColorMAGENTA);
+  paint->setColor(SK_ColorMAGENTA);
 #endif
-  current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
-  current_canvas_->drawRect(gfx::RectToSkRect(quad->rect), current_paint_);
+  paint->setAlpha(quad->shared_quad_state->opacity * 255);
+  current_canvas_->drawRect(gfx::RectToSkRect(quad->rect), *paint);
 }
 
 void SkiaRenderer::CopyDrawnRenderPass(
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h
index d5da31a..4e9d693 100644
--- a/components/viz/service/display/skia_renderer.h
+++ b/components/viz/service/display/skia_renderer.h
@@ -96,21 +96,22 @@
       const gfx::QuadF* draw_region,
       const gfx::Rect* scissor_rect,
       base::Optional<SkAutoCanvasRestore>* auto_canvas_restore);
-  void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad);
-  void DrawPictureQuad(const PictureDrawQuad* quad);
-  void DrawRenderPassQuad(const RenderPassDrawQuad* quad);
+  void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad, SkPaint* paint);
+  void DrawPictureQuad(const PictureDrawQuad* quad, SkPaint* paint);
+  void DrawRenderPassQuad(const RenderPassDrawQuad* quad, SkPaint* paint);
   void DrawRenderPassQuadInternal(const RenderPassDrawQuad* quad,
-                                  sk_sp<SkImage> content_image);
+                                  sk_sp<SkImage> content_image,
+                                  SkPaint* paint);
 
-  void DrawSolidColorQuad(const SolidColorDrawQuad* quad);
-  void DrawTextureQuad(const TextureDrawQuad* quad);
+  void DrawSolidColorQuad(const SolidColorDrawQuad* quad, SkPaint* paint);
+  void DrawTextureQuad(const TextureDrawQuad* quad, SkPaint* paint);
   bool MustDrawBatchedTileQuadsBeforeQuad(const DrawQuad* new_quad,
                                           const gfx::QuadF* draw_region);
   void AddTileQuadToBatch(const TileDrawQuad* quad,
                           const gfx::QuadF* draw_region);
   void DrawBatchedTileQuads();
-  void DrawYUVVideoQuad(const YUVVideoDrawQuad* quad);
-  void DrawUnsupportedQuad(const DrawQuad* quad);
+  void DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, SkPaint* paint);
+  void DrawUnsupportedQuad(const DrawQuad* quad, SkPaint* paint);
   bool CalculateRPDQParams(sk_sp<SkImage> src_image,
                            const RenderPassDrawQuad* quad,
                            DrawRenderPassDrawQuadParams* params);
@@ -164,7 +165,6 @@
   bool disable_picture_quad_image_filtering_ = false;
   bool is_scissor_enabled_ = false;
   gfx::Rect scissor_rect_;
-  SkPaint current_paint_;
 
   // Specific for overdraw.
   sk_sp<SkSurface> overdraw_surface_;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 8cc48f5..2987224 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -991,6 +991,7 @@
     "indexed_db/indexed_db_quota_client.h",
     "indexed_db/indexed_db_reporting.cc",
     "indexed_db/indexed_db_reporting.h",
+    "indexed_db/indexed_db_return_value.cc",
     "indexed_db/indexed_db_return_value.h",
     "indexed_db/indexed_db_tombstone_sweeper.cc",
     "indexed_db/indexed_db_tombstone_sweeper.h",
diff --git a/content/browser/indexed_db/indexed_db_blob_info.cc b/content/browser/indexed_db/indexed_db_blob_info.cc
index 0e40f9c..9b9690e1 100644
--- a/content/browser/indexed_db/indexed_db_blob_info.cc
+++ b/content/browser/indexed_db/indexed_db_blob_info.cc
@@ -7,9 +7,32 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 namespace content {
 
+// static
+void IndexedDBBlobInfo::ConvertBlobInfo(
+    const std::vector<IndexedDBBlobInfo>& blob_info,
+    std::vector<blink::mojom::IDBBlobInfoPtr>* blob_or_file_info) {
+  blob_or_file_info->reserve(blob_info.size());
+  for (const auto& iter : blob_info) {
+    if (!iter.mark_used_callback().is_null())
+      iter.mark_used_callback().Run();
+
+    auto info = blink::mojom::IDBBlobInfo::New();
+    info->mime_type = iter.type();
+    info->size = iter.size();
+    if (iter.is_file()) {
+      info->file = blink::mojom::IDBFileInfo::New();
+      info->file->name = iter.file_name();
+      info->file->path = iter.file_path();
+      info->file->last_modified = iter.last_modified();
+    }
+    blob_or_file_info->push_back(std::move(info));
+  }
+}
+
 IndexedDBBlobInfo::IndexedDBBlobInfo()
     : is_file_(false), size_(-1), key_(DatabaseMetaDataKey::kInvalidBlobKey) {
 }
diff --git a/content/browser/indexed_db/indexed_db_blob_info.h b/content/browser/indexed_db/indexed_db_blob_info.h
index 2e36c081..6826ee9 100644
--- a/content/browser/indexed_db/indexed_db_blob_info.h
+++ b/content/browser/indexed_db/indexed_db_blob_info.h
@@ -15,6 +15,7 @@
 #include "base/time/time.h"
 #include "content/common/content_export.h"
 #include "storage/browser/blob/blob_data_handle.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 namespace content {
 
@@ -22,6 +23,11 @@
  public:
   // TODO(mek): Use ShareableFileReference::FinalReleaseCallback somehow.
   typedef base::RepeatingCallback<void(const base::FilePath&)> ReleaseCallback;
+
+  static void ConvertBlobInfo(
+      const std::vector<IndexedDBBlobInfo>& blob_info,
+      std::vector<blink::mojom::IDBBlobInfoPtr>* blob_or_file_info);
+
   IndexedDBBlobInfo();
   // These two are used for Blobs.
   IndexedDBBlobInfo(std::unique_ptr<storage::BlobDataHandle> blob_handle,
diff --git a/content/browser/indexed_db/indexed_db_callbacks.cc b/content/browser/indexed_db/indexed_db_callbacks.cc
index be43dc3..81b168e 100644
--- a/content/browser/indexed_db/indexed_db_callbacks.cc
+++ b/content/browser/indexed_db/indexed_db_callbacks.cc
@@ -92,49 +92,6 @@
   DISALLOW_COPY_AND_ASSIGN(SafeIOThreadCursorWrapper);
 };
 
-void ConvertBlobInfo(
-    const std::vector<IndexedDBBlobInfo>& blob_info,
-    std::vector<blink::mojom::IDBBlobInfoPtr>* blob_or_file_info) {
-  blob_or_file_info->reserve(blob_info.size());
-  for (const auto& iter : blob_info) {
-    if (!iter.mark_used_callback().is_null())
-      iter.mark_used_callback().Run();
-
-    auto info = blink::mojom::IDBBlobInfo::New();
-    info->mime_type = iter.type();
-    info->size = iter.size();
-    if (iter.is_file()) {
-      info->file = blink::mojom::IDBFileInfo::New();
-      info->file->name = iter.file_name();
-      info->file->path = iter.file_path();
-      info->file->last_modified = iter.last_modified();
-    }
-    blob_or_file_info->push_back(std::move(info));
-  }
-}
-
-// Destructively converts an IndexedDBReturnValue to a Mojo ReturnValue.
-blink::mojom::IDBReturnValuePtr ConvertReturnValue(
-    IndexedDBReturnValue* value) {
-  auto mojo_value = blink::mojom::IDBReturnValue::New();
-  mojo_value->value = blink::mojom::IDBValue::New();
-  if (value->primary_key.IsValid()) {
-    mojo_value->primary_key = value->primary_key;
-    mojo_value->key_path = value->key_path;
-  }
-  if (!value->empty()) {
-    // TODO(crbug.com/902498): Use mojom traits to map directly from
-    //                         std::string.
-    const char* value_data = value->bits.data();
-    mojo_value->value->bits =
-        std::vector<uint8_t>(value_data, value_data + value->bits.length());
-    // Release value->bits std::string.
-    value->bits.clear();
-  }
-  ConvertBlobInfo(value->blob_info, &mojo_value->value->blob_or_file_info);
-  return mojo_value;
-}
-
 }  // namespace
 
 // Expected to be created and called from IO thread.
@@ -198,23 +155,6 @@
   DISALLOW_COPY_AND_ASSIGN(IOThreadHelper);
 };
 
-// static
-blink::mojom::IDBValuePtr IndexedDBCallbacks::ConvertAndEraseValue(
-    IndexedDBValue* value) {
-  auto mojo_value = blink::mojom::IDBValue::New();
-  if (!value->empty()) {
-    // TODO(crbug.com/902498): Use mojom traits to map directly from
-    //                         std::string.
-    const char* value_data = value->bits.data();
-    mojo_value->bits =
-        std::vector<uint8_t>(value_data, value_data + value->bits.length());
-    // Release value->bits std::string.
-    value->bits.clear();
-  }
-  ConvertBlobInfo(value->blob_info, &mojo_value->blob_or_file_info);
-  return mojo_value;
-}
-
 IndexedDBCallbacks::IndexedDBCallbacks(
     base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
     const url::Origin& origin,
@@ -347,7 +287,7 @@
   blink::mojom::IDBValuePtr mojo_value;
   std::vector<IndexedDBBlobInfo> blob_info;
   if (value) {
-    mojo_value = ConvertAndEraseValue(value);
+    mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
     blob_info.swap(value->blob_info);
   }
 
@@ -374,7 +314,7 @@
   blink::mojom::IDBValuePtr mojo_value;
   std::vector<IndexedDBBlobInfo> blob_info;
   if (value) {
-    mojo_value = ConvertAndEraseValue(value);
+    mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
     blob_info.swap(value->blob_info);
   }
 
@@ -401,7 +341,7 @@
   std::vector<blink::mojom::IDBValuePtr> mojo_values;
   mojo_values.reserve(values->size());
   for (size_t i = 0; i < values->size(); ++i)
-    mojo_values.push_back(ConvertAndEraseValue(&(*values)[i]));
+    mojo_values.push_back(IndexedDBValue::ConvertAndEraseValue(&(*values)[i]));
 
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
@@ -420,7 +360,7 @@
   blink::mojom::IDBReturnValuePtr mojo_value;
   std::vector<IndexedDBBlobInfo> blob_info;
   if (value) {
-    mojo_value = ConvertReturnValue(value);
+    mojo_value = IndexedDBReturnValue::ConvertReturnValue(value);
     blob_info = value->blob_info;
   }
 
@@ -442,8 +382,10 @@
 
   std::vector<blink::mojom::IDBReturnValuePtr> mojo_values;
   mojo_values.reserve(values->size());
-  for (size_t i = 0; i < values->size(); ++i)
-    mojo_values.push_back(ConvertReturnValue(&(*values)[i]));
+  for (size_t i = 0; i < values->size(); ++i) {
+    mojo_values.push_back(
+        IndexedDBReturnValue::ConvertReturnValue(&(*values)[i]));
+  }
 
   base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
                            base::BindOnce(&IOThreadHelper::SendSuccessArray,
diff --git a/content/browser/indexed_db/indexed_db_callbacks.h b/content/browser/indexed_db/indexed_db_callbacks.h
index 4ec6798..d9ddd61 100644
--- a/content/browser/indexed_db/indexed_db_callbacks.h
+++ b/content/browser/indexed_db/indexed_db_callbacks.h
@@ -43,9 +43,6 @@
 class CONTENT_EXPORT IndexedDBCallbacks
     : public base::RefCounted<IndexedDBCallbacks> {
  public:
-  // Destructively converts an IndexedDBValue to a Mojo Value.
-  static blink::mojom::IDBValuePtr ConvertAndEraseValue(IndexedDBValue* value);
-
   IndexedDBCallbacks(base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
                      const url::Origin& origin,
                      blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info,
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index 418c1a2..8a7b204 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -870,7 +870,7 @@
         // indexeddb value. crbug.com/682363
         IndexedDBValue copy = *value;
         changes->observations.back()->value =
-            IndexedDBCallbacks::ConvertAndEraseValue(&copy);
+            IndexedDBValue::ConvertAndEraseValue(&copy);
       }
     }
   }
diff --git a/content/browser/indexed_db/indexed_db_return_value.cc b/content/browser/indexed_db/indexed_db_return_value.cc
new file mode 100644
index 0000000..d0b68ae
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_return_value.cc
@@ -0,0 +1,34 @@
+// 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.
+
+#include "content/browser/indexed_db/indexed_db_return_value.h"
+
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+
+namespace content {
+
+// static
+blink::mojom::IDBReturnValuePtr IndexedDBReturnValue::ConvertReturnValue(
+    IndexedDBReturnValue* value) {
+  auto mojo_value = blink::mojom::IDBReturnValue::New();
+  mojo_value->value = blink::mojom::IDBValue::New();
+  if (value->primary_key.IsValid()) {
+    mojo_value->primary_key = value->primary_key;
+    mojo_value->key_path = value->key_path;
+  }
+  if (!value->empty()) {
+    // TODO(crbug.com/902498): Use mojom traits to map directly from
+    //                         std::string.
+    const char* value_data = value->bits.data();
+    mojo_value->value->bits =
+        std::vector<uint8_t>(value_data, value_data + value->bits.length());
+    // Release value->bits std::string.
+    value->bits.clear();
+  }
+  IndexedDBBlobInfo::ConvertBlobInfo(value->blob_info,
+                                     &mojo_value->value->blob_or_file_info);
+  return mojo_value;
+}
+
+}  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_return_value.h b/content/browser/indexed_db/indexed_db_return_value.h
index a528519..ca8436b 100644
--- a/content/browser/indexed_db/indexed_db_return_value.h
+++ b/content/browser/indexed_db/indexed_db_return_value.h
@@ -18,6 +18,10 @@
 // database, so they are kept separately, and sent back with the original data
 // so that the render process can amend the returned object.
 struct CONTENT_EXPORT IndexedDBReturnValue : public IndexedDBValue {
+  // Destructively converts an IndexedDBReturnValue to a Mojo ReturnValue.
+  static blink::mojom::IDBReturnValuePtr ConvertReturnValue(
+      IndexedDBReturnValue* value);
+
   blink::IndexedDBKey
       primary_key;  // primary key (only when using key generator)
   blink::IndexedDBKeyPath key_path;
diff --git a/content/browser/indexed_db/indexed_db_value.cc b/content/browser/indexed_db/indexed_db_value.cc
index 6feed69..2566540 100644
--- a/content/browser/indexed_db/indexed_db_value.cc
+++ b/content/browser/indexed_db/indexed_db_value.cc
@@ -5,9 +5,28 @@
 #include "content/browser/indexed_db/indexed_db_value.h"
 
 #include "base/logging.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
 namespace content {
 
+// static
+blink::mojom::IDBValuePtr IndexedDBValue::ConvertAndEraseValue(
+    IndexedDBValue* value) {
+  auto mojo_value = blink::mojom::IDBValue::New();
+  if (!value->empty()) {
+    // TODO(crbug.com/902498): Use mojom traits to map directly from
+    //                         std::string.
+    const char* value_data = value->bits.data();
+    mojo_value->bits =
+        std::vector<uint8_t>(value_data, value_data + value->bits.length());
+    // Release value->bits std::string.
+    value->bits.clear();
+  }
+  IndexedDBBlobInfo::ConvertBlobInfo(value->blob_info,
+                                     &mojo_value->blob_or_file_info);
+  return mojo_value;
+}
+
 IndexedDBValue::IndexedDBValue() = default;
 IndexedDBValue::IndexedDBValue(
     const std::string& input_bits,
diff --git a/content/browser/indexed_db/indexed_db_value.h b/content/browser/indexed_db/indexed_db_value.h
index 33bb45a..9314b5b64 100644
--- a/content/browser/indexed_db/indexed_db_value.h
+++ b/content/browser/indexed_db/indexed_db_value.h
@@ -17,6 +17,9 @@
 namespace content {
 
 struct CONTENT_EXPORT IndexedDBValue {
+  // Destructively converts an IndexedDBValue to a Mojo Value.
+  static blink::mojom::IDBValuePtr ConvertAndEraseValue(IndexedDBValue* value);
+
   IndexedDBValue();
   IndexedDBValue(const std::string& input_bits,
                  const std::vector<IndexedDBBlobInfo>& input_blob_info);
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 91f3a642..46ea2b1 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -1435,12 +1435,13 @@
   DownloadTestObserverInProgress observer(
       BrowserContext::GetDownloadManager(opener->GetBrowserContext()),
       1 /* wait_count */);
-  EXPECT_TRUE(ExecuteScript(
+  EXPECT_TRUE(ExecuteScriptWithoutUserGesture(
       popup,
       "window.opener.location ='data:html/text;base64,'+btoa('payload');"));
   observer.WaitForFinished();
   histograms.ExpectUniqueSample("Navigation.DownloadPolicy",
-                                NavigationDownloadPolicy::kAllowOpener, 1);
+                                NavigationDownloadPolicy::kAllowOpenerNoGesture,
+                                1);
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.h b/content/browser/renderer_host/browser_compositor_view_mac.h
index e271d4bc..da7e339 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.h
+++ b/content/browser/renderer_host/browser_compositor_view_mac.h
@@ -127,7 +127,7 @@
   void OnBeginFrame(base::TimeTicks frame_time) override;
   void OnFrameTokenChanged(uint32_t frame_token) override;
   float GetDeviceScaleFactor() const override;
-  void AllocateNewSurfaceIdOnEviction() override;
+  void InvalidateLocalSurfaceIdOnEviction() override;
   std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override;
 
   base::WeakPtr<BrowserCompositorMac> GetWeakPtr() {
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.mm b/content/browser/renderer_host/browser_compositor_view_mac.mm
index 0240d73..20b88ff4 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.mm
+++ b/content/browser/renderer_host/browser_compositor_view_mac.mm
@@ -346,8 +346,8 @@
   return dfh_display_.device_scale_factor();
 }
 
-void BrowserCompositorMac::AllocateNewSurfaceIdOnEviction() {
-  dfh_local_surface_id_allocator_.GenerateId();
+void BrowserCompositorMac::InvalidateLocalSurfaceIdOnEviction() {
+  dfh_local_surface_id_allocator_.Invalidate();
 }
 
 std::vector<viz::SurfaceId>
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 2ff513d..3fa783b 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -360,7 +360,7 @@
     host_frame_sink_manager_->EvictSurfaces(surface_ids);
   }
   frame_evictor_->OnSurfaceDiscarded();
-  client_->AllocateNewSurfaceIdOnEviction();
+  client_->InvalidateLocalSurfaceIdOnEviction();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/content/browser/renderer_host/delegated_frame_host.h b/content/browser/renderer_host/delegated_frame_host.h
index 9689e8e..3a855f7 100644
--- a/content/browser/renderer_host/delegated_frame_host.h
+++ b/content/browser/renderer_host/delegated_frame_host.h
@@ -50,7 +50,7 @@
   virtual void OnBeginFrame(base::TimeTicks frame_time) = 0;
   virtual void OnFrameTokenChanged(uint32_t frame_token) = 0;
   virtual float GetDeviceScaleFactor() const = 0;
-  virtual void AllocateNewSurfaceIdOnEviction() = 0;
+  virtual void InvalidateLocalSurfaceIdOnEviction() = 0;
   virtual std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() = 0;
 };
 
diff --git a/content/browser/renderer_host/delegated_frame_host_client_aura.cc b/content/browser/renderer_host/delegated_frame_host_client_aura.cc
index 973a35f..75a43f1 100644
--- a/content/browser/renderer_host/delegated_frame_host_client_aura.cc
+++ b/content/browser/renderer_host/delegated_frame_host_client_aura.cc
@@ -55,8 +55,8 @@
   return render_widget_host_view_->device_scale_factor_;
 }
 
-void DelegatedFrameHostClientAura::AllocateNewSurfaceIdOnEviction() {
-  render_widget_host_view_->AllocateNewSurfaceIdOnEviction();
+void DelegatedFrameHostClientAura::InvalidateLocalSurfaceIdOnEviction() {
+  render_widget_host_view_->InvalidateLocalSurfaceIdOnEviction();
 }
 
 std::vector<viz::SurfaceId>
diff --git a/content/browser/renderer_host/delegated_frame_host_client_aura.h b/content/browser/renderer_host/delegated_frame_host_client_aura.h
index 8f93648..40e1f52e 100644
--- a/content/browser/renderer_host/delegated_frame_host_client_aura.h
+++ b/content/browser/renderer_host/delegated_frame_host_client_aura.h
@@ -33,7 +33,7 @@
   void OnBeginFrame(base::TimeTicks frame_time) override;
   void OnFrameTokenChanged(uint32_t frame_token) override;
   float GetDeviceScaleFactor() const override;
-  void AllocateNewSurfaceIdOnEviction() override;
+  void InvalidateLocalSurfaceIdOnEviction() override;
   std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override;
 
  private:
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index 77d4db9e5..4603cff 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -276,6 +276,12 @@
 void InputRouterImpl::SetWhiteListedTouchAction(cc::TouchAction touch_action,
                                                 uint32_t unique_touch_event_id,
                                                 InputEventAckState state) {
+  DCHECK(!compositor_touch_action_enabled_);
+  OnSetWhiteListedTouchAction(touch_action);
+}
+
+void InputRouterImpl::OnSetWhiteListedTouchAction(
+    cc::TouchAction touch_action) {
   touch_action_filter_.OnSetWhiteListedTouchAction(touch_action);
   client_->OnSetWhiteListedTouchAction(touch_action);
   if (compositor_touch_action_enabled_) {
@@ -574,8 +580,18 @@
   // The SetTouchAction IPC occurs on a different channel so always
   // send it in the input event ack to ensure it is available at the
   // time the ACK is handled.
-  if (touch_action.has_value())
-    OnSetTouchAction(touch_action.value());
+  if (touch_action.has_value()) {
+    if (!compositor_touch_action_enabled_) {
+      OnSetTouchAction(touch_action.value());
+    } else {
+      if (source == InputEventAckSource::COMPOSITOR_THREAD)
+        OnSetWhiteListedTouchAction(touch_action.value());
+      else if (source == InputEventAckSource::MAIN_THREAD)
+        OnSetTouchAction(touch_action.value());
+      else
+        NOTREACHED();
+    }
+  }
 
   bool should_stop_timeout_monitor =
       !compositor_touch_action_enabled_ ||
diff --git a/content/browser/renderer_host/input/input_router_impl.h b/content/browser/renderer_host/input/input_router_impl.h
index 05bb0d01..dd4e239 100644
--- a/content/browser/renderer_host/input/input_router_impl.h
+++ b/content/browser/renderer_host/input/input_router_impl.h
@@ -111,6 +111,7 @@
   void ForceResetTouchActionForTest();
 
  private:
+  friend class InputRouterImplTest;
   friend class InputRouterImplTestBase;
   friend class MockRenderWidgetHost;
   friend class RenderWidgetHostSitePerProcessTest;
@@ -216,6 +217,7 @@
       GestureEventWithLatencyInfo& gesture_event,
       const FilterGestureEventResult& existing_result);
   void ProcessDeferredGestureEventQueue();
+  void OnSetWhiteListedTouchAction(cc::TouchAction touch_action);
 
   InputRouterImplClient* client_;
   InputDispositionHandler* disposition_handler_;
diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc
index 49327ed..45cff45 100644
--- a/content/browser/renderer_host/input/input_router_impl_unittest.cc
+++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -425,13 +425,14 @@
     PressTouchPoint(1, 1);
     base::Optional<ui::DidOverscrollParams> overscroll;
     input_router_->SendTouchEvent(TouchEventWithLatencyInfo(touch_event_));
-    input_router_->TouchEventHandled(
-        TouchEventWithLatencyInfo(touch_event_), InputEventAckSource::BROWSER,
-        ui::LatencyInfo(), state, overscroll, touch_action);
+    input_router_->TouchEventHandled(TouchEventWithLatencyInfo(touch_event_),
+                                     InputEventAckSource::MAIN_THREAD,
+                                     ui::LatencyInfo(), state, overscroll,
+                                     touch_action);
     EXPECT_EQ(input_router_->touch_action_filter_.num_of_active_touches_, 1);
     ReleaseTouchPoint(0);
     input_router_->OnTouchEventAck(TouchEventWithLatencyInfo(touch_event_),
-                                   InputEventAckSource::BROWSER, state);
+                                   InputEventAckSource::MAIN_THREAD, state);
     EXPECT_EQ(input_router_->touch_action_filter_.num_of_active_touches_, 0);
   }
 
@@ -470,6 +471,14 @@
     }
   }
 
+  base::Optional<cc::TouchAction> AllowedTouchAction() {
+    return input_router_->touch_action_filter_.allowed_touch_action_;
+  }
+
+  base::Optional<cc::TouchAction> WhiteListedTouchAction() {
+    return input_router_->touch_action_filter_.white_listed_touch_action_;
+  }
+
  protected:
   const bool compositor_touch_action_enabled_;
 
@@ -1272,7 +1281,7 @@
   // kTouchActionNone should disable the timeout.
   CancelTouchTimeout();
   dispatched_messages[0]->ToEvent()->CallCallback(
-      InputEventAckSource::COMPOSITOR_THREAD, ui::LatencyInfo(),
+      InputEventAckSource::MAIN_THREAD, ui::LatencyInfo(),
       INPUT_EVENT_ACK_STATE_CONSUMED, base::nullopt, cc::kTouchActionNone);
   EXPECT_EQ(1U, disposition_handler_->GetAndResetAckCount());
   EXPECT_FALSE(TouchEventTimeoutEnabled());
@@ -1356,7 +1365,7 @@
   ASSERT_TRUE(touch_release_event2[0]->ToEvent());
 
   touch_press_event1[0]->ToEvent()->CallCallback(
-      InputEventAckSource::COMPOSITOR_THREAD, ui::LatencyInfo(),
+      InputEventAckSource::MAIN_THREAD, ui::LatencyInfo(),
       INPUT_EVENT_ACK_STATE_CONSUMED, base::nullopt, cc::kTouchActionNone);
   touch_move_event1[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
 
@@ -1416,7 +1425,7 @@
   ASSERT_TRUE(touch_move_event1[0]->ToEvent());
   CancelTouchTimeout();
   touch_press_event1[0]->ToEvent()->CallCallback(
-      InputEventAckSource::COMPOSITOR_THREAD, ui::LatencyInfo(),
+      InputEventAckSource::MAIN_THREAD, ui::LatencyInfo(),
       INPUT_EVENT_ACK_STATE_CONSUMED, base::nullopt, cc::kTouchActionNone);
   touch_move_event1[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
 
@@ -1490,7 +1499,7 @@
 
   // Ensure we have touch-action:none, suppressing scroll events.
   dispatched_messages[0]->ToEvent()->CallCallback(
-      InputEventAckSource::COMPOSITOR_THREAD, ui::LatencyInfo(),
+      InputEventAckSource::MAIN_THREAD, ui::LatencyInfo(),
       INPUT_EVENT_ACK_STATE_CONSUMED, base::nullopt, cc::kTouchActionNone);
   EXPECT_EQ(0U, GetAndResetDispatchedMessages().size());
   dispatched_messages[1]->ToEvent()->CallCallback(
@@ -1577,7 +1586,7 @@
   ASSERT_EQ(1U, dispatched_messages.size());
   ASSERT_TRUE(dispatched_messages[0]->ToEvent());
   dispatched_messages[0]->ToEvent()->CallCallback(
-      InputEventAckSource::COMPOSITOR_THREAD, ui::LatencyInfo(),
+      InputEventAckSource::MAIN_THREAD, ui::LatencyInfo(),
       INPUT_EVENT_ACK_STATE_CONSUMED, base::nullopt, cc::kTouchActionNone);
   ReleaseTouchPoint(0);
   SendTouchEvent();
@@ -1966,6 +1975,9 @@
 // Test proper routing of whitelisted touch action notifications received from
 // |SetWhiteListedTouchAction| IPC messages.
 TEST_P(InputRouterImplTest, OnSetWhiteListedTouchAction) {
+  // The white listed touch action is bundled in the ack.
+  if (compositor_touch_action_enabled_)
+    return;
   cc::TouchAction touch_action = cc::kTouchActionPanY;
   OnSetWhiteListedTouchAction(touch_action, 0,
                               INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
@@ -2021,14 +2033,24 @@
   DispatchedMessages dispatched_messages = GetAndResetDispatchedMessages();
   ASSERT_EQ(1U, dispatched_messages.size());
   ASSERT_TRUE(dispatched_messages[0]->ToEvent());
+  InputEventAckSource source = InputEventAckSource::MAIN_THREAD;
+  base::Optional<cc::TouchAction> expected_touch_action = cc::kTouchActionPan;
+  if (compositor_touch_action_enabled_)
+    source = InputEventAckSource::COMPOSITOR_THREAD;
   dispatched_messages[0]->ToEvent()->CallCallback(
-      InputEventAckSource::COMPOSITOR_THREAD, ui::LatencyInfo(),
-      INPUT_EVENT_ACK_STATE_CONSUMED, base::nullopt, cc::kTouchActionNone);
+      source, ui::LatencyInfo(), INPUT_EVENT_ACK_STATE_CONSUMED, base::nullopt,
+      expected_touch_action);
   ASSERT_EQ(1U, disposition_handler_->GetAndResetAckCount());
-  base::Optional<cc::TouchAction> allowed_touch_action =
-      input_router_->AllowedTouchAction();
-  DCHECK(allowed_touch_action.has_value());
-  EXPECT_EQ(cc::TouchAction::kTouchActionNone, allowed_touch_action.value());
+  base::Optional<cc::TouchAction> allowed_touch_action = AllowedTouchAction();
+  base::Optional<cc::TouchAction> white_listed_touch_action =
+      WhiteListedTouchAction();
+  if (compositor_touch_action_enabled_) {
+    EXPECT_FALSE(allowed_touch_action.has_value());
+    EXPECT_EQ(expected_touch_action, white_listed_touch_action);
+  } else {
+    EXPECT_FALSE(white_listed_touch_action.has_value());
+    EXPECT_EQ(expected_touch_action, allowed_touch_action);
+  }
 }
 
 namespace {
diff --git a/content/browser/renderer_host/input/touch_action_filter.h b/content/browser/renderer_host/input/touch_action_filter.h
index 4ee9a50..d5d6efd 100644
--- a/content/browser/renderer_host/input/touch_action_filter.h
+++ b/content/browser/renderer_host/input/touch_action_filter.h
@@ -76,6 +76,7 @@
   void AppendToGestureSequenceForDebugging(const char* str);
 
  private:
+  friend class InputRouterImplTest;
   friend class InputRouterImplTestBase;
   friend class MockRenderWidgetHost;
   friend class TouchActionFilterTest;
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index cb745652..98fea8b 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -2400,7 +2400,7 @@
 }
 
 void RenderWidgetHostViewAndroid::WasEvicted() {
-  local_surface_id_allocator_.GenerateId();
+  local_surface_id_allocator_.Invalidate();
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 2f878e4..4572744 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -501,6 +501,15 @@
   if (is_mus_browser_plugin_guest_)
     return;
 
+  // If the viz::LocalSurfaceIdAllocation is invalid, we may have been evicted,
+  // and no other visual properties have since been changed. Allocate a new id
+  // and start synchronizing.
+  if (!window_->GetLocalSurfaceIdAllocation().IsValid()) {
+    window_->AllocateLocalSurfaceId();
+    SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
+                                window_->GetLocalSurfaceIdAllocation());
+  }
+
   window_->Show();
   WasUnOccluded();
 }
@@ -2096,6 +2105,10 @@
   DCHECK(window_);
   window_->UpdateLocalSurfaceIdFromEmbeddedClient(
       child_local_surface_id_allocation);
+  // If the viz::LocalSurfaceIdAllocation is invalid, we may have been evicted,
+  // allocate a new one to establish bounds.
+  if (!GetLocalSurfaceIdAllocation().IsValid())
+    window_->AllocateLocalSurfaceId();
 
   if (delegated_frame_host_) {
     delegated_frame_host_->EmbedSurface(
@@ -2589,8 +2602,8 @@
   host()->GetContentRenderingTimeoutFrom(view_aura->host());
 }
 
-void RenderWidgetHostViewAura::AllocateNewSurfaceIdOnEviction() {
-  window_->UpdateLocalSurfaceIdFromEmbeddedClient(base::nullopt);
+void RenderWidgetHostViewAura::InvalidateLocalSurfaceIdOnEviction() {
+  window_->InvalidateLocalSurfaceId();
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index f567a62..df797fe 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -558,7 +558,7 @@
   // Called when the window title is changed.
   void WindowTitleChanged();
 
-  void AllocateNewSurfaceIdOnEviction();
+  void InvalidateLocalSurfaceIdOnEviction();
 
   const bool is_mus_browser_plugin_guest_;
 
diff --git a/content/browser/tracing/background_tracing_manager_browsertest.cc b/content/browser/tracing/background_tracing_manager_browsertest.cc
index 9dd2cb9..d846263e 100644
--- a/content/browser/tracing/background_tracing_manager_browsertest.cc
+++ b/content/browser/tracing/background_tracing_manager_browsertest.cc
@@ -394,8 +394,10 @@
     wait_for_tracing.Run();
   }
 
-  TRACE_EVENT1("benchmark", "whitelisted", "find_this", 1);
-  TRACE_EVENT1("benchmark", "not_whitelisted", "this_not_found", 1);
+  {
+    TRACE_EVENT1("benchmark", "whitelisted", "find_this", 1);
+    TRACE_EVENT1("benchmark", "not_whitelisted", "this_not_found", 1);
+  }
 
   BackgroundTracingManager::GetInstance()->WhenIdle(
       base::Bind(&DisableScenarioWhenIdle));
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index 03ff4ea..df69487 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -513,6 +513,18 @@
   is_tracing_ = TracingControllerImpl::GetInstance()->StartTracing(
       config, base::BindOnce(&BackgroundTracingManagerImpl::OnStartTracingDone,
                              base::Unretained(this), preset));
+
+  // Activate the categories immediately. StartTracing eventually does this
+  // itself, but asynchronously via PostTask, and in the meantime events will be
+  // dropped. This ensures that we start recording events for those categories
+  // immediately.
+  if (is_tracing_) {
+    uint8_t modes = base::trace_event::TraceLog::RECORDING_MODE;
+    if (!config.event_filters().empty())
+      modes |= base::trace_event::TraceLog::FILTERING_MODE;
+    base::trace_event::TraceLog::GetInstance()->SetEnabled(config, modes);
+  }
+
   RecordBackgroundTracingMetric(RECORDING_ENABLED);
 }
 
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index ed6586cf..18f8533 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -172,7 +172,8 @@
   // The drag invokes a nested event loop, arrange to continue
   // processing events.
   base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
-  NSDragOperation mask = static_cast<NSDragOperation>(allowed_operations);
+  NSDragOperation mask = static_cast<NSDragOperation>(allowed_operations) &
+                         ~NSDragOperationGeneric;
   NSPoint offset = NSPointFromCGPoint(
       gfx::PointAtOffsetFromOrigin(image_offset).ToCGPoint());
   [cocoa_view_ startDragWithDropData:drop_data
diff --git a/content/renderer/input/widget_input_handler_manager.cc b/content/renderer/input/widget_input_handler_manager.cc
index 202f08a..67a2a5e 100644
--- a/content/renderer/input/widget_input_handler_manager.cc
+++ b/content/renderer/input/widget_input_handler_manager.cc
@@ -276,6 +276,10 @@
     cc::TouchAction touch_action,
     uint32_t unique_touch_event_id,
     ui::InputHandlerProxy::EventDisposition event_disposition) {
+  if (base::FeatureList::IsEnabled(features::kCompositorTouchAction)) {
+    white_listed_touch_action_ = touch_action;
+    return;
+  }
   mojom::WidgetInputHandlerHost* host = GetWidgetInputHandlerHost();
   if (!host)
     return;
@@ -478,6 +482,10 @@
   if (!callback)
     return;
 
+  if (!touch_action.has_value()) {
+    touch_action = white_listed_touch_action_;
+    white_listed_touch_action_.reset();
+  }
   // This method is called from either the main thread or the compositor thread.
   bool is_compositor_thread = compositor_task_runner_ &&
                               compositor_task_runner_->BelongsToCurrentThread();
diff --git a/content/renderer/input/widget_input_handler_manager.h b/content/renderer/input/widget_input_handler_manager.h
index 0682118..6ebdf02 100644
--- a/content/renderer/input/widget_input_handler_manager.h
+++ b/content/renderer/input/widget_input_handler_manager.h
@@ -146,6 +146,8 @@
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
 
+  base::Optional<cc::TouchAction> white_listed_touch_action_;
+
 #if defined(OS_ANDROID)
   std::unique_ptr<SynchronousCompositorProxyRegistry>
       synchronous_compositor_registry_;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index b99f6ef9..3adeac7 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -662,7 +662,8 @@
   DCHECK(content::RenderThread::Get());
   if (closing_)
     return;
-  NotifyOnClose();
+  for (auto& observer : render_frames_)
+    observer.WidgetWillClose();
   closing_ = true;
 
   // Browser correspondence is no longer needed at this point.
@@ -806,9 +807,10 @@
 void RenderWidget::OnWasShown(base::TimeTicks show_request_timestamp,
                               bool was_evicted) {
   TRACE_EVENT0("renderer", "RenderWidget::OnWasShown");
-  // TODO(crbug.com/896836): CHECK to try track down why we make a frame sink
-  // for a RenderWidget without a main frame.
-  CHECK(!is_frozen_);
+  // TODO(danakj): Nothing should happen ideally if the RenderWidget is frozen!
+  // It's not visible! However.. the RenderView needs to see it as visible in
+  // order to make the Page visible /o\ so this is hard. We need to detangle
+  // page visibility from the main widget. https://crbug.com/419087
 
   was_shown_time_ = base::TimeTicks::Now();
   // See OnWasHidden
@@ -1603,17 +1605,9 @@
 }
 
 void RenderWidget::DoDeferredClose() {
-  // Prevent compositor from setting up new IPC channels, since we know a
-  // WidgetMsg_Close is coming.
-  host_will_close_this_ = true;
   Send(new WidgetHostMsg_Close(routing_id_));
 }
 
-void RenderWidget::NotifyOnClose() {
-  for (auto& observer : render_frames_)
-    observer.WidgetWillClose();
-}
-
 void RenderWidget::CloseWidgetSoon() {
   DCHECK(content::RenderThread::Get());
   if (is_frozen_) {
@@ -1624,9 +1618,17 @@
     return;
   }
 
+  // Prevent compositor from setting up new IPC channels, since we know a
+  // WidgetMsg_Close is coming. We do this immediately, not in DoDeferredClose,
+  // as the caller (eg WebPagePopupImpl) may start tearing down things after
+  // calling this method, including detaching the frame from this RenderWidget.
+  // Then trying to make a LayerTreeFrameSink would crash.
+  // https://crbug.com/906340
+  host_will_close_this_ = true;
+
   // If a page calls window.close() twice, we'll end up here twice, but that's
   // OK.  It is safe to send multiple Close messages.
-
+  //
   // Ask the RenderWidgetHost to initiate close.  We could be called from deep
   // in Javascript.  If we ask the RendwerWidgetHost to close now, the window
   // could be closed before the JS finishes executing.  So instead, post a
@@ -2260,7 +2262,15 @@
   if (render_widget_scheduling_state_)
     render_widget_scheduling_state_->SetHidden(hidden);
 
-  layer_tree_view_->SetVisible(!is_hidden_);
+  // When the RenderWidget is frozen, visibility of the compositor is overridden
+  // to always be hidden to prevent it from using resources that are not needed.
+  // Unfortunately the main RenderWidget for a RenderView must be marked visible
+  // even if the RenderView has a proxy main frame (and the RenderWidget is
+  // frozen), in order for the RenderView to use the visibility signal from the
+  // RenderWidget. This is bad. But we don't need to show the compositor to
+  // satisfy that requirement.
+  if (!is_frozen_)
+    layer_tree_view_->SetVisible(!is_hidden_);
 }
 
 void RenderWidget::DidToggleFullscreen() {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 1c2f30f7..7a6d45a9 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -584,7 +584,6 @@
   void StopCompositor();
 
   void DoDeferredClose();
-  void NotifyOnClose();
 
   gfx::Size GetSizeForWebWidget() const;
   void ResizeWebWidget();
diff --git a/content/shell/browser/layout_test/layout_test_content_browser_client.cc b/content/shell/browser/layout_test/layout_test_content_browser_client.cc
index 54c8835c..95148d3 100644
--- a/content/shell/browser/layout_test/layout_test_content_browser_client.cc
+++ b/content/shell/browser/layout_test/layout_test_content_browser_client.cc
@@ -4,7 +4,14 @@
 
 #include "content/shell/browser/layout_test/layout_test_content_browser_client.h"
 
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
 #include "base/strings/pattern.h"
 #include "base/task/post_task.h"
 #include "content/public/browser/browser_context.h"
@@ -14,7 +21,9 @@
 #include "content/public/browser/overlay_window.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/browser/site_isolation_policy.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/common/content_switches.h"
 #include "content/shell/browser/layout_test/blink_test_controller.h"
 #include "content/shell/browser/layout_test/fake_bluetooth_chooser.h"
 #include "content/shell/browser/layout_test/layout_test_bluetooth_fake_adapter_setter_impl.h"
@@ -31,6 +40,7 @@
 #include "device/bluetooth/test/fake_bluetooth.h"
 #include "gpu/config/gpu_switches.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
+#include "url/origin.h"
 
 namespace content {
 namespace {
@@ -234,6 +244,41 @@
       "https://devtools.oopif.test:8443/",
   };
 
+  // On platforms with strict Site Isolation, the also isolate WPT origins for
+  // additional OOPIF coverage.
+  //
+  // Don't isolate WPT origins on
+  // 1) platforms where strict Site Isolation is not the default.
+  // 2) in layout tests under virtual/not-site-per-process where
+  //    --disable-site-isolation-trials switch is used.
+  if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) {
+    // The list of hostnames below is based on
+    // https://web-platform-tests.org/writing-tests/server-features.html
+    const char* kWptHostnames[] = {
+        "www.web-platform.test",
+        "www1.web-platform.test",
+        "www2.web-platform.test",
+        "xn--n8j6ds53lwwkrqhv28a.web-platform.test",
+        "xn--lve-6lad.web-platform.test",
+    };
+
+    // The list of schemes and ports below is based on
+    // third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json
+    const char* kOriginTemplates[] = {
+        "http://%s:8001/", "http://%s:8081/", "https://%s:8444/",
+    };
+
+    origins_to_isolate.reserve(origins_to_isolate.size() +
+                               base::size(kWptHostnames) *
+                                   base::size(kOriginTemplates));
+    for (const char* kWptHostname : kWptHostnames) {
+      for (const char* kOriginTemplate : kOriginTemplates) {
+        std::string origin = base::StringPrintf(kOriginTemplate, kWptHostname);
+        origins_to_isolate.push_back(origin);
+      }
+    }
+  }
+
   // Translate std::vector<std::string> into std::vector<url::Origin>.
   std::vector<url::Origin> result;
   result.reserve(origins_to_isolate.size());
@@ -265,14 +310,6 @@
   return !block_popups_ || user_gesture;
 }
 
-bool LayoutTestContentBrowserClient::ShouldEnableStrictSiteIsolation() {
-  // TODO(lukasza, alexmos): Layout tests should have the same default state of
-  // site-per-process as everything else, but because of a backlog of layout
-  // test failures (see https://crbug.com/477150), layout tests still use no
-  // isolation by default.
-  return false;
-}
-
 bool LayoutTestContentBrowserClient::CanIgnoreCertificateErrorIfNeeded() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kRunWebTests);
diff --git a/content/shell/browser/layout_test/layout_test_content_browser_client.h b/content/shell/browser/layout_test/layout_test_content_browser_client.h
index c1f0405..b416ba6 100644
--- a/content/shell/browser/layout_test/layout_test_content_browser_client.h
+++ b/content/shell/browser/layout_test/layout_test_content_browser_client.h
@@ -70,7 +70,6 @@
                        bool user_gesture,
                        bool opener_suppressed,
                        bool* no_javascript_access) override;
-  bool ShouldEnableStrictSiteIsolation() override;
   bool CanIgnoreCertificateErrorIfNeeded() override;
 
   // ShellContentBrowserClient overrides.
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 245eaff..46697a8 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -640,8 +640,6 @@
     self.Flaky('conformance/textures/image/' +
                'tex-2d-rgb-rgb-unsigned_byte.html',
                ['linux', 'nvidia'], bug=596622)
-    self.Flaky('conformance2/context/methods-2.html',
-               ['linux', 'nvidia'], bug=906212)
 
     # NVIDIA P400 OpenGL
     self.Fail('conformance/limits/gl-max-texture-dimensions.html',
diff --git a/docs/infra/cq.md b/docs/infra/cq.md
index 75a2f3e..b7e67a1 100644
--- a/docs/infra/cq.md
+++ b/docs/infra/cq.md
@@ -56,7 +56,9 @@
 
 ### What exactly does CQ run?
 
-CQ runs the jobs specified in [cq.cfg](../../infra/config/branch/cq.cfg).
+CQ runs the jobs specified in [cq.cfg](../../infra/config/branch/cq.cfg). See
+[`cq_builders.md`](cq_builders.md) for an auto generated file with links to
+information about the builders on the CQ.
 
 Some of these jobs are experimental. This means they are executed on a
 percentage of CQ builds, and the outcome of the build doesn't affect if the CL
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
new file mode 100644
index 0000000..4a55b9c
--- /dev/null
+++ b/docs/infra/cq_builders.md
@@ -0,0 +1,269 @@
+# List of CQ builders
+
+This page is auto generated using the script
+//infra/config/branch/cq_config_presubmit.py. Do not manually edit.
+
+[TOC]
+
+Each builder name links to that builder on Milo. The "Backing builders" links
+point to the file used to determine which configurations a builder should copy
+when running. These links might 404 or error; they are hard-coded right now,
+using common assumptions about how builders are configured.
+
+## Required builders
+
+These builders must pass before a CL may land.
+
+* [android-binary-size](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-binary-size) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android-binary-size)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-binary-size))
+
+* [android-kitkat-arm-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-kitkat-arm-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android-kitkat-arm-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-kitkat-arm-rel))
+
+* [android-marshmallow-arm64-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-marshmallow-arm64-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android-marshmallow-arm64-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-marshmallow-arm64-rel))
+
+* [android_arm64_dbg_recipe](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_arm64_dbg_recipe) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_arm64_dbg_recipe)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_arm64_dbg_recipe))
+
+* [android_clang_dbg_recipe](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_clang_dbg_recipe) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_clang_dbg_recipe)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_clang_dbg_recipe))
+
+* [android_compile_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_dbg) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_compile_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_dbg))
+
+* [android_cronet](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_cronet) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_cronet)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_cronet))
+
+* [cast_shell_android](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/cast_shell_android) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+cast_shell_android)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+cast_shell_android))
+
+* [cast_shell_linux](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/cast_shell_linux) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+cast_shell_linux)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+cast_shell_linux))
+
+* [chromeos-amd64-generic-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-amd64-generic-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+chromeos-amd64-generic-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-amd64-generic-rel))
+
+* [chromeos-daisy-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromeos-daisy-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+chromeos-daisy-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromeos-daisy-rel))
+
+* [chromium_presubmit](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/chromium_presubmit) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+chromium_presubmit)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+chromium_presubmit))
+
+* [fuchsia_arm64](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/fuchsia_arm64) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+fuchsia_arm64)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+fuchsia_arm64))
+
+* [fuchsia_x64](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/fuchsia_x64) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+fuchsia_x64)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+fuchsia_x64))
+
+* [ios-simulator](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-simulator) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+ios-simulator)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-simulator))
+
+* [linux-chromeos-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-chromeos-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-chromeos-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-chromeos-rel))
+
+* [linux-jumbo-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-jumbo-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-jumbo-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-jumbo-rel))
+
+* [linux-libfuzzer-asan-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-libfuzzer-asan-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-libfuzzer-asan-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-libfuzzer-asan-rel))
+
+* [linux-ozone-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-ozone-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-ozone-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-ozone-rel))
+
+* [linux_chromium_asan_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_asan_rel_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_chromium_asan_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_asan_rel_ng))
+
+* [linux_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_compile_dbg_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_compile_dbg_ng))
+
+* [linux_chromium_headless_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_headless_rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_chromium_headless_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_headless_rel))
+
+* [linux_chromium_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_rel_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_chromium_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_rel_ng))
+
+* [linux_chromium_tsan_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_tsan_rel_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_chromium_tsan_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_tsan_rel_ng))
+
+* [mac_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/mac_chromium_compile_dbg_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+mac_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+mac_chromium_compile_dbg_ng))
+
+* [mac_chromium_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/mac_chromium_rel_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+mac_chromium_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+mac_chromium_rel_ng))
+
+* [win10_chromium_x64_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win10_chromium_x64_rel_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+win10_chromium_x64_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win10_chromium_x64_rel_ng))
+
+* [win7_chromium_rel_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win7_chromium_rel_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+win7_chromium_rel_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win7_chromium_rel_ng))
+
+* [win_chromium_compile_dbg_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win_chromium_compile_dbg_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+win_chromium_compile_dbg_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win_chromium_compile_dbg_ng))
+
+
+## Optional builders
+
+These builders optionally run, depending on the files in a
+CL. For example, a CL which touches `//gpu/BUILD.gn` would trigger the builder
+`android_optional_gpu_tests_rel`, due to the `path_regexp` values for that
+builder.
+
+* [android_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_optional_gpu_tests_rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_optional_gpu_tests_rel))
+
+  Path regular expressions:
+    * [`//cc/.+`](https://cs.chromium.org/chromium/src/cc/)
+    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+    * [`//components/viz/.+`](https://cs.chromium.org/chromium/src/components/viz/)
+    * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
+    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+    * [`//media/(audio|filters|gpu)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:media/(audio|filters|gpu)/)
+    * [`//services/viz/.+`](https://cs.chromium.org/chromium/src/services/viz/)
+    * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
+    * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
+    * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
+
+* [closure_compilation](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/closure_compilation) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+closure_compilation)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+closure_compilation))
+
+  Path regular expressions:
+    * [`//components/offline_pages/resources/.+`](https://cs.chromium.org/chromium/src/components/offline_pages/resources/)
+    * [`//third_party/closure_compiler/.+`](https://cs.chromium.org/chromium/src/third_party/closure_compiler/)
+    * [`//third_party/polymer/.+`](https://cs.chromium.org/chromium/src/third_party/polymer/)
+
+* [ios-simulator-cronet](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-simulator-cronet) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+ios-simulator-cronet)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-simulator-cronet))
+
+  Path regular expressions:
+    * [`//components/cronet/.+`](https://cs.chromium.org/chromium/src/components/cronet/)
+    * [`//components/grpc_support/.+`](https://cs.chromium.org/chromium/src/components/grpc_support/)
+    * [`//ios/.+`](https://cs.chromium.org/chromium/src/ios/)
+
+* [ios-simulator-full-configs](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-simulator-full-configs) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+ios-simulator-full-configs)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-simulator-full-configs))
+
+  Path regular expressions:
+    * [`//ios/.+`](https://cs.chromium.org/chromium/src/ios/)
+
+* [linux-blink-gen-property-trees](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-blink-gen-property-trees) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-blink-gen-property-trees)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-blink-gen-property-trees))
+
+  Path regular expressions:
+    * [`//third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees)
+    * [`//third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/.+`](https://cs.chromium.org/chromium/src/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/)
+
+* [linux_chromium_dbg_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_chromium_dbg_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_chromium_dbg_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_chromium_dbg_ng))
+
+  Path regular expressions:
+    * [`//build/.*check_gn_headers.*`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:build/.*check_gn_headers.*)
+
+* [linux_layout_tests_layout_ng](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_layout_tests_layout_ng) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_layout_tests_layout_ng)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_layout_tests_layout_ng))
+
+  Path regular expressions:
+    * [`//third_party/Webkit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/Webkit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG)
+    * [`//third_party/Webkit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/.+`](https://cs.chromium.org/chromium/src/third_party/Webkit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/)
+    * [`//third_party/blink/renderer/core/editing/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/)
+    * [`//third_party/blink/renderer/core/(layout|paint)/ng/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/renderer/core/(layout|paint)/ng/)
+    * [`//third_party/blink/renderer/platform/fonts/shaping/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/)
+
+* [linux_layout_tests_slimming_paint_v2](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_layout_tests_slimming_paint_v2) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_layout_tests_slimming_paint_v2)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_layout_tests_slimming_paint_v2))
+
+  Path regular expressions:
+    * [`//third_party/WebKit/LayoutTests/FlagExpectations/(enable-slimming-paint-v2|enable-blink-gen-property-trees)`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/WebKit/LayoutTests/FlagExpectations/(enable-slimming-paint-v2|enable-blink-gen-property-trees))
+    * [`//third_party/WebKit/LayoutTests/flag-specific/(enable-slimming-paint-v2|enable-blink-gen-property-trees)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/WebKit/LayoutTests/flag-specific/(enable-slimming-paint-v2|enable-blink-gen-property-trees)/)
+    * [`//third_party/blink/renderer/core/layout/compositing/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/layout/compositing/)
+    * [`//third_party/blink/renderer/core/(svg|paint)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/renderer/core/(svg|paint)/)
+    * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
+
+* [linux_mojo](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_mojo) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_mojo)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_mojo))
+
+  Path regular expressions:
+    * [`//services/network/.+`](https://cs.chromium.org/chromium/src/services/network/)
+    * [`//testing/buildbot/filters/mojo\\.fyi\\.network_.*`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:testing/buildbot/filters/mojo\\.fyi\\.network_.*)
+    * [`//third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService)
+
+* [linux_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_optional_gpu_tests_rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_optional_gpu_tests_rel))
+
+  Path regular expressions:
+    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+    * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
+    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+    * [`//media/(audio|filters|gpu)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:media/(audio|filters|gpu)/)
+    * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
+    * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
+    * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
+
+* [linux_vr](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_vr) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_vr)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_vr))
+
+  Path regular expressions:
+    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+
+* [mac_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/mac_optional_gpu_tests_rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+mac_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+mac_optional_gpu_tests_rel))
+
+  Path regular expressions:
+    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+    * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
+    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+    * [`//media/(audio|filters|gpu)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:media/(audio|filters|gpu)/)
+    * [`//services/shape_detection/.+`](https://cs.chromium.org/chromium/src/services/shape_detection/)
+    * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
+    * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
+    * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
+
+* [win_optional_gpu_tests_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win_optional_gpu_tests_rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+win_optional_gpu_tests_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win_optional_gpu_tests_rel))
+
+  Path regular expressions:
+    * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
+    * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
+    * [`//device/vr/.+`](https://cs.chromium.org/chromium/src/device/vr/)
+    * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
+    * [`//media/(audio|filters|gpu)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:media/(audio|filters|gpu)/)
+    * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
+    * [`//third_party/blink/renderer/modules/vr/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/vr/)
+    * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
+    * [`//third_party/blink/renderer/modules/xr/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/xr/)
+    * [`//third_party/blink/renderer/platform/graphics/gpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/gpu/)
+    * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
+
+* [linux_trusty_blink_rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_trusty_blink_rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_trusty_blink_rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_trusty_blink_rel))
+
+  Path regular expressions:
+    * [`//cc/.+`](https://cs.chromium.org/chromium/src/cc/)
+    * [`//third_party/WebKit/LayoutTests/FlagExpectations/(enable-slimming-paint-v2|enable-blink-gen-property-trees)`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/WebKit/LayoutTests/FlagExpectations/(enable-slimming-paint-v2|enable-blink-gen-property-trees))
+    * [`//third_party/WebKit/LayoutTests/flag-specific/(enable-slimming-paint-v2|enable-blink-gen-property-trees)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/WebKit/LayoutTests/flag-specific/(enable-slimming-paint-v2|enable-blink-gen-property-trees)/)
+    * [`//third_party/blink/renderer/core/layout/compositing/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/layout/compositing/)
+    * [`//third_party/blink/renderer/core/(svg|paint)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/renderer/core/(svg|paint)/)
+    * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
+
+* [android_compile_x64_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x64_dbg) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_compile_x64_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x64_dbg))
+
+  Path regular expressions:
+    * [`//sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/)
+
+* [android_compile_x86_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x86_dbg) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_compile_x86_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x86_dbg))
+
+  Path regular expressions:
+    * [`//sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/)
+
+* [android_cronet_tester](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_cronet_tester) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_cronet_tester)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_cronet_tester))
+
+  Path regular expressions:
+    * [`//components/cronet/.+`](https://cs.chromium.org/chromium/src/components/cronet/)
+    * [`//components/grpc_support/.+`](https://cs.chromium.org/chromium/src/components/grpc_support/)
+
+
+## Experimental builders
+
+These builders are run on some percentage of builds. Their results are ignored
+by CQ. These are often used to test new configurations before they are added
+as required builders.
+
+* [ios-device](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-device) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+ios-device)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-device))
+
+  https://crbug.com/739556; make this non-experimental ASAP.
+
+  * Experimental percentage: 10
+
+* [ios-device-xcode-clang](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-device-xcode-clang) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+ios-device-xcode-clang)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-device-xcode-clang))
+
+  https://crbug.com/739556
+
+  * Experimental percentage: 10
+
+* [ios-simulator-xcode-clang](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/ios-simulator-xcode-clang) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+ios-simulator-xcode-clang)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+ios-simulator-xcode-clang))
+
+  https://crbug.com/739556
+
+  * Experimental percentage: 10
+
+* [linux-chromeos-compile-dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-chromeos-compile-dbg) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-chromeos-compile-dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-chromeos-compile-dbg))
+
+  * Experimental percentage: 50
+
+* [linux-dcheck-off-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-dcheck-off-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-dcheck-off-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-dcheck-off-rel))
+
+  https://crbug.com/833482
+
+  * Experimental percentage: 10
+
+* [linux-goma-rbe-staging-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-goma-rbe-staging-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-goma-rbe-staging-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-goma-rbe-staging-rel))
+
+  https://crbug.com/855319
+
+  * Experimental percentage: 20
+
+* [win-libfuzzer-asan-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win-libfuzzer-asan-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+win-libfuzzer-asan-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win-libfuzzer-asan-rel))
+
+  * Experimental percentage: 100
+
+* [win7_chromium_rel_loc_exp](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/win7_chromium_rel_loc_exp) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+win7_chromium_rel_loc_exp)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+win7_chromium_rel_loc_exp))
+
+  * Experimental percentage: 20
+
diff --git a/infra/config/PRESUBMIT.py b/infra/config/PRESUBMIT.py
index e51d843..cf1e7f1 100644
--- a/infra/config/PRESUBMIT.py
+++ b/infra/config/PRESUBMIT.py
@@ -3,9 +3,35 @@
 # found in the LICENSE file.
 
 
-def CheckChangeOnUpload(input_api, output_api):
-  return input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api)
+def _CheckForTranslations(input_api, output_api):
+  del output_api
 
+  return []
+
+def _CommonChecks(input_api, output_api):
+  commands = []
+  # TODO(martiniss): Move this check to the root presubmit. It checks to ensure
+  # that path regexps in cq.cfg reference something locally.
+  for f in input_api.AffectedFiles():
+    local_path = f.LocalPath()
+    if local_path.endswith('cq.cfg'):
+      commands.append(
+        input_api.Command(
+          name='cq.cfg presubmit', cmd=[
+              input_api.python_executable, 'branch/cq_cfg_presubmit.py',
+              '--check'],
+          kwargs={}, message=output_api.PresubmitError),
+      )
+
+  results = []
+
+  results.extend(input_api.canned_checks.CheckChangedLUCIConfigs(
+      input_api, output_api))
+  results.extend(input_api.RunTests(commands))
+  return results
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _CommonChecks(input_api, output_api)
 
 def CheckChangeOnCommit(input_api, output_api):
-  return input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api)
+  return _CommonChecks(input_api, output_api)
diff --git a/infra/config/branch/cq.cfg b/infra/config/branch/cq.cfg
index 9c19999..e72daa4 100644
--- a/infra/config/branch/cq.cfg
+++ b/infra/config/branch/cq.cfg
@@ -1,6 +1,27 @@
 # See http://luci-config.appspot.com/schemas/projects/refs:cq.cfg for the
 # documentation of this file format.
 
+# This file is also used to auto generate //docs/infra/cq_builders.md. If you
+# change this file, run //infra/config/branch/cq_cfg_presubmit.py, which will
+# generate that file. That script also requires that the builders in this file
+# remain sorted. The script is invoked via presubmit, and will complain if this
+# file is change but the documentation isn't.
+#
+# The auto generated file copies comments made to builders in this file. If you
+# comment on the line directly above a builder, that comment will get copied to
+# the documentation.
+#
+# The following comment will get copied.
+#
+# # This is a great builder!
+# builders { name: "chromium_presubmit" }
+#
+# The following comment will not get copied.
+#
+# # This is a ok builder!
+#
+# builders { name: "chromium_presubmit" }
+
 version: 1
 cq_status_url: "https://chromium-cq-status.appspot.com"
 git_repo_url: "https://chromium.googlesource.com/chromium/src"
@@ -30,13 +51,50 @@
   try_job {
     buckets {
       name: "luci.chromium.try"
-      builders { name: "android_arm64_dbg_recipe" }
+
+      #############################
+      # Always required builders. #
+      #############################
+
       builders { name: "android-binary-size" }
+      builders { name: "android-kitkat-arm-rel" }
+      builders { name: "android-marshmallow-arm64-rel" }
+      builders { name: "android_arm64_dbg_recipe" }
       builders { name: "android_clang_dbg_recipe" }
       builders { name: "android_compile_dbg" }
       builders { name: "android_cronet" }
-      builders { name: "android-kitkat-arm-rel" }
-      builders { name: "android-marshmallow-arm64-rel" }
+      builders { name: "cast_shell_android" }
+      builders { name: "cast_shell_linux" }
+      builders { name: "chromeos-amd64-generic-rel" }
+      builders { name: "chromeos-daisy-rel" }
+      builders {
+        name: "chromium_presubmit"
+        # Presubmit builder should be re-run every time CQ is triggered
+        # for last minute lint, OWNERS, etc checks.
+        disable_reuse: true
+      }
+      builders { name: "fuchsia_arm64" }
+      builders { name: "fuchsia_x64" }
+      builders { name: "ios-simulator" }
+      builders { name: "linux-chromeos-rel" }
+      builders { name: "linux-jumbo-rel" }
+      builders { name: "linux-libfuzzer-asan-rel" }
+      builders { name: "linux-ozone-rel" }
+      builders { name: "linux_chromium_asan_rel_ng" }
+      builders { name: "linux_chromium_compile_dbg_ng" }
+      builders { name: "linux_chromium_headless_rel" }
+      builders { name: "linux_chromium_rel_ng" }
+      builders { name: "linux_chromium_tsan_rel_ng" }
+      builders { name: "mac_chromium_compile_dbg_ng" }
+      builders { name: "mac_chromium_rel_ng" }
+      builders { name: "win10_chromium_x64_rel_ng" }
+      builders { name: "win7_chromium_rel_ng"}
+      builders { name: "win_chromium_compile_dbg_ng" }
+
+      ######################
+      # Optional builders. #
+      ######################
+
       builders {
         name: "android_optional_gpu_tests_rel"
         path_regexp: "cc/.+"
@@ -50,35 +108,12 @@
         path_regexp: "third_party/blink/renderer/modules/webgl/.+"
         path_regexp: "ui/gl/.+"
       }
-      builders { name: "cast_shell_android" }
-      builders { name: "cast_shell_linux" }
-      builders { name: "chromeos-amd64-generic-rel" }
-      builders { name: "chromeos-daisy-rel" }
-      builders {
-        name: "chromium_presubmit"
-        # Presubmit builder should be re-run every time CQ is triggered
-        # for last minute lint, OWNERS, etc checks.
-        disable_reuse: true
-      }
       builders {
         name: "closure_compilation"
         path_regexp: "components/offline_pages/resources/.+"
         path_regexp: "third_party/closure_compiler/.+"
         path_regexp: "third_party/polymer/.+"
       }
-      builders { name: "fuchsia_arm64" }
-      builders { name: "fuchsia_x64" }
-      # https://crbug.com/739556; make this non-experimental ASAP.
-      builders {
-        name: "ios-device"
-        experiment_percentage: 10
-      }
-      # https://crbug.com/739556
-      builders {
-        name: "ios-device-xcode-clang"
-        experiment_percentage: 10
-      }
-      builders { name: "ios-simulator" }
       builders {
         name: "ios-simulator-cronet"
         path_regexp: "components/cronet/.+"
@@ -90,41 +125,15 @@
         name: "ios-simulator-full-configs"
         path_regexp: "ios/.+"
       }
-      # https://crbug.com/739556
-      builders {
-        name: "ios-simulator-xcode-clang"
-        experiment_percentage: 10
-      }
       builders {
         name: "linux-blink-gen-property-trees"
         path_regexp: "third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees"
         path_regexp: "third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/.+"
       }
-      builders { name: "linux-chromeos-rel" }
-      builders {
-        name: "linux-chromeos-compile-dbg"
-        experiment_percentage: 50
-      }
-      builders { name: "linux_chromium_asan_rel_ng" }
-      builders { name: "linux_chromium_compile_dbg_ng" }
       builders {
         name: "linux_chromium_dbg_ng"
         path_regexp: "build/.*check_gn_headers.*"
       }
-      builders { name: "linux_chromium_headless_rel" }
-      builders { name: "linux_chromium_rel_ng" }
-      builders { name: "linux_chromium_tsan_rel_ng" }
-      # https://crbug.com/833482
-      builders {
-        name: "linux-dcheck-off-rel"
-        experiment_percentage: 10
-      }
-      # https://crbug.com/855319
-      builders {
-        name: "linux-goma-rbe-staging-rel"
-        experiment_percentage: 20
-      }
-      builders { name: "linux-jumbo-rel" }
       builders {
         name: "linux_layout_tests_layout_ng"
         path_regexp: "third_party/Webkit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG"
@@ -141,14 +150,12 @@
         path_regexp: "third_party/blink/renderer/core/(svg|paint)/.+"
         path_regexp: "third_party/blink/renderer/platform/graphics/.+"
       }
-      builders { name: "linux-libfuzzer-asan-rel" }
       builders {
         name: "linux_mojo"
         path_regexp: "services/network/.+"
         path_regexp: "testing/buildbot/filters/mojo\\.fyi\\.network_.*"
         path_regexp: "third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService"
       }
-      builders { name: "linux-ozone-rel" }
       builders {
         name: "linux_optional_gpu_tests_rel"
         path_regexp: "chrome/browser/vr/.+"
@@ -163,8 +170,6 @@
         name: "linux_vr"
         path_regexp: "chrome/browser/vr/.+"
       }
-      builders { name: "mac_chromium_compile_dbg_ng" }
-      builders { name: "mac_chromium_rel_ng" }
       builders {
         name: "mac_optional_gpu_tests_rel"
         path_regexp: "chrome/browser/vr/.+"
@@ -177,17 +182,6 @@
         path_regexp: "ui/gl/.+"
       }
       builders {
-        name: "win-libfuzzer-asan-rel"
-        experiment_percentage: 100
-      }
-      builders { name: "win10_chromium_x64_rel_ng" }
-      builders {
-        name: "win7_chromium_rel_loc_exp"
-        experiment_percentage: 20
-      }
-      builders { name: "win7_chromium_rel_ng"}
-      builders { name: "win_chromium_compile_dbg_ng" }
-      builders {
         name: "win_optional_gpu_tests_rel"
         path_regexp: "chrome/browser/vr/.+"
         path_regexp: "content/test/gpu/.+"
@@ -201,6 +195,48 @@
         path_regexp: "third_party/blink/renderer/platform/graphics/gpu/.+"
         path_regexp: "ui/gl/.+"
       }
+
+      ##########################
+      # Experimental builders. #
+      ##########################
+
+      # https://crbug.com/739556; make this non-experimental ASAP.
+      builders {
+        name: "ios-device"
+        experiment_percentage: 10
+      }
+      # https://crbug.com/739556
+      builders {
+        name: "ios-device-xcode-clang"
+        experiment_percentage: 10
+      }
+      # https://crbug.com/739556
+      builders {
+        name: "ios-simulator-xcode-clang"
+        experiment_percentage: 10
+      }
+      builders {
+        name: "linux-chromeos-compile-dbg"
+        experiment_percentage: 50
+      }
+      # https://crbug.com/833482
+      builders {
+        name: "linux-dcheck-off-rel"
+        experiment_percentage: 10
+      }
+      # https://crbug.com/855319
+      builders {
+        name: "linux-goma-rbe-staging-rel"
+        experiment_percentage: 20
+      }
+      builders {
+        name: "win-libfuzzer-asan-rel"
+        experiment_percentage: 100
+      }
+      builders {
+        name: "win7_chromium_rel_loc_exp"
+        experiment_percentage: 20
+      }
     }
 
     buckets {
diff --git a/infra/config/branch/cq_cfg_presubmit.py b/infra/config/branch/cq_cfg_presubmit.py
new file mode 100755
index 0000000..9d2fe21
--- /dev/null
+++ b/infra/config/branch/cq_cfg_presubmit.py
@@ -0,0 +1,310 @@
+#!/usr/bin/env python
+# Copyright (c) 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import difflib
+import os
+import string
+import sys
+
+
+# Path to the root of the current chromium checkout.
+CHROMIUM_DIR = os.path.abspath(os.path.join(
+    os.path.dirname(__file__), '..', '..', '..'))
+
+
+MD_HEADER = """# List of CQ builders
+
+This page is auto generated using the script
+//infra/config/branch/cq_config_presubmit.py. Do not manually edit.
+
+[TOC]
+
+Each builder name links to that builder on Milo. The "Backing builders" links
+point to the file used to determine which configurations a builder should copy
+when running. These links might 404 or error; they are hard-coded right now,
+using common assumptions about how builders are configured.
+"""
+
+
+REQUIRED_HEADER = """
+These builders must pass before a CL may land."""
+
+
+OPTIONAL_HEADER = """These builders optionally run, depending on the files in a
+CL. For example, a CL which touches `//gpu/BUILD.gn` would trigger the builder
+`android_optional_gpu_tests_rel`, due to the `path_regexp` values for that
+builder."""
+
+
+EXPERIMENTAL_HEADER = """
+These builders are run on some percentage of builds. Their results are ignored
+by CQ. These are often used to test new configurations before they are added
+as required builders."""
+
+
+BUILDER_VIEW_URL = (
+    'https://ci.chromium.org/p/chromium/builders/luci.chromium.try/')
+
+
+CODE_SEARCH_BASE = 'https://cs.chromium.org/'
+
+
+TRYBOT_SOURCE_URL = CODE_SEARCH_BASE + 'search/?q=file:trybots.py+'
+
+
+CQ_CONFIG_LOCATION_URL = (
+    CODE_SEARCH_BASE + 'search/?q=package:%5Echromium$+file:cq.cfg+')
+
+
+REGEX_SEARCH_URL = CODE_SEARCH_BASE + 'search/?q=package:%5Echromium$+'
+
+
+def parse_text_proto_message(lines):
+  """Parses a text proto. LOW QUALITY, MAY EASILY BREAK.
+
+  If you really need to parse text protos, use the actual python library for
+  protobufs. This exists because the .proto file for cq.cfg lives in another
+  repository.
+  """
+  data = {}
+
+  linenum = 0
+  # Tracks the current comment. Gets cleared if there's a blank line. Is added
+  # to submessages, to allow for builders to contain comments.
+  current_comment = None
+  while linenum < len(lines):
+    line = lines[linenum].strip()
+    if not line:
+      current_comment = None
+      linenum += 1
+    elif line.startswith('#'):
+      if current_comment:
+        current_comment += '\n' + line[1:]
+      else:
+        current_comment = line[1:]
+      linenum += 1
+    elif '{' in line:
+      # Sub message. Put before the ':' clause so that it correctly handles one
+      # line messages.
+      end = linenum
+      count = 0
+      newlines = []
+      while end < len(lines):
+        inner_line = lines[end]
+        if '{' in inner_line:
+          count += 1
+        if '}' in inner_line:
+          count -= 1
+
+        if end == linenum:
+          newline = inner_line.split('{', 1)[1]
+          if count == 0:
+            newline = newline.split('}')[0]
+          newlines.append(newline)
+        elif count == 0:
+          newlines.append(inner_line.split('}')[0])
+        else:
+          newlines.append(inner_line)
+        end += 1
+        if count == 0:
+          break
+      name = line.split('{')[0].strip()
+      value = parse_text_proto_message(newlines)
+      if current_comment:
+        value['comment'] = current_comment
+        current_comment = None
+
+      if name in data:
+        data[name].append(value)
+      else:
+        data[name] = [value]
+      linenum = end
+    elif ':' in line:
+      # It's a field
+      name, value = line.split(':', 1)
+      value = value.strip()
+      if value.startswith('"'):
+        value = value.strip('"')
+
+      if name in data:
+        data[name].append(value)
+      else:
+        data[name] = [value]
+      linenum += 1
+    else:
+      raise ValueError('Invalid line (number %d):\n%s' % (linenum, line))
+
+  return data
+
+
+class BuilderList(object):
+  def __init__(self, builders):
+    self.builders = builders
+
+  def sort(self):
+    """Sorts the builder list.
+
+    Sorts the builders in place. Orders them into three groups: experimental,
+    required, and optional."""
+    self.builders.sort(key=lambda b: '%s|%s|%s' % (
+        'z' if b.get('experiment_percentage') else 'a',
+        'z' if b.get('path_regexp') else 'a',
+        b['name']))
+
+  def by_section(self):
+    required = []
+    experimental = []
+    optional = []
+    for b in self.builders:
+      # Don't handle if something is both optional and experimental
+      if b.get('path_regexp'):
+        optional.append(b)
+      elif b.get('experiment_percentage'):
+        experimental.append(b)
+      else:
+        required.append(b)
+
+    return required, optional, experimental
+
+
+class CQConfig(object):
+  def __init__(self, lines):
+    self._value = parse_text_proto_message(lines)
+
+  @staticmethod
+  def from_file(path):
+    with open(path) as f:
+      lines = f.readlines()
+
+    return CQConfig(lines)
+
+  @property
+  def version(self):
+    return int(self._value['version'][0])
+
+  def builder_list(self, pred=None):
+    """Returns a list of builders.
+
+    pred is a predicate used to decide if a builder should be returned. It takes
+    the bucket and builder as arguments."""
+    items = []
+    for bucket in (
+        self._value['verifiers'][0]['try_job'][0]['buckets']):
+      for b in bucket['builders']:
+        if pred and not pred(bucket, b):
+          continue
+        items.append(b)
+    return BuilderList(items)
+
+  def get_markdown_doc(self):
+    lines = []
+    for l in MD_HEADER.split('\n'):
+      lines.append(l)
+
+    bl = self.builder_list()
+    req, opt, exp = bl.by_section()
+    for title, header, builders in (
+        ('Required builders', REQUIRED_HEADER, req),
+        ('Optional builders', OPTIONAL_HEADER, opt),
+        ('Experimental builders', EXPERIMENTAL_HEADER, exp),
+    ):
+      lines.append('## %s' % title)
+      lines.append('')
+      for l in header.strip().split('\n'):
+        lines.append(l)
+      lines.append('')
+      for b in builders:
+        lines.append(
+            '* [%s](%s) ([`cq.cfg` entry](%s)) ([matching builders](%s))' % (
+            b['name'][0], BUILDER_VIEW_URL + b['name'][0],
+            CQ_CONFIG_LOCATION_URL + b['name'][0],
+            TRYBOT_SOURCE_URL + b['name'][0],))
+        lines.append('')
+        if 'comment' in b:
+          for l in b['comment'].split('\n'):
+            lines.append('  ' + l.strip())
+          lines.append('')
+        if 'path_regexp' in b:
+          lines.append('  Path regular expressions:')
+          for regex in b['path_regexp']:
+            regex_title = '//' + regex.lstrip('/')
+            url = None
+            if regex.endswith('.+'):
+              regex = regex[:-len('.+')]
+              if all(
+                  # Equals sign and dashes used by layout tests.
+                  c in string.ascii_letters + string.digits + '/-_='
+                  for c in regex):
+                # Assume the regex is targeting a single path, direct link to
+                # it. Check to make sure we don't have weird characters, like
+                # ()|, which could mean it's a regex.
+                url = CODE_SEARCH_BASE + 'chromium/src/' + regex
+            lines.append('    * [`%s`](%s)' % (
+                regex_title, url or REGEX_SEARCH_URL + 'file:' + regex))
+          lines.append('')
+        if 'experiment_percentage' in b:
+          lines.append('  * Experimental percentage: %s' % (
+              b['experiment_percentage'][0]))
+          lines.append('')
+      lines.append('')
+
+    return '\n'.join(lines)
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+    '-c', '--check', action='store_true', help=
+    'Do consistency checks of cq.cfg and generated files. Used during'
+    'presubmit. Causes the tool to not generate any files.')
+  args = parser.parse_args()
+
+  exit_code = 0
+
+  cfg = CQConfig.from_file(os.path.join(
+      CHROMIUM_DIR, 'infra', 'config', 'branch', 'cq.cfg'))
+  if cfg.version != 1:
+    raise ValueError("Expected version 1, got %r" % cfg.version)
+
+  # Only force sorting on luci.chromium.try builders. Others should go away soon
+  # anyways...
+  bl = cfg.builder_list(lambda bucket, builder: bucket == 'luci.chromium.try')
+  names = [b['name'][0] for b in bl.builders]
+  bl.sort() # Changes the bl, so the next line is sorted.
+  sorted_names = [b['name'][0] for b in bl.builders]
+  if sorted_names != names:
+    print 'ERROR: cq.cfg is unsorted.',
+    if args.check:
+      print
+    else:
+      print ' Please sort as follows:'
+      for line in difflib.unified_diff(
+          names,
+          sorted_names, fromfile='current', tofile='sorted'):
+        print line
+    exit_code = 1
+
+  if args.check:
+    # TODO(martiniss): Add a check for path_regexp, to make sure they're valid
+    # paths.
+    with open(os.path.join(
+        CHROMIUM_DIR, 'docs', 'infra', 'cq_builders.md')) as f:
+      if cfg.get_markdown_doc() != f.read():
+        print (
+            'Markdown file is out of date. Please run '
+            '`//infra/config/branch/cq_cfg_presubmit.py to regenerate the '
+            'docs.')
+        exit_code = 1
+  else:
+    with open(os.path.join(
+        CHROMIUM_DIR, 'docs', 'infra', 'cq_builders.md'), 'w') as f:
+      f.write(cfg.get_markdown_doc())
+
+  return exit_code
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/infra/config/global/luci-scheduler-dev.cfg b/infra/config/global/luci-scheduler-dev.cfg
index aa26630..4616dd00 100644
--- a/infra/config/global/luci-scheduler-dev.cfg
+++ b/infra/config/global/luci-scheduler-dev.cfg
@@ -109,3 +109,21 @@
   # TODO(smut): Adjust frequency.
   schedule: "with 300s interval"
 }
+
+job {
+  # One trigger/schedule for each DUT pool to regularly flash.
+  id: "cros-dut-flash-scheduler-kevin"
+  acl_sets: "default"
+  # Run this early in MTV's morning (ie: UTC's late morning).
+  schedule: "0 10 * * * "
+  buildbucket: {
+    server: "cr-buildbucket-dev.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "cros-dut-flash-scheduler"
+    # All the properties needed to specify a single DUT pool.
+    properties: "swarming_server:chromium-swarm-dev.appspot.com"
+    properties: "swarming_pool:luci.chromium.ci"
+    properties: "device_type:kevin"
+    properties: "bb_host:cr-buildbucket-dev.appspot.com"
+  }
+}
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index 124e8eb..c75639d8 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -197,6 +197,7 @@
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/test/fakes:fakes",
+    "//ios/web/web_state:navigation_context",
     "//net",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/ios/chrome/browser/tabs/DEPS b/ios/chrome/browser/tabs/DEPS
index 6787c46..0b437c3d 100644
--- a/ios/chrome/browser/tabs/DEPS
+++ b/ios/chrome/browser/tabs/DEPS
@@ -7,6 +7,7 @@
   ],
   "^tab_unittest\.mm$": [
     "+ios/web/web_state/ui/crw_web_controller.h",
+    "+ios/web/web_state/navigation_context_impl.h",
     "+ios/web/navigation/navigation_manager_impl.h",
     "+ios/web/web_state/web_state_impl.h",
     "+ios/web/test/fakes/crw_fake_back_forward_list.h",
diff --git a/ios/chrome/browser/tabs/tab_unittest.mm b/ios/chrome/browser/tabs/tab_unittest.mm
index 67f6373a..b58b46d5 100644
--- a/ios/chrome/browser/tabs/tab_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_unittest.mm
@@ -48,6 +48,7 @@
 #import "ios/web/public/test/fakes/fake_navigation_context.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #import "ios/web/test/fakes/crw_fake_back_forward_list.h"
+#import "ios/web/web_state/navigation_context_impl.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/web_state_impl.h"
 #import "net/base/mac/url_conversions.h"
@@ -237,9 +238,13 @@
                 NSString* title) {
     DCHECK_EQ(tab_.webState, web_state_impl_.get());
 
-    web::FakeNavigationContext context1;
-    context1.SetUrl(user_url);
-    web_state_impl_->OnNavigationStarted(&context1);
+    std::unique_ptr<web::NavigationContextImpl> context1 =
+        web::NavigationContextImpl::CreateNavigationContext(
+            web_state_impl_.get(), user_url,
+            /*has_user_gesture=*/true,
+            ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK,
+            /*is_renderer_initiated=*/true);
+    web_state_impl_->OnNavigationStarted(context1.get());
 
     web::Referrer empty_referrer;
     web_state_impl_->GetNavigationManagerImpl().AddPendingItem(
@@ -247,9 +252,13 @@
         web::NavigationInitiationType::RENDERER_INITIATED,
         web::NavigationManager::UserAgentOverrideOption::INHERIT);
 
-    web::FakeNavigationContext context2;
-    context2.SetUrl(redirect_url);
-    web_state_impl_->OnNavigationStarted(&context2);
+    std::unique_ptr<web::NavigationContextImpl> context2 =
+        web::NavigationContextImpl::CreateNavigationContext(
+            web_state_impl_.get(), redirect_url,
+            /*has_user_gesture=*/true,
+            ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK,
+            /*is_renderer_initiated=*/true);
+    web_state_impl_->OnNavigationStarted(context2.get());
 
     if (GetParam() == NavigationManagerChoice::WK_BASED) {
       [fake_wk_list_
@@ -257,9 +266,9 @@
     }
     web_state_impl_->GetNavigationManagerImpl().CommitPendingItem();
 
-    context2.SetHasCommitted(true);
+    context2->SetHasCommitted(true);
     web_state_impl_->UpdateHttpResponseHeaders(redirect_url);
-    web_state_impl_->OnNavigationFinished(&context2);
+    web_state_impl_->OnNavigationFinished(context2.get());
     web_state_impl_->SetIsLoading(true);
 
     base::string16 new_title = base::SysNSStringToUTF16(title);
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn
index 332de7cf..2ce5acc 100644
--- a/ios/chrome/browser/ui/BUILD.gn
+++ b/ios/chrome/browser/ui/BUILD.gn
@@ -311,7 +311,6 @@
     "//ios/chrome/browser/ui/activity_services/requirements",
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/app_launcher",
-    "//ios/chrome/browser/ui/authentication/consent_bump",
     "//ios/chrome/browser/ui/autofill:autofill",
     "//ios/chrome/browser/ui/autofill/manual_fill",
     "//ios/chrome/browser/ui/bookmarks",
@@ -354,7 +353,6 @@
     "//ios/chrome/browser/ui/print",
     "//ios/chrome/browser/ui/qr_scanner:coordinator",
     "//ios/chrome/browser/ui/reading_list",
-    "//ios/chrome/browser/ui/recent_tabs",
     "//ios/chrome/browser/ui/sad_tab",
     "//ios/chrome/browser/ui/sad_tab:coordinator",
     "//ios/chrome/browser/ui/sad_tab:features",
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
index c84d0c8..a3edfa79 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
@@ -39,7 +39,7 @@
 
 }  // namespace
 
-static NSTimeInterval MFAnimationDuration = 0.20;
+static NSTimeInterval MFAnimationDuration = 0;
 
 @interface ManualFillAccessoryViewController ()
 
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 98917ab..eabe70e7 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -113,8 +113,6 @@
 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
 #import "ios/chrome/browser/ui/alert_coordinator/repost_form_coordinator.h"
 #import "ios/chrome/browser/ui/app_launcher/app_launcher_coordinator.h"
-#import "ios/chrome/browser/ui/authentication/consent_bump/consent_bump_coordinator.h"
-#import "ios/chrome/browser/ui/authentication/consent_bump/consent_bump_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h"
 #import "ios/chrome/browser/ui/background_generator.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.h"
@@ -175,9 +173,7 @@
 #import "ios/chrome/browser/ui/presenters/vertical_animation_container.h"
 #import "ios/chrome/browser/ui/print/print_controller.h"
 #import "ios/chrome/browser/ui/reading_list/offline_page_native_content.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notifier.h"
-#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
 #include "ios/chrome/browser/ui/sad_tab/features.h"
 #import "ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.h"
 #import "ios/chrome/browser/ui/sad_tab/sad_tab_legacy_coordinator.h"
@@ -415,7 +411,6 @@
 @interface BrowserViewController ()<ActivityServicePresentation,
                                     BubblePresenterDelegate,
                                     CaptivePortalDetectorTabHelperDelegate,
-                                    ConsentBumpCoordinatorDelegate,
                                     CRWNativeContentProvider,
                                     CRWWebStateDelegate,
                                     DialogPresenterDelegate,
@@ -479,9 +474,6 @@
   // Used to display the Voice Search UI.  Nil if not visible.
   scoped_refptr<VoiceSearchController> _voiceSearchController;
 
-  // Used to display the Reading List.
-  ChromeCoordinator* _readingListCoordinator;
-
   // Used to display the Find In Page UI. Nil if not visible.
   FindBarControllerIOS* _findBarController;
 
@@ -627,8 +619,6 @@
 // for the presentation of a new tab. Can be used to record performance metrics.
 @property(nonatomic, strong, nullable)
     ProceduralBlock foregroundTabWasAddedCompletionBlock;
-// Coordinator for Recent Tabs.
-@property(nonatomic, strong) ChromeCoordinator* recentTabsCoordinator;
 // Coordinator for tablet tab strip.
 @property(nonatomic, strong) TabStripLegacyCoordinator* tabStripCoordinator;
 // Coordinator for Infobars.
@@ -697,9 +687,6 @@
 // Whether the safe area insets should be used to adjust the viewport.
 @property(nonatomic, readonly) BOOL usesSafeInsetsForViewportAdjustments;
 
-// Coordinator to ask the user for the new consent.
-@property(nonatomic, strong) ConsentBumpCoordinator* consentBumpCoordinator;
-
 // BVC initialization
 // ------------------
 // If the BVC is initialized with a valid browser state & tab model immediately,
@@ -848,13 +835,6 @@
 // Adds the given url to the reading list.
 - (void)addToReadingListURL:(const GURL&)URL title:(NSString*)title;
 
-// Recent Tabs
-// ------------
-// Creates the right RecentTabs Coordinator, once we stop supporting the legacy
-// implementation we can delete this method and start the coordinator on
-// |showRecentTabs|.
-- (void)createRecentTabsCoordinator;
-
 @end
 
 @implementation BrowserViewController
@@ -871,7 +851,6 @@
 @synthesize activityOverlayCoordinator = _activityOverlayCoordinator;
 @synthesize foregroundTabWasAddedCompletionBlock =
     _foregroundTabWasAddedCompletionBlock;
-@synthesize recentTabsCoordinator = _recentTabsCoordinator;
 @synthesize tabStripCoordinator = _tabStripCoordinator;
 @synthesize tabStripView = _tabStripView;
 @synthesize popupMenuCoordinator = _popupMenuCoordinator;
@@ -895,7 +874,6 @@
 // DialogPresenterDelegate property
 @synthesize dialogPresenterDelegateIsPresenting =
     _dialogPresenterDelegateIsPresenting;
-@synthesize consentBumpCoordinator = _consentBumpCoordinator;
 
 #pragma mark - Object lifecycle
 
@@ -1780,8 +1758,6 @@
     self.typingShield = nil;
     if (_voiceSearchController)
       _voiceSearchController->SetDispatcher(nil);
-    _readingListCoordinator = nil;
-    self.recentTabsCoordinator = nil;
     self.primaryToolbarCoordinator = nil;
     self.secondaryToolbarContainerCoordinator = nil;
     self.secondaryToolbarCoordinator = nil;
@@ -3019,17 +2995,6 @@
                          IDS_IOS_READING_LIST_SNACKBAR_MESSAGE)];
 }
 
-#pragma mark - Private Methods: Recent Tabs
-
-- (void)createRecentTabsCoordinator {
-  RecentTabsCoordinator* recentTabsCoordinator =
-      [[RecentTabsCoordinator alloc] initWithBaseViewController:self
-                                                   browserState:_browserState];
-  recentTabsCoordinator.loader = self;
-  recentTabsCoordinator.dispatcher = self.dispatcher;
-  self.recentTabsCoordinator = recentTabsCoordinator;
-}
-
 #pragma mark - ** Protocol Implementations and Helpers **
 
 #pragma mark - BubblePresenterDelegate
@@ -4484,14 +4449,6 @@
   [self addToReadingListURL:[command URL] title:[command title]];
 }
 
-- (void)showReadingList {
-  _readingListCoordinator = [[ReadingListCoordinator alloc]
-      initWithBaseViewController:self
-                    browserState:self.browserState
-                          loader:self];
-  [_readingListCoordinator start];
-}
-
 - (void)preloadVoiceSearch {
   // Preload VoiceSearchController and views and view controllers needed
   // for voice search.
@@ -4614,24 +4571,6 @@
   [_bookmarkInteractionController presentBookmarks];
 }
 
-- (void)showRecentTabs {
-  // TODO(crbug.com/825431): If BVC's clearPresentedState is ever called (such
-  // as in tearDown after a failed egtest), then this coordinator is left in a
-  // started state even though its corresponding VC is no longer on screen.
-  // That causes issues when the coordinator is started again and we destroy the
-  // old mediator without disconnecting it first.  Temporarily work around these
-  // issues by not having a long lived coordinator.  A longer-term solution will
-  // require finding a way to stop this coordinator so that the mediator is
-  // properly disconnected and destroyed and does not live longer than its
-  // associated VC.
-  if (self.recentTabsCoordinator) {
-    [self.recentTabsCoordinator stop];
-    self.recentTabsCoordinator = nil;
-  }
-  [self createRecentTabsCoordinator];
-  [self.recentTabsCoordinator start];
-}
-
 - (void)requestDesktopSite {
   if (self.userAgentType != web::UserAgentType::MOBILE)
     return;
@@ -4694,24 +4633,6 @@
   }
 }
 
-- (void)showConsentBumpIfNeeded {
-  DCHECK(!self.consentBumpCoordinator);
-  if (![ConsentBumpCoordinator
-          shouldShowConsentBumpWithBrowserState:_browserState]) {
-    return;
-  }
-  self.consentBumpCoordinator =
-      [[ConsentBumpCoordinator alloc] initWithBaseViewController:self
-                                                    browserState:_browserState];
-  self.consentBumpCoordinator.delegate = self;
-  [self.consentBumpCoordinator start];
-  self.consentBumpCoordinator.viewController.modalPresentationStyle =
-      UIModalPresentationFormSheet;
-  [self presentViewController:self.consentBumpCoordinator.viewController
-                     animated:YES
-                   completion:nil];
-}
-
 - (void)focusFakebox {
   id nativeController = [self nativeControllerForTab:self.tabModel.currentTab];
   DCHECK([nativeController conformsToProtocol:@protocol(NewTabPageOwning)]);
@@ -5437,23 +5358,6 @@
   [self.dispatcher showSyncPassphraseSettingsFromViewController:self];
 }
 
-#pragma mark - ConsentBumpCoordinatorDelegate
-
-- (void)consentBumpCoordinator:(ConsentBumpCoordinator*)coordinator
-    didFinishNeedingToShowSettings:(BOOL)shouldShowSettings {
-  DCHECK(self.consentBumpCoordinator);
-  DCHECK(self.consentBumpCoordinator.viewController);
-  auto completion = ^{
-    if (shouldShowSettings) {
-      [self.dispatcher showGoogleServicesSettingsFromViewController:self];
-    }
-  };
-  [self.consentBumpCoordinator.viewController
-      dismissViewControllerAnimated:YES
-                         completion:completion];
-  self.consentBumpCoordinator = nil;
-}
-
 #pragma mark - NewTabPageTabHelperDelegate
 
 - (void)newTabPageHelperDidChangeVisibility:(NewTabPageTabHelper*)NTPHelper
diff --git a/ios/chrome/browser/ui/commands/BUILD.gn b/ios/chrome/browser/ui/commands/BUILD.gn
index 8076e435..1cc1670 100644
--- a/ios/chrome/browser/ui/commands/BUILD.gn
+++ b/ios/chrome/browser/ui/commands/BUILD.gn
@@ -8,6 +8,7 @@
     "activity_service_commands.h",
     "application_commands.h",
     "browser_commands.h",
+    "browser_coordinator_commands.h",
     "browsing_data_commands.h",
     "command_dispatcher.h",
     "command_dispatcher.mm",
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index bf10a63..141eaae2 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -8,6 +8,7 @@
 #import <Foundation/Foundation.h>
 
 #import "ios/chrome/browser/ui/commands/activity_service_commands.h"
+#import "ios/chrome/browser/ui/commands/browser_coordinator_commands.h"
 #import "ios/chrome/browser/ui/commands/page_info_commands.h"
 #import "ios/chrome/browser/ui/commands/popup_menu_commands.h"
 #import "ios/chrome/browser/ui/commands/qr_scanner_commands.h"
@@ -19,8 +20,11 @@
 
 // Protocol for commands that will generally be handled by the "current tab",
 // which in practice is the BrowserViewController instance displaying the tab.
+// TODO(crbug.com/906662) : Extract BrowserCoordinatorCommands from
+// BrowserCommands.
 @protocol BrowserCommands<NSObject,
                           ActivityServiceCommands,
+                          BrowserCoordinatorCommands,
                           PageInfoCommands,
                           PopupMenuCommands,
                           QRScannerCommands,
@@ -50,9 +54,6 @@
 // Adds a page to the reading list using data in |command|.
 - (void)addToReadingList:(ReadingListAddCommand*)command;
 
-// Shows the Reading List UI.
-- (void)showReadingList;
-
 // Preloads voice search on the current BVC.
 - (void)preloadVoiceSearch;
 
@@ -86,9 +87,6 @@
 // Shows the bookmarks manager.
 - (void)showBookmarksManager;
 
-// Shows recent tabs.
-- (void)showRecentTabs;
-
 // Requests the "desktop" version of the current page in the active tab.
 - (void)requestDesktopSite;
 
@@ -102,9 +100,6 @@
 // Prepares the browser to display a popup menu.
 - (void)prepareForPopupMenuPresentation:(PopupMenuCommandType)type;
 
-// Shows the consent bump if it is required.
-- (void)showConsentBumpIfNeeded;
-
 // Animates the NTP fakebox to the focused position and focuses the real
 // omnibox.
 - (void)focusFakebox;
diff --git a/ios/chrome/browser/ui/commands/browser_coordinator_commands.h b/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
new file mode 100644
index 0000000..a636167a70
--- /dev/null
+++ b/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_BROWSER_COORDINATOR_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_COMMANDS_BROWSER_COORDINATOR_COMMANDS_H_
+
+#import <Foundation/Foundation.h>
+
+// Protocol for commands that will be handled by the BrowserCoordinator.
+// TODO(crbug.com/906662) : Rename this protocol to one that is more descriptive
+// and representative of the contents.
+@protocol BrowserCoordinatorCommands
+
+// Shows the consent bump if it is required.
+- (void)showConsentBumpIfNeeded;
+
+// Shows the Reading List UI.
+- (void)showReadingList;
+
+// Shows recent tabs.
+- (void)showRecentTabs;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_COMMANDS_BROWSER_COORDINATOR_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index 7327f287..7ca2843 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -27,10 +27,13 @@
     "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/tabs:tabs_internal",
+    "//ios/chrome/browser/ui/authentication/consent_bump",
     "//ios/chrome/browser/ui/autofill",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/qr_scanner:coordinator",
+    "//ios/chrome/browser/ui/reading_list",
+    "//ios/chrome/browser/ui/recent_tabs",
     "//ios/chrome/browser/ui/snackbar",
     "//ios/public/provider/chrome/browser",
   ]
diff --git a/ios/chrome/browser/ui/main/browser_coordinator.mm b/ios/chrome/browser/ui/main/browser_coordinator.mm
index 272aaf7c..07902c8 100644
--- a/ios/chrome/browser/ui/main/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/main/browser_coordinator.mm
@@ -4,19 +4,25 @@
 
 #import "ios/chrome/browser/ui/main/browser_coordinator.h"
 
+#import "ios/chrome/browser/ui/authentication/consent_bump/consent_bump_coordinator.h"
+#import "ios/chrome/browser/ui/authentication/consent_bump/consent_bump_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
 #import "ios/chrome/browser/ui/browser_view_controller_dependency_factory.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
+#import "ios/chrome/browser/ui/commands/browser_coordinator_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
+#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
 #import "ios/chrome/browser/ui/snackbar/snackbar_coordinator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface BrowserCoordinator ()<FormInputAccessoryCoordinatorDelegate>
+@interface BrowserCoordinator ()<ConsentBumpCoordinatorDelegate,
+                                 FormInputAccessoryCoordinatorDelegate>
 
 // Handles command dispatching.
 @property(nonatomic, strong) CommandDispatcher* dispatcher;
@@ -25,25 +31,37 @@
 // Child Coordinators, listed in alphabetical order.
 // =================================================
 
+// Coordinator to ask the user for the new consent.
+@property(nonatomic, strong) ConsentBumpCoordinator* consentBumpCoordinator;
+
 // Coordinator in charge of the presenting autofill options above the
 // keyboard.
 @property(nonatomic, strong)
     FormInputAccessoryCoordinator* formInputAccessoryCoordinator;
 
-// Coordinator for displaying snackbars.
-@property(nonatomic, strong) SnackbarCoordinator* snackbarCoordinator;
-
 // Coordinator for the QR scanner.
 @property(nonatomic, strong) QRScannerLegacyCoordinator* qrScannerCoordinator;
 
+// Coordinator for displaying the Reading List.
+@property(nonatomic, strong) ReadingListCoordinator* readingListCoordinator;
+
+// Coordinator for Recent Tabs.
+@property(nonatomic, strong) RecentTabsCoordinator* recentTabsCoordinator;
+
+// Coordinator for displaying snackbars.
+@property(nonatomic, strong) SnackbarCoordinator* snackbarCoordinator;
+
 @end
 
 @implementation BrowserCoordinator
 @synthesize dispatcher = _dispatcher;
-// Private child coordinators
+// Child coordinators
+@synthesize consentBumpCoordinator = _consentBumpCoordinator;
 @synthesize formInputAccessoryCoordinator = _formInputAccessoryCoordinator;
-@synthesize snackbarCoordinator = _snackbarCoordinator;
 @synthesize qrScannerCoordinator = _qrScannerCoordinator;
+@synthesize readingListCoordinator = _readingListCoordinator;
+@synthesize recentTabsCoordinator = _recentTabsCoordinator;
+@synthesize snackbarCoordinator = _snackbarCoordinator;
 
 #pragma mark - ChromeCoordinator
 
@@ -53,11 +71,15 @@
   self.dispatcher = [[CommandDispatcher alloc] init];
   [self createViewController];
   [self startChildCoordinators];
+  [self.dispatcher
+      startDispatchingToTarget:self
+                   forProtocol:@protocol(BrowserCoordinatorCommands)];
   [super start];
 }
 
 - (void)stop {
   [super stop];
+  [self.dispatcher stopDispatchingToTarget:self];
   [self stopChildCoordinators];
   [self destroyViewController];
   self.dispatcher = nil;
@@ -91,6 +113,9 @@
   // Dispatcher should be instantiated so that it can be passed to child
   // coordinators.
   DCHECK(self.dispatcher);
+
+  /* ConsentBumpCoordinator is created and started by a BrowserCommand */
+
   self.formInputAccessoryCoordinator = [[FormInputAccessoryCoordinator alloc]
       initWithBaseViewController:self.viewController
                     browserState:self.browserState
@@ -98,25 +123,109 @@
   self.formInputAccessoryCoordinator.delegate = self;
   [self.formInputAccessoryCoordinator start];
 
-  self.snackbarCoordinator = [[SnackbarCoordinator alloc] init];
-  self.snackbarCoordinator.dispatcher = self.dispatcher;
-  [self.snackbarCoordinator start];
-
   self.qrScannerCoordinator = [[QRScannerLegacyCoordinator alloc]
       initWithBaseViewController:self.viewController];
   self.qrScannerCoordinator.dispatcher = self.dispatcher;
+
+  /* ReadingListCoordinator is created and started by a BrowserCommand */
+
+  /* RecentTabsCoordinator is created and started by a BrowserCommand */
+
+  self.snackbarCoordinator = [[SnackbarCoordinator alloc] init];
+  self.snackbarCoordinator.dispatcher = self.dispatcher;
+  [self.snackbarCoordinator start];
 }
 
 // Stops child coordinators.
 - (void)stopChildCoordinators {
+  [self.consentBumpCoordinator stop];
+  self.consentBumpCoordinator = nil;
+
   [self.formInputAccessoryCoordinator stop];
   self.formInputAccessoryCoordinator = nil;
 
-  [self.snackbarCoordinator stop];
-  self.snackbarCoordinator = nil;
-
   [self.qrScannerCoordinator stop];
   self.qrScannerCoordinator = nil;
+
+  [self.readingListCoordinator stop];
+  self.readingListCoordinator = nil;
+
+  [self.recentTabsCoordinator stop];
+  self.recentTabsCoordinator = nil;
+
+  [self.snackbarCoordinator stop];
+  self.snackbarCoordinator = nil;
+}
+
+#pragma mark - BrowserCoordinatorCommands
+
+- (void)showConsentBumpIfNeeded {
+  DCHECK(!self.consentBumpCoordinator);
+  if (![ConsentBumpCoordinator
+          shouldShowConsentBumpWithBrowserState:self.browserState]) {
+    return;
+  }
+  self.consentBumpCoordinator = [[ConsentBumpCoordinator alloc]
+      initWithBaseViewController:self.viewController
+                    browserState:self.browserState];
+  self.consentBumpCoordinator.delegate = self;
+  [self.consentBumpCoordinator start];
+  self.consentBumpCoordinator.viewController.modalPresentationStyle =
+      UIModalPresentationFormSheet;
+  [self.viewController
+      presentViewController:self.consentBumpCoordinator.viewController
+                   animated:YES
+                 completion:nil];
+}
+
+- (void)showReadingList {
+  self.readingListCoordinator = [[ReadingListCoordinator alloc]
+      initWithBaseViewController:self.viewController
+                    browserState:self.browserState
+                          loader:self.viewController];
+  [self.readingListCoordinator start];
+}
+
+- (void)showRecentTabs {
+  // TODO(crbug.com/825431): If BVC's clearPresentedState is ever called (such
+  // as in tearDown after a failed egtest), then this coordinator is left in a
+  // started state even though its corresponding VC is no longer on screen.
+  // That causes issues when the coordinator is started again and we destroy the
+  // old mediator without disconnecting it first.  Temporarily work around these
+  // issues by not having a long lived coordinator.  A longer-term solution will
+  // require finding a way to stop this coordinator so that the mediator is
+  // properly disconnected and destroyed and does not live longer than its
+  // associated VC.
+  if (self.recentTabsCoordinator) {
+    [self.recentTabsCoordinator stop];
+    self.recentTabsCoordinator = nil;
+  }
+
+  self.recentTabsCoordinator = [[RecentTabsCoordinator alloc]
+      initWithBaseViewController:self.viewController
+                    browserState:self.browserState];
+  self.recentTabsCoordinator.loader = self.viewController;
+  self.recentTabsCoordinator.dispatcher = self.applicationCommandHandler;
+  [self.recentTabsCoordinator start];
+}
+
+#pragma mark - ConsentBumpCoordinatorDelegate
+
+- (void)consentBumpCoordinator:(ConsentBumpCoordinator*)coordinator
+    didFinishNeedingToShowSettings:(BOOL)shouldShowSettings {
+  DCHECK(self.consentBumpCoordinator);
+  DCHECK(self.consentBumpCoordinator.viewController);
+  auto completion = ^{
+    if (shouldShowSettings) {
+      [self.applicationCommandHandler
+          showGoogleServicesSettingsFromViewController:self.viewController];
+    }
+  };
+  [self.consentBumpCoordinator.viewController
+      dismissViewControllerAnimated:YES
+                         completion:completion];
+  [self.consentBumpCoordinator stop];
+  self.consentBumpCoordinator = nil;
 }
 
 #pragma mark - FormInputAccessoryCoordinatorDelegate
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h b/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h
index 5d39548..4fc4d5ca 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h
@@ -10,13 +10,12 @@
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 
 @protocol ApplicationCommands;
-@protocol BrowserCommands;
 @protocol UrlLoader;
 
 // Coordinator that presents Recent Tabs.
 @interface RecentTabsCoordinator : ChromeCoordinator
 // The dispatcher for this Coordinator.
-@property(nonatomic, weak) id<ApplicationCommands, BrowserCommands> dispatcher;
+@property(nonatomic, weak) id<ApplicationCommands> dispatcher;
 // URL loader being managed by this Coordinator.
 @property(nonatomic, weak) id<UrlLoader> loader;
 @end
diff --git a/ios/chrome/browser/ui/sad_tab/features.cc b/ios/chrome/browser/ui/sad_tab/features.cc
index 88251fe0..ac5cc11c 100644
--- a/ios/chrome/browser/ui/sad_tab/features.cc
+++ b/ios/chrome/browser/ui/sad_tab/features.cc
@@ -5,4 +5,4 @@
 #include "ios/chrome/browser/ui/sad_tab/features.h"
 
 const base::Feature kPresentSadTabInViewController{
-    "PresentSadTabInViewController", base::FEATURE_DISABLED_BY_DEFAULT};
+    "PresentSadTabInViewController", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/settings/google_services_settings_egtest.mm b/ios/chrome/browser/ui/settings/google_services_settings_egtest.mm
index dd09d54..9bb32d2 100644
--- a/ios/chrome/browser/ui/settings/google_services_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services_settings_egtest.mm
@@ -147,6 +147,11 @@
 // Tests that "Activity and Interactions" switch should be disabled when the
 // "History" sync is off.
 - (void)testActivityAndInteractionsDisabledWithHistoryDisabled {
+// TODO(crbug.com/906680): Re-enable this test when it's fixed.
+#if TARGET_IPHONE_SIMULATOR
+  EARL_GREY_TEST_DISABLED(@"Test disabled on simulators.");
+#endif
+
   [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]];
   [self resetUnifiedConsent];
   [self openGoogleServicesSettings];
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
index 1f9c43aa..40e150d9 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
@@ -78,8 +78,13 @@
 - (void)setScrollProgressForTabletOmnibox:(CGFloat)progress {
   [super setScrollProgressForTabletOmnibox:progress];
 
-  self.view.locationBarBottomConstraint.constant =
-      [self verticalMarginForLocationBarForFullscreenProgress:1] * progress;
+  if (progress == 1) {
+    self.view.locationBarContainer.transform = CGAffineTransformIdentity;
+  } else {
+    self.view.locationBarContainer.transform = CGAffineTransformMakeTranslation(
+        0, [self verticalMarginForLocationBarForFullscreenProgress:1] *
+               (progress - 1));
+  }
   self.view.locationBarContainer.alpha = progress;
 
   // When the locationBarContainer is hidden, show the |fakeOmniboxTarget|.
diff --git a/ios/web/web_state/BUILD.gn b/ios/web/web_state/BUILD.gn
index ccf460c..3074ddb0 100644
--- a/ios/web/web_state/BUILD.gn
+++ b/ios/web/web_state/BUILD.gn
@@ -58,6 +58,7 @@
 source_set("navigation_context") {
   deps = [
     "//base",
+    "//ios/web/navigation:core",
     "//ios/web/public",
   ]
 
diff --git a/ios/web/web_state/navigation_context_impl.h b/ios/web/web_state/navigation_context_impl.h
index f70f4e92..b815cdca 100644
--- a/ios/web/web_state/navigation_context_impl.h
+++ b/ios/web/web_state/navigation_context_impl.h
@@ -85,6 +85,10 @@
   bool IsNativeContentPresented() const;
   void SetIsNativeContentPresented(bool is_native_content_presented);
 
+  // true if this navigation context is a placeholder navigation.
+  bool IsPlaceholderNavigation() const;
+  void SetPlaceholderNavigation(bool flag);
+
  private:
   NavigationContextImpl(WebState* web_state,
                         const GURL& url,
@@ -109,6 +113,7 @@
   bool is_loading_error_page_ = false;
   bool is_loading_html_string_ = false;
   bool is_native_content_presented_ = false;
+  bool is_placeholder_navigation_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(NavigationContextImpl);
 };
diff --git a/ios/web/web_state/navigation_context_impl.mm b/ios/web/web_state/navigation_context_impl.mm
index ad1f0f7d..7a5895d4 100644
--- a/ios/web/web_state/navigation_context_impl.mm
+++ b/ios/web/web_state/navigation_context_impl.mm
@@ -175,6 +175,14 @@
   is_native_content_presented_ = is_native_content_presented;
 }
 
+bool NavigationContextImpl::IsPlaceholderNavigation() const {
+  return is_placeholder_navigation_;
+}
+
+void NavigationContextImpl::SetPlaceholderNavigation(bool flag) {
+  is_placeholder_navigation_ = flag;
+}
+
 NavigationContextImpl::NavigationContextImpl(WebState* web_state,
                                              const GURL& url,
                                              bool has_user_gesture,
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index e41cb30..7c89e06 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -472,7 +472,7 @@
 // information about the navigation that triggered the document/URL change.
 // TODO(stuartmorgan): The code conflates URL changes and document object
 // changes; the two need to be separated and handled differently.
-- (void)webPageChangedWithContext:(const web::NavigationContext*)context;
+- (void)webPageChangedWithContext:(const web::NavigationContextImpl*)context;
 // Resets any state that is associated with a specific document object (e.g.,
 // page interaction tracking).
 - (void)resetDocumentSpecificState;
@@ -637,7 +637,8 @@
 - (std::unique_ptr<web::NavigationContextImpl>)
 registerLoadRequestForURL:(const GURL&)URL
    sameDocumentNavigation:(BOOL)sameDocumentNavigation
-           hasUserGesture:(BOOL)hasUserGesture;
+           hasUserGesture:(BOOL)hasUserGesture
+    placeholderNavigation:(BOOL)placeholderNavigation;
 // Prepares web controller and delegates for anticipated page change.
 // Allows several methods to invoke webWill/DidAddPendingURL on anticipated page
 // change, using the same cached request and calculated transition types.
@@ -647,7 +648,8 @@
                  referrer:(const web::Referrer&)referrer
                transition:(ui::PageTransition)transition
    sameDocumentNavigation:(BOOL)sameDocumentNavigation
-           hasUserGesture:(BOOL)hasUserGesture;
+           hasUserGesture:(BOOL)hasUserGesture
+    placeholderNavigation:(BOOL)placeholderNavigation;
 // Maps WKNavigationType to ui::PageTransition.
 - (ui::PageTransition)pageTransitionFromNavigationType:
     (WKNavigationType)navigationType;
@@ -1369,7 +1371,8 @@
 - (std::unique_ptr<web::NavigationContextImpl>)
 registerLoadRequestForURL:(const GURL&)URL
    sameDocumentNavigation:(BOOL)sameDocumentNavigation
-           hasUserGesture:(BOOL)hasUserGesture {
+           hasUserGesture:(BOOL)hasUserGesture
+    placeholderNavigation:(BOOL)placeholderNavigation {
   // Get the navigation type from the last main frame load request, and try to
   // map that to a PageTransition.
   WKNavigationType navigationType =
@@ -1384,7 +1387,8 @@
                              referrer:emptyReferrer
                            transition:transition
                sameDocumentNavigation:sameDocumentNavigation
-                       hasUserGesture:(BOOL)hasUserGesture];
+                       hasUserGesture:hasUserGesture
+                placeholderNavigation:placeholderNavigation];
   context->SetWKNavigationType(navigationType);
   return context;
 }
@@ -1394,7 +1398,8 @@
                  referrer:(const web::Referrer&)referrer
                transition:(ui::PageTransition)transition
    sameDocumentNavigation:(BOOL)sameDocumentNavigation
-           hasUserGesture:(BOOL)hasUserGesture {
+           hasUserGesture:(BOOL)hasUserGesture
+    placeholderNavigation:(BOOL)placeholderNavigation {
   // Transfer time is registered so that further transitions within the time
   // envelope are not also registered as links.
   _lastTransferTimeInSeconds = CFAbsoluteTimeGetCurrent();
@@ -1454,6 +1459,7 @@
       web::NavigationContextImpl::CreateNavigationContext(
           _webStateImpl, requestURL, hasUserGesture, transition,
           isRendererInitiated);
+  context->SetPlaceholderNavigation(placeholderNavigation);
 
   // TODO(crbug.com/676129): LegacyNavigationManagerImpl::AddPendingItem does
   // not create a pending item in case of reload. Remove this workaround once
@@ -1794,8 +1800,9 @@
   // load for a provisional load failure. Rewrite the context URL to actual URL
   // so the navigation event is broadcasted.
   // TODO(crbug.com/803503) Clean up callbcks for native error.
-  if (IsPlaceholderUrl(context->GetUrl())) {
+  if (context->IsPlaceholderNavigation()) {
     context->SetUrl(item->GetURL());
+    context->SetPlaceholderNavigation(false);
   }
   [self loadNativeViewWithSuccess:NO navigationContext:context];
   _webStateImpl->SetIsLoading(false);
@@ -1848,7 +1855,8 @@
                              referrer:referrer
                            transition:self.currentTransition
                sameDocumentNavigation:NO
-                       hasUserGesture:YES];
+                       hasUserGesture:YES
+                placeholderNavigation:NO];
   [self loadNativeViewWithSuccess:YES
                 navigationContext:navigationContext.get()];
   _loadPhase = web::PAGE_LOADED;
@@ -1870,7 +1878,8 @@
   std::unique_ptr<web::NavigationContextImpl> navigationContext =
       [self registerLoadRequestForURL:placeholderURL
                sameDocumentNavigation:NO
-                       hasUserGesture:NO];
+                       hasUserGesture:NO
+                placeholderNavigation:YES];
   [_navigationStates setContext:std::move(navigationContext)
                   forNavigation:navigation];
   return [_navigationStates contextForNavigation:navigation];
@@ -1903,7 +1912,8 @@
       std::unique_ptr<web::NavigationContextImpl> navigationContext =
           [self registerLoadRequestForURL:item->GetURL()
                    sameDocumentNavigation:NO
-                           hasUserGesture:NO];
+                           hasUserGesture:NO
+                    placeholderNavigation:NO];
       WKNavigation* navigation =
           [_webView loadHTMLString:@""
                            baseURL:net::NSURLWithGURL(item->GetURL())];
@@ -2030,7 +2040,8 @@
                          referrer:self.currentNavItemReferrer
                        transition:ui::PageTransition::PAGE_TRANSITION_RELOAD
            sameDocumentNavigation:NO
-                   hasUserGesture:YES];
+                   hasUserGesture:YES
+            placeholderNavigation:NO];
     navigationContext->SetIsRendererInitiated(isRendererInitiated);
     _webStateImpl->OnNavigationStarted(navigationContext.get());
     [self didStartLoading];
@@ -2067,7 +2078,8 @@
                              referrer:self.currentNavItemReferrer
                            transition:ui::PageTransition::PAGE_TRANSITION_RELOAD
                sameDocumentNavigation:NO
-                       hasUserGesture:YES];
+                       hasUserGesture:YES
+                placeholderNavigation:NO];
         [_navigationStates setContext:std::move(navigationContext)
                         forNavigation:navigation];
       } else {
@@ -2195,7 +2207,10 @@
   // placeholder URLs because this may be the only opportunity to update
   // |isLoading| for native view reload.
 
-  if (context && IsWKInternalUrl(context->GetUrl()))
+  if (context && context->IsPlaceholderNavigation())
+    return;
+
+  if (context && IsRestoreSessionUrl(context->GetUrl()))
     return;
 
   if (IsRestoreSessionUrl(net::GURLWithNSURL(_webView.URL)))
@@ -2912,7 +2927,7 @@
 // TODO(stuartmorgan): This method conflates document changes and URL changes;
 // we should be distinguishing better, and be clear about the expected
 // WebDelegate and WCO callbacks in each case.
-- (void)webPageChangedWithContext:(const web::NavigationContext*)context {
+- (void)webPageChangedWithContext:(const web::NavigationContextImpl*)context {
   DCHECK_EQ(_loadPhase, web::LOAD_REQUESTED);
 
   web::Referrer referrer = [self currentReferrer];
@@ -2931,7 +2946,7 @@
   [self didStartLoading];
   // Do not commit pending item in the middle of loading a placeholder URL. The
   // item will be committed when the native content or webUI is displayed.
-  if (!IsPlaceholderUrl(context->GetUrl())) {
+  if (!context->IsPlaceholderNavigation()) {
     self.navigationManagerImpl->CommitPendingItem();
   }
 }
@@ -4174,7 +4189,8 @@
                                      referrer:web::Referrer()
                                    transition:loadHTMLTransition
                        sameDocumentNavigation:NO
-                               hasUserGesture:true];
+                               hasUserGesture:YES
+                        placeholderNavigation:NO];
   }
   context->SetIsRendererInitiated(false);
   context->SetLoadingHtmlString(true);
@@ -4370,10 +4386,11 @@
       action.navigationType == WKNavigationTypeBackForward) {
     // WKBackForwardList would have already been updated for back/forward
     // navigation. Create the pending item here to match.
-    std::unique_ptr<web::NavigationContextImpl> context = [self
-        registerLoadRequestForURL:net::GURLWithNSURL(action.request.URL)
-           sameDocumentNavigation:NO
-                   hasUserGesture:[_pendingNavigationInfo hasUserGesture]];
+    std::unique_ptr<web::NavigationContextImpl> context =
+        [self registerLoadRequestForURL:requestURL
+                 sameDocumentNavigation:NO
+                         hasUserGesture:[_pendingNavigationInfo hasUserGesture]
+                  placeholderNavigation:IsPlaceholderUrl(requestURL)];
     [_pendingNavigationInfo setPendingBackForwardContext:std::move(context)];
   }
 
@@ -4616,7 +4633,8 @@
       return;
     }
 
-    if (context->GetUrl() != webViewURL) {
+    if (context->GetUrl() != webViewURL &&
+        !context->IsPlaceholderNavigation()) {
       // Update last seen URL because it may be changed by WKWebView (f.e. by
       // performing characters escaping).
       web::NavigationItem* item = web::GetItemWithUniqueID(
@@ -4675,7 +4693,8 @@
   std::unique_ptr<web::NavigationContextImpl> navigationContext =
       [self registerLoadRequestForURL:webViewURL
                sameDocumentNavigation:NO
-                       hasUserGesture:[_pendingNavigationInfo hasUserGesture]];
+                       hasUserGesture:[_pendingNavigationInfo hasUserGesture]
+                placeholderNavigation:IsPlaceholderUrl(webViewURL)];
   _webStateImpl->OnNavigationStarted(navigationContext.get());
   [_navigationStates setContext:std::move(navigationContext)
                   forNavigation:navigation];
@@ -4824,7 +4843,8 @@
     // because redirect callback was not called.
     if (@available(iOS 12, *)) {
       // rdar://37547029 was fixed on iOS 12.
-    } else if (context && context->GetUrl() != webViewURL) {
+    } else if (context && !context->IsPlaceholderNavigation() &&
+               context->GetUrl() != webViewURL) {
       [self didReceiveRedirectForNavigation:context withURL:webViewURL];
     }
   }
@@ -4923,7 +4943,7 @@
   // Do not update the HTML5 history state or states of the last committed item
   // for placeholder page because the actual navigation item will not be
   // committed until the native content or WebUI is shown.
-  if (context && !IsPlaceholderUrl(context->GetUrl()) &&
+  if (context && !context->IsPlaceholderNavigation() &&
       !context->GetUrl().SchemeIs(url::kAboutScheme)) {
     [self updateSSLStatusForCurrentNavigationItem];
     [self updateHTML5HistoryState];
@@ -5286,7 +5306,8 @@
       std::unique_ptr<web::NavigationContextImpl> newContext =
           [self registerLoadRequestForURL:webViewURL
                    sameDocumentNavigation:isSameDocumentNavigation
-                           hasUserGesture:NO];
+                           hasUserGesture:NO
+                    placeholderNavigation:IsPlaceholderUrl(webViewURL)];
       [self webPageChangedWithContext:newContext.get()];
       newContext->SetHasCommitted(!isSameDocumentNavigation);
       _webStateImpl->OnNavigationFinished(newContext.get());
@@ -5533,7 +5554,8 @@
       //   4.) Back-forward same document navigation
       newNavigationContext = [self registerLoadRequestForURL:newURL
                                       sameDocumentNavigation:YES
-                                              hasUserGesture:NO];
+                                              hasUserGesture:NO
+                                       placeholderNavigation:NO];
 
       // Use the current title for items created by same document navigations.
       auto* pendingItem = self.navigationManagerImpl->GetPendingItem();
@@ -5653,7 +5675,8 @@
                                  referrer:self.currentNavItemReferrer
                                transition:self.currentTransition
                    sameDocumentNavigation:sameDocumentNavigation
-                           hasUserGesture:YES];
+                           hasUserGesture:YES
+                    placeholderNavigation:NO];
       WKNavigation* navigation = [self loadPOSTRequest:request];
       [_navigationStates setContext:std::move(navigationContext)
                       forNavigation:navigation];
@@ -5671,7 +5694,8 @@
                                referrer:self.currentNavItemReferrer
                              transition:self.currentTransition
                  sameDocumentNavigation:sameDocumentNavigation
-                         hasUserGesture:YES];
+                         hasUserGesture:YES
+                  placeholderNavigation:NO];
     navigationContext->SetIsRendererInitiated(false);
     WKNavigation* navigation = [self loadRequest:request];
     [_navigationStates setContext:std::move(navigationContext)
@@ -5714,7 +5738,8 @@
                                referrer:self.currentNavItemReferrer
                              transition:self.currentTransition
                  sameDocumentNavigation:sameDocumentNavigation
-                         hasUserGesture:YES];
+                         hasUserGesture:YES
+                  placeholderNavigation:NO];
     navigationContext->SetIsRendererInitiated(false);
     WKNavigation* navigation = nil;
     if (navigationURL == net::GURLWithNSURL([_webView URL])) {
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index 7e83c0a..6063ba8 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -46,7 +46,7 @@
 struct ContextMenuParams;
 struct FaviconURL;
 struct LoadCommittedDetails;
-class NavigationContext;
+class NavigationContextImpl;
 class NavigationManager;
 class SessionCertificatePolicyCacheImpl;
 class WebInterstitialImpl;
@@ -78,12 +78,12 @@
   void SetWebController(CRWWebController* web_controller);
 
   // Notifies the observers that a navigation has started.
-  void OnNavigationStarted(web::NavigationContext* context);
+  void OnNavigationStarted(web::NavigationContextImpl* context);
 
   // Notifies the observers that a navigation has finished. For same-document
   // navigations notifies the observers about favicon URLs update using
   // candidates received in OnFaviconUrlUpdated.
-  void OnNavigationFinished(web::NavigationContext* context);
+  void OnNavigationFinished(web::NavigationContextImpl* context);
 
   // Called when current window's canGoBack / canGoForward state was changed.
   void OnBackForwardStateChanged();
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index f983c67..eade297 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -735,23 +735,27 @@
                 }];
 }
 
-void WebStateImpl::OnNavigationStarted(web::NavigationContext* context) {
+void WebStateImpl::OnNavigationStarted(web::NavigationContextImpl* context) {
   // Navigation manager loads internal URLs to restore session history and
   // create back-forward entries for Native View and WebUI. Do not trigger
   // external callbacks.
-  if (wk_navigation_util::IsWKInternalUrl(context->GetUrl()))
+  if (context->IsPlaceholderNavigation() ||
+      wk_navigation_util::IsRestoreSessionUrl(context->GetUrl())) {
     return;
+  }
 
   for (auto& observer : observers_)
     observer.DidStartNavigation(this, context);
 }
 
-void WebStateImpl::OnNavigationFinished(web::NavigationContext* context) {
+void WebStateImpl::OnNavigationFinished(web::NavigationContextImpl* context) {
   // Navigation manager loads internal URLs to restore session history and
   // create back-forward entries for Native View and WebUI. Do not trigger
   // external callbacks.
-  if (wk_navigation_util::IsWKInternalUrl(context->GetUrl()))
+  if (context->IsPlaceholderNavigation() ||
+      wk_navigation_util::IsRestoreSessionUrl(context->GetUrl())) {
     return;
+  }
 
   for (auto& observer : observers_)
     observer.DidFinishNavigation(this, context);
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index 5746425..ad0ac21 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -415,7 +415,7 @@
   // Test that DidFinishNavigation() is called.
   ASSERT_FALSE(observer->did_finish_navigation_info());
   const GURL url("http://test");
-  std::unique_ptr<web::NavigationContext> context =
+  std::unique_ptr<NavigationContextImpl> context =
       NavigationContextImpl::CreateNavigationContext(
           web_state_.get(), url, /*has_user_gesture=*/true,
           ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK,
@@ -505,22 +505,27 @@
 // Tests that placeholder navigations are not visible to WebStateObservers.
 TEST_P(WebStateImplTest, PlaceholderNavigationNotExposedToObservers) {
   TestWebStateObserver observer(web_state_.get());
-  FakeNavigationContext context;
-  context.SetUrl(
-      wk_navigation_util::CreatePlaceholderUrlForUrl(GURL("chrome://newtab")));
-
+  GURL placeholder_url =
+      wk_navigation_util::CreatePlaceholderUrlForUrl(GURL("chrome://newtab"));
+  std::unique_ptr<NavigationContextImpl> context =
+      NavigationContextImpl::CreateNavigationContext(
+          web_state_.get(), placeholder_url,
+          /*has_user_gesture=*/true,
+          ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK,
+          /*is_renderer_initiated=*/true);
+  context->SetPlaceholderNavigation(true);
   // Test that OnPageLoaded() is not called.
-  web_state_->OnPageLoaded(context.GetUrl(), true /* load_success */);
+  web_state_->OnPageLoaded(placeholder_url, /*load_success=*/true);
   EXPECT_FALSE(observer.load_page_info());
-  web_state_->OnPageLoaded(context.GetUrl(), false /* load_success */);
+  web_state_->OnPageLoaded(placeholder_url, /*load_success=*/false);
   EXPECT_FALSE(observer.load_page_info());
 
   // Test that OnNavigationStarted() is not called.
-  web_state_->OnNavigationStarted(&context);
+  web_state_->OnNavigationStarted(context.get());
   EXPECT_FALSE(observer.did_start_navigation_info());
 
   // Test that OnNavigationFinished() is not called.
-  web_state_->OnNavigationFinished(&context);
+  web_state_->OnNavigationFinished(context.get());
   EXPECT_FALSE(observer.did_finish_navigation_info());
 }
 
@@ -666,8 +671,12 @@
 
   // Test that DidStartNavigation() is called.
   EXPECT_FALSE(observer->did_start_navigation_called());
-  FakeNavigationContext context;
-  web_state_->OnNavigationStarted(&context);
+  std::unique_ptr<NavigationContextImpl> context =
+      NavigationContextImpl::CreateNavigationContext(
+          web_state_.get(), GURL::EmptyGURL(), /*has_user_gesture=*/true,
+          ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK,
+          /*is_renderer_initiated=*/true);
+  web_state_->OnNavigationStarted(context.get());
   EXPECT_TRUE(observer->did_start_navigation_called());
 
   // Test that WebStateDidStartLoading() is called.
@@ -927,9 +936,13 @@
   auto observer = std::make_unique<TestWebStateObserver>(web_state_.get());
 
   // No callback if icons has not been fetched yet.
-  FakeNavigationContext context;
-  context.SetIsSameDocument(true);
-  web_state_->OnNavigationFinished(&context);
+  std::unique_ptr<NavigationContextImpl> context =
+      NavigationContextImpl::CreateNavigationContext(
+          web_state_.get(), GURL::EmptyGURL(),
+          /*has_user_gesture=*/false, ui::PageTransition::PAGE_TRANSITION_LINK,
+          /*is_renderer_initiated=*/false);
+  context->SetIsSameDocument(true);
+  web_state_->OnNavigationFinished(context.get());
   EXPECT_FALSE(observer->update_favicon_url_candidates_info());
 
   // Callback is called when icons were fetched.
@@ -942,7 +955,7 @@
 
   // Callback is now called after same-document navigation.
   observer = std::make_unique<TestWebStateObserver>(web_state_.get());
-  web_state_->OnNavigationFinished(&context);
+  web_state_->OnNavigationFinished(context.get());
   ASSERT_TRUE(observer->update_favicon_url_candidates_info());
   ASSERT_EQ(1U,
             observer->update_favicon_url_candidates_info()->candidates.size());
@@ -959,14 +972,14 @@
 
   // Document change navigation does not call callback.
   observer = std::make_unique<TestWebStateObserver>(web_state_.get());
-  context.SetIsSameDocument(false);
-  web_state_->OnNavigationFinished(&context);
+  context->SetIsSameDocument(false);
+  web_state_->OnNavigationFinished(context.get());
   EXPECT_FALSE(observer->update_favicon_url_candidates_info());
 
   // Previous candidates were invalidated by the document change. No callback
   // if icons has not been fetched yet.
-  context.SetIsSameDocument(true);
-  web_state_->OnNavigationFinished(&context);
+  context->SetIsSameDocument(true);
+  web_state_->OnNavigationFinished(context.get());
   EXPECT_FALSE(observer->update_favicon_url_candidates_info());
 }
 
diff --git a/net/base/host_port_pair.h b/net/base/host_port_pair.h
index a5ee6bd..1efb721e 100644
--- a/net/base/host_port_pair.h
+++ b/net/base/host_port_pair.h
@@ -40,6 +40,8 @@
     return std::tie(port_, host_) < std::tie(other.port_, other.host_);
   }
 
+  bool operator==(const HostPortPair& other) const { return Equals(other); }
+
   // Equality test of contents. (Probably another violation of style guide).
   bool Equals(const HostPortPair& other) const {
     return host_ == other.host_ && port_ == other.port_;
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index dc348add..bacb6f1 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -4,11 +4,11 @@
 
 #include "net/dns/host_cache.h"
 
-#include <utility>
+#include <algorithm>
 
-#include "base/logging.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/default_tick_clock.h"
 #include "base/trace_event/trace_event.h"
@@ -43,9 +43,18 @@
 const char kNetworkChangesKey[] = "network_changes";
 const char kErrorKey[] = "error";
 const char kAddressesKey[] = "addresses";
+const char kTextRecordsKey[] = "text_records";
+const char kHostnameResultsKey[] = "hostname_results";
+const char kHostPortsKey[] = "host_ports";
 
-bool AddressListFromListValue(const base::ListValue* value, AddressList* list) {
-  list->clear();
+bool AddressListFromListValue(const base::ListValue* value,
+                              base::Optional<AddressList>* out_list) {
+  if (!value) {
+    out_list->reset();
+    return true;
+  }
+
+  out_list->emplace();
   for (auto it = value->begin(); it != value->end(); it++) {
     IPAddress address;
     std::string addr_string;
@@ -53,7 +62,7 @@
         !address.AssignFromIPLiteral(addr_string)) {
       return false;
     }
-    list->push_back(IPEndPoint(address, 0));
+    out_list->value().push_back(IPEndPoint(address, 0));
   }
   return true;
 }
@@ -105,20 +114,6 @@
 HostCache::Key::Key()
     : Key("", DnsQueryType::UNSPECIFIED, 0, HostResolverSource::ANY) {}
 
-HostCache::Entry::Entry(int error,
-                        const AddressList& addresses,
-                        Source source,
-                        base::TimeDelta ttl)
-    : error_(error), addresses_(addresses), source_(source), ttl_(ttl) {
-  DCHECK(ttl >= base::TimeDelta());
-}
-
-HostCache::Entry::Entry(int error, const AddressList& addresses, Source source)
-    : error_(error),
-      addresses_(addresses),
-      source_(source),
-      ttl_(base::TimeDelta::FromSeconds(-1)) {}
-
 HostCache::Entry::~Entry() = default;
 
 HostCache::Entry::Entry(HostCache::Entry&& entry) = default;
@@ -129,6 +124,8 @@
                         int network_changes)
     : error_(entry.error()),
       addresses_(entry.addresses()),
+      text_records_(entry.text_records()),
+      hostnames_(entry.hostnames()),
       source_(entry.source()),
       ttl_(entry.ttl()),
       expires_(now + ttl),
@@ -137,12 +134,16 @@
       stale_hits_(0) {}
 
 HostCache::Entry::Entry(int error,
-                        const AddressList& addresses,
+                        const base::Optional<AddressList>& addresses,
+                        base::Optional<std::vector<std::string>>&& text_records,
+                        base::Optional<std::vector<HostPortPair>>&& hostnames,
                         Source source,
                         base::TimeTicks expires,
                         int network_changes)
     : error_(error),
       addresses_(addresses),
+      text_records_(std::move(text_records)),
+      hostnames_(std::move(hostnames)),
       source_(source),
       ttl_(base::TimeDelta::FromSeconds(-1)),
       expires_(expires),
@@ -246,21 +247,69 @@
   auto it = entries_.find(key);
   if (it != entries_.end()) {
     bool is_stale = it->second.IsStale(now, network_changes_);
-    AddressListDeltaType delta =
-        FindAddressListDeltaType(it->second.addresses(), entry.addresses());
-    RecordSet(is_stale ? SET_UPDATE_STALE : SET_UPDATE_VALID, now, &it->second,
-              entry, delta);
+
+    base::Optional<AddressListDeltaType> addresses_delta;
+    if (entry.addresses() || it->second.addresses()) {
+      if (entry.addresses() && it->second.addresses()) {
+        addresses_delta = FindAddressListDeltaType(
+            it->second.addresses().value(), entry.addresses().value());
+      } else {
+        addresses_delta = DELTA_DISJOINT;
+      }
+    }  // Else no addresses in old or new, so nullopt delta.
+
+    // For non-address results, delta is only considered for whole-list
+    // equality. The meaning of partial list equality varies too much depending
+    // on the context of a DNS record.
+    base::Optional<AddressListDeltaType> nonaddress_delta;
+    if (entry.text_records() || it->second.text_records() ||
+        entry.hostnames() || it->second.hostnames()) {
+      if (entry.text_records() == it->second.text_records() &&
+          entry.hostnames() == it->second.hostnames()) {
+        nonaddress_delta = DELTA_IDENTICAL;
+      } else if (entry.text_records() == it->second.text_records() ||
+                 entry.hostnames() == it->second.hostnames()) {
+        nonaddress_delta = DELTA_OVERLAP;
+      } else {
+        nonaddress_delta = DELTA_DISJOINT;
+      }
+    }  // Else no nonaddress results in old or new, so nullopt delta.
+
+    AddressListDeltaType overall_delta;
+    if (!addresses_delta && !nonaddress_delta) {
+      // No results in old or new is IDENTICAL.
+      overall_delta = DELTA_IDENTICAL;
+    } else if (!addresses_delta) {
+      overall_delta = nonaddress_delta.value();
+    } else if (!nonaddress_delta) {
+      overall_delta = addresses_delta.value();
+    } else if (addresses_delta == DELTA_DISJOINT &&
+               nonaddress_delta == DELTA_DISJOINT) {
+      overall_delta = DELTA_DISJOINT;
+    } else if (addresses_delta == DELTA_DISJOINT ||
+               nonaddress_delta == DELTA_DISJOINT) {
+      // If only some result types are DISJOINT, some match and we have OVERLAP.
+      overall_delta = DELTA_OVERLAP;
+    } else {
+      // No DISJOINT result types, so we have at least partial match.  Take the
+      // least matching amount (highest enum value).
+      overall_delta =
+          std::max(addresses_delta.value(), nonaddress_delta.value());
+    }
+
+    LogRecordSet(is_stale ? SET_UPDATE_STALE : SET_UPDATE_VALID, now,
+                 &it->second, entry, overall_delta);
     // TODO(juliatuttle): Remember some old metadata (hit count or frequency or
     // something like that) if it's useful for better eviction algorithms?
     result_changed =
-        entry.error() == OK &&
-        (it->second.error() != entry.error() || delta != DELTA_IDENTICAL);
+        entry.error() == OK && (it->second.error() != entry.error() ||
+                                overall_delta != DELTA_IDENTICAL);
     entries_.erase(it);
   } else {
     result_changed = true;
     if (size() == max_entries_)
       EvictOneEntry(now);
-    RecordSet(SET_INSERT, now, nullptr, entry, DELTA_DISJOINT);
+    LogRecordSet(SET_INSERT, now, nullptr, entry, DELTA_DISJOINT);
   }
 
   AddEntry(Key(key), Entry(entry, now, ttl, network_changes_));
@@ -367,12 +416,35 @@
     if (entry.error() != OK) {
       entry_dict->SetInteger(kErrorKey, entry.error());
     } else {
-      const AddressList& addresses = entry.addresses();
-      // Append all of the resolved addresses.
-      auto addresses_value = std::make_unique<base::ListValue>();
-      for (size_t i = 0; i < addresses.size(); ++i)
-        addresses_value->AppendString(addresses[i].ToStringWithoutPort());
-      entry_dict->SetList(kAddressesKey, std::move(addresses_value));
+      if (entry.addresses()) {
+        // Append all of the resolved addresses.
+        base::ListValue addresses_value;
+        for (const IPEndPoint& address : entry.addresses().value()) {
+          addresses_value.GetList().emplace_back(address.ToStringWithoutPort());
+        }
+        entry_dict->SetKey(kAddressesKey, std::move(addresses_value));
+      }
+
+      if (entry.text_records()) {
+        // Append all resolved text records.
+        base::ListValue text_list_value;
+        for (const std::string& text_record : entry.text_records().value()) {
+          text_list_value.GetList().emplace_back(text_record);
+        }
+        entry_dict->SetKey(kTextRecordsKey, std::move(text_list_value));
+      }
+
+      if (entry.hostnames()) {
+        // Append all the resolved hostnames.
+        base::ListValue hostnames_value;
+        base::ListValue host_ports_value;
+        for (const HostPortPair& hostname : entry.hostnames().value()) {
+          hostnames_value.GetList().emplace_back(hostname.host());
+          host_ports_value.GetList().emplace_back(hostname.port());
+        }
+        entry_dict->SetKey(kHostnameResultsKey, std::move(hostnames_value));
+        entry_dict->SetKey(kHostPortsKey, std::move(host_ports_value));
+      }
     }
 
     entry_list->Append(std::move(entry_dict));
@@ -386,15 +458,8 @@
       return false;
 
     std::string hostname;
-    int dns_query_type;
     HostResolverFlags flags;
-    int host_resolver_source;
-    int error = OK;
     std::string expiration;
-    base::ListValue empty_list;
-    const base::ListValue* addresses_value = &empty_list;
-    AddressList address_list;
-
     if (!entry_dict->GetString(kHostnameKey, &hostname) ||
         !entry_dict->GetInteger(kFlagsKey, &flags) ||
         !entry_dict->GetString(kExpirationKey, &expiration)) {
@@ -406,25 +471,39 @@
     // TODO(crbug.com/846423): Remove kAddressFamilyKey support after a enough
     // time has passed to minimize loss-of-persistence impact from backwards
     // incompatibility.
-    if (!entry_dict->GetInteger(kDnsQueryTypeKey, &dns_query_type)) {
+    int dns_query_type_val;
+    DnsQueryType dns_query_type;
+    if (entry_dict->GetInteger(kDnsQueryTypeKey, &dns_query_type_val)) {
+      dns_query_type = static_cast<DnsQueryType>(dns_query_type_val);
+    } else {
       int address_family;
       if (!entry_dict->GetInteger(kAddressFamilyKey, &address_family)) {
         return false;
       }
-      dns_query_type = static_cast<int>(AddressFamilyToDnsQueryType(
-          static_cast<AddressFamily>(address_family)));
+      dns_query_type = AddressFamilyToDnsQueryType(
+          static_cast<AddressFamily>(address_family));
     }
 
     // HostResolverSource is optional.
+    int host_resolver_source;
     if (!entry_dict->GetInteger(kHostResolverSourceKey,
                                 &host_resolver_source)) {
       host_resolver_source = static_cast<int>(HostResolverSource::ANY);
     }
 
-    // Only one of these fields should be in the dictionary.
-    if (!entry_dict->GetInteger(kErrorKey, &error) &&
-        !entry_dict->GetList(kAddressesKey, &addresses_value)) {
-      return false;
+    int error = OK;
+    const base::ListValue* addresses_value = nullptr;
+    const base::ListValue* text_records_value = nullptr;
+    const base::ListValue* hostname_records_value = nullptr;
+    const base::ListValue* host_ports_value = nullptr;
+    if (!entry_dict->GetInteger(kErrorKey, &error)) {
+      entry_dict->GetList(kAddressesKey, &addresses_value);
+      entry_dict->GetList(kTextRecordsKey, &text_records_value);
+
+      if (entry_dict->GetList(kHostnameResultsKey, &hostname_records_value) !=
+          entry_dict->GetList(kHostPortsKey, &host_ports_value)) {
+        return false;
+      }
     }
 
     int64_t time_internal;
@@ -435,19 +514,60 @@
         tick_clock_->NowTicks() -
         (base::Time::Now() - base::Time::FromInternalValue(time_internal));
 
-    Key key(hostname, static_cast<DnsQueryType>(dns_query_type), flags,
-            static_cast<HostResolverSource>(host_resolver_source));
-    if (error == OK &&
-        !AddressListFromListValue(addresses_value, &address_list)) {
+    base::Optional<AddressList> address_list;
+    if (!AddressListFromListValue(addresses_value, &address_list)) {
       return false;
     }
 
+    base::Optional<std::vector<std::string>> text_records;
+    if (text_records_value) {
+      text_records.emplace();
+      for (const base::Value& value : text_records_value->GetList()) {
+        if (!value.is_string())
+          return false;
+        text_records.value().push_back(value.GetString());
+      }
+    }
+
+    base::Optional<std::vector<HostPortPair>> hostname_records;
+    if (hostname_records_value) {
+      DCHECK(host_ports_value);
+      if (hostname_records_value->GetList().size() !=
+          host_ports_value->GetList().size()) {
+        return false;
+      }
+
+      hostname_records.emplace();
+      for (size_t i = 0; i < hostname_records_value->GetList().size(); ++i) {
+        if (!hostname_records_value->GetList()[i].is_string() ||
+            !host_ports_value->GetList()[i].is_int() ||
+            !base::IsValueInRangeForNumericType<uint16_t>(
+                host_ports_value->GetList()[i].GetInt())) {
+          return false;
+        }
+        hostname_records.value().push_back(
+            HostPortPair(hostname_records_value->GetList()[i].GetString(),
+                         base::checked_cast<uint16_t>(
+                             host_ports_value->GetList()[i].GetInt())));
+      }
+    }
+
+    // Assume an empty address list if we have an address type and no results.
+    if (IsAddressType(dns_query_type) && !address_list && !text_records &&
+        !hostname_records) {
+      address_list.emplace();
+    }
+
+    Key key(hostname, dns_query_type, flags,
+            static_cast<HostResolverSource>(host_resolver_source));
+
     // If the key is already in the cache, assume it's more recent and don't
     // replace the entry. If the cache is already full, don't bother
     // prioritizing what to evict, just stop restoring.
     auto found = entries_.find(key);
     if (found == entries_.end() && size() < max_entries_) {
-      AddEntry(key, Entry(error, address_list, Entry::SOURCE_UNKNOWN,
+      AddEntry(key, Entry(error, address_list, std::move(text_records),
+                          std::move(hostname_records), Entry::SOURCE_UNKNOWN,
                           expiration_time, network_changes_ - 1));
     }
   }
@@ -491,11 +611,11 @@
   entries_.erase(oldest_it);
 }
 
-void HostCache::RecordSet(SetOutcome outcome,
-                          base::TimeTicks now,
-                          const Entry* old_entry,
-                          const Entry& new_entry,
-                          AddressListDeltaType delta) {
+void HostCache::LogRecordSet(SetOutcome outcome,
+                             base::TimeTicks now,
+                             const Entry* old_entry,
+                             const Entry& new_entry,
+                             AddressListDeltaType delta) {
   CACHE_HISTOGRAM_ENUM("Set", outcome, MAX_SET_OUTCOME);
   switch (outcome) {
     case SET_INSERT:
@@ -510,7 +630,7 @@
                             stale.network_changes);
       CACHE_HISTOGRAM_COUNT("UpdateStale.StaleHits", stale.stale_hits);
       if (old_entry->error() == OK && new_entry.error() == OK) {
-        RecordUpdateStale(delta, stale);
+        LogRecordUpdateStale(delta, stale);
       }
       break;
     }
@@ -520,8 +640,8 @@
   }
 }
 
-void HostCache::RecordUpdateStale(AddressListDeltaType delta,
-                                  const EntryStaleness& stale) {
+void HostCache::LogRecordUpdateStale(AddressListDeltaType delta,
+                                     const EntryStaleness& stale) {
   CACHE_HISTOGRAM_ENUM("UpdateStale.AddressListDelta", delta, MAX_DELTA_TYPE);
   switch (delta) {
     case DELTA_IDENTICAL:
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index ffd63c14..87c3dbfa 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -12,14 +12,19 @@
 #include <memory>
 #include <string>
 #include <tuple>
+#include <utility>
+#include <vector>
 
 #include "base/gtest_prod_util.h"
+#include "base/logging.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "net/base/address_family.h"
 #include "net/base/address_list.h"
 #include "net/base/expiring_cache.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/net_export.h"
 #include "net/dns/dns_util.h"
 #include "net/dns/host_resolver_source.h"
@@ -89,17 +94,33 @@
       SOURCE_HOSTS,
     };
 
-    Entry(int error,
-          const AddressList& addresses,
-          Source source,
-          base::TimeDelta ttl);
+    template <typename T>
+    Entry(int error, T&& results, Source source, base::TimeDelta ttl)
+        : error_(error), source_(source), ttl_(ttl) {
+      DCHECK(ttl >= base::TimeDelta());
+      SetResult(std::forward<T>(results));
+    }
+
     // Use when |ttl| is unknown.
-    Entry(int error, const AddressList& addresses, Source source);
+    template <typename T>
+    Entry(int error, T&& results, Source source)
+        : error_(error),
+          source_(source),
+          ttl_(base::TimeDelta::FromSeconds(-1)) {
+      SetResult(std::forward<T>(results));
+    }
+
     Entry(Entry&& entry);
     ~Entry();
 
     int error() const { return error_; }
-    const AddressList& addresses() const { return addresses_; }
+    const base::Optional<AddressList>& addresses() const { return addresses_; }
+    const base::Optional<std::vector<std::string>>& text_records() const {
+      return text_records_;
+    }
+    const base::Optional<std::vector<HostPortPair>>& hostnames() const {
+      return hostnames_;
+    }
     Source source() const { return source_; }
     bool has_ttl() const { return ttl_ >= base::TimeDelta(); }
     base::TimeDelta ttl() const { return ttl_; }
@@ -118,11 +139,21 @@
           int network_changes);
 
     Entry(int error,
-          const AddressList& addresses,
+          const base::Optional<AddressList>& addresses,
+          base::Optional<std::vector<std::string>>&& text_results,
+          base::Optional<std::vector<HostPortPair>>&& hostnames,
           Source source,
           base::TimeTicks expires,
           int network_changes);
 
+    void SetResult(AddressList addresses) { addresses_ = std::move(addresses); }
+    void SetResult(std::vector<std::string> text_records) {
+      text_records_ = std::move(text_records);
+    }
+    void SetResult(std::vector<HostPortPair> hostnames) {
+      hostnames_ = std::move(hostnames);
+    }
+
     int total_hits() const { return total_hits_; }
     int stale_hits() const { return stale_hits_; }
 
@@ -134,8 +165,10 @@
 
     // The resolve results for this entry.
     int error_;
-    AddressList addresses_;
-    // Where addresses_ were obtained (e.g. DNS lookup, hosts file, etc).
+    base::Optional<AddressList> addresses_;
+    base::Optional<std::vector<std::string>> text_records_;
+    base::Optional<std::vector<HostPortPair>> hostnames_;
+    // Where results were obtained (e.g. DNS lookup, hosts file, etc).
     Source source_;
     // TTL obtained from the nameserver. Negative if unknown.
     base::TimeDelta ttl_;
@@ -243,13 +276,13 @@
 
   Entry* LookupInternal(const Key& key);
 
-  void RecordSet(SetOutcome outcome,
-                 base::TimeTicks now,
-                 const Entry* old_entry,
-                 const Entry& new_entry,
-                 AddressListDeltaType delta);
-  void RecordUpdateStale(AddressListDeltaType delta,
-                         const EntryStaleness& stale);
+  void LogRecordSet(SetOutcome outcome,
+                    base::TimeTicks now,
+                    const Entry* old_entry,
+                    const Entry& new_entry,
+                    AddressListDeltaType delta);
+  void LogRecordUpdateStale(AddressListDeltaType delta,
+                            const EntryStaleness& stale);
   void RecordLookup(LookupOutcome outcome,
                     base::TimeTicks now,
                     const Entry* entry);
diff --git a/net/dns/host_cache_unittest.cc b/net/dns/host_cache_unittest.cc
index b75becc..38932d2 100644
--- a/net/dns/host_cache_unittest.cc
+++ b/net/dns/host_cache_unittest.cc
@@ -694,8 +694,11 @@
   const HostCache::Entry* result1 =
       restored_cache.LookupStale(key1, now, &stale);
   EXPECT_TRUE(result1);
-  EXPECT_EQ(1u, result1->addresses().size());
-  EXPECT_EQ(address_ipv4, result1->addresses().front().address());
+  ASSERT_TRUE(result1->addresses());
+  EXPECT_FALSE(result1->text_records());
+  EXPECT_FALSE(result1->hostnames());
+  EXPECT_EQ(1u, result1->addresses().value().size());
+  EXPECT_EQ(address_ipv4, result1->addresses().value().front().address());
   EXPECT_EQ(1, stale.network_changes);
   // Time to TimeTicks conversion is fuzzy, so just check that expected and
   // actual expiration times are close.
@@ -707,9 +710,10 @@
   const HostCache::Entry* result2 =
       restored_cache.LookupStale(key2, now, &stale);
   EXPECT_TRUE(result2);
-  EXPECT_EQ(2u, result2->addresses().size());
-  EXPECT_EQ(address_ipv6, result2->addresses().front().address());
-  EXPECT_EQ(address_ipv4, result2->addresses().back().address());
+  ASSERT_TRUE(result2->addresses());
+  EXPECT_EQ(2u, result2->addresses().value().size());
+  EXPECT_EQ(address_ipv6, result2->addresses().value().front().address());
+  EXPECT_EQ(address_ipv4, result2->addresses().value().back().address());
   EXPECT_EQ(1, stale.network_changes);
   EXPECT_GT(base::TimeDelta::FromMilliseconds(100),
             (base::TimeDelta::FromSeconds(-3) - stale.expired_by).magnitude());
@@ -717,18 +721,77 @@
   // The "foobar3.com" entry is the new one, not the restored one.
   const HostCache::Entry* result3 = restored_cache.Lookup(key3, now);
   EXPECT_TRUE(result3);
-  EXPECT_EQ(1u, result3->addresses().size());
-  EXPECT_EQ(address_ipv4, result3->addresses().front().address());
+  ASSERT_TRUE(result3->addresses());
+  EXPECT_EQ(1u, result3->addresses().value().size());
+  EXPECT_EQ(address_ipv4, result3->addresses().value().front().address());
 
   // The "foobar4.com" entry is still present and usable.
   const HostCache::Entry* result4 = restored_cache.Lookup(key4, now);
   EXPECT_TRUE(result4);
-  EXPECT_EQ(1u, result4->addresses().size());
-  EXPECT_EQ(address_ipv4, result4->addresses().front().address());
+  ASSERT_TRUE(result4->addresses());
+  EXPECT_EQ(1u, result4->addresses().value().size());
+  EXPECT_EQ(address_ipv4, result4->addresses().value().front().address());
 
   EXPECT_EQ(3u, restored_cache.last_restore_size());
 }
 
+TEST(HostCacheTest, SerializeAndDeserialize_Text) {
+  base::TimeTicks now;
+
+  base::TimeDelta ttl = base::TimeDelta::FromSeconds(99);
+  std::vector<std::string> text_records({"foo", "bar"});
+  HostCache::Key key("example.com", DnsQueryType::A, 0,
+                     HostResolverSource::DNS);
+  HostCache::Entry entry(OK, text_records, HostCache::Entry::SOURCE_DNS, ttl);
+  EXPECT_TRUE(entry.text_records());
+
+  HostCache cache(kMaxCacheEntries);
+  cache.Set(key, entry, now, ttl);
+  EXPECT_EQ(1u, cache.size());
+
+  base::ListValue serialized_cache;
+  cache.GetAsListValue(&serialized_cache, false /* include_staleness */);
+  HostCache restored_cache(kMaxCacheEntries);
+  restored_cache.RestoreFromListValue(serialized_cache);
+
+  ASSERT_EQ(1u, cache.size());
+  const HostCache::Entry* result = cache.Lookup(key, now);
+  ASSERT_TRUE(result);
+  EXPECT_FALSE(result->addresses());
+  ASSERT_TRUE(result->text_records());
+  EXPECT_FALSE(result->hostnames());
+  EXPECT_EQ(text_records, result->text_records().value());
+}
+
+TEST(HostCacheTest, SerializeAndDeserialize_Hostname) {
+  base::TimeTicks now;
+
+  base::TimeDelta ttl = base::TimeDelta::FromSeconds(99);
+  std::vector<HostPortPair> hostnames(
+      {HostPortPair("example.com", 95), HostPortPair("chromium.org", 122)});
+  HostCache::Key key("example.com", DnsQueryType::A, 0,
+                     HostResolverSource::DNS);
+  HostCache::Entry entry(OK, hostnames, HostCache::Entry::SOURCE_DNS, ttl);
+  EXPECT_TRUE(entry.hostnames());
+
+  HostCache cache(kMaxCacheEntries);
+  cache.Set(key, entry, now, ttl);
+  EXPECT_EQ(1u, cache.size());
+
+  base::ListValue serialized_cache;
+  cache.GetAsListValue(&serialized_cache, false /* include_staleness */);
+  HostCache restored_cache(kMaxCacheEntries);
+  restored_cache.RestoreFromListValue(serialized_cache);
+
+  ASSERT_EQ(1u, cache.size());
+  const HostCache::Entry* result = cache.Lookup(key, now);
+  ASSERT_TRUE(result);
+  EXPECT_FALSE(result->addresses());
+  EXPECT_FALSE(result->text_records());
+  ASSERT_TRUE(result->hostnames());
+  EXPECT_EQ(hostnames, result->hostnames().value());
+}
+
 TEST(HostCacheTest, PersistenceDelegate) {
   const base::TimeDelta kTTL = base::TimeDelta::FromSeconds(10);
   HostCache cache(kMaxCacheEntries);
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 47cf9efd..6411200f 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -1933,7 +1933,7 @@
       }
       if (entry.error() == OK && !req->parameters().is_speculative) {
         req->set_address_results(EnsurePortOnAddressList(
-            entry.addresses(), req->request_host().port()));
+            entry.addresses().value(), req->request_host().port()));
       }
       req->OnJobCompleted(this, entry.error());
 
@@ -2519,7 +2519,8 @@
   if (*net_error == OK) {
     if (cache_entry->has_ttl())
       RecordTTL(cache_entry->ttl());
-    *addresses = EnsurePortOnAddressList(cache_entry->addresses(), host_port);
+    *addresses =
+        EnsurePortOnAddressList(cache_entry->addresses().value(), host_port);
   }
   return true;
 }
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index 51d913c..9a292fe 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -455,7 +455,8 @@
     if (entry) {
       rv = entry->error();
       if (rv == OK)
-        *addresses = AddressList::CopyWithPort(entry->addresses(), host.port());
+        *addresses =
+            AddressList::CopyWithPort(entry->addresses().value(), host.port());
     }
   }
   return rv;
diff --git a/net/docs/life-of-a-url-request.md b/net/docs/life-of-a-url-request.md
index a31288e..2e7b152 100644
--- a/net/docs/life-of-a-url-request.md
+++ b/net/docs/life-of-a-url-request.md
@@ -1,10 +1,10 @@
 # Life of a URLRequest
 
 This document is intended as an overview of the core layers of the network
-stack, their basic responsibilities, how they fit together, and where some of
-the pain points are, without going into too much detail. Though it touches a
-bit on child processes and the content/loader stack, the focus is on net/
-itself.
+stack and the network service, their basic responsibilities, and how they fit
+together, without going into too much detail. This doc assumes the network
+service is enabled, though the network service is not yet enabled by default
+on any platform.
 
 It's particularly targeted at people new to the Chrome network stack, but
 should also be useful for team members who may be experts at some parts of the
@@ -15,40 +15,25 @@
 If you notice any inaccuracies in this document, or feel that things could be
 better explained, please do not hesitate to submit patches.
 
+
 # Anatomy of the Network Stack
 
+The network stack is located in //net/ in the Chrome repo, and uses the
+namespace "net". Whenever a class name in this doc has no namespace, it can
+generally be assumed it's in //net/ and is in the net namespace.
+
 The top-level network stack object is the URLRequestContext. The context has
 non-owning pointers to everything needed to create and issue a URLRequest. The
 context must outlive all requests that use it. Creating a context is a rather
 complicated process, and it's recommended that most consumers use
 URLRequestContextBuilder to do this.
 
-Chrome has a number of different URLRequestContexts, as there is often a need to
-keep cookies, caches, and socket pools separate for different types of requests.
-Here are the main ones used by Chrome browser:
-
-* The system URLRequestContext, also owned by the IOThread, used for requests
-that aren't associated with a profile.
-* Each profile, including incognito profiles, has a number of URLRequestContexts
-that are created as needed:
-    * The main URLRequestContext is mostly created in ProfileIOData, though it
-    has a couple components that are passed in from content's StoragePartition
-    code. Several other components are shared with the system URLRequestContext,
-    like the HostResolver.
-    * Each non-incognito profile also has a media request context, which uses a
-    different on-disk cache than the main request context. This prevents a
-    single huge media file from evicting everything else in the cache. (See also
-    crbug.com/789657)
-    * On desktop platforms, each profile has a request context for extensions.
-    * Each profile has two contexts for each isolated app (One for media, one
-    for everything else).
-
 The primary use of the URLRequestContext is to create URLRequest objects using
 URLRequestContext::CreateRequest(). The URLRequest is the main interface used
-by consumers of the network stack.  It is used to make the actual requests to a
-server. Each URLRequest tracks a single request across all redirects until an
-error occurs, it's canceled, or a final response is received, with a (possibly
-empty) body.
+by direct consumers of the network stack. It use used to drive requests for
+http, https, ftp, and some data URLs. Each URLRequest tracks a single request
+across all redirects until an error occurs, it's canceled, or a final response
+is received, with a (possibly empty) body.
 
 The HttpNetworkSession is another major network stack object. It owns the
 HttpStreamFactory, the socket pools, and the HTTP/2 and QUIC session pools. It
@@ -63,94 +48,130 @@
 
 # How many "Delegates"?
 
-The network stack informs the embedder of important events for a request using
-two main interfaces: the URLRequest::Delegate interface and the NetworkDelegate
+A URLRequest informs the consumer of important events for a request using two
+main interfaces: the URLRequest::Delegate interface and the NetworkDelegate
 interface.
 
 The URLRequest::Delegate interface consists of a small set of callbacks needed
-to let the embedder drive a request forward. URLRequest::Delegates generally own
-the URLRequest.
+to let the embedder drive a request forward. The NetworkDelegate is an object
+pointed to by the URLRequestContext and shared by all requests, and includes
+callbacks corresponding to most of the URLRequest::Delegate's callbacks, as
+well as an assortment of other methods.
 
-The NetworkDelegate is an object pointed to by the URLRequestContext and shared
-by all requests, and includes callbacks corresponding to most of the
-URLRequest::Delegate's callbacks, as well as an assortment of other methods. The
-NetworkDelegate is optional, while the URLRequest::Delegate is not.
+# The Network Service and Mojo
+
+The network service, which lives in //services/network/, wraps //net/ objects,
+and provides cross-process network APIs and their implementations for the rest
+of Chrome. The network service uses the namespace "network" for all its classes.
+The Mojo interfaces it provides are in the network::mojom namespace. Mojo is
+Chrome's IPC layer. Generally there's a network::mojom::FooPtr proxy object in
+the consumer's process which also implements the network::mojom::Foo interface.
+When the proxy object's methods are invoked, it passes the call and all its
+arguments over a Mojo IPC channel to another the implementation of the
+network::mojom::Foo interface in the network service (typically implemented by a
+class named network::Foo), which may be running in another process, or possibly
+another thread in the consumer's process.
+
+The network::NetworkService object is singleton that is used by Chrome to create
+all other network service objects. The primary objects it is used to create are
+the network::NetworkContexts, each of which owns its own mostly independent
+URLRequestContext. Chrome has a number of different NetworkContexts, as there
+is often a need to keep cookies, caches, and socket pools separate for different
+types of requests, depending on what's making the request. Here are the main
+NetworkContexts used by Chrome:
+
+* The system NetworkContext, created and owned by Chrome's
+SystemNetworkContextManager, is used for requests that aren't associated with
+particular user or Profile. It has no on-disk storage, so loses all state, like
+cookies, after each browser restart. It has no in-memory http cache, either.
+SystemNetworkContextManager also sets up global network service preferences.
+* Each Chrome Profile, including incognito Profiles, has its own NetworkContext.
+Except for incognito and guest profiles, these contexts store information in
+their own on-disk store, which includes cookies and an HTTP cache, among other
+things. Each of these NetworkContexts is owned by a StoragePartition object in
+the browser process, and created by a Profile's ProfileNetworkContextService.
+* On platforms that support apps, each Profile has a NetworkContext for each app
+installed on that Profile. As with the main NetworkContext, these may have
+on-disk data, depending on the Profile and the App.
 
 
 # Life of a Simple URLRequest
 
-A request for data is normally dispatched from a child to the browser process.
-There a URLRequest is created to drive the request. A protocol-specific job
-(e.g. HTTP, data, file) is attached to the request. That job first checks the
-cache, and then creates a network connection object, if necessary, to actually
-fetch the data. That connection object interacts with network socket pools to
-potentially re-use sockets; the socket pools create and connect a socket if
-there is no appropriate existing socket. Once that socket exists, the HTTP
-request is dispatched, the response read and parsed, and the result returned
-back up the stack and sent over to the child process.
+A request for data is dispatched from some other process which results in
+creating a network::URLLoader in the network process. The URLLoader then
+creates a URLRequest to drive the request. A protocol-specific job
+(e.g. HTTP, data, file) is attached to the request. In the HTTP case, that job
+first checks the cache, and then creates a network connection object, if
+necessary, to actually fetch the data. That connection object interacts with
+network socket pools to potentially re-use sockets; the socket pools create and
+connect a socket if there is no appropriate existing socket. Once that socket
+exists, the HTTP request is dispatched, the response read and parsed, and the
+result returned back up the stack and sent over to the child process.
 
 Of course, it's not quite that simple :-}.
 
-Consider a simple request issued by a child process. Suppose it's an HTTP
-request, the response is uncompressed, no matching entry in the cache, and there
-are no idle sockets connected to the server in the socket pool.
+Consider a simple request issued by some process other than the network
+service's process. Suppose it's an HTTP request, the response is uncompressed,
+no matching entry in the cache, and there are no idle sockets connected to the
+server in the socket pool.
 
 Continuing with a "simple" URLRequest, here's a bit more detail on how things
 work.
 
-### Request starts in a child process
+### Request starts in some (non-network) process
 
 Summary:
 
-* A user (e.g. the WebURLLoaderImpl for Blink) asks ResourceDispatcher to start
-the request.
-* ResourceDispatcher sends an IPC to the ResourceDispatcherHost in the
-browser process.
+* A consumer (e.g. the content::ResourceDispatcher for Blink, the
+content::NavigationURLLoaderImpl for frame navigations, or a
+network::SimpleURLLoader) passes a network::ResourceRequest object and
+network::mojom::URLLoaderClient Mojo channel to a
+network::mojom::URLLoaderFactory, and tells it to create and start a
+network::mojom::URLLoader.
+* Mojo sends the network::ResourceRequest over an IPC pipe to a
+network::URLLoaderFactory in the network process.
 
-Chrome has a single browser process, which handles network requests and tab
-management, among other things, and multiple child processes, which are
-generally sandboxed so can't send out network requests directly. There are
-multiple types of child processes (renderer, GPU, plugin, etc). The renderer
-processes are the ones that layout webpages and run HTML.
+Chrome has a single browser process which handles starting and configuring other
+processes, tab management, and navigation, among other things, and multiple
+child processes, which are generally sandboxed and have no network access
+themselves, apart from the network service (Which either runs in its own
+process, or potentially in the browser process to preserve RAM). There are
+multiple types of child processes (renderer, GPU, plugin, network, etc). The
+renderer processes are the ones that layout webpages and run HTML.
 
-Each child process has at most one ResourceDispatcher, which is responsible for
-all URL request-related communication with the browser process. When something
-in another process needs to issue a resource request, it calls into the
-ResourceDispatcher to start a request. A RequestPeer is passed in to receive
-messages related to the request. When started, the
-ResourceDispatcher assigns the request a per-renderer ID, and then sends the
-ID, along with all information needed to issue the request, to the
-ResourceDispatcherHost in the browser process.
+The browser process creates the top level network::mojom::NetworkContext
+objects, and uses them to create network::mojom::URLLoaderFactories, which it
+can set some security-related options on, before vending them to child
+processes. Child processes can then use them to directly talk to the network
+service.
 
-### ResourceDispatcherHost sets up the request in the browser process
+A consumer that wants to make a network request gets a URLLoaderFactory through
+some manner, assembles a bunch of parameters in the large ResourceRequest
+object, creates a network::mojom::URLLoaderClient Mojo channel for the
+network::mojom::URLLoader to use to talk back to it, and then passes them to
+the URLLoaderFactory, which returns a URLLoader object that it can use to
+manage the network request.
+
+### network::URLLoaderFactory sets up the request in the browser process
 
 Summary:
 
-* ResourceDispatcherHost uses the URLRequestContext to create the URLRequest.
-* ResourceDispatcherHost creates a ResourceLoader and a chain of
-ResourceHandlers to manage the URLRequest.
-* ResourceLoader starts the URLRequest.
+* network::URLLoaderFactory creates a network::URLLoader.
+* network::URLLoader uses the network::NetworkContext's URLRequestContext to
+create and start a URLRequest.
 
-The ResourceDispatcherHost (RDH), along with most of the network stack, lives
-on the browser process's IO thread. The browser process only has one RDH,
-which is responsible for handling all network requests initiated by
-ResourceDispatchers in all child processes, not just renderer processes.
-Requests initiated in the browser process don't go through the RDH, with some
-exceptions.
+The URLLoaderFactory, along with all NetworkContexts and most of the network
+stack, lives on a single thread in the network service. It gets a reconstituted
+ResourceRequest object from the Mojo pipe, does some checks to make sure it
+can service the request, and if so, creates a URLLoader, passing the request and
+the NetworkContext associated with the URLLoaderFactory.
 
-When the RDH sees the request, it calls into a URLRequestContext to create the
-URLRequest. The URLRequestContext has pointers to all the network stack
-objects needed to issue the request over the network, such as the cache, cookie
-store, and host resolver. The RDH then creates a chain of ResourceHandlers
-each of which can monitor/modify/delay/cancel the URLRequest and the
-information it returns. The only one of these I'll talk about here is the
-AsyncResourceHandler, which is the last ResourceHandler in the chain. The RDH
-then creates a ResourceLoader (which is the URLRequest::Delegate), passes
-ownership of the URLRequest and the ResourceHandler chain to it, and then starts
-the ResourceLoader.
-
-The ResourceLoader checks that none of the ResourceHandlers want to cancel,
-modify, or delay the request, and then finally starts the URLRequest.
+The URLLoader then calls into a URLRequestContext to create the URLRequest. The
+URLRequestContext has pointers to all the network stack objects needed to issue
+the request over the network, such as the cache, cookie store, and host
+resolver. The URLLoader then calls into the ResourceScheduler, which may delay
+starting the request, based on priority and other activity. Eventually, the
+ResourceScheduler starts the request.
 
 ### Check the cache, request an HttpStream
 
@@ -222,10 +243,9 @@
 * HttpBasicStream sends the request, and waits for the response.
 * The HttpBasicStream sends the response headers back to the
 HttpNetworkTransaction.
-* The response headers are sent up to the URLRequest, to the ResourceLoader,
-and down through the ResourceHandler chain.
-* They're then sent by the the last ResourceHandler in the chain (the
-AsyncResourceHandler) to the ResourceDispatcher, with an IPC.
+* The response headers are sent up through the URLRequest, to the
+network::URLLoader.
+* They're then sent to the network::mojom::URLLoaderClient via Mojo.
 
 The HttpNetworkTransaction passes the request headers to the HttpBasicStream,
 which uses an HttpStreamParser to (finally) format the request headers and body
@@ -235,56 +255,46 @@
 response headers, and then passes them up through both the
 HttpNetworkTransaction and HttpCache::Transaction to the URLRequestHttpJob. The
 URLRequestHttpJob saves any cookies, if needed, and then passes the headers up
-to the URLRequest and on to the ResourceLoader.
-
-The ResourceLoader passes them through the chain of ResourceHandlers, and then
-they make their way to the AsyncResourceHandler. The AsyncResourceHandler uses
-the renderer process ID ("child ID") to figure out which process the request
-was associated with, and then sends the headers along with the request ID to
-that process's ResourceDispatcher. The ResourceDispatcher uses the ID to
-figure out which RequestPeer the headers should be sent to, which
-sends them on to the RequestPeer.
+to the URLRequest and on to the network::URLLoader, which sends the data over
+a Mojo pipe to the network::mojom::URLLoaderClient, passed in to the URLLoader
+when it was created.
 
 ### Response body is read
 
 Summary:
 
-* AsyncResourceHandler allocates a 512k ring buffer of shared memory to read
-the body of the request.
-* AsyncResourceHandler tells the ResourceLoader to read the response body to
-the buffer, 32kB at a time.
-* AsyncResourceHandler informs the ResourceDispatcher of each read using
-cross-process IPCs.
-* ResourceDispatcher tells the AsyncResourceHandler when it's done with the
-data with each read, so it knows when parts of the buffer can be reused.
+* network::URLLoader creates a raw Mojo data pipe, and passes one end to the
+network::mojom::URLLoaderClient.
+* The URLLoader requests shared memory buffer from the Mojo data pipe.
+* The URLLoader tells the URLRequest to write to the memory buffer, and tells
+the pipe when data has been written to the buffer.
+* The last two steps repeat until the request is complete.
 
-Without waiting to hear back from the ResourceDispatcher, the ResourceLoader
-tells its ResourceHandler chain to allocate memory to receive the response
-body. The AsyncResourceHandler creates a 512KB ring buffer of shared memory,
-and then passes the first 32KB of it to the ResourceLoader for the first read.
-The ResourceLoader then passes a 32KB body read request down through the
-URLRequest all the way down to the HttpStreamParser. Once some data is read,
-possibly less than 32KB, the number of bytes read makes its way back to the
-AsyncResourceHandler, which passes the shared memory buffer and the offset and
-amount of data read to the renderer process.
-
-The AsyncResourceHandler relies on ACKs from the renderer to prevent it from
-overwriting data that the renderer has yet to consume. This process repeats
-until the response body is completely read.
+Without waiting to hear back from the network::mojom::URLLoaderClient, the
+network::URLLoader allocates a raw mojo data pipe, and passes the client the
+read end of the pipe. The URLLoader then grabs an IPC buffer from the pipe,
+and passes a 64KB body read request down through the URLRequest all the way
+down to the HttpStreamParser. Once some data is read, possibly less than 64KB,
+the number of bytes read makes its way back to the URLLoader, which then tells
+the Mojo pipe the read was complete, and then requests another buffer from the
+pipe, to continue writing data to. The pipe may apply back pressure, to limit
+the amount of unconsumed data that can be in shared memory buffers at once.
+This process repeats until the response body is completely read.
 
 ### URLRequest is destroyed
 
 Summary:
 
-* When complete, the RDH deletes the ResourceLoader, which deletes the
-URLRequest and the ResourceHandler chain.
+* When complete, the network::URLLoaderFactory deletes the network::URLLoader,
+which deletes the URLRequest.
 * During destruction, the HttpNetworkTransaction determines if the socket is
 reusable, and if so, tells the HttpBasicStream to return it to the socket pool.
 
-When the URLRequest informs the ResourceLoader it's complete, the
-ResourceLoader tells the ResourceHandlers, and the AsyncResourceHandler tells
-the ResourceDispatcher the request is complete. The RDH then deletes
-ResourceLoader, which deletes the URLRequest and ResourceHandler chain.
+When the URLRequest informs the network::URLLoader the request is complete, the
+URLLoader passes the message along to the network::mojom::URLLoaderClient, over
+its Mojo pipe, before telling the URLLoaderFactory to destroy the URLLoader,
+which results in destroying the URLRequest and closing all Mojo pipes related to
+the request.
 
 When the HttpNetworkTransaction is being torn down, it figures out if the
 socket is reusable. If not, it tells the HttpBasicStream to close the socket.
@@ -341,11 +351,9 @@
 
 ## Cancellation
 
-A request can be cancelled by the child process, by any of the
-ResourceHandlers in the chain, or by the ResourceDispatcherHost itself. When the
-cancellation message reaches the URLRequest, it passes on the fact it's been
-cancelled back to the ResourceLoader, which then sends the message down the
-ResourceHandler chain.
+A consumer can cancel a request at any time by deleting the
+network::mojom::URLLoader pipe used by the request. This will cause the
+network::URLLoader to destroy itself and its URLRequest.
 
 When an HttpNetworkTransaction for a cancelled request is being torn down, it
 figures out if the socket the HttpStream owns can potentially be reused, based
@@ -366,18 +374,24 @@
 entry, even if the headers indicated there will be a body.
 
 The URLRequestHttpJob then checks with the URLRequest if the redirect should be
-followed. The URLRequest then informs the ResourceLoader about the redirect, to
-give it a chance to cancel the request. The information makes its way down
-through the AsyncResourceHandler into the other process, via the
-ResourceDispatcher. Whatever issued the original request then checks if the
-redirect should be followed.
+followed. The URLRequest then informs the network::URLLoader about the redirect,
+which passes information about the redirect to network::mojom::URLLoaderClient,
+in the consumer process. Whatever issued the original request then checks
+if the redirect should be followed.
 
-The ResourceDispatcher then asynchronously sends a message back to either
-follow the redirect or cancel the request. In either case, the old
-HttpTransaction is destroyed, and the HttpNetworkTransaction attempts to drain
-the socket for reuse, just as in the cancellation case. If the redirect is
-followed, the URLRequest calls into the URLRequestJobFactory to create a new
-URLRequestJob, and then starts it.
+If the redirect should be followed, the URLLoaderClient calls back into the
+URLLoader over the network::mojom::URLLoader Mojo interface, which tells the
+URLRequest to follow the redirect. The URLRequest then creates a new
+URLRequestJob to send the new request. If the URLLoaderClient chooses to
+cancel the request instead, it can delete the network::mojom::URLLoader
+pipe, just like the cancellation case discussed above. In either case, the
+old HttpTransaction is destroyed, and the HttpNetworkTransaction attempts to
+drain the socket for reuse, as discussed in the previous section.
+
+In some cases, the consumer may choose to handle a redirect itself, like
+passing off the redirect to a ServiceWorker. In this case, the consumer cancels
+the request and then call into some other network::mojom::URLLoaderFactory
+the new URL to continue the request.
 
 ## Filters (gzip, deflate, brotli, etc)
 
@@ -546,8 +560,13 @@
 
 ## Non-HTTP Schemes
 
-The URLRequestJobFactory has a ProtocolHander for each supported scheme.
-Non-HTTP URLRequests have their own ProtocolHandlers. Some are implemented in
-net/, (like FTP, file, and data, though the renderer handles some data URLs
-internally), and others are implemented in content/ or chrome (like blob,
-chrome, and chrome-extension).
+The URLRequestJobFactory has a ProtocolHander for ftp, http, https, and data
+URLs, though most data URLs are handled directly in the renderer. For other
+schemes, and non-network code that can intercept HTTP/HTTPS requests (Like
+ServiceWorker, or extensions), there's typically another
+network::mojom::URLLoaderFactory class that is used instead of
+network::URLLoaderFactory.  These URLLoaderFactories are not part of the
+network service. Some of these are web standards and handled in content/
+code (Like blob:// and file:// URLs), while other of these are
+chrome-specific, and implemented in chrome/ (like chrome:// and
+chrome-extension:// URLs).
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index b85d278a..fa48265 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -61087,7 +61087,7 @@
       "policy": "custom",
       "mode": "force-https", "include_subdomains": true,
       "expect_ct": true,
-      "expect_ct_report_uri": "https://report-uri.cloudflare.com/expect-ct"
+      "expect_ct_report_uri": "https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
     },
     {
       "name": "weeblrpress.com",
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index f4ebcde..653a291 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -310,3 +310,9 @@
 // If true, QuicSpdyStream::WritevBody() will convert iovs into QuicMemSliceSpan
 // and call WriteMemSlices instead.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_call_write_mem_slices, false)
+
+// If true, enables the BBS4 and BBS5 connection options, which reduce BBR's
+// pacing rate in STARTUP as more losses occur as a fraction of CWND.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_bbr_startup_rate_reduction,
+          false)
diff --git a/net/third_party/http2/decoder/decode_http2_structures_test.cc b/net/third_party/http2/decoder/decode_http2_structures_test.cc
index 5538bb8..2578ae50 100644
--- a/net/third_party/http2/decoder/decode_http2_structures_test.cc
+++ b/net/third_party/http2/decoder/decode_http2_structures_test.cc
@@ -105,12 +105,12 @@
   {
     // Realistic input.
     const char kData[] = {
-        0x00, 0x00, 0x05,        // Payload length: 5
-        0x01,                    // Frame type: HEADERS
-        0x08,                    // Flags: PADDED
-        0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
-        0x04,                    // Padding length: 4
-        0x00, 0x00, 0x00, 0x00,  // Padding bytes
+        '\x00', '\x00', '\x05',          // Payload length: 5
+        '\x01',                          // Frame type: HEADERS
+        '\x08',                          // Flags: PADDED
+        '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1
+        '\x04',                          // Padding length: 4
+        '\x00', '\x00', '\x00', '\x00',  // Padding bytes
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -123,10 +123,10 @@
   {
     // Unlikely input.
     const char kData[] = {
-        0xffu, 0xffu, 0xffu,         // Payload length: uint24 max
-        0xffu,                       // Frame type: Unknown
-        0xffu,                       // Flags: Unknown/All
-        0xffu, 0xffu, 0xffu, 0xffu,  // Stream ID: uint31 max, plus R-bit
+        '\xff', '\xff', '\xff',          // Payload length: uint24 max
+        '\xff',                          // Frame type: Unknown
+        '\xff',                          // Flags: Unknown/All
+        '\xff', '\xff', '\xff', '\xff',  // Stream ID: uint31 max, plus R-bit
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -150,8 +150,8 @@
 TEST_F(PriorityFieldsDecoderTest, DecodesLiteral) {
   {
     const char kData[] = {
-        0x80u, 0x00, 0x00, 0x05,  // Exclusive (yes) and Dependency (5)
-        0xffu,                    // Weight: 256 (after adding 1)
+        '\x80', '\x00', '\x00', '\x05',  // Exclusive (yes) and Dependency (5)
+        '\xff',                          // Weight: 256 (after adding 1)
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -162,9 +162,9 @@
   }
   {
     const char kData[] = {
-        0x7f,  0xffu,
-        0xffu, 0xffu,  // Exclusive (no) and Dependency (0x7fffffff)
-        0x00,          // Weight: 1 (after adding 1)
+        '\x7f', '\xff',
+        '\xff', '\xff',  // Exclusive (no) and Dependency (0x7fffffff)
+        '\x00',          // Weight: 1 (after adding 1)
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -187,7 +187,7 @@
 TEST_F(RstStreamFieldsDecoderTest, DecodesLiteral) {
   {
     const char kData[] = {
-        0x00, 0x00, 0x00, 0x01,  // Error: PROTOCOL_ERROR
+        '\x00', '\x00', '\x00', '\x01',  // Error: PROTOCOL_ERROR
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -197,7 +197,8 @@
   }
   {
     const char kData[] = {
-        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+        '\xff', '\xff', '\xff',
+        '\xff',  // Error: max uint32 (Unknown error code)
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -219,8 +220,8 @@
 TEST_F(SettingFieldsDecoderTest, DecodesLiteral) {
   {
     const char kData[] = {
-        0x00, 0x01,              // Setting: HEADER_TABLE_SIZE
-        0x00, 0x00, 0x40, 0x00,  // Value: 16K
+        '\x00', '\x01',                  // Setting: HEADER_TABLE_SIZE
+        '\x00', '\x00', '\x40', '\x00',  // Value: 16K
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -232,8 +233,8 @@
   }
   {
     const char kData[] = {
-        0x00,  0x00,                 // Setting: Unknown (0)
-        0xffu, 0xffu, 0xffu, 0xffu,  // Value: max uint32
+        '\x00', '\x00',                  // Setting: Unknown (0)
+        '\xff', '\xff', '\xff', '\xff',  // Value: max uint32
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -255,7 +256,7 @@
 TEST_F(PushPromiseFieldsDecoderTest, DecodesLiteral) {
   {
     const char kData[] = {
-        0x00, 0x01, 0x8au, 0x92u,  // Promised Stream ID: 101010
+        '\x00', '\x01', '\x8a', '\x92',  // Promised Stream ID: 101010
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -266,7 +267,8 @@
     // Promised stream id has R-bit (reserved for future use) set, which
     // should be cleared by the decoder.
     const char kData[] = {
-        0xffu, 0xffu, 0xffu, 0xffu,  // Promised Stream ID: max uint31 and R-bit
+        '\xff', '\xff', '\xff',
+        '\xff',  // Promised Stream ID: max uint31 and R-bit
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -287,7 +289,7 @@
   {
     // Each byte is different, so can detect if order changed.
     const char kData[] = {
-        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -298,7 +300,7 @@
   {
     // All zeros, detect problems handling NULs.
     const char kData[] = {
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -308,7 +310,7 @@
   }
   {
     const char kData[] = {
-        0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
+        '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -330,8 +332,8 @@
 TEST_F(GoAwayFieldsDecoderTest, DecodesLiteral) {
   {
     const char kData[] = {
-        0x00, 0x00, 0x00, 0x00,  // Last Stream ID: 0
-        0x00, 0x00, 0x00, 0x00,  // Error: NO_ERROR (0)
+        '\x00', '\x00', '\x00', '\x00',  // Last Stream ID: 0
+        '\x00', '\x00', '\x00', '\x00',  // Error: NO_ERROR (0)
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -342,8 +344,8 @@
   }
   {
     const char kData[] = {
-        0x00, 0x00, 0x00, 0x01,  // Last Stream ID: 1
-        0x00, 0x00, 0x00, 0x0d,  // Error: HTTP_1_1_REQUIRED
+        '\x00', '\x00', '\x00', '\x01',  // Last Stream ID: 1
+        '\x00', '\x00', '\x00', '\x0d',  // Error: HTTP_1_1_REQUIRED
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -354,8 +356,10 @@
   }
   {
     const char kData[] = {
-        0xffu, 0xffu, 0xffu, 0xffu,  // Last Stream ID: max uint31 and R-bit
-        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+        '\xff', '\xff',
+        '\xff', '\xff',  // Last Stream ID: max uint31 and R-bit
+        '\xff', '\xff',
+        '\xff', '\xff',  // Error: max uint32 (Unknown error code)
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -378,7 +382,7 @@
 TEST_F(WindowUpdateFieldsDecoderTest, DecodesLiteral) {
   {
     const char kData[] = {
-        0x00, 0x01, 0x00, 0x00,  // Window Size Increment: 2 ^ 16
+        '\x00', '\x01', '\x00', '\x00',  // Window Size Increment: 2 ^ 16
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -389,7 +393,7 @@
     // Increment must be non-zero, but we need to be able to decode the invalid
     // zero to detect it.
     const char kData[] = {
-        0x00, 0x00, 0x00, 0x00,  // Window Size Increment: 0
+        '\x00', '\x00', '\x00', '\x00',  // Window Size Increment: 0
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -402,7 +406,7 @@
     // clang-format off
     const char kData[] = {
         // Window Size Increment: max uint31 and R-bit
-        0xffu, 0xffu, 0xffu, 0xffu,
+        '\xff', '\xff', '\xff', '\xff',
     };
     // clang-format on
     DecodeLeadingStructure(kData);
@@ -424,7 +428,7 @@
 TEST_F(AltSvcFieldsDecoderTest, DecodesLiteral) {
   {
     const char kData[] = {
-        0x00, 0x00,  // Origin Length: 0
+        '\x00', '\x00',  // Origin Length: 0
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -433,7 +437,7 @@
   }
   {
     const char kData[] = {
-        0x00, 0x14,  // Origin Length: 20
+        '\x00', '\x14',  // Origin Length: 20
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
@@ -442,7 +446,7 @@
   }
   {
     const char kData[] = {
-        0xffu, 0xffu,  // Origin Length: uint16 max
+        '\xff', '\xff',  // Origin Length: uint16 max
     };
     DecodeLeadingStructure(kData);
     if (!HasFailure()) {
diff --git a/net/third_party/http2/decoder/http2_frame_decoder_test.cc b/net/third_party/http2/decoder/http2_frame_decoder_test.cc
index f74c9115..96b8aaa 100644
--- a/net/third_party/http2/decoder/http2_frame_decoder_test.cc
+++ b/net/third_party/http2/decoder/http2_frame_decoder_test.cc
@@ -226,10 +226,11 @@
 
 TEST_F(Http2FrameDecoderTest, DataEmpty) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x00,        // Payload length: 0
-      0x00,                    // DATA
-      0x00,                    // Flags: none
-      0x00, 0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
+      '\x00', '\x00', '\x00',  // Payload length: 0
+      '\x00',                  // DATA
+      '\x00',                  // Flags: none
+      '\x00', '\x00', '\x00',
+      '\x00',  // Stream ID: 0 (invalid but unchecked here)
   };
   Http2FrameHeader header(0, Http2FrameType::DATA, 0, 0);
   FrameParts expected(header, "");
@@ -238,10 +239,10 @@
 
 TEST_F(Http2FrameDecoderTest, HeadersEmpty) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x00,        // Payload length: 0
-      0x01,                    // HEADERS
-      0x00,                    // Flags: none
-      0x00, 0x00, 0x00, 0x01,  // Stream ID: 0  (REQUIRES ID)
+      '\x00', '\x00', '\x00',          // Payload length: 0
+      '\x01',                          // HEADERS
+      '\x00',                          // Flags: none
+      '\x00', '\x00', '\x00', '\x01',  // Stream ID: 0  (REQUIRES ID)
   };
   Http2FrameHeader header(0, Http2FrameType::HEADERS, 0, 1);
   FrameParts expected(header, "");
@@ -250,12 +251,12 @@
 
 TEST_F(Http2FrameDecoderTest, Priority) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x05,        // Length: 5
-      0x02,                     //   Type: PRIORITY
-      0x00,                     //  Flags: none
-      0x00,  0x00, 0x00, 0x02,  // Stream: 2
-      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
-      0x10,                     // Weight: 17
+      '\x00', '\x00', '\x05',          // Length: 5
+      '\x02',                          //   Type: PRIORITY
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x02',  // Stream: 2
+      '\x80', '\x00', '\x00', '\x01',  // Parent: 1 (Exclusive)
+      '\x10',                          // Weight: 17
   };
   Http2FrameHeader header(5, Http2FrameType::PRIORITY, 0, 2);
   FrameParts expected(header);
@@ -265,11 +266,11 @@
 
 TEST_F(Http2FrameDecoderTest, RstStream) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x04,        // Length: 4
-      0x03,                    //   Type: RST_STREAM
-      0x00,                    //  Flags: none
-      0x00, 0x00, 0x00, 0x01,  // Stream: 1
-      0x00, 0x00, 0x00, 0x01,  //  Error: PROTOCOL_ERROR
+      '\x00', '\x00', '\x04',          // Length: 4
+      '\x03',                          //   Type: RST_STREAM
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x01',  // Stream: 1
+      '\x00', '\x00', '\x00', '\x01',  //  Error: PROTOCOL_ERROR
   };
   Http2FrameHeader header(4, Http2FrameType::RST_STREAM, 0, 1);
   FrameParts expected(header);
@@ -279,10 +280,10 @@
 
 TEST_F(Http2FrameDecoderTest, SettingsEmpty) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x00,        // Length: 0
-      0x04,                    //   Type: SETTINGS
-      0x00,                    //  Flags: none
-      0x00, 0x00, 0x00, 0x01,  // Stream: 1 (invalid but unchecked here)
+      '\x00', '\x00', '\x00',          // Length: 0
+      '\x04',                          //   Type: SETTINGS
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x01',  // Stream: 1 (invalid but unchecked here)
   };
   Http2FrameHeader header(0, Http2FrameType::SETTINGS, 0, 1);
   EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, header));
@@ -290,10 +291,10 @@
 
 TEST_F(Http2FrameDecoderTest, SettingsAck) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x00,        //   Length: 6
-      0x04,                    //     Type: SETTINGS
-      0x01,                    //    Flags: ACK
-      0x00, 0x00, 0x00, 0x00,  //   Stream: 0
+      '\x00', '\x00', '\x00',          //   Length: 6
+      '\x04',                          //     Type: SETTINGS
+      '\x01',                          //    Flags: ACK
+      '\x00', '\x00', '\x00', '\x00',  //   Stream: 0
   };
   Http2FrameHeader header(0, Http2FrameType::SETTINGS, Http2FrameFlag::ACK, 0);
   FrameParts expected(header);
@@ -302,11 +303,13 @@
 
 TEST_F(Http2FrameDecoderTest, PushPromiseMinimal) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x04,        // Payload length: 4
-      0x05,                    // PUSH_PROMISE
-      0x04,                    // Flags: END_HEADERS
-      0x00, 0x00, 0x00, 0x02,  //   Stream: 2 (invalid but unchecked here)
-      0x00, 0x00, 0x00, 0x01,  // Promised: 1 (invalid but unchecked here)
+      '\x00', '\x00', '\x04',  // Payload length: 4
+      '\x05',                  // PUSH_PROMISE
+      '\x04',                  // Flags: END_HEADERS
+      '\x00', '\x00', '\x00',
+      '\x02',  //   Stream: 2 (invalid but unchecked here)
+      '\x00', '\x00', '\x00',
+      '\x01',  // Promised: 1 (invalid but unchecked here)
   };
   Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE,
                           Http2FrameFlag::END_HEADERS, 2);
@@ -317,12 +320,12 @@
 
 TEST_F(Http2FrameDecoderTest, Ping) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x08,        //   Length: 8
-      0x06,                     //     Type: PING
-      0xfeu,                    //    Flags: no valid flags
-      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
-      's',   'o',  'm',  'e',   // "some"
-      'd',   'a',  't',  'a',   // "data"
+      '\x00', '\x00', '\x08',          //   Length: 8
+      '\x06',                          //     Type: PING
+      '\xfe',                          //    Flags: no valid flags
+      '\x00', '\x00', '\x00', '\x00',  //   Stream: 0
+      's',    'o',    'm',    'e',     // "some"
+      'd',    'a',    't',    'a',     // "data"
   };
   Http2FrameHeader header(8, Http2FrameType::PING, 0, 0);
   FrameParts expected(header);
@@ -333,12 +336,12 @@
 
 TEST_F(Http2FrameDecoderTest, PingAck) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x08,        //   Length: 8
-      0x06,                     //     Type: PING
-      0xffu,                    //    Flags: ACK (plus all invalid flags)
-      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
-      's',   'o',  'm',  'e',   // "some"
-      'd',   'a',  't',  'a',   // "data"
+      '\x00', '\x00', '\x08',          //   Length: 8
+      '\x06',                          //     Type: PING
+      '\xff',                          //    Flags: ACK (plus all invalid flags)
+      '\x00', '\x00', '\x00', '\x00',  //   Stream: 0
+      's',    'o',    'm',    'e',     // "some"
+      'd',    'a',    't',    'a',     // "data"
   };
   Http2FrameHeader header(8, Http2FrameType::PING, Http2FrameFlag::ACK, 0);
   FrameParts expected(header);
@@ -349,12 +352,12 @@
 
 TEST_F(Http2FrameDecoderTest, GoAwayMinimal) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x08,         // Length: 8 (no opaque data)
-      0x07,                      //   Type: GOAWAY
-      0xffu,                     //  Flags: 0xff (no valid flags)
-      0x00,  0x00, 0x00, 0x01,   // Stream: 1 (invalid but unchecked here)
-      0x80u, 0x00, 0x00, 0xffu,  //   Last: 255 (plus R bit)
-      0x00,  0x00, 0x00, 0x09,   //  Error: COMPRESSION_ERROR
+      '\x00', '\x00', '\x08',          // Length: 8 (no opaque data)
+      '\x07',                          //   Type: GOAWAY
+      '\xff',                          //  Flags: 0xff (no valid flags)
+      '\x00', '\x00', '\x00', '\x01',  // Stream: 1 (invalid but unchecked here)
+      '\x80', '\x00', '\x00', '\xff',  //   Last: 255 (plus R bit)
+      '\x00', '\x00', '\x00', '\x09',  //  Error: COMPRESSION_ERROR
   };
   Http2FrameHeader header(8, Http2FrameType::GOAWAY, 0, 1);
   FrameParts expected(header);
@@ -365,11 +368,11 @@
 
 TEST_F(Http2FrameDecoderTest, WindowUpdate) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x04,        // Length: 4
-      0x08,                     //   Type: WINDOW_UPDATE
-      0x0f,                     //  Flags: 0xff (no valid flags)
-      0x00,  0x00, 0x00, 0x01,  // Stream: 1
-      0x80u, 0x00, 0x04, 0x00,  //   Incr: 1024 (plus R bit)
+      '\x00', '\x00', '\x04',          // Length: 4
+      '\x08',                          //   Type: WINDOW_UPDATE
+      '\x0f',                          //  Flags: 0xff (no valid flags)
+      '\x00', '\x00', '\x00', '\x01',  // Stream: 1
+      '\x80', '\x00', '\x04', '\x00',  //   Incr: 1024 (plus R bit)
   };
   Http2FrameHeader header(4, Http2FrameType::WINDOW_UPDATE, 0, 1);
   FrameParts expected(header);
@@ -379,10 +382,11 @@
 
 TEST_F(Http2FrameDecoderTest, ContinuationEmpty) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x00,        // Payload length: 0
-      0x09,                    // CONTINUATION
-      0x00,                    // Flags: none
-      0x00, 0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
+      '\x00', '\x00', '\x00',  // Payload length: 0
+      '\x09',                  // CONTINUATION
+      '\x00',                  // Flags: none
+      '\x00', '\x00', '\x00',
+      '\x00',  // Stream ID: 0 (invalid but unchecked here)
   };
   Http2FrameHeader header(0, Http2FrameType::CONTINUATION, 0, 0);
   FrameParts expected(header);
@@ -391,11 +395,12 @@
 
 TEST_F(Http2FrameDecoderTest, AltSvcMinimal) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x02,        // Payload length: 2
-      0x0a,                     // ALTSVC
-      0xffu,                    // Flags: none (plus 0xff)
-      0x00,  0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
-      0x00,  0x00,              // Origin Length: 0
+      '\x00', '\x00', '\x02',  // Payload length: 2
+      '\x0a',                  // ALTSVC
+      '\xff',                  // Flags: none (plus 0xff)
+      '\x00', '\x00', '\x00',
+      '\x00',          // Stream ID: 0 (invalid but unchecked here)
+      '\x00', '\x00',  // Origin Length: 0
   };
   Http2FrameHeader header(2, Http2FrameType::ALTSVC, 0, 0);
   FrameParts expected(header);
@@ -406,10 +411,10 @@
 
 TEST_F(Http2FrameDecoderTest, UnknownEmpty) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x00,        // Payload length: 0
-      0x20,                     // 32 (unknown)
-      0xffu,                    // Flags: all
-      0x00,  0x00, 0x00, 0x00,  // Stream ID: 0
+      '\x00', '\x00', '\x00',          // Payload length: 0
+      '\x20',                          // 32 (unknown)
+      '\xff',                          // Flags: all
+      '\x00', '\x00', '\x00', '\x00',  // Stream ID: 0
   };
   Http2FrameHeader header(0, static_cast<Http2FrameType>(32), 0xff, 0);
   FrameParts expected(header);
@@ -421,11 +426,11 @@
 
 TEST_F(Http2FrameDecoderTest, DataPayload) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x03,        // Payload length: 7
-      0x00,                     // DATA
-      0x80u,                    // Flags: 0x80
-      0x00,  0x00, 0x02, 0x02,  // Stream ID: 514
-      'a',   'b',  'c',         // Data
+      '\x00', '\x00', '\x03',          // Payload length: 7
+      '\x00',                          // DATA
+      '\x80',                          // Flags: 0x80
+      '\x00', '\x00', '\x02', '\x02',  // Stream ID: 514
+      'a',    'b',    'c',             // Data
   };
   Http2FrameHeader header(3, Http2FrameType::DATA, 0, 514);
   FrameParts expected(header, "abc");
@@ -434,11 +439,11 @@
 
 TEST_F(Http2FrameDecoderTest, HeadersPayload) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x03,        // Payload length: 3
-      0x01,                    // HEADERS
-      0x05,                    // Flags: END_STREAM | END_HEADERS
-      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
-      'a',  'b',  'c',         // HPACK fragment (doesn't have to be valid)
+      '\x00', '\x00', '\x03',          // Payload length: 3
+      '\x01',                          // HEADERS
+      '\x05',                          // Flags: END_STREAM | END_HEADERS
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID)
+      'a',    'b',    'c',  // HPACK fragment (doesn't have to be valid)
   };
   Http2FrameHeader header(
       3, Http2FrameType::HEADERS,
@@ -449,12 +454,12 @@
 
 TEST_F(Http2FrameDecoderTest, HeadersPriority) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x05,        // Payload length: 5
-      0x01,                     // HEADERS
-      0x20,                     // Flags: PRIORITY
-      0x00,  0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
-      0x00,  0x00, 0x00, 0x01,  // Parent: 1 (Not Exclusive)
-      0xffu,                    // Weight: 256
+      '\x00', '\x00', '\x05',          // Payload length: 5
+      '\x01',                          // HEADERS
+      '\x20',                          // Flags: PRIORITY
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID)
+      '\x00', '\x00', '\x00', '\x01',  // Parent: 1 (Not Exclusive)
+      '\xff',                          // Weight: 256
   };
   Http2FrameHeader header(5, Http2FrameType::HEADERS, Http2FrameFlag::PRIORITY,
                           2);
@@ -465,14 +470,14 @@
 
 TEST_F(Http2FrameDecoderTest, Settings) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x0c,        // Length: 12
-      0x04,                    //   Type: SETTINGS
-      0x00,                    //  Flags: none
-      0x00, 0x00, 0x00, 0x00,  // Stream: 0
-      0x00, 0x04,              //  Param: INITIAL_WINDOW_SIZE
-      0x0a, 0x0b, 0x0c, 0x0d,  //  Value: 168496141
-      0x00, 0x02,              //  Param: ENABLE_PUSH
-      0x00, 0x00, 0x00, 0x03,  //  Value: 3 (invalid but unchecked here)
+      '\x00', '\x00', '\x0c',          // Length: 12
+      '\x04',                          //   Type: SETTINGS
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x00',  // Stream: 0
+      '\x00', '\x04',                  //  Param: INITIAL_WINDOW_SIZE
+      '\x0a', '\x0b', '\x0c', '\x0d',  //  Value: 168496141
+      '\x00', '\x02',                  //  Param: ENABLE_PUSH
+      '\x00', '\x00', '\x00', '\x03',  //  Value: 3 (invalid but unchecked here)
   };
   Http2FrameHeader header(12, Http2FrameType::SETTINGS, 0, 0);
   FrameParts expected(header);
@@ -485,12 +490,12 @@
 
 TEST_F(Http2FrameDecoderTest, PushPromisePayload) {
   const char kFrameData[] = {
-      0x00, 0x00, 7,            // Payload length: 7
-      0x05,                     // PUSH_PROMISE
-      0x04,                     // Flags: END_HEADERS
-      0x00, 0x00, 0x00, 0xffu,  // Stream ID: 255
-      0x00, 0x00, 0x01, 0x00,   // Promised: 256
-      'a',  'b',  'c',          // HPACK fragment (doesn't have to be valid)
+      '\x00', '\x00', 7,               // Payload length: 7
+      '\x05',                          // PUSH_PROMISE
+      '\x04',                          // Flags: END_HEADERS
+      '\x00', '\x00', '\x00', '\xff',  // Stream ID: 255
+      '\x00', '\x00', '\x01', '\x00',  // Promised: 256
+      'a',    'b',    'c',  // HPACK fragment (doesn't have to be valid)
   };
   Http2FrameHeader header(7, Http2FrameType::PUSH_PROMISE,
                           Http2FrameFlag::END_HEADERS, 255);
@@ -501,13 +506,13 @@
 
 TEST_F(Http2FrameDecoderTest, GoAwayOpaqueData) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x0e,        // Length: 14
-      0x07,                     //   Type: GOAWAY
-      0xffu,                    //  Flags: 0xff (no valid flags)
-      0x80u, 0x00, 0x00, 0x00,  // Stream: 0 (plus R bit)
-      0x00,  0x00, 0x01, 0x00,  //   Last: 256
-      0x00,  0x00, 0x00, 0x03,  //  Error: FLOW_CONTROL_ERROR
-      'o',   'p',  'a',  'q',  'u', 'e',
+      '\x00', '\x00', '\x0e',          // Length: 14
+      '\x07',                          //   Type: GOAWAY
+      '\xff',                          //  Flags: 0xff (no valid flags)
+      '\x80', '\x00', '\x00', '\x00',  // Stream: 0 (plus R bit)
+      '\x00', '\x00', '\x01', '\x00',  //   Last: 256
+      '\x00', '\x00', '\x00', '\x03',  //  Error: FLOW_CONTROL_ERROR
+      'o',    'p',    'a',    'q',    'u', 'e',
   };
   Http2FrameHeader header(14, Http2FrameType::GOAWAY, 0, 0);
   FrameParts expected(header, "opaque");
@@ -518,11 +523,11 @@
 
 TEST_F(Http2FrameDecoderTest, ContinuationPayload) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x03,        // Payload length: 3
-      0x09,                     // CONTINUATION
-      0xffu,                    // Flags: END_HEADERS | 0xfb
-      0x00,  0x00, 0x00, 0x02,  // Stream ID: 2
-      'a',   'b',  'c',         // Data
+      '\x00', '\x00', '\x03',          // Payload length: 3
+      '\x09',                          // CONTINUATION
+      '\xff',                          // Flags: END_HEADERS | 0xfb
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2
+      'a',    'b',    'c',             // Data
   };
   Http2FrameHeader header(3, Http2FrameType::CONTINUATION,
                           Http2FrameFlag::END_HEADERS, 2);
@@ -532,13 +537,13 @@
 
 TEST_F(Http2FrameDecoderTest, AltSvcPayload) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x08,        // Payload length: 3
-      0x0a,                    // ALTSVC
-      0x00,                    // Flags: none
-      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
-      0x00, 0x03,              // Origin Length: 0
-      'a',  'b',  'c',         // Origin
-      'd',  'e',  'f',         // Value
+      '\x00', '\x00', '\x08',          // Payload length: 3
+      '\x0a',                          // ALTSVC
+      '\x00',                          // Flags: none
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2
+      '\x00', '\x03',                  // Origin Length: 0
+      'a',    'b',    'c',             // Origin
+      'd',    'e',    'f',             // Value
   };
   Http2FrameHeader header(8, Http2FrameType::ALTSVC, 0, 2);
   FrameParts expected(header);
@@ -548,11 +553,11 @@
 
 TEST_F(Http2FrameDecoderTest, UnknownPayload) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x03,        // Payload length: 3
-      0x30,                    // 48 (unknown)
-      0x00,                    // Flags: none
-      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
-      'a',  'b',  'c',         // Payload
+      '\x00', '\x00', '\x03',          // Payload length: 3
+      '\x30',                          // 48 (unknown)
+      '\x00',                          // Flags: none
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2
+      'a',    'b',    'c',             // Payload
   };
   Http2FrameHeader header(3, static_cast<Http2FrameType>(48), 0, 2);
   FrameParts expected(header, "abc");
@@ -564,13 +569,13 @@
 
 TEST_F(Http2FrameDecoderTest, DataPayloadAndPadding) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x07,        // Payload length: 7
-      0x00,                    // DATA
-      0x09,                    // Flags: END_STREAM | PADDED
-      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
-      0x03,                    // Pad Len
-      'a',  'b',  'c',         // Data
-      0x00, 0x00, 0x00,        // Padding
+      '\x00', '\x00', '\x07',          // Payload length: 7
+      '\x00',                          // DATA
+      '\x09',                          // Flags: END_STREAM | PADDED
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID)
+      '\x03',                          // Pad Len
+      'a',    'b',    'c',             // Data
+      '\x00', '\x00', '\x00',          // Padding
   };
   Http2FrameHeader header(7, Http2FrameType::DATA,
                           Http2FrameFlag::END_STREAM | Http2FrameFlag::PADDED,
@@ -582,13 +587,13 @@
 
 TEST_F(Http2FrameDecoderTest, HeadersPayloadAndPadding) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x07,        // Payload length: 7
-      0x01,                    // HEADERS
-      0x08,                    // Flags: PADDED
-      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
-      0x03,                    // Pad Len
-      'a',  'b',  'c',         // HPACK fragment (doesn't have to be valid)
-      0x00, 0x00, 0x00,        // Padding
+      '\x00', '\x00', '\x07',          // Payload length: 7
+      '\x01',                          // HEADERS
+      '\x08',                          // Flags: PADDED
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID)
+      '\x03',                          // Pad Len
+      'a',    'b',    'c',     // HPACK fragment (doesn't have to be valid)
+      '\x00', '\x00', '\x00',  // Padding
   };
   Http2FrameHeader header(7, Http2FrameType::HEADERS, Http2FrameFlag::PADDED,
                           2);
@@ -599,15 +604,15 @@
 
 TEST_F(Http2FrameDecoderTest, HeadersPayloadPriorityAndPadding) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x0c,        // Payload length: 12
-      0x01,                     // HEADERS
-      0xffu,                    // Flags: all, including undefined
-      0x00,  0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
-      0x03,                     // Pad Len
-      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
-      0x10,                     // Weight: 17
-      'a',   'b',  'c',         // HPACK fragment (doesn't have to be valid)
-      0x00,  0x00, 0x00,        // Padding
+      '\x00', '\x00', '\x0c',          // Payload length: 12
+      '\x01',                          // HEADERS
+      '\xff',                          // Flags: all, including undefined
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID)
+      '\x03',                          // Pad Len
+      '\x80', '\x00', '\x00', '\x01',  // Parent: 1 (Exclusive)
+      '\x10',                          // Weight: 17
+      'a',    'b',    'c',     // HPACK fragment (doesn't have to be valid)
+      '\x00', '\x00', '\x00',  // Padding
   };
   Http2FrameHeader header(12, Http2FrameType::HEADERS,
                           Http2FrameFlag::END_STREAM |
@@ -622,14 +627,14 @@
 
 TEST_F(Http2FrameDecoderTest, PushPromisePayloadAndPadding) {
   const char kFrameData[] = {
-      0x00,  0x00, 11,          // Payload length: 11
-      0x05,                     // PUSH_PROMISE
-      0xffu,                    // Flags: END_HEADERS | PADDED | 0xf3
-      0x00,  0x00, 0x00, 0x01,  // Stream ID: 1
-      0x03,                     // Pad Len
-      0x00,  0x00, 0x00, 0x02,  // Promised: 2
-      'a',   'b',  'c',         // HPACK fragment (doesn't have to be valid)
-      0x00,  0x00, 0x00,        // Padding
+      '\x00', '\x00', 11,              // Payload length: 11
+      '\x05',                          // PUSH_PROMISE
+      '\xff',                          // Flags: END_HEADERS | PADDED | 0xf3
+      '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1
+      '\x03',                          // Pad Len
+      '\x00', '\x00', '\x00', '\x02',  // Promised: 2
+      'a',    'b',    'c',     // HPACK fragment (doesn't have to be valid)
+      '\x00', '\x00', '\x00',  // Padding
   };
   Http2FrameHeader header(11, Http2FrameType::PUSH_PROMISE,
                           Http2FrameFlag::END_HEADERS | Http2FrameFlag::PADDED,
@@ -645,10 +650,10 @@
 
 TEST_F(Http2FrameDecoderTest, DataMissingPadLengthField) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x00,        // Payload length: 0
-      0x00,                    // DATA
-      0x08,                    // Flags: PADDED
-      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+      '\x00', '\x00', '\x00',          // Payload length: 0
+      '\x00',                          // DATA
+      '\x08',                          // Flags: PADDED
+      '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1
   };
   Http2FrameHeader header(0, Http2FrameType::DATA, Http2FrameFlag::PADDED, 1);
   FrameParts expected(header);
@@ -658,12 +663,12 @@
 
 TEST_F(Http2FrameDecoderTest, HeaderPaddingTooLong) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x02,        // Payload length: 0
-      0x01,                     // HEADERS
-      0x08,                     // Flags: PADDED
-      0x00,  0x01, 0x00, 0x00,  // Stream ID: 65536
-      0xffu,                    // Pad Len: 255
-      0x00,                     // Only one byte of padding
+      '\x00', '\x00', '\x02',          // Payload length: 0
+      '\x01',                          // HEADERS
+      '\x08',                          // Flags: PADDED
+      '\x00', '\x01', '\x00', '\x00',  // Stream ID: 65536
+      '\xff',                          // Pad Len: 255
+      '\x00',                          // Only one byte of padding
   };
   Http2FrameHeader header(2, Http2FrameType::HEADERS, Http2FrameFlag::PADDED,
                           65536);
@@ -674,11 +679,11 @@
 
 TEST_F(Http2FrameDecoderTest, HeaderMissingPriority) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x04,        // Payload length: 0
-      0x01,                    // HEADERS
-      0x20,                    // Flags: PRIORITY
-      0x00, 0x01, 0x00, 0x00,  // Stream ID: 65536
-      0x00, 0x00, 0x00, 0x00,  // Priority (truncated)
+      '\x00', '\x00', '\x04',          // Payload length: 0
+      '\x01',                          // HEADERS
+      '\x20',                          // Flags: PRIORITY
+      '\x00', '\x01', '\x00', '\x00',  // Stream ID: 65536
+      '\x00', '\x00', '\x00', '\x00',  // Priority (truncated)
   };
   Http2FrameHeader header(4, Http2FrameType::HEADERS, Http2FrameFlag::PRIORITY,
                           65536);
@@ -687,11 +692,11 @@
 
 TEST_F(Http2FrameDecoderTest, PriorityTooShort) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x04,        // Length: 5
-      0x02,                     //   Type: PRIORITY
-      0x00,                     //  Flags: none
-      0x00,  0x00, 0x00, 0x02,  // Stream: 2
-      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+      '\x00', '\x00', '\x04',          // Length: 5
+      '\x02',                          //   Type: PRIORITY
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x02',  // Stream: 2
+      '\x80', '\x00', '\x00', '\x01',  // Parent: 1 (Exclusive)
   };
   Http2FrameHeader header(4, Http2FrameType::PRIORITY, 0, 2);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -699,11 +704,11 @@
 
 TEST_F(Http2FrameDecoderTest, RstStreamTooShort) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x03,        // Length: 4
-      0x03,                    //   Type: RST_STREAM
-      0x00,                    //  Flags: none
-      0x00, 0x00, 0x00, 0x01,  // Stream: 1
-      0x00, 0x00, 0x00,        //  Truncated
+      '\x00', '\x00', '\x03',          // Length: 4
+      '\x03',                          //   Type: RST_STREAM
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x01',  // Stream: 1
+      '\x00', '\x00', '\x00',          //  Truncated
   };
   Http2FrameHeader header(3, Http2FrameType::RST_STREAM, 0, 1);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -713,14 +718,14 @@
 // invalid.
 TEST_F(Http2FrameDecoderTest, SettingsWrongSize) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x09,        // Length: 2
-      0x04,                    //   Type: SETTINGS
-      0x00,                    //  Flags: none
-      0x00, 0x00, 0x00, 0x00,  // Stream: 0
-      0x00, 0x02,              //  Param: ENABLE_PUSH
-      0x00, 0x00, 0x00, 0x03,  //  Value: 1
-      0x00, 0x04,              //  Param: INITIAL_WINDOW_SIZE
-      0x00,                    //  Value: Truncated
+      '\x00', '\x00', '\x09',          // Length: 2
+      '\x04',                          //   Type: SETTINGS
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x00',  // Stream: 0
+      '\x00', '\x02',                  //  Param: ENABLE_PUSH
+      '\x00', '\x00', '\x00', '\x03',  //  Value: 1
+      '\x00', '\x04',                  //  Param: INITIAL_WINDOW_SIZE
+      '\x00',                          //  Value: Truncated
   };
   Http2FrameHeader header(9, Http2FrameType::SETTINGS, 0, 0);
   FrameParts expected(header);
@@ -731,11 +736,11 @@
 
 TEST_F(Http2FrameDecoderTest, PushPromiseTooShort) {
   const char kFrameData[] = {
-      0x00, 0x00, 3,           // Payload length: 3
-      0x05,                    // PUSH_PROMISE
-      0x00,                    // Flags: none
-      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
-      0x00, 0x00, 0x00,        // Truncated promise id
+      '\x00', '\x00', 3,               // Payload length: 3
+      '\x05',                          // PUSH_PROMISE
+      '\x00',                          // Flags: none
+      '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1
+      '\x00', '\x00', '\x00',          // Truncated promise id
   };
   Http2FrameHeader header(3, Http2FrameType::PUSH_PROMISE, 0, 1);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -743,12 +748,12 @@
 
 TEST_F(Http2FrameDecoderTest, PushPromisePaddedTruncatedPromise) {
   const char kFrameData[] = {
-      0x00, 0x00, 4,           // Payload length: 4
-      0x05,                    // PUSH_PROMISE
-      0x08,                    // Flags: PADDED
-      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
-      0x00,                    // Pad Len
-      0x00, 0x00, 0x00,        // Truncated promise id
+      '\x00', '\x00', 4,               // Payload length: 4
+      '\x05',                          // PUSH_PROMISE
+      '\x08',                          // Flags: PADDED
+      '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1
+      '\x00',                          // Pad Len
+      '\x00', '\x00', '\x00',          // Truncated promise id
   };
   Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE,
                           Http2FrameFlag::PADDED, 1);
@@ -757,12 +762,12 @@
 
 TEST_F(Http2FrameDecoderTest, PingTooShort) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x07,        //   Length: 8
-      0x06,                     //     Type: PING
-      0xfeu,                    //    Flags: no valid flags
-      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
-      's',   'o',  'm',  'e',   // "some"
-      'd',   'a',  't',         // Too little
+      '\x00', '\x00', '\x07',          //   Length: 8
+      '\x06',                          //     Type: PING
+      '\xfe',                          //    Flags: no valid flags
+      '\x00', '\x00', '\x00', '\x00',  //   Stream: 0
+      's',    'o',    'm',    'e',     // "some"
+      'd',    'a',    't',             // Too little
   };
   Http2FrameHeader header(7, Http2FrameType::PING, 0, 0);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -770,10 +775,10 @@
 
 TEST_F(Http2FrameDecoderTest, GoAwayTooShort) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x00,        // Length: 0
-      0x07,                     //   Type: GOAWAY
-      0xffu,                    //  Flags: 0xff (no valid flags)
-      0x00,  0x00, 0x00, 0x00,  // Stream: 0
+      '\x00', '\x00', '\x00',          // Length: 0
+      '\x07',                          //   Type: GOAWAY
+      '\xff',                          //  Flags: 0xff (no valid flags)
+      '\x00', '\x00', '\x00', '\x00',  // Stream: 0
   };
   Http2FrameHeader header(0, Http2FrameType::GOAWAY, 0, 0);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -781,11 +786,11 @@
 
 TEST_F(Http2FrameDecoderTest, WindowUpdateTooShort) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x03,        // Length: 3
-      0x08,                     //   Type: WINDOW_UPDATE
-      0x0f,                     //  Flags: 0xff (no valid flags)
-      0x00,  0x00, 0x00, 0x01,  // Stream: 1
-      0x80u, 0x00, 0x04,        // Truncated
+      '\x00', '\x00', '\x03',          // Length: 3
+      '\x08',                          //   Type: WINDOW_UPDATE
+      '\x0f',                          //  Flags: 0xff (no valid flags)
+      '\x00', '\x00', '\x00', '\x01',  // Stream: 1
+      '\x80', '\x00', '\x04',          // Truncated
   };
   Http2FrameHeader header(3, Http2FrameType::WINDOW_UPDATE, 0, 1);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -793,11 +798,11 @@
 
 TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOriginLength) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x01,        // Payload length: 3
-      0x0a,                    // ALTSVC
-      0x00,                    // Flags: none
-      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
-      0x00,                    // Origin Length: truncated
+      '\x00', '\x00', '\x01',          // Payload length: 3
+      '\x0a',                          // ALTSVC
+      '\x00',                          // Flags: none
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2
+      '\x00',                          // Origin Length: truncated
   };
   Http2FrameHeader header(1, Http2FrameType::ALTSVC, 0, 2);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -805,12 +810,12 @@
 
 TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOrigin) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x05,        // Payload length: 3
-      0x0a,                    // ALTSVC
-      0x00,                    // Flags: none
-      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
-      0x00, 0x04,              // Origin Length: 4 (too long)
-      'a',  'b',  'c',         // Origin
+      '\x00', '\x00', '\x05',          // Payload length: 3
+      '\x0a',                          // ALTSVC
+      '\x00',                          // Flags: none
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2
+      '\x00', '\x04',                  // Origin Length: 4 (too long)
+      'a',    'b',    'c',             // Origin
   };
   Http2FrameHeader header(5, Http2FrameType::ALTSVC, 0, 2);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -824,13 +829,13 @@
 TEST_F(Http2FrameDecoderTest, BeyondMaximum) {
   decoder_.set_maximum_payload_size(2);
   const char kFrameData[] = {
-      0x00, 0x00, 0x07,        // Payload length: 7
-      0x00,                    // DATA
-      0x09,                    // Flags: END_STREAM | PADDED
-      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
-      0x03,                    // Pad Len
-      'a',  'b',  'c',         // Data
-      0x00, 0x00, 0x00,        // Padding
+      '\x00', '\x00', '\x07',          // Payload length: 7
+      '\x00',                          // DATA
+      '\x09',                          // Flags: END_STREAM | PADDED
+      '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID)
+      '\x03',                          // Pad Len
+      'a',    'b',    'c',             // Data
+      '\x00', '\x00', '\x00',          // Padding
   };
   Http2FrameHeader header(7, Http2FrameType::DATA,
                           Http2FrameFlag::END_STREAM | Http2FrameFlag::PADDED,
@@ -854,13 +859,13 @@
 
 TEST_F(Http2FrameDecoderTest, PriorityTooLong) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x06,        // Length: 5
-      0x02,                     //   Type: PRIORITY
-      0x00,                     //  Flags: none
-      0x00,  0x00, 0x00, 0x02,  // Stream: 2
-      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
-      0x10,                     // Weight: 17
-      0x00,                     // Too much
+      '\x00', '\x00', '\x06',          // Length: 5
+      '\x02',                          //   Type: PRIORITY
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x02',  // Stream: 2
+      '\x80', '\x00', '\x00', '\x01',  // Parent: 1 (Exclusive)
+      '\x10',                          // Weight: 17
+      '\x00',                          // Too much
   };
   Http2FrameHeader header(6, Http2FrameType::PRIORITY, 0, 2);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -868,12 +873,12 @@
 
 TEST_F(Http2FrameDecoderTest, RstStreamTooLong) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x05,        // Length: 4
-      0x03,                    //   Type: RST_STREAM
-      0x00,                    //  Flags: none
-      0x00, 0x00, 0x00, 0x01,  // Stream: 1
-      0x00, 0x00, 0x00, 0x01,  //  Error: PROTOCOL_ERROR
-      0x00,                    // Too much
+      '\x00', '\x00', '\x05',          // Length: 4
+      '\x03',                          //   Type: RST_STREAM
+      '\x00',                          //  Flags: none
+      '\x00', '\x00', '\x00', '\x01',  // Stream: 1
+      '\x00', '\x00', '\x00', '\x01',  //  Error: PROTOCOL_ERROR
+      '\x00',                          // Too much
   };
   Http2FrameHeader header(5, Http2FrameType::RST_STREAM, 0, 1);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -881,12 +886,12 @@
 
 TEST_F(Http2FrameDecoderTest, SettingsAckTooLong) {
   const char kFrameData[] = {
-      0x00, 0x00, 0x06,        //   Length: 6
-      0x04,                    //     Type: SETTINGS
-      0x01,                    //    Flags: ACK
-      0x00, 0x00, 0x00, 0x00,  //   Stream: 0
-      0x00, 0x00,              //   Extra
-      0x00, 0x00, 0x00, 0x00,  //   Extra
+      '\x00', '\x00', '\x06',          //   Length: 6
+      '\x04',                          //     Type: SETTINGS
+      '\x01',                          //    Flags: ACK
+      '\x00', '\x00', '\x00', '\x00',  //   Stream: 0
+      '\x00', '\x00',                  //   Extra
+      '\x00', '\x00', '\x00', '\x00',  //   Extra
   };
   Http2FrameHeader header(6, Http2FrameType::SETTINGS, Http2FrameFlag::ACK, 0);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -894,13 +899,13 @@
 
 TEST_F(Http2FrameDecoderTest, PingAckTooLong) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x09,        //   Length: 8
-      0x06,                     //     Type: PING
-      0xffu,                    //    Flags: ACK | 0xfe
-      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
-      's',   'o',  'm',  'e',   // "some"
-      'd',   'a',  't',  'a',   // "data"
-      0x00,                     // Too much
+      '\x00', '\x00', '\x09',          //   Length: 8
+      '\x06',                          //     Type: PING
+      '\xff',                          //    Flags: ACK | 0xfe
+      '\x00', '\x00', '\x00', '\x00',  //   Stream: 0
+      's',    'o',    'm',    'e',     // "some"
+      'd',    'a',    't',    'a',     // "data"
+      '\x00',                          // Too much
   };
   Http2FrameHeader header(9, Http2FrameType::PING, Http2FrameFlag::ACK, 0);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
@@ -908,12 +913,12 @@
 
 TEST_F(Http2FrameDecoderTest, WindowUpdateTooLong) {
   const char kFrameData[] = {
-      0x00,  0x00, 0x05,        // Length: 5
-      0x08,                     //   Type: WINDOW_UPDATE
-      0x0f,                     //  Flags: 0xff (no valid flags)
-      0x00,  0x00, 0x00, 0x01,  // Stream: 1
-      0x80u, 0x00, 0x04, 0x00,  //   Incr: 1024 (plus R bit)
-      0x00,                     // Too much
+      '\x00', '\x00', '\x05',          // Length: 5
+      '\x08',                          //   Type: WINDOW_UPDATE
+      '\x0f',                          //  Flags: 0xff (no valid flags)
+      '\x00', '\x00', '\x00', '\x01',  // Stream: 1
+      '\x80', '\x00', '\x04', '\x00',  //   Incr: 1024 (plus R bit)
+      '\x00',                          // Too much
   };
   Http2FrameHeader header(5, Http2FrameType::WINDOW_UPDATE, 0, 1);
   EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
diff --git a/net/third_party/http2/hpack/decoder/hpack_entry_decoder_test.cc b/net/third_party/http2/hpack/decoder/hpack_entry_decoder_test.cc
index 952dbf1d..cf0e9fc 100644
--- a/net/third_party/http2/hpack/decoder/hpack_entry_decoder_test.cc
+++ b/net/third_party/http2/hpack/decoder/hpack_entry_decoder_test.cc
@@ -56,7 +56,7 @@
 
 TEST_F(HpackEntryDecoderTest, IndexedHeader_Literals) {
   {
-    const char input[] = {0x82u};  // == Index 2 ==
+    const char input[] = {'\x82'};  // == Index 2 ==
     DecodeBuffer b(input);
     auto do_check = [this]() {
       VERIFY_AND_RETURN_SUCCESS(collector_.ValidateIndexedHeader(2));
@@ -67,7 +67,7 @@
   }
   collector_.Clear();
   {
-    const char input[] = {0xfeu};  // == Index 126 ==
+    const char input[] = {'\xfe'};  // == Index 126 ==
     DecodeBuffer b(input);
     auto do_check = [this]() {
       VERIFY_AND_RETURN_SUCCESS(collector_.ValidateIndexedHeader(126));
@@ -78,7 +78,7 @@
   }
   collector_.Clear();
   {
-    const char input[] = {0xffu, 0x00};  // == Index 127 ==
+    const char input[] = {'\xff', '\x00'};  // == Index 127 ==
     DecodeBuffer b(input);
     auto do_check = [this]() {
       VERIFY_AND_RETURN_SUCCESS(collector_.ValidateIndexedHeader(127));
diff --git a/net/third_party/http2/hpack/decoder/hpack_string_decoder_test.cc b/net/third_party/http2/hpack/decoder/hpack_string_decoder_test.cc
index 996895da..57a6f3c 100644
--- a/net/third_party/http2/hpack/decoder/hpack_string_decoder_test.cc
+++ b/net/third_party/http2/hpack/decoder/hpack_string_decoder_test.cc
@@ -81,7 +81,7 @@
 TEST_F(HpackStringDecoderTest, DecodeEmptyString) {
   {
     Validator validator = ValidateDoneAndEmpty(MakeValidator("", kCompressed));
-    const char kData[] = {0x80u};
+    const char kData[] = {'\x80'};
     DecodeBuffer b(kData);
     EXPECT_TRUE(
         DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
@@ -90,7 +90,7 @@
     // Make sure it stops after decoding the empty string.
     Validator validator =
         ValidateDoneAndOffset(1, MakeValidator("", kUncompressed));
-    const char kData[] = {0x00, 0xffu};
+    const char kData[] = {'\x00', '\xff'};
     DecodeBuffer b(kData);
     EXPECT_EQ(2u, b.Remaining());
     EXPECT_TRUE(
diff --git a/net/third_party/http2/hpack/tools/hpack_block_builder_test.cc b/net/third_party/http2/hpack/tools/hpack_block_builder_test.cc
index 4cd786e..33e8a5f 100644
--- a/net/third_party/http2/hpack/tools/hpack_block_builder_test.cc
+++ b/net/third_party/http2/hpack/tools/hpack_block_builder_test.cc
@@ -115,9 +115,9 @@
     b.AppendIndexedHeader(kStaticTableMethodGET);
     b.AppendIndexedHeader(kStaticTableSchemeHttp);
     b.AppendIndexedHeader(kStaticTablePathSlash);
-    const char kHuffmanWwwExampleCom[] = {0xf1u, 0xe3u, 0xc2u, 0xe5u,
-                                          0xf2u, 0x3au, 0x6bu, 0xa0u,
-                                          0xabu, 0x90u, 0xf4u, 0xffu};
+    const char kHuffmanWwwExampleCom[] = {'\xf1', '\xe3', '\xc2', '\xe5',
+                                          '\xf2', '\x3a', '\x6b', '\xa0',
+                                          '\xab', '\x90', '\xf4', '\xff'};
     b.AppendNameIndexAndLiteralValue(
         HpackEntryType::kIndexedLiteralHeader, 1, kCompressed,
         Http2StringPiece(kHuffmanWwwExampleCom, sizeof kHuffmanWwwExampleCom));
@@ -139,7 +139,7 @@
     b.AppendDynamicTableSizeUpdate(0);
     EXPECT_EQ(1u, b.size());
 
-    const char kData[] = {0x20};
+    const char kData[] = {'\x20'};
     Http2StringPiece expected(kData, sizeof kData);
     EXPECT_EQ(expected, b.buffer());
   }
@@ -148,7 +148,7 @@
     b.AppendDynamicTableSizeUpdate(4096);  // The default size.
     EXPECT_EQ(3u, b.size());
 
-    const char kData[] = {0x3f, 0xe1u, 0x1f};
+    const char kData[] = {'\x3f', '\xe1', '\x1f'};
     Http2StringPiece expected(kData, sizeof kData);
     EXPECT_EQ(expected, b.buffer());
   }
@@ -157,7 +157,8 @@
     b.AppendDynamicTableSizeUpdate(1000000000000);  // A very large value.
     EXPECT_EQ(7u, b.size());
 
-    const char kData[] = {0x3fu, 0xe1u, 0x9fu, 0x94u, 0xa5u, 0x8du, 0x1du};
+    const char kData[] = {'\x3f', '\xe1', '\x9f', '\x94',
+                          '\xa5', '\x8d', '\x1d'};
     Http2StringPiece expected(kData, sizeof kData);
     EXPECT_EQ(expected, b.buffer());
   }
diff --git a/net/third_party/quic/core/congestion_control/bbr_sender.cc b/net/third_party/quic/core/congestion_control/bbr_sender.cc
index 7546844..02b29f5 100644
--- a/net/third_party/quic/core/congestion_control/bbr_sender.cc
+++ b/net/third_party/quic/core/congestion_control/bbr_sender.cc
@@ -127,6 +127,8 @@
       is_app_limited_recovery_(false),
       slower_startup_(false),
       rate_based_startup_(false),
+      startup_rate_reduction_multiplier_(0),
+      startup_bytes_lost_(0),
       initial_conservation_in_startup_(CONSERVATION),
       enable_ack_aggregation_during_startup_(false),
       expire_ack_aggregation_in_startup_(false),
@@ -267,6 +269,18 @@
   if (config.HasClientRequestedIndependentOption(kBBS3, perspective)) {
     initial_conservation_in_startup_ = GROWTH;
   }
+  if (GetQuicReloadableFlag(quic_bbr_startup_rate_reduction) &&
+      config.HasClientRequestedIndependentOption(kBBS4, perspective)) {
+    rate_based_startup_ = true;
+    // Hits 1.25x pacing multiplier when ~2/3 CWND is lost.
+    startup_rate_reduction_multiplier_ = 1;
+  }
+  if (GetQuicReloadableFlag(quic_bbr_startup_rate_reduction) &&
+      config.HasClientRequestedIndependentOption(kBBS5, perspective)) {
+    rate_based_startup_ = true;
+    // Hits 1.25x pacing multiplier when ~1/3 CWND is lost.
+    startup_rate_reduction_multiplier_ = 2;
+  }
   if (config.HasClientRequestedIndependentOption(kBBR4, perspective)) {
     max_ack_height_.SetWindowLength(2 * kBandwidthWindowSize);
   }
@@ -446,6 +460,9 @@
 void BbrSender::DiscardLostPackets(const LostPacketVector& lost_packets) {
   for (const LostPacket& packet : lost_packets) {
     sampler_.OnPacketLost(packet.packet_number);
+    if (startup_rate_reduction_multiplier_ != 0 && mode_ == STARTUP) {
+      startup_bytes_lost_ += packet.bytes_lost;
+    }
   }
 }
 
@@ -749,7 +766,23 @@
     return;
   }
 
-  // Do not decrease the pacing rate during the startup.
+  // Slow the pacing rate in STARTUP by the bytes_lost / CWND.
+  if (startup_rate_reduction_multiplier_ != 0 && has_ever_detected_loss &&
+      has_non_app_limited_sample_) {
+    if (startup_bytes_lost_ > congestion_window_) {
+      pacing_rate_ = BandwidthEstimate();
+    } else {
+      pacing_rate_ =
+          (1 - (startup_bytes_lost_ * startup_rate_reduction_multiplier_ *
+                1.0f / congestion_window_)) *
+          target_rate;
+      // Ensure the pacing rate doesn't drop below the bandwidth estimate.
+      pacing_rate_ = std::max(pacing_rate_, BandwidthEstimate());
+    }
+    return;
+  }
+
+  // Do not decrease the pacing rate during startup.
   pacing_rate_ = std::max(pacing_rate_, target_rate);
 }
 
diff --git a/net/third_party/quic/core/congestion_control/bbr_sender.h b/net/third_party/quic/core/congestion_control/bbr_sender.h
index a8b2ea2..a33f65f 100644
--- a/net/third_party/quic/core/congestion_control/bbr_sender.h
+++ b/net/third_party/quic/core/congestion_control/bbr_sender.h
@@ -369,6 +369,12 @@
   bool slower_startup_;
   // When true, disables packet conservation in STARTUP.
   bool rate_based_startup_;
+  // When non-zero, decreases the rate in STARTUP by the total number of bytes
+  // lost in STARTUP divided by CWND.
+  uint8_t startup_rate_reduction_multiplier_;
+  // Sum of bytes lost in STARTUP.
+  QuicByteCount startup_bytes_lost_;
+
   // Used as the initial packet conservation mode when first entering recovery.
   RecoveryState initial_conservation_in_startup_;
   // When true, add the most recent ack aggregation measurement during STARTUP.
diff --git a/net/third_party/quic/core/congestion_control/bbr_sender_test.cc b/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
index 11d366e29..9b13a9d6 100644
--- a/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
+++ b/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
@@ -1152,6 +1152,106 @@
   EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
 }
 
+// Ensures no change in congestion window in STARTUP after loss, but that the
+// rate decreases.
+TEST_F(BbrSenderTest, SimpleTransferStartupRateReduction) {
+  SetQuicReloadableFlag(quic_bbr_startup_rate_reduction, true);
+  CreateSmallBufferSetup();
+
+  SetConnectionOption(kBBS4);
+
+  // Run until the full bandwidth is reached and check how many rounds it was.
+  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+  bool used_conservation_cwnd = false;
+  bool simulator_result = simulator_.RunUntilOrTimeout(
+      [this, &used_conservation_cwnd]() {
+        if (!sender_->ExportDebugState().is_at_full_bandwidth &&
+            sender_->GetCongestionWindow() <
+                sender_->ExportDebugState().congestion_window) {
+          used_conservation_cwnd = true;
+        }
+        // Exit once a loss is hit.
+        return bbr_sender_.connection()->GetStats().packets_lost > 0 ||
+               sender_->ExportDebugState().is_at_full_bandwidth;
+      },
+      QuicTime::Delta::FromSeconds(5));
+  ASSERT_TRUE(simulator_result);
+  EXPECT_TRUE(sender_->InRecovery());
+  EXPECT_FALSE(used_conservation_cwnd);
+  EXPECT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
+  EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost);
+
+  // Lose each outstanding packet and the pacing rate decreases.
+  const QuicBandwidth original_pacing_rate = sender_->PacingRate(0);
+  QuicBandwidth pacing_rate = original_pacing_rate;
+  const QuicByteCount original_cwnd = sender_->GetCongestionWindow();
+  LostPacketVector lost_packets;
+  lost_packets.push_back(LostPacket(0, kMaxPacketSize));
+  QuicPacketNumber largest_sent =
+      bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket();
+  for (QuicPacketNumber packet_number =
+           bbr_sender_.connection()->sent_packet_manager().GetLeastUnacked();
+       packet_number <= largest_sent; ++packet_number) {
+    lost_packets[0].packet_number = packet_number;
+    sender_->OnCongestionEvent(false, 0, clock_->Now(), {}, lost_packets);
+    EXPECT_EQ(original_cwnd, sender_->GetCongestionWindow());
+    EXPECT_GT(original_pacing_rate, sender_->PacingRate(0));
+    EXPECT_GE(pacing_rate, sender_->PacingRate(0));
+    EXPECT_LE(sender_->BandwidthEstimate(), sender_->PacingRate(0));
+    pacing_rate = sender_->PacingRate(0);
+  }
+}
+
+// Ensures no change in congestion window in STARTUP after loss, but that the
+// rate decreases twice as fast as BBS4.
+TEST_F(BbrSenderTest, SimpleTransferDoubleStartupRateReduction) {
+  SetQuicReloadableFlag(quic_bbr_startup_rate_reduction, true);
+  CreateSmallBufferSetup();
+
+  SetConnectionOption(kBBS5);
+
+  // Run until the full bandwidth is reached and check how many rounds it was.
+  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+  bool used_conservation_cwnd = false;
+  bool simulator_result = simulator_.RunUntilOrTimeout(
+      [this, &used_conservation_cwnd]() {
+        if (!sender_->ExportDebugState().is_at_full_bandwidth &&
+            sender_->GetCongestionWindow() <
+                sender_->ExportDebugState().congestion_window) {
+          used_conservation_cwnd = true;
+        }
+        // Exit once a loss is hit.
+        return bbr_sender_.connection()->GetStats().packets_lost > 0 ||
+               sender_->ExportDebugState().is_at_full_bandwidth;
+      },
+      QuicTime::Delta::FromSeconds(5));
+  ASSERT_TRUE(simulator_result);
+  EXPECT_TRUE(sender_->InRecovery());
+  EXPECT_FALSE(used_conservation_cwnd);
+  EXPECT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
+  EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost);
+
+  // Lose each outstanding packet and the pacing rate decreases.
+  const QuicBandwidth original_pacing_rate = sender_->PacingRate(0);
+  QuicBandwidth pacing_rate = original_pacing_rate;
+  const QuicByteCount original_cwnd = sender_->GetCongestionWindow();
+  LostPacketVector lost_packets;
+  lost_packets.push_back(LostPacket(0, kMaxPacketSize));
+  QuicPacketNumber largest_sent =
+      bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket();
+  for (QuicPacketNumber packet_number =
+           bbr_sender_.connection()->sent_packet_manager().GetLeastUnacked();
+       packet_number <= largest_sent; ++packet_number) {
+    lost_packets[0].packet_number = packet_number;
+    sender_->OnCongestionEvent(false, 0, clock_->Now(), {}, lost_packets);
+    EXPECT_EQ(original_cwnd, sender_->GetCongestionWindow());
+    EXPECT_GT(original_pacing_rate, sender_->PacingRate(0));
+    EXPECT_GE(pacing_rate, sender_->PacingRate(0));
+    EXPECT_LE(sender_->BandwidthEstimate(), sender_->PacingRate(0));
+    pacing_rate = sender_->PacingRate(0);
+  }
+}
+
 TEST_F(BbrSenderTest, DerivedPacingGainStartup) {
   SetQuicReloadableFlag(quic_bbr_slower_startup3, true);
   CreateDefaultSetup();
diff --git a/net/third_party/quic/core/crypto/crypto_protocol.h b/net/third_party/quic/core/crypto/crypto_protocol.h
index 8bee977..b4c185c 100644
--- a/net/third_party/quic/core/crypto/crypto_protocol.h
+++ b/net/third_party/quic/core/crypto/crypto_protocol.h
@@ -86,6 +86,10 @@
                                                  // conservation in BBR STARTUP
 const QuicTag kBBS3 = TAG('B', 'B', 'S', '3');   // Slowstart packet
                                                  // conservation in BBR STARTUP
+const QuicTag kBBS4 = TAG('B', 'B', 'S', '4');   // Reduce rate in STARTUP by
+                                                 // bytes_lost / CWND.
+const QuicTag kBBS5 = TAG('B', 'B', 'S', '5');   // Reduce rate in STARTUP by
+                                                 // 2 * bytes_lost / CWND.
 const QuicTag kBBRR = TAG('B', 'B', 'R', 'R');   // Rate-based recovery in BBR
 const QuicTag kBBR1 = TAG('B', 'B', 'R', '1');   // DEPRECATED
 const QuicTag kBBR2 = TAG('B', 'B', 'R', '2');   // DEPRECATED
diff --git a/services/audio/loopback_stream.cc b/services/audio/loopback_stream.cc
index 9755a46..11d741b21 100644
--- a/services/audio/loopback_stream.cc
+++ b/services/audio/loopback_stream.cc
@@ -376,29 +376,35 @@
   // or more intervals.
   const int frames_per_buffer = mix_bus_->frames();
   frames_elapsed_ += frames_per_buffer;
-  const base::TimeTicks now = clock_->NowTicks();
-  const int64_t required_frames_elapsed =
-      (now - first_generate_time_).InMicroseconds() *
-      output_params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
-  if (frames_elapsed_ < required_frames_elapsed) {
-    TRACE_EVENT_INSTANT1("audio", "GenerateMoreAudio Is Behind",
-                         TRACE_EVENT_SCOPE_THREAD, "frames_behind",
-                         (required_frames_elapsed - frames_elapsed_));
-    // Audio generation has fallen behind. Skip-ahead the frame counter so that
-    // audio generation will resume for the next buffer after the one that
-    // should be generating right now. http://crbug.com/847487
-    const int64_t required_buffers_elapsed =
-        ((required_frames_elapsed + frames_per_buffer - 1) / frames_per_buffer);
-    frames_elapsed_ = (required_buffers_elapsed + 1) * frames_per_buffer;
-  }
   next_generate_time_ =
       first_generate_time_ +
       base::TimeDelta::FromMicroseconds(frames_elapsed_ *
                                         base::Time::kMicrosecondsPerSecond /
                                         output_params_.sample_rate());
+  const base::TimeTicks now = clock_->NowTicks();
+  if (next_generate_time_ < now) {
+    TRACE_EVENT_INSTANT1("audio", "GenerateMoreAudio Is Behind",
+                         TRACE_EVENT_SCOPE_THREAD, u8"µsec_behind",
+                         (now - next_generate_time_).InMicroseconds());
+    // Audio generation has fallen behind. Skip-ahead the frame counter so that
+    // audio generation will resume for the next buffer after the one that
+    // should be generating right now. http://crbug.com/847487
+    const int64_t target_frame_count =
+        (now - first_generate_time_).InMicroseconds() *
+        output_params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
+    frames_elapsed_ =
+        (target_frame_count / frames_per_buffer + 1) * frames_per_buffer;
+    next_generate_time_ =
+        first_generate_time_ +
+        base::TimeDelta::FromMicroseconds(frames_elapsed_ *
+                                          base::Time::kMicrosecondsPerSecond /
+                                          output_params_.sample_rate());
+  }
 
-  // Use the OneShotTimer to call this method again at the desired time.
-  DCHECK_GE(next_generate_time_ - now, base::TimeDelta());
+  // Note: It's acceptable for |next_generate_time_| to be slightly before |now|
+  // due to integer truncation behaviors in the math above. The timer task
+  // started below will just run immediately and there will be no harmful
+  // effects in the next GenerateMoreAudio() call. http://crbug.com/847487
   timer_->Start(FROM_HERE, next_generate_time_ - now, this,
                 &FlowNetwork::GenerateMoreAudio);
 }
diff --git a/services/device/time_zone_monitor/time_zone_monitor_linux.cc b/services/device/time_zone_monitor/time_zone_monitor_linux.cc
index aae2faa8..bceb85f 100644
--- a/services/device/time_zone_monitor/time_zone_monitor_linux.cc
+++ b/services/device/time_zone_monitor/time_zone_monitor_linux.cc
@@ -17,7 +17,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
-#include "base/threading/thread_restrictions.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 
@@ -88,9 +88,15 @@
   ~TimeZoneMonitorLinuxImpl() { DCHECK(!owner_); }
 
   void StartWatchingOnFileThread() {
-    base::AssertBlockingAllowedDeprecated();
     DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
 
+    auto callback =
+        base::BindRepeating(&TimeZoneMonitorLinuxImpl::OnTimeZoneFileChanged,
+                            base::RetainedRef(this));
+
+    base::ScopedBlockingCall scoped_blocking_call(
+        base::BlockingType::MAY_BLOCK);
+
     // There is no true standard for where time zone information is actually
     // stored. glibc uses /etc/localtime, uClibc uses /etc/TZ, and some older
     // systems store the name of the time zone file within /usr/share/zoneinfo
@@ -101,10 +107,6 @@
     const char* const kFilesToWatch[] = {
         "/etc/localtime", "/etc/timezone", "/etc/TZ",
     };
-
-    auto callback =
-        base::BindRepeating(&TimeZoneMonitorLinuxImpl::OnTimeZoneFileChanged,
-                            base::RetainedRef(this));
     for (size_t index = 0; index < arraysize(kFilesToWatch); ++index) {
       file_path_watchers_.push_back(std::make_unique<base::FilePathWatcher>());
       file_path_watchers_.back()->Watch(base::FilePath(kFilesToWatch[index]),
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn
index 5301bd1..749cace 100644
--- a/services/network/BUILD.gn
+++ b/services/network/BUILD.gn
@@ -193,6 +193,8 @@
       "cert_verifier_with_trust_anchors.h",
       "cert_verify_proc_chromeos.cc",
       "cert_verify_proc_chromeos.h",
+      "nss_temp_certs_cache_chromeos.cc",
+      "nss_temp_certs_cache_chromeos.h",
     ]
   }
 
@@ -338,6 +340,7 @@
     sources += [
       "cert_verifier_with_trust_anchors_unittest.cc",
       "cert_verify_proc_chromeos_unittest.cc",
+      "nss_temp_certs_cache_chromeos_unittest.cc",
     ]
   }
 
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 8e4d8eb..7def63c 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -104,6 +104,7 @@
 #include "net/cert/multi_threaded_cert_verifier.h"
 #include "services/network/cert_verifier_with_trust_anchors.h"
 #include "services/network/cert_verify_proc_chromeos.h"
+#include "services/network/nss_temp_certs_cache_chromeos.h"
 #endif
 
 #if !defined(OS_IOS)
@@ -993,9 +994,17 @@
 }
 
 #if defined(OS_CHROMEOS)
-void NetworkContext::UpdateTrustAnchors(
-    const net::CertificateList& trust_anchors) {
-  cert_verifier_with_trust_anchors_->SetTrustAnchors(trust_anchors);
+void NetworkContext::UpdateAdditionalCertificates(
+    mojom::AdditionalCertificatesPtr additional_certificates) {
+  if (!additional_certificates) {
+    nss_temp_certs_cache_.reset();
+    cert_verifier_with_trust_anchors_->SetTrustAnchors(net::CertificateList());
+    return;
+  }
+  nss_temp_certs_cache_ = std::make_unique<network::NSSTempCertsCacheChromeOS>(
+      additional_certificates->all_certificates);
+  cert_verifier_with_trust_anchors_->SetTrustAnchors(
+      additional_certificates->trust_anchors);
 }
 #endif
 
@@ -1980,8 +1989,8 @@
 
       cert_verifier_with_trust_anchors_ = new CertVerifierWithTrustAnchors(
           base::Bind(&NetworkContext::TrustAnchorUsed, base::Unretained(this)));
-      cert_verifier_with_trust_anchors_->SetTrustAnchors(
-          params_->initial_trust_anchors);
+      UpdateAdditionalCertificates(
+          std::move(params_->initial_additional_certificates));
       cert_verifier_with_trust_anchors_->InitializeOnIOThread(verify_proc);
       cert_verifier = base::WrapUnique(cert_verifier_with_trust_anchors_);
     }
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 1752ada..d105ee7 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -76,6 +76,7 @@
 class NetworkService;
 class NetworkServiceProxyDelegate;
 class MdnsResponderManager;
+class NSSTempCertsCacheChromeOS;
 class P2PSocketManager;
 class ProxyLookupRequest;
 class ResourceScheduler;
@@ -210,7 +211,8 @@
   void SetAcceptLanguage(const std::string& new_accept_language) override;
   void SetEnableReferrers(bool enable_referrers) override;
 #if defined(OS_CHROMEOS)
-  void UpdateTrustAnchors(const net::CertificateList& trust_anchors) override;
+  void UpdateAdditionalCertificates(
+      mojom::AdditionalCertificatesPtr additional_certificates) override;
 #endif
 #if BUILDFLAG(IS_CT_SUPPORTED)
   void SetCTPolicy(
@@ -499,6 +501,9 @@
 
 #if defined(OS_CHROMEOS)
   CertVerifierWithTrustAnchors* cert_verifier_with_trust_anchors_ = nullptr;
+  // Additional certificates made available to NSS cert validation as temporary
+  // certificates.
+  std::unique_ptr<network::NSSTempCertsCacheChromeOS> nss_temp_certs_cache_;
 #endif
 
   // Created on-demand. Null if unused.
diff --git a/services/network/nss_temp_certs_cache_chromeos.cc b/services/network/nss_temp_certs_cache_chromeos.cc
new file mode 100644
index 0000000..cd20188f
--- /dev/null
+++ b/services/network/nss_temp_certs_cache_chromeos.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/network/nss_temp_certs_cache_chromeos.h"
+
+#include "net/cert/x509_util_nss.h"
+
+namespace network {
+
+NSSTempCertsCacheChromeOS::NSSTempCertsCacheChromeOS(
+    const net::CertificateList& certificates) {
+  for (const auto& certificate : certificates) {
+    net::ScopedCERTCertificate x509_cert =
+        net::x509_util::CreateCERTCertificateFromX509Certificate(
+            certificate.get());
+    if (!x509_cert) {
+      LOG(ERROR) << "Unable to create CERTCertificate";
+      continue;
+    }
+
+    temp_certs_.push_back(std::move(x509_cert));
+  }
+}
+
+NSSTempCertsCacheChromeOS::~NSSTempCertsCacheChromeOS() {}
+
+}  // namespace network
diff --git a/chrome/browser/chromeos/policy/temp_certs_cache_nss.h b/services/network/nss_temp_certs_cache_chromeos.h
similarity index 69%
rename from chrome/browser/chromeos/policy/temp_certs_cache_nss.h
rename to services/network/nss_temp_certs_cache_chromeos.h
index d305dc1..bda81c6 100644
--- a/chrome/browser/chromeos/policy/temp_certs_cache_nss.h
+++ b/services/network/nss_temp_certs_cache_chromeos.h
@@ -2,21 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POLICY_TEMP_CERTS_CACHE_NSS_H_
-#define CHROME_BROWSER_CHROMEOS_POLICY_TEMP_CERTS_CACHE_NSS_H_
+#ifndef SERVICES_NETWORK_NSS_TEMP_CERTS_CACHE_CHROMEOS_H_
+#define SERVICES_NETWORK_NSS_TEMP_CERTS_CACHE_CHROMEOS_H_
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "net/cert/scoped_nss_types.h"
 #include "net/cert/x509_certificate.h"
 
-namespace policy {
+namespace network {
 
 // Holds NSS temporary certificates in memory as ScopedCERTCertificates, making
 // them available e.g. for client certificate discovery.
-class TempCertsCacheNSS {
+class COMPONENT_EXPORT(NETWORK_SERVICE) NSSTempCertsCacheChromeOS {
  public:
-  explicit TempCertsCacheNSS(const net::CertificateList& certificates);
-  ~TempCertsCacheNSS();
+  explicit NSSTempCertsCacheChromeOS(const net::CertificateList& certificates);
+  ~NSSTempCertsCacheChromeOS();
 
  private:
   // The actual cache of NSS temporary certificates.
@@ -30,9 +31,9 @@
   // permanent databases, nor are the trust settings mutated to trust them.
   net::ScopedCERTCertificateList temp_certs_;
 
-  DISALLOW_COPY_AND_ASSIGN(TempCertsCacheNSS);
+  DISALLOW_COPY_AND_ASSIGN(NSSTempCertsCacheChromeOS);
 };
 
-}  // namespace policy
+}  // namespace network
 
-#endif  // CHROME_BROWSER_CHROMEOS_POLICY_TEMP_CERTS_CACHE_NSS_H_
+#endif  // SERVICES_NETWORK_NSS_TEMP_CERTS_CACHE_CHROMEOS_H_
diff --git a/chrome/browser/chromeos/policy/temp_certs_cache_nss_unittest.cc b/services/network/nss_temp_certs_cache_chromeos_unittest.cc
similarity index 87%
rename from chrome/browser/chromeos/policy/temp_certs_cache_nss_unittest.cc
rename to services/network/nss_temp_certs_cache_chromeos_unittest.cc
index 41e7355a..b1b995d 100644
--- a/chrome/browser/chromeos/policy/temp_certs_cache_nss_unittest.cc
+++ b/services/network/nss_temp_certs_cache_chromeos_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/policy/temp_certs_cache_nss.h"
+#include "services/network/nss_temp_certs_cache_chromeos.h"
 
 #include <cert.h>
 #include <certdb.h>
@@ -23,14 +23,14 @@
 #include "net/test/test_data_directory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace policy {
+namespace network {
 
 namespace {
 
-class TempCertsCacheNSSTest : public testing::Test {
+class NSSTempCertsCacheChromeOSTest : public testing::Test {
  public:
-  TempCertsCacheNSSTest() {}
-  ~TempCertsCacheNSSTest() override {}
+  NSSTempCertsCacheChromeOSTest() {}
+  ~NSSTempCertsCacheChromeOSTest() override {}
 
  protected:
   // Checks if the certificate stored in |pem_cert_file| can be found in the
@@ -92,17 +92,17 @@
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(TempCertsCacheNSSTest);
+  DISALLOW_COPY_AND_ASSIGN(NSSTempCertsCacheChromeOSTest);
 };
 
 // Checks that a certificate made available through the
-// TempCertsCacheNSS can be found by NSS. We specifically check for
+// NSSTempCertsCacheChromeOS can be found by NSS. We specifically check for
 // lookup through the CERT_FindCertByName function, as this is what is used in
 // client certificate matching (see MatchClientCertificateIssuers in
 // net/third_party/nss/ssl/cmpcert.cc). Additionally, checks that the
-// certificate is not available after the TempCertsCacheNSS goes out of
+// certificate is not available after the NSSTempCertsCacheChromeOS goes out of
 // scope.
-TEST_F(TempCertsCacheNSSTest, CertMadeAvailable) {
+TEST_F(NSSTempCertsCacheChromeOSTest, CertMadeAvailable) {
   base::FilePath cert_file_path =
       net::GetTestCertsDirectory().AppendASCII("client_1_ca.pem");
   {
@@ -113,7 +113,7 @@
             x509_authority_cert.data(), x509_authority_cert.length(),
             net::X509Certificate::Format::FORMAT_AUTO);
 
-    TempCertsCacheNSS cache(x509_authority_certs);
+    NSSTempCertsCacheChromeOS cache(x509_authority_certs);
 
     bool cert_available = false;
     ASSERT_NO_FATAL_FAILURE(
@@ -128,4 +128,4 @@
 }
 
 }  // namespace
-}  // namespace policy
+}  // namespace network
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 8f3b73d..c1d6e7f 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -81,6 +81,15 @@
                    ProxyList bad_proxies) => ();
 };
 
+[EnableIf=is_chromeos]
+struct AdditionalCertificates {
+  // List of all additional certificates.
+  array<X509Certificate> all_certificates;
+
+  // List of additional trust anchors.
+  array<X509Certificate> trust_anchors;
+};
+
 // Parameters for constructing a network context.
 struct NetworkContextParams {
   // Name used by memory tools to identify the context.
@@ -244,9 +253,10 @@
   [EnableIf=is_chromeos]
   string username_hash;
 
-  // Initial list of additional trust anchors.
+  // Initial additional certificates that will be used for certificate
+  // validation.
   [EnableIf=is_chromeos]
-  array<X509Certificate> initial_trust_anchors;
+  AdditionalCertificates? initial_additional_certificates;
 
   // Parameters for constructing the cookie manager.
   CookieManagerParams? cookie_manager_params;
@@ -523,7 +533,7 @@
 
   // Updates the additional trust anchors for certificate verification.
   [EnableIf=is_chromeos]
-  UpdateTrustAnchors(array<X509Certificate> trust_anchors);
+  UpdateAdditionalCertificates(AdditionalCertificates? additional_certificates);
 
   // Updates the CT policy to be used for requests. Only applies if the
   // NetworkContextParams set enforce_chrome_ct_policy to true.
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index f6c03e0..0c47c8c 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -89,7 +89,8 @@
   void SetAcceptLanguage(const std::string& new_accept_language) override {}
   void SetEnableReferrers(bool enable_referrers) override {}
 #if defined(OS_CHROMEOS)
-  void UpdateTrustAnchors(const net::CertificateList& trust_anchors) override {}
+  void UpdateAdditionalCertificates(
+      mojom::AdditionalCertificatesPtr additional_certificates) override {}
 #endif
 #if BUILDFLAG(IS_CT_SUPPORTED)
   void SetCTPolicy(
diff --git a/services/proxy_resolver/host_resolver_mojo.cc b/services/proxy_resolver/host_resolver_mojo.cc
index b9b3fac6..dde987f 100644
--- a/services/proxy_resolver/host_resolver_mojo.cc
+++ b/services/proxy_resolver/host_resolver_mojo.cc
@@ -153,7 +153,8 @@
   if (!entry)
     return net::ERR_DNS_CACHE_MISS;
 
-  *addresses = net::AddressList::CopyWithPort(entry->addresses(), info.port());
+  *addresses =
+      net::AddressList::CopyWithPort(entry->addresses().value(), info.port());
   return entry->error();
 }
 
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc
index 29d07d2ec..b8708ff 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc
@@ -8,7 +8,7 @@
 #include "base/format_macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/memory_dump_manager.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "build/build_config.h"
 
 namespace memory_instrumentation {
diff --git a/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc b/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc
index c8e16e6..c861b9a 100644
--- a/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc
+++ b/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc
@@ -8,7 +8,7 @@
 #include "base/hash.h"
 #include "base/json/string_escape.h"
 #include "base/trace_event/trace_event.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "base/values.h"
 #include "services/tracing/public/cpp/perfetto/heap_scattered_stream_delegate.h"
 #include "third_party/perfetto/include/perfetto/protozero/message_handle.h"
diff --git a/services/tracing/public/cpp/perfetto/traced_value_proto_writer_unittest.cc b/services/tracing/public/cpp/perfetto/traced_value_proto_writer_unittest.cc
index e268f219..d4bbd5e 100644
--- a/services/tracing/public/cpp/perfetto/traced_value_proto_writer_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/traced_value_proto_writer_unittest.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <string>
 
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
 #include "services/tracing/public/cpp/perfetto/heap_scattered_stream_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pb.h"
diff --git a/services/ws/common/util.h b/services/ws/common/util.h
index 36d0694..4c2c6576 100644
--- a/services/ws/common/util.h
+++ b/services/ws/common/util.h
@@ -19,6 +19,11 @@
   return static_cast<ClientSpecificId>(id & 0xFFFFFFFF);
 }
 
+inline Id BuildTransportId(ClientSpecificId connection_id,
+                           ClientSpecificId window_id) {
+  return (static_cast<Id>(connection_id) << 32) | static_cast<Id>(window_id);
+}
+
 }  // namespace ws
 
 #endif  // SERVICES_WS_COMMON_UTIL_H_
diff --git a/services/ws/window_service.cc b/services/ws/window_service.cc
index bc14d3d7..d49190fe 100644
--- a/services/ws/window_service.cc
+++ b/services/ws/window_service.cc
@@ -79,10 +79,6 @@
   DCHECK(window_trees_.empty());
 }
 
-ClientSpecificId WindowService::GetFirstWindowTreeClientId() const {
-  return decrement_client_ids_ ? kInitialClientIdDecrement : kInitialClientId;
-}
-
 ServerWindow* WindowService::GetServerWindowForWindowCreateIfNecessary(
     aura::Window* window) {
   ServerWindow* server_window = ServerWindow::GetMayBeNull(window);
@@ -131,6 +127,17 @@
   return server_window && server_window->IsTopLevel();
 }
 
+ws::Id WindowService::GetTopLevelWindowId(aura::Window* window) {
+  ServerWindow* server_window = ServerWindow::GetMayBeNull(window);
+  if (!server_window || !server_window->IsTopLevel())
+    return kInvalidTransportId;
+
+  const ws::WindowTree* owning_tree = server_window->owning_window_tree();
+  return BuildTransportId(
+      owning_tree->client_id(),
+      owning_tree->ClientWindowIdForWindow(window).sink_id());
+}
+
 aura::Window* WindowService::GetWindowByClientId(Id transport_id) {
   const ClientSpecificId client_id = ClientIdFromTransportId(transport_id);
   WindowTree* window_tree = GetTreeById(client_id);
diff --git a/services/ws/window_service.h b/services/ws/window_service.h
index 9d23e9e..221f822e 100644
--- a/services/ws/window_service.h
+++ b/services/ws/window_service.h
@@ -87,9 +87,6 @@
                 aura::Env* env = nullptr);
   ~WindowService() override;
 
-  // Returns the id of the first WindowTreeClient.
-  ClientSpecificId GetFirstWindowTreeClientId() const;
-
   // Gets the ServerWindow for |window|, creating if necessary.
   ServerWindow* GetServerWindowForWindowCreateIfNecessary(aura::Window* window);
 
@@ -113,6 +110,10 @@
   // Returns true if |window| hosts a remote client and is a toplevel window.
   static bool IsTopLevelWindow(const aura::Window* window);
 
+  // Returns the transport id for |window|. If |window| is not a top-level
+  // window, returns kInvalidTransportId.
+  ws::Id GetTopLevelWindowId(aura::Window* window);
+
   // Returns the window representing the specified id.
   aura::Window* GetWindowByClientId(Id transport_id);
 
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 84b14809..6db6d7b 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -9,7 +9,6 @@
 import("//gpu/vulkan/features.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//testing/test.gni")
-import("//testing/libfuzzer/fuzzer_test.gni")
 import("//third_party/skia/gn/shared_sources.gni")
 import("//third_party/skia/third_party/skcms/skcms.gni")
 
@@ -864,155 +863,8 @@
   }
 }
 
-# Build Skia fuzzers from OSS-Fuzz on Windows since Windows is not supported by
-# OSS-Fuzz.
-if (is_win && use_libfuzzer) {
-  static_library("skia_fuzzer_lib") {
-    sources = [
-      "//third_party/skia/fuzz/Fuzz.cpp",
-      "//third_party/skia/fuzz/FuzzCommon.cpp",
-    ]
-    configs += [ ":skia_library_config" ]
-
-    # Use public_deps so each fuzzer_test doesn't need to explicitly depend on
-    # ":skia".
-    public_deps = [
-      ":skia",
-    ]
-  }
-
-  static_library("skia_encoder_fuzzer_lib") {
-    sources = [
-      "//third_party/skia/fuzz/FuzzEncoders.cpp",
-    ]
-    configs += [ ":skia_library_config" ]
-    deps = [
-      ":skia",
-    ]
-  }
-
-  # TODO(metzman): Enable the other fuzzers that cannot yet build in Chromium.
-  fuzzer_test("skia_region_deserialize_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzRegionDeserialize.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_image_filter_deserialize_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzImageFilterDeserialize.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_region_set_path_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzRegionSetPath.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_textblob_deserialize_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzTextBlobDeserialize.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_path_deserialize_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzPathDeserialize.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_image_decode_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzImage.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_png_encoder_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzPNGEncoder.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_encoder_fuzzer_lib",
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_jpeg_encoder_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzJPEGEncoder.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_encoder_fuzzer_lib",
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_webp_encoder_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzWEBPEncoder.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_encoder_fuzzer_lib",
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_skjson_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzJSON.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_fuzzer_lib",
-    ]
-  }
-
-  fuzzer_test("skia_image_decode_incremental_fuzzer") {
-    sources = [
-      "//third_party/skia/fuzz/oss_fuzz/FuzzIncrementalImage.cpp",
-    ]
-    additional_configs = [ ":skia_library_config" ]
-    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
-    deps = [
-      ":skia_fuzzer_lib",
-    ]
-  }
+group("fuzzers") {
+  deps = [
+    "//skia/tools/fuzzers",
+  ]
 }
diff --git a/skia/tools/fuzzers/BUILD.gn b/skia/tools/fuzzers/BUILD.gn
new file mode 100644
index 0000000..d28c893
--- /dev/null
+++ b/skia/tools/fuzzers/BUILD.gn
@@ -0,0 +1,155 @@
+# Copyright (c) 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/libfuzzer/fuzzer_test.gni")
+
+group("fuzzers") {
+}
+
+# Build Skia fuzzers from OSS-Fuzz on Windows since Windows is not supported by
+# OSS-Fuzz.
+if (is_win && use_libfuzzer) {
+  source_set("skia_fuzzer_sources") {
+    testonly = true
+    sources = [
+      "//skia/tools/fuzzers/fuzzer_environment.cc",
+      "//third_party/skia/fuzz/Fuzz.cpp",
+      "//third_party/skia/fuzz/FuzzCommon.cpp",
+    ]
+
+    # Use public_deps and public_configs so each fuzzer_test doesn't need to
+    # explicitly depend on "//skia" and "//skia:skia_library_config".
+    public_deps = [
+      "//skia",
+    ]
+    public_configs = [ "//skia:skia_library_config" ]
+    deps = [
+      "//base/test:test_support",
+    ]
+  }
+
+  static_library("skia_encoder_fuzzer_lib") {
+    sources = [
+      "//third_party/skia/fuzz/FuzzEncoders.cpp",
+    ]
+    configs += [ "//skia:skia_library_config" ]
+    deps = [
+      "//skia",
+    ]
+  }
+
+  # TODO(metzman): Enable the other fuzzers that cannot yet build in Chromium.
+  fuzzer_test("skia_region_deserialize_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzRegionDeserialize.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_image_filter_deserialize_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzImageFilterDeserialize.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_region_set_path_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzRegionSetPath.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_textblob_deserialize_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzTextBlobDeserialize.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_path_deserialize_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzPathDeserialize.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_image_decode_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzImage.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_png_encoder_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzPNGEncoder.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_encoder_fuzzer_lib",
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_jpeg_encoder_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzJPEGEncoder.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_encoder_fuzzer_lib",
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_webp_encoder_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzWEBPEncoder.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_encoder_fuzzer_lib",
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_skjson_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzJSON.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_fuzzer_sources",
+    ]
+  }
+
+  fuzzer_test("skia_image_decode_incremental_fuzzer") {
+    sources = [
+      "//third_party/skia/fuzz/oss_fuzz/FuzzIncrementalImage.cpp",
+    ]
+    defines = [ "IS_FUZZING_WITH_LIBFUZZER" ]
+    deps = [
+      ":skia_fuzzer_sources",
+    ]
+  }
+}
diff --git a/skia/tools/fuzzers/fuzzer_environment.cc b/skia/tools/fuzzers/fuzzer_environment.cc
new file mode 100644
index 0000000..72f10a7
--- /dev/null
+++ b/skia/tools/fuzzers/fuzzer_environment.cc
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_discardable_memory_allocator.h"
+
+namespace {
+
+class Environment {
+  base::TestDiscardableMemoryAllocator test_memory_allocator_;
+
+ public:
+  Environment() {
+    base::DiscardableMemoryAllocator::SetInstance(&test_memory_allocator_);
+  }
+};
+
+static Environment env;
+};  // namespace
diff --git a/testing/buildbot/README.md b/testing/buildbot/README.md
index e1cdb92..516f2260 100644
--- a/testing/buildbot/README.md
+++ b/testing/buildbot/README.md
@@ -5,6 +5,16 @@
 In addition to specifying what tests run on which builders, they also specify
 special arguments and constraints for the tests.
 
+Adding a new test suite?
+
+The bar for adding new test suites is high. New test suites result in extra
+linking time for builders, and sending binaries around to the swarming bots.
+This is especially onerous for suites such as browser_tests (more than 300MB
+as of this writing). Unless there is a compelling reason to have a standalone
+suite, include your tests in existing test suites. For example, all
+InProcessBrowserTests should be in browser_tests. Similarly any unit-tests in
+components should be in components_unittests.
+
 ## A tour of the directory
 
 * <master_name\>.json -- buildbot configuration json files. These are used to
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 77b7748..9db351c 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -5920,11 +5920,7 @@
       {
         "args": [
           "--additional-driver-flag",
-          "--site-per-process",
-          "--additional-driver-flag",
-          "--isolate-origins=http://www.web-platform.test:8001/,http://www1.web-platform.test:8001/,http://www2.web-platform.test:8001/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001/,http://xn--lve-6lad.web-platform.test:8001/,http://www.web-platform.test:8081/,http://www1.web-platform.test:8081/,http://www2.web-platform.test:8081/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8081/,http://xn--lve-6lad.web-platform.test:8081/,https://www.web-platform.test:8444/,https://www1.web-platform.test:8444/,https://www2.web-platform.test:8444/,https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8444/,https://xn--lve-6lad.web-platform.test:8444/",
-          "--additional-expectations",
-          "src/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process",
+          "--disable-site-isolation-trials",
           "--num-retries=3"
         ],
         "isolate_name": "webkit_layout_tests_exparchive",
@@ -5934,7 +5930,7 @@
           ],
           "script": "//third_party/blink/tools/merge_web_test_results.py"
         },
-        "name": "site_per_process_webkit_layout_tests",
+        "name": "not_site_per_process_webkit_layout_tests",
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -7348,11 +7344,7 @@
       {
         "args": [
           "--additional-driver-flag",
-          "--site-per-process",
-          "--additional-driver-flag",
-          "--isolate-origins=http://www.web-platform.test:8001/,http://www1.web-platform.test:8001/,http://www2.web-platform.test:8001/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001/,http://xn--lve-6lad.web-platform.test:8001/,http://www.web-platform.test:8081/,http://www1.web-platform.test:8081/,http://www2.web-platform.test:8081/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8081/,http://xn--lve-6lad.web-platform.test:8081/,https://www.web-platform.test:8444/,https://www1.web-platform.test:8444/,https://www2.web-platform.test:8444/,https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8444/,https://xn--lve-6lad.web-platform.test:8444/",
-          "--additional-expectations",
-          "src/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process",
+          "--disable-site-isolation-trials",
           "--num-retries=3"
         ],
         "isolate_name": "webkit_layout_tests_exparchive",
@@ -7362,7 +7354,7 @@
           ],
           "script": "//third_party/blink/tools/merge_web_test_results.py"
         },
-        "name": "site_per_process_webkit_layout_tests",
+        "name": "not_site_per_process_webkit_layout_tests",
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 46e65ee..d6100c1b 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -1552,11 +1552,7 @@
       {
         "args": [
           "--additional-driver-flag",
-          "--site-per-process",
-          "--additional-driver-flag",
-          "--isolate-origins=http://www.web-platform.test:8001/,http://www1.web-platform.test:8001/,http://www2.web-platform.test:8001/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001/,http://xn--lve-6lad.web-platform.test:8001/,http://www.web-platform.test:8081/,http://www1.web-platform.test:8081/,http://www2.web-platform.test:8081/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8081/,http://xn--lve-6lad.web-platform.test:8081/,https://www.web-platform.test:8444/,https://www1.web-platform.test:8444/,https://www2.web-platform.test:8444/,https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8444/,https://xn--lve-6lad.web-platform.test:8444/",
-          "--additional-expectations",
-          "src/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process",
+          "--disable-site-isolation-trials",
           "--num-retries=3"
         ],
         "isolate_name": "webkit_layout_tests_exparchive",
@@ -1566,7 +1562,7 @@
           ],
           "script": "//third_party/blink/tools/merge_web_test_results.py"
         },
-        "name": "site_per_process_webkit_layout_tests",
+        "name": "not_site_per_process_webkit_layout_tests",
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -4164,11 +4160,7 @@
       {
         "args": [
           "--additional-driver-flag",
-          "--site-per-process",
-          "--additional-driver-flag",
-          "--isolate-origins=http://www.web-platform.test:8001/,http://www1.web-platform.test:8001/,http://www2.web-platform.test:8001/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001/,http://xn--lve-6lad.web-platform.test:8001/,http://www.web-platform.test:8081/,http://www1.web-platform.test:8081/,http://www2.web-platform.test:8081/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8081/,http://xn--lve-6lad.web-platform.test:8081/,https://www.web-platform.test:8444/,https://www1.web-platform.test:8444/,https://www2.web-platform.test:8444/,https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8444/,https://xn--lve-6lad.web-platform.test:8444/",
-          "--additional-expectations",
-          "src/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process",
+          "--disable-site-isolation-trials",
           "--num-retries=3"
         ],
         "isolate_name": "webkit_layout_tests_exparchive",
@@ -4178,7 +4170,7 @@
           ],
           "script": "//third_party/blink/tools/merge_web_test_results.py"
         },
-        "name": "site_per_process_webkit_layout_tests",
+        "name": "not_site_per_process_webkit_layout_tests",
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 9b75cfd..73d293b 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -839,6 +839,14 @@
       },
     },
   },
+  'not_site_per_process_webkit_layout_tests': {
+    'remove_from': [
+      # chromium.linux
+      # TODO(dpranke): Should we be running this step on Linux Tests (dbg)(1)?
+      'Linux Tests (dbg)(1)',
+      'Linux Tests (dbg)(1)(32)',
+    ],
+  },
   'notification_helper_unittests': {
     'remove_from': [
       # The test uses WinRT, which only exists in Windows 8 or above.
@@ -921,14 +929,6 @@
       },
     },
   },
-  'site_per_process_webkit_layout_tests': {
-    'remove_from': [
-      # chromium.linux
-      # TODO(dpranke): Should we be running this step on Linux Tests (dbg)(1)?
-      'Linux Tests (dbg)(1)',
-      'Linux Tests (dbg)(1)(32)',
-    ],
-  },
   'sizes': {
     'remove_from': [
       'win32-dbg',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index afc832d..a8b885f 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1821,14 +1821,15 @@
     'linux_specific_chromium_isolated_scripts': {
       'devtools_closure_compile': {},
       'devtools_eslint': {},
-      'site_per_process_webkit_layout_tests': {
+      'not_site_per_process_webkit_layout_tests': {
+        # not_site_per_process_webkit_layout_tests provides coverage for
+        # running Layout Tests without site-per-process.  This is the mode used
+        # on Android and Android bots currently do not run the full set of
+        # layout tests.  Running in this mode on linux compensates for lack of
+        # direct Android coverage.
         'args': [
           '--additional-driver-flag',
-          '--site-per-process',
-          '--additional-driver-flag',
-          '--isolate-origins=http://www.web-platform.test:8001/,http://www1.web-platform.test:8001/,http://www2.web-platform.test:8001/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001/,http://xn--lve-6lad.web-platform.test:8001/,http://www.web-platform.test:8081/,http://www1.web-platform.test:8081/,http://www2.web-platform.test:8081/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8081/,http://xn--lve-6lad.web-platform.test:8081/,https://www.web-platform.test:8444/,https://www1.web-platform.test:8444/,https://www2.web-platform.test:8444/,https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8444/,https://xn--lve-6lad.web-platform.test:8444/',
-          '--additional-expectations',
-          'src/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process',
+          '--disable-site-isolation-trials',
 
           # layout test failures are retried 3 times when '--test-list' is not
           # passed, but 0 times when '--test-list' is passed. We want to always
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index ea771e5f..87889b1 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1105,25 +1105,6 @@
             ]
         }
     ],
-    "BlockTabUnders": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BlockTabUnders"
-                    ]
-                }
-            ]
-        }
-    ],
     "BookmarkInProductHelp": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index c9236509..fc99895 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -840,3 +840,4 @@
 Bug(none) compositing/overflow/siblings-with-border-radius-ancestor.html [ Failure ]
 Bug(none) compositing/overflow/grandchild-composited-with-border-radius-ancestor.html [ Failure ]
 Bug(none) paint/invalidation/float-offscreen.html [ Failure ]
+Bug(none) compositing/video/video-controls-squashing.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
deleted file mode 100644
index 5c8bdef5..0000000
--- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
+++ /dev/null
@@ -1,121 +0,0 @@
-# These tests currently fail when they run with --site-per-process.
-# See https://crbug.com/477150.
-
-# https://crbug.com/793127: NOTREACHED() from clamy@ tickled by frame consolidation CL?
-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/793127 virtual/outofblink-cors-ns/http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Crash ]
-crbug.com/793127 external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html [ Crash ]
-
-# https://crbug.com/769508: PlzNavigate-related, not-yet-investigated failures.
-crbug.com/769508 fast/css/acid2.html [ Failure ]
-
-# https://crbug.com/765779: unique name trouble with PlzNavigate
-crbug.com/765779 http/tests/loading/bad-server-subframe.html [ Failure ]
-
-# https://crbug.com/393285: Text-autosizing doesn't support OOPIFs.
-# https://crbug.com/667551: Pixel dumps don't support OOPIFs.
-# Both of the bugs above need to be fixed, before enabling the tests below.
-crbug.com/393285 http/tests/text-autosizing/narrow-iframe.html [ Failure Crash ]
-crbug.com/393285 http/tests/text-autosizing/wide-iframe.html [ Failure Crash ]
-
-# https://crbug.com/669083: console messages mismatch (origin-only VS full-URI)
-crbug.com/669083 http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
-crbug.com/669083 virtual/outofblink-cors/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
-crbug.com/669083 virtual/outofblink-cors-ns/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
-
-# https://crbug.com/608015 - node.contentDocument is undefined.
-crbug.com/608015 http/tests/inspector-protocol/access-inspected-object.js [ Failure Timeout ]
-
-# https://crbug.com/771003 - Dump history from all processes in layout tests
-crbug.com/771003 http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
-crbug.com/771003 virtual/outofblink-cors/http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
-crbug.com/771003 virtual/outofblink-cors-ns/http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
-
-# https://crbug.com/585188 - testRunner.addOriginAccessAllowListEntry is not replicated to OOPIFs.
-crbug.com/585188 http/tests/xmlhttprequest/origin-whitelisting-all.html [ Failure ]
-crbug.com/585188 virtual/outofblink-cors/http/tests/xmlhttprequest/origin-whitelisting-all.html [ Failure ]
-crbug.com/585188 http/tests/xmlhttprequest/origin-whitelisting-ip-addresses.html [ Failure ]
-crbug.com/585188 virtual/outofblink-cors/http/tests/xmlhttprequest/origin-whitelisting-ip-addresses.html [ Failure ]
-
-# https://crbug.com/606594 - UaF of delegate_ in WebFrameTestClient::willSendRequest
-# https://crbug.com/786510 - test tries to access cross-origin document body
-crbug.com/606594 http/tests/local/serviceworker/fetch-request-body-file.html [ Skip ]
-
-# https://crbug.com/616626 - allow_universal_access_from_file_urls doesn't work with --site-per-process.
-# https://crbug.com/665058 - EventSender drag-and-drop simulation doesn't support OOPIFs.
-crbug.com/665058 http/tests/local/drag-over-remote-content.html [ Crash ]
-
-# https://crbug.com/619662 - Expected console output differences.
-crbug.com/619662 http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Failure ]
-crbug.com/619662 virtual/outofblink-cors/http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Failure ]
-crbug.com/619662 virtual/outofblink-cors-ns/http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Failure ]
-crbug.com/619662 virtual/stable/http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Failure ]
-
-# https://crbug.com/623268 - Can't inspect OOPIFs from main page's DevTools window.
-crbug.com/623268 http/tests/inspector-protocol/request-mixed-content-status-blockable.js [ Timeout ]
-crbug.com/623268 http/tests/inspector-protocol/request-mixed-content-status-none.js [ Timeout ]
-crbug.com/623268 http/tests/inspector-protocol/request-mixed-content-status-optionally-blockable.js [ Timeout ]
-crbug.com/623268 http/tests/devtools/console-cross-origin-iframe-logging.js [ Timeout ]
-
-# https://crbug.com/645641 - test_runner.cc(1863) Check failed:
-# layout_test_runtime_flags_.have_top_loading_frame()
-crbug.com/645641 external/wpt/html/syntax/parsing/html5lib_tests19.html [ Crash Failure ]
-
-# http/ flaky tests w/ --site-per-process
-crbug.com/678482 http/tests/devtools/debugger/fetch-breakpoints.js [ Timeout Pass ]
-crbug.com/678491 http/tests/misc/webtiming-no-origin.html [ Crash Pass ]
-
-# Slow tests. These are listed in SlowTests listed here also because
-# expectations are unfortunately not inherited automatically (e.g. see
-# https://crbug.com/594216)
-crbug.com/451577 http/tests/devtools/network/network-datareceived.js [ Timeout Pass ]
-crbug.com/24182 http/tests/perf/large-inlined-script.html [ Timeout Pass ]
-
-# https://crbug.com/650348: Remove input event forwarding path from RenderFrameProxies
-# This test needs to be modified to not rely on EventSender passing mouse events directly to an OOPIF.
-crbug.com/650348 http/tests/webaudio/autoplay-crossorigin.html [ Timeout ]
-
-# https://crbug.com/778372: Unique name conflict with old entries?
-crbug.com/778372 external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Crash ]
-
-# https://crbug.com/819800: Feature policy propagation trouble?  Test synchronization issue?
-crbug.com/819800 external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html [ Failure ]
-
-# The tests below set document.domain.  Such tests are skipped, because they
-# cannot work when same-site WPT origins are isolated from each other via
-# --isolate-origins cmdline flag (which is how bots run the
-# |site_per_process_webkit_layout_tests| step).  Note that
-# https://crbug.com/783416 tracks adding a WPT origin that is cross-site, but
-# even after this bug is fixed we may decide to keep the current
-# --isolate-origins coverage and therefore the tests might end up being skipped
-# for the foreseeable future.
-Bug(none) external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction [ Skip ]
-Bug(none) external/wpt/FileAPI/url/multi-global-origin-serialization.sub.html [ Skip ]
-Bug(none) virtual/mojo-blob-urls/external/wpt/FileAPI/url/multi-global-origin-serialization.sub.html [ Skip ]
-Bug(none) external/wpt/dom/events/EventListener-incumbent-global-1.sub.html [ Skip ]
-Bug(none) external/wpt/dom/events/EventListener-incumbent-global-2.sub.html [ Skip ]
-Bug(none) external/wpt/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html [ Skip ]
-Bug(none) external/wpt/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub.html [ Skip ]
-Bug(none) external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html [ Skip ]
-Bug(none) external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html [ Skip ]
-Bug(none) external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html [ Skip ]
-Bug(none) external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html [ Skip ]
-Bug(none) virtual/sharedarraybuffer/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html [ Skip ]
-Bug(none) virtual/sharedarraybuffer/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html [ Skip ]
-Bug(none) external/wpt/wasm/serialization/window-domain-success.sub.html [ Skip ]
-Bug(none) external/wpt/wasm/serialization/window-similar-but-cross-origin-success.sub.html [ Skip ]
-
-# Layout tests don't work for printing cross-site frames.
-crbug.com/822372 http/tests/printing/cross-site-frame.html [ Crash ]
-crbug.com/822372 http/tests/printing/cross-site-frame-scrolled.html [ Crash ]
-
-# TODO(lukasza, alexmos): Triage these failures.
-crbug.com/801992 http/tests/misc/iframe-script-modify-attr.html [ Pass Crash ]
-crbug.com/807675 http/tests/images/image-decode-in-frame.html [ Crash Failure ]
-
-# https://crbug.com/872952: Autoplay is still disabled if there is no gesture.
-crbug.com/872952 http/tests/media/autoplay/document-user-activation-feature-policy-iframe-no-gesture.html [ Failure Pass Timeout ]
-
-# https://crbug.com/895001: Blink layout tests with cross-origin mouse clicks can fail without slow path hit testing
-crbug.com/895001 external/wpt/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html [ Timeout Pass ]
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index bf0035c..03737586 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -2075,3 +2075,43 @@
 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-stop-manual.https.html [ WontFix ]
 external/wpt/pointerevents/pointerevent_pointermove-manual.html [ WontFix ]
 [ Retina ] external/wpt/pointerevents/pointerevent_touch-action-table-test_touch-manual.html [ WontFix ]
+
+# ====== Tests incompatible with the default Site Isolation from here ======
+# See also third_party/WebKit/LayoutTests/virtual/not-site-per-process/README.md
+#
+# When modifying the list of files that behave differently with and without
+# OOPIFs, please consider modifying all the locations below:
+# - LayoutTests/NeverFixTests (the section here)
+# - LayoutTests/VirtualTestSuites (virtual/not-site-per-process suite)
+# - LayoutTests/virtual/not-site-per-process/README.md
+
+# Tests permanently disabled with Site Isolation (e.g. tests that use
+# document.domain are are therefore inherently incompatible with isolation of
+# WPT origins):
+Bug(none) external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction [ WontFix ]
+Bug(none) external/wpt/FileAPI/url/multi-global-origin-serialization.sub.html [ WontFix ]
+Bug(none) virtual/mojo-blob-urls/external/wpt/FileAPI/url/multi-global-origin-serialization.sub.html [ WontFix ]
+Bug(none) external/wpt/dom/events/EventListener-incumbent-global-1.sub.html [ WontFix ]
+Bug(none) external/wpt/dom/events/EventListener-incumbent-global-2.sub.html [ WontFix ]
+Bug(none) external/wpt/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html [ WontFix ]
+Bug(none) external/wpt/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub.html [ WontFix ]
+Bug(none) external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html [ WontFix ]
+Bug(none) external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html [ WontFix ]
+Bug(none) external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html [ WontFix ]
+Bug(none) external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html [ WontFix ]
+Bug(none) virtual/sharedarraybuffer/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html [ WontFix ]
+Bug(none) virtual/sharedarraybuffer/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html [ WontFix ]
+Bug(none) external/wpt/wasm/serialization/window-domain-success.sub.html [ WontFix ]
+Bug(none) external/wpt/wasm/serialization/window-similar-but-cross-origin-success.sub.html [ WontFix ]
+
+# Code cache isolation tests only make sense if either
+# 1) site isolation is disabled or
+#    (covered by virtual/not-site-per-process/.../isolated-code-cache)
+# 2) site isolation is enabled together with --enable-features=IsolatedCodeCache
+#    (covered by virtual/site-isolated-code-cache/.../isolated-code-cache)
+# but don't make sense if
+# 3) site isolation is enabled without also enabling
+#    IsolatedCodeCache (tests disabled by the test expectation below).
+Bug(none) http/tests/devtools/isolated-code-cache/cross-origin-test.js [ WontFix ]
+
+# ====== Tests incompatible with the default Site Isolation until here ======
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 28278ef..8905091 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -592,6 +592,7 @@
 crbug.com/874695 external/wpt/html/browsers/history/joint-session-history/joint-session-history-remove-iframe.html [ Slow ]
 crbug.com/874695 external/wpt/html/browsers/offline/application-cache-api/api_status_idle.https.html [ Slow ]
 crbug.com/874695 external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Slow ]
+crbug.com/874695 virtual/not-site-per-process/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Slow ]
 crbug.com/874695 external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noopener.html [ Slow ]
 crbug.com/874695 external/wpt/html/browsers/windows/noreferrer-window-name.html [ Slow ]
 crbug.com/874695 external/wpt/html/dom/documents/resource-metadata-management/document-lastModified-01.html [ Slow ]
@@ -638,6 +639,7 @@
 crbug.com/874695 external/wpt/html/user-activation/activation-api-iframe-no-activate.tenative.html [ Slow ]
 crbug.com/874695 external/wpt/html/user-activation/activation-thru-contextmenu-event-manual.html [ Slow ]
 crbug.com/874695 external/wpt/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html [ Slow ]
+crbug.com/874695 virtual/not-site-per-process/external/wpt/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html [ Slow ]
 crbug.com/874695 external/wpt/IndexedDB/interleaved-cursors-large.html [ Slow ]
 crbug.com/874695 external/wpt/IndexedDB/interleaved-cursors-small.html [ Slow ]
 crbug.com/874695 external/wpt/IndexedDB/nested-cloning-large-multiple.html [ Slow ]
@@ -660,6 +662,7 @@
 crbug.com/874695 external/wpt/orientation-sensor/AbsoluteOrientationSensor.https.html [ Slow ]
 crbug.com/874695 external/wpt/orientation-sensor/RelativeOrientationSensor.https.html [ Slow ]
 crbug.com/874695 external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html [ Slow ]
+crbug.com/874695 virtual/not-site-per-process/external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html [ Slow ]
 crbug.com/874695 external/wpt/performance-timeline/po-observe.html [ Slow ]
 crbug.com/874695 external/wpt/picture-in-picture/leave-picture-in-picture.html [ Slow ]
 crbug.com/874695 external/wpt/picture-in-picture/picture-in-picture-window.html [ Slow ]
@@ -860,7 +863,6 @@
 crbug.com/874695 http/tests/devtools/audits2/audits2-limited-run.js [ Slow ]
 crbug.com/874695 http/tests/devtools/audits2/audits2-successful-run.js [ Slow ]
 crbug.com/874695 http/tests/devtools/console/console-correct-suggestions.js [ Slow ]
-crbug.com/874695 http/tests/devtools/console-cross-origin-iframe-logging.js [ Slow ]
 crbug.com/874695 http/tests/devtools/editor/text-editor-formatter.js [ Slow ]
 crbug.com/874695 http/tests/devtools/inspect-iframe-from-different-domain.js [ Slow ]
 crbug.com/874695 http/tests/devtools/oopif/oopif-cookies-refresh.js [ Slow ]
@@ -955,8 +957,11 @@
 crbug.com/874695 http/tests/images/png-progressive-load.html [ Slow ]
 crbug.com/874695 http/tests/images/webp-progressive-load.html [ Slow ]
 crbug.com/874695 http/tests/inspector-protocol/request-mixed-content-status-blockable.js [ Slow ]
+crbug.com/874695 virtual/not-site-per-process/http/tests/inspector-protocol/request-mixed-content-status-blockable.js [ Slow ]
 crbug.com/874695 http/tests/inspector-protocol/request-mixed-content-status-none.js [ Slow ]
+crbug.com/874695 virtual/not-site-per-process/http/tests/inspector-protocol/request-mixed-content-status-none.js [ Slow ]
 crbug.com/874695 http/tests/inspector-protocol/request-mixed-content-status-optionally-blockable.js [ Slow ]
+crbug.com/874695 virtual/not-site-per-process/http/tests/inspector-protocol/request-mixed-content-status-optionally-blockable.js [ Slow ]
 crbug.com/874695 http/tests/media/controls/toggle-class-with-state-source-buffer.html [ Slow ]
 crbug.com/874695 http/tests/media/preload-conditions.html [ Slow ]
 crbug.com/874695 http/tests/media/video-buffered.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index e359937..a4e3da25 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -13,7 +13,6 @@
 
 crbug.com/807686 crbug.com/24182 jquery/manipulation.html [ Timeout Pass ]
 
-
 # See crbug.com/891427. These tests either fail or become flaky on one or more
 # platforms when --enable-display-compositor-pixel-dump is turned on. They need
 # triage. Note that these overlap with several existing entries in this
@@ -99,6 +98,71 @@
 crbug.com/887140 hdr/video-canvas-alpha.html [ Failure ]
 
 
+# ====== Site Isolation failures from here ======
+# See also third_party/WebKit/LayoutTests/virtual/not-site-per-process/README.md
+#
+# When modifying the list of files that behave differently with and without
+# OOPIFs, please consider modifying all the locations below:
+# - LayoutTests/TestExpectations (the section here)
+# - LayoutTests/VirtualTestSuites (virtual/not-site-per-process suite)
+# - LayoutTests/virtual/not-site-per-process/README.md
+
+# Tests temporarily disabled with Site Isolation - test issues or test harness
+# issues (e.g. missing OOPIF support in the test harness).
+# TODO(lukasza, alexmos): Burn down this list.
+crbug.com/585188 http/tests/xmlhttprequest/origin-whitelisting-all.html [ Failure ]
+crbug.com/585188 http/tests/xmlhttprequest/origin-whitelisting-ip-addresses.html [ Failure ]
+crbug.com/585188 virtual/outofblink-cors/http/tests/xmlhttprequest/origin-whitelisting-all.html [ Failure ]
+crbug.com/585188 virtual/outofblink-cors/http/tests/xmlhttprequest/origin-whitelisting-ip-addresses.html [ Failure ]
+crbug.com/606594 http/tests/local/serviceworker/fetch-request-body-file.html [ Skip ]
+crbug.com/645641 external/wpt/html/syntax/parsing/html5lib_tests19.html [ Crash Failure ]
+crbug.com/650348 http/tests/webaudio/autoplay-crossorigin.html [ Timeout ]
+crbug.com/665058 http/tests/local/drag-over-remote-content.html [ Crash ]
+crbug.com/771003 http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
+crbug.com/771003 virtual/outofblink-cors-ns/http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
+crbug.com/771003 virtual/outofblink-cors/http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
+
+# Tests temporarily disabled with Site Isolation - known differences in product
+# behavior (either accepted for the long-term, or for the short-term):
+# TODO(lukasza, alexmos): Burn down this list.
+# Text auto-sizing:
+crbug.com/393285 http/tests/text-autosizing/narrow-iframe.html [ Failure Crash ]
+crbug.com/393285 http/tests/text-autosizing/wide-iframe.html [ Failure Crash ]
+# Console message differences (e.g. full URI vs origin-only):
+crbug.com/619662 http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Failure ]
+crbug.com/619662 virtual/outofblink-cors-ns/http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Failure ]
+crbug.com/619662 virtual/outofblink-cors/http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Failure ]
+crbug.com/619662 virtual/stable/http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Failure ]
+crbug.com/669083 http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
+crbug.com/669083 virtual/outofblink-cors-ns/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
+crbug.com/669083 virtual/outofblink-cors/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
+crbug.com/886588 external/wpt/dom/events/EventListener-addEventListener.sub.window.html [ Failure ]
+
+# Tests temporarily disabled with Site Isolation - uninvestigated bugs:
+# TODO(lukasza, alexmos): Burn down this list.
+crbug.com/608015 http/tests/inspector-protocol/access-inspected-object.js [ Failure Timeout ]
+crbug.com/623268 http/tests/inspector-protocol/request-mixed-content-status-blockable.js [ Timeout ]
+crbug.com/623268 http/tests/inspector-protocol/request-mixed-content-status-none.js [ Timeout ]
+crbug.com/623268 http/tests/inspector-protocol/request-mixed-content-status-optionally-blockable.js [ Timeout ]
+crbug.com/678482 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/769508 fast/css/acid2.html [ Failure ]
+crbug.com/778372 external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Crash ]
+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-ns/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 ]
+crbug.com/807675 http/tests/images/image-decode-in-frame.html [ Crash Failure ]
+crbug.com/819800 external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html [ Failure ]
+crbug.com/822372 http/tests/printing/cross-site-frame-scrolled.html [ Crash ]
+crbug.com/822372 http/tests/printing/cross-site-frame.html [ Crash ]
+crbug.com/872952 http/tests/media/autoplay/document-user-activation-feature-policy-iframe-no-gesture.html [ Failure Pass Timeout ]
+crbug.com/895001 external/wpt/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html [ Timeout Pass ]
+crbug.com/901502 http/tests/devtools/oopif/oopif-storage.js [ Pass Failure ]
+# ====== Site Isolation failures until here ======
+
 # ====== Oilpan-only failures from here ======
 # Most of these actually cause the tests to report success, rather than
 # failure. Expected outputs will be adjusted for the better once Oilpan
@@ -1749,7 +1813,7 @@
 crbug.com/667560 [ Debug ] http/tests/devtools/elements/styles-3/styles-change-node-while-editing.js [ Pass Failure ]
 crbug.com/778515 http/tests/devtools/elements/styles-3/styles-add-new-rule.js [ Pass Failure ]
 
-crbug.com/667560 [ Win ] http/tests/devtools/console-cross-origin-iframe-logging.js [ Pass Timeout ]
+crbug.com/667560 http/tests/devtools/console-cross-origin-iframe-logging.js [ Pass Timeout ]
 
 crbug.com/778391 http/tests/devtools/elements/styles-3/styles-add-new-rule-tab.js [ Pass Failure ]
 crbug.com/778391 http/tests/devtools/elements/styles-3/styles-disable-inherited.js [ Pass Failure ]
@@ -4038,9 +4102,9 @@
 crbug.com/798864 [ Android ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-idb.any.worker.html [ Failure ]
 crbug.com/798864 [ Android ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-notifications-api.any.html [ Failure ]
 crbug.com/798864 [ Android ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-notifications-api.any.worker.html [ Failure ]
-crbug.com/798864 [ Android ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html [ Failure ]
+crbug.com/798864 [ Android ] virtual/not-site-per-process/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html [ Failure ]
 crbug.com/798864 [ Android ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html [ Failure ]
-crbug.com/798864 [ Android ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html [ Failure ]
+crbug.com/798864 [ Android ] virtual/not-site-per-process/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html [ Failure ]
 crbug.com/798864 [ Android ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.html [ Failure ]
 crbug.com/798864 [ Android ] external/wpt/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/canblock-dedicatedworker.html [ Failure ]
 crbug.com/798864 [ Android ] external/wpt/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/canblock-serviceworker.https.html [ Failure ]
@@ -4713,7 +4777,7 @@
 crbug.com/839332 [ Mac ] virtual/video-surface-layer/media/video-no-audio.html [ Pass Failure ]
 
 # This will break for now
-crbug.com/841933 http/tests/webaudio/autoplay-crossorigin.html [ Skip ]
+crbug.com/841933 virtual/not-site-per-process/http/tests/webaudio/autoplay-crossorigin.html [ Skip ]
 
 # Sheriff 2018-05-22
 crbug.com/845610 [ Win ] http/tests/inspector-protocol/target/target-browser-context.js [ Pass Failure ]
@@ -5279,5 +5343,5 @@
 crbug.com/905827 [ Mac10.13 ] fast/dom/StyleSheet/stylesheet-move-between-documents-crash.html [ Failure Pass ]
 
 #Sheriff 2018-11-19
-crbug.com/906591 [ Win10 ] printing/single-line-must-not-be-split-into-two-pages.html [ Failure ]
-crbug.com/906591 [ Win10 ] virtual/threaded/printing/single-line-must-not-be-split-into-two-pages.html [ Failure ]
+crbug.com/906591 printing/single-line-must-not-be-split-into-two-pages.html [ Failure ]
+crbug.com/906591 virtual/threaded/printing/single-line-must-not-be-split-into-two-pages.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index d8194cf..b86771e0 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -801,5 +801,225 @@
     "base": "http/tests/devtools/isolated-code-cache",
     "args": ["--enable-features=IsolatedCodeCache",
              "--site-per-process"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/devtools/isolated-code-cache",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/FileAPI/url/multi-global-origin-serialization.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/dom/events/EventListener-addEventListener.sub.window.js",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/dom/events/EventListener-incumbent-global-1.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/dom/events/EventListener-incumbent-global-2.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/syntax/parsing/html5lib_tests19.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/wasm/serialization/window-domain-success.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "external/wpt/wasm/serialization/window-similar-but-cross-origin-success.sub.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "fast/css/acid2.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/devtools/debugger/fetch-breakpoints.js",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/devtools/oopif/oopif-storage.js",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/images/image-decode-in-frame.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/inspector-protocol/access-inspected-object.js",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/inspector-protocol/request-mixed-content-status-blockable.js",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/inspector-protocol/request-mixed-content-status-none.js",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/inspector-protocol/request-mixed-content-status-optionally-blockable.js",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/loading/bad-server-subframe.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/local/drag-over-remote-content.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/local/serviceworker/fetch-request-body-file.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/media/autoplay/document-user-activation-feature-policy-iframe-no-gesture.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/misc/iframe-script-modify-attr.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/misc/webtiming-no-origin.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/navigation/cross-origin-fragment-navigation-is-async.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/printing/cross-site-frame-scrolled.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/printing/cross-site-frame.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/security/mixedContent/insecure-iframe-in-main-frame.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/text-autosizing/narrow-iframe.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/text-autosizing/wide-iframe.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/webaudio/autoplay-crossorigin.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/xmlhttprequest/origin-whitelisting-all.html",
+    "args": ["--disable-site-isolation-trials"]
+  },
+  {
+    "prefix": "not-site-per-process",
+    "base": "http/tests/xmlhttprequest/origin-whitelisting-ip-addresses.html",
+    "args": ["--disable-site-isolation-trials"]
   }
 ]
diff --git a/third_party/WebKit/LayoutTests/compositing/video/video-controls-squashing.html b/third_party/WebKit/LayoutTests/compositing/video/video-controls-squashing.html
new file mode 100644
index 0000000..4a1af663
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/video/video-controls-squashing.html
@@ -0,0 +1,27 @@
+<!doctype HTML>
+<style>
+.container {
+    border-radius: 8px;
+    overflow: hidden;
+}
+
+video {
+  display: block;
+  width: 200px;
+  height: 200px;
+  background: lightblue;
+}
+
+.bluebutton {
+  position: relative;
+  top: -20px;
+  height: 100px;
+    width: 100px;
+  background: blue;
+}
+</style>
+Should show a video with a blue square on it that is not clipped to the video.
+<div class="container">
+  <video src="../../media/content/test.webm"></video>
+  <div class="bluebutton"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
index b85274cb..38da7d4 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
@@ -180122,71 +180122,36 @@
      {}
     ]
    ],
-   "svg/geometry/parsing/cx-computed-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "svg/geometry/parsing/cx-invalid-expected.txt": [
     [
      {}
     ]
    ],
-   "svg/geometry/parsing/cy-computed-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "svg/geometry/parsing/cy-invalid-expected.txt": [
     [
      {}
     ]
    ],
-   "svg/geometry/parsing/r-computed-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "svg/geometry/parsing/r-invalid-expected.txt": [
     [
      {}
     ]
    ],
-   "svg/geometry/parsing/rx-computed-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "svg/geometry/parsing/rx-invalid-expected.txt": [
     [
      {}
     ]
    ],
-   "svg/geometry/parsing/ry-computed-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "svg/geometry/parsing/ry-invalid-expected.txt": [
     [
      {}
     ]
    ],
-   "svg/geometry/parsing/x-computed-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "svg/geometry/parsing/x-invalid-expected.txt": [
     [
      {}
     ]
    ],
-   "svg/geometry/parsing/y-computed-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "svg/geometry/parsing/y-invalid-expected.txt": [
     [
      {}
@@ -269739,6 +269704,12 @@
      {}
     ]
    ],
+   "svg/painting/parsing/stroke-width-computed.svg": [
+    [
+     "/svg/painting/parsing/stroke-width-computed.svg",
+     {}
+    ]
+   ],
    "svg/painting/parsing/stroke-width-invalid.svg": [
     [
      "/svg/painting/parsing/stroke-width-invalid.svg",
@@ -317311,7 +317282,7 @@
    "testharness"
   ],
   "css/css-align/gaps/column-gap-parsing-001.html": [
-   "a0a92a911e29aba357a8c39c2f8afe7d48e83a97",
+   "7feeddf85127ba82ad1713c1b970be6816faf11e",
    "testharness"
   ],
   "css/css-align/gaps/gap-animation-001.html": [
@@ -317343,19 +317314,19 @@
    "reftest"
   ],
   "css/css-align/gaps/gap-parsing-001.html": [
-   "0a2a9bda7c69f998baff8bbb085bfb07faa590e4",
+   "9ff3872114dcfbf2ccbbddb41c1e0f2b1fa1edb0",
    "testharness"
   ],
   "css/css-align/gaps/grid-column-gap-parsing-001.html": [
-   "66d81998b668ef54734beca6640f6bba3cc8d717",
+   "baec0641cdaef532e12a1ba2511991cb86ffa14b",
    "testharness"
   ],
   "css/css-align/gaps/grid-gap-parsing-001.html": [
-   "aa43f772996b208b05b2908eb2fba3479548a20d",
+   "fc10b620f1d02c1933007f3f2b68cbd98cdb60bd",
    "testharness"
   ],
   "css/css-align/gaps/grid-row-gap-parsing-001.html": [
-   "e394ea1446b08f7b8d12d1fd1b9b4918e96ee8fb",
+   "d3b44f4bd7206e965a6c96a24888a3923cbccc28",
    "testharness"
   ],
   "css/css-align/gaps/row-gap-animation-001.html": [
@@ -317371,7 +317342,7 @@
    "testharness"
   ],
   "css/css-align/gaps/row-gap-parsing-001.html": [
-   "71b971d3a8a18cc7cd9710ac00ffe10e4f4111e4",
+   "59be718cee487270658c18c1b69c6f0be89ae53c",
    "testharness"
   ],
   "css/css-align/inheritance.html": [
@@ -341375,7 +341346,7 @@
    "reftest"
   ],
   "css/css-paint-api/registered-property-interpolation-004.https.html": [
-   "1596a934b396a0f7b7d0c4daa3580f172eaaf2c8",
+   "430326a0663281678dc6133a9d0b7cf6889352e3",
    "reftest"
   ],
   "css/css-paint-api/registered-property-interpolation-005.https.html": [
@@ -341447,7 +341418,7 @@
    "reftest"
   ],
   "css/css-paint-api/registered-property-value-009.https.html": [
-   "795770f9a2eea785b086ef8ba5b3a895c0402a4a",
+   "2ce2a0d56bd6a8bf230c5bae4790c633c34643c6",
    "reftest"
   ],
   "css/css-paint-api/registered-property-value-010.https.html": [
@@ -341931,7 +341902,7 @@
    "support"
   ],
   "css/css-properties-values-api/registered-property-computation.html": [
-   "2525e43ef51e1e5af94426346e51a68b87fea055",
+   "b1e5d23738b9dda1ea280dfd321bc5b2838b519a",
    "testharness"
   ],
   "css/css-properties-values-api/registered-property-cssom.html": [
@@ -341939,7 +341910,7 @@
    "testharness"
   ],
   "css/css-properties-values-api/registered-property-initial.html": [
-   "77aa9cd11a3eabfd154cc98869699e6f24c64c5c",
+   "82a012e2f68532761b18428c3eaed9479ec8c7e5",
    "testharness"
   ],
   "css/css-properties-values-api/resources/utils.js": [
@@ -343515,7 +343486,7 @@
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-margin-003-expected.txt": [
-   "62062868c8521252076f86441576bf4d126915e8",
+   "104e0bb978653db65bf16fbc5ce0552dfce44f6c",
    "support"
   ],
   "css/css-shapes/shape-outside/values/shape-margin-003.html": [
@@ -343579,7 +343550,7 @@
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-circle-010-expected.txt": [
-   "3ce035e80a4438cb40f0655de14c492f8017ab04",
+   "f6d4093fb00555dffe00a4fa6601887fa0b6fbf1",
    "support"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-circle-010.html": [
@@ -343587,7 +343558,7 @@
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-circle-011-expected.txt": [
-   "fb6a89f0e67552cd6d9fb631b7ef92f4bea6961e",
+   "823faea146d58624135b8be1d3e8b40392964e68",
    "support"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-circle-011.html": [
@@ -343643,7 +343614,7 @@
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-ellipse-010-expected.txt": [
-   "7a8a2075dcbde4ac56361829759eb1b47ea418ad",
+   "d2bcdb95138fecd7a04cb98956a8027c55e04f5c",
    "support"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-ellipse-010.html": [
@@ -343651,7 +343622,7 @@
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-ellipse-011-expected.txt": [
-   "27a37189e0e10a3465195289fe4c29dc816db206",
+   "ebe434e047586370070a202bdafb56599b300af9",
    "support"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-ellipse-011.html": [
@@ -343699,7 +343670,7 @@
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-inset-008-expected.txt": [
-   "5f10cf98031e6e1a52f6f62eba27e8310b4e8157",
+   "b969e062ea7e2350987cb41b776cfb60010610e8",
    "support"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-inset-008.html": [
@@ -343707,7 +343678,7 @@
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-inset-009-expected.txt": [
-   "0d19e73dbb7f0af9606e2e4c8ab508d28483ae46",
+   "4a387910582b289d12e908f71614c77c223a171a",
    "support"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-inset-009.html": [
@@ -343739,7 +343710,7 @@
    "testharness"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-polygon-006-expected.txt": [
-   "299a6712158c4774b368dbea7dcf190e13064edf",
+   "f6747faa635680d2f1aaf812f0eb0f972d221057",
    "support"
   ],
   "css/css-shapes/shape-outside/values/shape-outside-polygon-006.html": [
@@ -354523,7 +354494,7 @@
    "reftest"
   ],
   "css/css-transforms/transforms-support-calc-expected.txt": [
-   "f58ab6ddfe4e3704446cdca6ab169f80c2cdd4b5",
+   "3e16e0ca86bde1b0371515ac76f51630fb8249bc",
    "support"
   ],
   "css/css-transforms/transforms-support-calc.html": [
@@ -354531,7 +354502,7 @@
    "testharness"
   ],
   "css/css-transforms/translate-getComputedStyle.html": [
-   "1e6759d1b0cccac754c62dde85102f052c73ff42",
+   "46a2c309020b5c66daba37f4d39879998b750214",
    "testharness"
   ],
   "css/css-transforms/translate-optional-second-001.html": [
@@ -369539,7 +369510,7 @@
    "support"
   ],
   "css/motion/offset-supports-calc.html": [
-   "79aa9a9f50acd57848df17908390c770d7b88976",
+   "9ec737e6a87198e390b4adc0c0cc5a349dc1bc43",
    "testharness"
   ],
   "css/motion/parsing/offset-anchor-parsing-invalid.html": [
@@ -417399,7 +417370,7 @@
    "testharness"
   ],
   "payment-request/payment-response/complete-method-manual.https.html": [
-   "a9da9eed9cbaed0540ef542f75896f532a60d13e",
+   "0e24de84f9b9c70b4a9f8964c7934ef7b8edb918",
    "manual"
   ],
   "payment-request/payment-response/helpers.js": [
@@ -417439,7 +417410,7 @@
    "manual"
   ],
   "payment-request/payment-response/retry-method-manual.https.html": [
-   "16ed15e54902b93485ee3be7a2635823e8763b06",
+   "69bda5ed3f32010c601ebb1cbff1c086a9ab4477",
    "manual"
   ],
   "payment-request/payment-response/shippingAddress-attribute-manual.https.html": [
@@ -433790,12 +433761,8 @@
    "760328ac31818d86a9410fd7c412d803e30416a2",
    "testharness"
   ],
-  "svg/geometry/parsing/cx-computed-expected.txt": [
-   "3bc668f75db08208aa67f8498384c1e35ef9df6a",
-   "support"
-  ],
   "svg/geometry/parsing/cx-computed.svg": [
-   "5a408ba4d28f1acf36f2bde6767b65bc5401cf48",
+   "95c39fa0920c9a2ad53f36de769eacae90e77aff",
    "testharness"
   ],
   "svg/geometry/parsing/cx-invalid-expected.txt": [
@@ -433810,12 +433777,8 @@
    "e7f627ac721fcc90597314ef0c97765566c343b3",
    "testharness"
   ],
-  "svg/geometry/parsing/cy-computed-expected.txt": [
-   "3924738568928d31b04ecfb255466b7d357d1504",
-   "support"
-  ],
   "svg/geometry/parsing/cy-computed.svg": [
-   "97a41aed9c1621c8da0f5318cf1a08339867d03f",
+   "23b56cbe79410616bddb7c687d7076ad922fea9c",
    "testharness"
   ],
   "svg/geometry/parsing/cy-invalid-expected.txt": [
@@ -433830,12 +433793,8 @@
    "4372c36ea225328926030830e895f64858e73784",
    "testharness"
   ],
-  "svg/geometry/parsing/r-computed-expected.txt": [
-   "06df88a859de902b7bf796120b9e9e56bffe84b9",
-   "support"
-  ],
   "svg/geometry/parsing/r-computed.svg": [
-   "fc8ea983ff00e82e61fbc8b974657c1956391f8e",
+   "b0f6bb0f671267c4895413ff749f8a7a96f2cb5a",
    "testharness"
   ],
   "svg/geometry/parsing/r-invalid-expected.txt": [
@@ -433850,12 +433809,8 @@
    "70cd5503b795cef3f5db7eb4db1034d63b3e63fc",
    "testharness"
   ],
-  "svg/geometry/parsing/rx-computed-expected.txt": [
-   "4a7473248a0e6a913282ec7cc733a03929da4aed",
-   "support"
-  ],
   "svg/geometry/parsing/rx-computed.svg": [
-   "7e4c42d29304a1f03e712149960356c5e0c7464b",
+   "a3210801ae3f748ba1612ff88d73cb438e5eb6b1",
    "testharness"
   ],
   "svg/geometry/parsing/rx-invalid-expected.txt": [
@@ -433870,12 +433825,8 @@
    "e2b628f1de4bd8e2a3fec98fda1aa76b6af79b87",
    "testharness"
   ],
-  "svg/geometry/parsing/ry-computed-expected.txt": [
-   "d4cfd52e00f1c276f2e7db9706af8cbbeeaa4832",
-   "support"
-  ],
   "svg/geometry/parsing/ry-computed.svg": [
-   "390d63dc7e5aa9802edea9ad2193695e1dbdb688",
+   "b274199cec30beda41fda815b01f7445e2a9ffbf",
    "testharness"
   ],
   "svg/geometry/parsing/ry-invalid-expected.txt": [
@@ -433890,12 +433841,8 @@
    "4ce5bec4084cf0e6cf1e6744701b756cc18e2f91",
    "testharness"
   ],
-  "svg/geometry/parsing/x-computed-expected.txt": [
-   "b7b5a0d3fc426b529122aa553e5ccf74eb401b35",
-   "support"
-  ],
   "svg/geometry/parsing/x-computed.svg": [
-   "f7da0ed9564380250bff469f388bf1a88d31a43a",
+   "9355ea3ae95767b9cee92d49b4f1d1809e2edc06",
    "testharness"
   ],
   "svg/geometry/parsing/x-invalid-expected.txt": [
@@ -433910,12 +433857,8 @@
    "5ff2fbd831d7373faee706538f08528dc4ac8a99",
    "testharness"
   ],
-  "svg/geometry/parsing/y-computed-expected.txt": [
-   "71c245f57f18e8a6a445c381948efb896df4ccd2",
-   "support"
-  ],
   "svg/geometry/parsing/y-computed.svg": [
-   "83ef887da671eb532661c00e113118d6daa2e4b4",
+   "6c425e4f518eff45f781bd9ad4b6bab51126f2df",
    "testharness"
   ],
   "svg/geometry/parsing/y-invalid-expected.txt": [
@@ -434082,12 +434025,16 @@
    "4499b9249a84a9d7de4a5a101993ad14f9cc21c9",
    "testharness"
   ],
+  "svg/painting/parsing/stroke-width-computed.svg": [
+   "c9284be9a13a633872d9abb670f1c4390b540030",
+   "testharness"
+  ],
   "svg/painting/parsing/stroke-width-invalid.svg": [
    "0d3f63d077f29a0a36f6443164dc7f24421a3f62",
    "testharness"
   ],
   "svg/painting/parsing/stroke-width-valid.svg": [
-   "312b8923046a2944c052b085dabcf94aab25196a",
+   "02bca189f74fe91088ebe913f848b80dfc24868c",
    "testharness"
   ],
   "svg/painting/reftests/markers-orient-001-ref.svg": [
@@ -436727,7 +436674,7 @@
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property-expected.txt": [
-   "949315d5fcb6d93b2ac5ce71f7f2c2078d910de9",
+   "27a349b10bde8c291aece41e93b16190f3034133",
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property.html": [
@@ -436735,7 +436682,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/addition-per-property-expected.txt": [
-   "482d5373d90e2ef706c606ca7eebc34978049e10",
+   "75190da20f6d5a3a8856432131a20130527ddbb3",
    "support"
   ],
   "web-animations/animation-model/animation-types/addition-per-property.html": [
@@ -436747,7 +436694,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property-expected.txt": [
-   "6a24e5f47ff35cbedda98bf6793952d27f1e7a0d",
+   "afd0cfa2ec220a5eff1649acc6365892d5cae9d1",
    "support"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property.html": [
@@ -436759,7 +436706,7 @@
    "support"
   ],
   "web-animations/animation-model/animation-types/property-types.js": [
-   "519e38ae8aea2a6e5d762af56069ebddbb231838",
+   "9382c6297b9bb07ae06f95fbd099bcda5727a85f",
    "support"
   ],
   "web-animations/animation-model/animation-types/visibility.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-features b/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-features
new file mode 100644
index 0000000..143ff58
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-features
@@ -0,0 +1,6 @@
+{
+  "feature-policy": [
+    "camera 'self' https://example.org",
+    "geolocation https://example.org/"
+  ]
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/abort.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/abort.https.window.js
index b699406..9cc8c25 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/abort.https.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/abort.https.window.js
@@ -8,7 +8,7 @@
 backgroundFetchTest(async (test, backgroundFetch) => {
   const registration = await backgroundFetch.fetch(
       uniqueId(),
-      ['resources/feature-name.txt', '/serviceworker/resources/slow-response.php']);
+      ['resources/feature-name.txt', '/common/slow.py']);
 
   assert_true(await registration.abort());
   assert_false(await registration.abort());
@@ -18,7 +18,7 @@
 backgroundFetchTest(async (test, backgroundFetch) => {
   const registration = await backgroundFetch.fetch(
       uniqueId(),
-      ['resources/feature-name.txt', '/serviceworker/resources/slow-response.php']);
+      ['resources/feature-name.txt', '/common/slow.py']);
 
   await new Promise(resolve => {
     let aborted = false;
@@ -63,7 +63,7 @@
 
 backgroundFetchTest(async (test, backgroundFetch) => {
   const registration = await backgroundFetch.fetch(
-      uniqueId(), '/serviceworker/resources/slow-response.php');
+      uniqueId(), '/common/slow.py');
   assert_true(await registration.abort());
 
   const {results} = await getMessageFromServiceWorker();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch-uploads.https.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch-uploads.https.window-expected.txt
new file mode 100644
index 0000000..b422061
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch-uploads.https.window-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Fetch with an upload should work promise_test: Unhandled rejection with value: object "TypeError: Requests with a body are not yet supported. For updates check http://crbug.com/774054"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch-uploads.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch-uploads.https.window.js
new file mode 100644
index 0000000..c53b2996
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch-uploads.https.window.js
@@ -0,0 +1,23 @@
+// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
+// META: script=resources/utils.js
+'use strict';
+
+// Covers basic functionality provided by BackgroundFetchManager.fetch().
+// Specifically, when `fetch` contains request uploads.
+// https://wicg.github.io/background-fetch/#background-fetch-manager-fetch
+
+backgroundFetchTest(async (test, backgroundFetch) => {
+  const uploadData = 'Background Fetch!';
+  const request =
+    new Request('resources/upload.py', {method: 'POST', body: uploadData});
+
+  await backgroundFetch.fetch(uniqueId(), request);
+  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
+
+  assert_equals(type, 'backgroundfetchsuccess');
+  assert_equals(results.length, 1);
+  assert_equals(eventRegistration.result, 'success');
+  assert_equals(eventRegistration.failureReason, '');
+  assert_equals(results[0].text, uploadData);
+
+}, 'Fetch with an upload should work');
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window-expected.txt
index c2f9ace..d2026e32 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window-expected.txt
@@ -9,7 +9,6 @@
 PASS Registration object gets updated values when a background fetch completes.
 PASS Background Fetch that exceeds the quota throws a QuotaExceededError
 FAIL Fetches can have requests with duplicate URLs promise_test: Unhandled rejection with value: object "TypeError: Fetches with duplicate requests are not yet supported. Consider adding query params to make the requests unique. For updates check http://crbug.com/871174"
-FAIL Fetches can have requests with a body promise_test: Unhandled rejection with value: object "TypeError: Requests with a body are not yet supported. For updates check http://crbug.com/774054"
 PASS recordsAvailable is false after onbackgroundfetchsuccess finishes execution.
 PASS Using Background Fetch to fetch a non-existent resource should fail.
 PASS Fetches with mixed content should fail.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
index aee777d..52d29db5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
@@ -196,27 +196,6 @@
 }, 'Fetches can have requests with duplicate URLs');
 
 backgroundFetchTest(async (test, backgroundFetch) => {
-  const request =
-    new Request('resources/feature-name.txt',
-                {method: 'POST', body: 'TestBody'});
-
-  const registration = await backgroundFetch.fetch('my-id', request);
-
-  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
-  assert_equals('backgroundfetchsuccess', type);
-  assert_equals(results.length, 1);
-
-  assert_equals(eventRegistration.id, registration.id);
-  assert_equals(eventRegistration.failureReason, '');
-
-  assert_true(results[0].url.includes('resources/feature-name.txt'));
-  assert_equals(results[0].status, 200);
-  assert_equals(results[0].text, 'Background Fetch');
-
-
-}, 'Fetches can have requests with a body');
-
-backgroundFetchTest(async (test, backgroundFetch) => {
   const registrationId = uniqueId();
   const registration =
     await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
@@ -319,4 +298,5 @@
   assert_equals(eventRegistration.result, 'success');
   assert_equals(eventRegistration.failureReason, '');
 
-}, 'Matching to a non-existing request should work');
\ No newline at end of file
+}, 'Matching to a non-existing request should work');
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/resources/upload.py b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/resources/upload.py
new file mode 100644
index 0000000..585144e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/resources/upload.py
@@ -0,0 +1,3 @@
+# Simply returns the request body to check if the upload succeeded.
+def main(request, response):
+    return 200, [("Content-Type", request.headers['content-type'])], request.body
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/dom/documents/dom-tree-accessors/document.title-not-in-html-svg.html b/third_party/WebKit/LayoutTests/external/wpt/html/dom/documents/dom-tree-accessors/document.title-not-in-html-svg.html
new file mode 100644
index 0000000..40eccd3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/dom/documents/dom-tree-accessors/document.title-not-in-html-svg.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+function newXMLDocument() {
+  return document.implementation.createDocument(null, "foo", null);
+}
+
+test(function() {
+  var doc = newXMLDocument();
+  assert_equals(doc.title, "");
+  doc.title = "fail";
+  assert_equals(doc.title, "");
+}, "Should not be able to set document title in XML document");
+
+test(function() {
+  var doc = newXMLDocument();
+  doc.documentElement.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "html:title"));
+  assert_equals(doc.title, "");
+  doc.title = "fail";
+  assert_equals(doc.title, "");
+}, "Should not be able to set document title in XML document with html:title element");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-features.https.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-features.https.tentative.html
new file mode 100644
index 0000000..b5dbe3e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-features.https.tentative.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script src='/resources/testharness.js'></script>
+  <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+  <script>
+    async_test(t => {
+      assert_false(document.policy.allowsFeature('geolocation'));
+      assert_true(document.policy.allowsFeature('camera'));
+      t.done();
+    }, "Origin-Policy-based Feature policy");
+  </script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-features.https.tentative.html.headers b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-features.https.tentative.html.headers
new file mode 100644
index 0000000..3e5ab40
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-features.https.tentative.html.headers
@@ -0,0 +1 @@
+Sec-Origin-Policy: policy-features
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/complete-method-manual.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/complete-method-manual.https.html
index a9da9ee..0e24de8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/complete-method-manual.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/complete-method-manual.https.html
@@ -12,12 +12,15 @@
   button.disabled = true;
   const { response, request } = await getPaymentRequestResponse();
   promise_test(async t => {
+    let completePromise;
+    let invalidComplete;
+    let afterComplete;
     try {
       // We .complete() as normal, using the passed test value
-      const completePromise = response.complete(result);
+      completePromise = response.complete(result);
       assert_true(completePromise instanceof Promise, "returns a promise");
       // Immediately calling complete() again yields a rejected promise.
-      const invalidComplete = response.complete(result);
+      invalidComplete = response.complete(result);
       await promise_rejects(t, "InvalidStateError", invalidComplete);
       // but the original promise is unaffected
       const returnedValue = await completePromise;
@@ -28,7 +31,7 @@
       );
       // We now call .complete() again, to force an exception
       // because [[complete]] is true.
-      const afterComplete = response.complete(result);
+      afterComplete = response.complete(result);
       await promise_rejects(t, "InvalidStateError", afterComplete);
       button.innerHTML = `✅  ${button.textContent}`;
     } catch (err) {
@@ -41,7 +44,7 @@
       afterComplete,
     ]);
     assert_equals(
-      allPromises.length,
+      allPromises.size,
       3,
       "Calling complete() multiple times is always a new object."
     );
@@ -68,7 +71,7 @@
     </button>
   </li>
   <li>
-    <button onclick="runManualTest({completeWith: 'unknown'}, this)">
+    <button onclick="runManualTest({completeWith: undefined}, this)">
       Passing no argument defaults to "unknown",
       eventually closing the sheet and doesn't throw.
     </button>
@@ -79,10 +82,18 @@
     </button>
   </li>
   <li>
-    <button onclick="runManualTest({completeWith: 'fail'}, this).then(done)">
+    <button onclick="runManualTest({completeWith: 'fail'}, this)">
       Passing "fail" eventually closes the sheet and doesn't throw.
     </button>
   </li>
+  <li>
+    <button onclick="runManualTest({completeWith: 'unknown'}, this)">
+      Passing "unknown" eventually closes the sheet and doesn't throw.
+    </button>
+  </li>
+  <li>
+    <button onclick="done()">Done!</button>
+  </li>
 </ol>
 <small>
   If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/retry-method-manual.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/retry-method-manual.https.html
index 16ed15e5..69bda5e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/retry-method-manual.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/retry-method-manual.https.html
@@ -55,12 +55,30 @@
   promise_test(async t => {
     const { response } = await getPaymentRequestResponse();
     const retryPromise = response.retry();
+    const completePromise1 = response.complete("success");
+    const completePromise2 = response.complete("failure");
+    assert_not_equals(
+      completePromise1,
+      completePromise2,
+      "complete() must return unique promises"
+    );
     await promise_rejects(
       t,
       "InvalidStateError",
-      response.complete("success"),
+      completePromise1,
       "Calling complete() while retrying rejects with an InvalidStateError"
     );
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      completePromise2,
+      "Calling complete() while retrying rejects with an InvalidStateError"
+    );
+    assert_not_equals(
+      completePromise1,
+      completePromise2,
+      "complete() must return unique promises"
+    );
     await retryPromise;
     await response.complete("success");
   }, button.textContent.trim());
diff --git a/third_party/WebKit/LayoutTests/external/wpt/svg/embedded/image-embedding-svg-with-viewport-units.svg b/third_party/WebKit/LayoutTests/external/wpt/svg/embedded/image-embedding-svg-with-viewport-units.svg
new file mode 100644
index 0000000..6feb54b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/svg/embedded/image-embedding-svg-with-viewport-units.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     xmlns:h="http://www.w3.org/1999/xhtml">
+  <metadata>
+    <title>&lt;image&gt; embedding SVG image with viewport units</title>
+    <h:link rel="match" href="support/green-rect-100x100.svg"/>
+  </metadata>
+  <image xlink:href="data:image/svg+xml,&lt;svg xmlns='http://www.w3.org/2000/svg'&gt;&lt;rect width='50vw' height='50vh' fill='green'/&gt;&lt;/svg&gt;"
+         width="200" height="200"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/svg/embedded/support/green-rect-100x100.svg b/third_party/WebKit/LayoutTests/external/wpt/svg/embedded/support/green-rect-100x100.svg
new file mode 100644
index 0000000..1209414
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/svg/embedded/support/green-rect-100x100.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+  <rect width="100" height="100" fill="green"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/flag-specific/site-per-process/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/disable-site-isolation-trials/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt
similarity index 92%
rename from third_party/WebKit/LayoutTests/flag-specific/site-per-process/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt
rename to third_party/WebKit/LayoutTests/flag-specific/disable-site-isolation-trials/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt
index daf6459..16ba086 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/site-per-process/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/disable-site-isolation-trials/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt
@@ -4,5 +4,5 @@
 Set-Cookie: name=value
 
 <script src="http://devtools.oopif.test:8000/inspector-protocol/network/resources/cookie.pl">
-Set-Cookie: undefined
+Set-Cookie: name=value
 
diff --git a/third_party/WebKit/LayoutTests/flag-specific/site-per-process/http/tests/inspector-protocol/network/security-info-on-response-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/disable-site-isolation-trials/http/tests/inspector-protocol/network/security-info-on-response-expected.txt
similarity index 81%
rename from third_party/WebKit/LayoutTests/flag-specific/site-per-process/http/tests/inspector-protocol/network/security-info-on-response-expected.txt
rename to third_party/WebKit/LayoutTests/flag-specific/disable-site-isolation-trials/http/tests/inspector-protocol/network/security-info-on-response-expected.txt
index 0ca4430..547fe81 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/site-per-process/http/tests/inspector-protocol/network/security-info-on-response-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/disable-site-isolation-trials/http/tests/inspector-protocol/network/security-info-on-response-expected.txt
@@ -1,5 +1,5 @@
 Tests that security details are reported, even in the case of site isolation.
 
 Security state: insecure
-Set-Cookie: undefined
+Set-Cookie: name=value
 
diff --git a/third_party/WebKit/LayoutTests/flag-specific/site-per-process/external/wpt/dom/events/EventListener-addEventListener.sub.window-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/site-per-process/external/wpt/dom/events/EventListener-addEventListener.sub.window-expected.txt
deleted file mode 100644
index d925bb0..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/site-per-process/external/wpt/dom/events/EventListener-addEventListener.sub.window-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL EventListener.addEventListener doesn't throw when a cross origin object is passed in. Failed to execute 'addEventListener' on 'EventTarget': The callback provided as parameter 2 is a cross origin object.
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt
index 16ba086..daf6459 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-headers-for-protected-document-expected.txt
@@ -4,5 +4,5 @@
 Set-Cookie: name=value
 
 <script src="http://devtools.oopif.test:8000/inspector-protocol/network/resources/cookie.pl">
-Set-Cookie: name=value
+Set-Cookie: undefined
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/security-info-on-response-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/security-info-on-response-expected.txt
index 547fe81..0ca4430 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/security-info-on-response-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/security-info-on-response-expected.txt
@@ -1,5 +1,5 @@
 Tests that security details are reported, even in the case of site isolation.
 
 Security state: insecure
-Set-Cookie: name=value
+Set-Cookie: undefined
 
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/video-paint-invalidation-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/video-paint-invalidation-expected.txt
index e2b1cf40..9f880cef 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/video-paint-invalidation-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/video-paint-invalidation-expected.txt
@@ -24,16 +24,18 @@
       "drawsContent": false
     },
     {
-      "name": "Squashing Containment Layer",
-      "drawsContent": false
-    },
-    {
       "name": "LayoutFlexibleBox (relative positioned) DIV class='sizing-small test-mode phase-ready state-stopped'",
       "position": [8, 8],
       "bounds": [320, 240]
     },
     {
-      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
+      "name": "LayoutBlockFlow (positioned) DIV",
+      "position": [8, 8],
+      "bounds": [320, 240],
+      "drawsContent": false
+    },
+    {
+      "name": "LayoutFlexibleBox DIV",
       "position": [8, 8],
       "bounds": [320, 240]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/video/video-controls-squashing-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/video/video-controls-squashing-expected.png
new file mode 100644
index 0000000..c663e2dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/video/video-controls-squashing-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
index 89bf264..f68a5f7 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -466,18 +466,15 @@
       "drawsContent": false
     },
     {
-      "name": "Squashing Containment Layer",
-      "drawsContent": false
-    },
-    {
       "name": "LayoutFlexibleBox (relative positioned) DIV class='sizing-small phase-ready state-stopped'",
       "position": [15, 859],
       "bounds": [150, 60]
     },
     {
-      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
+      "name": "LayoutBlockFlow (positioned) DIV",
       "position": [15, 859],
-      "bounds": [150, 60]
+      "bounds": [150, 60],
+      "drawsContent": false
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-controls-squashing-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-controls-squashing-expected.png
new file mode 100644
index 0000000..cc3dc94e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-controls-squashing-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-poster-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-poster-expected.txt
index f01c7710..e90e866 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-poster-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-poster-expected.txt
@@ -76,18 +76,15 @@
       "drawsContent": false
     },
     {
-      "name": "Squashing Containment Layer",
-      "drawsContent": false
-    },
-    {
       "name": "LayoutFlexibleBox (relative positioned) DIV class='sizing-small phase-ready state-stopped'",
       "position": [8, 8],
       "bounds": [352, 288]
     },
     {
-      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
+      "name": "LayoutBlockFlow (positioned) DIV",
       "position": [8, 8],
-      "bounds": [352, 288]
+      "bounds": [352, 288],
+      "drawsContent": false
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
index 53b6b0a..b0b8f757 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -466,18 +466,15 @@
       "drawsContent": false
     },
     {
-      "name": "Squashing Containment Layer",
-      "drawsContent": false
-    },
-    {
       "name": "LayoutFlexibleBox (relative positioned) DIV class='sizing-small phase-ready state-stopped'",
       "position": [15, 854],
       "bounds": [150, 60]
     },
     {
-      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
+      "name": "LayoutBlockFlow (positioned) DIV",
       "position": [15, 854],
-      "bounds": [150, 60]
+      "bounds": [150, 60],
+      "drawsContent": false
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-controls-squashing-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-controls-squashing-expected.png
new file mode 100644
index 0000000..e09a30a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-controls-squashing-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-poster-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-poster-expected.txt
index d5a38eef..1737809 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-poster-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-poster-expected.txt
@@ -76,18 +76,15 @@
       "drawsContent": false
     },
     {
-      "name": "Squashing Containment Layer",
-      "drawsContent": false
-    },
-    {
       "name": "LayoutFlexibleBox (relative positioned) DIV class='sizing-small phase-ready state-stopped'",
       "position": [8, 8],
       "bounds": [352, 288]
     },
     {
-      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
+      "name": "LayoutBlockFlow (positioned) DIV",
       "position": [8, 8],
-      "bounds": [352, 288]
+      "bounds": [352, 288],
+      "drawsContent": false
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
index 3dc2f94..5957b1d 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -466,18 +466,15 @@
       "drawsContent": false
     },
     {
-      "name": "Squashing Containment Layer",
-      "drawsContent": false
-    },
-    {
       "name": "LayoutFlexibleBox (relative positioned) DIV class='sizing-small phase-ready state-stopped'",
       "position": [15, 859],
       "bounds": [150, 60]
     },
     {
-      "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
+      "name": "LayoutBlockFlow (positioned) DIV",
       "position": [15, 859],
-      "bounds": [150, 60]
+      "bounds": [150, 60],
+      "drawsContent": false
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/virtual/not-site-per-process/README.md b/third_party/WebKit/LayoutTests/virtual/not-site-per-process/README.md
new file mode 100644
index 0000000..6fc09489
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/not-site-per-process/README.md
@@ -0,0 +1,55 @@
+# virtual/not-site-per-process
+
+## Summary
+
+Layout tests use the default site isolation from the platform they are
+run on.  For example, strict site isolation (aka site-per-process) is
+used on desktop platforms.
+
+Additionally, on platforms where strict site isolation is enabled,
+layout tests opt into slightly stricter isolation and enabling isolating
+same-site origins used by Web Platform Tests. This ensures that features
+covered by WPT also provide sufficient coverage of how these feature
+behave in presence of out-of-process iframes.
+
+Tests under `virtual/not-site-per-process` are run with
+`--disable-site-isolation-trials` cmdline flag which turns off site
+isolation.  This is needed to preserve test coverage provided by around
+60 tests that fail when run with site isolation.
+
+When modifying the list of files that behave differently with and without
+OOPIFs, please consider modifying all the locations below:
+- LayoutTests/VirtualTestSuites (virtual/not-site-per-process suite)
+- LayoutTests/virtual/not-site-per-process/README.md
+- LayoutTests/TestExpectations and/or LayoutTests/NeverFixTests
+  ("Site Isolation failures" section)
+
+
+## Tests incompatible with WPT origin isolation
+
+The following tests modify `document.domain` and are therefore incompatible with
+isolation of WPT origins.  The tests need to stay under
+`virtual/not-site-per-process` forever.  These tests are covered by
+`LayoutTests/NeverFixTests` expectations file.
+
+- external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction
+- external/wpt/FileAPI/url/multi-global-origin-serialization.sub.html
+- external/wpt/dom/events/EventListener-incumbent-global-1.sub.html
+- external/wpt/dom/events/EventListener-incumbent-global-2.sub.html
+- external/wpt/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html
+- external/wpt/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub.html
+- external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html
+- external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html
+- external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-success.sub.html
+- external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html
+- external/wpt/wasm/serialization/window-domain-success.sub.html
+- external/wpt/wasm/serialization/window-similar-but-cross-origin-success.sub.html
+
+## Tests that need further investigation and/or decisions
+
+Remaining tests need further investigation as they may either 1) hide
+previously unknown OOPIF-related bugs or 2) expose known OOPIF-related
+differences in product behavior or 3) expose known OOPIF-support issues
+in tests or in the test harness.  Over time, such tests should be
+removed from `virtual/not-site-per-process`.  These tests are covered
+by `LayoutTests/TestExpectations` file.
diff --git a/third_party/blink/common/origin_policy/origin_policy_parser.cc b/third_party/blink/common/origin_policy/origin_policy_parser.cc
index 98e0bc1..df42efe 100644
--- a/third_party/blink/common/origin_policy/origin_policy_parser.cc
+++ b/third_party/blink/common/origin_policy/origin_policy_parser.cc
@@ -33,7 +33,12 @@
     return false;
 
   base::Value* csp = json->FindKey("content-security-policy");
-  return !csp || ParseContentSecurityPolicies(*csp);
+  bool csp_ok = !csp || ParseContentSecurityPolicies(*csp);
+
+  base::Value* features = json->FindKey("feature-policy");
+  bool features_ok = !features || ParseFeaturePolicies(*features);
+
+  return csp_ok && features_ok;
 }
 
 bool OriginPolicyParser::ParseContentSecurityPolicies(
@@ -63,4 +68,23 @@
   return true;
 }
 
+bool OriginPolicyParser::ParseFeaturePolicies(const base::Value& policies) {
+  if (!policies.is_list())
+    return false;
+
+  bool ok = true;
+  for (const auto& feature : policies.GetList()) {
+    ok &= ParseFeaturePolicy(feature);
+  }
+  return ok;
+}
+
+bool OriginPolicyParser::ParseFeaturePolicy(const base::Value& policy) {
+  if (!policy.is_string())
+    return false;
+
+  policy_->features_.push_back(policy.GetString());
+  return true;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/common/origin_policy/origin_policy_parser.h b/third_party/blink/common/origin_policy/origin_policy_parser.h
index abc7a7b..5deb3bc 100644
--- a/third_party/blink/common/origin_policy/origin_policy_parser.h
+++ b/third_party/blink/common/origin_policy/origin_policy_parser.h
@@ -29,6 +29,8 @@
   bool DoParse(base::StringPiece);
   bool ParseContentSecurityPolicies(const base::Value&);
   bool ParseContentSecurityPolicy(const base::Value&);
+  bool ParseFeaturePolicies(const base::Value&);
+  bool ParseFeaturePolicy(const base::Value&);
 
   std::unique_ptr<OriginPolicy> policy_;
 
diff --git a/third_party/blink/common/origin_policy/origin_policy_unittest.cc b/third_party/blink/common/origin_policy/origin_policy_unittest.cc
index 76fafcc7..13f8dbc 100644
--- a/third_party/blink/common/origin_policy/origin_policy_unittest.cc
+++ b/third_party/blink/common/origin_policy/origin_policy_unittest.cc
@@ -137,3 +137,38 @@
   )");
   ASSERT_FALSE(policy->GetContentSecurityPolicies()[0].report_only);
 }
+
+TEST(OriginPolicy, FeatureOne) {
+  auto policy = blink::OriginPolicy::From(R"(
+      { "feature-policy": ["geolocation 'self' http://maps.google.com"] } )");
+  ASSERT_EQ(1U, policy->GetFeaturePolicies().size());
+  ASSERT_EQ("geolocation 'self' http://maps.google.com",
+            policy->GetFeaturePolicies()[0]);
+}
+
+TEST(OriginPolicy, FeatureTwo) {
+  auto policy = blink::OriginPolicy::From(R"(
+      { "feature-policy": ["geolocation 'self' http://maps.google.com",
+                     "camera https://example.com"]} )");
+  ASSERT_EQ(2U, policy->GetFeaturePolicies().size());
+  ASSERT_EQ("geolocation 'self' http://maps.google.com",
+            policy->GetFeaturePolicies()[0]);
+  ASSERT_EQ("camera https://example.com", policy->GetFeaturePolicies()[1]);
+}
+
+TEST(OriginPolicy, FeatureTwoPolicies) {
+  auto policy = blink::OriginPolicy::From(R"(
+      { "feature-policy": ["geolocation 'self' http://maps.google.com"],
+        "feature-policy": ["camera https://example.com"] } )");
+
+  // TODO(vogelheim): Determine whether this is the correct behaviour.
+  ASSERT_EQ(1U, policy->GetFeaturePolicies().size());
+}
+
+TEST(OriginPolicy, FeatureComma) {
+  auto policy = blink::OriginPolicy::From(R"(
+      { "feature-policy": ["geolocation 'self' http://maps.google.com, camera https://example.com"]} )");
+
+  // TODO: Determine what to do with this case !
+  ASSERT_EQ(1U, policy->GetFeaturePolicies().size());
+}
diff --git a/third_party/blink/public/common/origin_policy/origin_policy.h b/third_party/blink/public/common/origin_policy/origin_policy.h
index c86631c..5c4583fb 100644
--- a/third_party/blink/public/common/origin_policy/origin_policy.h
+++ b/third_party/blink/public/common/origin_policy/origin_policy.h
@@ -28,12 +28,17 @@
   };
   const std::vector<CSP>& GetContentSecurityPolicies() const { return csp_; }
 
+  const std::vector<std::string>& GetFeaturePolicies() const {
+    return features_;
+  }
+
  private:
   friend class OriginPolicyParser;
 
   OriginPolicy();
 
   std::vector<CSP> csp_;
+  std::vector<std::string> features_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index dc92675..d1b6ad7 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -1681,31 +1681,25 @@
 
 void Document::setTitle(const String& title) {
   // Title set by JavaScript -- overrides any title elements.
-  if (!title_element_) {
-    if (IsHTMLDocument() || IsXHTMLDocument()) {
+  Element* element = documentElement();
+  if (IsSVGSVGElement(element)) {
+    if (!title_element_) {
+      title_element_ = SVGTitleElement::Create(*this);
+      element->InsertBefore(title_element_.Get(), element->firstChild());
+    }
+    if (auto* svg_title = ToSVGTitleElementOrNull(title_element_))
+      svg_title->SetText(title);
+  } else if (element && element->IsHTMLElement()) {
+    if (!title_element_) {
       HTMLElement* head_element = head();
       if (!head_element)
         return;
       title_element_ = HTMLTitleElement::Create(*this);
       head_element->AppendChild(title_element_.Get());
-    } else if (IsSVGDocument()) {
-      Element* element = documentElement();
-      if (!IsSVGSVGElement(element))
-        return;
-      title_element_ = SVGTitleElement::Create(*this);
-      element->InsertBefore(title_element_.Get(), element->firstChild());
     }
-  } else {
-    if (!IsHTMLDocument() && !IsXHTMLDocument() && !IsSVGDocument())
-      title_element_ = nullptr;
+    if (auto* html_title = ToHTMLTitleElementOrNull(title_element_))
+      html_title->setText(title);
   }
-
-  if (auto* html_title = ToHTMLTitleElementOrNull(title_element_))
-    html_title->setText(title);
-  else if (auto* svg_title = ToSVGTitleElementOrNull(title_element_))
-    svg_title->SetText(title);
-  else
-    UpdateTitle(title);
 }
 
 void Document::SetTitleElement(Element* title_element) {
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index ca882ba..46e5042 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -11524,6 +11524,60 @@
       << visual_viewport.VisibleRectInDocument().ToString() << "]";
 }
 
+// Test bubbling a document (End key) scroll from an inner iframe. This test
+// passes if it does not crash. https://crbug.com/904247.
+TEST_F(WebFrameSimTest, ScrollToEndBubblingCrash) {
+  WebView().Resize(WebSize(500, 300));
+  WebView().GetPage()->GetSettings().SetScrollAnimatorEnabled(false);
+
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        body, html {
+          width: 100%;
+          height: 100%;
+          margin: 0;
+        }
+        #frame {
+          width: 100%;
+          height: 100%;
+          border: 0;
+        }
+      </style>
+      <iframe id="frame" srcdoc="
+          <!DOCTYPE html>
+          <style>html {height: 300%;}</style>
+      "></iframe>
+  )HTML");
+
+  Compositor().BeginFrame();
+  RunPendingTasks();
+
+  // Focus the iframe.
+  WebView().AdvanceFocus(false);
+
+  WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
+                             WebInputEvent::kNoModifiers,
+                             WebInputEvent::GetStaticTimeStampForTests());
+  key_event.windows_key_code = VKEY_END;
+
+  // Scroll the iframe to the end.
+  key_event.SetType(WebInputEvent::kRawKeyDown);
+  WebView().HandleInputEvent(WebCoalescedInputEvent(key_event));
+  key_event.SetType(WebInputEvent::kKeyUp);
+  WebView().HandleInputEvent(WebCoalescedInputEvent(key_event));
+
+  Compositor().BeginFrame();
+
+  // End key should now bubble from the iframe up to the main viewport.
+  key_event.SetType(WebInputEvent::kRawKeyDown);
+  WebView().HandleInputEvent(WebCoalescedInputEvent(key_event));
+  key_event.SetType(WebInputEvent::kKeyUp);
+  WebView().HandleInputEvent(WebCoalescedInputEvent(key_event));
+}
+
 // Basic smoke test of the paint path used by the Android disambiguation popup.
 TEST_F(WebFrameSimTest, DisambiguationPopupPixelTest) {
   WebView().Resize(WebSize(400, 600));
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index ae744f5..68e636a 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -187,10 +187,10 @@
   if (!current_node.GetLayoutBox())
     return false;
 
-  // We need to always add the root scroller in the main frame even if the
-  // viewport isn't scrollable since we can always pinch-zoom and scroll as
-  // well as for overscroll effects.
-  if (current_node.IsEffectiveRootScroller() && frame_->IsMainFrame())
+  // We need to always add the global root scroller even if it isn't scrollable
+  // since we can always pinch-zoom and scroll as well as for overscroll
+  // effects.
+  if (current_node.GetLayoutBox()->IsGlobalRootScroller())
     return true;
 
   ScrollableArea* scrollable_area =
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 491b95a..aaa30cf4 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1129,6 +1129,29 @@
       previous_security_origin);
 }
 
+// Helper function: Merge the feature policy strings from HTTP headers and the
+// origin policy (if any).
+// Headers go first, which means that the per-page headers override the
+// origin policy features.
+void MergeFeaturesFromOriginPolicy(WTF::String& feature_policy,
+                                   const String& origin_policy_string) {
+  if (origin_policy_string.IsEmpty())
+    return;
+
+  std::unique_ptr<OriginPolicy> origin_policy = OriginPolicy::From(
+      StringUTF8Adaptor(origin_policy_string).AsStringPiece());
+  if (!origin_policy)
+    return;
+
+  for (const std::string& policy : origin_policy->GetFeaturePolicies()) {
+    if (!feature_policy.IsEmpty()) {
+      feature_policy.append(',');
+    }
+    feature_policy.append(
+        WTF::String::FromUTF8(policy.data(), policy.length()));
+  }
+}
+
 void DocumentLoader::InstallNewDocument(
     const KURL& url,
     Document* owner_document,
@@ -1246,8 +1269,10 @@
   // FeaturePolicy is reset in the browser process on commit, so this needs to
   // be initialized and replicated to the browser process after commit messages
   // are sent in didCommitNavigation().
-  document->ApplyFeaturePolicyFromHeader(
+  WTF::String feature_policy(
       response_.HttpHeaderField(http_names::kFeaturePolicy));
+  MergeFeaturesFromOriginPolicy(feature_policy, request_.GetOriginPolicy());
+  document->ApplyFeaturePolicyFromHeader(feature_policy);
 
   GetFrameLoader().DispatchDidClearDocumentOfWindowObject();
 }
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.cc b/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.cc
index 60fcf54..3881838 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.cc
@@ -238,6 +238,9 @@
         layer->DirectCompositingReasons());
   }
 
+  if (layer->GetLayoutObject().IsVideo())
+    info.is_under_video = true;
+
   bool should_recurse =
       layer->ChildNeedsCompositingInputsUpdate() || update_type == kForceUpdate;
 
@@ -360,6 +363,8 @@
   if (info.needs_reparent_scroll && layout_object.StyleRef().IsStacked())
     properties.scroll_parent = info.scrolling_ancestor;
 
+  properties.is_under_video = info.is_under_video;
+
   layer->UpdateAncestorDependentCompositingInputs(properties);
 }
 
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h b/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h
index 5303a1e9..823f156 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h
@@ -61,6 +61,8 @@
     bool needs_reparent_scroll = false;
     bool needs_reparent_scroll_for_absolute = false;
     bool needs_reparent_scroll_for_fixed = false;
+
+    bool is_under_video = false;
   };
 
   void UpdateRecursive(PaintLayer*, UpdateType, AncestorInfo);
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc b/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc
index 1e011f6df..e048a9f2 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc
@@ -135,15 +135,10 @@
   const PaintLayer& squashing_layer =
       squashing_state.most_recent_mapping->OwningLayer();
 
-  // FIXME: this special case for video exists only to deal with corner cases
-  // where a LayoutVideo does not report that it needs to be directly
-  // composited.  Video does not currently support sharing a backing, but this
-  // could be generalized in the future. The following layout tests fail if we
-  // permit the video to share a backing with other layers.
-  //
-  // compositing/video/video-controls-layer-creation.html
-  if (layer->GetLayoutObject().IsVideo() ||
-      squashing_layer.GetLayoutObject().IsVideo())
+  // Don't squash into or out of any thing underneath a video, including the
+  // user-agent shadow DOM for controls. This is is to work around a
+  // bug involving overflow clip of videos. See crbug.com/900602.
+  if (layer->IsUnderVideo() || squashing_layer.IsUnderVideo())
     return SquashingDisallowedReason::kSquashingVideoIsDisallowed;
 
   // Don't squash iframes, frames or plugins.
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index 30cfec7..d21a2b7 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -282,8 +282,8 @@
     base::TimeTicks timestamp) {
   // The callback is safe from race-condition only when running on main-thread.
   DCHECK(ThreadState::Current()->IsMainThread());
-  // Not guranteed to be non-empty, because record can be removed before the
-  // callback.
+  // Not guranteed to be non-empty, because records can be removed between
+  // callback registration and invocation.
   while (records_pending_timing_.size() > 0) {
     DOMNodeId node_id = records_pending_timing_.front();
     if (!id_record_map_.Contains(node_id)) {
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
index 37f41fb..8da96b7 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
@@ -231,6 +231,52 @@
   EXPECT_EQ(LargestPaintStoredResult(), base::TimeTicks());
 }
 
+TEST_F(ImagePaintTimingDetectorTest,
+       LargestImagePaint_NodeRemovedBetweenRegistrationAndInvocation) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="parent">
+      <img id="target"></img>
+    </div>
+  )HTML");
+  SetImageAndPaint("target", 5, 5);
+  UpdateAllLifecyclePhasesForTest();
+
+  GetDocument().getElementById("parent")->RemoveChild(
+      GetDocument().getElementById("target"));
+
+  InvokeCallback();
+
+  ImageRecord* record;
+  record = FindLargestPaintCandidate();
+  EXPECT_FALSE(record);
+}
+
+TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_IgnoreReAttached) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="parent">
+    </div>
+  )HTML");
+  HTMLImageElement* image = HTMLImageElement::Create(GetDocument());
+  image->setAttribute("id", "target");
+  GetDocument().getElementById("parent")->AppendChild(image);
+  SetImageAndPaint("target", 5, 5);
+  UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
+  ImageRecord* record;
+  record = FindLargestPaintCandidate();
+  EXPECT_TRUE(record);
+
+  GetDocument().getElementById("parent")->RemoveChild(image);
+  UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
+  record = FindLargestPaintCandidate();
+  EXPECT_FALSE(record);
+
+  GetDocument().getElementById("parent")->AppendChild(image);
+  SetImageAndPaint("target", 5, 5);
+  UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
+  record = FindLargestPaintCandidate();
+  EXPECT_TRUE(record);
+}
+
 // This test dipicts a situation when a smaller image has loaded, but a larger
 // image is loading. When we call analyze, the result will be empty because
 // we don't know when the largest image will finish loading. We wait until
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index bf62c42..232b549 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -754,6 +754,8 @@
     IntRect unclipped_absolute_bounding_box;
 
     const LayoutBoxModelObject* clipping_container = nullptr;
+
+    bool is_under_video = false;
   };
 
   void SetNeedsCompositingInputsUpdate();
@@ -822,6 +824,9 @@
   const PaintLayer* MaskAncestor() const {
     return GetAncestorDependentCompositingInputs().mask_ancestor;
   }
+  bool IsUnderVideo() const {
+    return GetAncestorDependentCompositingInputs().is_under_video;
+  }
   bool HasDescendantWithClipPath() const {
     DCHECK(!needs_descendant_dependent_flags_update_);
     return has_descendant_with_clip_path_;
diff --git a/third_party/blink/tools/blinkpy/common/net/buildbot_unittest.py b/third_party/blink/tools/blinkpy/common/net/buildbot_unittest.py
index cce42c0..231b923 100644
--- a/third_party/blink/tools/blinkpy/common/net/buildbot_unittest.py
+++ b/third_party/blink/tools/blinkpy/common/net/buildbot_unittest.py
@@ -107,7 +107,7 @@
         def fetch_file(_):
             return ('ADD_RESULTS(%s);' % (json.dumps(
                 [{"TestType": "webkit_layout_tests (with patch)"},
-                 {"TestType": "site_per_process_webkit_layout_tests (with patch)"},
+                 {"TestType": "not_site_per_process_webkit_layout_tests (with patch)"},
                  {"TestType": "webkit_layout_tests (retry with patch)"},
                  {"TestType": "base_unittests (with patch)"}])))
 
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/README.chromium b/third_party/blink/tools/blinkpy/third_party/wpt/README.chromium
index b9745167..ae06882bb 100644
--- a/third_party/blink/tools/blinkpy/third_party/wpt/README.chromium
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/README.chromium
@@ -28,9 +28,7 @@
 
 - update `WPT_HOST_AND_PORTS` in
   `//third_party/blink/tools/blinkpy/web_tests/port/driver.py`
-- update bot configs (e.g. `Site Isolation Linux` bot forces OOPIFs by using
-  `--isolate-origins=http://www.web-platform.test:8001/` as specified in
-  `//testing/buildbot/chromium.fyi.json`).
+- update LayoutTestContentBrowserClient::GetOriginsRequiringDedicatedProcess()
 
 checkout.sh
 ===========
diff --git a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
index 190ab13..5b7acc9 100644
--- a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
+++ b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
@@ -87,10 +87,17 @@
 
     failures = []
     for suite in virtual_suites:
+        # A virtual test suite needs either
+        # - a top-level README.md (e.g. virtual/foo/README.md)
+        # - a README.txt for each covered dir/file (e.g.
+        #   virtual/foo/http/tests/README.txt, virtual/foo/fast/README.txt, ...)
         comps = [layout_tests_dir] + suite.name.split('/') + ['README.txt']
-        path_to_readme = fs.join(*comps)
-        if not fs.exists(path_to_readme):
-            failure = '{} is missing (each virtual suite must have one).'.format(path_to_readme)
+        path_to_readme_txt = fs.join(*comps)
+        comps = [layout_tests_dir] + suite.name.split('/')[:2] + ['README.md']
+        path_to_readme_md = fs.join(*comps)
+        if not fs.exists(path_to_readme_txt) and not fs.exists(path_to_readme_md):
+            failure = '{} and {} are both missing (each virtual suite must have one).'.format(
+                path_to_readme_txt, path_to_readme_md)
             _log.error(failure)
             failures.append(failure)
     if failures:
diff --git a/third_party/sqlite/BUILD.gn b/third_party/sqlite/BUILD.gn
index fac654a..74c1608 100644
--- a/third_party/sqlite/BUILD.gn
+++ b/third_party/sqlite/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/dcheck_always_on.gni")
+import("//build/config/sanitizers/sanitizers.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 
 # Compile-time options passed to SQLite.
@@ -188,7 +189,8 @@
     }
   }
 
-  if (use_fuzzing_engine && (is_debug || dcheck_always_on)) {
+  if (use_fuzzing_engine && use_sanitizer_coverage &&
+      (is_debug || dcheck_always_on)) {
     # Enable SQLite's assert() macros.
     defines += [ "SQLITE_DEBUG" ]
 
@@ -208,10 +210,11 @@
     # sqlite3Fts3InitTok).
     cflags += [ "-Wno-unused-function" ]
 
-    if (use_fuzzing_engine && (is_debug || dcheck_always_on)) {
+    if (use_fuzzing_engine && use_sanitizer_coverage &&
+        (is_debug || dcheck_always_on)) {
       cflags += [
         "-Wno-implicit-function-declaration",
-        "-Wno-string-conversion"
+        "-Wno-string-conversion",
       ]
     }
   }
@@ -376,9 +379,7 @@
 }
 
 fuzzer_test("sqlite3_ossfuzz_fuzzer") {
-  include_dirs = [
-    ".",
-  ]
+  include_dirs = [ "." ]
   sources = [
     "src/test/ossfuzz.c",
   ]
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 0aabee49..b6df1de 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -31653,6 +31653,7 @@
   <int value="2" label="ClosedRelaunch"/>
   <int value="3" label="ClosedFail"/>
   <int value="4" label="ClosedAbort"/>
+  <int value="5" label="ClosedFailAndIgnore"/>
 </enum>
 
 <enum name="MachKernReturn">
diff --git a/tools/perf/generate_legacy_perf_dashboard_json.py b/tools/perf/generate_legacy_perf_dashboard_json.py
index 3b39aff4..b61b7df8 100755
--- a/tools/perf/generate_legacy_perf_dashboard_json.py
+++ b/tools/perf/generate_legacy_perf_dashboard_json.py
@@ -73,11 +73,12 @@
 
     def __init__(self):
       self.important = False
-      self.value = 0.0
+      self.values = []
+      self.mean = 0.0
       self.stddev = 0.0
 
     def __str__(self):
-      result = _FormatHumanReadable(self.value)
+      result = _FormatHumanReadable(self.mean)
       if self.stddev:
         result += '+/-%s' % _FormatHumanReadable(self.stddev)
       return result
@@ -100,7 +101,7 @@
       """Returns a dictionary mapping trace names to [value, stddev]."""
       traces_dict = {}
       for name, trace in self.traces.items():
-        traces_dict[name] = [str(trace.value), str(trace.stddev)]
+        traces_dict[name] = [str(trace.mean), str(trace.stddev)]
       return traces_dict
 
 
@@ -141,37 +142,43 @@
     graph = self._graphs.get(graph_name, self.Graph())
     graph.units = (match_dict['UNITS'] or '').strip()
     trace = graph.traces.get(trace_name, self.Trace())
-    trace.value = match_dict['VALUE']
+    value = match_dict['VALUE']
     trace.important = match_dict['IMPORTANT'] or False
 
     # Compute the mean and standard deviation for a list or a histogram,
     # or the numerical value of a scalar value.
-    if trace.value.startswith('['):
+    if value.startswith('['):
       try:
-        value_list = [float(x) for x in trace.value.strip('[],').split(',')]
+        value_list = [float(x) for x in value.strip('[],').split(',')]
       except ValueError:
         # Report, but ignore, corrupted data lines. (Lines that are so badly
         # broken that they don't even match the RESULTS_REGEX won't be
         # detected.)
-        logging.warning("Bad test output: '%s'" % trace.value.strip())
+        logging.warning("Bad test output: '%s'" % value.strip())
         return
-      trace.value, trace.stddev, filedata = self._CalculateStatistics(
-          value_list, trace_name)
+      trace.values += value_list
+      trace.mean, trace.stddev, filedata = self._CalculateStatistics(
+        trace.values, trace_name)
       assert filedata is not None
       for filename in filedata:
         self._PrependLog(filename, filedata[filename])
-    elif trace.value.startswith('{'):
-      stripped = trace.value.strip('{},')
+    elif value.startswith('{'):
+      stripped = value.strip('{},')
       try:
-        trace.value, trace.stddev = [float(x) for x in stripped.split(',')]
+        trace.mean, trace.stddev = [float(x) for x in stripped.split(',')]
       except ValueError:
-        logging.warning("Bad test output: '%s'" % trace.value.strip())
+        logging.warning("Bad test output: '%s'" % value.strip())
         return
     else:
       try:
-        trace.value = float(trace.value)
+        trace.values.append(float(value))
+        trace.mean, trace.stddev, filedata = self._CalculateStatistics(
+          trace.values, trace_name)
+        assert filedata is not None
+        for filename in filedata:
+          self._PrependLog(filename, filedata[filename])
       except ValueError:
-        logging.warning("Bad test output: '%s'" % trace.value.strip())
+        logging.warning("Bad test output: '%s'" % value.strip())
         return
 
     graph.traces[trace_name] = trace
@@ -183,8 +190,14 @@
     """
     charts = {}
     for graph_name, graph in self._graphs.iteritems():
+      traces = graph.BuildTracesDict()
+
+      # Traces should contain exactly two elements: [mean, stddev].
+      for _, trace in traces.iteritems():
+        assert len(trace) == 2
+
       graph_dict = collections.OrderedDict([
-        ('traces', graph.BuildTracesDict()),
+        ('traces', traces),
         ('units', str(graph.units)),
       ])
 
diff --git a/tools/perf/testdata/artificial_graph-summary.dat b/tools/perf/testdata/artificial_graph-summary.dat
index 7ef13d59..fac0d13 100644
--- a/tools/perf/testdata/artificial_graph-summary.dat
+++ b/tools/perf/testdata/artificial_graph-summary.dat
@@ -1 +1 @@
-{"traces": {"trace_with_one_sample": ["177.0", "0.0"], "trace_with_one_sample_comma": ["177.0", "0.0"], "trace_with_three_samples": ["140.0", "43.2049379894"], "trace_with_three_samples_comma": ["140.0", "43.2049379894"]}, "units": "you-nits"}
+{"units": "you-nits", "traces": {"trace_with_three_samples": ["140.0", "43.2049379894"], "trace_with_multiple_samples": ["250.0", "111.803398875"], "trace_with_one_sample_comma": ["177.0", "0.0"], "trace_with_one_sample": ["177.0", "0.0"], "trace_with_three_samples_comma": ["140.0", "43.2049379894"]}}
\ No newline at end of file
diff --git a/tools/perf/testdata/graphing_processor.log b/tools/perf/testdata/graphing_processor.log
index 868c725fc..a71b2a27 100644
--- a/tools/perf/testdata/graphing_processor.log
+++ b/tools/perf/testdata/graphing_processor.log
@@ -47,3 +47,10 @@
 RESULT artificial_graph: trace_with_one_sample_comma= [177.0,] you-nits
 RESULT artificial_graph: trace_with_three_samples= [100.0,120.0,200.0] you-nits
 RESULT artificial_graph: trace_with_three_samples_comma= [100.0,120.0,200.0,] you-nits
+
+# Artificial log output to test multiple results with the same name
+
+RESULT artificial_graph: trace_with_multiple_samples= 100 you-nits
+RESULT artificial_graph: trace_with_multiple_samples= 200 you-nits
+RESULT artificial_graph: trace_with_multiple_samples= 300 you-nits
+RESULT artificial_graph: trace_with_multiple_samples= 400 you-nits
diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
index c15283ae..30320dc0 100644
--- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
@@ -773,7 +773,7 @@
      * Return the current window token, or null.
      */
     @CalledByNative
-    private IBinder getWindowToken() {
+    protected IBinder getWindowToken() {
         Activity activity = activityFromContext(mContextRef.get());
         if (activity == null) return null;
         Window window = activity.getWindow();
diff --git a/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java b/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
index 5af31fc6..6d7fda3 100644
--- a/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
+++ b/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
@@ -39,6 +39,7 @@
     private static final String V8_SNAPSHOT_DATA_FILENAME = "snapshot_blob.bin";
     private static final String FALLBACK_LOCALE = "en-US";
     private static final String COMPRESSED_LOCALES_DIR = "locales";
+    private static final String COMPRESSED_LOCALES_FALLBACK_DIR = "fallback-locales";
     private static final int BUFFER_SIZE = 16 * 1024;
 
     private class ExtractTask extends AsyncTask<Void> {
@@ -173,6 +174,10 @@
         //
         //   Where <lang> is an Android-specific ISO-639-1 language identifier.
         //
+        // * With the exception of the fallback (English) pak files which are stored
+        //   in the base module under:
+        //       assets/locales-fallback/<locale>.pak
+        //
         //   Moreover, when the bundle uses APK splits, there is no guarantee that the split
         //   corresponding to the current device locale is installed yet, but the one matching
         //   uiLanguage should be there, since the value is determined by loading a resource string
@@ -193,6 +198,10 @@
                            assetManager, COMPRESSED_LOCALES_DIR, activeLocales.get(0) + ".pak")) {
             // This is a regular APK, and all pak files are available.
             localesSrcDir = COMPRESSED_LOCALES_DIR;
+        } else if (assetPathHasFile(assetManager, COMPRESSED_LOCALES_FALLBACK_DIR,
+                           activeLocales.get(0) + ".pak")) {
+            // This is a fallback language pak file.
+            localesSrcDir = COMPRESSED_LOCALES_FALLBACK_DIR;
         } else {
             // This is an app bundle, but the split containing the pak files for the current UI
             // locale is *not* installed yet. This should never happen in theory, and there is
diff --git a/ui/aura/local/window_port_local.cc b/ui/aura/local/window_port_local.cc
index 417285a..e690a42 100644
--- a/ui/aura/local/window_port_local.cc
+++ b/ui/aura/local/window_port_local.cc
@@ -192,6 +192,12 @@
   UpdateLocalSurfaceId();
 }
 
+void WindowPortLocal::InvalidateLocalSurfaceId() {
+  if (!parent_local_surface_id_allocator_)
+    return;
+  parent_local_surface_id_allocator_->Invalidate();
+}
+
 viz::ScopedSurfaceIdAllocator WindowPortLocal::GetSurfaceIdAllocator(
     base::OnceCallback<void()> allocation_task) {
   return viz::ScopedSurfaceIdAllocator(
diff --git a/ui/aura/local/window_port_local.h b/ui/aura/local/window_port_local.h
index 5ee3101..d0744d6b 100644
--- a/ui/aura/local/window_port_local.h
+++ b/ui/aura/local/window_port_local.h
@@ -51,6 +51,7 @@
   void AllocateLocalSurfaceId() override;
   viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator(
       base::OnceCallback<void()> allocation_task) override;
+  void InvalidateLocalSurfaceId() override;
   void UpdateLocalSurfaceIdFromEmbeddedClient(
       const viz::LocalSurfaceIdAllocation&
           embedded_client_local_surface_id_allocation) override;
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 60a58a11..d26cdabf 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -519,6 +519,10 @@
                                        std::move(allocation_task));
 }
 
+void WindowPortMus::InvalidateLocalSurfaceId() {
+  parent_local_surface_id_allocator_.Invalidate();
+}
+
 void WindowPortMus::UpdateLocalSurfaceIdFromEmbeddedClient(
     const viz::LocalSurfaceIdAllocation&
         embedded_client_local_surface_id_allocation) {
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h
index 9569eb6..19a24cf 100644
--- a/ui/aura/mus/window_port_mus.h
+++ b/ui/aura/mus/window_port_mus.h
@@ -297,6 +297,7 @@
   viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator(
       base::OnceCallback<void()> allocation_task) override;
   const viz::LocalSurfaceIdAllocation& GetLocalSurfaceIdAllocation() override;
+  void InvalidateLocalSurfaceId() override;
   void OnEventTargetingPolicyChanged() override;
   bool ShouldRestackTransientChildren() override;
   void RegisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) override;
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 286f3fb..f2894704 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -222,6 +222,11 @@
   return valid_wait;
 }
 
+WindowMus* WindowTreeClient::GetWindowByServerId(ws::Id id) {
+  IdToWindowMap::const_iterator it = windows_.find(id);
+  return it != windows_.end() ? it->second : nullptr;
+}
+
 void WindowTreeClient::SetCanFocus(Window* window, bool can_focus) {
   DCHECK(tree_);
   DCHECK(window);
@@ -374,11 +379,6 @@
   }
 }
 
-WindowMus* WindowTreeClient::GetWindowByServerId(ws::Id id) {
-  IdToWindowMap::const_iterator it = windows_.find(id);
-  return it != windows_.end() ? it->second : nullptr;
-}
-
 bool WindowTreeClient::IsWindowKnown(aura::Window* window) {
   WindowMus* window_mus = WindowMus::Get(window);
   // NOTE: this function explicitly checks for a null WindowMus as it may be
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index a7b8245..7f47dcea 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -128,6 +128,8 @@
 
   const base::Optional<uint32_t>& id() const { return id_; }
 
+  WindowMus* GetWindowByServerId(ws::Id id);
+
   void SetCanFocus(Window* window, bool can_focus);
   void SetCanAcceptDrops(WindowMus* window, bool can_accept_drops);
   void SetEventTargetingPolicy(WindowMus* window,
@@ -242,8 +244,6 @@
 
   void RegisterWindowMus(WindowMus* window);
 
-  WindowMus* GetWindowByServerId(ws::Id id);
-
   bool IsWindowKnown(aura::Window* window);
 
   // Returns the oldest InFlightChange that matches |change|.
diff --git a/ui/aura/test/env_test_helper.h b/ui/aura/test/env_test_helper.h
index e583cfed9..0dde0c2 100644
--- a/ui/aura/test/env_test_helper.h
+++ b/ui/aura/test/env_test_helper.h
@@ -57,6 +57,7 @@
     env_->mode_ = mode;
     if (mode == Env::Mode::MUS)
       env_->EnableMusOSExchangeDataProvider();
+    env_->in_mus_shutdown_ = false;
     return old_mode;
   }
 
diff --git a/ui/aura/test/mus/change_completion_waiter.cc b/ui/aura/test/mus/change_completion_waiter.cc
index 95c5b09..5741c6f3 100644
--- a/ui/aura/test/mus/change_completion_waiter.cc
+++ b/ui/aura/test/mus/change_completion_waiter.cc
@@ -113,7 +113,20 @@
 void WaitForAllChangesToComplete(WindowTreeClient* client) {
   if (Env::GetInstance()->mode() == Env::Mode::LOCAL)
     return;
-  AllChangesCompletedWaiter(client ? client : GetWindowTreeClient()).Wait();
+  if (!client)
+    client = GetWindowTreeClient();
+
+  // Wait for any local changes.
+  AllChangesCompletedWaiter(client).Wait();
+
+  // Do this to flush any incoming calls from the server.
+  WindowTreeClientTestApi(client).FlushForTesting();
+
+  // And wait for any outgoing calls.
+  AllChangesCompletedWaiter(client).Wait();
+
+  // In theory we could keep doing the above until there are no more outgoing
+  // requests.
 }
 
 }  // namespace test
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index df706aa5..03a1cb5 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -1141,6 +1141,10 @@
   return port_->GetLocalSurfaceIdAllocation();
 }
 
+void Window::InvalidateLocalSurfaceId() {
+  port_->InvalidateLocalSurfaceId();
+}
+
 void Window::UpdateLocalSurfaceIdFromEmbeddedClient(
     const base::Optional<viz::LocalSurfaceIdAllocation>&
         embedded_client_local_surface_id_allocation) {
diff --git a/ui/aura/window.h b/ui/aura/window.h
index e8ca96b..f0cca07 100644
--- a/ui/aura/window.h
+++ b/ui/aura/window.h
@@ -419,6 +419,10 @@
   // Returns the current viz::LocalSurfaceIdAllocation.
   const viz::LocalSurfaceIdAllocation& GetLocalSurfaceIdAllocation() const;
 
+  // Marks the current viz::LocalSurfaceId as invalid. AllocateLocalSurfaceId
+  // must be called before submitting new CompositorFrames.
+  void InvalidateLocalSurfaceId();
+
   // Sets the current viz::LocalSurfaceId, in cases where the embedded client
   // has allocated one. Also sets child sequence number component of the
   // viz::LocalSurfaceId allocator.
diff --git a/ui/aura/window_port.h b/ui/aura/window_port.h
index 00fc1840..4c4073a 100644
--- a/ui/aura/window_port.h
+++ b/ui/aura/window_port.h
@@ -115,6 +115,10 @@
   virtual viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator(
       base::OnceCallback<void()> allocation_task) = 0;
 
+  // Marks the current viz::LocalSurfaceId as invalid. AllocateLocalSurfaceId
+  // must be called before submitting new CompositorFrames.
+  virtual void InvalidateLocalSurfaceId() = 0;
+
   virtual void UpdateLocalSurfaceIdFromEmbeddedClient(
       const viz::LocalSurfaceIdAllocation&
           embedded_client_local_surface_id_allocation) = 0;
diff --git a/ui/aura/window_port_for_shutdown.cc b/ui/aura/window_port_for_shutdown.cc
index cbb6698..5ff4b04 100644
--- a/ui/aura/window_port_for_shutdown.cc
+++ b/ui/aura/window_port_for_shutdown.cc
@@ -58,6 +58,7 @@
 }
 
 void WindowPortForShutdown::AllocateLocalSurfaceId() {}
+void WindowPortForShutdown::InvalidateLocalSurfaceId() {}
 void WindowPortForShutdown::UpdateLocalSurfaceIdFromEmbeddedClient(
     const viz::LocalSurfaceIdAllocation&
         embedded_client_local_surface_id_allocation) {}
diff --git a/ui/aura/window_port_for_shutdown.h b/ui/aura/window_port_for_shutdown.h
index 6f9d357..4d7bd08 100644
--- a/ui/aura/window_port_for_shutdown.h
+++ b/ui/aura/window_port_for_shutdown.h
@@ -43,6 +43,7 @@
   void AllocateLocalSurfaceId() override;
   viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator(
       base::OnceCallback<void()> allocation_task) override;
+  void InvalidateLocalSurfaceId() override;
   void UpdateLocalSurfaceIdFromEmbeddedClient(
       const viz::LocalSurfaceIdAllocation&
           embedded_client_local_surface_id_allocation) override;
diff --git a/ui/gfx/image/image_unittest.cc b/ui/gfx/image/image_unittest.cc
index a38cfd9b..c25b09ac 100644
--- a/ui/gfx/image/image_unittest.cc
+++ b/ui/gfx/image/image_unittest.cc
@@ -249,15 +249,7 @@
 #endif
 }
 
-// TODO(crbug.com/153782): disable this test as it fails on iOS retina devices.
-#if defined(OS_IOS)
-#define MAYBE_MultiResolutionPNGToPlatform \
-    DISABLED_MultiResolutionPNGToPlatform
-#else
-#define MAYBE_MultiResolutionPNGToPlatform \
-    MultiResolutionPNGToPlatform
-#endif
-TEST_F(ImageTest, MAYBE_MultiResolutionPNGToPlatform) {
+TEST_F(ImageTest, MultiResolutionPNGToPlatform) {
   const int kSize1x = 25;
   const int kSize2x = 50;
 
@@ -307,15 +299,7 @@
 
 // The platform types use the platform provided encoding/decoding of PNGs. Make
 // sure these work with the Skia Encode/Decode.
-// TODO(crbug.com/153782): disable this test as it fails on iOS retina devices.
-#if defined(OS_IOS)
-#define MAYBE_PNGEncodeFromSkiaDecodeToPlatform \
-    DISABLED_PNGEncodeFromSkiaDecodeToPlatform
-#else
-#define MAYBE_PNGEncodeFromSkiaDecodeToPlatform \
-    PNGEncodeFromSkiaDecodeToPlatform
-#endif
-TEST_F(ImageTest, MAYBE_PNGEncodeFromSkiaDecodeToPlatform) {
+TEST_F(ImageTest, PNGEncodeFromSkiaDecodeToPlatform) {
   // Force the conversion sequence skia to png to platform_type.
   gfx::Image from_bitmap = gfx::Image::CreateFrom1xBitmap(
       gt::CreateBitmap(25, 25));
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index e020a79e..f157e37 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -149,6 +149,47 @@
                             KEYBOARD_CONTROL_MAX);
 }
 
+class InputMethodKeyboardController : public ui::InputMethodKeyboardController {
+ public:
+  explicit InputMethodKeyboardController(
+      KeyboardController* keyboard_controller)
+      : keyboard_controller_(keyboard_controller) {}
+
+  ~InputMethodKeyboardController() override = default;
+
+  // ui::InputMethodKeyboardController
+  bool DisplayVirtualKeyboard() override {
+    // Calling |ShowKeyboardInternal| may move the keyboard to another display.
+    if (keyboard_controller_->IsKeyboardEnableRequested() &&
+        !keyboard_controller_->keyboard_locked()) {
+      keyboard_controller_->ShowKeyboard(false /* locked */);
+      return true;
+    }
+    return false;
+  }
+
+  void DismissVirtualKeyboard() override {
+    keyboard_controller_->HideKeyboardByUser();
+  }
+
+  void AddObserver(
+      ui::InputMethodKeyboardControllerObserver* observer) override {
+    // TODO: Implement.
+  }
+
+  void RemoveObserver(
+      ui::InputMethodKeyboardControllerObserver* observer) override {
+    // TODO: Implement.
+  }
+
+  bool IsKeyboardVisible() override {
+    return keyboard_controller_->IsKeyboardVisible();
+  }
+
+ private:
+  KeyboardController* keyboard_controller_;
+};
+
 }  // namespace
 
 // Observer for both keyboard show and hide animations. It should be owned by
@@ -178,7 +219,9 @@
 };
 
 KeyboardController::KeyboardController()
-    : ime_observer_(this),
+    : input_method_keyboard_controller_(
+          std::make_unique<InputMethodKeyboardController>(this)),
+      ime_observer_(this),
       weak_factory_report_lingering_state_(this),
       weak_factory_will_hide_(this) {
   DCHECK_EQ(g_keyboard_controller, nullptr);
@@ -233,6 +276,10 @@
   if (parent_container_)
     DeactivateKeyboard();
 
+  aura::Window* keyboard_window = GetKeyboardWindow();
+  if (keyboard_window)
+    keyboard_window->RemoveObserver(this);
+
   // Return to the INITIAL state to ensure that transitions entering a state
   // is equal to transitions leaving the state.
   if (state_ != KeyboardControllerState::INITIAL)
@@ -577,10 +624,6 @@
       base::TimeDelta::FromMilliseconds(kHideKeyboardDelayMs));
 }
 
-void KeyboardController::DismissVirtualKeyboard() {
-  HideKeyboardByUser();
-}
-
 // private
 void KeyboardController::HideAnimationFinished() {
   if (state_ == KeyboardControllerState::HIDDEN) {
@@ -1041,26 +1084,6 @@
   container_behavior_->SetDraggableArea(rect);
 }
 
-// InputMethodKeyboardController overrides:
-
-bool KeyboardController::DisplayVirtualKeyboard() {
-  // Calling |ShowKeyboardInternal| may move the keyboard to another display.
-  if (keyboard::IsKeyboardEnabled() && !keyboard_locked_) {
-    ShowKeyboardInternal(display::Display());
-    return true;
-  }
-  return false;
-}
-void KeyboardController::AddObserver(
-    ui::InputMethodKeyboardControllerObserver* observer) {
-  // TODO: Implement me
-}
-
-void KeyboardController::RemoveObserver(
-    ui::InputMethodKeyboardControllerObserver* observer) {
-  // TODO: Implement me
-}
-
 bool KeyboardController::IsKeyboardVisible() {
   if (state_ == KeyboardControllerState::SHOWN) {
     DCHECK(IsEnabled());
@@ -1088,8 +1111,10 @@
   ime_observer_.RemoveAll();
   ime_observer_.Add(ime);
 
-  // TODO(https://crbug.com/845780): Investigate whether this does anything.
-  OnTextInputStateChanged(ime->GetTextInputClient());
+  // Note: We used to call OnTextInputStateChanged(ime->GetTextInputClient())
+  // here, but that can trigger HideKeyboardImplicitlyBySystem() from a call to
+  // ShowKeyboard() when using mojo APIs in Chrome (SingleProcessMash) if
+  // ime->GetTextInputClient() isn't focused.
 }
 
 void KeyboardController::EnsureCaretInWorkArea(
diff --git a/ui/keyboard/keyboard_controller.h b/ui/keyboard/keyboard_controller.h
index 99b1d6f..1214f328 100644
--- a/ui/keyboard/keyboard_controller.h
+++ b/ui/keyboard/keyboard_controller.h
@@ -69,11 +69,9 @@
 
 // Provides control of the virtual keyboard, including enabling/disabling the
 // keyboard and controlling its visibility.
-class KEYBOARD_EXPORT KeyboardController
-    : public ui::InputMethodObserver,
-      public aura::WindowObserver,
-      public ui::InputMethodKeyboardController,
-      public ContainerBehavior::Delegate {
+class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver,
+                                           public aura::WindowObserver,
+                                           public ContainerBehavior::Delegate {
  public:
   KeyboardController();
   ~KeyboardController() override;
@@ -240,14 +238,11 @@
   // Sets floating keyboard draggable rect.
   void SetDraggableArea(const gfx::Rect& rect);
 
-  // InputMethodKeyboardController overrides:
-  bool DisplayVirtualKeyboard() override;
-  void DismissVirtualKeyboard() override;
-  void AddObserver(
-      ui::InputMethodKeyboardControllerObserver* observer) override;
-  void RemoveObserver(
-      ui::InputMethodKeyboardControllerObserver* observer) override;
-  bool IsKeyboardVisible() override;
+  bool IsKeyboardVisible();
+
+  ui::InputMethodKeyboardController* input_method_keyboard_controller() {
+    return input_method_keyboard_controller_.get();
+  }
 
   bool keyboard_locked() const { return keyboard_locked_; }
   void set_keyboard_locked(bool lock) { keyboard_locked_ = lock; }
@@ -385,6 +380,8 @@
   void MarkKeyboardLoadFinished();
 
   std::unique_ptr<KeyboardUI> ui_;
+  std::unique_ptr<ui::InputMethodKeyboardController>
+      input_method_keyboard_controller_;
   KeyboardLayoutDelegate* layout_delegate_ = nullptr;
   ScopedObserver<ui::InputMethod, ui::InputMethodObserver> ime_observer_;
 
diff --git a/ui/message_center/views/notification_view.cc b/ui/message_center/views/notification_view.cc
index 7094e2d..2c88dd4 100644
--- a/ui/message_center/views/notification_view.cc
+++ b/ui/message_center/views/notification_view.cc
@@ -146,6 +146,8 @@
 // NotificationView ////////////////////////////////////////////////////////////
 
 void NotificationView::CreateOrUpdateViews(const Notification& notification) {
+  top_view_count_ = 0;
+
   CreateOrUpdateTitleView(notification);
   CreateOrUpdateMessageView(notification);
   CreateOrUpdateProgressBarView(notification);
@@ -370,10 +372,12 @@
     title_view_->SetLineLimit(kMaxTitleLines);
     title_view_->SetColor(kRegularTextColor);
     title_view_->SetBorder(MakeTextBorder(padding, 3, 0));
-    top_view_->AddChildView(title_view_);
+    top_view_->AddChildViewAt(title_view_, top_view_count_);
   } else {
     title_view_->SetText(title);
   }
+
+  top_view_count_++;
 }
 
 void NotificationView::CreateOrUpdateMessageView(
@@ -396,12 +400,13 @@
     message_view_->SetLineHeight(kMessageLineHeight);
     message_view_->SetColor(kRegularTextColor);
     message_view_->SetBorder(MakeTextBorder(padding, 4, 0));
-    top_view_->AddChildView(message_view_);
+    top_view_->AddChildViewAt(message_view_, top_view_count_);
   } else {
     message_view_->SetText(text);
   }
 
   message_view_->SetVisible(notification.items().empty());
+  top_view_count_++;
 }
 
 base::string16 NotificationView::FormatContextMessage(
@@ -440,10 +445,12 @@
     context_message_view_->SetLineHeight(kMessageLineHeight);
     context_message_view_->SetColor(kDimTextColor);
     context_message_view_->SetBorder(MakeTextBorder(padding, 4, 0));
-    top_view_->AddChildView(context_message_view_);
+    top_view_->AddChildViewAt(context_message_view_, top_view_count_);
   } else {
     context_message_view_->SetText(message);
   }
+
+  top_view_count_++;
 }
 
 void NotificationView::CreateOrUpdateProgressBarView(
@@ -461,11 +468,12 @@
     progress_bar_view_ = new views::ProgressBar();
     progress_bar_view_->SetBorder(MakeProgressBarBorder(
         kProgressBarTopPadding, kProgressBarBottomPadding));
-    top_view_->AddChildView(progress_bar_view_);
+    top_view_->AddChildViewAt(progress_bar_view_, top_view_count_);
   }
 
   progress_bar_view_->SetValue(notification.progress() / 100.0);
   progress_bar_view_->SetVisible(notification.items().empty());
+  top_view_count_++;
 }
 
 void NotificationView::CreateOrUpdateListItemViews(
@@ -485,7 +493,7 @@
     NotificationItemView* item_view = new NotificationItemView(items[i]);
     item_view->SetBorder(MakeTextBorder(padding, i ? 0 : 4, 0));
     item_views_.push_back(item_view);
-    top_view_->AddChildView(item_view);
+    top_view_->AddChildViewAt(item_view, top_view_count_++);
   }
 }
 
diff --git a/ui/message_center/views/notification_view.h b/ui/message_center/views/notification_view.h
index b0869eba..76db06f 100644
--- a/ui/message_center/views/notification_view.h
+++ b/ui/message_center/views/notification_view.h
@@ -62,6 +62,7 @@
   FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, TestImageSizing);
   FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, UpdateButtonsStateTest);
   FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, UpdateButtonCountTest);
+  FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, UpdateViewsOrderingTest);
 
   friend class NotificationViewTest;
 
@@ -108,6 +109,10 @@
   std::unique_ptr<views::ImageView> small_image_view_;
   NotificationControlButtonsView* control_buttons_view_;
 
+  // Counter for view layouting, which is used during the CreateOrUpdate*
+  // phases to keep track of the view ordering. See crbug.com/901045
+  int top_view_count_;
+
   DISALLOW_COPY_AND_ASSIGN(NotificationView);
 };
 
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index 04911581..d7fed2f 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -477,6 +477,8 @@
 // ////////////////////////////////////////////////////////////
 
 void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) {
+  left_content_count_ = 0;
+
   CreateOrUpdateContextTitleView(notification);
   CreateOrUpdateTitleView(notification);
   CreateOrUpdateMessageView(notification);
@@ -801,10 +803,12 @@
     title_view_->SetFontList(font_list);
     title_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     title_view_->SetEnabledColor(kRegularTextColorMD);
-    left_content_->AddChildView(title_view_);
+    left_content_->AddChildViewAt(title_view_, left_content_count_);
   } else {
     title_view_->SetText(title);
   }
+
+  left_content_count_++;
 }
 
 void NotificationViewMD::CreateOrUpdateMessageView(
@@ -827,12 +831,13 @@
     message_view_->SetLineLimit(kMaxLinesForMessageView);
     message_view_->SetColor(kDimTextColorMD);
 
-    left_content_->AddChildView(message_view_);
+    left_content_->AddChildViewAt(message_view_, left_content_count_);
   } else {
     message_view_->SetText(text);
   }
 
   message_view_->SetVisible(notification.items().empty());
+  left_content_count_++;
 }
 
 void NotificationViewMD::CreateOrUpdateCompactTitleMessageView(
@@ -846,12 +851,14 @@
   }
   if (!compact_title_message_view_) {
     compact_title_message_view_ = new CompactTitleMessageView();
-    left_content_->AddChildView(compact_title_message_view_);
+    left_content_->AddChildViewAt(compact_title_message_view_,
+                                  left_content_count_);
   }
 
   compact_title_message_view_->set_title(notification.title());
   compact_title_message_view_->set_message(notification.message());
   left_content_->InvalidateLayout();
+  left_content_count_++;
 }
 
 void NotificationViewMD::CreateOrUpdateProgressBarView(
@@ -871,7 +878,7 @@
                                                 /* allow_round_corner */ false);
     progress_bar_view_->SetBorder(
         views::CreateEmptyBorder(kProgressBarTopPadding, 0, 0, 0));
-    left_content_->AddChildView(progress_bar_view_);
+    left_content_->AddChildViewAt(progress_bar_view_, left_content_count_);
   }
 
   progress_bar_view_->SetValue(notification.progress() / 100.0);
@@ -881,6 +888,8 @@
     header_row_->SetProgress(notification.progress());
   else
     header_row_->ClearProgress();
+
+  left_content_count_++;
 }
 
 void NotificationViewMD::CreateOrUpdateProgressStatusView(
@@ -902,10 +911,11 @@
     status_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     status_view_->SetEnabledColor(kDimTextColorMD);
     status_view_->SetBorder(views::CreateEmptyBorder(kStatusTextPadding));
-    left_content_->AddChildView(status_view_);
+    left_content_->AddChildViewAt(status_view_, left_content_count_);
   }
 
   status_view_->SetText(notification.progress_status());
+  left_content_count_++;
 }
 
 void NotificationViewMD::CreateOrUpdateListItemViews(
@@ -920,7 +930,7 @@
        ++i) {
     std::unique_ptr<views::View> item_view = CreateItemView(items[i]);
     item_views_.push_back(item_view.get());
-    left_content_->AddChildView(item_view.release());
+    left_content_->AddChildViewAt(item_view.release(), left_content_count_++);
   }
 
   list_items_count_ = items.size();
diff --git a/ui/message_center/views/notification_view_md.h b/ui/message_center/views/notification_view_md.h
index d897066..0fa2810 100644
--- a/ui/message_center/views/notification_view_md.h
+++ b/ui/message_center/views/notification_view_md.h
@@ -222,6 +222,7 @@
   FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UseImageAsIcon);
   FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, NotificationWithoutIcon);
   FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, InlineSettings);
+  FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateViewsOrderingTest);
 
   friend class NotificationViewMDTest;
 
@@ -292,6 +293,10 @@
   views::View* action_buttons_row_ = nullptr;
   NotificationInputContainerMD* inline_reply_ = nullptr;
 
+  // Counter for view layouting, which is used during the CreateOrUpdate*
+  // phases to keep track of the view ordering. See crbug.com/901045
+  int left_content_count_;
+
   // Views for inline settings.
   views::RadioButton* block_all_button_ = nullptr;
   views::RadioButton* dont_block_button_ = nullptr;
diff --git a/ui/message_center/views/notification_view_md_unittest.cc b/ui/message_center/views/notification_view_md_unittest.cc
index 3bc20c67..f0319d9 100644
--- a/ui/message_center/views/notification_view_md_unittest.cc
+++ b/ui/message_center/views/notification_view_md_unittest.cc
@@ -344,6 +344,36 @@
   EXPECT_EQ(nullptr, notification_view()->icon_view_);
 }
 
+TEST_F(NotificationViewMDTest, UpdateViewsOrderingTest) {
+  EXPECT_NE(nullptr, notification_view()->title_view_);
+  EXPECT_NE(nullptr, notification_view()->message_view_);
+  EXPECT_EQ(0, notification_view()->left_content_->GetIndexOf(
+                   notification_view()->title_view_));
+  EXPECT_EQ(1, notification_view()->left_content_->GetIndexOf(
+                   notification_view()->message_view_));
+
+  std::unique_ptr<Notification> notification = CreateSimpleNotification();
+  notification->set_title(base::string16());
+
+  notification_view()->CreateOrUpdateViews(*notification);
+
+  EXPECT_EQ(nullptr, notification_view()->title_view_);
+  EXPECT_NE(nullptr, notification_view()->message_view_);
+  EXPECT_EQ(0, notification_view()->left_content_->GetIndexOf(
+                   notification_view()->message_view_));
+
+  notification->set_title(base::UTF8ToUTF16("title"));
+
+  notification_view()->CreateOrUpdateViews(*notification);
+
+  EXPECT_NE(nullptr, notification_view()->title_view_);
+  EXPECT_NE(nullptr, notification_view()->message_view_);
+  EXPECT_EQ(0, notification_view()->left_content_->GetIndexOf(
+                   notification_view()->title_view_));
+  EXPECT_EQ(1, notification_view()->left_content_->GetIndexOf(
+                   notification_view()->message_view_));
+}
+
 TEST_F(NotificationViewMDTest, TestIconSizing) {
   // TODO(tetsui): Remove duplicated integer literal in CreateOrUpdateIconView.
   const int kNotificationIconSize = 36;
diff --git a/ui/message_center/views/notification_view_unittest.cc b/ui/message_center/views/notification_view_unittest.cc
index 7b96f171..f10c742 100644
--- a/ui/message_center/views/notification_view_unittest.cc
+++ b/ui/message_center/views/notification_view_unittest.cc
@@ -28,6 +28,7 @@
 #include "ui/message_center/public/cpp/message_center_constants.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_types.h"
+#include "ui/message_center/views/bounded_label.h"
 #include "ui/message_center/views/message_view_factory.h"
 #include "ui/message_center/views/notification_button.h"
 #include "ui/message_center/views/notification_control_buttons_view.h"
@@ -284,6 +285,35 @@
   EXPECT_TRUE(NULL != notification_view()->icon_view_);
 }
 
+TEST_F(NotificationViewTest, UpdateViewsOrderingTest) {
+  EXPECT_NE(nullptr, notification_view()->title_view_);
+  EXPECT_NE(nullptr, notification_view()->message_view_);
+  EXPECT_EQ(0, notification_view()->top_view_->GetIndexOf(
+                   notification_view()->title_view_));
+  EXPECT_EQ(1, notification_view()->top_view_->GetIndexOf(
+                   notification_view()->message_view_));
+
+  notification()->set_title(base::string16());
+
+  notification_view()->CreateOrUpdateViews(*notification());
+
+  EXPECT_EQ(nullptr, notification_view()->title_view_);
+  EXPECT_NE(nullptr, notification_view()->message_view_);
+  EXPECT_EQ(0, notification_view()->top_view_->GetIndexOf(
+                   notification_view()->message_view_));
+
+  notification()->set_title(base::UTF8ToUTF16("title"));
+
+  notification_view()->CreateOrUpdateViews(*notification());
+
+  EXPECT_NE(nullptr, notification_view()->title_view_);
+  EXPECT_NE(nullptr, notification_view()->message_view_);
+  EXPECT_EQ(0, notification_view()->top_view_->GetIndexOf(
+                   notification_view()->title_view_));
+  EXPECT_EQ(1, notification_view()->top_view_->GetIndexOf(
+                   notification_view()->message_view_));
+}
+
 TEST_F(NotificationViewTest, CreateOrUpdateTestSettingsButton) {
   data()->settings_button_handler = SettingsButtonHandler::INLINE;
   Notification notification(
diff --git a/ui/ozone/platform/scenic/scenic_window.cc b/ui/ozone/platform/scenic/scenic_window.cc
index 840f441..e0c38fe 100644
--- a/ui/ozone/platform/scenic/scenic_window.cc
+++ b/ui/ozone/platform/scenic/scenic_window.cc
@@ -191,14 +191,16 @@
     screen->OnWindowBoundsChanged(window_id_, size_rect);
 
   // Set node shape to rectangle that matches size of the view.
-  scenic::Rectangle rect(&scenic_session_, size_dips_.width(),
-                         size_dips_.height());
+  scenic::Rectangle rect(&scenic_session_, 1.f, 1.f);
   shape_node_.SetShape(rect);
 
   // Translate the node by half of the view dimensions to put it in the center
   // of the view.
-  shape_node_.SetTranslation(size_dips_.width() / 2.0,
-                             size_dips_.height() / 2.0, 0.f);
+  node_.SetTranslation(size_dips_.width() / 2.0, size_dips_.height() / 2.0,
+                       0.f);
+
+  // Scale the node so that surface rect can always be 1x1.
+  node_.SetScale(size_dips_.width(), size_dips_.height(), 1.f);
 
   // This is necessary when using vulkan because ImagePipes are presented
   // separately and we need to make sure our sizes change is committed.
diff --git a/ui/views/controls/native/native_view_host.cc b/ui/views/controls/native/native_view_host.cc
index bef81e99..270f22b0 100644
--- a/ui/views/controls/native/native_view_host.cc
+++ b/ui/views/controls/native/native_view_host.cc
@@ -206,6 +206,11 @@
   return native_wrapper_->GetCursor(event.x(), event.y());
 }
 
+void NativeViewHost::SetVisible(bool visible) {
+  native_wrapper_->SetVisible(visible);
+  View::SetVisible(visible);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // NativeViewHost, private:
 
diff --git a/ui/views/controls/native/native_view_host.h b/ui/views/controls/native/native_view_host.h
index ec0b8e9..5c47f29 100644
--- a/ui/views/controls/native/native_view_host.h
+++ b/ui/views/controls/native/native_view_host.h
@@ -98,6 +98,7 @@
   void OnFocus() override;
   gfx::NativeViewAccessible GetNativeViewAccessible() override;
   gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override;
+  void SetVisible(bool visible) override;
 
  protected:
   bool GetNeedsNotificationWhenVisibleBoundsChange() const override;
diff --git a/ui/views/controls/native/native_view_host_aura.cc b/ui/views/controls/native/native_view_host_aura.cc
index 7efa51a3..dabae67 100644
--- a/ui/views/controls/native/native_view_host_aura.cc
+++ b/ui/views/controls/native/native_view_host_aura.cc
@@ -233,6 +233,13 @@
   return gfx::kNullCursor;
 }
 
+void NativeViewHostAura::SetVisible(bool visible) {
+  if (!visible)
+    host_->native_view()->Hide();
+  else
+    host_->native_view()->Show();
+}
+
 void NativeViewHostAura::OnWindowBoundsChanged(
     aura::Window* window,
     const gfx::Rect& old_bounds,
diff --git a/ui/views/controls/native/native_view_host_aura.h b/ui/views/controls/native/native_view_host_aura.h
index 0adf8fb3..1e247145 100644
--- a/ui/views/controls/native/native_view_host_aura.h
+++ b/ui/views/controls/native/native_view_host_aura.h
@@ -44,6 +44,7 @@
   gfx::NativeView GetNativeViewContainer() const override;
   gfx::NativeViewAccessible GetNativeViewAccessible() override;
   gfx::NativeCursor GetCursor(int x, int y) override;
+  void SetVisible(bool visible) override;
 
  private:
   friend class NativeViewHostAuraTest;
diff --git a/ui/views/controls/native/native_view_host_mac.h b/ui/views/controls/native/native_view_host_mac.h
index f46e575e..08a0ce3 100644
--- a/ui/views/controls/native/native_view_host_mac.h
+++ b/ui/views/controls/native/native_view_host_mac.h
@@ -51,6 +51,7 @@
   gfx::NativeView GetNativeViewContainer() const override;
   gfx::NativeViewAccessible GetNativeViewAccessible() override;
   gfx::NativeCursor GetCursor(int x, int y) override;
+  void SetVisible(bool visible) override;
 
  private:
   // Return the BridgedNativeWidgetHostImpl for this hosted view.
diff --git a/ui/views/controls/native/native_view_host_mac.mm b/ui/views/controls/native/native_view_host_mac.mm
index 07d7165..7ce36315 100644
--- a/ui/views/controls/native/native_view_host_mac.mm
+++ b/ui/views/controls/native/native_view_host_mac.mm
@@ -261,6 +261,10 @@
   return gfx::kNullCursor;
 }
 
+void NativeViewHostMac::SetVisible(bool visible) {
+  [native_view_ setHidden:!visible];
+}
+
 // static
 NativeViewHostWrapper* NativeViewHostWrapper::CreateWrapper(
     NativeViewHost* host) {
diff --git a/ui/views/controls/native/native_view_host_wrapper.h b/ui/views/controls/native/native_view_host_wrapper.h
index 07cf9f1..acd9b2f 100644
--- a/ui/views/controls/native/native_view_host_wrapper.h
+++ b/ui/views/controls/native/native_view_host_wrapper.h
@@ -90,6 +90,11 @@
   // in the native view.
   virtual gfx::NativeCursor GetCursor(int x, int y) = 0;
 
+  // Sets the visibility of the gfx::NativeView. This differs from
+  // {Show,Hide}Widget because it doesn't affect the placement, size,
+  // or clipping of the view.
+  virtual void SetVisible(bool visible) = 0;
+
   // Creates a platform-specific instance of an object implementing this
   // interface.
   static NativeViewHostWrapper* CreateWrapper(NativeViewHost* host);
diff --git a/ui/views/controls/webview/webview.cc b/ui/views/controls/webview/webview.cc
index 2a4cc7e..15e497f 100644
--- a/ui/views/controls/webview/webview.cc
+++ b/ui/views/controls/webview/webview.cc
@@ -129,6 +129,10 @@
 
   if (crashed_overlay_view_) {
     RemoveChildView(crashed_overlay_view_);
+    // Show the hosted web contents view iff the crashed
+    // overlay is NOT showing, to ensure hit testing is
+    // correct on Mac. See https://crbug.com/896508
+    holder_->SetVisible(true);
     if (!crashed_overlay_view_->owned_by_client())
       delete crashed_overlay_view_;
   }
@@ -136,6 +140,7 @@
   crashed_overlay_view_ = crashed_overlay_view;
   if (crashed_overlay_view_) {
     AddChildView(crashed_overlay_view_);
+    holder_->SetVisible(false);
     crashed_overlay_view_->SetBoundsRect(gfx::Rect(size()));
   }
 
diff --git a/webrunner/browser/frame_impl_browsertest.cc b/webrunner/browser/frame_impl_browsertest.cc
index f3e2759..63df269 100644
--- a/webrunner/browser/frame_impl_browsertest.cc
+++ b/webrunner/browser/frame_impl_browsertest.cc
@@ -892,7 +892,8 @@
   // MessagePortImpl to self-destruct and tear down its FIDL channel.
   {
     base::RunLoop run_loop;
-    message_port.set_error_handler([&run_loop]() { run_loop.Quit(); });
+    message_port.set_error_handler(
+        [&run_loop](zx_status_t status) { run_loop.Quit(); });
     controller->LoadUrl(url::kAboutBlankURL, nullptr);
     CheckRunWithTimeout(&run_loop);
   }