diff --git a/AUTHORS b/AUTHORS
index f0fbef00..ea13f21 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -998,6 +998,7 @@
 IBM Inc. <*@*.ibm.com>
 IBM Inc. <*@ibm.com>
 Igalia S.L. <*@igalia.com>
+Imagination Technologies Limited <*@imagination.corp-partner.google.com>
 Impossible Dreams Network <*@impossibledreams.net>
 LG Electronics, Inc. <*@lge.com>
 Loongson Technology Corporation Limited. <*@loongson.cn>
diff --git a/DEPS b/DEPS
index 3152e38..84dd2aa 100644
--- a/DEPS
+++ b/DEPS
@@ -116,7 +116,7 @@
   # 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': '00b29398901e556bf9f2854aa602c20f5e11deba',
+  'skia_revision': '5d3deb7301b1f41764b9959d44c7d6d6312d74a4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # 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': '3d86e89bce7abacff20285c0d6e8525d9ef0ce60',
+  'angle_revision': '1bf18ce9e2caca7067b4439373f4fb04a5cd7d21',
   # 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.
@@ -136,7 +136,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '155f802a6be3720a0176bc1674b5af3b5b6e31f9',
+  'swiftshader_revision': '4169b31090dbe7d48b3e053c571c12414089bf28',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # 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': '6ba6af1e543b60cced283cf7490d81415fe934d1',
+  'catapult_revision': '0273c5e3c23ad25ac6626cbadd00695bf0d93b3f',
   # 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': 'f2cc71e5cbeaac27a5c7e6d339a4fbe2e3acf362',
+  'spv_tools_revision': '398f37a2e0e6f285ee3a7da9f55b502723f0fcfd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -670,7 +670,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'b54e73475dd0ac786c006f3e027f30e6f782189e',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'ef4d21b915d88b2f79ca7ddda4593f25a4323282',
       'condition': 'checkout_linux',
   },
 
@@ -1009,7 +1009,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '5b640e3d331bc668378c8f2e05190edee01d3737',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'cc7964918f7c9fd3ba0eba8fb7a097ad05a36ec1',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1161,7 +1161,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '0d55c887e92b645f6effe753528323ab2ffd94c2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'a8f54617c08dbeb2b4e39d4ee20a59a6c3d821c9',
+    Var('webrtc_git') + '/src.git' + '@' + '34fc346a0c77e21bffb6a6631e265a707e6e4ae2',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1192,7 +1192,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@04f1c7dac0fd1d48262db3ae8badafebcbe43b15',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6eef2ff50b8c294d3a0fa6dd457546d6e60f08a5',
     'condition': 'checkout_src_internal',
   },
 
@@ -1708,7 +1708,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core',
-              'version': 'version:1.3.0-cr0',
+              'version': 'version:1.3.5-cr0',
           },
       ],
       'condition': 'checkout_android',
diff --git a/WATCHLISTS b/WATCHLISTS
index 37ad3a1..cd89c83 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1886,7 +1886,7 @@
                         'ortuno+watch@chromium.org',
                         'odejesush+watch@chromium.org'],
     'blink_canvas': [ 'dongseong.hwang@intel.com',
-                      'fserb@chromium.org'],
+                      'fserb+watch@chromium.org'],
     'blink_client_hints': ['yoav@yoav.ws'],
     'blink_clipboard': ['dcheng@chromium.org'],
     'blink_common': ['jbroman+watch@chromium.org',
@@ -1973,7 +1973,7 @@
                                 'drott+blinkwatch@chromium.org',
                                 'dschulze@chromium.org',
                                 'fmalita+watch@chromium.org',
-                                'fserb@chromium.org',
+                                'fserb+watch@chromium.org',
                                 'pdr+graphicswatchlist@chromium.org',
                                 'schenney@chromium.org'],
     'blink_preloadScanner': ['yoav@yoav.ws'],
diff --git a/android_webview/browser/aw_contents.h b/android_webview/browser/aw_contents.h
index a621d32..5abbceb 100644
--- a/android_webview/browser/aw_contents.h
+++ b/android_webview/browser/aw_contents.h
@@ -27,7 +27,6 @@
 #include "content/public/browser/web_contents_observer.h"
 
 class SkBitmap;
-class TabContents;
 
 namespace autofill {
 class AutofillProvider;
@@ -39,7 +38,6 @@
 
 namespace android_webview {
 
-class AwContentsContainer;
 class AwContentsClientBridge;
 class AwGLFunctor;
 class AwPdfExporter;
@@ -47,9 +45,6 @@
 class PermissionRequestHandler;
 
 // Native side of java-class of same name.
-// Provides the ownership of and access to browser components required for
-// WebView functionality; analogous to chrome's TabContents, but with a
-// level of indirection provided by the AwContentsContainer abstraction.
 //
 // Object lifetime:
 // For most purposes the java and native objects can be considered to have
diff --git a/android_webview/browser/net/aw_url_request_context_getter.cc b/android_webview/browser/net/aw_url_request_context_getter.cc
index 12b5ec0..6a46c25 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter.cc
@@ -21,6 +21,7 @@
 #include "base/base_paths_android.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
@@ -65,6 +66,7 @@
 #include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_intercepting_job_factory.h"
 #include "net/url_request/url_request_interceptor.h"
+#include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/network_switches.h"
 
 using base::FilePath;
@@ -306,8 +308,13 @@
                                       std::move(channel_id_service));
 
   net::URLRequestContextBuilder::HttpCacheParams cache_params;
+  // Note: we create this as IN_MEMORY when the network service is enabled
+  // only as a temporary measure, to avoid accessing the same HTTP cache from
+  // two spots in the code.
   cache_params.type =
-      net::URLRequestContextBuilder::HttpCacheParams::DISK_SIMPLE;
+      base::FeatureList::IsEnabled(network::features::kNetworkService)
+          ? net::URLRequestContextBuilder::HttpCacheParams::IN_MEMORY
+          : net::URLRequestContextBuilder::HttpCacheParams::DISK_SIMPLE;
   cache_params.max_size = 20 * 1024 * 1024;  // 20M
   cache_params.path = cache_path_;
   builder.EnableHttpCache(cache_params);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index a198d2674..54a93a7e1 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1070,10 +1070,8 @@
     "wm/gestures/overview_gesture_handler.h",
     "wm/immersive_context_ash.cc",
     "wm/immersive_context_ash.h",
-    "wm/immersive_gesture_handler_classic.cc",
-    "wm/immersive_gesture_handler_classic.h",
-    "wm/immersive_handler_factory_ash.cc",
-    "wm/immersive_handler_factory_ash.h",
+    "wm/immersive_gesture_drag_handler.cc",
+    "wm/immersive_gesture_drag_handler.h",
     "wm/lock_action_handler_layout_manager.cc",
     "wm/lock_action_handler_layout_manager.h",
     "wm/lock_layout_manager.cc",
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 261ffb0d..39c06c6c 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -97,7 +97,7 @@
 constexpr int kAppListHomeLaucherGesturesThreshold = 32;
 
 // Quality of the shield background blur.
-constexpr float kAppListBlurQuality = 0.25f;
+constexpr float kAppListBlurQuality = 0.33f;
 
 // Set animation durations to 0 for testing.
 static bool short_animations_for_testing;
diff --git a/ash/dbus/OWNERS b/ash/dbus/OWNERS
new file mode 100644
index 0000000..f3164c9c
--- /dev/null
+++ b/ash/dbus/OWNERS
@@ -0,0 +1,3 @@
+file://chromeos/dbus/OWNERS
+per-file org.chromium.*.conf=set noparent
+per-file org.chromium.*.conf=file://chromeos/SECURITY_OWNERS
diff --git a/ash/display/cros_display_config.cc b/ash/display/cros_display_config.cc
index 849879f..52fd730 100644
--- a/ash/display/cros_display_config.cc
+++ b/ash/display/cros_display_config.cc
@@ -618,8 +618,10 @@
     primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
   } else {
     displays = display_manager->software_mirroring_display_list();
-    primary_id =
-        display_manager->GetPrimaryMirroringDisplayForUnifiedDesktop()->id();
+    primary_id = Shell::Get()
+                     ->display_configuration_controller()
+                     ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                     .id();
   }
 
   for (const display::Display& display : displays)
diff --git a/ash/display/display_configuration_controller.cc b/ash/display/display_configuration_controller.cc
index 223e6b0..2cb90d56b 100644
--- a/ash/display/display_configuration_controller.cc
+++ b/ash/display/display_configuration_controller.cc
@@ -7,7 +7,10 @@
 #include "ash/display/display_animator.h"
 #include "ash/display/display_util.h"
 #include "ash/display/window_tree_host_manager.h"
+#include "ash/public/cpp/shelf_types.h"
+#include "ash/root_window_controller.h"
 #include "ash/rotator/screen_rotation_animator.h"
+#include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/time/time.h"
@@ -19,6 +22,8 @@
 
 DEFINE_UI_CLASS_PROPERTY_TYPE(ash::ScreenRotationAnimator*);
 
+namespace ash {
+
 namespace {
 
 // Specifies how long the display change should have been disabled
@@ -39,9 +44,26 @@
 
 bool g_disable_animator_for_test = false;
 
-}  // namespace
+display::DisplayPositionInUnifiedMatrix GetUnifiedModeShelfCellPosition() {
+  const ShelfAlignment alignment =
+      Shell::GetPrimaryRootWindowController()->shelf()->alignment();
+  switch (alignment) {
+    case SHELF_ALIGNMENT_BOTTOM:
+    case SHELF_ALIGNMENT_BOTTOM_LOCKED:
+      return display::DisplayPositionInUnifiedMatrix::kBottomLeft;
 
-namespace ash {
+    case SHELF_ALIGNMENT_LEFT:
+      return display::DisplayPositionInUnifiedMatrix::kTopLeft;
+
+    case SHELF_ALIGNMENT_RIGHT:
+      return display::DisplayPositionInUnifiedMatrix::kTopRight;
+  }
+
+  NOTREACHED();
+  return display::DisplayPositionInUnifiedMatrix::kBottomLeft;
+}
+
+}  // namespace
 
 class DisplayConfigurationController::DisplayChangeLimiter {
  public:
@@ -177,6 +199,15 @@
   }
 }
 
+display::Display
+DisplayConfigurationController::GetPrimaryMirroringDisplayForUnifiedDesktop()
+    const {
+  DCHECK(display_manager_->IsInUnifiedMode());
+
+  return display_manager_->GetMirroringDisplayForUnifiedDesktop(
+      GetUnifiedModeShelfCellPosition());
+}
+
 void DisplayConfigurationController::OnDisplayConfigurationChanged() {
   // TODO(oshima): Stop all animations.
   SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs);
diff --git a/ash/display/display_configuration_controller.h b/ash/display/display_configuration_controller.h
index 0a1e999..cfd39495 100644
--- a/ash/display/display_configuration_controller.h
+++ b/ash/display/display_configuration_controller.h
@@ -75,6 +75,16 @@
   // called within the throttle time.
   void SetPrimaryDisplayId(int64_t display_id, bool throttle);
 
+  // In Unified Desktop mode, we consider the display in which the shelf will be
+  // placed to be the "primary mirroring display". Note that this is different
+  // from the "normal" primary display, which is just the single unified display
+  // in unified mode. This display will be:
+  //   - The bottom-left in the matrix if the shelf alignment is "bottom",
+  //   - The top-left in the matrix if the shelf alignment is "left",
+  //   - The top-right in the matrix if the shelf alignment is "right".
+  // This should only be called when Unified Desktop mode is active.
+  display::Display GetPrimaryMirroringDisplayForUnifiedDesktop() const;
+
   // WindowTreeHostManager::Observer
   void OnDisplayConfigurationChanged() override;
 
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 276afb4..b30f9b1a 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -15,7 +15,9 @@
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
+#include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/test/ash_test_base.h"
@@ -2718,10 +2720,12 @@
     display_manager()->SetUnifiedDesktopMatrix(matrix);
     // 500 + 400 * 200 / 300 ~= 766.
     EXPECT_EQ(gfx::Size(400, 766), screen->GetPrimaryDisplay().size());
-    // Display in top-left cell is considered primary.
-    EXPECT_EQ(
-        list[0],
-        display_manager()->GetPrimaryMirroringDisplayForUnifiedDesktop()->id());
+    // Default shelf alignment is bottom. Display in bottom-left cell is
+    // considered the primary mirroring display.
+    EXPECT_EQ(list[1], Shell::Get()
+                           ->display_configuration_controller()
+                           ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                           .id());
 
     // Validate display rows and max heights.
     EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
@@ -2747,10 +2751,11 @@
     // 200 + 300 * 500 / 400 ~= 574 (Note that we actually scale the max unified
     // bounds).
     EXPECT_EQ(gfx::Size(300, 574), screen->GetPrimaryDisplay().size());
-    // Display in top-left cell is considered primary.
-    EXPECT_EQ(
-        list[1],
-        display_manager()->GetPrimaryMirroringDisplayForUnifiedDesktop()->id());
+    // Display in bottom-left cell is considered primary.
+    EXPECT_EQ(list[0], Shell::Get()
+                           ->display_configuration_controller()
+                           ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                           .id());
 
     // Validate display rows and max heights.
     EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
@@ -2781,10 +2786,11 @@
                                                            list[1]);
     display_manager()->OnNativeDisplaysChanged(display_info_list);
     EXPECT_EQ(gfx::Size(300, 574), screen->GetPrimaryDisplay().size());
-    // Display in top-left cell is considered primary.
-    EXPECT_EQ(
-        list[0],
-        display_manager()->GetPrimaryMirroringDisplayForUnifiedDesktop()->id());
+    // Display in bottom-left cell is considered primary.
+    EXPECT_EQ(list[1], Shell::Get()
+                           ->display_configuration_controller()
+                           ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                           .id());
 
     // Validate display rows and max heights.
     EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
@@ -2820,10 +2826,11 @@
     matrix[2].emplace_back(list[2]);
     display_manager()->SetUnifiedDesktopMatrix(matrix);
     EXPECT_EQ(gfx::Size(500, 1225), screen->GetPrimaryDisplay().size());
-    // Display in top-left cell is considered primary.
-    EXPECT_EQ(
-        list[0],
-        display_manager()->GetPrimaryMirroringDisplayForUnifiedDesktop()->id());
+    // Display in bottom-left cell is considered primary.
+    EXPECT_EQ(list[2], Shell::Get()
+                           ->display_configuration_controller()
+                           ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                           .id());
 
     // Validate display rows and max heights.
     EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
@@ -2851,10 +2858,11 @@
     matrix[2].emplace_back(list[2]);
     display_manager()->SetUnifiedDesktopMatrix(matrix);
     EXPECT_EQ(gfx::Size(400, 980), screen->GetPrimaryDisplay().size());
-    // Display in top-left cell is considered primary.
-    EXPECT_EQ(
-        list[1],
-        display_manager()->GetPrimaryMirroringDisplayForUnifiedDesktop()->id());
+    // Display in bottom-left cell is considered primary.
+    EXPECT_EQ(list[2], Shell::Get()
+                           ->display_configuration_controller()
+                           ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                           .id());
 
     // Validate display rows and max heights.
     EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
@@ -2892,10 +2900,16 @@
   matrix[1].emplace_back(list[3]);
   display_manager()->SetUnifiedDesktopMatrix(matrix);
   EXPECT_EQ(gfx::Size(739, 933), screen->GetPrimaryDisplay().size());
-  // Display in top-left cell is considered primary.
-  EXPECT_EQ(
-      list[0],
-      display_manager()->GetPrimaryMirroringDisplayForUnifiedDesktop()->id());
+
+  // Default shelf alignment is bottom.
+  Shelf* shelf = Shell::GetPrimaryRootWindowController()->shelf();
+  EXPECT_EQ(shelf->alignment(), SHELF_ALIGNMENT_BOTTOM);
+
+  // Display in bottom-left cell is considered the primary mirroring display.
+  EXPECT_EQ(list[2], Shell::Get()
+                         ->display_configuration_controller()
+                         ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                         .id());
 
   // Validate display rows and max heights.
   EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
@@ -2909,6 +2923,22 @@
   EXPECT_EQ(300, display_manager()->GetUnifiedDesktopRowMaxHeight(0));
   EXPECT_EQ(633, display_manager()->GetUnifiedDesktopRowMaxHeight(1));
   EXPECT_FALSE(OverlappingMirroringDisplaysExist());
+
+  // Change the shelf alignment to left, and expect that the primary mirroring
+  // display in the top-left display in the matrix.
+  shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
+  EXPECT_EQ(list[0], Shell::Get()
+                         ->display_configuration_controller()
+                         ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                         .id());
+
+  // Change the shelf alignment to right, and expect that the primary mirroring
+  // display in the top-right display in the matrix.
+  shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT);
+  EXPECT_EQ(list[1], Shell::Get()
+                         ->display_configuration_controller()
+                         ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                         .id());
 }
 
 TEST_F(DisplayManagerTest, UnifiedDesktopGridLayout3x2) {
@@ -2935,10 +2965,16 @@
   matrix[2].emplace_back(list[5]);
   display_manager()->SetUnifiedDesktopMatrix(matrix);
   EXPECT_EQ(gfx::Size(739, 1108), screen->GetPrimaryDisplay().size());
-  // Display in top-left cell is considered primary.
-  EXPECT_EQ(
-      list[0],
-      display_manager()->GetPrimaryMirroringDisplayForUnifiedDesktop()->id());
+
+  // Default shelf alignment is bottom.
+  Shelf* shelf = Shell::GetPrimaryRootWindowController()->shelf();
+  EXPECT_EQ(shelf->alignment(), SHELF_ALIGNMENT_BOTTOM);
+
+  // Display in bottom-left cell is considered the primary mirroring display.
+  EXPECT_EQ(list[4], Shell::Get()
+                         ->display_configuration_controller()
+                         ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                         .id());
 
   // Validate display rows and max heights.
   EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
@@ -2957,6 +2993,22 @@
   EXPECT_EQ(633, display_manager()->GetUnifiedDesktopRowMaxHeight(1));
   EXPECT_EQ(175, display_manager()->GetUnifiedDesktopRowMaxHeight(2));
   EXPECT_FALSE(OverlappingMirroringDisplaysExist());
+
+  // Change the shelf alignment to left, and expect that the primary mirroring
+  // display in the top-left display in the matrix.
+  shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
+  EXPECT_EQ(list[0], Shell::Get()
+                         ->display_configuration_controller()
+                         ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                         .id());
+
+  // Change the shelf alignment to right, and expect that the primary mirroring
+  // display in the top-right display in the matrix.
+  shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT);
+  EXPECT_EQ(list[1], Shell::Get()
+                         ->display_configuration_controller()
+                         ->GetPrimaryMirroringDisplayForUnifiedDesktop()
+                         .id());
 }
 
 TEST_F(DisplayManagerTest, DockMode) {
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 43f7a56..ed0012b 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -418,6 +418,9 @@
 }
 
 void LockContentsView::FocusNextUser() {
+  if (users_.empty())
+    return;
+
   if (login_views_utils::HasFocusInAnyChildView(primary_big_view_)) {
     if (opt_secondary_big_view_) {
       SwapActiveAuthBetweenPrimaryAndSecondary(false /*is_primary*/);
@@ -454,6 +457,9 @@
 }
 
 void LockContentsView::FocusPreviousUser() {
+  if (users_.empty())
+    return;
+
   if (login_views_utils::HasFocusInAnyChildView(primary_big_view_)) {
     if (users_list_) {
       users_list_->user_view_at(users_list_->user_count() - 1)->RequestFocus();
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 8df9d71..d35d155a 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -2138,4 +2138,17 @@
   Shell::Get()->power_button_controller()->OnTabletModeEnded();
 }
 
+TEST_F(LockContentsViewUnitTest, RightAndLeftAcceleratorsWithNoUser) {
+  // Show lock screen but do *not* initialize any users.
+  auto* lock = new LockContentsView(
+      mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock,
+      data_dispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(data_dispatcher()));
+  SetWidget(CreateWidgetWithContent(lock));
+
+  // Nothing to validate except that there is no crash.
+  lock->AcceleratorPressed(ui::Accelerator(ui::VKEY_RIGHT, 0));
+  lock->AcceleratorPressed(ui::Accelerator(ui::VKEY_LEFT, 0));
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 776dcb7..e20b7c06 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -79,9 +79,6 @@
     "immersive/immersive_fullscreen_controller.cc",
     "immersive/immersive_fullscreen_controller.h",
     "immersive/immersive_fullscreen_controller_delegate.h",
-    "immersive/immersive_gesture_handler.h",
-    "immersive/immersive_handler_factory.cc",
-    "immersive/immersive_handler_factory.h",
     "immersive/immersive_revealed_lock.cc",
     "immersive/immersive_revealed_lock.h",
     "login_constants.h",
diff --git a/ash/public/cpp/immersive/immersive_context.h b/ash/public/cpp/immersive/immersive_context.h
index eaab141..00dea65 100644
--- a/ash/public/cpp/immersive/immersive_context.h
+++ b/ash/public/cpp/immersive/immersive_context.h
@@ -39,9 +39,6 @@
 
   // Returns true if any window has capture.
   virtual bool DoesAnyWindowHaveCapture() = 0;
-
-  // See Shell::IsMouseEventsEnabled() for details.
-  virtual bool IsMouseEventsEnabled() = 0;
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/immersive/immersive_fullscreen_controller.cc b/ash/public/cpp/immersive/immersive_fullscreen_controller.cc
index 13d73662..ff40a3b 100644
--- a/ash/public/cpp/immersive/immersive_fullscreen_controller.cc
+++ b/ash/public/cpp/immersive/immersive_fullscreen_controller.cc
@@ -9,13 +9,13 @@
 #include "ash/public/cpp/immersive/immersive_context.h"
 #include "ash/public/cpp/immersive/immersive_focus_watcher.h"
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
-#include "ash/public/cpp/immersive/immersive_gesture_handler.h"
-#include "ash/public/cpp/immersive/immersive_handler_factory.h"
 #include "ash/public/cpp/window_properties.h"
 #include "base/metrics/histogram_macros.h"
+#include "ui/aura/client/cursor_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_targeter.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/events/base_event_utils.h"
@@ -126,45 +126,28 @@
                                    animate_reveal);
 }
 
-////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// ui::EventObserver overrides:
 
-void ImmersiveFullscreenController::OnMouseEvent(
-    const ui::MouseEvent& event,
-    const gfx::Point& location_in_screen,
-    views::Widget* target) {
-  if (!enabled_)
+void ImmersiveFullscreenController::OnEvent(const ui::Event& event) {
+  if (!event.IsLocatedEvent())
     return;
 
-  if (event.type() != ui::ET_MOUSE_MOVED &&
-      event.type() != ui::ET_MOUSE_PRESSED &&
-      event.type() != ui::ET_MOUSE_RELEASED &&
-      event.type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
-    return;
-  }
-
-  // Mouse hover can initiate revealing the top-of-window views while |widget_|
-  // is inactive.
-  if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
-    top_edge_hover_timer_.Stop();
-    UpdateLocatedEventRevealedLock(&event, location_in_screen);
-  } else if (event.type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
-    // Trigger reveal if the cursor pauses at the top of the screen for a while.
-    UpdateTopEdgeHoverTimer(event, location_in_screen, target);
+  const ui::LocatedEvent* located_event = event.AsLocatedEvent();
+  aura::Window* target = static_cast<aura::Window*>(event.target());
+  if (event.IsMouseEvent()) {
+    HandleMouseEvent(*event.AsMouseEvent(), located_event->root_location(),
+                     views::Widget::GetTopLevelWidgetForNativeView(target));
+  } else if (event.IsTouchEvent()) {
+    HandleTouchEvent(*event.AsTouchEvent(), located_event->root_location());
   }
 }
 
-void ImmersiveFullscreenController::OnTouchEvent(
-    const ui::TouchEvent& event,
-    const gfx::Point& location_in_screen) {
-  if (!enabled_ || event.type() != ui::ET_TOUCH_PRESSED)
-    return;
+////////////////////////////////////////////////////////////////////////////////
+// ui::EventHandler overrides:
 
-  // Touch should not initiate revealing the top-of-window views while |widget_|
-  // is inactive.
-  if (!widget_->IsActive())
-    return;
-
-  UpdateLocatedEventRevealedLock(&event, location_in_screen);
+void ImmersiveFullscreenController::OnEvent(ui::Event* event) {
+  ui::EventHandler::OnEvent(event);
 }
 
 void ImmersiveFullscreenController::OnGestureEvent(ui::GestureEvent* event) {
@@ -202,20 +185,6 @@
   }
 }
 
-void ImmersiveFullscreenController::OnEvent(const ui::Event& event) {
-  if (!event.IsLocatedEvent())
-    return;
-
-  const ui::LocatedEvent* located_event = event.AsLocatedEvent();
-  aura::Window* target = static_cast<aura::Window*>(event.target());
-  if (event.IsMouseEvent()) {
-    OnMouseEvent(*event.AsMouseEvent(), located_event->root_location(),
-                 views::Widget::GetTopLevelWidgetForNativeView(target));
-  } else if (event.IsTouchEvent()) {
-    OnTouchEvent(*event.AsTouchEvent(), located_event->root_location());
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // aura::WindowObserver overrides:
 
@@ -326,24 +295,66 @@
     return;
   event_observers_enabled_ = enable;
 
-  aura::Env* env = widget_->GetNativeWindow()->env();
+  aura::Window* window = widget_->GetNativeWindow();
+  // For Mash, handle events sent to the Mus client's root window.
+  if (features::IsUsingWindowService())
+    window = window->GetRootWindow();
+  aura::Env* env = window->env();
   if (enable) {
     immersive_focus_watcher_ = std::make_unique<ImmersiveFocusWatcher>(this);
-    immersive_gesture_handler_ =
-        ImmersiveHandlerFactory::Get()->CreateGestureHandler(this);
     std::set<ui::EventType> types = {
         ui::ET_MOUSE_MOVED, ui::ET_MOUSE_PRESSED,         ui::ET_MOUSE_RELEASED,
         ui::ET_MOUSEWHEEL,  ui::ET_MOUSE_CAPTURE_CHANGED, ui::ET_TOUCH_PRESSED};
     env->AddEventObserver(this, env, types);
+    window->AddPreTargetHandler(this);
   } else {
+    window->RemovePreTargetHandler(this);
     env->RemoveEventObserver(this);
-    immersive_gesture_handler_.reset();
     immersive_focus_watcher_.reset();
 
     animation_.Stop();
   }
 }
 
+void ImmersiveFullscreenController::HandleMouseEvent(
+    const ui::MouseEvent& event,
+    const gfx::Point& location_in_screen,
+    views::Widget* target) {
+  if (!enabled_)
+    return;
+
+  if (event.type() != ui::ET_MOUSE_MOVED &&
+      event.type() != ui::ET_MOUSE_PRESSED &&
+      event.type() != ui::ET_MOUSE_RELEASED &&
+      event.type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
+    return;
+  }
+
+  // Mouse hover can initiate revealing the top-of-window views while |widget_|
+  // is inactive.
+  if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
+    top_edge_hover_timer_.Stop();
+    UpdateLocatedEventRevealedLock(&event, location_in_screen);
+  } else if (event.type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
+    // Trigger reveal if the cursor pauses at the top of the screen for a while.
+    UpdateTopEdgeHoverTimer(event, location_in_screen, target);
+  }
+}
+
+void ImmersiveFullscreenController::HandleTouchEvent(
+    const ui::TouchEvent& event,
+    const gfx::Point& location_in_screen) {
+  if (!enabled_ || event.type() != ui::ET_TOUCH_PRESSED)
+    return;
+
+  // Touch should not initiate revealing the top-of-window views while |widget_|
+  // is inactive.
+  if (!widget_->IsActive())
+    return;
+
+  UpdateLocatedEventRevealedLock(&event, location_in_screen);
+}
+
 void ImmersiveFullscreenController::UpdateTopEdgeHoverTimer(
     const ui::MouseEvent& event,
     const gfx::Point& location_in_screen,
@@ -404,7 +415,7 @@
 
   // Neither the mouse nor touch can initiate a reveal when the top-of-window
   // views are sliding closed or are closed with the following exceptions:
-  // - Hovering at y = 0 which is handled in OnMouseEvent().
+  // - Hovering at y = 0 which is handled in HandleMouseEvent().
   // - Doing a SWIPE_OPEN edge gesture which is handled in OnGestureEvent().
   if (reveal_state_ == CLOSED || reveal_state_ == SLIDING_CLOSED)
     return;
@@ -453,9 +464,11 @@
 }
 
 void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock() {
-  if (!immersive_context_->IsMouseEventsEnabled()) {
+  if (!aura::client::GetCursorClient(
+           widget_->GetNativeWindow()->GetRootWindow())
+           ->IsMouseEventsEnabled()) {
     // If mouse events are disabled, the user's last interaction was probably
-    // via touch. Do no do further processing in this case as there is no easy
+    // via touch. Do no further processing in this case as there is no easy
     // way of retrieving the position of the user's last touch.
     return;
   }
diff --git a/ash/public/cpp/immersive/immersive_fullscreen_controller.h b/ash/public/cpp/immersive/immersive_fullscreen_controller.h
index b604907..312f97f 100644
--- a/ash/public/cpp/immersive/immersive_fullscreen_controller.h
+++ b/ash/public/cpp/immersive/immersive_fullscreen_controller.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/timer/timer.h"
 #include "ui/aura/window_observer.h"
+#include "ui/events/event_handler.h"
 #include "ui/events/event_observer.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/gfx/animation/slide_animation.h"
@@ -45,12 +46,12 @@
 class ImmersiveFocusWatcher;
 class ImmersiveFullscreenControllerDelegate;
 class ImmersiveFullscreenControllerTestApi;
-class ImmersiveGestureHandler;
 
 class ASH_PUBLIC_EXPORT ImmersiveFullscreenController
     : public aura::WindowObserver,
       public gfx::AnimationDelegate,
       public ui::EventObserver,
+      public ui::EventHandler,
       public views::ViewObserver,
       public ImmersiveRevealedLock::Delegate {
  public:
@@ -109,18 +110,13 @@
   views::Widget* widget() { return widget_; }
   views::View* top_container() { return top_container_; }
 
-  // TODO(sky): move OnMouseEvent/OnTouchEvent to private section.
-  void OnMouseEvent(const ui::MouseEvent& event,
-                    const gfx::Point& location_in_screen,
-                    views::Widget* target);
-  void OnTouchEvent(const ui::TouchEvent& event,
-                    const gfx::Point& location_in_screen);
-  // Processes a GestureEvent. This may call SetHandled() on the supplied event.
-  void OnGestureEvent(ui::GestureEvent* event);
-
   // ui::EventObserver:
   void OnEvent(const ui::Event& event) override;
 
+  // ui::EventHandler:
+  void OnEvent(ui::Event* event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
+
   // aura::WindowObserver:
   void OnWindowPropertyChanged(aura::Window* window,
                                const void* key,
@@ -165,6 +161,13 @@
   // Enables or disables observers for mouse, touch, focus, and activation.
   void EnableEventObservers(bool enable);
 
+  // Called to handle EventObserver::OnEvent.
+  void HandleMouseEvent(const ui::MouseEvent& event,
+                        const gfx::Point& location_in_screen,
+                        views::Widget* target);
+  void HandleTouchEvent(const ui::TouchEvent& event,
+                        const gfx::Point& location_in_screen);
+
   // Updates |top_edge_hover_timer_| based on a mouse |event|. If the mouse is
   // hovered at the top of the screen the timer is started. If the mouse moves
   // away from the top edge, or moves too much in the x direction, the timer is
@@ -289,7 +292,6 @@
   bool animations_disabled_for_test_;
 
   std::unique_ptr<ImmersiveFocusWatcher> immersive_focus_watcher_;
-  std::unique_ptr<ImmersiveGestureHandler> immersive_gesture_handler_;
 
   // The window targeter that was in use before immersive fullscreen mode was
   // entered, if any. Will be re-installed on the window after leaving immersive
diff --git a/ash/public/cpp/immersive/immersive_gesture_handler.h b/ash/public/cpp/immersive/immersive_gesture_handler.h
deleted file mode 100644
index dc89054..0000000
--- a/ash/public/cpp/immersive/immersive_gesture_handler.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_GESTURE_HANDLER_H_
-#define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_GESTURE_HANDLER_H_
-
-#include "ash/public/cpp/ash_public_export.h"
-
-namespace ash {
-
-// ImmersiveGestureHandler is responsible for calling
-// ImmersiveFullscreenController::OnGestureEvent() to show/hide the title bar or
-// TabletAppModeWindowDragController::DragWindowFromTop() to drag the window
-// from the top if CanDragWindow is true when a gesture is received.
-class ASH_PUBLIC_EXPORT ImmersiveGestureHandler {
- public:
-  virtual ~ImmersiveGestureHandler() {}
-};
-
-}  // namespace ash
-
-#endif  // ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_GESTURE_HANDLER_H_
diff --git a/ash/public/cpp/immersive/immersive_handler_factory.cc b/ash/public/cpp/immersive/immersive_handler_factory.cc
deleted file mode 100644
index 6ee501db..0000000
--- a/ash/public/cpp/immersive/immersive_handler_factory.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/public/cpp/immersive/immersive_handler_factory.h"
-
-#include "base/logging.h"
-
-namespace ash {
-
-// static
-ImmersiveHandlerFactory* ImmersiveHandlerFactory::instance_ = nullptr;
-
-ImmersiveHandlerFactory::ImmersiveHandlerFactory() {
-  DCHECK(!instance_);
-  instance_ = this;
-}
-
-ImmersiveHandlerFactory::~ImmersiveHandlerFactory() {
-  DCHECK_EQ(instance_, this);
-  instance_ = nullptr;
-}
-
-}  // namespace ash
diff --git a/ash/public/cpp/immersive/immersive_handler_factory.h b/ash/public/cpp/immersive/immersive_handler_factory.h
deleted file mode 100644
index 418153e..0000000
--- a/ash/public/cpp/immersive/immersive_handler_factory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_HANDLER_FACTORY_H_
-#define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_HANDLER_FACTORY_H_
-
-#include <memory>
-
-#include "ash/public/cpp/ash_public_export.h"
-
-namespace ash {
-
-class ImmersiveFullscreenController;
-class ImmersiveGestureHandler;
-
-// Used by ImmersiveFullscreenController to create event handlers/watchers.
-class ASH_PUBLIC_EXPORT ImmersiveHandlerFactory {
- public:
-  static ImmersiveHandlerFactory* Get() { return instance_; }
-
-  virtual std::unique_ptr<ImmersiveGestureHandler> CreateGestureHandler(
-      ImmersiveFullscreenController* controller) = 0;
-
- protected:
-  ImmersiveHandlerFactory();
-  virtual ~ImmersiveHandlerFactory();
-
- private:
-  static ImmersiveHandlerFactory* instance_;
-};
-
-}  // namespace ash
-
-#endif  // ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_HANDLER_FACTORY_H_
diff --git a/ash/screen_util.cc b/ash/screen_util.cc
index 2b1d2a71..2507787c 100644
--- a/ash/screen_util.cc
+++ b/ash/screen_util.cc
@@ -4,6 +4,10 @@
 
 #include "ash/screen_util.h"
 
+#include "ash/display/display_configuration_controller.h"
+#include "ash/display/mirror_window_controller.h"
+#include "ash/display/window_tree_host_manager.h"
+#include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "base/logging.h"
@@ -50,31 +54,21 @@
   if (!Shell::Get()->display_manager()->IsInUnifiedMode())
     return window->GetRootWindow()->bounds();
 
-  const display::DisplayManager* display_manager =
-      Shell::Get()->display_manager();
-  // Calculate the unified height scale value.
-  const int unified_logical_height = window->GetRootWindow()->bounds().height();
-  const auto& unified_display_info = display_manager->GetDisplayInfo(
-      display::Screen::GetScreen()->GetPrimaryDisplay().id());
-  const int unified_physical_height =
-      unified_display_info.bounds_in_native().height();
-  const float unified_height_scale =
-      static_cast<float>(unified_logical_height) / unified_physical_height;
+  // In Unified Mode, the display that should contain the shelf depends on the
+  // current shelf alignment.
+  const display::Display shelf_display =
+      Shell::Get()
+          ->display_configuration_controller()
+          ->GetPrimaryMirroringDisplayForUnifiedDesktop();
+  DCHECK_NE(shelf_display.id(), display::kInvalidDisplayId);
+  gfx::RectF shelf_display_screen_bounds(shelf_display.bounds());
 
-  // In unified desktop mode, there is only one shelf in the primary mirroing
-  // display which exists in the first row in the top left cell.
-  const int row_index = 0;
-  const int row_physical_height =
-      display_manager->GetUnifiedDesktopRowMaxHeight(row_index);
-  const int row_logical_height = row_physical_height * unified_height_scale;
+  // Transform the bounds back to the unified host's coordinates.
+  auto inverse_unified_transform =
+      window->GetRootWindow()->GetHost()->GetInverseRootTransform();
+  inverse_unified_transform.TransformRect(&shelf_display_screen_bounds);
 
-  const display::Display* first_display =
-      display_manager->GetPrimaryMirroringDisplayForUnifiedDesktop();
-  DCHECK(first_display);
-  gfx::SizeF size(first_display->size());
-  const float scale = row_logical_height / size.height();
-  size.Scale(scale, scale);
-  return gfx::Rect(gfx::ToCeiledSize(size));
+  return gfx::ToEnclosingRect(shelf_display_screen_bounds);
 }
 
 gfx::Rect SnapBoundsToDisplayEdge(const gfx::Rect& bounds,
diff --git a/ash/screen_util_unittest.cc b/ash/screen_util_unittest.cc
index f155bd1..1cbc09b 100644
--- a/ash/screen_util_unittest.cc
+++ b/ash/screen_util_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "ash/screen_util.h"
 
+#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -158,15 +160,41 @@
   display::Screen* screen = display::Screen::GetScreen();
   EXPECT_EQ(gfx::Size(766, 1254), screen->GetPrimaryDisplay().size());
 
-  // Regardless of where the window is, the shelf is always in the top left
-  // display in the matrix.
-  EXPECT_EQ(gfx::Rect(0, 0, 499, 400),
+  Shelf* shelf = Shell::GetPrimaryRootWindowController()->shelf();
+  EXPECT_EQ(shelf->alignment(), SHELF_ALIGNMENT_BOTTOM);
+
+  // Regardless of where the window is, the shelf with a bottom alignment is
+  // always in the bottom left display in the matrix.
+  EXPECT_EQ(gfx::Rect(0, 1057, 593, 198),
             screen_util::GetDisplayBoundsWithShelf(window));
 
   // Move to the bottom right display.
   widget->SetBounds(gfx::Rect(620, 940, 100, 100));
+  EXPECT_EQ(gfx::Rect(0, 1057, 593, 198),
+            screen_util::GetDisplayBoundsWithShelf(window));
+
+  // Change the shelf alignment to left, and expect that it now resides in the
+  // top left display in the matrix.
+  shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
   EXPECT_EQ(gfx::Rect(0, 0, 499, 400),
             screen_util::GetDisplayBoundsWithShelf(window));
+
+  // Change the shelf alignment to right, and expect that it now resides in the
+  // top right display in the matrix.
+  shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT);
+  EXPECT_EQ(gfx::Rect(499, 0, 267, 400),
+            screen_util::GetDisplayBoundsWithShelf(window));
+
+  // Change alignment back to bottom and change the unified display zoom factor.
+  // Expect that the display with shelf bounds will take into account the zoom
+  // factor.
+  shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
+  display_manager()->UpdateZoomFactor(display::kUnifiedDisplayId, 3.f);
+  const display::Display unified_display =
+      display_manager()->GetDisplayForId(display::kUnifiedDisplayId);
+  EXPECT_FLOAT_EQ(unified_display.device_scale_factor(), 3.f);
+  EXPECT_EQ(gfx::Rect(0, 352, 198, 67),
+            screen_util::GetDisplayBoundsWithShelf(window));
 }
 
 TEST_F(ScreenUtilTest, SnapBoundsToDisplayEdge) {
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index f01d08e1..7d5e072 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -942,7 +942,8 @@
   gfx::Rect shelf_region = shelf_widget_->GetWindowBoundsInScreen();
   DCHECK(!display_.bounds().IsEmpty());
   shelf_region.Intersect(display_.bounds());
-  return shelf_region;
+  return screen_util::SnapBoundsToDisplayEdge(shelf_region,
+                                              shelf_widget_->GetNativeWindow());
 }
 
 gfx::Rect ShelfLayoutManager::GetAutoHideShowShelfRegionInScreen() const {
diff --git a/ash/shell.cc b/ash/shell.cc
index 9b35dfd..54d24ed1e 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -131,7 +131,6 @@
 #include "ash/wm/cursor_manager_chromeos.h"
 #include "ash/wm/event_client_impl.h"
 #include "ash/wm/immersive_context_ash.h"
-#include "ash/wm/immersive_handler_factory_ash.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/native_cursor_manager_ash.h"
@@ -1021,8 +1020,6 @@
 
   wallpaper_controller_ = std::make_unique<WallpaperController>();
 
-  immersive_handler_factory_ = std::make_unique<ImmersiveHandlerFactoryAsh>();
-
   window_positioner_ = std::make_unique<WindowPositioner>();
 
   native_cursor_manager_ = new NativeCursorManagerAsh;
diff --git a/ash/shell.h b/ash/shell.h
index cba263d..f9854c3 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -125,7 +125,6 @@
 class ImeController;
 class ImeFocusHandler;
 class ImmersiveContext;
-class ImmersiveHandlerFactoryAsh;
 class KeyAccessibilityEnabler;
 class KeyboardBrightnessControlDelegate;
 class AshKeyboardController;
@@ -875,8 +874,6 @@
   // For testing only: simulate that a modal window is open
   bool simulate_modal_window_open_for_test_ = false;
 
-  std::unique_ptr<ImmersiveHandlerFactoryAsh> immersive_handler_factory_;
-
   std::unique_ptr<MessageCenterController> message_center_controller_;
 
   base::ObserverList<ShellObserver>::Unchecked shell_observers_;
diff --git a/ash/wm/immersive_context_ash.cc b/ash/wm/immersive_context_ash.cc
index ea4ff00..d756b3f 100644
--- a/ash/wm/immersive_context_ash.cc
+++ b/ash/wm/immersive_context_ash.cc
@@ -12,7 +12,6 @@
 #include "base/logging.h"
 #include "ui/display/screen.h"
 #include "ui/views/widget/widget.h"
-#include "ui/wm/core/cursor_manager.h"
 
 namespace ash {
 
@@ -43,8 +42,4 @@
   return wm::GetCaptureWindow() != nullptr;
 }
 
-bool ImmersiveContextAsh::IsMouseEventsEnabled() {
-  return Shell::Get()->cursor_manager()->IsMouseEventsEnabled();
-}
-
 }  // namespace ash
diff --git a/ash/wm/immersive_context_ash.h b/ash/wm/immersive_context_ash.h
index cce453e..3684ccf 100644
--- a/ash/wm/immersive_context_ash.h
+++ b/ash/wm/immersive_context_ash.h
@@ -21,7 +21,6 @@
                                     bool entering) override;
   gfx::Rect GetDisplayBoundsInScreen(views::Widget* widget) override;
   bool DoesAnyWindowHaveCapture() override;
-  bool IsMouseEventsEnabled() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ImmersiveContextAsh);
diff --git a/ash/wm/immersive_gesture_handler_classic.cc b/ash/wm/immersive_gesture_drag_handler.cc
similarity index 76%
rename from ash/wm/immersive_gesture_handler_classic.cc
rename to ash/wm/immersive_gesture_drag_handler.cc
index 5e2f607..1ddabb3 100644
--- a/ash/wm/immersive_gesture_handler_classic.cc
+++ b/ash/wm/immersive_gesture_drag_handler.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 "ash/wm/immersive_gesture_handler_classic.h"
+#include "ash/wm/immersive_gesture_drag_handler.h"
 
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/ash_features.h"
@@ -46,14 +46,31 @@
   // There may be a bezel sensor off screen logically above
   // |hit_bounds_in_screen|. Handles the ET_GESTURE_SCROLL_BEGIN event
   // triggerd in the bezel area too.
-  return (location_in_screen.y() < hit_bounds_in_screen.y() &&
-          location_in_screen.x() >= hit_bounds_in_screen.x() &&
-          location_in_screen.x() < hit_bounds_in_screen.right());
+  return location_in_screen.y() < hit_bounds_in_screen.y() &&
+         location_in_screen.x() >= hit_bounds_in_screen.x() &&
+         location_in_screen.x() < hit_bounds_in_screen.right();
 }
 
 }  // namespace
 
-bool ImmersiveGestureHandlerClassic::CanDrag(ui::GestureEvent* event) {
+ImmersiveGestureDragHandler::ImmersiveGestureDragHandler(aura::Window* window)
+    : window_(window) {
+  Shell::Get()->AddPreTargetHandler(this);
+}
+
+ImmersiveGestureDragHandler::~ImmersiveGestureDragHandler() {
+  Shell::Get()->RemovePreTargetHandler(this);
+}
+
+void ImmersiveGestureDragHandler::OnGestureEvent(ui::GestureEvent* event) {
+  if (CanDrag(event)) {
+    DCHECK(tablet_mode_app_window_drag_controller_);
+    if (tablet_mode_app_window_drag_controller_->DragWindowFromTop(event))
+      event->SetHandled();
+  }
+}
+
+bool ImmersiveGestureDragHandler::CanDrag(ui::GestureEvent* event) {
   if (!base::FeatureList::IsEnabled(ash::features::kDragAppsInTabletMode))
     return false;
 
@@ -62,15 +79,12 @@
   if (!widget)
     return false;
 
-  aura::Window* window = widget->GetNativeWindow();
-  // Only process the event if its target has the same native window as
-  // |immersive_fullscreen_controller_->widget()|.
-  if (window != immersive_fullscreen_controller_->widget()->GetNativeWindow())
+  if (widget->GetNativeWindow() != window_)
     return false;
 
   // Maximized, fullscreened and snapped windows in tablet mode are allowed to
   // be dragged.
-  wm::WindowState* window_state = wm::GetWindowState(window);
+  wm::WindowState* window_state = wm::GetWindowState(window_);
   if (!window_state ||
       (!window_state->IsMaximized() && !window_state->IsFullscreen() &&
        !window_state->IsSnapped()) ||
@@ -83,7 +97,7 @@
   // Fullscreen browser windows are not draggable. Dragging from top should show
   // the frame.
   if (window_state->IsFullscreen() &&
-      window->GetProperty(aura::client::kAppType) ==
+      window_->GetProperty(aura::client::kAppType) ==
           static_cast<int>(AppType::BROWSER)) {
     return false;
   }
@@ -107,24 +121,4 @@
   return true;
 }
 
-ImmersiveGestureHandlerClassic::ImmersiveGestureHandlerClassic(
-    ImmersiveFullscreenController* controller)
-    : immersive_fullscreen_controller_(controller) {
-  Shell::Get()->AddPreTargetHandler(this);
-}
-
-ImmersiveGestureHandlerClassic::~ImmersiveGestureHandlerClassic() {
-  Shell::Get()->RemovePreTargetHandler(this);
-}
-
-void ImmersiveGestureHandlerClassic::OnGestureEvent(ui::GestureEvent* event) {
-  if (CanDrag(event)) {
-    DCHECK(tablet_mode_app_window_drag_controller_);
-    if (tablet_mode_app_window_drag_controller_->DragWindowFromTop(event))
-      event->SetHandled();
-    return;
-  }
-  immersive_fullscreen_controller_->OnGestureEvent(event);
-}
-
 }  // namespace ash
diff --git a/ash/wm/immersive_gesture_drag_handler.h b/ash/wm/immersive_gesture_drag_handler.h
new file mode 100644
index 0000000..07c19ee
--- /dev/null
+++ b/ash/wm/immersive_gesture_drag_handler.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WM_IMMERSIVE_GESTURE_DRAG_HANDLER_H_
+#define ASH_WM_IMMERSIVE_GESTURE_DRAG_HANDLER_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ui/events/event_handler.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+
+class TabletModeAppWindowDragController;
+
+// ImmersiveGestureHandler is responsible for calling
+// TabletAppModeWindowDragController::DragWindowFromTop() to drag the window
+// from the top if CanDrag is true when a gesture is received.
+class ASH_EXPORT ImmersiveGestureDragHandler : public ui::EventHandler {
+ public:
+  explicit ImmersiveGestureDragHandler(aura::Window* window);
+  ~ImmersiveGestureDragHandler() override;
+
+  // ui::EventHandler:
+  void OnGestureEvent(ui::GestureEvent* event) override;
+
+ private:
+  // Returns true if the target of |event| can be dragged.
+  bool CanDrag(ui::GestureEvent* event);
+
+  std::unique_ptr<TabletModeAppWindowDragController>
+      tablet_mode_app_window_drag_controller_;
+
+  aura::Window* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImmersiveGestureDragHandler);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_IMMERSIVE_GESTURE_DRAG_HANDLER_H_
diff --git a/ash/wm/immersive_gesture_handler_classic.h b/ash/wm/immersive_gesture_handler_classic.h
deleted file mode 100644
index 1abd180..0000000
--- a/ash/wm/immersive_gesture_handler_classic.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WM_IMMERSIVE_GESTURE_HANDLER_CLASSIC_H_
-#define ASH_WM_IMMERSIVE_GESTURE_HANDLER_CLASSIC_H_
-
-#include <memory>
-
-#include "ash/ash_export.h"
-#include "ash/public/cpp/immersive/immersive_gesture_handler.h"
-#include "ui/events/event_handler.h"
-
-namespace ash {
-
-class ImmersiveFullscreenController;
-class TabletModeAppWindowDragController;
-
-// ImmersiveGestureHandler is responsible for calling
-// ImmersiveFullscreenController::OnGestureEvent() to show/hide the title bar or
-// TabletAppModeWindowDragController::DragWindowFromTop() to drag the window
-// from the top if CanDrag is true when a gesture is received.
-class ASH_EXPORT ImmersiveGestureHandlerClassic
-    : public ImmersiveGestureHandler,
-      public ui::EventHandler {
- public:
-  explicit ImmersiveGestureHandlerClassic(
-      ImmersiveFullscreenController* controller);
-  ~ImmersiveGestureHandlerClassic() override;
-
-  // ui::EventHandler:
-  void OnGestureEvent(ui::GestureEvent* event) override;
-
- private:
-  // Returns true if the target of |event| can be dragged.
-  bool CanDrag(ui::GestureEvent* event);
-
-  ImmersiveFullscreenController*
-      immersive_fullscreen_controller_;  // Not owned.
-
-  std::unique_ptr<TabletModeAppWindowDragController>
-      tablet_mode_app_window_drag_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImmersiveGestureHandlerClassic);
-};
-
-}  // namespace ash
-
-#endif  // ASH_WM_IMMERSIVE_GESTURE_HANDLER_CLASSIC_H_
diff --git a/ash/wm/immersive_handler_factory_ash.cc b/ash/wm/immersive_handler_factory_ash.cc
deleted file mode 100644
index d88adb8..0000000
--- a/ash/wm/immersive_handler_factory_ash.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/wm/immersive_handler_factory_ash.h"
-
-#include <memory>
-
-#include "ash/wm/immersive_gesture_handler_classic.h"
-
-namespace ash {
-
-ImmersiveHandlerFactoryAsh::ImmersiveHandlerFactoryAsh() = default;
-
-ImmersiveHandlerFactoryAsh::~ImmersiveHandlerFactoryAsh() = default;
-
-std::unique_ptr<ImmersiveGestureHandler>
-ImmersiveHandlerFactoryAsh::CreateGestureHandler(
-    ImmersiveFullscreenController* controller) {
-  return std::make_unique<ImmersiveGestureHandlerClassic>(controller);
-}
-
-}  // namespace ash
diff --git a/ash/wm/immersive_handler_factory_ash.h b/ash/wm/immersive_handler_factory_ash.h
deleted file mode 100644
index a6eeecc1..0000000
--- a/ash/wm/immersive_handler_factory_ash.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WM_IMMERSIVE_HANDLER_FACTORY_ASH_H_
-#define ASH_WM_IMMERSIVE_HANDLER_FACTORY_ASH_H_
-
-#include "ash/ash_export.h"
-#include "ash/public/cpp/immersive/immersive_handler_factory.h"
-#include "base/macros.h"
-
-namespace ash {
-
-class ASH_EXPORT ImmersiveHandlerFactoryAsh : public ImmersiveHandlerFactory {
- public:
-  ImmersiveHandlerFactoryAsh();
-  ~ImmersiveHandlerFactoryAsh() override;
-
-  // ImmersiveHandlerFactory:
-  std::unique_ptr<ImmersiveGestureHandler> CreateGestureHandler(
-      ImmersiveFullscreenController* controller) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ImmersiveHandlerFactoryAsh);
-};
-
-}  // namespace ash
-
-#endif  // ASH_WM_IMMERSIVE_HANDLER_FACTORY_ASH_H_
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index aab7f11..77b995c2 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -15,6 +15,7 @@
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/wm/default_state.h"
+#include "ash/wm/immersive_gesture_drag_handler.h"
 #include "ash/wm/pip/pip_positioner.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_animations.h"
@@ -451,7 +452,7 @@
 }
 
 bool WindowState::IsInImmersiveFullscreen() const {
-  return window_->GetProperty(ash::kImmersiveIsActive);
+  return window_->GetProperty(kImmersiveIsActive);
 }
 
 void WindowState::set_bounds_changed_by_user(bool bounds_changed_by_user) {
@@ -733,6 +734,7 @@
 void WindowState::OnWindowPropertyChanged(aura::Window* window,
                                           const void* key,
                                           intptr_t old) {
+  DCHECK_EQ(window_, window);
   if (key == aura::client::kShowStateKey) {
     if (!ignore_property_change_) {
       WMEvent event(WMEventTypeFromShowState(GetShowState()));
@@ -764,12 +766,22 @@
     }
     return;
   }
-  if (key == kHideShelfWhenFullscreenKey || key == ash::kImmersiveIsActive) {
+  if (key == kHideShelfWhenFullscreenKey || key == kImmersiveIsActive) {
     if (!ignore_property_change_) {
       // This change came from outside ash. Update our shelf visibility based
       // on our changed state.
       ash::Shell::Get()->UpdateShelfVisibility();
     }
+    if (key == kImmersiveIsActive) {
+      if (IsInImmersiveFullscreen()) {
+        if (!immersive_gesture_drag_handler_) {
+          immersive_gesture_drag_handler_ =
+              std::make_unique<ImmersiveGestureDragHandler>(window);
+        }
+      } else {
+        immersive_gesture_drag_handler_.reset();
+      }
+    }
     return;
   }
 }
@@ -783,6 +795,7 @@
 
 void WindowState::OnWindowDestroying(aura::Window* window) {
   DCHECK_EQ(window_, window);
+  immersive_gesture_drag_handler_.reset();
   current_state_->OnWindowDestroying(this);
   delegate_.reset();
 }
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index f64e64b..cc79de3 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -26,6 +26,7 @@
 }
 
 namespace ash {
+class ImmersiveGestureDragHandler;
 class LockWindowState;
 class TabletModeWindowState;
 
@@ -475,6 +476,10 @@
 
   std::unique_ptr<State> current_state_;
 
+  // An object that assists with dragging immersive mode windows in tablet mode.
+  // Only non-null when immersive mode is active.
+  std::unique_ptr<ImmersiveGestureDragHandler> immersive_gesture_drag_handler_;
+
   DISALLOW_COPY_AND_ASSIGN(WindowState);
 };
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index c2dae03b..64fbeb1 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2239,6 +2239,7 @@
     "allocator/tcmalloc_unittest.cc",
     "android/android_image_reader_compat_unittest.cc",
     "android/application_status_listener_unittest.cc",
+    "android/child_process_unittest.cc",
     "android/content_uri_utils_unittest.cc",
     "android/jni_android_unittest.cc",
     "android/jni_array_unittest.cc",
@@ -2966,6 +2967,7 @@
       "android/java/src/org/chromium/base/annotations/DoNotInline.java",
       "android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java",
       "android/java/src/org/chromium/base/annotations/JNINamespace.java",
+      "android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java",
       "android/java/src/org/chromium/base/annotations/MainDex.java",
       "android/java/src/org/chromium/base/annotations/NativeCall.java",
       "android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java",
@@ -3022,8 +3024,8 @@
   android_aidl("base_java_aidl") {
     import_include = [ "android/java/src" ]
     sources = [
-      "android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl",
       "android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl",
+      "android/java/src/org/chromium/base/process_launcher/IParentProcess.aidl",
     ]
   }
 
diff --git a/base/android/child_process_unittest.cc b/base/android/child_process_unittest.cc
new file mode 100644
index 0000000..fc69cd10
--- /dev/null
+++ b/base/android/child_process_unittest.cc
@@ -0,0 +1,41 @@
+// 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/android/jni_android.h"
+#include "base/run_loop.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace base {
+namespace android {
+
+MULTIPROCESS_TEST_MAIN(BasicMain) {
+  return 0;
+}
+
+MULTIPROCESS_TEST_MAIN(WaitingMain) {
+  base::RunLoop().Run();
+  return 0;
+}
+
+class ChildProcessTest : public MultiProcessTest {};
+
+TEST_F(ChildProcessTest, ChildHasCleanExit) {
+  Process process = SpawnChild("BasicMain");
+  int exit_code = 0;
+  EXPECT_TRUE(WaitForMultiprocessTestChildExit(
+      process, TestTimeouts::action_timeout(), &exit_code));
+  EXPECT_EQ(exit_code, 0);
+  EXPECT_TRUE(MultiProcessTestChildHasCleanExit(process));
+}
+
+TEST_F(ChildProcessTest, ChildTerminated) {
+  Process process = SpawnChild("WaitingMain");
+  EXPECT_TRUE(TerminateMultiProcessTestChild(process, 0, true));
+  EXPECT_FALSE(MultiProcessTestChildHasCleanExit(process));
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java b/base/android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java
new file mode 100644
index 0000000..2305f296
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java
@@ -0,0 +1,16 @@
+// 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.base.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Causes generate_jni_registration() to skip native methods in a file.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.SOURCE)
+public @interface JniIgnoreNatives {}
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 5e30cfa..a203dee 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -17,6 +17,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.AccessedByNative;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JniIgnoreNatives;
 import org.chromium.base.annotations.MainDex;
 
 import java.util.HashMap;
@@ -150,6 +151,7 @@
  *
  *    This method also ensures the process uses the shared RELROs.
  */
+@JniIgnoreNatives
 public class Linker {
     // Log tag for this class.
     private static final String TAG = "LibraryLoader";
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
index f63408c..e6abc0c 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
@@ -255,6 +255,10 @@
 
     private MemoryPressureCallback mMemoryPressureCallback;
 
+    // Whether the process exited cleanly or not.
+    @GuardedBy("sBindingStateLock")
+    private boolean mCleanExit;
+
     public ChildProcessConnection(Context context, ComponentName serviceName, boolean bindToCaller,
             boolean bindAsExternalService, Bundle serviceBundle) {
         this(context, serviceName, bindToCaller, bindAsExternalService, serviceBundle,
@@ -502,6 +506,10 @@
     }
 
     private void onSetupConnectionResult(int pid) {
+        if (mPid != 0) {
+            Log.e(TAG, "sendPid was called more than once: pid=%d", mPid);
+            return;
+        }
         mPid = pid;
         assert mPid != 0 : "Child service claims to be run by a process of pid=0.";
 
@@ -522,9 +530,9 @@
             assert mServiceConnectComplete && mService != null;
             assert mConnectionParams != null;
 
-            ICallbackInt pidCallback = new ICallbackInt.Stub() {
+            IParentProcess parentProcess = new IParentProcess.Stub() {
                 @Override
-                public void call(final int pid) {
+                public void sendPid(final int pid) {
                     mLauncherHandler.post(new Runnable() {
                         @Override
                         public void run() {
@@ -532,9 +540,22 @@
                         }
                     });
                 }
+
+                @Override
+                public void reportCleanExit() {
+                    synchronized (sBindingStateLock) {
+                        mCleanExit = true;
+                    }
+                    mLauncherHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            unbind();
+                        }
+                    });
+                }
             };
             try {
-                mService.setupConnection(mConnectionParams.mConnectionBundle, pidCallback,
+                mService.setupConnection(mConnectionParams.mConnectionBundle, parentProcess,
                         mConnectionParams.mClientInterfaces);
             } catch (RemoteException re) {
                 Log.e(TAG, "Failed to setup connection.", re);
@@ -676,6 +697,15 @@
     }
 
     /**
+     * @return true if the process exited cleanly.
+     */
+    public boolean hasCleanExit() {
+        synchronized (sBindingStateLock) {
+            return mCleanExit;
+        }
+    }
+
+    /**
      * Returns the binding state of remaining processes, excluding the current connection.
      *
      * If the current process is dead then returns the binding state of all processes when it died.
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
index f9d7ca7..6e285f9 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
@@ -89,6 +89,9 @@
 
     private final Semaphore mActivitySemaphore = new Semaphore(1);
 
+    // Interface to send notifications to the parent process.
+    private IParentProcess mParentProcess;
+
     // These values are persisted to logs. Entries should not be renumbered and numeric values
     // should never be reused.
     @IntDef({SplitApkWorkaroundResult.NOT_RUN, SplitApkWorkaroundResult.NO_ENTRIES,
@@ -137,18 +140,19 @@
         }
 
         @Override
-        public void setupConnection(Bundle args, ICallbackInt pidCallback, List<IBinder> callbacks)
-                throws RemoteException {
+        public void setupConnection(Bundle args, IParentProcess parentProcess,
+                List<IBinder> callbacks) throws RemoteException {
             assert mServiceBound;
             synchronized (mBinderLock) {
                 if (mBindToCallerCheck && mBoundCallingPid == 0) {
                     Log.e(TAG, "Service has not been bound with bindToCaller()");
-                    pidCallback.call(-1);
+                    parentProcess.sendPid(-1);
                     return;
                 }
             }
 
-            pidCallback.call(Process.myPid());
+            parentProcess.sendPid(Process.myPid());
+            mParentProcess = parentProcess;
             processConnectionBundle(args, callbacks);
         }
 
@@ -272,6 +276,11 @@
                     }
                     if (mActivitySemaphore.tryAcquire()) {
                         mDelegate.runMain();
+                        try {
+                            mParentProcess.reportCleanExit();
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Failed to call clean exit callback.", e);
+                        }
                         nativeExitChildProcess();
                     }
                 } catch (InterruptedException e) {
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl b/base/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl
deleted file mode 100644
index db93cb0d..0000000
--- a/base/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl
+++ /dev/null
@@ -1,9 +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.
-
-package org.chromium.base.process_launcher;
-
-oneway interface ICallbackInt {
-    void call(int value);
-}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
index 298e0bf4..bca9c171 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
+++ b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
@@ -6,7 +6,7 @@
 
 import android.os.Bundle;
 
-import org.chromium.base.process_launcher.ICallbackInt;
+import org.chromium.base.process_launcher.IParentProcess;
 
 interface IChildProcessService {
   // On the first call to this method, the service will record the calling PID
@@ -15,8 +15,8 @@
   boolean bindToCaller();
 
   // Sets up the initial IPC channel.
-  oneway void setupConnection(in Bundle args, ICallbackInt pidCallback,
-          in List<IBinder>  clientInterfaces);
+  oneway void setupConnection(in Bundle args, IParentProcess parentProcess,
+          in List<IBinder> clientInterfaces);
 
   // Forcefully kills the child process.
   oneway void forceKill();
diff --git a/base/android/java/src/org/chromium/base/process_launcher/IParentProcess.aidl b/base/android/java/src/org/chromium/base/process_launcher/IParentProcess.aidl
new file mode 100644
index 0000000..325466c
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/IParentProcess.aidl
@@ -0,0 +1,15 @@
+// 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.base.process_launcher;
+
+interface IParentProcess {
+    // Sends the child pid to the parent process. This will be called before any
+    // third-party code is loaded, and will be a no-op after the first call.
+    oneway void sendPid(int pid);
+
+    // Tells the parent proces the child exited cleanly. Not oneway to ensure
+    // the browser receives the message before child exits.
+    void reportCleanExit();
+}
diff --git a/base/android/jni_generator/BUILD.gn b/base/android/jni_generator/BUILD.gn
index 3639eab..b02b506e 100644
--- a/base/android/jni_generator/BUILD.gn
+++ b/base/android/jni_generator/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/config/android/rules.gni")
 import("//testing/test.gni")
 
@@ -72,7 +71,6 @@
 generate_jni_registration("sample_jni_registration") {
   target = ":sample_jni_apk"
   output = "$target_gen_dir/${target_name}.h"
-  exception_files = jni_exception_files
 }
 
 # Serves to test that generated bindings compile properly.
diff --git a/base/android/jni_generator/jni_exception_list.gni b/base/android/jni_generator/jni_exception_list.gni
deleted file mode 100644
index 11c7f6a..0000000
--- a/base/android/jni_generator/jni_exception_list.gni
+++ /dev/null
@@ -1,13 +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.
-
-import("//device/vr/buildflags/buildflags.gni")
-
-jni_exception_files =
-    [ "//base/android/java/src/org/chromium/base/library_loader/Linker.java" ]
-
-# Exclude it from JNI registration if VR is not enabled.
-if (!enable_vr) {
-  jni_exception_files += [ "//chrome/android/java/src/org/chromium/chrome/browser/vr/VrModuleProvider.java" ]
-}
diff --git a/base/android/jni_generator/jni_registration_generator.py b/base/android/jni_generator/jni_registration_generator.py
index e6d91e2..c3eefc1 100755
--- a/base/android/jni_generator/jni_registration_generator.py
+++ b/base/android/jni_generator/jni_registration_generator.py
@@ -71,6 +71,9 @@
 def _DictForPath(path):
   with open(path) as f:
     contents = jni_generator.RemoveComments(f.read())
+    if '@JniIgnoreNatives' in contents:
+      return None
+
   fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName(
       path, contents)
   natives = jni_generator.ExtractNatives(contents, 'long')
diff --git a/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java b/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
index a12d50d..85ef7018 100644
--- a/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
+++ b/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
@@ -116,7 +116,7 @@
 
     // Parameters captured from the IChildProcessService.setupConnection() call
     private Bundle mConnectionBundle;
-    private ICallbackInt mConnectionPidCallback;
+    private IParentProcess mConnectionParentProcess;
     private IBinder mConnectionIBinderCallback;
 
     @Before
@@ -129,7 +129,7 @@
             @Override
             public Void answer(InvocationOnMock invocation) {
                 mConnectionBundle = (Bundle) invocation.getArgument(0);
-                mConnectionPidCallback = (ICallbackInt) invocation.getArgument(1);
+                mConnectionParentProcess = (IParentProcess) invocation.getArgument(1);
                 mConnectionIBinderCallback = (IBinder) invocation.getArgument(2);
                 return null;
             }
@@ -305,12 +305,30 @@
         verify(mConnectionCallback, never()).onConnected(any());
         mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
         ShadowLooper.runUiThreadTasks();
-        assertNotNull(mConnectionPidCallback);
-        mConnectionPidCallback.call(34 /* pid */);
+        assertNotNull(mConnectionParentProcess);
+        mConnectionParentProcess.sendPid(34);
         verify(mConnectionCallback, times(1)).onConnected(connection);
     }
 
     @Test
+    public void testSendPidOnlyWorksOnce() throws RemoteException {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, null /* serviceCallback */);
+        connection.setupConnection(
+                null /* connectionBundle */, null /* callback */, mConnectionCallback);
+        verify(mConnectionCallback, never()).onConnected(any());
+        mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
+        ShadowLooper.runUiThreadTasks();
+        assertNotNull(mConnectionParentProcess);
+
+        mConnectionParentProcess.sendPid(34);
+        assertEquals(34, connection.getPid());
+        mConnectionParentProcess.sendPid(543);
+        assertEquals(34, connection.getPid());
+    }
+
+    @Test
     public void testSetupConnectionAfterServiceConnected() throws RemoteException {
         ChildProcessConnection connection = createDefaultTestConnection();
         assertNotNull(mFirstServiceConnection);
@@ -320,8 +338,8 @@
                 null /* connectionBundle */, null /* callback */, mConnectionCallback);
         verify(mConnectionCallback, never()).onConnected(any());
         ShadowLooper.runUiThreadTasks();
-        assertNotNull(mConnectionPidCallback);
-        mConnectionPidCallback.call(34 /* pid */);
+        assertNotNull(mConnectionParentProcess);
+        mConnectionParentProcess.sendPid(34);
         verify(mConnectionCallback, times(1)).onConnected(connection);
     }
 
@@ -335,8 +353,8 @@
                 null /* connectionBundle */, null /* callback */, mConnectionCallback);
         verify(mConnectionCallback, never()).onConnected(any());
         ShadowLooper.runUiThreadTasks();
-        assertNotNull(mConnectionPidCallback);
-        mConnectionPidCallback.call(34 /* pid */);
+        assertNotNull(mConnectionParentProcess);
+        mConnectionParentProcess.sendPid(34);
         verify(mConnectionCallback, times(1)).onConnected(connection);
 
         // Add strong binding so that connection is oom protected.
diff --git a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
index 227d31f..a0640ce1 100644
--- a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
@@ -321,6 +321,26 @@
         }
     }
 
+    @CalledByNative
+    private static boolean hasCleanExit(final int pid) {
+        return runOnLauncherAndGetResult(new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return hasCleanExitOnLauncherThread(pid);
+            }
+        });
+    }
+
+    private static boolean hasCleanExitOnLauncherThread(int pid) {
+        assert isRunningOnLauncherThread();
+
+        MultiprocessTestClientLauncher launcher = sPidToLauncher.get(pid);
+        if (launcher == null) {
+            return false;
+        }
+        return launcher.mLauncher.getConnection().hasCleanExit();
+    }
+
     /** Does not take ownership of of fds. */
     @CalledByNative
     private static FileDescriptorInfo[] makeFdInfoArray(int[] keys, int[] fds) {
diff --git a/base/test/multiprocess_test.h b/base/test/multiprocess_test.h
index 3c8675f3..52c35e0 100644
--- a/base/test/multiprocess_test.h
+++ b/base/test/multiprocess_test.h
@@ -81,6 +81,11 @@
                                     int exit_code,
                                     bool wait);
 
+#if defined(OS_ANDROID)
+// Returns whether the child process exited cleanly from the main runloop.
+bool MultiProcessTestChildHasCleanExit(const Process& process);
+#endif
+
 // MultiProcessTest ------------------------------------------------------------
 
 // A MultiProcessTest is a test class which makes it easier to
diff --git a/base/test/multiprocess_test_android.cc b/base/test/multiprocess_test_android.cc
index 4108593..e20ed14 100644
--- a/base/test/multiprocess_test_android.cc
+++ b/base/test/multiprocess_test_android.cc
@@ -83,4 +83,12 @@
       env, process.Pid(), exit_code, wait);
 }
 
+bool MultiProcessTestChildHasCleanExit(const Process& process) {
+  JNIEnv* env = android::AttachCurrentThread();
+  DCHECK(env);
+
+  return android::Java_MultiprocessTestClientLauncher_hasCleanExit(
+      env, process.Pid());
+}
+
 }  // namespace base
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 67b73a3..498fcf8b 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -3168,6 +3168,7 @@
         data = [
           "$_final_apk_path.mapping",
         ]
+        data_deps += [ "//build/android/stacktrace:java_deobfuscate" ]
       }
 
       dist_ijar_path = _dist_ijar_path
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 29161fe0..946fb59 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1606,13 +1606,6 @@
       # suppressing them individually, we just blanket suppress them here.
       "-Wno-unused-variable",
     ]
-
-    if (!is_nacl) {
-      cflags += [
-        # TODO(hans): https://crbug.com/681136
-        "-Wno-unused-lambda-capture",
-      ]
-    }
   }
 
   configs = [ ":default_warnings" ]
diff --git a/build/fuchsia/fidlgen_js/gen.py b/build/fuchsia/fidlgen_js/gen.py
index 218b20f..c1083ad 100755
--- a/build/fuchsia/fidlgen_js/gen.py
+++ b/build/fuchsia/fidlgen_js/gen.py
@@ -70,8 +70,10 @@
         'uint64': 8,
         'uint8': 1,
     }[t.subtype]
+  elif t.kind == fidl.TypeKind.STRING:
+    return 16
   else:
-    raise NotImplementedError()
+    raise NotImplementedError(t.kind)
 
 
 def _CompileConstant(val, assignment_type):
@@ -223,10 +225,8 @@
   enc: function(e, o, v) {
     e.data.setUint32(o, v ? 0xffffffff : 0, $fidl__kLE);
     e.data.setUint32(o + 4, v ? 0xffffffff : 0, $fidl__kLE);
-    e.outOfLine.push([function() {
-      var start = e.alloc(%(size)s);
-      _kTT_%(name)s.enc(e, start, v);
-    }]);
+    var start = e.alloc(%(size)s);
+    _kTT_%(name)s.enc(e, start, v);
   },
   dec: function(d, o) {
     if (d.data.getUint32(o, $fidl__kLE) === 0) {
@@ -344,7 +344,7 @@
     if t.kind == fidl.TypeKind.PRIMITIVE:
       return t.subtype
     elif t.kind == fidl.TypeKind.STRING:
-      return 'String' + ('_Nullable' if t.nullable else '_Nonnull')
+      return 'String' + ('_Nullable' if t.nullable else '')
     elif t.kind == fidl.TypeKind.IDENTIFIER:
       compound = _ParseCompoundIdentifier(t.identifier)
       name = _CompileCompoundIdentifier(compound)
@@ -382,7 +382,7 @@
     elif t.kind == fidl.TypeKind.VECTOR:
       element_ttname = self._CompileType(t.element_type)
       ttname = (
-          'VEC_' + ('Nullable_' if t.nullable else 'Nonnull_') + element_ttname)
+          'VEC_' + ('Nullable_' if t.nullable else '') + element_ttname)
       pointer_set = '''    if (v === null || v === undefined) {
       e.data.setUint32(o + 8, 0, $fidl__kLE);
       e.data.setUint32(o + 12, 0, $fidl__kLE);
@@ -407,13 +407,10 @@
     e.data.setUint32(o, v.length, $fidl__kLE);
     e.data.setUint32(o + 4, 0, $fidl__kLE);
 %(pointer_set)s
-    e.outOfLine.push([function(e, body) {
-        var start = e.alloc(body.length * %(element_size)s);
-        for (var i = 0; i < body.length; i++) {
-          _kTT_%(element_ttname)s.enc(e, start + (i * %(element_size)s), body[i]);
-        }
-      },
-      v]);
+    var start = e.alloc(v.length * %(element_size)s);
+    for (var i = 0; i < v.length; i++) {
+      _kTT_%(element_ttname)s.enc(e, start + (i * %(element_size)s), v[i]);
+    }
   },
   dec: function(d, o) {
     var len = d.data.getUint32(o, $fidl__kLE);
diff --git a/build/fuchsia/fidlgen_js/runtime/fidl.mjs b/build/fuchsia/fidlgen_js/runtime/fidl.mjs
index be7f343..c46c8e88 100644
--- a/build/fuchsia/fidlgen_js/runtime/fidl.mjs
+++ b/build/fuchsia/fidlgen_js/runtime/fidl.mjs
@@ -36,7 +36,6 @@
   this.data = new DataView(buf);
   this.extent = 0;
   this.handles = [];
-  this.outOfLine = [];
   this._encodeMessageHeader(ordinal);
 }
 
@@ -88,13 +87,6 @@
 };
 
 $fidl_Encoder.prototype.messageData = function() {
-  // Add all out of line data.
-  var len = this.outOfLine.length;
-  for (var i = 0; i < len; i++) {
-    this.outOfLine[i][0](this, this.outOfLine[i][1]);
-  }
-
-  // Return final result.
   return new DataView(this.data.buffer, 0, this.extent);
 };
 
@@ -189,17 +181,21 @@
   },
 };
 
-const _kTT_String_Nonnull = {
+const _kTT_String = {
   enc: function(e, o, v) {
     if (v === null || v === undefined) throw "non-null string required";
     // Both size and data are uint64, but that's awkward in JS, so for now only
-    // support a maximum of 32b lengths.
+    // support a maximum of 32b lengths. The maximum length of a FIDL message is
+    // shorter than 32b in any case.
     var asUtf8 = $FidlJsStrToUtf8Array(v);
     e.data.setUint32(o, asUtf8.length, $fidl__kLE);
     e.data.setUint32(o + 4, 0, $fidl__kLE);
     e.data.setUint32(o + 8, 0xffffffff, $fidl__kLE);
     e.data.setUint32(o + 12, 0xffffffff, $fidl__kLE);
-    e.outOfLine.push([$fidl_OutOfLineStringEnc, asUtf8]);
+    var body = e.alloc(asUtf8.length);
+    for (var i = 0; i < asUtf8.length; i++) {
+      e.data.setUint8(body + i, asUtf8[i], $fidl__kLE);
+    }
   },
   dec: function(d, o) {
     var len = d.data.getUint32(o, $fidl__kLE);
@@ -210,9 +206,25 @@
   }
 };
 
-function $fidl_OutOfLineStringEnc(e, strAsUtf8Array) {
-  var start = e.alloc(strAsUtf8Array.length);
-  for (var i = 0; i < strAsUtf8Array.length; i++) {
-    e.data.setUint8(start + i, strAsUtf8Array[i], $fidl__kLE);
+const _kTT_String_Nullable = {
+  enc: function(e, o, v) {
+    if (v === null || v === undefined) {
+      e.data.setUint32(o, 0, $fidl__kLE);
+      e.data.setUint32(o + 4, 0, $fidl__kLE);
+      e.data.setUint32(o + 8, 0, $fidl__kLE);
+      e.data.setUint32(o + 12, 0, $fidl__kLE);
+    } else {
+      _kTT_String.enc(e, o, v);
+    }
+  },
+  dec: function(d, o) {
+    if (v === null || v === undefined) {
+      var pointer = d.data.getUint32(o + 8, $fidl__kLE);
+      if (pointer === 0) {
+        return null;
+      }
+    } else {
+      return _kTT_String.dec(e, o, v);
+    }
   }
-}
+};
diff --git a/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc b/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
index 63ae2b6..4bd64fd7 100644
--- a/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
+++ b/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
@@ -251,6 +251,27 @@
     callback(std::move(resp));
   }
 
+  void SendVectorsOfString(fidl::VectorPtr<fidl::StringPtr> unsized,
+                           fidl::VectorPtr<fidl::StringPtr> nullable,
+                           fidl::VectorPtr<fidl::StringPtr> sized10) override {
+    ASSERT_EQ(unsized->size(), 3u);
+    EXPECT_EQ((*unsized)[0], "str0");
+    EXPECT_EQ((*unsized)[1], "str1");
+    EXPECT_EQ((*unsized)[2], "str2");
+
+    ASSERT_EQ(nullable->size(), 5u);
+    EXPECT_EQ((*nullable)[0], "str3");
+    EXPECT_TRUE((*nullable)[1].is_null());
+    EXPECT_TRUE((*nullable)[2].is_null());
+    EXPECT_TRUE((*nullable)[3].is_null());
+    EXPECT_EQ((*nullable)[4], "str4");
+
+    ASSERT_EQ(sized10->size(), 1u);
+    EXPECT_EQ((*sized10)[0], "0123456789");
+
+    did_get_vectors_of_string_ = true;
+  }
+
   bool was_do_something_called() const { return was_do_something_called_; }
   int32_t received_int() const { return received_int_; }
   const std::string& received_msg() const { return received_msg_; }
@@ -265,6 +286,8 @@
 
   bool did_receive_union() const { return did_receive_union_; }
 
+  bool did_get_vectors_of_string() const { return did_get_vectors_of_string_; }
+
   void CallResponseCallbacks() {
     for (auto& cb : response_callbacks_) {
       std::move(cb).Run();
@@ -283,6 +306,7 @@
   std::vector<base::OnceClosure> response_callbacks_;
   zx_handle_t unowned_log_handle_;
   bool did_receive_union_ = false;
+  bool did_get_vectors_of_string_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestolaImpl);
 };
@@ -723,6 +747,52 @@
             static_cast<int>(fidljstest::Blorp::BETA));
 }
 
+TEST_F(FidlGenJsTest, VectorOfStrings) {
+  v8::Isolate* isolate = instance_->isolate();
+  BindingsSetupHelper helper(isolate);
+
+  TestolaImpl testola_impl;
+  fidl::Binding<fidljstest::Testola> binding(&testola_impl);
+  binding.Bind(std::move(helper.server()));
+
+  std::string source = R"(
+    var proxy = new TestolaProxy();
+    proxy.$bind(testHandle);
+
+    var v1 = ['str0', 'str1', 'str2'];
+    var v2 = ['str3', null, null, null, 'str4'];
+    var v3 = ['0123456789'];  // This is the maximum allowed length.
+    proxy.SendVectorsOfString(v1, v2, v3);
+  )";
+  helper.runner().Run(source, "test.js");
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(testola_impl.did_get_vectors_of_string());
+}
+
+TEST_F(FidlGenJsTest, VectorOfStringsTooLongString) {
+  v8::Isolate* isolate = instance_->isolate();
+  BindingsSetupHelper helper(isolate);
+
+  TestolaImpl testola_impl;
+  fidl::Binding<fidljstest::Testola> binding(&testola_impl);
+  binding.Bind(std::move(helper.server()));
+
+  std::string source = R"(
+    var proxy = new TestolaProxy();
+    proxy.$bind(testHandle);
+
+    var too_long = ['this string is longer than allowed'];
+    proxy.SendVectorsOfString([], [], too_long);
+    this.tried_to_send = true;
+  )";
+  helper.runner().Run(source, "test.js");
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(helper.Get<bool>("tried_to_send"));
+  EXPECT_FALSE(testola_impl.did_get_vectors_of_string());
+}
+
 int main(int argc, char** argv) {
   base::TestSuite test_suite(argc, argv);
 
diff --git a/build/fuchsia/fidlgen_js/test/simple.fidl b/build/fuchsia/fidlgen_js/test/simple.fidl
index a7e714a..43a2f72a 100644
--- a/build/fuchsia/fidlgen_js/test/simple.fidl
+++ b/build/fuchsia/fidlgen_js/test/simple.fidl
@@ -81,4 +81,8 @@
   9: ReceiveUnions(StructOfMultipleUnions somu);
 
   10: SendUnions() -> (StructOfMultipleUnions somu);
+
+  11: SendVectorsOfString(vector<string> unsized,
+                          vector<string?> nullable,
+                          vector<string:10> sized10);
 };
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 68ec7c5..ef171c2c 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -363,7 +363,6 @@
     "//gpu",
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/command_buffer/client:raster_interface",
-    "//gpu/ipc:gl_in_process_context",
     "//gpu/skia_bindings:skia_bindings",
     "//gpu/vulkan:buildflags",
     "//media",  # For VideoLayerImpl.
diff --git a/cc/paint/transfer_cache_unittest.cc b/cc/paint/transfer_cache_unittest.cc
index e3a744b..f24917a 100644
--- a/cc/paint/transfer_cache_unittest.cc
+++ b/cc/paint/transfer_cache_unittest.cc
@@ -20,6 +20,7 @@
 #include "gpu/command_buffer/service/service_transfer_cache.h"
 #include "gpu/config/gpu_switches.h"
 #include "gpu/ipc/raster_in_process_context.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "ui/gl/gl_implementation.h"
@@ -47,8 +48,8 @@
 
     context_ = std::make_unique<gpu::RasterInProcessContext>();
     auto result = context_->Initialize(
-        /*service=*/nullptr, attribs, gpu::SharedMemoryLimits(),
-        &gpu_memory_buffer_manager_, &image_factory_,
+        gpu::GetTestGpuThreadHolder()->GetTaskExecutor(), attribs,
+        gpu::SharedMemoryLimits(), &gpu_memory_buffer_manager_, &image_factory_,
         /*gpu_channel_manager_delegate=*/nullptr, nullptr, nullptr);
 
     ASSERT_EQ(result, gpu::ContextResult::kSuccess);
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index d984306b0..2a73128 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
@@ -348,7 +349,9 @@
       unpremultiply_and_dither_low_bit_depth_tiles_(
           unpremultiply_and_dither_low_bit_depth_tiles),
       enable_oop_rasterization_(enable_oop_rasterization),
-      raster_metric_frequency_(raster_metric_frequency) {
+      raster_metric_frequency_(raster_metric_frequency),
+      random_generator_(base::RandUint64()),
+      uniform_distribution_(1, raster_metric_frequency) {
   DCHECK(compositor_context_provider);
   DCHECK(worker_context_provider);
 }
@@ -500,12 +503,8 @@
   gpu::raster::RasterInterface* ri = scoped_context.RasterInterface();
   DCHECK(ri);
 
-  raster_tasks_count_++;
-  bool measure_raster_metric = false;
-  if (raster_tasks_count_ == raster_metric_frequency_) {
-    measure_raster_metric = true;
-    raster_tasks_count_ = 0;
-  }
+  const bool measure_raster_metric =
+      uniform_distribution_(random_generator_) == raster_metric_frequency_;
 
   gfx::Rect playback_rect = raster_full_rect;
   if (resource_has_previous_content) {
diff --git a/cc/raster/gpu_raster_buffer_provider.h b/cc/raster/gpu_raster_buffer_provider.h
index 9e53243c..0ef41dd0 100644
--- a/cc/raster/gpu_raster_buffer_provider.h
+++ b/cc/raster/gpu_raster_buffer_provider.h
@@ -6,6 +6,7 @@
 #define CC_RASTER_GPU_RASTER_BUFFER_PROVIDER_H_
 
 #include <stdint.h>
+#include <random>
 
 #include "base/macros.h"
 #include "cc/raster/raster_buffer_provider.h"
@@ -160,7 +161,8 @@
       GUARDED_BY(pending_raster_queries_lock_);
 
   // Accessed with the worker context lock acquired.
-  int raster_tasks_count_ = 0;
+  std::mt19937 random_generator_;
+  std::uniform_int_distribution<int> uniform_distribution_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuRasterBufferProvider);
 };
diff --git a/cc/test/cc_test_suite.cc b/cc/test/cc_test_suite.cc
index 53f2531..cf60062 100644
--- a/cc/test/cc_test_suite.cc
+++ b/cc/test/cc_test_suite.cc
@@ -9,10 +9,7 @@
 #include "base/threading/thread_id_name_manager.h"
 #include "cc/base/histograms.h"
 #include "components/viz/test/paths.h"
-#include "gpu/config/gpu_info_collector.h"
-#include "gpu/config/gpu_preferences.h"
-#include "gpu/config/gpu_util.h"
-#include "gpu/ipc/in_process_command_buffer.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 #include "ui/gl/test/gl_surface_test_support.h"
 
 namespace cc {
@@ -27,18 +24,13 @@
   message_loop_ = std::make_unique<base::MessageLoop>();
 
   gl::GLSurfaceTestSupport::InitializeOneOff();
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  gpu::GPUInfo gpu_info;
-  gpu::CollectGraphicsInfoForTesting(&gpu_info);
-  gpu::GpuFeatureInfo gpu_feature_info = gpu::ComputeGpuFeatureInfo(
-      gpu_info, gpu::GpuPreferences(), command_line, nullptr);
+
   // Always enable gpu and oop raster, regardless of platform and blacklist.
-  gpu_feature_info.status_values[gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION] =
+  auto* gpu_feature_info = gpu::GetTestGpuThreadHolder()->GetGpuFeatureInfo();
+  gpu_feature_info->status_values[gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION] =
       gpu::kGpuFeatureStatusEnabled;
-  gpu_feature_info.status_values[gpu::GPU_FEATURE_TYPE_OOP_RASTERIZATION] =
+  gpu_feature_info->status_values[gpu::GPU_FEATURE_TYPE_OOP_RASTERIZATION] =
       gpu::kGpuFeatureStatusEnabled;
-  gpu::InProcessCommandBuffer::InitializeDefaultServiceForTesting(
-      gpu_feature_info);
 
   viz::Paths::RegisterPathProvider();
 
diff --git a/cc/test/test_in_process_context_provider.cc b/cc/test/test_in_process_context_provider.cc
index b1dcbfa1..923704a 100644
--- a/cc/test/test_in_process_context_provider.cc
+++ b/cc/test/test_in_process_context_provider.cc
@@ -21,12 +21,14 @@
 #include "gpu/command_buffer/common/skia_utils.h"
 #include "gpu/ipc/gl_in_process_context.h"
 #include "gpu/ipc/raster_in_process_context.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
 #include "ui/gfx/native_widget_types.h"
+
 namespace cc {
 
 namespace {
@@ -49,9 +51,9 @@
 
   auto context = std::make_unique<gpu::GLInProcessContext>();
   auto result = context->Initialize(
-      nullptr, nullptr, is_offscreen, gpu::kNullSurfaceHandle, attribs,
-      gpu::SharedMemoryLimits(), gpu_memory_buffer_manager, image_factory,
-      std::move(task_runner));
+      gpu::GetTestGpuThreadHolder()->GetTaskExecutor(), nullptr, is_offscreen,
+      gpu::kNullSurfaceHandle, attribs, gpu::SharedMemoryLimits(),
+      gpu_memory_buffer_manager, image_factory, std::move(task_runner));
 
   DCHECK_EQ(result, gpu::ContextResult::kSuccess);
   return context;
@@ -94,10 +96,10 @@
     attribs.enable_raster_interface = true;
     attribs.enable_gles2_interface = false;
 
-    raster_context_.reset(new gpu::RasterInProcessContext);
+    raster_context_ = std::make_unique<gpu::RasterInProcessContext>();
     auto result = raster_context_->Initialize(
-        /*service=*/nullptr, attribs, gpu::SharedMemoryLimits(),
-        &gpu_memory_buffer_manager_, &image_factory_,
+        gpu::GetTestGpuThreadHolder()->GetTaskExecutor(), attribs,
+        gpu::SharedMemoryLimits(), &gpu_memory_buffer_manager_, &image_factory_,
         /*gpu_channel_manager_delegate=*/nullptr, gr_shader_cache_,
         activity_flags_);
     DCHECK_EQ(result, gpu::ContextResult::kSuccess);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 4b60609..baf019c 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/config/android/chrome_version.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
@@ -63,6 +62,16 @@
       "$target_gen_dir/monochrome_locale_whitelist.txt"
 }
 
+# Exclude it from JNI registration if VR is not enabled.
+jni_exception_files = []
+if (!enable_vr) {
+  jni_exception_files += [ "//chrome/android/java/src/org/chromium/chrome/browser/vr/VrModuleProvider.java" ]
+}
+chrome_jni_registration_header =
+    "$root_build_dir/gen/chrome/browser/android/chrome_jni_registration.h"
+chrome_jni_for_test_registration_header = "$root_build_dir/gen/chrome/browser/android/chrome_jni_for_test_registration.h"
+chrome_sync_shell_jni_registration_header = "$root_build_dir/gen/chrome/browser/android/chrome_sync_shell_jni_registration.h"
+
 jinja_template("chrome_public_android_manifest") {
   input = "java/AndroidManifest.xml"
   output = chrome_public_android_manifest
@@ -309,6 +318,7 @@
     "//net/android:net_java",
     "//printing:printing_java",
     "//services/data_decoder/public/cpp/android:safe_json_java",
+    "//services/media_session/public/mojom:mojom_java",
     "//services/network/public/mojom:mojom_java",
     "//services/service_manager/public/java:service_manager_java",
     "//services/service_manager/public/mojom:mojom_java",
@@ -1044,6 +1054,7 @@
 chrome_shared_library("libchrome") {
   sources = [
     "../browser/android/chrome_entry_point.cc",
+    chrome_jni_registration_header,
   ]
   deps = [
     ":chrome_jni_registration($default_toolchain)",
@@ -1059,6 +1070,7 @@
   testonly = true
   sources = [
     "../browser/android/chrome_entry_point_for_test.cc",
+    chrome_jni_for_test_registration_header,
   ]
   deps = [
     ":browser_test_support",
@@ -1083,21 +1095,21 @@
 if (current_toolchain == default_toolchain) {
   generate_jni_registration("chrome_jni_registration") {
     target = ":chrome_public_base_module_java"
-    output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
+    output = chrome_jni_registration_header
     exception_files = jni_exception_files
   }
 
   generate_jni_registration("chrome_jni_for_test_registration") {
     testonly = true
     target = ":chrome_public_base_module_java_for_test"
-    output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
+    output = chrome_jni_for_test_registration_header
     exception_files = jni_exception_files
   }
 
   generate_jni_registration("chrome_sync_shell_jni_registration") {
     testonly = true
     target = ":chrome_sync_shell_java"
-    output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
+    output = chrome_sync_shell_jni_registration_header
     exception_files = jni_exception_files
   }
 
@@ -1276,6 +1288,7 @@
   testonly = true
   sources = [
     "../browser/android/chrome_sync_shell_entry_point.cc",
+    chrome_sync_shell_jni_registration_header,
   ]
   deps = [
     ":chrome_sync_shell_jni_registration($default_toolchain)",
diff --git a/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_default_details.xml b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_default_details.xml
new file mode 100644
index 0000000..3cb5527
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_default_details.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <solid android:color="@color/modern_grey_100"/>
+  <corners android:radius="4dp"/>
+</shape>
diff --git a/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
index bdef8cd..4c5d5286 100644
--- a/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
+++ b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
@@ -108,20 +108,24 @@
                 android:orientation="vertical">
                 <TextView
                     android:id="@+id/details_title"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"
+                    android:layout_width="wrap_content"
+                    android:minWidth="184dp"
+                    android:layout_height="16dp"
+                    android:layout_marginBottom="2dp"
+                    android:layout_marginTop="7dp"
                     android:gravity="bottom"
-                    android:textAppearance="@style/BlackTitle2"
+                    android:textAppearance="@style/BlackCaptionDefault"
+                    android:textStyle="bold"
                     android:maxLines="1"
+                    android:background="@color/modern_grey_100"
                     android:ellipsize="end"/>
                 <TextView
                     android:id="@+id/details_text"
                     android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"
+                    android:layout_height="16dp"
                     android:gravity="top"
-                    android:textAppearance="@style/BlackBody"
+                    android:textAppearance="@style/BlackCaption"
+                    android:background="@color/modern_grey_100"
                     android:maxLines="1"
                     android:ellipsize="end"/>
             </LinearLayout>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index 21691b4..0d144b9e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -367,7 +367,7 @@
      * in the same task.
      */
     private void launchCustomTabActivity() {
-        boolean handled = BrowserSessionContentUtils.handleInActiveContentIfNeeded(mIntent);
+        boolean handled = BrowserSessionContentUtils.handleBrowserServicesIntent(mIntent);
         if (handled) return;
 
         maybePrefetchDnsInBackground();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 71c18479..3786b32 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -27,10 +27,13 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentOptions;
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -54,6 +57,8 @@
     private static final String AUTH_TOKEN_TYPE =
             "oauth2:https://www.googleapis.com/auth/userinfo.profile";
 
+    private static final String RFC_3339_FORMAT = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ";
+
     private final WebContents mWebContents;
     private final long mUiControllerAndroid;
     private final UiDelegateHolder mUiDelegateHolder;
@@ -103,6 +108,12 @@
         Map<String, String> parameters = extractParameters(activity.getInitialIntent().getExtras());
         parameters.remove(PARAMETER_ENABLED);
 
+        AutofillAssistantUiDelegate.Details initialDetails = makeDetailsFromParameters(parameters);
+        if (!initialDetails.isEmpty()) {
+            mUiDelegateHolder.performUiOperation(
+                    uiDelegate -> uiDelegate.showDetails(initialDetails));
+        }
+
         Tab activityTab = activity.getActivityTab();
         mWebContents = activityTab.getWebContents();
         mUiControllerAndroid =
@@ -172,6 +183,37 @@
         return result;
     }
 
+    // TODO(crbug.com/806868): Create a fallback when there are no parameters for details.
+    private static AutofillAssistantUiDelegate.Details makeDetailsFromParameters(
+            Map<String, String> parameters) {
+        String title = "";
+        String description = "";
+        Date date = null;
+        for (String key : parameters.keySet()) {
+            if (key.contains("E_NAME")) {
+                title = parameters.get(key);
+                continue;
+            }
+
+            if (key.contains("R_NAME")) {
+                description = parameters.get(key);
+                continue;
+            }
+
+            if (key.contains("DATETIME")) {
+                try {
+                    date = new SimpleDateFormat(RFC_3339_FORMAT, Locale.getDefault())
+                                   .parse(parameters.get(key));
+                } catch (ParseException e) {
+                    // Ignore.
+                }
+            }
+        }
+
+        return new AutofillAssistantUiDelegate.Details(
+                title, /* url= */ "", date, description, /* isFinal= */ false);
+    }
+
     @Override
     public void onClickOverlay() {
         // TODO(crbug.com/806868): Notify native side.
@@ -225,7 +267,7 @@
         // TODO(crbug.com/806868): Remove this method once all scripts use payment request.
         mUiDelegateHolder.performUiOperation(uiDelegate
                 -> uiDelegate.showProfiles(PersonalDataManager.getInstance().getProfilesToSuggest(
-                        true /* includeNameInLabel */)));
+                        /* includeNameInLabel= */ true)));
     }
 
     @CalledByNative
@@ -233,7 +275,7 @@
         // TODO(crbug.com/806868): Remove this method once all scripts use payment request.
         mUiDelegateHolder.performUiOperation(uiDelegate
                 -> uiDelegate.showCards(PersonalDataManager.getInstance().getCreditCardsToSuggest(
-                        true /* includeServerCards */)));
+                        /* includeServerCards= */ true)));
     }
 
     @CalledByNative
@@ -284,8 +326,8 @@
         }
 
         mUiDelegateHolder.performUiOperation(uiDelegate
-                -> uiDelegate.showDetails(
-                        new AutofillAssistantUiDelegate.Details(title, url, date, description)));
+                -> uiDelegate.showDetails(new AutofillAssistantUiDelegate.Details(
+                        title, url, date, description, /* isFinal= */ true)));
     }
 
     @CalledByNative
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 f186eeee..858f8d0 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
@@ -6,12 +6,14 @@
 
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.media.ThumbnailUtils;
 import android.os.AsyncTask;
 import android.support.annotation.Nullable;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.support.v7.content.res.AppCompatResources;
 import android.text.TextUtils;
 import android.util.TypedValue;
 import android.view.Gravity;
@@ -145,36 +147,51 @@
     /**
      * Java side equivalent of autofill_assistant::DetailsProto.
      */
-    protected static class Details {
+    static class Details {
         private final String mTitle;
         private final String mUrl;
         @Nullable
         private final Date mDate;
         private final String mDescription;
+        private final boolean mIsFinal;
 
-        public Details(String title, String url, @Nullable Date date, String description) {
+        public Details(String title, String url, @Nullable Date date, String description,
+                boolean isFinal) {
             this.mTitle = title;
             this.mUrl = url;
             this.mDate = date;
             this.mDescription = description;
+            this.mIsFinal = isFinal;
         }
 
-        public String getTitle() {
+        String getTitle() {
             return mTitle;
         }
 
-        public String getUrl() {
+        String getUrl() {
             return mUrl;
         }
 
         @Nullable
-        public Date getDate() {
+        Date getDate() {
             return mDate;
         }
 
-        public String getDescription() {
+        String getDescription() {
             return mDescription;
         }
+
+        /**
+         * Whether the details are not subject to change anymore. If set to false the animated
+         * placeholders will be displayed in place of missing data.
+         */
+        boolean isFinal() {
+            return mIsFinal;
+        }
+
+        boolean isEmpty() {
+            return mTitle.isEmpty() && mUrl.isEmpty() && mDescription.isEmpty() && mDate == null;
+        }
     }
 
     // Names borrowed from :
@@ -381,10 +398,19 @@
 
     /** Called to show contextual information. */
     public void showDetails(Details details) {
+        // TODO(crbug.com/806868): Add loading animation for placeholders if isFinal == false.
         mDetailsTitle.setText(details.getTitle());
-        mDetailsText.setText(getDetailsText(details));
+        if (!details.getTitle().isEmpty() || details.isFinal()) {
+            mDetailsTitle.setBackgroundColor(Color.WHITE);
+        }
 
-        mDetailsImage.setVisibility(View.INVISIBLE);
+        String detailsText = getDetailsText(details);
+        mDetailsText.setText(detailsText);
+        if (!detailsText.isEmpty() || details.isFinal()) {
+            mDetailsText.setBackgroundColor(Color.WHITE);
+        }
+
+        mDetailsImage.setVisibility(View.GONE);
         mDetails.setVisibility(View.VISIBLE);
         setCarouselTopPadding();
         show();
@@ -397,6 +423,10 @@
                 mDetailsImage.setImageDrawable(getRoundedImage(image));
                 mDetailsImage.setVisibility(View.VISIBLE);
             }, ignoredError -> {});
+        } else if (!details.isFinal()) {
+            mDetailsImage.setImageDrawable(AppCompatResources.getDrawable(
+                    mActivity, R.drawable.autofill_assistant_default_details));
+            mDetailsImage.setVisibility(View.VISIBLE);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentHandler.java
index 32b4de97..a0c6cafe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentHandler.java
@@ -62,4 +62,9 @@
      * @return The url of a pending navigation, if any.
      */
     @Nullable String getPendingUrl();
+
+    /**
+     * Triggers sharing of currently shown webpage similarly to the "Share" menu action.
+     */
+    void triggerSharingFlow();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java
index 3c83cae6..72f89c5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java
@@ -5,10 +5,12 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.app.PendingIntent;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.IBinder;
+import android.support.annotation.Nullable;
 import android.support.customtabs.CustomTabsService;
 import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
@@ -20,6 +22,7 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
+import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.content_public.browser.LoadUrlParams;
 
 /**
@@ -29,8 +32,15 @@
  */
 public class BrowserSessionContentUtils {
     private static final String TAG = "BrowserSession_Utils";
+    @Nullable
     private static BrowserSessionContentHandler sActiveContentHandler;
 
+    /** Extra that is passed to intent to trigger a certain action within a running activity. */
+    private static final String EXTRA_INTERNAL_ACTION =
+            "org.chromium.chrome.extra.EXTRA_INTERNAL_ACTION";
+    private static final String INTERNAL_ACTION_SHARE =
+            "org.chromium.chrome.action.INTERNAL_ACTION_SHARE";
+
     /**
      * Sets the currently active {@link BrowserSessionContentHandler} in focus.
      * @param contentHandler {@link BrowserSessionContentHandler} to set.
@@ -47,15 +57,37 @@
      *
      * @return Whether the active {@link BrowserSessionContentHandler} has handled the intent.
      */
-    public static boolean handleInActiveContentIfNeeded(Intent intent) {
-        CustomTabsSessionToken session = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
-
+    public static boolean handleBrowserServicesIntent(Intent intent) {
         String url = IntentHandler.getUrlFromIntent(intent);
         if (TextUtils.isEmpty(url)) return false;
+
+        CustomTabsSessionToken session = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
+
+        if (handleInternalIntent(intent, session)) return true;
+
+        // Must be called regardless of whether or not an external intent can be handled in active
+        // content.
         CustomTabsConnection.getInstance().onHandledIntent(session, url, intent);
 
-        if (sActiveContentHandler == null) return false;
-        if (session == null || !session.equals(sActiveContentHandler.getSession())) return false;
+        return handleExternalIntent(intent, url, session);
+    }
+
+    private static boolean handleInternalIntent(Intent intent,
+            @Nullable CustomTabsSessionToken session) {
+        if (!IntentHandler.wasIntentSenderChrome(intent)) return false;
+        if (!sessionMatchesActiveContent(session)) return false;
+
+        String internalAction = intent.getStringExtra(EXTRA_INTERNAL_ACTION);
+        if (INTERNAL_ACTION_SHARE.equals(internalAction)) {
+            sActiveContentHandler.triggerSharingFlow();
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean handleExternalIntent(Intent intent, String url,
+           @Nullable CustomTabsSessionToken session) {
+        if (!sessionMatchesActiveContent(session)) return false;
         if (sActiveContentHandler.shouldIgnoreIntent(intent)) {
             Log.w(TAG, "Incoming intent to Custom Tab was ignored.");
             return false;
@@ -65,6 +97,11 @@
         return true;
     }
 
+    private static boolean sessionMatchesActiveContent(@Nullable CustomTabsSessionToken session) {
+        return session != null && sActiveContentHandler != null &&
+                session.equals(sActiveContentHandler.getSession());
+    }
+
     /**
      * @return Whether the given session is the currently active session.
      */
@@ -151,4 +188,17 @@
         }
         return sActiveContentHandler.updateRemoteViews(remoteViews, clickableIDs, pendingIntent);
     }
+
+    /**
+     * Creates a share intent to be triggered in currently running activity.
+     * @param originalIntent - intent with which the activity was launched.
+     */
+    public static Intent createShareIntent(Context context, Intent originalIntent) {
+        Intent intent = new Intent(originalIntent)
+                .putExtra(EXTRA_INTERNAL_ACTION, INTERNAL_ACTION_SHARE)
+                // Make the new intent follow the same route as the original one
+                .setClass(context, ChromeLauncherActivity.class);
+        IntentHandler.addTrustedIntentExtras(intent);
+        return intent;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PersistentNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PersistentNotificationController.java
new file mode 100644
index 0000000..8fb5503
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PersistentNotificationController.java
@@ -0,0 +1,223 @@
+// 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.
+
+package org.chromium.chrome.browser.browserservices;
+
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+
+import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.APP_CONTEXT;
+import static org.chromium.chrome.browser.notifications.NotificationConstants.NOTIFICATION_ID_TWA_PERSISTENT;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.support.annotation.Nullable;
+import android.support.v4.app.NotificationCompat;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
+import org.chromium.chrome.browser.dependency_injection.ActivityScope;
+import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
+import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
+import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
+import org.chromium.chrome.browser.preferences.Preferences;
+import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Publishes and dismisses the notification when running Trusted Web Activities. The notification
+ * offers to manage site data and to share info about it.
+ *
+ * The notification is shown while the activity is in started state. It is not removed when the user
+ * leaves the origin associated with the app by following links.
+ */
+@ActivityScope
+public class PersistentNotificationController implements StartStopWithNativeObserver, Destroyable {
+    private final Context mAppContext;
+    private final CustomTabIntentDataProvider mIntentDataProvider;
+
+    @Nullable
+    private String mVerifiedPackage;
+    @Nullable
+    private Origin mVerifiedOrigin;
+    private boolean mStarted;
+
+    @Nullable
+    private Handler mHandler;
+
+    @Inject
+    public PersistentNotificationController(@Named(APP_CONTEXT) Context context,
+            ActivityLifecycleDispatcher lifecycleDispatcher,
+            CustomTabIntentDataProvider intentDataProvider) {
+        mAppContext = context;
+        mIntentDataProvider = intentDataProvider;
+        lifecycleDispatcher.register(this);
+    }
+
+    @Override
+    public void onStartWithNative() {
+        mStarted = true;
+        if (mVerifiedPackage != null) {
+            publish();
+        }
+    }
+
+    @Override
+    public void onStopWithNative() {
+        mStarted = false;
+        dismiss();
+    }
+
+    @Override
+    public void destroy() {
+        killBackgroundThread();
+    }
+
+    /**
+     * Called when the relationship between an origin and an app with given package name has been
+     * verified.
+     */
+    public void onOriginVerifiedForPackage(Origin origin, String packageName) {
+        if (packageName.equals(mVerifiedPackage)) {
+            return;
+        }
+        mVerifiedPackage = packageName;
+        mVerifiedOrigin = origin;
+        if (mStarted) {
+            publish();
+        }
+    }
+
+    private void publish() {
+        postToBackgroundThread(new PublishTask(
+                mVerifiedPackage, mVerifiedOrigin, mAppContext, mIntentDataProvider.getIntent()));
+    }
+
+    private void dismiss() {
+        postToBackgroundThread(new DismissTask(mAppContext, mVerifiedPackage));
+    }
+
+    private void postToBackgroundThread(Runnable task) {
+        if (mHandler == null) {
+            HandlerThread backgroundThread = new HandlerThread("TwaPersistentNotification");
+            backgroundThread.start();
+            mHandler = new Handler(backgroundThread.getLooper());
+        }
+        mHandler.post(task);
+    }
+
+    private void killBackgroundThread() {
+        if (mHandler == null) {
+            return;
+        }
+        Looper looper = mHandler.getLooper();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            looper.quitSafely();
+        } else {
+            looper.quit();
+        }
+    }
+
+    private static class PublishTask implements Runnable {
+        private final String mPackageName;
+        private final Origin mOrigin;
+        private final Context mAppContext;
+        private final Intent mCustomTabsIntent;
+
+        private PublishTask(String packageName, Origin origin, Context appContext,
+                Intent customTabsIntent) {
+            mPackageName = packageName;
+            mOrigin = origin;
+            mAppContext = appContext;
+            mCustomTabsIntent = customTabsIntent;
+        }
+
+        @Override
+        public void run() {
+            Notification notification = createNotification();
+            NotificationManager nm = (NotificationManager) mAppContext.getSystemService(
+                    Context.NOTIFICATION_SERVICE);
+            if (nm != null) {
+                nm.notify(mPackageName, NOTIFICATION_ID_TWA_PERSISTENT, notification);
+            }
+        }
+
+        private Notification createNotification() {
+            return NotificationBuilderFactory
+                    .createChromeNotificationBuilder(true /* preferCompat */,
+                            ChannelDefinitions.ChannelId.BROWSER)
+                    .setSmallIcon(R.drawable.ic_chrome)
+                    .setContentTitle(makeTitle())
+                    .setContentText(
+                            mAppContext.getString(R.string.app_running_in_chrome_disclosure))
+                    .setAutoCancel(false)
+                    .setOngoing(true)
+                    .setPriorityBeforeO(NotificationCompat.PRIORITY_LOW)
+                    .addAction(0 /* icon */, // TODO(pshmakov): set the icons.
+                            mAppContext.getString(R.string.share),
+                            makeShareIntent())
+                    .addAction(0 /* icon */,
+                            mAppContext.getString(R.string.twa_manage_data),
+                            makeManageDataIntent())
+                    .build();
+        }
+
+        private String makeTitle() {
+            PackageManager packageManager = mAppContext.getPackageManager();
+            try {
+                return packageManager
+                        .getApplicationLabel(packageManager.getApplicationInfo(mPackageName, 0))
+                        .toString();
+            } catch (PackageManager.NameNotFoundException e) {
+                assert false : mPackageName + " not found";
+                return "";
+            }
+        }
+
+        private PendingIntent makeManageDataIntent() {
+            Intent settingsIntent = PreferencesLauncher.createIntentForSettingsPage(
+                    mAppContext, SingleWebsitePreferences.class.getName());
+            settingsIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS,
+                    SingleWebsitePreferences.createFragmentArgsForSite(mOrigin.toString()));
+            return PendingIntent.getActivity(mAppContext, 0, settingsIntent, FLAG_UPDATE_CURRENT);
+        }
+
+        private PendingIntent makeShareIntent() {
+            Intent shareIntent =
+                    BrowserSessionContentUtils.createShareIntent(mAppContext, mCustomTabsIntent);
+            return PendingIntent.getActivity(mAppContext, 0, shareIntent, FLAG_UPDATE_CURRENT);
+        }
+    }
+
+    private static class DismissTask implements Runnable {
+        private final Context mAppContext;
+        private final String mPackageName;
+
+        private DismissTask(Context appContext, String packageName) {
+            mAppContext = appContext;
+            mPackageName = packageName;
+        }
+
+        @Override
+        public void run() {
+            NotificationManager nm = (NotificationManager) mAppContext.getSystemService(
+                    Context.NOTIFICATION_SERVICE);
+            if (nm != null) {
+                nm.cancel(mPackageName, NOTIFICATION_ID_TWA_PERSISTENT);
+            }
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUi.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUi.java
index 650533c..6adf29b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUi.java
@@ -47,6 +47,7 @@
     private final CustomTabIntentDataProvider mIntentDataProvider;
     private final ActivityTabProvider mActivityTabProvider;
     private final CustomTabBrowserControlsVisibilityDelegate mControlsVisibilityDelegate;
+    private final PersistentNotificationController mNotificationController;
 
     private boolean mInTrustedWebActivity = true;
 
@@ -69,8 +70,7 @@
             Origin origin = new Origin(url);
             boolean verified =
                     OriginVerifier.isValidOrigin(packageName, origin, RELATIONSHIP);
-            if (verified) registerClientAppData(packageName, origin);
-            setTrustedWebActivityMode(verified);
+            handleVerificationResult(verified, packageName, origin);
         }
     };
 
@@ -81,7 +81,8 @@
             CustomTabsConnection customTabsConnection,
             ActivityLifecycleDispatcher lifecycleDispatcher,
             TabObserverRegistrar tabObserverRegistrar, ActivityTabProvider activityTabProvider,
-            CustomTabBrowserControlsVisibilityDelegate controlsVisibilityDelegate) {
+            CustomTabBrowserControlsVisibilityDelegate controlsVisibilityDelegate,
+            PersistentNotificationController notificationController) {
         mFullscreenManager = fullscreenManager;
         mClientAppDataRecorder = clientAppDataRecorder;
         mDisclosure = disclosure;
@@ -89,6 +90,7 @@
         mIntentDataProvider = intentDataProvider;
         mActivityTabProvider = activityTabProvider;
         mControlsVisibilityDelegate = controlsVisibilityDelegate;
+        mNotificationController = notificationController;
         tabObserverRegistrar.registerTabObserver(mVerifyOnPageLoadObserver);
         lifecycleDispatcher.register(this);
     }
@@ -127,11 +129,18 @@
             if (!origin.equals(new Origin(tab.getUrl()))) return;
 
             BrowserServicesMetrics.recordTwaOpened();
-            if (verified) registerClientAppData(packageName, origin);
-            setTrustedWebActivityMode(verified);
+            handleVerificationResult(verified, packageName, origin);
         }, packageName, RELATIONSHIP).start(origin);
     }
 
+    private void handleVerificationResult(boolean verified, String packageName, Origin origin) {
+        if (verified) {
+            registerClientAppData(packageName, origin);
+            mNotificationController.onOriginVerifiedForPackage(origin, packageName);
+        }
+        setTrustedWebActivityMode(verified);
+    }
+
     @Override
     public void onPreInflationStartup() {}
 
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 be97435..15ecb54 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
@@ -84,6 +84,7 @@
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.page_info.PageInfoController;
 import org.chromium.chrome.browser.rappor.RapporServiceBridge;
+import org.chromium.chrome.browser.share.ShareMenuActionHandler;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
@@ -114,6 +115,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
 
 /**
  * The activity for custom tabs. It will be launched on top of a client's task.
@@ -404,7 +406,8 @@
 
     public void setTopBarContentView(View view) {
         mTopBarDelegate.setTopBarContentView(view);
-        mTopBarDelegate.showTopBarIfNecessary();
+        mTopBarDelegate.showTopBarIfNecessary(
+                isModuleManagedUrl(mIntentDataProvider.getUrlToLoad()));
     }
 
     @Override
@@ -610,6 +613,12 @@
                         .getPendingEntry();
                 return entry != null ? entry.getUrl() : null;
             }
+
+            @Override
+            public void triggerSharingFlow() {
+                ShareMenuActionHandler.getInstance().onShareMenuItemSelected(CustomTabActivity.this,
+                        getActivityTab(), false /* shareDirectly */, false /* isIncognito */);
+            }
         };
 
         maybeLoadModule();
@@ -787,6 +796,7 @@
             public void onUrlUpdated(Tab tab) {
                 // Update the color on every new URL.
                 updateColor(tab);
+                mTopBarDelegate.showTopBarIfNecessary(isModuleManagedUrl(tab.getUrl()));
             }
 
             /**
@@ -1503,4 +1513,9 @@
         return ChromeApplication.getComponent().createCustomTabActivityComponent(
                 commonsModule, contextualSuggestionsModule, customTabsModule);
     }
+
+    private boolean isModuleManagedUrl(String url) {
+        Pattern urlsPattern = mIntentDataProvider.getExtraModuleManagedUrlsPattern();
+        return !TextUtils.isEmpty(url) && urlsPattern != null && urlsPattern.matcher(url).matches();
+    }
 }
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 57bfc44..348f477 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
@@ -47,6 +47,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Pattern;
 
 /**
  * A model class that parses the incoming intent for Custom Tabs specific customization data.
@@ -128,6 +129,10 @@
     public static final String EXTRA_SEND_TO_EXTERNAL_DEFAULT_HANDLER =
             "android.support.customtabs.extra.SEND_TO_EXTERNAL_HANDLER";
 
+    /** Extra that defines the module managed URLs regex. */
+    public static final String EXTRA_MODULE_MANAGED_URLS_REGEX =
+            "org.chromium.chrome.browser.customtabs.EXTRA_MODULE_MANAGED_URLS_REGEX";
+
     /** The APK package to load the module from. */
     private static final String EXTRA_MODULE_PACKAGE_NAME =
             "org.chromium.chrome.browser.customtabs.EXTRA_MODULE_PACKAGE_NAME";
@@ -177,6 +182,8 @@
     private PendingIntent mRemoteViewsPendingIntent;
     // OnFinished listener for PendingIntents. Used for testing only.
     private PendingIntent.OnFinished mOnFinished;
+    @Nullable
+    private Pattern mModuleManagedUrlsPattern;
 
     /** Whether this CustomTabActivity was explicitly started by another Chrome Activity. */
     private final boolean mIsOpenedByChrome;
@@ -289,6 +296,13 @@
         } else {
             mModuleComponentName = null;
         }
+        String moduleManagedUrlsRegex =
+                IntentUtils.safeGetStringExtra(intent, EXTRA_MODULE_MANAGED_URLS_REGEX);
+        if (moduleManagedUrlsRegex != null) {
+            mModuleManagedUrlsPattern = Pattern.compile(moduleManagedUrlsRegex);
+        } else {
+            mModuleManagedUrlsPattern = null;
+        }
     }
 
     private boolean isIncognitoForPaymentsFlow(Intent intent) {
@@ -762,4 +776,21 @@
     ComponentName getModuleComponentName() {
         return mModuleComponentName;
     }
+
+    /**
+     * See {@link #EXTRA_MODULE_MANAGED_URLS_REGEX}.
+     * @return The pattern compiled from the regex that defines the module managed URLs,
+     * or null if not specified.
+     */
+    @Nullable
+    Pattern getExtraModuleManagedUrlsPattern() {
+        return mModuleManagedUrlsPattern;
+    }
+
+    /**
+     * @return the Intent this instance was created with.
+     */
+    public Intent getIntent() {
+        return mIntent;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTopBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTopBarDelegate.java
index 2802de7..23007fe4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTopBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTopBarDelegate.java
@@ -26,12 +26,15 @@
     }
 
     /**
-     * Makes the top bar area to show, if any.
+     * Adds the top bar, if any, to the view hierarchy and updates its visibility.
      */
-    public void showTopBarIfNecessary() {
+    public void showTopBarIfNecessary(boolean isVisible) {
         if (mTopBarContentView != null && mTopBarContentView.getParent() == null) {
             getTopBarView().addView(mTopBarContentView);
         }
+        if (mTopBarContentView != null) {
+            mTopBarContentView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index 4305081e..c9258a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -39,7 +39,6 @@
 import org.chromium.base.Log;
 import org.chromium.base.SysUtils;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.blink.mojom.MediaSessionAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -49,6 +48,7 @@
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
 import org.chromium.content_public.common.MediaMetadata;
+import org.chromium.media_session.mojom.MediaSessionAction;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -69,7 +69,7 @@
     static final int MINIMAL_MEDIA_IMAGE_SIZE_PX = 114;
 
     @VisibleForTesting
-    static final int CUSTOM_MEDIA_SESSION_ACTION_STOP = MediaSessionAction.LAST + 1;
+    static final int CUSTOM_MEDIA_SESSION_ACTION_STOP = MediaSessionAction.MAX_VALUE + 1;
 
     // The media artwork image resolution on high-end devices.
     private static final int HIGH_IMAGE_SIZE_PX = 512;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java
index aaa71f4..0a509bb0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java
@@ -15,7 +15,6 @@
 import org.chromium.base.Log;
 import org.chromium.base.SysUtils;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.blink.mojom.MediaSessionAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
 import org.chromium.chrome.browser.metrics.MediaNotificationUma;
@@ -29,6 +28,7 @@
 import org.chromium.content_public.browser.MediaSessionObserver;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.common.MediaMetadata;
+import org.chromium.media_session.mojom.MediaSessionAction;
 import org.chromium.ui.base.WindowAndroid;
 
 import java.net.URI;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
index 09a65858..8b0e7de5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
@@ -69,6 +69,12 @@
     public static final int NOTIFICATION_ID_WEBAPP_ACTIONS = 5;
 
     /**
+     * Unique identifier for the persistent notification displayed while a Trusted Web Activity is
+     * in foreground.
+     */
+    public static final int NOTIFICATION_ID_TWA_PERSISTENT = 6;
+
+    /**
      * Unique identifier for the summary notification for downloads.  Using the ID this summary was
      * going to have before it was migrated here.
      * TODO(dtrainor): Clean up this ID and make sure it's in line with existing id counters without
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 60b54c4..fbb99a6f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -167,11 +167,6 @@
                     }
 
                     @Override
-                    public boolean isIncognito() {
-                        return mToolbarDataProvider.isIncognito();
-                    }
-
-                    @Override
                     public WindowDelegate getWindowDelegate() {
                         return mWindowDelegate;
                     }
@@ -371,7 +366,6 @@
      * @return Whether the URL focus change is taking place, e.g. a focus animation is running on
      *         a phone device.
      */
-    @Override
     public boolean isUrlFocusChangeInProgress() {
         return mUrlFocusChangeInProgress;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index 50dc5eb..66bc0ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -19,10 +19,12 @@
 import android.widget.ListView;
 
 import org.chromium.base.Log;
-import org.chromium.base.StrictModeContext;
+import org.chromium.base.Supplier;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
@@ -31,11 +33,11 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteMediator.OmniboxSuggestionDelegate;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsList.OmniboxSuggestionListEmbedder;
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionListViewBinder.SuggestionListViewHolder;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.util.KeyNavigationUtil;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.PageTransition;
 
 import java.util.ArrayList;
@@ -55,21 +57,19 @@
     private final Context mContext;
     private final ViewGroup mParent;
     private final AutocompleteDelegate mDelegate;
-    private final OmniboxSuggestionListEmbedder mSuggestionListEmbedder;
     private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
     private final AutocompleteMediator mMediator;
+    private final PropertyModel mListModel;
+    private final SuggestionListViewHolder mListViewHolder;
 
     private final OmniboxResultsAdapter mSuggestionListAdapter;
     private final AnswersImageFetcher mAnswersImageFetcher;
     private final List<Runnable> mDeferredNativeRunnables = new ArrayList<Runnable>();
 
     private ToolbarDataProvider mToolbarDataProvider;
-    private OmniboxSuggestionsList mSuggestionList;
     private boolean mNativeInitialized;
     private AutocompleteController mAutocomplete;
-    private boolean mSuggestionsShown;
     private boolean mSuggestionModalShown;
-    private ViewGroup mOmniboxResultsContainer;
     private float mMaxRequiredWidth;
     private float mMaxMatchContentsWidth;
     private boolean mCanShowSuggestions;
@@ -145,11 +145,6 @@
          * @return Whether the URL currently has focus.
          */
         boolean isUrlBarFocused();
-
-        /**
-         * @return Whether a URL focus change animation is currently in progress.
-         */
-        boolean isUrlFocusChangeInProgress();
     }
 
     /**
@@ -167,162 +162,27 @@
         mParent = parent;
         mContext = parent.getContext();
         mDelegate = delegate;
-        mSuggestionListEmbedder = listEmbedder;
         mUrlBarEditingTextProvider = urlBarEditingTextProvider;
 
         mAnswersImageFetcher = new AnswersImageFetcher();
         mSuggestionListAdapter = new OmniboxResultsAdapter(mContext);
+
+        Supplier<ViewStub> containerStubSupplier = () -> {
+            return (ViewStub) mParent.getRootView().findViewById(
+                    R.id.omnibox_results_container_stub);
+        };
+        mListViewHolder = new SuggestionListViewHolder(
+                mContext, containerStubSupplier, mSuggestionListAdapter);
+        mListModel = new PropertyModel(SuggestionListProperties.ALL_KEYS);
+        // TODO(tedchoc): Investigate replacing with LazyConstructionPropertyMcp to simplify the
+        //                view binder handling property updates when the view hasn't been created
+        //                yet.
+        PropertyModelChangeProcessor.create(
+                mListModel, mListViewHolder, SuggestionListViewBinder::bind);
+        mListModel.set(SuggestionListProperties.EMBEDDER, listEmbedder);
+
         mAutocomplete = new AutocompleteController(this);
-        mMediator = new AutocompleteMediator(
-                mContext, urlBarEditingTextProvider, mSuggestionListAdapter::updateSuggestions);
-    }
-
-    @Override
-    public void onUrlFocusChange(boolean hasFocus) {
-        if (hasFocus) {
-            if (mNativeInitialized) {
-                startZeroSuggest();
-            } else {
-                mDeferredNativeRunnables.add(() -> {
-                    if (TextUtils.isEmpty(mUrlBarEditingTextProvider.getTextWithAutocomplete())) {
-                        startZeroSuggest();
-                    }
-                });
-            }
-        } else {
-            // Prevent any upcoming omnibox suggestions from showing once a URL is loaded (and as
-            // a consequence the omnibox is unfocused).
-            stopAutocomplete(true);
-
-            mCanShowSuggestions = false;
-            mHasStartedNewOmniboxEditSession = false;
-            mNewOmniboxEditSessionTimestamp = -1;
-            hideSuggestions();
-            mAnswersImageFetcher.clearCache();
-        }
-    }
-
-    @Override
-    public void onUrlAnimationFinished(boolean hasFocus) {
-        mCanShowSuggestions = hasFocus;
-        updateOmniboxSuggestionsVisibility();
-    }
-
-    /**
-     * Provides data and state for the toolbar component.
-     * @param toolbarDataProvider The data provider.
-     */
-    public void setToolbarDataProvider(ToolbarDataProvider toolbarDataProvider) {
-        mToolbarDataProvider = toolbarDataProvider;
-        mMediator.setToolbarDataProvider(toolbarDataProvider);
-    }
-
-    /**
-     * Updates the profile used for generating autocomplete suggestions.
-     * @param profile The profile to be used.
-     */
-    public void setAutocompleteProfile(Profile profile) {
-        mAutocomplete.setProfile(profile);
-    }
-
-    /**
-     * Whether omnibox autocomplete should currently be prevented from generating suggestions.
-     */
-    public void setShouldPreventOmniboxAutocomplete(boolean prevent) {
-        mShouldPreventOmniboxAutocomplete = prevent;
-    }
-
-    /**
-     * @return The number of current autocomplete suggestions.
-     */
-    public int getSuggestionCount() {
-        return mMediator.getSuggestionCount();
-    }
-
-    /**
-     * Retrieve the omnibox suggestion at the specified index.  The index represents the ordering
-     * in the underlying model.  The index does not represent visibility due to the current scroll
-     * position of the list.
-     *
-     * @param index The index of the suggestion to fetch.
-     * @return The suggestion at the given index.
-     */
-    public OmniboxSuggestion getSuggestionAt(int index) {
-        return mMediator.getSuggestionAt(index);
-    }
-
-    /**
-     * Signals that native initialization has completed.
-     */
-    public void onNativeInitialized() {
-        mNativeInitialized = true;
-
-        for (Runnable deferredRunnable : mDeferredNativeRunnables) {
-            mParent.post(deferredRunnable);
-        }
-        mDeferredNativeRunnables.clear();
-    }
-
-    /**
-     * @return The suggestion list popup containing the omnibox results (or null if it has not yet
-     *         been created).
-     */
-    @VisibleForTesting
-    public OmniboxSuggestionsList getSuggestionList() {
-        return mSuggestionList;
-    }
-
-    /**
-     * @return Whether the suggestions list is currently visible.
-     */
-    public boolean isSuggestionsListShown() {
-        return mSuggestionsShown;
-    }
-
-    /**
-     * @return Whether a modal dialog triggered from the suggestions is currently visible.
-     */
-    public boolean isSuggestionModalShown() {
-        return mSuggestionModalShown;
-    }
-
-    /**
-     * @see AutocompleteController#onVoiceResults(List)
-     */
-    public void onVoiceResults(
-            @Nullable List<LocationBarVoiceRecognitionHandler.VoiceResult> results) {
-        mAutocomplete.onVoiceResults(results);
-    }
-
-    /**
-     * @return The current native pointer to the autocomplete results.
-     */
-    // TODO(tedchoc): Figure out how to remove this.
-    public long getCurrentNativeAutocompleteResult() {
-        return mAutocomplete.getCurrentNativeAutocompleteResult();
-    }
-
-    /**
-     * Initiates the mSuggestionListPopup.  Done on demand to not slow down the initial inflation of
-     * the location bar.
-     */
-    private void initSuggestionList() {
-        // Only called from onSuggestionsReceived(), which is a callback from a listener set up by
-        // onNativeLibraryReady(), so this assert is safe.
-        assert mNativeInitialized
-                || mShowCachedZeroSuggestResults
-            : "Trying to initialize native suggestions list before native init";
-        if (mSuggestionList != null) return;
-
-        // TODO(tedchoc): Investigate lazily building the suggestion list off of the UI thread.
-        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
-            mSuggestionList = new OmniboxSuggestionsList(mContext, mSuggestionListEmbedder);
-        }
-
-        // Start with visibility GONE to ensure that show() is called. http://crbug.com/517438
-        mSuggestionList.setVisibility(View.GONE);
-        mSuggestionList.setAdapter(mSuggestionListAdapter);
-        mSuggestionList.setClipToPadding(false);
+        mMediator = new AutocompleteMediator(mContext, urlBarEditingTextProvider, mListModel);
         mMediator.setSuggestionDelegate(new OmniboxSuggestionDelegate() {
             private long mLastActionUpTimestamp;
 
@@ -437,6 +297,131 @@
         });
     }
 
+    @Override
+    public void onUrlFocusChange(boolean hasFocus) {
+        if (hasFocus) {
+            if (mNativeInitialized) {
+                startZeroSuggest();
+            } else {
+                mDeferredNativeRunnables.add(() -> {
+                    if (TextUtils.isEmpty(mUrlBarEditingTextProvider.getTextWithAutocomplete())) {
+                        startZeroSuggest();
+                    }
+                });
+            }
+        } else {
+            // Prevent any upcoming omnibox suggestions from showing once a URL is loaded (and as
+            // a consequence the omnibox is unfocused).
+            stopAutocomplete(true);
+
+            mCanShowSuggestions = false;
+            mHasStartedNewOmniboxEditSession = false;
+            mNewOmniboxEditSessionTimestamp = -1;
+            hideSuggestions();
+            mAnswersImageFetcher.clearCache();
+        }
+    }
+
+    @Override
+    public void onUrlAnimationFinished(boolean hasFocus) {
+        mCanShowSuggestions = hasFocus;
+        updateOmniboxSuggestionsVisibility();
+    }
+
+    /**
+     * Provides data and state for the toolbar component.
+     * @param toolbarDataProvider The data provider.
+     */
+    public void setToolbarDataProvider(ToolbarDataProvider toolbarDataProvider) {
+        mToolbarDataProvider = toolbarDataProvider;
+        mMediator.setToolbarDataProvider(toolbarDataProvider);
+    }
+
+    /**
+     * Updates the profile used for generating autocomplete suggestions.
+     * @param profile The profile to be used.
+     */
+    public void setAutocompleteProfile(Profile profile) {
+        mAutocomplete.setProfile(profile);
+    }
+
+    /**
+     * Whether omnibox autocomplete should currently be prevented from generating suggestions.
+     */
+    public void setShouldPreventOmniboxAutocomplete(boolean prevent) {
+        mShouldPreventOmniboxAutocomplete = prevent;
+    }
+
+    /**
+     * @return The number of current autocomplete suggestions.
+     */
+    public int getSuggestionCount() {
+        return mMediator.getSuggestionCount();
+    }
+
+    /**
+     * Retrieve the omnibox suggestion at the specified index.  The index represents the ordering
+     * in the underlying model.  The index does not represent visibility due to the current scroll
+     * position of the list.
+     *
+     * @param index The index of the suggestion to fetch.
+     * @return The suggestion at the given index.
+     */
+    public OmniboxSuggestion getSuggestionAt(int index) {
+        return mMediator.getSuggestionAt(index);
+    }
+
+    /**
+     * Signals that native initialization has completed.
+     */
+    public void onNativeInitialized() {
+        mNativeInitialized = true;
+
+        for (Runnable deferredRunnable : mDeferredNativeRunnables) {
+            mParent.post(deferredRunnable);
+        }
+        mDeferredNativeRunnables.clear();
+    }
+
+    /**
+     * @return The suggestion list popup containing the omnibox results (or null if it has not yet
+     *         been created).
+     */
+    @VisibleForTesting
+    public OmniboxSuggestionsList getSuggestionList() {
+        return mListViewHolder.mListView;
+    }
+
+    /**
+     * @return Whether the suggestions list is currently visible.
+     */
+    public boolean isSuggestionsListShown() {
+        return mListModel.get(SuggestionListProperties.VISIBLE);
+    }
+
+    /**
+     * @return Whether a modal dialog triggered from the suggestions is currently visible.
+     */
+    public boolean isSuggestionModalShown() {
+        return mSuggestionModalShown;
+    }
+
+    /**
+     * @see AutocompleteController#onVoiceResults(List)
+     */
+    public void onVoiceResults(
+            @Nullable List<LocationBarVoiceRecognitionHandler.VoiceResult> results) {
+        mAutocomplete.onVoiceResults(results);
+    }
+
+    /**
+     * @return The current native pointer to the autocomplete results.
+     */
+    // TODO(tedchoc): Figure out how to remove this.
+    public long getCurrentNativeAutocompleteResult() {
+        return mAutocomplete.getCurrentNativeAutocompleteResult();
+    }
+
     /**
      * Updates the maximum widths required to render the suggestions.
      * This is needed for infinite suggestions where we try to vertically align the leading
@@ -447,46 +432,15 @@
         mMaxMatchContentsWidth = 0;
     }
 
-    private void initOmniboxResultsContainer() {
-        if (mOmniboxResultsContainer != null) return;
-        ViewStub overlayStub =
-                (ViewStub) mParent.getRootView().findViewById(R.id.omnibox_results_container_stub);
-        mOmniboxResultsContainer = (ViewGroup) overlayStub.inflate();
-    }
-
     /**
      * Update whether the omnibox suggestions are visible.
      */
     private void updateOmniboxSuggestionsVisibility() {
-        initOmniboxResultsContainer();
-        if (mSuggestionList == null) return;
-
-        boolean isContainerVisible = mOmniboxResultsContainer.getVisibility() == View.VISIBLE;
         boolean shouldBeVisible = mCanShowSuggestions && getSuggestionCount() > 0;
-        if (isContainerVisible == shouldBeVisible) return;
-
-        mSuggestionsShown = shouldBeVisible;
-
-        final boolean isListVisible = mSuggestionList.getVisibility() == View.VISIBLE;
-        if (shouldBeVisible && !isListVisible) {
+        boolean wasVisible = mListModel.get(SuggestionListProperties.VISIBLE);
+        mListModel.set(SuggestionListProperties.VISIBLE, shouldBeVisible);
+        if (shouldBeVisible && !wasVisible) {
             mIgnoreOmniboxItemSelection = true; // Reset to default value.
-
-            if (mSuggestionList.getParent() == null) {
-                mOmniboxResultsContainer.addView(mSuggestionList);
-            }
-
-            mSuggestionList.show();
-            updateSuggestionListLayoutDirection();
-        } else if (!shouldBeVisible && isListVisible) {
-            mSuggestionList.setVisibility(View.GONE);
-
-            UiUtils.removeViewFromParent(mSuggestionList);
-        }
-
-        if (shouldBeVisible) {
-            mOmniboxResultsContainer.setVisibility(View.VISIBLE);
-        } else {
-            mOmniboxResultsContainer.setVisibility(View.INVISIBLE);
         }
     }
 
@@ -505,10 +459,11 @@
      * @return Whether the key event was handled.
      */
     public boolean handleKeyEvent(int keyCode, KeyEvent event) {
-        if (KeyNavigationUtil.isGoDown(event) && mSuggestionList != null
-                && mSuggestionList.isShown()) {
+        OmniboxSuggestionsList suggestionList = mListViewHolder.mListView;
+        if (KeyNavigationUtil.isGoDown(event) && suggestionList != null
+                && suggestionList.isShown()) {
             int suggestionCount = mSuggestionListAdapter.getCount();
-            if (mSuggestionList.getSelectedItemPosition() < suggestionCount - 1) {
+            if (suggestionList.getSelectedItemPosition() < suggestionCount - 1) {
                 if (suggestionCount > 0) mIgnoreOmniboxItemSelection = false;
             } else {
                 // Do not pass down events when the last item is already selected as it will
@@ -516,32 +471,32 @@
                 return true;
             }
 
-            if (mSuggestionList.getSelectedItemPosition() == ListView.INVALID_POSITION) {
+            if (suggestionList.getSelectedItemPosition() == ListView.INVALID_POSITION) {
                 // When clearing the selection after a text change, state is not reset
                 // correctly so hitting down again will cause it to start from the previous
                 // selection point. We still have to send the key down event to let the list
                 // view items take focus, but then we select the first item explicitly.
-                boolean result = mSuggestionList.onKeyDown(keyCode, event);
-                mSuggestionList.setSelection(0);
+                boolean result = suggestionList.onKeyDown(keyCode, event);
+                suggestionList.setSelection(0);
                 return result;
             } else {
-                return mSuggestionList.onKeyDown(keyCode, event);
+                return suggestionList.onKeyDown(keyCode, event);
             }
-        } else if (KeyNavigationUtil.isGoUp(event) && mSuggestionList != null
-                && mSuggestionList.isShown()) {
-            if (mSuggestionList.getSelectedItemPosition() != 0
+        } else if (KeyNavigationUtil.isGoUp(event) && suggestionList != null
+                && suggestionList.isShown()) {
+            if (suggestionList.getSelectedItemPosition() != 0
                     && mSuggestionListAdapter.getCount() > 0) {
                 mIgnoreOmniboxItemSelection = false;
             }
-            return mSuggestionList.onKeyDown(keyCode, event);
-        } else if (KeyNavigationUtil.isGoRight(event) && mSuggestionList != null
-                && mSuggestionList.isShown()
-                && mSuggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
+            return suggestionList.onKeyDown(keyCode, event);
+        } else if (KeyNavigationUtil.isGoRight(event) && suggestionList != null
+                && suggestionList.isShown()
+                && suggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
             OmniboxSuggestion suggestion =
-                    mMediator.getSuggestionAt(mSuggestionList.getSelectedItemPosition());
+                    mMediator.getSuggestionAt(suggestionList.getSelectedItemPosition());
             mDelegate.setOmniboxEditingText(suggestion.getFillIntoEdit());
             onTextChangedForAutocomplete();
-            mSuggestionList.setSelection(0);
+            suggestionList.setSelection(0);
             return true;
         } else if (KeyNavigationUtil.isEnter(event) && mParent.getVisibility() == View.VISIBLE) {
             mDelegate.hideKeyboard();
@@ -562,11 +517,12 @@
         OmniboxSuggestion suggestionMatch;
         boolean skipOutOfBoundsCheck = false;
 
-        if (mSuggestionList != null && mSuggestionList.isShown()
-                && mSuggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
+        ListView suggestionList = mListViewHolder.mListView;
+        if (suggestionList != null && suggestionList.isShown()
+                && suggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
             // Bluetooth keyboard case: the user highlighted a suggestion with the arrow
             // keys, then pressed enter.
-            suggestionMatchPosition = mSuggestionList.getSelectedItemPosition();
+            suggestionMatchPosition = suggestionList.getSelectedItemPosition();
             suggestionMatch = mMediator.getSuggestionAt(suggestionMatchPosition);
         } else if (mMediator.getSuggestionCount() > 0
                 && urlText.equals(mUrlTextAfterSuggestionsReceived)) {
@@ -664,8 +620,8 @@
             mHasStartedNewOmniboxEditSession = true;
         }
 
-        if (!mParent.isInTouchMode() && mSuggestionList != null) {
-            mSuggestionList.setSelection(0);
+        if (!mParent.isInTouchMode() && mListViewHolder.mListView != null) {
+            mListViewHolder.mListView.setSelection(0);
         }
 
         stopAutocomplete(false);
@@ -734,10 +690,12 @@
         mUrlTextAfterSuggestionsReceived = userText + inlineAutocompleteText;
 
         // Show the suggestion list.
-        initSuggestionList(); // It may not have been initialized yet.
         resetMaxTextWidths();
         mMediator.onSuggestionsReceived(newSuggestions, inlineAutocompleteText);
-        if (mSuggestionsShown && mMediator.getSuggestionCount() == 0) hideSuggestions();
+        if (mListModel.get(SuggestionListProperties.VISIBLE)
+                && mMediator.getSuggestionCount() == 0) {
+            hideSuggestions();
+        }
         mDelegate.onSuggestionsChanged(inlineAutocompleteText);
 
         updateOmniboxSuggestionsVisibility();
@@ -830,9 +788,6 @@
      * @param useDarkColors Whether dark colors should be applied to the UI.
      */
     public void updateVisualsForState(boolean useDarkColors) {
-        if (mSuggestionList != null) {
-            mSuggestionList.refreshPopupBackground();
-        }
         mMediator.setUseDarkColors(useDarkColors);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 2681dea..50ab910 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -15,7 +15,6 @@
 import android.view.View;
 import android.widget.TextView;
 
-import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
@@ -42,7 +41,7 @@
 class AutocompleteMediator implements OnSuggestionsReceivedListener {
     private final Context mContext;
     private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
-    private final Callback<List<PropertyModel>> mModelsChangedCallback;
+    private final PropertyModel mListPropertyModel;
     private final List<Pair<OmniboxSuggestion, PropertyModel>> mCurrentModels;
     private final Map<String, List<PropertyModel>> mPendingAnswerRequestUrls;
     private final AnswersImageFetcher mImageFetcher;
@@ -54,10 +53,10 @@
     private boolean mPreventSuggestionListPropertyChanges;
 
     public AutocompleteMediator(Context context, UrlBarEditingTextStateProvider textProvider,
-            Callback<List<PropertyModel>> onModelsChanged) {
+            PropertyModel listPropertyModel) {
         mContext = context;
         mUrlBarEditingTextProvider = textProvider;
-        mModelsChangedCallback = onModelsChanged;
+        mListPropertyModel = listPropertyModel;
         mCurrentModels = new ArrayList<>();
         mPendingAnswerRequestUrls = new HashMap<>();
         mImageFetcher = new AnswersImageFetcher();
@@ -113,7 +112,7 @@
         if (mPreventSuggestionListPropertyChanges) return;
         List<PropertyModel> models = new ArrayList<>(mCurrentModels.size());
         for (int i = 0; i < mCurrentModels.size(); i++) models.add(mCurrentModels.get(i).second);
-        mModelsChangedCallback.onResult(models);
+        mListPropertyModel.set(SuggestionListProperties.SUGGESTION_MODELS, models);
     }
 
     private void populateModelForSuggestion(
@@ -425,6 +424,7 @@
      */
     public void setUseDarkColors(boolean useDarkColors) {
         mUseDarkColors = useDarkColors;
+        mListPropertyModel.set(SuggestionListProperties.USE_DARK_BACKGROUND, !useDarkColors);
         for (int i = 0; i < mCurrentModels.size(); i++) {
             PropertyModel model = mCurrentModels.get(i).second;
             model.set(SuggestionViewProperties.USE_DARK_COLORS, mUseDarkColors);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java
index cbe3e6f89..fb2cc00e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java
@@ -8,7 +8,6 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
 import android.support.v4.view.ViewCompat;
 import android.view.View;
@@ -29,18 +28,17 @@
  */
 @VisibleForTesting
 public class OmniboxSuggestionsList extends ListView {
-    private static final int OMNIBOX_RESULTS_BG_COLOR = 0xFFFFFFFF;
-    private static final int OMNIBOX_INCOGNITO_RESULTS_BG_COLOR = 0xFF3C4043;
-
-    private final OmniboxSuggestionListEmbedder mEmbedder;
-    private final View mAnchorView;
-    private final View mAlignmentView;
+    private static final int LIGHT_BG_COLOR = 0xFFFFFFFF;
+    private static final int DARK_BG_COLOR = 0xFF3C4043;
 
     private final int[] mTempPosition = new int[2];
     private final Rect mTempRect = new Rect();
 
-    private final OnGlobalLayoutListener mAnchorViewLayoutListener;
-    private final OnLayoutChangeListener mAlignmentViewLayoutListener;
+    private OmniboxSuggestionListEmbedder mEmbedder;
+    private View mAnchorView;
+    private View mAlignmentView;
+    private OnGlobalLayoutListener mAnchorViewLayoutListener;
+    private OnLayoutChangeListener mAlignmentViewLayoutListener;
 
     /**
      * Provides the capabilities required to embed the omnibox suggestion list into the UI.
@@ -62,20 +60,14 @@
 
         /** Return whether the suggestions are being rendered in the tablet UI. */
         boolean isTablet();
-
-        /** Return whether the current state is viewing incognito. */
-        boolean isIncognito();
     }
 
     /**
      * Constructs a new list designed for containing omnibox suggestions.
      * @param context Context used for contained views.
-     * @param embedder The embedder for the omnibox list providing access to external views and
-     *                 services.
      */
-    public OmniboxSuggestionsList(Context context, OmniboxSuggestionListEmbedder embedder) {
+    public OmniboxSuggestionsList(Context context) {
         super(context, null, android.R.attr.dropDownListViewStyle);
-        mEmbedder = embedder;
         setDivider(null);
         setFocusable(true);
         setFocusableInTouchMode(true);
@@ -83,9 +75,12 @@
         int paddingBottom = context.getResources().getDimensionPixelOffset(
                 R.dimen.omnibox_suggestion_list_padding_bottom);
         ViewCompat.setPaddingRelative(this, 0, 0, 0, paddingBottom);
+    }
 
-        refreshPopupBackground();
-
+    /** Set the embedder for the list view. */
+    void setEmbedder(OmniboxSuggestionListEmbedder embedder) {
+        assert mEmbedder == null;
+        mEmbedder = embedder;
         mAnchorView = mEmbedder.getAnchorView();
         // Prior to Android M, the contextual actions associated with the omnibox were anchored to
         // the top of the screen and not a floating copy/paste menu like on newer versions.  As a
@@ -146,19 +141,8 @@
     /**
      * Update the suggestion popup background to reflect the current state.
      */
-    void refreshPopupBackground() {
-        setBackground(getSuggestionPopupBackground());
-    }
-
-    /**
-     * @return The background for the omnibox suggestions popup.
-     */
-    private Drawable getSuggestionPopupBackground() {
-        int omniboxResultsColorForNonIncognito = OMNIBOX_RESULTS_BG_COLOR;
-        int omniboxResultsColorForIncognito = OMNIBOX_INCOGNITO_RESULTS_BG_COLOR;
-
-        int color = mEmbedder.isIncognito() ? omniboxResultsColorForIncognito
-                                            : omniboxResultsColorForNonIncognito;
+    void refreshPopupBackground(boolean useDarkBackground) {
+        int color = useDarkBackground ? DARK_BG_COLOR : LIGHT_BG_COLOR;
         if (!isHardwareAccelerated()) {
             // When HW acceleration is disabled, changing mSuggestionList' items somehow erases
             // mOmniboxResultsContainer' background from the area not covered by mSuggestionList.
@@ -169,7 +153,7 @@
                 color = Color.argb(254, Color.red(color), Color.green(color), Color.blue(color));
             }
         }
-        return new ColorDrawable(color);
+        setBackground(new ColorDrawable(color));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java
new file mode 100644
index 0000000..cb611e1
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java
@@ -0,0 +1,36 @@
+// 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.omnibox.suggestions;
+
+import org.chromium.chrome.browser.modelutil.PropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsList.OmniboxSuggestionListEmbedder;
+
+import java.util.List;
+
+/**
+ * The properties controlling the state of the list of suggestion items.
+ */
+public class SuggestionListProperties {
+    /** Whether the suggestion list is visible. */
+    public static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey();
+
+    /** The embedder for the suggestion list. */
+    public static final WritableObjectPropertyKey<OmniboxSuggestionListEmbedder> EMBEDDER =
+            new WritableObjectPropertyKey<>();
+
+    /** The list of models controlling the state of the suggestion items. */
+    public static final WritableObjectPropertyKey<List<PropertyModel>> SUGGESTION_MODELS =
+            new WritableObjectPropertyKey<>();
+
+    /** Whether the suggestion list should have a dark background. */
+    public static final WritableBooleanPropertyKey USE_DARK_BACKGROUND =
+            new WritableBooleanPropertyKey();
+
+    public static final PropertyKey[] ALL_KEYS =
+            new PropertyKey[] {VISIBLE, EMBEDDER, SUGGESTION_MODELS, USE_DARK_BACKGROUND};
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
new file mode 100644
index 0000000..dc11854
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
@@ -0,0 +1,97 @@
+// 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.omnibox.suggestions;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+
+import org.chromium.base.StrictModeContext;
+import org.chromium.base.Supplier;
+import org.chromium.chrome.browser.modelutil.PropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsList.OmniboxSuggestionListEmbedder;
+import org.chromium.ui.UiUtils;
+
+/**
+ * Handles property updates to the suggestion list component.
+ */
+class SuggestionListViewBinder {
+    /**
+     * Holds the view components needed to renderer the suggestion list.
+     */
+    public static class SuggestionListViewHolder {
+        public final Context context;
+        public final Supplier<ViewStub> containerStubSupplier;
+        public final OmniboxResultsAdapter adapter;
+
+        OmniboxSuggestionsList mListView;
+        ViewGroup mContainer;
+
+        public SuggestionListViewHolder(Context context, Supplier<ViewStub> containerStubSupplier,
+                OmniboxResultsAdapter adapter) {
+            this.context = context;
+            this.containerStubSupplier = containerStubSupplier;
+            this.adapter = adapter;
+        }
+    }
+
+    /**
+     * @see
+     * org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder#bind(Object,
+     * Object, Object)
+     */
+    public static void bind(
+            PropertyModel model, SuggestionListViewHolder view, PropertyKey propertyKey) {
+        if (SuggestionListProperties.VISIBLE.equals(propertyKey)) {
+            boolean visible = model.get(SuggestionListProperties.VISIBLE);
+            if (visible) {
+                if (view.mContainer == null) {
+                    view.mContainer = (ViewGroup) view.containerStubSupplier.get().inflate();
+                }
+                initializeSuggestionList(view, model);
+                view.mContainer.setVisibility(View.VISIBLE);
+                if (view.mListView.getParent() == null) view.mContainer.addView(view.mListView);
+                view.mListView.show();
+            } else {
+                if (view.mContainer == null) return;
+                view.mListView.setVisibility(View.GONE);
+                UiUtils.removeViewFromParent(view.mListView);
+                view.mContainer.setVisibility(View.INVISIBLE);
+            }
+        } else if (SuggestionListProperties.EMBEDDER.equals(propertyKey)) {
+            if (view.mListView == null) return;
+            view.mListView.setEmbedder(model.get(SuggestionListProperties.EMBEDDER));
+        } else if (SuggestionListProperties.SUGGESTION_MODELS.equals(propertyKey)) {
+            view.adapter.updateSuggestions(model.get(SuggestionListProperties.SUGGESTION_MODELS));
+        } else if (SuggestionListProperties.USE_DARK_BACKGROUND.equals(propertyKey)) {
+            if (view.mListView == null) return;
+            view.mListView.refreshPopupBackground(
+                    model.get(SuggestionListProperties.USE_DARK_BACKGROUND));
+        }
+    }
+
+    private static void initializeSuggestionList(
+            SuggestionListViewHolder view, PropertyModel model) {
+        if (view.mListView != null) return;
+
+        // TODO(tedchoc): Investigate lazily building the suggestion list off of the UI thread.
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+            view.mListView = new OmniboxSuggestionsList(view.context);
+        }
+
+        // Start with visibility GONE to ensure that show() is called. http://crbug.com/517438
+        view.mListView.setVisibility(View.GONE);
+        view.mListView.setAdapter(view.adapter);
+        view.mListView.setClipToPadding(false);
+
+        OmniboxSuggestionListEmbedder embedder = model.get(SuggestionListProperties.EMBEDDER);
+        if (embedder != null) view.mListView.setEmbedder(embedder);
+
+        view.mListView.refreshPopupBackground(
+                model.get(SuggestionListProperties.USE_DARK_BACKGROUND));
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
index a1880e1b..45c7a620 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.MailTo;
 import android.net.Uri;
 import android.provider.Browser;
@@ -227,11 +228,15 @@
         Intent chromeIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl));
         chromeIntent.setPackage(applicationContext.getPackageName());
         chromeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        if (applicationContext.getPackageManager()
-                        .queryIntentActivities(chromeIntent, 0)
-                        .isEmpty()) {
+        PackageManager packageManager = applicationContext.getPackageManager();
+
+        if (packageManager.queryIntentActivities(chromeIntent, 0).isEmpty()) {
             // If Chrome can't handle intent fallback to using any other VIEW handlers.
             chromeIntent.setPackage(null);
+
+            // Query again without the package name set and if there are still no handlers for the
+            // URI fail gracefully, and do nothing, since this will still cause a crash if launched.
+            if (packageManager.queryIntentActivities(chromeIntent, 0).isEmpty()) return;
         }
 
         // For "Open in Chrome" from the context menu in FullscreenActivity we want to bypass
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index df67a3ba..a534edbb 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3596,6 +3596,9 @@
       <message name="IDS_CLEAR_RELATED_DATA" desc="Notification text asking if the user wants to clear data for the given url as they have just uninstalled/cleared an app linked to that URL.">
         Would you like to clear data for <ph name="URL">%1$s<ex>youtube.com</ex></ph>?
       </message>
+      <message name="IDS_TWA_MANAGE_DATA" desc="Text on button of the notification that would direct the user to site settings for the site being run in a Trusted Web Activity" translateable="false">
+        Manage data
+      </message>
 
       <message name="IDS_WEBAPP_TAP_TO_COPY_URL" desc="Message on the notification that indicates that taping it will copy a Web App's URL into the clipboard.">
         Tap to copy the URL for this app
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 5ee0e0e..3143615 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -173,6 +173,7 @@
   "java/src/org/chromium/chrome/browser/browserservices/DomainDataCleaner.java",
   "java/src/org/chromium/chrome/browser/browserservices/Origin.java",
   "java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java",
+  "java/src/org/chromium/chrome/browser/browserservices/PersistentNotificationController.java",
   "java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java",
   "java/src/org/chromium/chrome/browser/browserservices/Relationship.java",
   "java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java",
@@ -1104,6 +1105,8 @@
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestion.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionAnswer.java",
+  "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java",
+  "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionView.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
index c8bea67b..72c2a01 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
@@ -47,7 +47,7 @@
     public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
 
     private static final String TEST_PAGE = "/chrome/test/data/android/google.html";
-    private static final String PACKAGE_NAME = "package.name";
+    private static final String PACKAGE_NAME = "org.chromium.chrome"; // Package name of test apk.
 
     private EmbeddedTestServer mTestServer;
     private String mTestPage;
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 f7d2850..cb246f2 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
@@ -1118,6 +1118,7 @@
             ViewGroup topBar = cctActivity.findViewById(R.id.topbar);
             Assert.assertNotNull(topBar);
             Assert.assertThat(anyView.getParent(), equalTo(topBar));
+            Assert.assertEquals(View.GONE, anyView.getVisibility());
         });
     }
 
@@ -1139,6 +1140,28 @@
 
     @Test
     @SmallTest
+    @RetryOnFailure
+    public void testSetTopBarContentView_moduleManagedUrl_topBarVisible() throws Exception {
+        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(
+                InstrumentationRegistry.getTargetContext(),
+                "https://www.google.com/search?q=london");
+        intent.putExtra(CustomTabIntentDataProvider.EXTRA_MODULE_MANAGED_URLS_REGEX,
+                "^https://www.google.com/search.*");
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+
+        ThreadUtils.runOnUiThread(() -> {
+            CustomTabActivity cctActivity = mCustomTabActivityTestRule.getActivity();
+            View anyView = new View(cctActivity);
+            cctActivity.setTopBarContentView(anyView);
+            ViewGroup topBar = cctActivity.findViewById(R.id.topbar);
+            Assert.assertNotNull(topBar);
+            Assert.assertThat(anyView.getParent(), equalTo(topBar));
+            Assert.assertEquals(View.VISIBLE, anyView.getVisibility());
+        });
+    }
+
+    @Test
+    @SmallTest
     @Feature({"UiCatalogue"})
     public void testRemoteViews() throws Exception {
         Intent intent = createMinimalCustomTabIntent();
@@ -1183,7 +1206,7 @@
         Assert.assertEquals(getActivity().getIntentDataProvider().getSession(), session);
         Assert.assertFalse("CustomTabContentHandler handled intent with wrong session",
                 ThreadUtils.runOnUiThreadBlockingNoException(() -> {
-                    return BrowserSessionContentUtils.handleInActiveContentIfNeeded(
+                    return BrowserSessionContentUtils.handleBrowserServicesIntent(
                             CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage2));
                 }));
         CriteriaHelper.pollInstrumentationThread(
@@ -1191,7 +1214,7 @@
         Assert.assertTrue("CustomTabContentHandler can't handle intent with same session",
                 ThreadUtils.runOnUiThreadBlockingNoException(() -> {
                     intent.setData(Uri.parse(mTestPage2));
-                    return BrowserSessionContentUtils.handleInActiveContentIfNeeded(intent);
+                    return BrowserSessionContentUtils.handleBrowserServicesIntent(intent);
                 }));
         final Tab tab = getActivity().getActivityTab();
         final CallbackHelper pageLoadFinishedHelper = new CallbackHelper();
@@ -1265,7 +1288,7 @@
         });
         Assert.assertTrue("CustomTabContentHandler can't handle intent with same session",
                 ThreadUtils.runOnUiThreadBlockingNoException(
-                        () -> BrowserSessionContentUtils.handleInActiveContentIfNeeded(intent)));
+                        () -> BrowserSessionContentUtils.handleBrowserServicesIntent(intent)));
         pageLoadFinishedHelper.waitForCallback(0);
     }
 
@@ -1305,7 +1328,7 @@
         });
         Assert.assertTrue("CustomTabContentHandler can't handle intent with same session",
                 ThreadUtils.runOnUiThreadBlockingNoException(
-                        () -> BrowserSessionContentUtils.handleInActiveContentIfNeeded(intent)));
+                        () -> BrowserSessionContentUtils.handleBrowserServicesIntent(intent)));
         pageLoadFinishedHelper.waitForCallback(0);
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
index 07c0661..816a266e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
@@ -395,10 +395,11 @@
     public int loadUrlAndAwaitInitialization(String url, int timeoutSec)
             throws InterruptedException {
         int result = mRule.loadUrl(url, timeoutSec);
-        // TODO(https://crbug.com/894796): Remove the first isInitializationComplete once loadUrl
-        // is fixed.
+        // TODO(https://crbug.com/894796): Remove the isInitializationComplete undefined check once
+        // loadUrl is fixed.
         Assert.assertTrue("Timed out waiting for JavaScript test initialization",
-                pollJavaScriptBoolean("isInitializationComplete && isInitializationComplete()",
+                pollJavaScriptBoolean("(typeof isInitializationComplete !== 'undefined') && "
+                                + "isInitializationComplete()",
                         POLL_TIMEOUT_LONG_MS, mRule.getWebContents()));
         return result;
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationActionsUpdatedTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationActionsUpdatedTest.java
index d67bc66..21c8fc0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationActionsUpdatedTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationActionsUpdatedTest.java
@@ -17,9 +17,9 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.blink.mojom.MediaSessionAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.media.ui.MediaNotificationManager.ListenerService;
+import org.chromium.media_session.mojom.MediaSessionAction;
 
 import java.util.HashSet;
 import java.util.Set;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationButtonComputationTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationButtonComputationTest.java
index cb4fe0e..94a48f6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationButtonComputationTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationButtonComputationTest.java
@@ -12,7 +12,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
-import org.chromium.blink.mojom.MediaSessionAction;
+import org.chromium.media_session.mojom.MediaSessionAction;
 
 import java.util.ArrayList;
 
@@ -93,4 +93,4 @@
         assertEquals(4, compactViewActions[1]);
         assertEquals(1, compactViewActions[2]);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerServiceActionsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerServiceActionsTest.java
index a2a2776..6619dbf 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerServiceActionsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerServiceActionsTest.java
@@ -20,8 +20,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.blink.mojom.MediaSessionAction;
 import org.chromium.chrome.browser.media.ui.MediaNotificationManager.ListenerService;
+import org.chromium.media_session.mojom.MediaSessionAction;
 
 /**
  * JUnit tests for checking {@link MediaNotificationManager.ListenerService} handles intent actionss
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9df707d..019960d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1187,6 +1187,10 @@
 const FeatureEntry::FeatureParam kTranslateForceTriggerOnEnglishGeo[] = {
     {language::kOverrideModelKey, language::kOverrideModelGeoValue},
     {language::kEnforceRankerKey, "false"}};
+const FeatureEntry::FeatureParam kTranslateForceTriggerOnEnglishBackoff[] = {
+    {language::kOverrideModelKey, language::kOverrideModelDefaultValue},
+    {language::kEnforceRankerKey, "false"},
+    {language::kBackoffThresholdKey, "0"}};
 const FeatureEntry::FeatureVariation
     kTranslateForceTriggerOnEnglishVariations[] = {
         {"(Heuristic model without Ranker)",
@@ -1194,7 +1198,8 @@
          arraysize(kTranslateForceTriggerOnEnglishHeuristic), nullptr},
         {"(Geo model without Ranker)", kTranslateForceTriggerOnEnglishGeo,
          arraysize(kTranslateForceTriggerOnEnglishGeo), nullptr},
-};
+        {"(Zero threshold)", kTranslateForceTriggerOnEnglishBackoff,
+         arraysize(kTranslateForceTriggerOnEnglishBackoff), nullptr}};
 #endif  // defined(OS_ANDROID)
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc b/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
index 053eab5..ad0d287 100644
--- a/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
+++ b/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
@@ -18,7 +18,7 @@
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/favicon_service.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/image_fetcher/core/image_fetcher.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/android/large_icon_bridge.cc b/chrome/browser/android/large_icon_bridge.cc
index 3cd6b2a5..99d1bc0b 100644
--- a/chrome/browser/android/large_icon_bridge.cc
+++ b/chrome/browser/android/large_icon_bridge.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/favicon/large_icon_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
 #include "jni/LargeIconBridge_jni.h"
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.h b/chrome/browser/chromeos/arc/arc_session_manager.h
index 24f2167..e9a3d69e 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.h
+++ b/chrome/browser/chromeos/arc/arc_session_manager.h
@@ -317,7 +317,7 @@
   // ACTIVE.
   void StartArc();
 
-  // Requests to stop ARC instnace. This resets two persistent flags:
+  // Requests to stop ARC instance. This resets two persistent flags:
   // kArcSignedIn and kArcTermsAccepted, so that, in next enabling,
   // it is started from Terms of Service negotiation.
   // TODO(hidehiko): Introduce STOPPING state, and this function should
diff --git a/chrome/browser/chromeos/arc/arc_util.cc b/chrome/browser/chromeos/arc/arc_util.cc
index 66a4d9a..aa9ba99 100644
--- a/chrome/browser/chromeos/arc/arc_util.cc
+++ b/chrome/browser/chromeos/arc/arc_util.cc
@@ -279,6 +279,11 @@
   return it->second;
 }
 
+bool IsArcProvisioned(const Profile* profile) {
+  return profile && profile->GetPrefs()->HasPrefPath(prefs::kArcSignedIn) &&
+         profile->GetPrefs()->GetBoolean(prefs::kArcSignedIn);
+}
+
 void ResetArcAllowedCheckForTesting(const Profile* profile) {
   g_profile_status_check.Get().erase(profile);
 }
diff --git a/chrome/browser/chromeos/arc/arc_util.h b/chrome/browser/chromeos/arc/arc_util.h
index d7a369c9..0f8982b 100644
--- a/chrome/browser/chromeos/arc/arc_util.h
+++ b/chrome/browser/chromeos/arc/arc_util.h
@@ -56,6 +56,10 @@
 // nullptr can be safely passed to this function. In that case, returns false.
 bool IsArcAllowedForProfile(const Profile* profile);
 
+// Returns whether ARC was successfully provisioned and the Primary/Device
+// Account has been signed into ARC.
+bool IsArcProvisioned(const Profile* profile);
+
 // Returns true if the profile is unmanaged or if the policy
 // EcryptfsMigrationStrategy for the user doesn't disable the migration.
 // Specifically if the policy states to ask the user, it is also considered that
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
index a1c3a7b..033054ff 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
@@ -184,6 +184,8 @@
   arc_bridge_service_->auth()->SetHost(this);
   arc_bridge_service_->auth()->AddObserver(this);
 
+  ArcSessionManager::Get()->AddObserver(this);
+
   if (chromeos::switches::IsAccountManagerEnabled()) {
     // TODO(sinhak): This will need to be independent of Profile, when
     // Multi-Profile on Chrome OS is launched.
@@ -198,10 +200,22 @@
   if (chromeos::switches::IsAccountManagerEnabled())
     account_manager_->RemoveObserver(this);
 
+  ArcSessionManager::Get()->RemoveObserver(this);
   arc_bridge_service_->auth()->RemoveObserver(this);
   arc_bridge_service_->auth()->SetHost(nullptr);
 }
 
+void ArcAuthService::OnConnectionReady() {
+  // |TriggerAccountsPushToArc()| will not be triggered for the first session,
+  // when ARC has not been provisioned yet. For the first session, an account
+  // push will be triggered by |OnArcInitialStart()|, after a successful device
+  // provisioning.
+  // For the second and subsequent sessions,
+  // |ArcSessionManager::Get()->IsArcProvisioned()| will be |true|.
+  if (arc::IsArcProvisioned(profile_))
+    TriggerAccountsPushToArc();
+}
+
 void ArcAuthService::OnConnectionClosed() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   pending_token_requests_.clear();
@@ -410,16 +424,44 @@
     const chromeos::AccountManager::AccountKey& account_key) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // TODO(sinhak): Implement sending notifications to ARC++.
-  NOTREACHED();
+  // Ignore the update if ARC has not been provisioned yet.
+  if (!arc::IsArcProvisioned(profile_))
+    return;
+
+  auto* instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->auth(),
+                                               OnAccountUpdated);
+  if (!instance)
+    return;
+
+  const std::string account_name =
+      account_mapper_util_.AccountKeyToGaiaAccountInfo(account_key).email;
+  DCHECK(!account_name.empty());
+  instance->OnAccountUpdated(account_name, mojom::AccountUpdateType::UPSERT);
 }
 
 void ArcAuthService::OnAccountRemoved(
     const chromeos::AccountManager::AccountKey& account_key) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // TODO(sinhak): Implement sending notifications to ARC++.
-  NOTREACHED();
+  DCHECK(!IsPrimaryAccount(account_key));
+
+  // Ignore the update if ARC has not been provisioned yet.
+  if (!arc::IsArcProvisioned(profile_))
+    return;
+
+  auto* instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->auth(),
+                                               OnAccountUpdated);
+  if (!instance)
+    return;
+
+  const std::string account_name =
+      account_mapper_util_.AccountKeyToGaiaAccountInfo(account_key).email;
+  DCHECK(!account_name.empty());
+  instance->OnAccountUpdated(account_name, mojom::AccountUpdateType::REMOVAL);
+}
+
+void ArcAuthService::OnArcInitialStart() {
+  TriggerAccountsPushToArc();
 }
 
 void ArcAuthService::OnActiveDirectoryEnrollmentTokenFetched(
@@ -597,4 +639,13 @@
   skip_merge_session_for_testing_ = true;
 }
 
+void ArcAuthService::TriggerAccountsPushToArc() {
+  if (!chromeos::switches::IsAccountManagerEnabled())
+    return;
+
+  DCHECK(account_manager_);
+  account_manager_->GetAccounts(base::BindOnce(&ArcAuthService::OnGetAccounts,
+                                               weak_ptr_factory_.GetWeakPtr()));
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service.h b/chrome/browser/chromeos/arc/auth/arc_auth_service.h
index aa02a73b..b46418c 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service.h
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "chrome/browser/chromeos/account_mapper_util.h"
+#include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher.h"
 #include "chromeos/account_manager/account_manager.h"
 #include "components/arc/common/auth.mojom.h"
@@ -42,7 +43,8 @@
 class ArcAuthService : public KeyedService,
                        public mojom::AuthHost,
                        public ConnectionObserver<mojom::AuthInstance>,
-                       public chromeos::AccountManager::Observer {
+                       public chromeos::AccountManager::Observer,
+                       public ArcSessionManager::Observer {
  public:
   // Returns singleton instance for the given BrowserContext,
   // or nullptr if the browser |context| is not allowed to use ARC.
@@ -56,6 +58,7 @@
   static const char kArcServiceName[];
 
   // ConnectionObserver<mojom::AuthInstance>:
+  void OnConnectionReady() override;
   void OnConnectionClosed() override;
 
   // mojom::AuthHost:
@@ -82,6 +85,9 @@
   void OnAccountRemoved(
       const chromeos::AccountManager::AccountKey& account_key) override;
 
+  // ArcSessionManager::Observer:
+  void OnArcInitialStart() override;
+
   void SkipMergeSessionForTesting();
 
  private:
@@ -149,7 +155,7 @@
 
   // Creates an |ArcBackgroundAuthCodeFetcher| for |account_id|. Can be used for
   // Device Account and Secondary Accounts. |initial_signin| denotes whether the
-  // fetcher is being created for the initial ARC++ provisioning flow or for a
+  // fetcher is being created for the initial ARC provisioning flow or for a
   // subsequent sign-in.
   std::unique_ptr<ArcBackgroundAuthCodeFetcher>
   CreateArcBackgroundAuthCodeFetcher(const std::string& account_id,
@@ -159,6 +165,9 @@
   // |pending_token_requests_|.
   void DeletePendingTokenRequest(ArcFetcherBase* fetcher);
 
+  // Triggers an async push of the accounts in Chrome OS Account Manager to ARC.
+  void TriggerAccountsPushToArc();
+
   // Non-owning pointers.
   Profile* const profile_;
   chromeos::AccountManager* account_manager_ = nullptr;
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
index 40a1b88..739b248 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
@@ -108,6 +108,20 @@
     std::move(done_closure_).Run();
   }
 
+  void OnAccountUpdated(const std::string& account_name,
+                        mojom::AccountUpdateType update_type) override {
+    switch (update_type) {
+      case mojom::AccountUpdateType::UPSERT:
+        ++num_account_upserted_calls_;
+        last_upserted_account_ = account_name;
+        break;
+      case mojom::AccountUpdateType::REMOVAL:
+        ++num_account_removed_calls_;
+        last_removed_account_ = account_name;
+        break;
+    }
+  }
+
   void RequestAccountInfoDeprecated(base::OnceClosure done_closure) {
     done_closure_ = std::move(done_closure);
     host_->RequestAccountInfoDeprecated(true /* initial_signin */);
@@ -131,6 +145,11 @@
 
   mojom::ArcSignInStatus sign_in_status() const { return status_; }
 
+  int num_account_upserted_calls_ = 0;
+  std::string last_upserted_account_;
+  int num_account_removed_calls_ = 0;
+  std::string last_removed_account_;
+
  private:
   void OnAccountInfoResponse(base::OnceClosure done_closure,
                              mojom::ArcSignInStatus status,
@@ -457,6 +476,37 @@
             auth_instance().sign_in_status());
 }
 
+IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, AccountUpsertsArePropagated) {
+  SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
+  SeedAccountInfo(kSecondaryAccountGaiaId, kSecondaryAccountEmail);
+
+  EXPECT_EQ(0, auth_instance().num_account_upserted_calls_);
+
+  chromeos::AccountManager::AccountKey account_key{
+      kSecondaryAccountGaiaId,
+      chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA};
+  auth_service().OnTokenUpserted(account_key);
+
+  EXPECT_EQ(1, auth_instance().num_account_upserted_calls_);
+  EXPECT_EQ(kSecondaryAccountEmail, auth_instance().last_upserted_account_);
+}
+
+IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, AccountRemovalsArePropagated) {
+  SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
+  SeedAccountInfo(kSecondaryAccountGaiaId, kSecondaryAccountEmail);
+
+  EXPECT_EQ(0, auth_instance().num_account_removed_calls_);
+
+  chromeos::AccountManager::AccountKey account_key{
+      kSecondaryAccountGaiaId,
+      chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA};
+  auth_service().OnAccountRemoved(account_key);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, auth_instance().num_account_removed_calls_);
+  EXPECT_EQ(kSecondaryAccountEmail, auth_instance().last_removed_account_);
+}
+
 class ArcRobotAccountAuthServiceTest : public ArcAuthServiceTest {
  public:
   ArcRobotAccountAuthServiceTest() = default;
diff --git a/chrome/browser/chromeos/dbus/OWNERS b/chrome/browser/chromeos/dbus/OWNERS
index 7134eae..f3164c9c 100644
--- a/chrome/browser/chromeos/dbus/OWNERS
+++ b/chrome/browser/chromeos/dbus/OWNERS
@@ -1,2 +1,3 @@
-hashimoto@chromium.org
-stevenjb@chromium.org
+file://chromeos/dbus/OWNERS
+per-file org.chromium.*.conf=set noparent
+per-file org.chromium.*.conf=file://chromeos/SECURITY_OWNERS
diff --git a/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc b/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
index e1a0c1fd..cb5000b 100644
--- a/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
+++ b/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
@@ -778,10 +778,11 @@
   EXPECT_TRUE(SetDisplayLayout(layout));
   EXPECT_EQ(gfx::Size(650, 743),
             display::Screen::GetScreen()->GetPrimaryDisplay().size());
-  EXPECT_EQ(displays[3].id,
-            std::to_string(GetDisplayManager()
+  EXPECT_EQ(displays[2].id,
+            std::to_string(ash::Shell::Get()
+                               ->display_configuration_controller()
                                ->GetPrimaryMirroringDisplayForUnifiedDesktop()
-                               ->id()));
+                               .id()));
 
   // Confirm the new layout.
   DisplayLayoutList new_layout = GetDisplayLayout();
diff --git a/chrome/browser/favicon/large_icon_service_factory.cc b/chrome/browser/favicon/large_icon_service_factory.cc
index bf53df7..95596208 100644
--- a/chrome/browser/favicon/large_icon_service_factory.cc
+++ b/chrome/browser/favicon/large_icon_service_factory.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/suggestions/image_decoder_impl.h"
 #include "components/favicon/core/favicon_service.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/image_fetcher/core/image_decoder.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
diff --git a/chrome/browser/notifications/win/notification_template_builder.cc b/chrome/browser/notifications/win/notification_template_builder.cc
index c85d4ea..e410ae3 100644
--- a/chrome/browser/notifications/win/notification_template_builder.cc
+++ b/chrome/browser/notifications/win/notification_template_builder.cc
@@ -334,15 +334,16 @@
 const char kNotificationToastElement[] = "toast";
 const char kNotificationLaunchAttribute[] = "launch";
 
+// libXml was preferred (over WinXml, which the samples in the link below tend
+// to use) for building the XML template because it is used frequently in
+// Chrome, is nicer to use and has already been vetted.
+// https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-adaptive-interactive-toasts
 base::string16 BuildNotificationTemplate(
     NotificationImageRetainer* image_retainer,
     const NotificationLaunchId& launch_id,
     const message_center::Notification& notification) {
   DCHECK(image_retainer);
 
-  // libXml was preferred (over WinXml, which the samples tend to use) because
-  // it is used frequently in Chrome, is nicer to use and has already been
-  // vetted.
   XmlWriter xml_writer;
   xml_writer.StartWriting();
 
diff --git a/chrome/browser/notifications/win/notification_template_builder.h b/chrome/browser/notifications/win/notification_template_builder.h
index 651d18b4..3fba5f522 100644
--- a/chrome/browser/notifications/win/notification_template_builder.h
+++ b/chrome/browser/notifications/win/notification_template_builder.h
@@ -23,7 +23,6 @@
 
 // Builds XML-based notification template for displaying a given |notification|
 // in the Windows Action Center.
-// https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-adaptive-interactive-toasts
 base::string16 BuildNotificationTemplate(
     NotificationImageRetainer* image_retainer,
     const NotificationLaunchId& launch_id,
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index d522e29..f5a37b5 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -281,6 +281,13 @@
 GURL PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
     const GURL& original_url) {
   DCHECK(original_url.is_valid());
+  std::string experiment_id =
+      previews::params::LitePageRedirectPreviewExperiment();
+  std::string experiment_query;
+  if (!experiment_id.empty()) {
+    experiment_query =
+        "&x=" + net::EscapeQueryParamValue(experiment_id, true /* use_plus */);
+  }
 
   std::string origin_hash = base::ToLowerASCII(base32::Base32Encode(
       crypto::SHA256HashString(
@@ -292,7 +299,8 @@
       previews_host.scheme() + "://" + origin_hash + "." +
       previews_host.host() +
       (previews_host.has_port() ? (":" + previews_host.port()) : "") + "/p?u=" +
-      net::EscapeQueryParamValue(original_url.spec(), true /* use_plus */));
+      net::EscapeQueryParamValue(original_url.spec(), true /* use_plus */) +
+      experiment_query);
   DCHECK(previews_url.is_valid());
   DCHECK_EQ(previews_host.scheme(), previews_url.scheme());
   return previews_url;
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle_unittest.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle_unittest.cc
index 46047d861..35070dea 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle_unittest.cc
@@ -24,6 +24,7 @@
     std::string previews_host;
     std::string original_url;
     std::string previews_url;
+    std::string experiment;
   };
   const TestCase kTestCases[]{
       // Use https://play.golang.org/p/HUM2HxmUTOW to compute |previews_url|.
@@ -33,6 +34,7 @@
           "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
           "previews.host.com/p?u="
           "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
       },
       {
           "https://previews.host.com",
@@ -40,6 +42,7 @@
           "https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
           "previews.host.com/p?u="
           "http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
       },
       {
           "https://previews.host.com",
@@ -47,6 +50,7 @@
           "https://mil6oxtqb4zpsbmutm4d7wrx5nlr6tzlxjp7y44u55zqhzsdzjpq."
           "previews.host.com/p?u=https%3A%2F%2Foriginal.host.com%3A1443"
           "%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
       },
       {
           "https://previews.host.com:1443",
@@ -54,6 +58,7 @@
           "https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
           "previews.host.com:1443/p?u="
           "http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
       },
       {
           "https://previews.host.com:1443",
@@ -61,6 +66,7 @@
           "https://mil6oxtqb4zpsbmutm4d7wrx5nlr6tzlxjp7y44u55zqhzsdzjpq."
           "previews.host.com:1443/p?u=https%3A%2F%2Foriginal.host.com%3A1443"
           "%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
+          "",
       },
       {
           "https://previews.host.com",
@@ -69,6 +75,16 @@
           "previews.host.com/p?u="
           "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
           "%23fragment",
+          "",
+      },
+      {
+          "https://previews.host.com",
+          "https://original.host.com/path/path/path?query=yes",
+          "https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
+          "previews.host.com/p?u="
+          "https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
+          "&x=enable_HTCPCP",
+          "enable_HTCPCP",
       },
   };
 
@@ -76,7 +92,8 @@
     base::test::ScopedFeatureList scoped_feature_list;
     scoped_feature_list.InitAndEnableFeatureWithParameters(
         previews::features::kLitePageServerPreviews,
-        {{"previews_host", test_case.previews_host}});
+        {{"previews_host", test_case.previews_host},
+         {"lite_page_preview_experiment", test_case.experiment}});
 
     EXPECT_EQ(PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
                   GURL(test_case.original_url)),
diff --git a/chrome/browser/resource_coordinator/decision_details.h b/chrome/browser/resource_coordinator/decision_details.h
index 233b2953..f8499b5 100644
--- a/chrome/browser/resource_coordinator/decision_details.h
+++ b/chrome/browser/resource_coordinator/decision_details.h
@@ -63,6 +63,7 @@
   LIVE_STATE_PLAYING_AUDIO,
   // The tab is opted out of the intervention as it is currently using
   // WebSockets.
+  // NOTE: This heuristic isn't used in the freezing/discarding interventions.
   LIVE_STATE_USING_WEB_SOCKETS,
   // The tab is opted out of the intervention as it is currently WebUSB.
   LIVE_STATE_USING_WEB_USB,
diff --git a/chrome/browser/resources/chromeos/mobile_setup.js b/chrome/browser/resources/chromeos/mobile_setup.js
index 3e947eb..276ecd2 100644
--- a/chrome/browser/resources/chromeos/mobile_setup.js
+++ b/chrome/browser/resources/chromeos/mobile_setup.js
@@ -140,27 +140,6 @@
     },
 
     /**
-     * Shows the payment portal webview when the payment portal starts loading.
-     * The goal is to ensure that any interstitial UI in the webview is visible
-     * to the user, at least temporarily, until https://crbug.com/894281 is
-     * resolved.
-     *
-     * @param {string} paymentUrl The payment portal URL - used to restrict
-     *     origins to which the message is sent.
-     * @private
-     */
-    showPaymentPortalOnLoadStart_: function(paymentUrl, evt) {
-      if (!evt.isTopLevel ||
-          new URL(evt.url).origin != new URL(paymentUrl).origin) {
-        return;
-      }
-
-      $('finalStatus').classList.add('hidden');
-      $('systemStatus').classList.add('hidden');
-      $('portalFrame').classList.remove('hidden');
-    },
-
-    /**
      * Loads payment URL defined by <code>deviceInfo</code> into the
      * portal frame webview.
      * If the webview element already exists, it will not be reused - the
@@ -208,11 +187,6 @@
           'loadstop',
           this.sendInitialMessage_.bind(this, deviceInfo.payment_url));
 
-      frame.addEventListener(
-          'loadstart',
-          this.showPaymentPortalOnLoadStart_.bind(
-              this, deviceInfo.payment_url));
-
       if (deviceInfo.post_data && deviceInfo.post_data.length) {
         mobile.util.postDeviceDataToWebview(frame, deviceInfo);
       } else {
@@ -444,10 +418,6 @@
     MobileSetup.getInstance().updateDeviceStatus_(deviceInfo);
   };
 
-  MobileSetup.portalFrameLoadError = function(errorCode) {};
-
-  MobileSetup.portalFrameLoadCompleted = function() {};
-
   MobileSetup.loadPage = function() {
     mobile.MobileSetup.getInstance().initialize(
         mobile.MobileSetup.ACTIVATION_PAGE_URL);
diff --git a/chrome/browser/resources/settings/languages_page/languages.js b/chrome/browser/resources/settings/languages_page/languages.js
index 50a2d02..f15055d 100644
--- a/chrome/browser/resources/settings/languages_page/languages.js
+++ b/chrome/browser/resources/settings/languages_page/languages.js
@@ -392,17 +392,12 @@
         /** @type {!Array<string>} */ (translateBlockedPref.value));
 
     for (let i = 0; i < this.languages.enabled.length; i++) {
-      if (this.languages.enabled[i].language.code ==
-          this.languages.prospectiveUILanguage) {
-        continue;
-      }
-      // This conversion primarily strips away the region part.
-      // For example "fr-CA" --> "fr".
-      const translateCode = this.convertLanguageCodeForTranslate(
-          this.languages.enabled[i].language.code);
+      const language = this.languages.enabled[i].language;
+      const translateEnabled = this.isTranslateEnabled_(
+          language.code, !!language.supportsTranslate, translateBlockedSet,
+          this.languages.translateTarget, this.languages.prospectiveUILanguage);
       this.set(
-          'languages.enabled.' + i + '.translateEnabled',
-          !translateBlockedSet.has(translateCode));
+          'languages.enabled.' + i + '.translateEnabled', translateEnabled);
     }
   },
 
@@ -542,13 +537,9 @@
       const languageState = /** @type {LanguageState} */ ({});
       languageState.language = language;
       languageState.spellCheckEnabled = !!spellCheckSet.has(code);
-      // Translate is considered disabled if this language maps to any translate
-      // language that is blocked.
-      const translateCode = this.convertLanguageCodeForTranslate(code);
-      languageState.translateEnabled = !!language.supportsTranslate &&
-          !translateBlockedSet.has(translateCode) &&
-          translateCode != translateTarget &&
-          (!prospectiveUILanguage || code != prospectiveUILanguage);
+      languageState.translateEnabled = this.isTranslateEnabled_(
+          code, !!language.supportsTranslate, translateBlockedSet,
+          translateTarget, prospectiveUILanguage);
       languageState.isManaged = !!spellCheckForcedSet.has(code);
       languageState.downloadDictionaryFailureCount = 0;
       enabledLanguageStates.push(languageState);
@@ -556,6 +547,29 @@
     return enabledLanguageStates;
   },
 
+  /**
+   * True iff we translate pages that are in the given language.
+   * @param {string} code Language code.
+   * @param {boolean} supportsTranslate If translation supports the given
+   *     language.
+   * @param {!Set<string>} translateBlockedSet Set of languages for which
+   *     translation is blocked.
+   * @param {string} translateTarget Language code of the default translate
+   *     target language.
+   * @param {(string|undefined)} prospectiveUILanguage Prospective UI display
+   *     language. Only defined on Windows and Chrome OS.
+   * @return {boolean}
+   * @private
+   */
+  isTranslateEnabled_: function(
+      code, supportsTranslate, translateBlockedSet, translateTarget,
+      prospectiveUILanguage) {
+    const translateCode = this.convertLanguageCodeForTranslate(code);
+    return supportsTranslate && !translateBlockedSet.has(translateCode) &&
+        translateCode != translateTarget &&
+        (!prospectiveUILanguage || code != prospectiveUILanguage);
+  },
+
   // <if expr="not is_macosx">
   /**
    * Updates the dictionary download status for languages in
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page.html b/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
index f1d9e08f..78aba32 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
@@ -99,7 +99,9 @@
       </template>
       <template is="dom-if" route-path="/multidevice/features/smartLock"
           restamp>
-        <settings-subpage page-title="$i18n{easyUnlockSectionTitle}">
+        <settings-subpage
+            associated-control="[[$$('#multidevice-item')]]"
+            page-title="$i18n{easyUnlockSectionTitle}">
           <settings-multidevice-smartlock-subpage
               prefs="{{prefs}}"
               page-content-data="[[pageContentData]]">
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 1c59d78..4468b5c6 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -68,6 +68,36 @@
 
 namespace safe_browsing {
 
+#if defined(FULL_SAFE_BROWSING)
+namespace {
+
+// 50 was chosen as an arbitrary upper bound on the likely font sizes. For
+// reference, the "Font size" dropdown in settings lets you select between 9,
+// 12, 16, 20, or 24.
+const int kMaximumTrackedFontSize = 50;
+
+void RecordFontSizeMetrics(const PrefService& pref_service) {
+  UMA_HISTOGRAM_EXACT_LINEAR(
+      "SafeBrowsing.FontSize.Default",
+      pref_service.GetInteger(prefs::kWebKitDefaultFontSize),
+      kMaximumTrackedFontSize);
+  UMA_HISTOGRAM_EXACT_LINEAR(
+      "SafeBrowsing.FontSize.DefaultFixed",
+      pref_service.GetInteger(prefs::kWebKitDefaultFixedFontSize),
+      kMaximumTrackedFontSize);
+  UMA_HISTOGRAM_EXACT_LINEAR(
+      "SafeBrowsing.FontSize.Minimum",
+      pref_service.GetInteger(prefs::kWebKitMinimumFontSize),
+      kMaximumTrackedFontSize);
+  UMA_HISTOGRAM_EXACT_LINEAR(
+      "SafeBrowsing.FontSize.MinimumLogical",
+      pref_service.GetInteger(prefs::kWebKitMinimumLogicalFontSize),
+      kMaximumTrackedFontSize);
+}
+
+}  // namespace
+#endif
+
 // static
 SafeBrowsingServiceFactory* SafeBrowsingService::factory_ = NULL;
 
@@ -462,6 +492,10 @@
                         pref_service->GetBoolean(prefs::kSafeBrowsingEnabled));
   // Extended Reporting metrics are handled together elsewhere.
   RecordExtendedReportingMetrics(*pref_service);
+
+#if defined(FULL_SAFE_BROWSING)
+  RecordFontSizeMetrics(*pref_service);
+#endif
 }
 
 void SafeBrowsingService::RemovePrefService(PrefService* pref_service) {
diff --git a/chrome/browser/translate/android/translate_bridge.cc b/chrome/browser/translate/android/translate_bridge.cc
index 12d7b0d..e24050a 100644
--- a/chrome/browser/translate/android/translate_bridge.cc
+++ b/chrome/browser/translate/android/translate_bridge.cc
@@ -58,7 +58,7 @@
 
   return base::StartsWith(page_lang, "en",
                           base::CompareCase::INSENSITIVE_ASCII) &&
-         language::ShouldForceTriggerTranslateOnEnglishPages(
+         !language::ShouldForceTriggerTranslateOnEnglishPages(
              translate_prefs->GetForceTriggerOnEnglishPagesCount()) &&
-         manager->GetLanguageState().translate_enabled();
+         !manager->GetLanguageState().translate_enabled();
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 773ba694..29729b464 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1448,8 +1448,6 @@
       "views/frame/browser_non_client_frame_view_ash.h",
       "views/frame/immersive_context_mus.cc",
       "views/frame/immersive_context_mus.h",
-      "views/frame/immersive_handler_factory_mus.cc",
-      "views/frame/immersive_handler_factory_mus.h",
       "views/frame/immersive_mode_controller_ash.cc",
       "views/frame/immersive_mode_controller_ash.h",
       "views/frame/native_browser_frame_factory_chromeos.cc",
diff --git a/chrome/browser/ui/app_list/search/internal_app_result.cc b/chrome/browser/ui/app_list/search/internal_app_result.cc
index ddfc90b1..aed5cf6 100644
--- a/chrome/browser/ui/app_list/search/internal_app_result.cc
+++ b/chrome/browser/ui/app_list/search/internal_app_result.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
 #include "chrome/browser/ui/app_list/search/search_util.h"
 #include "components/favicon/core/favicon_server_fetcher_params.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_skia_operations.h"
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 70fc4a2..3bdb63c 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -44,7 +44,6 @@
 #include "chrome/browser/ui/ash/vpn_list_forwarder.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 #include "chrome/browser/ui/views/frame/immersive_context_mus.h"
-#include "chrome/browser/ui/views/frame/immersive_handler_factory_mus.h"
 #include "chrome/browser/ui/views/ime_driver/ime_driver_mus.h"
 #include "chrome/browser/ui/views/select_file_dialog_extension.h"
 #include "chrome/browser/ui/views/select_file_dialog_extension_factory.h"
@@ -188,8 +187,6 @@
   if (!features::IsMultiProcessMash()) {
     ash_shell_init_ = std::make_unique<AshShellInit>();
   } else {
-    immersive_handler_factory_ = std::make_unique<ImmersiveHandlerFactoryMus>();
-
     // Enterprise support in the browser can monitor user activity. Connect to
     // the UI service to monitor activity. The ash process has its own monitor.
     // TODO(jamescook): Figure out if we need this for SingleProcessMash.
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
index 8574037..e308304e 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
@@ -39,7 +39,6 @@
 class DataPromoNotification;
 class ImeControllerClient;
 class ImmersiveContextMus;
-class ImmersiveHandlerFactoryMus;
 class LoginScreenClient;
 class MediaClient;
 class NetworkConnectDelegateChromeOS;
@@ -88,7 +87,6 @@
 
   // Initialized in PreProfileInit if ash config == MASH:
   std::unique_ptr<ImmersiveContextMus> immersive_context_;
-  std::unique_ptr<ImmersiveHandlerFactoryMus> immersive_handler_factory_;
   std::unique_ptr<aura::UserActivityForwarder> user_activity_forwarder_;
   std::unique_ptr<ui::UserActivityDetector> user_activity_detector_;
 
diff --git a/chrome/browser/ui/views/frame/immersive_context_mus.cc b/chrome/browser/ui/views/frame/immersive_context_mus.cc
index 85dc0324..1802e4d8 100644
--- a/chrome/browser/ui/views/frame/immersive_context_mus.cc
+++ b/chrome/browser/ui/views/frame/immersive_context_mus.cc
@@ -40,9 +40,3 @@
 bool ImmersiveContextMus::DoesAnyWindowHaveCapture() {
   return views::DesktopCaptureClient::GetCaptureWindowGlobal() != nullptr;
 }
-
-bool ImmersiveContextMus::IsMouseEventsEnabled() {
-  // TODO: http://crbug.com/640374.
-  NOTIMPLEMENTED();
-  return true;
-}
diff --git a/chrome/browser/ui/views/frame/immersive_context_mus.h b/chrome/browser/ui/views/frame/immersive_context_mus.h
index 4018f56e..51e0193 100644
--- a/chrome/browser/ui/views/frame/immersive_context_mus.h
+++ b/chrome/browser/ui/views/frame/immersive_context_mus.h
@@ -21,7 +21,6 @@
       bool entering) override;
   gfx::Rect GetDisplayBoundsInScreen(views::Widget* widget) override;
   bool DoesAnyWindowHaveCapture() override;
-  bool IsMouseEventsEnabled() override;
 
  private:
   static ImmersiveContextMus* instance_;
diff --git a/chrome/browser/ui/views/frame/immersive_handler_factory_mus.cc b/chrome/browser/ui/views/frame/immersive_handler_factory_mus.cc
deleted file mode 100644
index eabd98f..0000000
--- a/chrome/browser/ui/views/frame/immersive_handler_factory_mus.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/frame/immersive_handler_factory_mus.h"
-
-#include "ash/public/cpp/immersive/immersive_gesture_handler.h"
-#include "base/logging.h"
-
-ImmersiveHandlerFactoryMus::ImmersiveHandlerFactoryMus() {}
-
-ImmersiveHandlerFactoryMus::~ImmersiveHandlerFactoryMus() {}
-
-std::unique_ptr<ash::ImmersiveGestureHandler>
-ImmersiveHandlerFactoryMus::CreateGestureHandler(
-    ash::ImmersiveFullscreenController* controller) {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
diff --git a/chrome/browser/ui/views/frame/immersive_handler_factory_mus.h b/chrome/browser/ui/views/frame/immersive_handler_factory_mus.h
deleted file mode 100644
index ef55a5c..0000000
--- a/chrome/browser/ui/views/frame/immersive_handler_factory_mus.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_HANDLER_FACTORY_MUS_H_
-#define CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_HANDLER_FACTORY_MUS_H_
-
-#include "ash/public/cpp/immersive/immersive_handler_factory.h"
-#include "base/macros.h"
-
-class ImmersiveHandlerFactoryMus : public ash::ImmersiveHandlerFactory {
- public:
-  ImmersiveHandlerFactoryMus();
-  ~ImmersiveHandlerFactoryMus() override;
-
-  // ImmersiveHandlerFactory:
-  std::unique_ptr<ash::ImmersiveGestureHandler> CreateGestureHandler(
-      ash::ImmersiveFullscreenController* controller) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ImmersiveHandlerFactoryMus);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_HANDLER_FACTORY_MUS_H_
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 5cfe397c..9fbe8c92 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -21,6 +21,7 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/bind_test_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -1993,36 +1994,56 @@
                        DragBrowserWindowWhenMajorityOfBoundsInSecondDisplay) {
   // Set the browser's window bounds such that the majority of its bounds
   // resides in the second display.
-  display::Screen* screen = display::Screen::GetScreen();
-  ASSERT_EQ(2, screen->GetNumDisplays());
-  const std::pair<Display, Display> displays = GetDisplays(screen);
-  gfx::Rect browser_bounds =
-      browser()->window()->GetNativeWindow()->GetBoundsInScreen();
-  browser_bounds.set_x(displays.first.bounds().right() -
-                       (browser_bounds.width() / 2) + 20);
-  browser()->window()->GetNativeWindow()->SetBounds(browser_bounds);
-  EXPECT_EQ(
-      displays.first.id(),
-      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
-          .id());
+  const std::pair<Display, Display> displays =
+      GetDisplays(display::Screen::GetScreen());
+
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  {
+    // Moves the browser window through dragging so that the majority of its
+    // bounds are in the secondary display but it's still be in the primary
+    // display. Do not use SetBounds() or related, it may move the browser
+    // window to the secondary display in some configurations like Mash.
+    int target_x = displays.first.bounds().right() -
+                   browser()->window()->GetBounds().width() / 2 + 20;
+    const gfx::Point tab_0_center =
+        GetCenterInScreenCoordinates(tab_strip->tab_at(0));
+    gfx::Point target_point = tab_0_center;
+    target_point.Offset(target_x - browser()->window()->GetBounds().x(),
+                        GetDetachY(tab_strip));
+
+    ASSERT_TRUE(PressInput(tab_0_center));
+    ASSERT_TRUE(DragInputToNotifyWhenDone(
+        tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
+        base::BindRepeating(&DragSingleTabToSeparateWindowInSecondDisplayStep2,
+                            this, target_point)));
+    QuitWhenNotDragging();
+    StopAnimating(tab_strip);
+  }
+  EXPECT_EQ(displays.first.id(),
+            browser()->window()->GetNativeWindow()->GetHost()->GetDisplayId());
 
   // Start dragging the window by the tab strip, and move it only to the edge
   // of the first display. Expect at that point mouse would warp and the window
   // will therefore reside in the second display when mouse is released.
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
   const gfx::Point tab_0_center =
       GetCenterInScreenCoordinates(tab_strip->tab_at(0));
-  gfx::Point target_point = tab_0_center;
-  target_point.Offset(0, GetDetachY(tab_strip));
+  const int offset_x = tab_0_center.x() - browser()->window()->GetBounds().x();
+  const int detach_y = tab_0_center.y() + GetDetachY(tab_strip);
   const int first_display_warp_edge_x = displays.first.bounds().right() - 1;
-  target_point.set_x(first_display_warp_edge_x);
+  const gfx::Point warped_point(displays.second.bounds().x() + 1, detach_y);
 
   ASSERT_TRUE(PressInput(tab_0_center));
   ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep2, this,
-                 target_point)));
-
+      tab_0_center.x(), detach_y, base::BindLambdaForTesting([&]() {
+        // This makes another event on the warped location because the test
+        // system does not create it automatically as the result of pointer
+        // warp.
+        ASSERT_TRUE(DragInputToNotifyWhenDone(
+            first_display_warp_edge_x, detach_y,
+            base::BindRepeating(
+                &DragSingleTabToSeparateWindowInSecondDisplayStep2, this,
+                warped_point)));
+      })));
   QuitWhenNotDragging();
 
   // Should no longer be dragging.
@@ -2036,10 +2057,9 @@
   ASSERT_FALSE(tab_strip->IsDragSessionActive());
 
   // Browser now resides in display 2.
-  EXPECT_EQ(
-      displays.second.id(),
-      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
-          .id());
+  EXPECT_EQ(warped_point.x() - offset_x, browser()->window()->GetBounds().x());
+  EXPECT_EQ(displays.second.id(),
+            browser()->window()->GetNativeWindow()->GetHost()->GetDisplayId());
 }
 
 // Drags from browser to another browser on a second display and releases input.
diff --git a/chrome/browser/ui/webui/browsing_history_handler.cc b/chrome/browser/ui/webui/browsing_history_handler.cc
index 5b62926..20476b0 100644
--- a/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -34,7 +34,7 @@
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/favicon/core/fallback_url_util.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/prefs/pref_service.h"
 #include "components/query_parser/snippet.h"
diff --git a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
index fddcf63..22a3fb4 100644
--- a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
@@ -39,8 +39,6 @@
 #include "components/device_event_log/device_event_log.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -65,10 +63,6 @@
 
 const char kJsDeviceStatusChangedCallback[] =
     "mobile.MobileSetup.deviceStateChanged";
-const char kJsPortalFrameLoadFailedCallback[] =
-    "mobile.MobileSetup.portalFrameLoadError";
-const char kJsPortalFrameLoadCompletedCallback[] =
-    "mobile.MobileSetup.portalFrameLoadCompleted";
 const char kJsGetDeviceInfoCallback[] =
     "mobile.MobileSetupPortal.onGotDeviceInfo";
 const char kJsConnectivityChangedCallback[] =
@@ -631,33 +625,8 @@
   // Set up the chrome://mobilesetup/ source.
   content::URLDataSource::Add(Profile::FromWebUI(web_ui),
                               std::make_unique<MobileSetupUIHTMLSource>());
-
-  content::WebContentsObserver::Observe(web_ui->GetWebContents());
 }
 
 MobileSetupUI::~MobileSetupUI() = default;
 
-void MobileSetupUI::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  NET_LOG(EVENT) << "MobileSetupUI: DidFinishNavigation. Committed: "
-                 << navigation_handle->HasCommitted() << " Frame: "
-                 << navigation_handle->GetRenderFrameHost()->GetFrameName();
-  if (!navigation_handle->HasCommitted() ||
-      navigation_handle->GetRenderFrameHost()->GetFrameName() !=
-          "paymentForm") {
-    return;
-  }
-
-  if (navigation_handle->IsErrorPage()) {
-    NET_LOG(ERROR) << "MobileSetupUI: Error: "
-                   << navigation_handle->GetNetErrorCode();
-    base::Value result_value(-navigation_handle->GetNetErrorCode());
-    web_ui()->CallJavascriptFunctionUnsafe(kJsPortalFrameLoadFailedCallback,
-                                           result_value);
-    return;
-  }
-
-  web_ui()->CallJavascriptFunctionUnsafe(kJsPortalFrameLoadCompletedCallback);
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h
index a98f293..8334f11 100644
--- a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h
+++ b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h
@@ -12,17 +12,12 @@
 
 // A custom WebUI that defines datasource for mobile setup registration page
 // that is used in Chrome OS activate modem and perform plan subscription tasks.
-class MobileSetupUI : public ui::WebDialogUI,
-                      public content::WebContentsObserver {
+class MobileSetupUI : public ui::WebDialogUI {
  public:
   explicit MobileSetupUI(content::WebUI* web_ui);
   ~MobileSetupUI() override;
 
  private:
-  // content::WebContentsObserver overrides.
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-
   DISALLOW_COPY_AND_ASSIGN(MobileSetupUI);
 };
 
diff --git a/chrome/browser/vr/test/gl_test_environment_cmd_buffer.cc b/chrome/browser/vr/test/gl_test_environment_cmd_buffer.cc
index 8d33602..4a9c83f 100644
--- a/chrome/browser/vr/test/gl_test_environment_cmd_buffer.cc
+++ b/chrome/browser/vr/test/gl_test_environment_cmd_buffer.cc
@@ -11,6 +11,7 @@
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/command_buffer/common/context_creation_attribs.h"
 #include "gpu/ipc/gl_in_process_context.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 
 namespace {
 
@@ -65,14 +66,13 @@
   attributes.bind_generates_resource = false;
 
   context_ = std::make_unique<gpu::GLInProcessContext>();
-  auto result = context_->Initialize(nullptr,                 /* service */
-                                     nullptr,                 /* surface */
-                                     true,                    /* offscreen */
-                                     gpu::kNullSurfaceHandle, /* window */
-                                     attributes, gpu::SharedMemoryLimits(),
-                                     nullptr /* memory_buffer_manager */,
-                                     nullptr, /* image_factory */
-                                     base::ThreadTaskRunnerHandle::Get());
+  auto result = context_->Initialize(
+      gpu::GetTestGpuThreadHolder()->GetTaskExecutor(), nullptr, /* surface */
+      true,                                                      /* offscreen */
+      gpu::kNullSurfaceHandle,                                   /* window */
+      attributes, gpu::SharedMemoryLimits(),
+      nullptr /* memory_buffer_manager */, nullptr, /* image_factory */
+      base::ThreadTaskRunnerHandle::Get());
   DCHECK_EQ(result, gpu::ContextResult::kSuccess);
   gles2::SetGLContext(context_->GetImplementation());
 
diff --git a/chrome/browser/vr/test/vr_gl_test_suite.cc b/chrome/browser/vr/test/vr_gl_test_suite.cc
index 286cd19..2cc8dc2 100644
--- a/chrome/browser/vr/test/vr_gl_test_suite.cc
+++ b/chrome/browser/vr/test/vr_gl_test_suite.cc
@@ -9,10 +9,7 @@
 
 #if defined(VR_USE_COMMAND_BUFFER)
 #include "gpu/command_buffer/client/gles2_lib.h"  // nogncheck
-#include "gpu/config/gpu_info_collector.h"        // nogncheck
-#include "gpu/config/gpu_preferences.h"           // nogncheck
-#include "gpu/config/gpu_util.h"                  // nogncheck
-#include "gpu/ipc/in_process_command_buffer.h"    // nogncheck
+#include "gpu/ipc/test_gpu_thread_holder.h"       // nogncheck
 #endif  // defined(VR_USE_COMMAND_BUFFER)
 
 namespace vr {
@@ -25,18 +22,12 @@
   gl::GLImageTestSupport::InitializeGL(base::nullopt);
 
 #if defined(VR_USE_COMMAND_BUFFER)
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  gpu::GPUInfo gpu_info;
-  gpu::CollectGraphicsInfoForTesting(&gpu_info);
-  gpu::GpuFeatureInfo gpu_feature_info = gpu::ComputeGpuFeatureInfo(
-      gpu_info, gpu::GpuPreferences(), command_line, nullptr);
   // Always enable gpu and oop raster, regardless of platform and blacklist.
-  gpu_feature_info.status_values[gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION] =
+  auto* gpu_feature_info = gpu::GetTestGpuThreadHolder()->GetGpuFeatureInfo();
+  gpu_feature_info->status_values[gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION] =
       gpu::kGpuFeatureStatusEnabled;
-  gpu_feature_info.status_values[gpu::GPU_FEATURE_TYPE_OOP_RASTERIZATION] =
+  gpu_feature_info->status_values[gpu::GPU_FEATURE_TYPE_OOP_RASTERIZATION] =
       gpu::kGpuFeatureStatusEnabled;
-  gpu::InProcessCommandBuffer::InitializeDefaultServiceForTesting(
-      gpu_feature_info);
   gles2::Initialize();
 #endif  // defined(VR_USE_COMMAND_BUFFER)
 }
diff --git a/chrome/test/data/login/easy_unlock/auth_code_login.html b/chrome/test/data/login/easy_unlock/auth_code_login.html
deleted file mode 100644
index e95bf1f8..0000000
--- a/chrome/test/data/login/easy_unlock/auth_code_login.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<script>
-
-// Note this must be consistent with the one defined in fake_gaia.cc.
-var AUTH_CODE = 'fake-auth-code';
-
-function onMessage(e) {
-  var targetWindow = e.source;
-  targetWindow.postMessage({
-    'type': 'authorizationCode',
-    'authorizationCode': AUTH_CODE
-  }, e.origin);
-}
-
-window.addEventListener('message', onMessage);
-
-</script>
-
-<body>
-</body>
-</html>
diff --git a/chrome/test/data/login/easy_unlock/auto_pair_failure/easy_unlock_background.js b/chrome/test/data/login/easy_unlock/auto_pair_failure/easy_unlock_background.js
deleted file mode 100644
index 435a0e2..0000000
--- a/chrome/test/data/login/easy_unlock/auto_pair_failure/easy_unlock_background.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// 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.
-
-chrome.easyUnlockPrivate.onStartAutoPairing.addListener(function() {
-  chrome.easyUnlockPrivate.setAutoPairingResult({
-    success: false,
-    errorMessage: 'Test failure'
-  });
-});
diff --git a/chrome/test/data/login/easy_unlock/auto_pair_success/easy_unlock_background.js b/chrome/test/data/login/easy_unlock/auto_pair_success/easy_unlock_background.js
deleted file mode 100644
index f63e4632..0000000
--- a/chrome/test/data/login/easy_unlock/auto_pair_success/easy_unlock_background.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.
-
-chrome.easyUnlockPrivate.onStartAutoPairing.addListener(function() {
-  chrome.easyUnlockPrivate.setPermitAccess({
-    permitId: 'fake_permit_id',
-    id: 'fake_id',
-    type: 'access',
-    data: 'ZmFrZV9kYXRh'  // 'fake_data'
-  });
-  chrome.easyUnlockPrivate.setRemoteDevices([
-    {
-      bluetoothAddress: '11:11:11:11:11:11',
-      name: 'fake_remote_device',
-      permitRecord: {
-        permitId: 'fake_permit_id',
-        id: 'fake_id',
-        type: 'license',
-        data: 'ZmFrZV9kYXRh'  // 'fake_data'
-      },
-      psk: 'ZmFrZV9wc2s='  // 'fake_psk'
-    },
-  ]);
-  chrome.easyUnlockPrivate.setAutoPairingResult({success: true});
-});
diff --git a/chrome/test/data/webui/settings/languages_page_tests.js b/chrome/test/data/webui/settings/languages_page_tests.js
index ba007cb..942ad5b 100644
--- a/chrome/test/data/webui/settings/languages_page_tests.js
+++ b/chrome/test/data/webui/settings/languages_page_tests.js
@@ -330,6 +330,19 @@
         }, settings.kMenuCloseDelay + 1);
       });
 
+      test('toggle translate for target language', function() {
+        // Open options for 'en'.
+        const languageOptionsDropdownTrigger =
+            languagesCollapse.querySelectorAll('button')[0];
+        assertTrue(!!languageOptionsDropdownTrigger);
+        languageOptionsDropdownTrigger.click();
+        assertTrue(actionMenu.open);
+
+        // 'en' does not support.
+        const translateOption = getMenuItem('offerToTranslateInThisLanguage');
+        assertTrue(translateOption.disabled);
+      });
+
       test('disable translate hides language-specific option', function() {
         // Disables translate.
         languageHelper.setPrefValue('translate.enabled', false);
diff --git a/chromecast/browser/cast_media_blocker_unittest.cc b/chromecast/browser/cast_media_blocker_unittest.cc
index ee7d25e..bc96121 100644
--- a/chromecast/browser/cast_media_blocker_unittest.cc
+++ b/chromecast/browser/cast_media_blocker_unittest.cc
@@ -37,7 +37,8 @@
   MOCK_METHOD0(StartDucking, void());
   MOCK_METHOD0(StopDucking, void());
   MOCK_METHOD1(SetDuckingVolumeMultiplier, void(double));
-  MOCK_METHOD1(DidReceiveAction, void(blink::mojom::MediaSessionAction));
+  MOCK_METHOD1(DidReceiveAction,
+               void(media_session::mojom::MediaSessionAction));
   MOCK_METHOD1(AddObserver, void(content::MediaSessionObserver*));
   MOCK_METHOD1(AddObserver,
                void(media_session::mojom::MediaSessionObserverPtr));
diff --git a/chromeos/SECURITY_OWNERS b/chromeos/SECURITY_OWNERS
new file mode 100644
index 0000000..829175b
--- /dev/null
+++ b/chromeos/SECURITY_OWNERS
@@ -0,0 +1,3 @@
+jorgelo@chromium.org
+kerrnel@chromium.org
+mnissler@chromium.org
diff --git a/chromeos/dbus/OWNERS b/chromeos/dbus/OWNERS
index b6c283f..fa8612f 100644
--- a/chromeos/dbus/OWNERS
+++ b/chromeos/dbus/OWNERS
@@ -1,5 +1,6 @@
 stevenjb@chromium.org
 hashimoto@chromium.org
+derat@chromium.org
 
 per-file *audio*=jennyz@chromium.org
 per-file *audio*=hychao@chromium.org
diff --git a/components/arc/common/ARC_SECURITY_OWNERS b/components/arc/common/ARC_SECURITY_OWNERS
index f3326cf..84c8d11c9 100644
--- a/components/arc/common/ARC_SECURITY_OWNERS
+++ b/components/arc/common/ARC_SECURITY_OWNERS
@@ -1,4 +1,2 @@
 # Prefer ARC++ security owners for changes to ARC++ IPC.
-jorgelo@chromium.org
-kerrnel@chromium.org
-mnissler@chromium.org
+file://chromeos/SECURITY_OWNERS
diff --git a/components/arc/common/auth.mojom b/components/arc/common/auth.mojom
index 1b3fac57..098b0e5f 100644
--- a/components/arc/common/auth.mojom
+++ b/components/arc/common/auth.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 17
+// Next MinVersion: 18
 
 module arc.mojom;
 
@@ -172,6 +172,16 @@
   [MinVersion=9] ACCOUNT_CHECK_MILLISECONDS = 4,
 };
 
+// These values describe the type of account update.
+[Extensible]
+enum AccountUpdateType {
+  // An account was updated or inserted in Chrome OS Account Manager.
+  UPSERT = 0,
+
+  // An account was removed from Chrome OS Account Manager.
+  REMOVAL = 1,
+};
+
 // The necessary information for Android to sign in and provision itself.
 struct AccountInfo {
   // Name of account, used to map to existing Android account.
@@ -249,7 +259,7 @@
       => (ArcSignInStatus status, AccountInfo? account_info);
 };
 
-// Next Method ID: 3
+// Next Method ID: 4
 interface AuthInstance {
   // DEPRECATED: Please use Init@2 instead.
   InitDeprecated@0(AuthHost host_ptr);
@@ -267,4 +277,11 @@
   // than SUCCESS, and |account_info| is null.
   [MinVersion=5] OnAccountInfoReadyDeprecated@1(
       AccountInfo? account_info, [MinVersion=10] ArcSignInStatus status);
+
+  // A notification that an account was updated (or inserted, or removed; see
+  // |AccountUpdateType|) in Chrome OS Account Manager.
+  // This notification is sent for the Primary/Device Account and Secondary
+  // Accounts, both.
+  [MinVersion=17] OnAccountUpdated@3(
+      string account_name, AccountUpdateType update_type);
 };
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 987229a..70e97fd 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -6317,8 +6317,12 @@
   // of fields enforced.)
   {
     base::test::ScopedFeatureList features;
-    features.InitAndEnableFeature(
-        kAutofillEnforceMinRequiredFieldsForHeuristics);
+    features.InitWithFeatures(
+        // Enabled
+        {kAutofillEnforceMinRequiredFieldsForHeuristics,
+         kAutofillEnforceMinRequiredFieldsForQuery},
+        // Disabled
+        {});
     base::HistogramTester histogram_tester;
     base::UserActionTester user_action_tester;
     autofill_manager_->OnFormSubmitted(
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index 17fd51f..6dd7a75 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -1483,8 +1483,21 @@
 
 void ProfileSyncService::SetSyncAllowedByPlatform(bool allowed) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (sync_allowed_by_platform_ == allowed) {
+    return;
+  }
+
   sync_allowed_by_platform_ = allowed;
-  // TODO(crbug.com/867901): Start or stop Sync as needed here.
+
+  if (!sync_allowed_by_platform_) {
+    StopImpl(KEEP_DATA);
+    // TODO(crbug.com/856179): Evaluate whether we can get away without a full
+    // restart (i.e. just reconfigure plus whatever cleanup is necessary). See
+    // also similar comment in RequestStop.
+    if (IsStandaloneTransportEnabled()) {
+      startup_controller_->TryStart(/*force_immediate=*/false);
+    }
+  }
 }
 
 void ProfileSyncService::ConfigureDataTypeManager(
diff --git a/components/crash/content/browser/child_exit_observer_android.cc b/components/crash/content/browser/child_exit_observer_android.cc
index fe3c572..5d9aa4c6 100644
--- a/components/crash/content/browser/child_exit_observer_android.cc
+++ b/components/crash/content/browser/child_exit_observer_android.cc
@@ -171,6 +171,7 @@
   info.pid = data.GetProcess().Pid();
   info.process_type = static_cast<content::ProcessType>(data.process_type);
   info.app_state = base::android::ApplicationStatusListener::GetState();
+  info.normal_termination = content_info.clean_exit;
   PopulateTerminationInfo(content_info, &info);
   browser_child_process_info_.emplace(data.id, info);
   // Subsequent BrowserChildProcessHostDisconnected will call OnChildExit.
diff --git a/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc b/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
index 6ab90298..380b9ab 100644
--- a/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
+++ b/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
@@ -157,6 +157,22 @@
       nullptr);
 }
 
+TEST_F(CrashMetricsReporterTest, NormalTerminationIsNotOOMUtilityProcess) {
+  ChildExitObserver::TerminationInfo termination_info;
+  termination_info.process_host_id = 1;
+  termination_info.pid = base::kNullProcessHandle;
+  termination_info.process_type = content::PROCESS_TYPE_UTILITY;
+  termination_info.app_state =
+      base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+  termination_info.normal_termination = true;
+  termination_info.binding_state = base::android::ChildBindingState::STRONG;
+  termination_info.was_killed_intentionally_by_browser = false;
+  termination_info.was_oom_protected_status = true;
+  termination_info.renderer_has_visible_clients = true;
+
+  TestOomCrashProcessing(termination_info, {}, nullptr);
+}
+
 TEST_F(CrashMetricsReporterTest, UtilityProcessAll) {
   ChildExitObserver::TerminationInfo termination_info;
   termination_info.process_host_id = 1;
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index b4e5204..3b00214 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/buildflag_header.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
@@ -41,9 +40,8 @@
 generate_jni_registration("cronet_jni_registration") {
   target = ":cronet_jni_apk"
   output = "$root_gen_dir/components/cronet/android/${target_name}.h"
-  exception_files = jni_exception_files
 
-  exception_files += [
+  exception_files = [
     "//base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
     "//base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
     "//base/android/java/src/org/chromium/base/SysUtils.java",
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index a7419ff..65fbc3d0 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -150,7 +150,9 @@
       case network::mojom::ConnectionType::CONNECTION_ETHERNET:
         break;
       case network::mojom::ConnectionType::CONNECTION_WIFI:
-#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_WIN)
+// Get WiFi SSID only on Android since calling it on non-Android
+// platforms may result in hung IO loop. See https://crbg.com/896296.
+#if defined(OS_ANDROID)
         ssid_mccmnc = net::GetWifiSSID();
 #endif
         break;
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 796dd64..4c98e9e 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -960,7 +960,8 @@
   params.parent =
       parent_ ? parent_
               : ash::Shell::GetContainer(
-                    ash::Shell::GetRootWindowForNewWindows(), container_);
+                    WMHelper::GetInstance()->GetRootWindowForNewWindows(),
+                    container_);
   params.bounds = gfx::Rect(origin_, gfx::Size());
   bool activatable = activatable_;
   // ShellSurfaces in system modal container are only activatable if input
diff --git a/components/exo/wm_helper.h b/components/exo/wm_helper.h
index 42c7b55..9e06efb 100644
--- a/components/exo/wm_helper.h
+++ b/components/exo/wm_helper.h
@@ -91,6 +91,7 @@
   virtual aura::Window* GetPrimaryDisplayContainer(int container_id) = 0;
   virtual aura::Window* GetActiveWindow() const = 0;
   virtual aura::Window* GetFocusedWindow() const = 0;
+  virtual aura::Window* GetRootWindowForNewWindows() const = 0;
   virtual aura::client::CursorClient* GetCursorClient() = 0;
   virtual void AddPreTargetHandler(ui::EventHandler* handler) = 0;
   virtual void PrependPreTargetHandler(ui::EventHandler* handler) = 0;
diff --git a/components/exo/wm_helper_chromeos.cc b/components/exo/wm_helper_chromeos.cc
index 883fae7..3218238 100644
--- a/components/exo/wm_helper_chromeos.cc
+++ b/components/exo/wm_helper_chromeos.cc
@@ -170,6 +170,10 @@
   return focus_client->GetFocusedWindow();
 }
 
+aura::Window* WMHelperChromeOS::GetRootWindowForNewWindows() const {
+  return ash::Shell::GetRootWindowForNewWindows();
+}
+
 aura::client::CursorClient* WMHelperChromeOS::GetCursorClient() {
   return aura::client::GetCursorClient(ash::Shell::GetPrimaryRootWindow());
 }
diff --git a/components/exo/wm_helper_chromeos.h b/components/exo/wm_helper_chromeos.h
index 299d474d..b3ae59f0 100644
--- a/components/exo/wm_helper_chromeos.h
+++ b/components/exo/wm_helper_chromeos.h
@@ -86,6 +86,7 @@
   aura::Window* GetPrimaryDisplayContainer(int container_id) override;
   aura::Window* GetActiveWindow() const override;
   aura::Window* GetFocusedWindow() const override;
+  aura::Window* GetRootWindowForNewWindows() const override;
   aura::client::CursorClient* GetCursorClient() override;
   void AddPreTargetHandler(ui::EventHandler* handler) override;
   void PrependPreTargetHandler(ui::EventHandler* handler) override;
diff --git a/components/favicon/core/BUILD.gn b/components/favicon/core/BUILD.gn
index 798ad7bc..aed64ff 100644
--- a/components/favicon/core/BUILD.gn
+++ b/components/favicon/core/BUILD.gn
@@ -26,8 +26,8 @@
     "favicon_util.h",
     "features.cc",
     "features.h",
-    "large_icon_service.cc",
-    "large_icon_service.h",
+    "large_icon_service_impl.cc",
+    "large_icon_service_impl.h",
   ]
 
   deps = [
@@ -52,7 +52,7 @@
     "fallback_url_util_unittest.cc",
     "favicon_handler_unittest.cc",
     "favicon_service_impl_unittest.cc",
-    "large_icon_service_unittest.cc",
+    "large_icon_service_impl_unittest.cc",
   ]
 
   deps = [
diff --git a/components/favicon/core/large_icon_service.cc b/components/favicon/core/large_icon_service_impl.cc
similarity index 98%
rename from components/favicon/core/large_icon_service.cc
rename to components/favicon/core/large_icon_service_impl.cc
index 9c2d878..23cc8cc 100644
--- a/components/favicon/core/large_icon_service.cc
+++ b/components/favicon/core/large_icon_service_impl.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 "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 
 #include <algorithm>
 #include <memory>
@@ -423,7 +423,8 @@
     DLOG(WARNING) << "large icon server fetch empty " << server_request_url;
     favicon_service->UnableToDownloadFavicon(GURL(server_request_url));
     callback.Run(
-        metadata.http_response_code == net::URLFetcher::RESPONSE_CODE_INVALID
+        metadata.http_response_code ==
+                image_fetcher::RequestMetadata::RESPONSE_CODE_INVALID
             ? GoogleFaviconServerRequestStatus::FAILURE_CONNECTION_ERROR
             : GoogleFaviconServerRequestStatus::FAILURE_HTTP_ERROR);
     ReportDownloadedSize(0);
@@ -592,7 +593,8 @@
   //   a large icon is known but its bitmap is not available.
   return favicon_service_->GetLargestRawFaviconForPageURL(
       page_url, large_icon_types_, max_size_in_pixel,
-      base::Bind(&LargeIconWorker::OnIconLookupComplete, worker, page_url),
+      base::BindRepeating(&LargeIconWorker::OnIconLookupComplete, worker,
+                          page_url),
       tracker);
 }
 
diff --git a/components/favicon/core/large_icon_service.h b/components/favicon/core/large_icon_service_impl.h
similarity index 94%
rename from components/favicon/core/large_icon_service.h
rename to components/favicon/core/large_icon_service_impl.h
index 1d1a32c..9b389fb 100644
--- a/components/favicon/core/large_icon_service.h
+++ b/components/favicon/core/large_icon_service_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_H_
-#define COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_H_
+#ifndef COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_IMPL_H_
+#define COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_IMPL_H_
 
 #include <memory>
 #include <vector>
@@ -32,9 +32,8 @@
 // the favicon service.
 class LargeIconService : public KeyedService {
  public:
-  LargeIconService(
-      FaviconService* favicon_service,
-      std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher);
+  LargeIconService(FaviconService* favicon_service,
+                   std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher);
   ~LargeIconService() override;
 
   // Requests the best large icon for the page at |page_url|.
@@ -145,4 +144,4 @@
 
 }  // namespace favicon
 
-#endif  // COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_H_
+#endif  // COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_IMPL_H_
diff --git a/components/favicon/core/large_icon_service_unittest.cc b/components/favicon/core/large_icon_service_impl_unittest.cc
similarity index 98%
rename from components/favicon/core/large_icon_service_unittest.cc
rename to components/favicon/core/large_icon_service_impl_unittest.cc
index a45a41a..36e7b56 100644
--- a/components/favicon/core/large_icon_service_unittest.cc
+++ b/components/favicon/core/large_icon_service_impl_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 "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 
 #include <memory>
 #include <string>
@@ -523,14 +523,15 @@
     if (GetParam()) {
       large_icon_service_.GetLargeIconOrFallbackStyle(
           page_url, min_source_size_in_pixel, desired_size_in_pixel,
-          base::Bind(&LargeIconServiceGetterTest::RawBitmapResultCallback,
-                     base::Unretained(this)),
+          base::BindRepeating(
+              &LargeIconServiceGetterTest::RawBitmapResultCallback,
+              base::Unretained(this)),
           &cancelable_task_tracker_);
     } else {
       large_icon_service_.GetLargeIconImageOrFallbackStyle(
           page_url, min_source_size_in_pixel, desired_size_in_pixel,
-          base::Bind(&LargeIconServiceGetterTest::ImageResultCallback,
-                     base::Unretained(this)),
+          base::BindRepeating(&LargeIconServiceGetterTest::ImageResultCallback,
+                              base::Unretained(this)),
           &cancelable_task_tracker_);
     }
     scoped_task_environment_.RunUntilIdle();
diff --git a/components/language/core/common/language_experiments.cc b/components/language/core/common/language_experiments.cc
index cfd1112..7f6003e 100644
--- a/components/language/core/common/language_experiments.cc
+++ b/components/language/core/common/language_experiments.cc
@@ -26,6 +26,7 @@
 const char kEnforceRankerKey[] = "enforce_ranker";
 const char kOverrideModelHeuristicValue[] = "heuristic";
 const char kOverrideModelGeoValue[] = "geo";
+const char kOverrideModelDefaultValue[] = "default";
 
 OverrideLanguageModel GetOverrideLanguageModel() {
   std::map<std::string, std::string> params;
diff --git a/components/language/core/common/language_experiments.h b/components/language/core/common/language_experiments.h
index 544b118..b3a091f 100644
--- a/components/language/core/common/language_experiments.h
+++ b/components/language/core/common/language_experiments.h
@@ -27,6 +27,8 @@
 extern const char kEnforceRankerKey[];
 extern const char kOverrideModelHeuristicValue[];
 extern const char kOverrideModelGeoValue[];
+extern const char kOverrideModelDefaultValue[];
+extern const char kBackoffThresholdKey[];
 
 enum class OverrideLanguageModel {
   DEFAULT,
diff --git a/components/leveldb_proto/BUILD.gn b/components/leveldb_proto/BUILD.gn
index 523c133a..8ebf8d3 100644
--- a/components/leveldb_proto/BUILD.gn
+++ b/components/leveldb_proto/BUILD.gn
@@ -11,6 +11,8 @@
     "proto_database_impl.h",
     "proto_leveldb_wrapper.cc",
     "proto_leveldb_wrapper.h",
+    "proto_leveldb_wrapper_metrics.cc",
+    "proto_leveldb_wrapper_metrics.h",
     "unique_proto_database.h",
   ]
 
diff --git a/components/leveldb_proto/leveldb_database.cc b/components/leveldb_proto/leveldb_database.cc
index 29b8f60..0b574974 100644
--- a/components/leveldb_proto/leveldb_database.cc
+++ b/components/leveldb_proto/leveldb_database.cc
@@ -80,7 +80,8 @@
   if (open_histogram_)
     open_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(status));
   if (destroy_on_corruption && status.IsCorruption()) {
-    if (!Destroy())
+    auto destroy_status = Destroy();
+    if (!destroy_status.ok())
       return status;
     status = leveldb_env::OpenDB(open_options_, path, &db_);
     // Intentionally do not log the status of the second open. Doing so destroys
@@ -108,7 +109,9 @@
 }
 
 bool LevelDB::Save(const base::StringPairs& entries_to_save,
-                   const std::vector<std::string>& keys_to_remove) {
+                   const std::vector<std::string>& keys_to_remove,
+                   leveldb::Status* status) {
+  DCHECK(status);
   DFAKE_SCOPED_LOCK(thread_checker_);
   if (!db_)
     return false;
@@ -123,17 +126,19 @@
   leveldb::WriteOptions options;
   options.sync = true;
 
-  leveldb::Status status = db_->Write(options, &updates);
-  if (status.ok())
+  *status = db_->Write(options, &updates);
+  if (status->ok())
     return true;
 
   DLOG(WARNING) << "Failed writing leveldb_proto entries: "
-                << status.ToString();
+                << status->ToString();
   return false;
 }
 
 bool LevelDB::UpdateWithRemoveFilter(const base::StringPairs& entries_to_save,
-                                     const KeyFilter& delete_key_filter) {
+                                     const KeyFilter& delete_key_filter,
+                                     leveldb::Status* status) {
+  DCHECK(status);
   DFAKE_SCOPED_LOCK(thread_checker_);
   if (!db_)
     return false;
@@ -157,12 +162,12 @@
 
   leveldb::WriteOptions write_options;
   write_options.sync = true;
-  leveldb::Status status = db_->Write(write_options, &updates);
-  if (status.ok())
+  *status = db_->Write(write_options, &updates);
+  if (status->ok())
     return true;
 
   DLOG(WARNING) << "Failed deleting leveldb_proto entries: "
-                << status.ToString();
+                << status->ToString();
   return false;
 }
 
@@ -243,36 +248,40 @@
   return true;
 }
 
-bool LevelDB::Get(const std::string& key, bool* found, std::string* entry) {
+bool LevelDB::Get(const std::string& key,
+                  bool* found,
+                  std::string* entry,
+                  leveldb::Status* status) {
+  DCHECK(status);
   DFAKE_SCOPED_LOCK(thread_checker_);
   if (!db_)
     return false;
 
   leveldb::ReadOptions options;
-  leveldb::Status status = db_->Get(options, key, entry);
-  if (status.ok()) {
+  *status = db_->Get(options, key, entry);
+  if (status->ok()) {
     *found = true;
     return true;
   }
-  if (status.IsNotFound()) {
+  if (status->IsNotFound()) {
     *found = false;
     return true;
   }
 
   DLOG(WARNING) << "Failed loading leveldb_proto entry with key \"" << key
-                << "\": " << status.ToString();
+                << "\": " << status->ToString();
   return false;
 }
 
-bool LevelDB::Destroy() {
+leveldb::Status LevelDB::Destroy() {
   db_.reset();
   const std::string path = database_dir_.AsUTF8Unsafe();
-  const leveldb::Status s = leveldb::DestroyDB(path, open_options_);
-  if (!s.ok())
-    LOG(WARNING) << "Unable to destroy " << path << ": " << s.ToString();
+  leveldb::Status status = leveldb::DestroyDB(path, open_options_);
+  if (!status.ok())
+    LOG(WARNING) << "Unable to destroy " << path << ": " << status.ToString();
   if (destroy_histogram_)
-    destroy_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(s));
-  return s.ok();
+    destroy_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(status));
+  return status;
 }
 
 bool LevelDB::GetApproximateMemoryUse(uint64_t* approx_mem) {
diff --git a/components/leveldb_proto/leveldb_database.h b/components/leveldb_proto/leveldb_database.h
index a226005c0..af300aef 100644
--- a/components/leveldb_proto/leveldb_database.h
+++ b/components/leveldb_proto/leveldb_database.h
@@ -49,9 +49,11 @@
                                bool destroy_on_corruption);
 
   virtual bool Save(const base::StringPairs& pairs_to_save,
-                    const std::vector<std::string>& keys_to_remove);
+                    const std::vector<std::string>& keys_to_remove,
+                    leveldb::Status* status);
   virtual bool UpdateWithRemoveFilter(const base::StringPairs& entries_to_save,
-                                      const KeyFilter& delete_key_filter);
+                                      const KeyFilter& delete_key_filter,
+                                      leveldb::Status* status);
 
   virtual bool Load(std::vector<std::string>* entries);
   virtual bool LoadWithFilter(const KeyFilter& filter,
@@ -73,10 +75,13 @@
       const std::string& target_prefix);
 
   virtual bool LoadKeys(std::vector<std::string>* keys);
-  virtual bool Get(const std::string& key, bool* found, std::string* entry);
+  virtual bool Get(const std::string& key,
+                   bool* found,
+                   std::string* entry,
+                   leveldb::Status* status);
   // Close (if currently open) and then destroy (i.e. delete) the database
   // directory.
-  virtual bool Destroy();
+  virtual leveldb::Status Destroy();
 
   // Returns true if we successfully read the approximate memory usage property
   // from the LevelDB.
diff --git a/components/leveldb_proto/proto_leveldb_wrapper.cc b/components/leveldb_proto/proto_leveldb_wrapper.cc
index e82e4ae..7675f4d 100644
--- a/components/leveldb_proto/proto_leveldb_wrapper.cc
+++ b/components/leveldb_proto/proto_leveldb_wrapper.cc
@@ -4,23 +4,28 @@
 
 #include "components/leveldb_proto/proto_leveldb_wrapper.h"
 
+#include "components/leveldb_proto/proto_leveldb_wrapper_metrics.h"
+
 namespace leveldb_proto {
 
 namespace {
 
 void RunInitCallback(typename ProtoLevelDBWrapper::InitCallback callback,
-                     const bool* success) {
-  std::move(callback).Run(*success);
+                     const leveldb::Status* status) {
+  std::move(callback).Run(status->ok());
 }
 
 inline void InitFromTaskRunner(LevelDB* database,
                                const base::FilePath& database_dir,
                                const leveldb_env::Options& options,
-                               bool* success) {
-  DCHECK(success);
+                               bool destroy_on_corruption,
+                               leveldb::Status* status,
+                               const std::string& client_id) {
+  DCHECK(status);
 
   // TODO(cjhopman): Histogram for database size.
-  *success = database->Init(database_dir, options);
+  *status = database->Init(database_dir, options, destroy_on_corruption);
+  ProtoLevelDBWrapperMetrics::RecordInit(client_id, *status);
 }
 
 void RunDestroyCallback(typename ProtoLevelDBWrapper::DestroyCallback callback,
@@ -28,10 +33,14 @@
   std::move(callback).Run(*success);
 }
 
-inline void DestroyFromTaskRunner(LevelDB* database, bool* success) {
+inline void DestroyFromTaskRunner(LevelDB* database,
+                                  bool* success,
+                                  const std::string& client_id) {
   CHECK(success);
 
-  *success = database->Destroy();
+  auto status = database->Destroy();
+  *success = status.ok();
+  ProtoLevelDBWrapperMetrics::RecordDestroy(client_id, *success);
 }
 
 void RunLoadKeysCallback(
@@ -43,11 +52,13 @@
 
 inline void LoadKeysFromTaskRunner(LevelDB* database,
                                    std::vector<std::string>* keys,
-                                   bool* success) {
+                                   bool* success,
+                                   const std::string& client_id) {
   DCHECK(success);
   DCHECK(keys);
   keys->clear();
   *success = database->LoadKeys(keys);
+  ProtoLevelDBWrapperMetrics::RecordLoadKeys(client_id, *success);
 }
 
 }  // namespace
@@ -67,13 +78,16 @@
   DCHECK(!db_);
   DCHECK(database);
   db_ = database;
-  bool* success = new bool(false);
+  leveldb::Status* status = new leveldb::Status();
+  // Sets |destroy_on_corruption| to true to mimic the original behaviour for
+  // now.
   task_runner_->PostTaskAndReply(
       FROM_HERE,
       base::BindOnce(InitFromTaskRunner, base::Unretained(db_), database_dir,
-                     options, success),
+                     options, true /* destroy_on_corruption */, status,
+                     metrics_id_),
       base::BindOnce(RunInitCallback, std::move(callback),
-                     base::Owned(success)));
+                     base::Owned(status)));
 }
 
 void ProtoLevelDBWrapper::Destroy(
@@ -84,7 +98,8 @@
   bool* success = new bool(false);
   task_runner_->PostTaskAndReply(
       FROM_HERE,
-      base::BindOnce(DestroyFromTaskRunner, base::Unretained(db_), success),
+      base::BindOnce(DestroyFromTaskRunner, base::Unretained(db_), success,
+                     metrics_id_),
       base::BindOnce(RunDestroyCallback, std::move(callback),
                      base::Owned(success)));
 }
@@ -95,13 +110,17 @@
   auto success = std::make_unique<bool>(false);
   auto keys = std::make_unique<std::vector<std::string>>();
   auto load_task = base::BindOnce(LoadKeysFromTaskRunner, base::Unretained(db_),
-                                  keys.get(), success.get());
+                                  keys.get(), success.get(), metrics_id_);
   task_runner_->PostTaskAndReply(
       FROM_HERE, std::move(load_task),
       base::BindOnce(RunLoadKeysCallback, std::move(callback),
                      std::move(success), std::move(keys)));
 }
 
+void ProtoLevelDBWrapper::SetMetricsId(const std::string& id) {
+  metrics_id_ = id;
+}
+
 bool ProtoLevelDBWrapper::GetApproximateMemoryUse(uint64_t* approx_mem_use) {
   return db_->GetApproximateMemoryUse(approx_mem_use);
 }
diff --git a/components/leveldb_proto/proto_leveldb_wrapper.h b/components/leveldb_proto/proto_leveldb_wrapper.h
index 0a2be1a..9f0f30f 100644
--- a/components/leveldb_proto/proto_leveldb_wrapper.h
+++ b/components/leveldb_proto/proto_leveldb_wrapper.h
@@ -16,6 +16,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/threading/thread_checker.h"
 #include "components/leveldb_proto/leveldb_database.h"
+#include "components/leveldb_proto/proto_leveldb_wrapper_metrics.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
 namespace base {
@@ -125,7 +126,10 @@
                         const leveldb_env::Options& options,
                         InitCallback callback);
 
+  void SetMetricsId(const std::string& id);
+
   bool GetApproximateMemoryUse(uint64_t* approx_mem_use);
+
   const scoped_refptr<base::SequencedTaskRunner>& task_runner();
 
  private:
@@ -136,6 +140,10 @@
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   LevelDB* db_ = nullptr;
 
+  // The identifier used when recording metrics to determine the source of the
+  // LevelDB calls, likely the database client name.
+  std::string metrics_id_ = "Default";
+
   DISALLOW_COPY_AND_ASSIGN(ProtoLevelDBWrapper);
 };
 
@@ -179,7 +187,8 @@
     std::unique_ptr<typename ProtoLevelDBWrapper::Internal<T>::KeyEntryVector>
         entries_to_save,
     std::unique_ptr<KeyVector> keys_to_remove,
-    bool* success) {
+    bool* success,
+    const std::string& client_id) {
   DCHECK(success);
 
   // Serialize the values from Proto to string before passing on to database.
@@ -189,7 +198,9 @@
         std::make_pair(pair.first, pair.second.SerializeAsString()));
   }
 
-  *success = database->Save(pairs_to_save, *keys_to_remove);
+  leveldb::Status status;
+  *success = database->Save(pairs_to_save, *keys_to_remove, &status);
+  ProtoLevelDBWrapperMetrics::RecordUpdate(client_id, *success, status);
 }
 
 template <typename T>
@@ -198,7 +209,8 @@
     std::unique_ptr<typename ProtoLevelDBWrapper::Internal<T>::KeyEntryVector>
         entries_to_save,
     const LevelDB::KeyFilter& delete_key_filter,
-    bool* success) {
+    bool* success,
+    const std::string& client_id) {
   DCHECK(success);
 
   // Serialize the values from Proto to string before passing on to database.
@@ -208,7 +220,10 @@
         std::make_pair(pair.first, pair.second.SerializeAsString()));
   }
 
-  *success = database->UpdateWithRemoveFilter(pairs_to_save, delete_key_filter);
+  leveldb::Status status;
+  *success = database->UpdateWithRemoveFilter(pairs_to_save, delete_key_filter,
+                                              &status);
+  ProtoLevelDBWrapperMetrics::RecordUpdate(client_id, *success, status);
 }
 
 template <typename T>
@@ -217,7 +232,8 @@
                                       const leveldb::ReadOptions& options,
                                       const std::string& target_prefix,
                                       std::map<std::string, T>* keys_entries,
-                                      bool* success) {
+                                      bool* success,
+                                      const std::string& client_id) {
   DCHECK(success);
   DCHECK(keys_entries);
 
@@ -236,6 +252,8 @@
 
     keys_entries->insert(std::make_pair(pair.first, entry));
   }
+
+  ProtoLevelDBWrapperMetrics::RecordLoadKeysAndEntries(client_id, *success);
 }
 
 template <typename T>
@@ -244,7 +262,8 @@
                                const leveldb::ReadOptions& options,
                                const std::string& target_prefix,
                                std::vector<T>* entries,
-                               bool* success) {
+                               bool* success,
+                               const std::string& client_id) {
   DCHECK(success);
   DCHECK(entries);
 
@@ -263,6 +282,8 @@
 
     entries->push_back(entry);
   }
+
+  ProtoLevelDBWrapperMetrics::RecordLoadEntries(client_id, *success);
 }
 
 template <typename T>
@@ -270,27 +291,22 @@
                             const std::string& key,
                             T* entry,
                             bool* found,
-                            bool* success) {
+                            bool* success,
+                            const std::string& client_id) {
   DCHECK(success);
   DCHECK(found);
   DCHECK(entry);
 
   std::string serialized_entry;
-  *success = database->Get(key, found, &serialized_entry);
+  leveldb::Status status;
+  *success = database->Get(key, found, &serialized_entry, &status);
 
-  if (!*success) {
-    *found = false;
-    return;
-  }
-
-  if (!*found)
-    return;
-
-  if (!entry->ParseFromString(serialized_entry)) {
+  if (*found && !entry->ParseFromString(serialized_entry)) {
     *found = false;
     DLOG(WARNING) << "Unable to parse leveldb_proto entry";
-    // TODO(cjhopman): Decide what to do about un-parseable entries.
   }
+
+  ProtoLevelDBWrapperMetrics::RecordGet(client_id, *success, *found, status);
 }
 
 }  // namespace
@@ -307,7 +323,7 @@
       FROM_HERE,
       base::BindOnce(UpdateEntriesFromTaskRunner<T>, base::Unretained(db_),
                      std::move(entries_to_save), std::move(keys_to_remove),
-                     success),
+                     success, metrics_id_),
       base::BindOnce(RunUpdateCallback<T>, std::move(callback),
                      base::Owned(success)));
 }
@@ -320,12 +336,11 @@
     typename ProtoLevelDBWrapper::UpdateCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   bool* success = new bool(false);
-
   task_runner_->PostTaskAndReply(
       FROM_HERE,
       base::BindOnce(UpdateEntriesWithRemoveFilterFromTaskRunner<T>,
                      base::Unretained(db_), std::move(entries_to_save),
-                     delete_key_filter, success),
+                     delete_key_filter, success, metrics_id_),
       base::BindOnce(RunUpdateCallback<T>, std::move(callback),
                      base::Owned(success)));
 }
@@ -360,7 +375,8 @@
   task_runner_->PostTaskAndReply(
       FROM_HERE,
       base::BindOnce(LoadEntriesFromTaskRunner<T>, base::Unretained(db_),
-                     key_filter, options, target_prefix, entries_ptr, success),
+                     key_filter, options, target_prefix, entries_ptr, success,
+                     metrics_id_),
       base::BindOnce(RunLoadCallback<T>, std::move(callback),
                      base::Owned(success), std::move(entries)));
 }
@@ -399,7 +415,7 @@
       FROM_HERE,
       base::BindOnce(LoadKeysAndEntriesFromTaskRunner<T>, base::Unretained(db_),
                      key_filter, options, target_prefix, keys_entries_ptr,
-                     success),
+                     success, metrics_id_),
       base::BindOnce(RunLoadKeysAndEntriesCallback<T>, std::move(callback),
                      base::Owned(success), std::move(keys_entries)));
 }
@@ -419,7 +435,7 @@
   task_runner_->PostTaskAndReply(
       FROM_HERE,
       base::BindOnce(GetEntryFromTaskRunner<T>, base::Unretained(db_), key,
-                     entry_ptr, found, success),
+                     entry_ptr, found, success, metrics_id_),
       base::BindOnce(RunGetCallback<T>, std::move(callback),
                      base::Owned(success), base::Owned(found),
                      std::move(entry)));
diff --git a/components/leveldb_proto/proto_leveldb_wrapper_metrics.cc b/components/leveldb_proto/proto_leveldb_wrapper_metrics.cc
new file mode 100644
index 0000000..e4b656d
--- /dev/null
+++ b/components/leveldb_proto/proto_leveldb_wrapper_metrics.cc
@@ -0,0 +1,119 @@
+// 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/leveldb_proto/proto_leveldb_wrapper_metrics.h"
+
+#include "base/metrics/histogram.h"
+#include "third_party/leveldatabase/env_chromium.h"
+
+namespace leveldb_proto {
+
+// static
+void ProtoLevelDBWrapperMetrics::RecordInit(const std::string& client,
+                                            const leveldb::Status& status) {
+  base::HistogramBase* init_status_histogram =
+      base::LinearHistogram::FactoryGet(
+          std::string("ProtoDB.InitStatus.") + client, 1,
+          leveldb_env::LEVELDB_STATUS_MAX, leveldb_env::LEVELDB_STATUS_MAX + 1,
+          base::Histogram::kUmaTargetedHistogramFlag);
+
+  if (init_status_histogram)
+    init_status_histogram->Add(leveldb_env::GetLevelDBStatusUMAValue(status));
+}
+
+// static
+void ProtoLevelDBWrapperMetrics::RecordUpdate(const std::string& client,
+                                              bool success,
+                                              const leveldb::Status& status) {
+  base::HistogramBase* update_success_histogram_ =
+      base::BooleanHistogram::FactoryGet(
+          std::string("ProtoDB.UpdateSuccess.") + client,
+          base::Histogram::kUmaTargetedHistogramFlag);
+  base::HistogramBase* update_error_histogram_ =
+      base::LinearHistogram::FactoryGet(
+          std::string("ProtoDB.UpdateErrorStatus.") + client, 1,
+          leveldb_env::LEVELDB_STATUS_MAX, leveldb_env::LEVELDB_STATUS_MAX + 1,
+          base::Histogram::kUmaTargetedHistogramFlag);
+
+  if (update_success_histogram_)
+    update_success_histogram_->Add(success);
+  if (!success && update_error_histogram_)
+    update_error_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(status));
+}
+
+// static
+void ProtoLevelDBWrapperMetrics::RecordGet(const std::string& client,
+                                           bool success,
+                                           bool found,
+                                           const leveldb::Status& status) {
+  base::HistogramBase* get_success_histogram =
+      base::BooleanHistogram::FactoryGet(
+          std::string("ProtoDB.GetSuccess.") + client,
+          base::Histogram::kUmaTargetedHistogramFlag);
+  base::HistogramBase* get_found_histogram = base::BooleanHistogram::FactoryGet(
+      std::string("ProtoDB.GetFound.") + client,
+      base::Histogram::kUmaTargetedHistogramFlag);
+  base::HistogramBase* get_error_histogram = base::LinearHistogram::FactoryGet(
+      std::string("ProtoDB.GetErrorStatus.") + client, 1,
+      leveldb_env::LEVELDB_STATUS_MAX, leveldb_env::LEVELDB_STATUS_MAX + 1,
+      base::Histogram::kUmaTargetedHistogramFlag);
+
+  if (get_success_histogram)
+    get_success_histogram->Add(success);
+  if (get_found_histogram)
+    get_found_histogram->Add(found);
+  if (!success && get_error_histogram)
+    get_error_histogram->Add(leveldb_env::GetLevelDBStatusUMAValue(status));
+}
+
+// static
+void ProtoLevelDBWrapperMetrics::RecordLoadKeys(const std::string& client,
+                                                bool success) {
+  base::HistogramBase* load_keys_success_histogram =
+      base::BooleanHistogram::FactoryGet(
+          std::string("ProtoDB.LoadKeysSuccess.") + client,
+          base::Histogram::kUmaTargetedHistogramFlag);
+
+  if (load_keys_success_histogram)
+    load_keys_success_histogram->Add(success);
+}
+
+// static
+void ProtoLevelDBWrapperMetrics::RecordLoadEntries(const std::string& client,
+                                                   bool success) {
+  base::HistogramBase* load_entries_success_histogram =
+      base::BooleanHistogram::FactoryGet(
+          std::string("ProtoDB.LoadEntriesSuccess.") + client,
+          base::Histogram::kUmaTargetedHistogramFlag);
+
+  if (load_entries_success_histogram)
+    load_entries_success_histogram->Add(success);
+}
+
+// static
+void ProtoLevelDBWrapperMetrics::RecordLoadKeysAndEntries(
+    const std::string& client,
+    bool success) {
+  base::HistogramBase* load_keys_and_entries_success_histogram =
+      base::BooleanHistogram::FactoryGet(
+          std::string("ProtoDB.LoadKeysAndEntriesSuccess.") + client,
+          base::Histogram::kUmaTargetedHistogramFlag);
+
+  if (load_keys_and_entries_success_histogram)
+    load_keys_and_entries_success_histogram->Add(success);
+}
+
+// static
+void ProtoLevelDBWrapperMetrics::RecordDestroy(const std::string& client,
+                                               bool success) {
+  base::HistogramBase* destroy_success_histogram =
+      base::BooleanHistogram::FactoryGet(
+          std::string("ProtoDB.DestroySuccess.") + client,
+          base::Histogram::kUmaTargetedHistogramFlag);
+
+  if (destroy_success_histogram)
+    destroy_success_histogram->Add(success);
+}
+
+}  // namespace leveldb_proto
\ No newline at end of file
diff --git a/components/leveldb_proto/proto_leveldb_wrapper_metrics.h b/components/leveldb_proto/proto_leveldb_wrapper_metrics.h
new file mode 100644
index 0000000..d05bb29
--- /dev/null
+++ b/components/leveldb_proto/proto_leveldb_wrapper_metrics.h
@@ -0,0 +1,40 @@
+// 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_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_METRICS_H_
+#define COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_METRICS_H_
+
+#include <string>
+
+namespace leveldb {
+class Status;
+}  // namespace leveldb
+
+namespace leveldb_proto {
+
+// Static metrics recording helper functions for ProtoLevelDBWrapper.
+//
+// When adding database clients that require UMA metrics recording, ensure that
+// the client name is added as a suffix in histograms.xml for the appropriate
+// ProtoDB.* metrics.
+class ProtoLevelDBWrapperMetrics {
+ public:
+  static void RecordInit(const std::string& client,
+                         const leveldb::Status& status);
+  static void RecordUpdate(const std::string& client,
+                           bool success,
+                           const leveldb::Status& status);
+  static void RecordGet(const std::string& client,
+                        bool success,
+                        bool found,
+                        const leveldb::Status& status);
+  static void RecordLoadKeys(const std::string& client, bool success);
+  static void RecordLoadEntries(const std::string& client, bool success);
+  static void RecordLoadKeysAndEntries(const std::string& client, bool success);
+  static void RecordDestroy(const std::string& client, bool success);
+};
+
+}  // namespace leveldb_proto
+
+#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_METRICS_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/unique_proto_database_unittest.cc b/components/leveldb_proto/unique_proto_database_unittest.cc
index 721a5ec..081b618 100644
--- a/components/leveldb_proto/unique_proto_database_unittest.cc
+++ b/components/leveldb_proto/unique_proto_database_unittest.cc
@@ -48,12 +48,16 @@
 
 class MockDB : public LevelDB {
  public:
-  MOCK_METHOD2(Init,
-               bool(const base::FilePath& database_dir,
-                    const leveldb_env::Options& options));
-  MOCK_METHOD2(Save, bool(const KeyValueVector&, const KeyVector&));
-  MOCK_METHOD2(UpdateWithRemoveFilter,
-               bool(const base::StringPairs&, const KeyFilter&));
+  MOCK_METHOD3(Init,
+               leveldb::Status(const base::FilePath& database_dir,
+                               const leveldb_env::Options& options,
+                               bool));
+  MOCK_METHOD3(Save,
+               bool(const KeyValueVector&, const KeyVector&, leveldb::Status*));
+  MOCK_METHOD3(UpdateWithRemoveFilter,
+               bool(const base::StringPairs&,
+                    const KeyFilter&,
+                    leveldb::Status*));
   MOCK_METHOD1(Load, bool(std::vector<std::string>*));
   MOCK_METHOD2(LoadWithFilter,
                bool(const KeyFilter&, std::vector<std::string>*));
@@ -70,8 +74,9 @@
                     std::map<std::string, std::string>*,
                     const leveldb::ReadOptions&,
                     const std::string&));
-  MOCK_METHOD3(Get, bool(const std::string&, bool*, std::string*));
-  MOCK_METHOD0(Destroy, bool());
+  MOCK_METHOD4(Get,
+               bool(const std::string&, bool*, std::string*, leveldb::Status*));
+  MOCK_METHOD0(Destroy, leveldb::Status());
 
   MockDB() : LevelDB(kTestLevelDBClientName) {}
 };
@@ -200,7 +205,8 @@
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
   auto mock_db = std::make_unique<MockDB>();
-  EXPECT_CALL(*mock_db, Init(path, options_)).WillOnce(Return(true));
+  EXPECT_CALL(*mock_db, Init(path, options_, _))
+      .WillOnce(Return(leveldb::Status()));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(true));
@@ -218,7 +224,9 @@
   auto mock_db = std::make_unique<MockDB>();
   Options options;
   options.create_if_missing = true;
-  EXPECT_CALL(*mock_db, Init(path, OptionsEq(options))).WillOnce(Return(false));
+  EXPECT_CALL(*mock_db, Init(path, OptionsEq(options), _))
+      .WillOnce(
+          Return(leveldb::Status::IOError(leveldb::Slice(), leveldb::Slice())));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(false));
@@ -234,7 +242,8 @@
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
   auto mock_db = std::make_unique<MockDB>();
-  EXPECT_CALL(*mock_db, Init(path, options_)).WillOnce(Return(true));
+  EXPECT_CALL(*mock_db, Init(path, options_, _))
+      .WillOnce(Return(leveldb::Status()));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(true));
@@ -246,7 +255,7 @@
   EXPECT_CALL(caller, DestroyCallback(true));
   db_->Destroy(base::BindOnce(&MockDatabaseCaller::DestroyCallback,
                               base::Unretained(&caller)));
-  EXPECT_CALL(*mock_db, Destroy()).WillOnce(Return(true));
+  EXPECT_CALL(*mock_db, Destroy()).WillOnce(Return(leveldb::Status()));
 
   base::RunLoop().RunUntilIdle();
 }
@@ -255,7 +264,8 @@
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
   auto mock_db = std::make_unique<MockDB>();
-  EXPECT_CALL(*mock_db, Init(path, options_)).WillOnce(Return(true));
+  EXPECT_CALL(*mock_db, Init(path, options_, _))
+      .WillOnce(Return(leveldb::Status()));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(true));
@@ -267,7 +277,9 @@
   EXPECT_CALL(caller, DestroyCallback(false));
   db_->Destroy(base::BindOnce(&MockDatabaseCaller::DestroyCallback,
                               base::Unretained(&caller)));
-  EXPECT_CALL(*mock_db, Destroy()).WillOnce(Return(false));
+  EXPECT_CALL(*mock_db, Destroy())
+      .WillOnce(
+          Return(leveldb::Status::IOError(leveldb::Slice(), leveldb::Slice())));
 
   base::RunLoop().RunUntilIdle();
 }
@@ -312,7 +324,7 @@
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
@@ -335,7 +347,7 @@
   auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
@@ -375,7 +387,7 @@
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
@@ -383,7 +395,7 @@
 
   std::string key("1");
   ASSERT_TRUE(model.count(key));
-  EXPECT_CALL(*mock_db, Get(key, _, _)).WillOnce(SetGetEntry(model));
+  EXPECT_CALL(*mock_db, Get(key, _, _, _)).WillOnce(SetGetEntry(model));
   EXPECT_CALL(caller, GetCallback1(true, _))
       .WillOnce(VerifyGetEntry(model[key]));
   db_->GetEntry(key, base::BindOnce(&MockDatabaseCaller::GetCallback,
@@ -462,7 +474,7 @@
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
@@ -470,7 +482,7 @@
 
   std::string key("does_not_exist");
   ASSERT_FALSE(model.count(key));
-  EXPECT_CALL(*mock_db, Get(key, _, _)).WillOnce(SetGetEntry(model));
+  EXPECT_CALL(*mock_db, Get(key, _, _, _)).WillOnce(SetGetEntry(model));
   EXPECT_CALL(caller, GetCallback1(true, nullptr));
   db_->GetEntry(key, base::BindOnce(&MockDatabaseCaller::GetCallback,
                                     base::Unretained(&caller)));
@@ -485,7 +497,7 @@
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
@@ -493,7 +505,7 @@
 
   std::string key("does_not_exist");
   ASSERT_FALSE(model.count(key));
-  EXPECT_CALL(*mock_db, Get(key, _, _)).WillOnce(Return(false));
+  EXPECT_CALL(*mock_db, Get(key, _, _, _)).WillOnce(Return(false));
   EXPECT_CALL(caller, GetCallback1(false, nullptr));
   db_->GetEntry(key, base::BindOnce(&MockDatabaseCaller::GetCallback,
                                     base::Unretained(&caller)));
@@ -530,7 +542,7 @@
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
@@ -543,7 +555,7 @@
 
   std::unique_ptr<KeyVector> keys_to_remove(new KeyVector());
 
-  EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(VerifyUpdateEntries(model));
+  EXPECT_CALL(*mock_db, Save(_, _, _)).WillOnce(VerifyUpdateEntries(model));
   EXPECT_CALL(caller, SaveCallback(true));
   db_->UpdateEntries(std::move(entries), std::move(keys_to_remove),
                      base::BindOnce(&MockDatabaseCaller::SaveCallback,
@@ -561,13 +573,13 @@
       new ProtoDatabase<TestProto>::KeyEntryVector());
   std::unique_ptr<KeyVector> keys_to_remove(new KeyVector());
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
-  EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(Return(false));
+  EXPECT_CALL(*mock_db, Save(_, _, _)).WillOnce(Return(false));
   EXPECT_CALL(caller, SaveCallback(false));
   db_->UpdateEntries(std::move(entries), std::move(keys_to_remove),
                      base::BindOnce(&MockDatabaseCaller::SaveCallback,
@@ -586,7 +598,7 @@
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
@@ -599,7 +611,7 @@
     keys_to_remove->push_back(pair.second.id());
 
   KeyVector keys_copy(*keys_to_remove.get());
-  EXPECT_CALL(*mock_db, Save(_, keys_copy)).WillOnce(Return(true));
+  EXPECT_CALL(*mock_db, Save(_, keys_copy, _)).WillOnce(Return(true));
   EXPECT_CALL(caller, SaveCallback(true));
   db_->UpdateEntries(std::move(entries), std::move(keys_to_remove),
                      base::BindOnce(&MockDatabaseCaller::SaveCallback,
@@ -617,13 +629,13 @@
       new ProtoDatabase<TestProto>::KeyEntryVector());
   std::unique_ptr<KeyVector> keys_to_remove(new KeyVector());
 
-  EXPECT_CALL(*mock_db, Init(_, options_));
+  EXPECT_CALL(*mock_db, Init(_, options_, _));
   EXPECT_CALL(caller, InitCallback(_));
   db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
-  EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(Return(false));
+  EXPECT_CALL(*mock_db, Save(_, _, _)).WillOnce(Return(false));
   EXPECT_CALL(caller, SaveCallback(false));
   db_->UpdateEntries(std::move(entries), std::move(keys_to_remove),
                      base::BindOnce(&MockDatabaseCaller::SaveCallback,
@@ -715,7 +727,8 @@
 
   std::unique_ptr<LevelDB> db(new LevelDB(kTestLevelDBClientName));
   EXPECT_TRUE(db->Init(temp_dir.GetPath(), CreateSimpleOptions()));
-  EXPECT_TRUE(db->Save(save_entries, remove_keys));
+  leveldb::Status status;
+  EXPECT_TRUE(db->Save(save_entries, remove_keys, &status));
 
   if (close_after_save) {
     db.reset(new LevelDB(kTestLevelDBClientName));
@@ -760,7 +773,8 @@
 
   std::unique_ptr<LevelDB> db(new LevelDB(kTestLevelDBClientName));
   EXPECT_TRUE(db->Init(temp_dir.GetPath(), CreateSimpleOptions()));
-  EXPECT_TRUE(db->Save(save_entries, remove_keys));
+  leveldb::Status status;
+  EXPECT_TRUE(db->Save(save_entries, remove_keys, &status));
 
   EXPECT_TRUE(
       db->LoadWithFilter(base::BindRepeating(&ZeroFilter), &load_entries));
@@ -788,7 +802,8 @@
 
   std::unique_ptr<LevelDB> db(new LevelDB(kTestLevelDBClientName));
   EXPECT_TRUE(db->Init(temp_dir.GetPath(), CreateSimpleOptions()));
-  EXPECT_TRUE(db->Save(save_entries, remove_keys));
+  leveldb::Status status;
+  EXPECT_TRUE(db->Save(save_entries, remove_keys, &status));
 
   EXPECT_TRUE(db->LoadKeysAndEntries(&load_keys_entries));
 
@@ -814,7 +829,8 @@
 
   EXPECT_FALSE(db->Init(temp_dir.GetPath(), options));
   EXPECT_FALSE(db->Load(&load_entries));
-  EXPECT_FALSE(db->Save(save_entries, remove_keys));
+  leveldb::Status status;
+  EXPECT_FALSE(db->Save(save_entries, remove_keys, &status));
 }
 
 TEST_F(UniqueProtoDatabaseLevelDBTest, TestMemoryDatabase) {
@@ -830,7 +846,8 @@
   KeyValueVector save_entries(1, std::make_pair("foo", "bar"));
   KeyVector remove_keys;
 
-  ASSERT_TRUE(db->Save(save_entries, remove_keys));
+  leveldb::Status status;
+  ASSERT_TRUE(db->Save(save_entries, remove_keys, &status));
 
   std::vector<std::string> second_load_entries;
 
@@ -850,7 +867,8 @@
     base::StringPairs pairs_to_save;
     pairs_to_save.push_back(std::make_pair("TheKey", "KeyValue"));
     std::vector<std::string> keys_to_remove;
-    ASSERT_TRUE(db.Save(pairs_to_save, keys_to_remove));
+    leveldb::Status status;
+    ASSERT_TRUE(db.Save(pairs_to_save, keys_to_remove, &status));
   }
 
   EXPECT_TRUE(leveldb_chrome::CorruptClosedDBForTesting(temp_dir.GetPath()));
@@ -863,7 +881,8 @@
   ASSERT_TRUE(db.Init(temp_dir.GetPath(), options));
   bool found = false;
   std::string value;
-  ASSERT_TRUE(db.Get("TheKey", &found, &value));
+  leveldb::Status status;
+  ASSERT_TRUE(db.Get("TheKey", &found, &value, &status));
   ASSERT_EQ("", value);
   ASSERT_FALSE(found);
 }
@@ -885,7 +904,9 @@
 
   std::unique_ptr<LevelDB> db(new LevelDB(kTestLevelDBClientName));
   EXPECT_TRUE(db->Init(temp_dir.GetPath(), CreateSimpleOptions()));
-  EXPECT_TRUE(db->UpdateWithRemoveFilter(save_entries, LevelDB::KeyFilter()));
+  leveldb::Status status;
+  EXPECT_TRUE(
+      db->UpdateWithRemoveFilter(save_entries, LevelDB::KeyFilter(), &status));
 
   // Make sure the "0" entry is in database.
   EXPECT_TRUE(
@@ -894,8 +915,8 @@
 
   // Delete "0" entry.
   save_entries.clear();
-  EXPECT_TRUE(db->UpdateWithRemoveFilter(save_entries,
-                                         base::BindRepeating(&ZeroFilter)));
+  EXPECT_TRUE(db->UpdateWithRemoveFilter(
+      save_entries, base::BindRepeating(&ZeroFilter), &status));
 
   // Make sure the "0" entry is not there.
   load_entries.clear();
diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc
index 0e320f7..e643820 100644
--- a/components/ntp_snippets/content_suggestions_service.cc
+++ b/components/ntp_snippets/content_suggestions_service.cc
@@ -19,7 +19,7 @@
 #include "base/time/default_clock.h"
 #include "base/values.h"
 #include "components/favicon/core/favicon_server_fetcher_params.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/ntp_snippets/content_suggestions_metrics.h"
diff --git a/components/ntp_snippets/contextual/contextual_suggestions_features.cc b/components/ntp_snippets/contextual/contextual_suggestions_features.cc
index 34abbed..56a6b6c 100644
--- a/components/ntp_snippets/contextual/contextual_suggestions_features.cc
+++ b/components/ntp_snippets/contextual/contextual_suggestions_features.cc
@@ -13,6 +13,6 @@
     "ContextualSuggestionsIPHReverseScroll", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kContextualSuggestionsOptOut{
-    "ContextualSuggestionsOptOut", base::FEATURE_ENABLED_BY_DEFAULT};
+    "ContextualSuggestionsOptOut", base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace contextual_suggestions
diff --git a/components/ntp_tiles/icon_cacher_impl.cc b/components/ntp_tiles/icon_cacher_impl.cc
index 47ce15e..987a15c4 100644
--- a/components/ntp_tiles/icon_cacher_impl.cc
+++ b/components/ntp_tiles/icon_cacher_impl.cc
@@ -11,7 +11,7 @@
 #include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon/core/favicon_util.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/favicon_base/favicon_util.h"
diff --git a/components/ntp_tiles/icon_cacher_impl_unittest.cc b/components/ntp_tiles/icon_cacher_impl_unittest.cc
index 4f9a846..7c1cf9f3 100644
--- a/components/ntp_tiles/icon_cacher_impl_unittest.cc
+++ b/components/ntp_tiles/icon_cacher_impl_unittest.cc
@@ -19,7 +19,7 @@
 #include "components/favicon/core/favicon_client.h"
 #include "components/favicon/core/favicon_service_impl.h"
 #include "components/favicon/core/favicon_util.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/history/core/browser/history_database_params.h"
 #include "components/history/core/browser/history_service.h"
diff --git a/components/previews/core/previews_experiments.cc b/components/previews/core/previews_experiments.cc
index cb6a199..b80302d2 100644
--- a/components/previews/core/previews_experiments.cc
+++ b/components/previews/core/previews_experiments.cc
@@ -201,6 +201,11 @@
   return GURL("https://litepages.googlezip.net/");
 }
 
+std::string LitePageRedirectPreviewExperiment() {
+  return GetFieldTrialParamValueByFeature(features::kLitePageServerPreviews,
+                                          "lite_page_preview_experiment");
+}
+
 net::EffectiveConnectionType GetECTThresholdForPreview(
     previews::PreviewsType type) {
   switch (type) {
diff --git a/components/previews/core/previews_experiments.h b/components/previews/core/previews_experiments.h
index 360f92b..9e657e6 100644
--- a/components/previews/core/previews_experiments.h
+++ b/components/previews/core/previews_experiments.h
@@ -115,6 +115,9 @@
 // The maximum number of seconds to loadshed the Previews server for.
 int PreviewServerLoadshedMaxSeconds();
 
+// The experimental config to send to the previews server.
+std::string LitePageRedirectPreviewExperiment();
+
 // The threshold of EffectiveConnectionType above which preview |type| will be
 // triggered.
 net::EffectiveConnectionType GetECTThresholdForPreview(
diff --git a/components/safe_browsing/password_protection/BUILD.gn b/components/safe_browsing/password_protection/BUILD.gn
index 87b9e3f5..c9303ff 100644
--- a/components/safe_browsing/password_protection/BUILD.gn
+++ b/components/safe_browsing/password_protection/BUILD.gn
@@ -33,6 +33,7 @@
       "//components/safe_browsing/db:whitelist_checker_client",
       "//components/safe_browsing/web_ui:web_ui",
       "//components/sessions",
+      "//components/zoom",
       "//content/public/browser:browser",
       "//net:net",
       "//third_party/protobuf:protobuf_lite",
diff --git a/components/safe_browsing/password_protection/DEPS b/components/safe_browsing/password_protection/DEPS
index c305e06..9fb70be 100644
--- a/components/safe_browsing/password_protection/DEPS
+++ b/components/safe_browsing/password_protection/DEPS
@@ -5,6 +5,7 @@
   "+components/password_manager/core/browser/password_reuse_detector.h",
   "+components/sessions",
   "+components/sync_preferences/testing_pref_service_syncable.h",
+  "+components/zoom",
   "+content/public/test",
   "+net",
   "+services/network/public",
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
index e557987..c34b50b6 100644
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/password_protection/password_protection_service.cc
@@ -12,6 +12,7 @@
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -25,10 +26,12 @@
 #include "components/safe_browsing/db/whitelist_checker_client.h"
 #include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
 #include "components/safe_browsing/password_protection/password_protection_request.h"
+#include "components/zoom/zoom_controller.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/page_zoom.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
 #include "net/base/url_util.h"
@@ -378,6 +381,13 @@
   if (!IsSupportedPasswordTypeForPinging(reused_password_type))
     return;
 
+  // Collect metrics about typical page-zoom on login pages.
+  double zoom_level =
+      zoom::ZoomController::GetZoomLevelForWebContents(web_contents);
+  UMA_HISTOGRAM_COUNTS_1000(
+      "PasswordProtection.PageZoomFactor",
+      static_cast<int>(100 * content::ZoomLevelToZoomFactor(zoom_level)));
+
   RequestOutcome reason;
   if (CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                   main_frame_url, reused_password_type, &reason)) {
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index 4d4c7cd..43c5967 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -294,6 +294,7 @@
     "//gpu/command_buffer/client",
     "//gpu/command_buffer/client:gles2_implementation",
     "//gpu/ipc:gl_in_process_context",
+    "//gpu/ipc:gpu_thread_holder",
     "//testing/gmock",
     "//testing/gtest",
     "//ui/gfx",
diff --git a/components/viz/common/DEPS b/components/viz/common/DEPS
index 2c8d528..d1f52eb 100644
--- a/components/viz/common/DEPS
+++ b/components/viz/common/DEPS
@@ -26,11 +26,13 @@
     "+cc/test",
     "+components/viz/test",
     "+gpu/ipc/gl_in_process_context.h",
+    "+gpu/ipc/test_gpu_thread_holder.h",
     "+media/base",
     "+third_party/skia/include/core",
     "+ui/gl",
   ],
   ".*_benchmark\.cc": [
     "+gpu/ipc/gl_in_process_context.h",
+    "+gpu/ipc/test_gpu_thread_holder.h",
   ],
 }
diff --git a/components/viz/common/gl_helper_benchmark.cc b/components/viz/common/gl_helper_benchmark.cc
index ec352db..af88ea0 100644
--- a/components/viz/common/gl_helper_benchmark.cc
+++ b/components/viz/common/gl_helper_benchmark.cc
@@ -8,11 +8,11 @@
 // but cannot really "fail". There is no point in making these tests part
 // of any test automation run.
 
-#include <stddef.h>
-#include <stdio.h>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <GLES2/gl2extchromium.h>
+#include <stddef.h>
+#include <stdio.h>
 
 #include <cmath>
 #include <string>
@@ -32,6 +32,7 @@
 #include "gpu/command_buffer/client/gles2_implementation.h"
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/ipc/gl_in_process_context.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkTypes.h"
@@ -68,15 +69,14 @@
     attributes.gpu_preference = gl::PreferDiscreteGpu;
 
     context_ = std::make_unique<gpu::GLInProcessContext>();
-    auto result =
-        context_->Initialize(nullptr,                 /* service */
-                             nullptr,                 /* surface */
-                             true,                    /* offscreen */
-                             gpu::kNullSurfaceHandle, /* window */
-                             attributes, gpu::SharedMemoryLimits(),
-                             nullptr, /* gpu_memory_buffer_manager */
-                             nullptr, /* image_factory */
-                             base::ThreadTaskRunnerHandle::Get());
+    auto result = context_->Initialize(
+        gpu::GetTestGpuThreadHolder()->GetTaskExecutor(), nullptr, /* surface */
+        true,                    /* offscreen */
+        gpu::kNullSurfaceHandle, /* window */
+        attributes, gpu::SharedMemoryLimits(),
+        nullptr, /* gpu_memory_buffer_manager */
+        nullptr, /* image_factory */
+        base::ThreadTaskRunnerHandle::Get());
     DCHECK_EQ(result, gpu::ContextResult::kSuccess);
     gl_ = context_->GetImplementation();
     gpu::ContextSupport* support = context_->GetImplementation();
diff --git a/components/viz/common/gl_helper_unittest.cc b/components/viz/common/gl_helper_unittest.cc
index 13f80a2..1b671de 100644
--- a/components/viz/common/gl_helper_unittest.cc
+++ b/components/viz/common/gl_helper_unittest.cc
@@ -32,6 +32,7 @@
 #include "gpu/command_buffer/client/gles2_implementation.h"
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/ipc/gl_in_process_context.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkTypes.h"
@@ -64,15 +65,14 @@
     attributes.bind_generates_resource = false;
 
     context_ = std::make_unique<gpu::GLInProcessContext>();
-    auto result =
-        context_->Initialize(nullptr,                 /* service */
-                             nullptr,                 /* surface */
-                             true,                    /* offscreen */
-                             gpu::kNullSurfaceHandle, /* window */
-                             attributes, gpu::SharedMemoryLimits(),
-                             nullptr, /* gpu_memory_buffer_manager */
-                             nullptr, /* image_factory */
-                             base::ThreadTaskRunnerHandle::Get());
+    auto result = context_->Initialize(
+        gpu::GetTestGpuThreadHolder()->GetTaskExecutor(), nullptr, /* surface */
+        true,                    /* offscreen */
+        gpu::kNullSurfaceHandle, /* window */
+        attributes, gpu::SharedMemoryLimits(),
+        nullptr, /* gpu_memory_buffer_manager */
+        nullptr, /* image_factory */
+        base::ThreadTaskRunnerHandle::Get());
     DCHECK_EQ(result, gpu::ContextResult::kSuccess);
     gl_ = context_->GetImplementation();
     gpu::ContextSupport* support = context_->GetImplementation();
diff --git a/components/viz/common/resources/resource_format_utils.cc b/components/viz/common/resources/resource_format_utils.cc
index 57c8212d..1795b86 100644
--- a/components/viz/common/resources/resource_format_utils.cc
+++ b/components/viz/common/resources/resource_format_utils.cc
@@ -35,13 +35,14 @@
       return kRGB_565_SkColorType;
     case LUMINANCE_8:
       return kGray_8_SkColorType;
+    case RGBX_8888:
+      return kRGB_888x_SkColorType;
     case ETC1:
     case RED_8:
     case LUMINANCE_F16:
     case R16_EXT:
     case BGR_565:
     case RG_88:
-    case RGBX_8888:
     case BGRX_8888:
     case RGBX_1010102:
     case BGRX_1010102:
@@ -272,7 +273,7 @@
     case R16_EXT:
       return GL_R16_EXT;
     case RGBX_8888:
-      return GL_RGB;
+      return GL_RGB8_OES;
     case RGBX_1010102:
       return GL_RGB10_A2_EXT;
     case BGR_565:
diff --git a/components/viz/common/yuv_readback_unittest.cc b/components/viz/common/yuv_readback_unittest.cc
index cb392fb..9be7f99 100644
--- a/components/viz/common/yuv_readback_unittest.cc
+++ b/components/viz/common/yuv_readback_unittest.cc
@@ -16,6 +16,7 @@
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "gpu/ipc/gl_in_process_context.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -44,15 +45,14 @@
     attributes.bind_generates_resource = false;
 
     context_ = std::make_unique<gpu::GLInProcessContext>();
-    auto result =
-        context_->Initialize(nullptr,                 /* service */
-                             nullptr,                 /* surface */
-                             true,                    /* offscreen */
-                             gpu::kNullSurfaceHandle, /* window */
-                             attributes, gpu::SharedMemoryLimits(),
-                             nullptr, /* gpu_memory_buffer_manager */
-                             nullptr, /* image_factory */
-                             base::ThreadTaskRunnerHandle::Get());
+    auto result = context_->Initialize(
+        gpu::GetTestGpuThreadHolder()->GetTaskExecutor(), nullptr, /* surface */
+        true,                    /* offscreen */
+        gpu::kNullSurfaceHandle, /* window */
+        attributes, gpu::SharedMemoryLimits(),
+        nullptr, /* gpu_memory_buffer_manager */
+        nullptr, /* image_factory */
+        base::ThreadTaskRunnerHandle::Get());
     DCHECK_EQ(result, gpu::ContextResult::kSuccess);
     gl_ = context_->GetImplementation();
     gpu::ContextSupport* support = context_->GetImplementation();
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index 0ec1578..5d29aff 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -382,7 +382,6 @@
     return;
   }
 
-  bool corruption_detected = false;
   for (const std::pair<int64_t, std::string>& data : user_data) {
     BackgroundSyncRegistrationsProto registrations_proto;
     if (registrations_proto.ParseFromString(data.second)) {
@@ -406,19 +405,9 @@
             base::Time::FromInternalValue(registration_proto.delay_until()));
       }
     }
-
-    if (corruption_detected)
-      break;
-  }
-
-  if (corruption_detected) {
-    LOG(ERROR) << "Corruption detected in background sync backend";
-    DisableAndClearManager(std::move(callback));
-    return;
   }
 
   FireReadyEvents();
-
   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
diff --git a/content/browser/child_process_launcher_helper_android.cc b/content/browser/child_process_launcher_helper_android.cc
index cb665e1a..3d37885 100644
--- a/content/browser/child_process_launcher_helper_android.cc
+++ b/content/browser/child_process_launcher_helper_android.cc
@@ -185,6 +185,7 @@
     jlong termination_info_ptr,
     jint binding_state,
     jboolean killed_by_us,
+    jboolean clean_exit,
     jint remaining_process_with_strong_binding,
     jint remaining_process_with_moderate_binding,
     jint remaining_process_with_waived_binding) {
@@ -193,6 +194,7 @@
   info->binding_state =
       static_cast<base::android::ChildBindingState>(binding_state);
   info->was_killed_intentionally_by_browser = killed_by_us;
+  info->clean_exit = clean_exit;
   info->remaining_process_with_strong_binding =
       remaining_process_with_strong_binding;
   info->remaining_process_with_moderate_binding =
diff --git a/content/browser/devtools/devtools_network_interceptor.cc b/content/browser/devtools/devtools_network_interceptor.cc
index 006c585..9759751 100644
--- a/content/browser/devtools/devtools_network_interceptor.cc
+++ b/content/browser/devtools/devtools_network_interceptor.cc
@@ -44,7 +44,7 @@
 
 DevToolsNetworkInterceptor::Modifications::Modifications(
     scoped_refptr<net::HttpResponseHeaders> response_headers,
-    std::unique_ptr<std::string> response_body)
+    scoped_refptr<base::RefCountedMemory> response_body)
     : response_headers(std::move(response_headers)),
       response_body(std::move(response_body)) {}
 
@@ -65,7 +65,8 @@
 DevToolsNetworkInterceptor::Modifications::Modifications(
     base::Optional<net::Error> error_reason,
     scoped_refptr<net::HttpResponseHeaders> response_headers,
-    std::unique_ptr<std::string> response_body,
+    scoped_refptr<base::RefCountedMemory> response_body,
+    size_t body_offset,
     protocol::Maybe<std::string> modified_url,
     protocol::Maybe<std::string> modified_method,
     protocol::Maybe<std::string> modified_post_data,
@@ -74,6 +75,7 @@
     : error_reason(std::move(error_reason)),
       response_headers(std::move(response_headers)),
       response_body(std::move(response_body)),
+      body_offset(body_offset),
       modified_url(std::move(modified_url)),
       modified_method(std::move(modified_method)),
       modified_post_data(std::move(modified_post_data)),
diff --git a/content/browser/devtools/devtools_network_interceptor.h b/content/browser/devtools/devtools_network_interceptor.h
index c174e950..50f19a1 100644
--- a/content/browser/devtools/devtools_network_interceptor.h
+++ b/content/browser/devtools/devtools_network_interceptor.h
@@ -78,7 +78,7 @@
     explicit Modifications(
         std::unique_ptr<AuthChallengeResponse> auth_challenge_response);
     Modifications(scoped_refptr<net::HttpResponseHeaders> response_headers,
-                  std::unique_ptr<std::string> response_body);
+                  scoped_refptr<base::RefCountedMemory> response_body);
     Modifications(protocol::Maybe<std::string> modified_url,
                   protocol::Maybe<std::string> modified_method,
                   protocol::Maybe<std::string> modified_post_data,
@@ -86,7 +86,8 @@
     Modifications(
         base::Optional<net::Error> error_reason,
         scoped_refptr<net::HttpResponseHeaders> response_headers,
-        std::unique_ptr<std::string> response_body,
+        scoped_refptr<base::RefCountedMemory> response_body,
+        size_t body_offset,
         protocol::Maybe<std::string> modified_url,
         protocol::Maybe<std::string> modified_method,
         protocol::Maybe<std::string> modified_post_data,
@@ -100,7 +101,8 @@
     // If either of the below fields is set, complete the request by
     // responding with the provided headers and body.
     scoped_refptr<net::HttpResponseHeaders> response_headers;
-    std::unique_ptr<std::string> response_body;
+    scoped_refptr<base::RefCountedMemory> response_body;
+    size_t body_offset = 0;
 
     // Optionally modify before sending to network.
     protocol::Maybe<std::string> modified_url;
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.cc b/content/browser/devtools/devtools_url_interceptor_request_job.cc
index ae502a0..3c8a9bba 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.cc
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.cc
@@ -386,7 +386,8 @@
 class DevToolsURLInterceptorRequestJob::MockResponseDetails {
  public:
   MockResponseDetails(scoped_refptr<net::HttpResponseHeaders> response_headers,
-                      std::string response_bytes);
+                      scoped_refptr<base::RefCountedMemory> response_bytes,
+                      size_t response_bytes_offset);
 
   ~MockResponseDetails();
 
@@ -400,17 +401,18 @@
 
  private:
   scoped_refptr<net::HttpResponseHeaders> response_headers_;
-  std::string response_bytes_;
+  scoped_refptr<base::RefCountedMemory> response_bytes_;
   size_t read_offset_;
   base::TimeTicks response_time_;
 };
 
 DevToolsURLInterceptorRequestJob::MockResponseDetails::MockResponseDetails(
     scoped_refptr<net::HttpResponseHeaders> response_headers,
-    std::string response_bytes)
+    scoped_refptr<base::RefCountedMemory> response_bytes,
+    size_t repsonse_bytes_offset)
     : response_headers_(std::move(response_headers)),
       response_bytes_(std::move(response_bytes)),
-      read_offset_(0),
+      read_offset_(repsonse_bytes_offset),
       response_time_(base::TimeTicks::Now()) {
   if (!response_headers) {
     static const char kDummyHeaders[] = "HTTP/1.1 200 OK\0\0";
@@ -424,11 +426,13 @@
 int DevToolsURLInterceptorRequestJob::MockResponseDetails::ReadRawData(
     net::IOBuffer* buf,
     int buf_size) {
-  size_t bytes_available = response_bytes_.size() - read_offset_;
+  if (!response_bytes_)
+    return 0;
+  size_t bytes_available = response_bytes_->size() - read_offset_;
   size_t bytes_to_copy =
       std::min(static_cast<size_t>(buf_size), bytes_available);
   if (bytes_to_copy > 0) {
-    std::memcpy(buf->data(), &response_bytes_.data()[read_offset_],
+    std::memcpy(buf->data(), response_bytes_->front() + read_offset_,
                 bytes_to_copy);
     read_offset_ += bytes_to_copy;
   }
@@ -971,7 +975,8 @@
   raw_headers.append(new_url);
   raw_headers.append(2, '\0');
   mock_response_details_ = std::make_unique<MockResponseDetails>(
-      base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers), "");
+      base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers),
+      nullptr /* response_bytes */, 0 /* response_bytes_offset */);
 
   NotifyHeadersComplete();
 }
@@ -1047,8 +1052,7 @@
   if (modifications->response_headers || modifications->response_body) {
     mock_response_details_ = std::make_unique<MockResponseDetails>(
         std::move(modifications->response_headers),
-        modifications->response_body ? std::move(*modifications->response_body)
-                                     : "");
+        std::move(modifications->response_body), modifications->body_offset);
 
     // Set cookies in the network stack.
     net::CookieOptions options;
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index ff97ede..1d0f7d65 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -68,7 +68,8 @@
 class BodyReader : public mojo::DataPipeDrainer::Client {
  public:
   explicit BodyReader(base::OnceClosure download_complete_callback)
-      : download_complete_callback_(std::move(download_complete_callback)) {}
+      : download_complete_callback_(std::move(download_complete_callback)),
+        body_(base::MakeRefCounted<base::RefCountedString>()) {}
 
   void StartReading(mojo::ScopedDataPipeConsumerHandle body);
 
@@ -85,7 +86,11 @@
   }
 
   bool data_complete() const { return data_complete_; }
-  const std::string& body() const { return body_; }
+
+  scoped_refptr<base::RefCountedMemory> body() const {
+    DCHECK(data_complete_);
+    return body_;
+  }
 
   void CancelWithError(std::string error) {
     base::PostTaskWithTraits(
@@ -104,7 +109,8 @@
 
   void OnDataAvailable(const void* data, size_t num_bytes) override {
     DCHECK(!data_complete_);
-    body_.append(std::string(static_cast<const char*>(data), num_bytes));
+    body_->data().append(
+        std::string(static_cast<const char*>(data), num_bytes));
   }
 
   void OnDataComplete() override;
@@ -112,7 +118,7 @@
   std::unique_ptr<mojo::DataPipeDrainer> body_pipe_drainer_;
   CallbackVector callbacks_;
   base::OnceClosure download_complete_callback_;
-  std::string body_;
+  scoped_refptr<base::RefCountedString> body_;
   std::string encoded_body_;
   bool data_complete_ = false;
 };
@@ -130,7 +136,7 @@
   data_complete_ = true;
   body_pipe_drainer_.reset();
   // TODO(caseq): only encode if necessary.
-  base::Base64Encode(body_, &encoded_body_);
+  base::Base64Encode(body_->data(), &encoded_body_);
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(&BodyReader::DispatchBodyOnUI, std::move(callbacks_),
@@ -218,11 +224,12 @@
           auth_challenge_response);
   Response ProcessResponseOverride(
       scoped_refptr<net::HttpResponseHeaders> headers,
-      std::unique_ptr<std::string> body);
+      scoped_refptr<base::RefCountedMemory> body,
+      size_t response_body_offset);
   void ProcessRedirectByClient(const GURL& redirect_url);
   void ProcessSetCookies(const net::HttpResponseHeaders& response_headers,
                          base::OnceClosure callback);
-  void SendResponse(const base::StringPiece& body);
+  void SendResponse(scoped_refptr<base::RefCountedMemory> body, size_t offset);
   void ApplyModificationsToRequest(
       std::unique_ptr<Modifications> modifications);
 
@@ -831,7 +838,8 @@
 
   if (modifications->response_headers || modifications->response_body)
     return ProcessResponseOverride(std::move(modifications->response_headers),
-                                   std::move(modifications->response_body));
+                                   std::move(modifications->response_body),
+                                   modifications->body_offset);
 
   if (state_ == State::kFollowRedirect) {
     if (modifications->modified_url.isJust()) {
@@ -866,7 +874,7 @@
 
   if (body_reader_) {
     if (body_reader_->data_complete())
-      SendResponse(body_reader_->body());
+      SendResponse(body_reader_->body(), 0);
 
     // There are read callbacks pending, so let the reader do its job and come
     // back when it's done.
@@ -943,12 +951,12 @@
 
 Response InterceptionJob::ProcessResponseOverride(
     scoped_refptr<net::HttpResponseHeaders> headers,
-    std::unique_ptr<std::string> maybe_body) {
+    scoped_refptr<base::RefCountedMemory> body,
+    size_t response_body_offset) {
   CancelRequest();
 
-  std::string body = maybe_body ? std::move(*maybe_body) : "";
-  size_t body_size = body.size();
-
+  DCHECK_LE(response_body_offset, body ? body->size() : 0);
+  size_t body_size = body ? body->size() - response_body_offset : 0;
   response_metadata_ = std::make_unique<ResponseMetadata>();
   network::ResourceResponseHead* head = &response_metadata_->head;
 
@@ -969,12 +977,13 @@
         base::MakeRefCounted<net::HttpResponseHeaders>(kDummyHeaders);
   }
   head->headers->GetMimeTypeAndCharset(&head->mime_type, &head->charset);
-  if (head->mime_type.empty()) {
+  if (head->mime_type.empty() && body_size) {
     size_t bytes_to_sniff =
         std::min(body_size, static_cast<size_t>(net::kMaxBytesToSniff));
-    net::SniffMimeType(
-        body.data(), bytes_to_sniff, create_loader_params_->request.url, "",
-        net::ForceSniffFileUrlsForHtml::kDisabled, &head->mime_type);
+    net::SniffMimeType(body->front_as<const char>() + response_body_offset,
+                       bytes_to_sniff, create_loader_params_->request.url, "",
+                       net::ForceSniffFileUrlsForHtml::kDisabled,
+                       &head->mime_type);
     head->did_mime_sniff = true;
   }
   // TODO(caseq): we're cheating here a bit, raw_headers() have \0's
@@ -1007,7 +1016,7 @@
   if (!continue_after_cookies_set) {
     continue_after_cookies_set =
         base::BindOnce(&InterceptionJob::SendResponse, base::Unretained(this),
-                       std::move(body));
+                       std::move(body), response_body_offset);
   }
   ProcessSetCookies(*head->headers, std::move(continue_after_cookies_set));
 
@@ -1070,26 +1079,29 @@
                              response_metadata_->head);
 }
 
-void InterceptionJob::SendResponse(const base::StringPiece& body) {
+void InterceptionJob::SendResponse(scoped_refptr<base::RefCountedMemory> body,
+                                   size_t offset) {
   client_->OnReceiveResponse(response_metadata_->head);
-
-  // We shouldn't be able to transfer a string that big over the protocol,
-  // but just in case...
-  DCHECK_LE(body.size(), UINT32_MAX)
-      << "Response bodies larger than " << UINT32_MAX << " are not supported";
-  mojo::DataPipe pipe(body.size());
-  uint32_t num_bytes = body.size();
-  MojoResult res = pipe.producer_handle->WriteData(body.data(), &num_bytes,
-                                                   MOJO_WRITE_DATA_FLAG_NONE);
-  DCHECK_EQ(0u, res);
-  DCHECK_EQ(num_bytes, body.size());
-
   if (!response_metadata_->cached_metadata.empty())
     client_->OnReceiveCachedMetadata(response_metadata_->cached_metadata);
-  client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
+
+  if (body) {
+    DCHECK_LE(offset, body->size());
+    size_t body_size = body->size() - offset;
+    // We shouldn't be able to transfer a string that big over the protocol,
+    // but just in case...
+    DCHECK_LE(body_size, UINT32_MAX)
+        << "Response bodies larger than " << UINT32_MAX << " are not supported";
+    mojo::DataPipe pipe(body_size);
+    uint32_t num_bytes = body_size;
+    MojoResult res = pipe.producer_handle->WriteData(
+        body->front() + offset, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+    DCHECK_EQ(0u, res);
+    DCHECK_EQ(num_bytes, body_size);
+    client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
+  }
   if (response_metadata_->transfer_size)
     client_->OnTransferSizeUpdated(response_metadata_->transfer_size);
-
   client_->OnComplete(response_metadata_->status);
   Shutdown();
 }
@@ -1099,7 +1111,7 @@
     return;
   // We're here only if client has already told us to proceed with unmodified
   // response.
-  SendResponse(body_reader_->body());
+  SendResponse(body_reader_->body(), 0);
 }
 
 void InterceptionJob::StartRequest() {
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 57a7497..b797183 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -243,7 +243,8 @@
   EXPECT_EQ("\"Escape\"", key);
 }
 
-IN_PROC_BROWSER_TEST_F(SyntheticKeyEventTest, KeyboardEventAck) {
+// Flaky: https://crbug.com/889878
+IN_PROC_BROWSER_TEST_F(SyntheticKeyEventTest, DISABLED_KeyboardEventAck) {
   NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1);
   Attach();
   ASSERT_TRUE(content::ExecuteScript(
diff --git a/content/browser/devtools/protocol/fetch_handler.cc b/content/browser/devtools/protocol/fetch_handler.cc
index 15174bb..eaaf60e 100644
--- a/content/browser/devtools/protocol/fetch_handler.cc
+++ b/content/browser/devtools/protocol/fetch_handler.cc
@@ -216,15 +216,10 @@
     }
   }
   headers.append(1, '\0');
-  std::unique_ptr<std::string> body_ptr =
-      body.isJust() ? std::make_unique<std::string>(
-                          reinterpret_cast<const char*>(body.fromJust().data()),
-                          body.fromJust().size())
-                    : nullptr;
   auto modifications =
       std::make_unique<DevToolsNetworkInterceptor::Modifications>(
           base::MakeRefCounted<net::HttpResponseHeaders>(headers),
-          std::move(body_ptr));
+          body.isJust() ? body.fromJust().bytes() : nullptr);
   interceptor_->ContinueInterceptedRequest(requestId, std::move(modifications),
                                            WrapCallback(std::move(callback)));
 }
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 38ea6fe..492f013e 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1909,7 +1909,8 @@
     Maybe<protocol::Network::AuthChallengeResponse> auth_challenge_response,
     std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
   scoped_refptr<net::HttpResponseHeaders> response_headers;
-  std::unique_ptr<std::string> response_body;
+  scoped_refptr<base::RefCountedMemory> response_body;
+  size_t body_offset = 0;
 
   if (raw_response.isJust()) {
     const protocol::Binary& raw = raw_response.fromJust();
@@ -1927,8 +1928,8 @@
     CHECK_LE(static_cast<size_t>(header_size), raw.size());
     response_headers =
         base::MakeRefCounted<net::HttpResponseHeaders>(std::move(raw_headers));
-    response_body = std::make_unique<std::string>(raw.data() + header_size,
-                                                  raw.data() + raw.size());
+    response_body = raw.bytes();
+    body_offset = header_size;
   }
 
   base::Optional<net::Error> error;
@@ -1987,8 +1988,8 @@
   auto modifications =
       std::make_unique<DevToolsNetworkInterceptor::Modifications>(
           std::move(error), std::move(response_headers),
-          std::move(response_body), std::move(url), std::move(method),
-          std::move(post_data), std::move(override_headers),
+          std::move(response_body), body_offset, std::move(url),
+          std::move(method), std::move(post_data), std::move(override_headers),
           std::move(override_auth));
 
   if (url_loader_interceptor_) {
diff --git a/content/browser/devtools/protocol_string.h b/content/browser/devtools/protocol_string.h
index 90cc73dd..23144ca 100644
--- a/content/browser/devtools/protocol_string.h
+++ b/content/browser/devtools/protocol_string.h
@@ -96,6 +96,7 @@
 
   const uint8_t* data() const { return bytes_->front(); }
   size_t size() const { return bytes_->size(); }
+  scoped_refptr<base::RefCountedMemory> bytes() const { return bytes_; }
 
   String toBase64() const;
 
diff --git a/content/browser/media/session/media_session_android.cc b/content/browser/media/session/media_session_android.cc
index d8505bec..90458b4 100644
--- a/content/browser/media/session/media_session_android.cc
+++ b/content/browser/media/session/media_session_android.cc
@@ -101,7 +101,7 @@
 }
 
 void MediaSessionAndroid::MediaSessionActionsChanged(
-    const std::set<blink::mojom::MediaSessionAction>& actions) {
+    const std::set<media_session::mojom::MediaSessionAction>& actions) {
   ScopedJavaLocalRef<jobject> j_local_session = GetJavaObject();
   if (j_local_session.is_null())
     return;
@@ -160,7 +160,7 @@
                                            const JavaParamRef<jobject>& obj,
                                            int action) {
   media_session()->DidReceiveAction(
-      static_cast<blink::mojom::MediaSessionAction>(action));
+      static_cast<media_session::mojom::MediaSessionAction>(action));
 }
 
 void MediaSessionAndroid::RequestSystemAudioFocus(
diff --git a/content/browser/media/session/media_session_android.h b/content/browser/media/session/media_session_android.h
index 3a95d72..0e111a6 100644
--- a/content/browser/media/session/media_session_android.h
+++ b/content/browser/media/session/media_session_android.h
@@ -38,7 +38,8 @@
   void MediaSessionMetadataChanged(
       const base::Optional<MediaMetadata>& metadata) override;
   void MediaSessionActionsChanged(
-      const std::set<blink::mojom::MediaSessionAction>& actions) override;
+      const std::set<media_session::mojom::MediaSessionAction>& actions)
+      override;
 
   // MediaSession method wrappers.
   void Resume(JNIEnv* env, const base::android::JavaParamRef<jobject>& j_obj);
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index 750494fc..45d59771 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -64,19 +64,19 @@
 }
 
 MediaSessionUserAction MediaSessionActionToUserAction(
-    blink::mojom::MediaSessionAction action) {
+    media_session::mojom::MediaSessionAction action) {
   switch (action) {
-    case blink::mojom::MediaSessionAction::PLAY:
+    case media_session::mojom::MediaSessionAction::kPlay:
       return MediaSessionUserAction::Play;
-    case blink::mojom::MediaSessionAction::PAUSE:
+    case media_session::mojom::MediaSessionAction::kPause:
       return MediaSessionUserAction::Pause;
-    case blink::mojom::MediaSessionAction::PREVIOUS_TRACK:
+    case media_session::mojom::MediaSessionAction::kPreviousTrack:
       return MediaSessionUserAction::PreviousTrack;
-    case blink::mojom::MediaSessionAction::NEXT_TRACK:
+    case media_session::mojom::MediaSessionAction::kNextTrack:
       return MediaSessionUserAction::NextTrack;
-    case blink::mojom::MediaSessionAction::SEEK_BACKWARD:
+    case media_session::mojom::MediaSessionAction::kSeekBackward:
       return MediaSessionUserAction::SeekBackward;
-    case blink::mojom::MediaSessionAction::SEEK_FORWARD:
+    case media_session::mojom::MediaSessionAction::kSeekForward:
       return MediaSessionUserAction::SeekForward;
   }
   NOTREACHED();
@@ -189,7 +189,7 @@
       routed_service_ ? routed_service_->metadata() : base::nullopt);
   observer->MediaSessionActionsChanged(
       routed_service_ ? routed_service_->actions()
-                      : std::set<blink::mojom::MediaSessionAction>());
+                      : std::set<media_session::mojom::MediaSessionAction>());
   observer->MediaSessionStateChanged(IsControllable(), IsActuallyPaused());
 }
 
@@ -200,7 +200,7 @@
 }
 
 void MediaSessionImpl::NotifyMediaSessionActionsChange(
-    const std::set<blink::mojom::MediaSessionAction>& actions) {
+    const std::set<media_session::mojom::MediaSessionAction>& actions) {
   for (auto& observer : observers_)
     observer.MediaSessionActionsChanged(actions);
 }
@@ -740,11 +740,11 @@
 }
 
 void MediaSessionImpl::PreviousTrack() {
-  DidReceiveAction(blink::mojom::MediaSessionAction::PREVIOUS_TRACK);
+  DidReceiveAction(media_session::mojom::MediaSessionAction::kPreviousTrack);
 }
 
 void MediaSessionImpl::NextTrack() {
-  DidReceiveAction(blink::mojom::MediaSessionAction::NEXT_TRACK);
+  DidReceiveAction(media_session::mojom::MediaSessionAction::kNextTrack);
 }
 
 void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() {
@@ -874,7 +874,7 @@
 }
 
 void MediaSessionImpl::DidReceiveAction(
-    blink::mojom::MediaSessionAction action) {
+    media_session::mojom::MediaSessionAction action) {
   MediaSessionUmaHelper::RecordMediaSessionUserAction(
       MediaSessionActionToUserAction(action));
 
@@ -891,7 +891,7 @@
   // OneShot players are not really suspended, so that the session is still
   // active after this. See https://crbug.com/619084 and
   // https://crbug.com/596516.
-  if (blink::mojom::MediaSessionAction::PAUSE == action) {
+  if (media_session::mojom::MediaSessionAction::kPause == action) {
     RenderFrameHost* rfh_of_routed_service =
         routed_service_ ? routed_service_->GetRenderFrameHost() : nullptr;
     for (const auto& player : normal_players_) {
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index 5df1caa7..925e5fc 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -91,7 +91,7 @@
   void NotifyMediaSessionMetadataChange(
       const base::Optional<MediaMetadata>& metadata);
   void NotifyMediaSessionActionsChange(
-      const std::set<blink::mojom::MediaSessionAction>& actions);
+      const std::set<media_session::mojom::MediaSessionAction>& actions);
 
   // Adds the given player to the current media session. Returns whether the
   // player was successfully added. If it returns false, AddPlayer() should be
@@ -193,7 +193,8 @@
 
   // Called when a MediaSessionAction is received. The action will be forwarded
   // to blink::MediaSession corresponding to the current routed service.
-  void DidReceiveAction(blink::mojom::MediaSessionAction action) override;
+  void DidReceiveAction(
+      media_session::mojom::MediaSessionAction action) override;
 
   // Set the volume multiplier applied during ducking.
   CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override;
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc
index 7e686667..bb6d10f 100644
--- a/content/browser/media/session/media_session_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -1705,7 +1705,7 @@
               MediaSessionMetadataChanged(Eq(base::nullopt)));
   EXPECT_CALL(*mock_media_session_observer(),
               MediaSessionActionsChanged(
-                  Eq(std::set<blink::mojom::MediaSessionAction>())));
+                  Eq(std::set<media_session::mojom::MediaSessionAction>())));
   media_session_->AddObserver(mock_media_session_observer());
 }
 
@@ -1721,8 +1721,8 @@
   mock_media_session_service_->SetMetadata(metadata);
 
   mock_media_session_service_->EnableAction(
-      blink::mojom::MediaSessionAction::PLAY);
-  std::set<blink::mojom::MediaSessionAction> expectedActions =
+      media_session::mojom::MediaSessionAction::kPlay);
+  std::set<media_session::mojom::MediaSessionAction> expectedActions =
       mock_media_session_service_->actions();
 
   // Make sure the service is routed,
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
index 3cb6d8c..30e4d6c 100644
--- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc
+++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -46,7 +46,8 @@
     return client;
   }
 
-  MOCK_METHOD1(DidReceiveAction, void(blink::mojom::MediaSessionAction action));
+  MOCK_METHOD1(DidReceiveAction,
+               void(media_session::mojom::MediaSessionAction action));
 
  private:
   mojo::Binding<blink::mojom::MediaSessionClient> binding_;
@@ -266,7 +267,8 @@
   CreateServiceForFrame(main_frame_);
 
   services_[main_frame_]->SetMetadata(MediaMetadata());
-  services_[main_frame_]->EnableAction(blink::mojom::MediaSessionAction::PLAY);
+  services_[main_frame_]->EnableAction(
+      media_session::mojom::MediaSessionAction::kPlay);
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -276,9 +278,9 @@
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
 
-  std::set<blink::mojom::MediaSessionAction> empty_actions;
-  std::set<blink::mojom::MediaSessionAction> expected_actions;
-  expected_actions.insert(blink::mojom::MediaSessionAction::PLAY);
+  std::set<media_session::mojom::MediaSessionAction> empty_actions;
+  std::set<media_session::mojom::MediaSessionAction> expected_actions;
+  expected_actions.insert(media_session::mojom::MediaSessionAction::kPlay);
 
   EXPECT_CALL(*mock_media_session_observer(),
               MediaSessionMetadataChanged(Eq(base::nullopt)))
@@ -298,7 +300,8 @@
   StartPlayerForFrame(main_frame_);
 
   services_[main_frame_]->SetMetadata(expected_metadata);
-  services_[main_frame_]->EnableAction(blink::mojom::MediaSessionAction::PLAY);
+  services_[main_frame_]->EnableAction(
+      media_session::mojom::MediaSessionAction::kPlay);
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -308,8 +311,8 @@
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
 
-  std::set<blink::mojom::MediaSessionAction> expected_actions;
-  expected_actions.insert(blink::mojom::MediaSessionAction::PLAY);
+  std::set<media_session::mojom::MediaSessionAction> expected_actions;
+  expected_actions.insert(media_session::mojom::MediaSessionAction::kPlay);
 
   EXPECT_CALL(*mock_media_session_observer(),
               MediaSessionMetadataChanged(Eq(expected_metadata)))
@@ -321,7 +324,8 @@
   CreateServiceForFrame(main_frame_);
 
   services_[main_frame_]->SetMetadata(expected_metadata);
-  services_[main_frame_]->EnableAction(blink::mojom::MediaSessionAction::PLAY);
+  services_[main_frame_]->EnableAction(
+      media_session::mojom::MediaSessionAction::kPlay);
 
   StartPlayerForFrame(main_frame_);
 }
@@ -333,9 +337,9 @@
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
 
-  std::set<blink::mojom::MediaSessionAction> empty_actions;
-  std::set<blink::mojom::MediaSessionAction> expected_actions;
-  expected_actions.insert(blink::mojom::MediaSessionAction::PLAY);
+  std::set<media_session::mojom::MediaSessionAction> empty_actions;
+  std::set<media_session::mojom::MediaSessionAction> expected_actions;
+  expected_actions.insert(media_session::mojom::MediaSessionAction::kPlay);
 
   EXPECT_CALL(*mock_media_session_observer(), MediaSessionMetadataChanged(_))
       .Times(AnyNumber());
@@ -351,7 +355,8 @@
   CreateServiceForFrame(main_frame_);
 
   services_[main_frame_]->SetMetadata(expected_metadata);
-  services_[main_frame_]->EnableAction(blink::mojom::MediaSessionAction::PLAY);
+  services_[main_frame_]->EnableAction(
+      media_session::mojom::MediaSessionAction::kPlay);
 
   StartPlayerForFrame(main_frame_);
   ClearPlayersForFrame(main_frame_);
@@ -367,14 +372,16 @@
   CreateServiceForFrame(main_frame_);
 
   EXPECT_CALL(*GetPlayerForFrame(sub_frame_), OnSuspend(_));
-  EXPECT_CALL(*GetClientForFrame(main_frame_),
-              DidReceiveAction(blink::mojom::MediaSessionAction::PAUSE))
+  EXPECT_CALL(
+      *GetClientForFrame(main_frame_),
+      DidReceiveAction(media_session::mojom::MediaSessionAction::kPause))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
-  services_[main_frame_]->EnableAction(blink::mojom::MediaSessionAction::PAUSE);
+  services_[main_frame_]->EnableAction(
+      media_session::mojom::MediaSessionAction::kPause);
 
   MediaSessionImpl::Get(contents())
-      ->DidReceiveAction(blink::mojom::MediaSessionAction::PAUSE);
+      ->DidReceiveAction(media_session::mojom::MediaSessionAction::kPause);
 
   run_loop.Run();
 }
@@ -389,14 +396,16 @@
   CreateServiceForFrame(sub_frame_);
 
   EXPECT_CALL(*GetPlayerForFrame(main_frame_), OnSuspend(_));
-  EXPECT_CALL(*GetClientForFrame(sub_frame_),
-              DidReceiveAction(blink::mojom::MediaSessionAction::PAUSE))
+  EXPECT_CALL(
+      *GetClientForFrame(sub_frame_),
+      DidReceiveAction(media_session::mojom::MediaSessionAction::kPause))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
-  services_[sub_frame_]->EnableAction(blink::mojom::MediaSessionAction::PAUSE);
+  services_[sub_frame_]->EnableAction(
+      media_session::mojom::MediaSessionAction::kPause);
 
   MediaSessionImpl::Get(contents())
-      ->DidReceiveAction(blink::mojom::MediaSessionAction::PAUSE);
+      ->DidReceiveAction(media_session::mojom::MediaSessionAction::kPause);
 
   run_loop.Run();
 }
@@ -410,7 +419,7 @@
 
   // This should not crash.
   MediaSessionImpl::Get(contents())
-      ->DidReceiveAction(blink::mojom::MediaSessionAction::PAUSE);
+      ->DidReceiveAction(media_session::mojom::MediaSessionAction::kPause);
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -422,13 +431,13 @@
 
   CreateServiceForFrame(main_frame_);
 
-  EXPECT_CALL(
-      *GetClientForFrame(main_frame_),
-      DidReceiveAction(blink::mojom::MediaSessionAction::PREVIOUS_TRACK))
+  EXPECT_CALL(*GetClientForFrame(main_frame_),
+              DidReceiveAction(
+                  media_session::mojom::MediaSessionAction::kPreviousTrack))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
   services_[main_frame_]->EnableAction(
-      blink::mojom::MediaSessionAction::PREVIOUS_TRACK);
+      media_session::mojom::MediaSessionAction::kPreviousTrack);
 
   MediaSessionImpl::Get(contents())->PreviousTrack();
   run_loop.Run();
@@ -443,12 +452,13 @@
 
   CreateServiceForFrame(main_frame_);
 
-  EXPECT_CALL(*GetClientForFrame(main_frame_),
-              DidReceiveAction(blink::mojom::MediaSessionAction::NEXT_TRACK))
+  EXPECT_CALL(
+      *GetClientForFrame(main_frame_),
+      DidReceiveAction(media_session::mojom::MediaSessionAction::kNextTrack))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
   services_[main_frame_]->EnableAction(
-      blink::mojom::MediaSessionAction::NEXT_TRACK);
+      media_session::mojom::MediaSessionAction::kNextTrack);
 
   MediaSessionImpl::Get(contents())->NextTrack();
   run_loop.Run();
diff --git a/content/browser/media/session/media_session_impl_uma_unittest.cc b/content/browser/media/session/media_session_impl_uma_unittest.cc
index df1e97b2..83dc5677 100644
--- a/content/browser/media/session/media_session_impl_uma_unittest.cc
+++ b/content/browser/media/session/media_session_impl_uma_unittest.cc
@@ -13,13 +13,13 @@
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_web_contents.h"
 #include "media/base/media_content_type.h"
-#include "third_party/blink/public/platform/modules/mediasession/media_session.mojom.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
 
 namespace content {
 
 using MediaSessionUserAction = MediaSessionUmaHelper::MediaSessionUserAction;
 using SuspendType = MediaSession::SuspendType;
-using MediaSessionAction = blink::mojom::MediaSessionAction;
+using MediaSessionAction = media_session::mojom::MediaSessionAction;
 
 namespace {
 
@@ -48,17 +48,17 @@
 };
 
 struct ActionMappingEntry {
-  blink::mojom::MediaSessionAction action;
+  media_session::mojom::MediaSessionAction action;
   MediaSessionUserAction user_action;
 };
 
 ActionMappingEntry kActionMappings[] = {
-    {MediaSessionAction::PLAY, MediaSessionUserAction::Play},
-    {MediaSessionAction::PAUSE, MediaSessionUserAction::Pause},
-    {MediaSessionAction::PREVIOUS_TRACK, MediaSessionUserAction::PreviousTrack},
-    {MediaSessionAction::NEXT_TRACK, MediaSessionUserAction::NextTrack},
-    {MediaSessionAction::SEEK_BACKWARD, MediaSessionUserAction::SeekBackward},
-    {MediaSessionAction::SEEK_FORWARD, MediaSessionUserAction::SeekForward},
+    {MediaSessionAction::kPlay, MediaSessionUserAction::Play},
+    {MediaSessionAction::kPause, MediaSessionUserAction::Pause},
+    {MediaSessionAction::kPreviousTrack, MediaSessionUserAction::PreviousTrack},
+    {MediaSessionAction::kNextTrack, MediaSessionUserAction::NextTrack},
+    {MediaSessionAction::kSeekBackward, MediaSessionUserAction::SeekBackward},
+    {MediaSessionAction::kSeekForward, MediaSessionUserAction::SeekForward},
 };
 
 }  // anonymous namespace
diff --git a/content/browser/media/session/media_session_service_impl.cc b/content/browser/media/session/media_session_service_impl.cc
index 524399df..5f14c5e 100644
--- a/content/browser/media/session/media_session_service_impl.cc
+++ b/content/browser/media/session/media_session_service_impl.cc
@@ -85,7 +85,7 @@
 }
 
 void MediaSessionServiceImpl::EnableAction(
-    blink::mojom::MediaSessionAction action) {
+    media_session::mojom::MediaSessionAction action) {
   actions_.insert(action);
   MediaSessionImpl* session = GetMediaSession();
   if (session)
@@ -93,7 +93,7 @@
 }
 
 void MediaSessionServiceImpl::DisableAction(
-    blink::mojom::MediaSessionAction action) {
+    media_session::mojom::MediaSessionAction action) {
   actions_.erase(action);
   MediaSessionImpl* session = GetMediaSession();
   if (session)
diff --git a/content/browser/media/session/media_session_service_impl.h b/content/browser/media/session/media_session_service_impl.h
index 3614d5e..f04c441 100644
--- a/content/browser/media/session/media_session_service_impl.h
+++ b/content/browser/media/session/media_session_service_impl.h
@@ -32,7 +32,7 @@
     return playback_state_;
   }
   const base::Optional<MediaMetadata>& metadata() const { return metadata_; }
-  const std::set<blink::mojom::MediaSessionAction>& actions() const {
+  const std::set<media_session::mojom::MediaSessionAction>& actions() const {
     return actions_;
   }
 
@@ -44,8 +44,8 @@
   void SetPlaybackState(blink::mojom::MediaSessionPlaybackState state) override;
   void SetMetadata(const base::Optional<MediaMetadata>& metadata) override;
 
-  void EnableAction(blink::mojom::MediaSessionAction action) override;
-  void DisableAction(blink::mojom::MediaSessionAction action) override;
+  void EnableAction(media_session::mojom::MediaSessionAction action) override;
+  void DisableAction(media_session::mojom::MediaSessionAction action) override;
 
  protected:
   explicit MediaSessionServiceImpl(RenderFrameHost* render_frame_host);
@@ -66,7 +66,7 @@
   blink::mojom::MediaSessionClientPtr client_;
   blink::mojom::MediaSessionPlaybackState playback_state_;
   base::Optional<MediaMetadata> metadata_;
-  std::set<blink::mojom::MediaSessionAction> actions_;
+  std::set<media_session::mojom::MediaSessionAction> actions_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaSessionServiceImpl);
 };
diff --git a/content/browser/media/session/media_session_service_impl_browsertest.cc b/content/browser/media/session/media_session_service_impl_browsertest.cc
index 00717a3..4909703 100644
--- a/content/browser/media/session/media_session_service_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_service_impl_browsertest.cc
@@ -29,7 +29,8 @@
         closure_on_actions_change_(closure_on_actions_change) {}
 
   void MediaSessionActionsChanged(
-      const std::set<blink::mojom::MediaSessionAction>& actions) override {
+      const std::set<media_session::mojom::MediaSessionAction>& actions)
+      override {
     // The actions might be empty when the service becomes routed for the first
     // time.
     if (actions.size() == 1)
diff --git a/content/browser/media/session/media_session_uma_helper.h b/content/browser/media/session/media_session_uma_helper.h
index 58d909fc..6b882eaf 100644
--- a/content/browser/media/session/media_session_uma_helper.h
+++ b/content/browser/media/session/media_session_uma_helper.h
@@ -29,8 +29,8 @@
     kMaxValue = SystemTransientDuck,
   };
 
-  // Extended enum to blink::mojom::MediaSessionAction, distinguishing default
-  // action handling.
+  // Extended enum to media_session::mojom::MediaSessionAction, distinguishing
+  // default action handling.
   enum class MediaSessionUserAction {
     Play = 0,
     PlayDefault = 1,
diff --git a/content/browser/media/session/mock_media_session_observer.h b/content/browser/media/session/mock_media_session_observer.h
index 069fdc9..2c818f4 100644
--- a/content/browser/media/session/mock_media_session_observer.h
+++ b/content/browser/media/session/mock_media_session_observer.h
@@ -20,8 +20,9 @@
                void(bool is_controllable, bool is_suspended));
   MOCK_METHOD1(MediaSessionMetadataChanged,
                void(const base::Optional<MediaMetadata>& metadata));
-  MOCK_METHOD1(MediaSessionActionsChanged,
-               void(const std::set<blink::mojom::MediaSessionAction>& action));
+  MOCK_METHOD1(
+      MediaSessionActionsChanged,
+      void(const std::set<media_session::mojom::MediaSessionAction>& action));
 };
 }
 
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 6861f1a..a9c5a2a 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -488,7 +488,7 @@
     return;
   }
 
-  context_core_->storage()->FindRegistrationForScope(
+  FindReadyRegistrationForScope(
       net::SimplifyUrlForRequest(scope),
       base::BindOnce(&ServiceWorkerContextWrapper::
                          DidFindRegistrationForLongRunningMessage,
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index 4195603..4c88d24 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -413,8 +413,9 @@
 
         int bindingCounts[] = connection.remainingBindingStateCountsCurrentOrWhenDied();
         nativeSetTerminationInfo(terminationInfoPtr, connection.bindingStateCurrentOrWhenDied(),
-                connection.isKilledByUs(), bindingCounts[ChildBindingState.STRONG],
-                bindingCounts[ChildBindingState.MODERATE], bindingCounts[ChildBindingState.WAIVED]);
+                connection.isKilledByUs(), connection.hasCleanExit(),
+                bindingCounts[ChildBindingState.STRONG], bindingCounts[ChildBindingState.MODERATE],
+                bindingCounts[ChildBindingState.WAIVED]);
     }
 
     @CalledByNative
@@ -644,6 +645,6 @@
     }
 
     private static native void nativeSetTerminationInfo(long termiantionInfoPtr,
-            @ChildBindingState int bindingState, boolean killedByUs, int remainingStrong,
-            int remainingModerate, int remainingWaived);
+            @ChildBindingState int bindingState, boolean killedByUs, boolean cleanExit,
+            int remainingStrong, int remainingModerate, int remainingWaived);
 }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java
deleted file mode 100644
index 1f25156..0000000
--- a/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2012 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.content.browser;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-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.ThreadUtils;
-import org.chromium.base.test.util.Feature;
-import org.chromium.content_public.browser.ViewEventSink;
-import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_public.browser.test.ContentJUnit4ClassRunner;
-import org.chromium.content_shell_apk.ContentShellActivityTestRule;
-
-/**
- * Tests for PopupZoomer.
- */
-@RunWith(ContentJUnit4ClassRunner.class)
-public class PopupZoomerTest {
-    @Rule
-    public ContentShellActivityTestRule mActivityTestRule = new ContentShellActivityTestRule();
-
-    private CustomCanvasPopupZoomer mPopupZoomer;
-    private ViewEventSink mViewEventSink;
-
-    private static class CustomCanvasPopupZoomer extends PopupZoomer {
-        Canvas mCanvas;
-        long mPendingDraws;
-
-        CustomCanvasPopupZoomer(Context context, ViewGroup containerView, Canvas c) {
-            super(context, containerView, null, null);
-            mCanvas = c;
-        }
-
-        @Override
-        public void invalidate() {
-            mPendingDraws++;
-        }
-
-        @Override
-        public void onDraw(Canvas c) {
-            mPendingDraws--;
-            super.onDraw(c);
-        }
-
-        // Test doesn't attach PopupZoomer to the view hierarchy,
-        // but onDraw() should still go on.
-        @Override
-        protected boolean acceptZeroSizeView() {
-            return true;
-        }
-
-        public void finishPendingDraws() {
-            // Finish all pending draw calls. A draw call may change mPendingDraws.
-            while (mPendingDraws > 0) {
-                onDraw(mCanvas);
-            }
-        }
-    }
-
-    private CustomCanvasPopupZoomer createPopupZoomerForTest(
-            Context context, ViewGroup containerView) {
-        return new CustomCanvasPopupZoomer(context, containerView,
-                new Canvas(Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8)));
-    }
-
-    private void sendSingleTapTouchEventOnView(View view, float x, float y) {
-        final long downEvent = SystemClock.uptimeMillis();
-        view.onTouchEvent(
-                MotionEvent.obtain(downEvent, downEvent, MotionEvent.ACTION_DOWN, x, y, 0));
-        view.onTouchEvent(
-                MotionEvent.obtain(downEvent, downEvent + 10, MotionEvent.ACTION_UP, x, y, 0));
-    }
-
-    @Before
-    public void setUp() throws Throwable {
-        mActivityTestRule.launchActivity(null);
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                Context context = mActivityTestRule.getActivity();
-                WebContents webContents = mActivityTestRule.getWebContents();
-                mViewEventSink = new ViewEventSinkImpl(webContents);
-                mPopupZoomer = createPopupZoomerForTest(InstrumentationRegistry.getTargetContext(),
-                        mActivityTestRule.getContainerView());
-                TapDisambiguator.fromWebContents(webContents).setPopupZoomerForTest(mPopupZoomer);
-            }
-        });
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Navigation"})
-    public void testDefaultCreateState() throws Exception {
-        Assert.assertEquals(View.INVISIBLE, mPopupZoomer.getVisibility());
-        Assert.assertFalse(mPopupZoomer.isShowing());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Navigation"})
-    public void testShowWithoutBitmap() throws Exception {
-        mPopupZoomer.show(new Rect(0, 0, 5, 5));
-
-        // The view should be invisible.
-        Assert.assertEquals(View.INVISIBLE, mPopupZoomer.getVisibility());
-        Assert.assertFalse(mPopupZoomer.isShowing());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Navigation"})
-    public void testShowWithBitmap() throws Exception {
-        mPopupZoomer.setBitmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8));
-        mPopupZoomer.show(new Rect(0, 0, 5, 5));
-
-        // The view should become visible.
-        Assert.assertEquals(View.VISIBLE, mPopupZoomer.getVisibility());
-        Assert.assertTrue(mPopupZoomer.isShowing());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Navigation"})
-    public void testHide() throws Exception {
-        mPopupZoomer.setBitmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8));
-        mPopupZoomer.show(new Rect(0, 0, 5, 5));
-
-        // The view should become visible.
-        Assert.assertEquals(View.VISIBLE, mPopupZoomer.getVisibility());
-        Assert.assertTrue(mPopupZoomer.isShowing());
-
-        // Call hide without animation.
-        mPopupZoomer.hide(false);
-
-        // The view should be invisible.
-        Assert.assertEquals(View.INVISIBLE, mPopupZoomer.getVisibility());
-        Assert.assertFalse(mPopupZoomer.isShowing());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Navigation"})
-    public void testOnTouchEventOutsidePopup() throws Exception {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPopupZoomer.setBitmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8));
-                mPopupZoomer.show(new Rect(0, 0, 5, 5));
-
-                // Wait for the show animation to finish.
-                mPopupZoomer.finishPendingDraws();
-
-                // The view should be visible.
-                Assert.assertEquals(View.VISIBLE, mPopupZoomer.getVisibility());
-                Assert.assertTrue(mPopupZoomer.isShowing());
-
-                // Send tap event at a point outside the popup.
-                // i.e. coordinates greater than 10 + PopupZoomer.ZOOM_BOUNDS_MARGIN
-                sendSingleTapTouchEventOnView(mPopupZoomer, 50, 50);
-
-                // Wait for the hide animation to finish.
-                mPopupZoomer.finishPendingDraws();
-
-                // The view should be invisible.
-                Assert.assertEquals(View.INVISIBLE, mPopupZoomer.getVisibility());
-                Assert.assertFalse(mPopupZoomer.isShowing());
-            }
-        });
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Navigation"})
-    public void testOnTouchEventInsidePopupNoOnTapListener() throws Exception {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPopupZoomer.setBitmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8));
-                mPopupZoomer.show(new Rect(0, 0, 5, 5));
-
-                // Wait for the animation to finish.
-                mPopupZoomer.finishPendingDraws();
-
-                // The view should be visible.
-                Assert.assertEquals(View.VISIBLE, mPopupZoomer.getVisibility());
-                Assert.assertTrue(mPopupZoomer.isShowing());
-
-                // Send tap event at a point inside the popup.
-                // i.e. coordinates between PopupZoomer.ZOOM_BOUNDS_MARGIN and
-                // PopupZoomer.ZOOM_BOUNDS_MARGIN + 10
-                sendSingleTapTouchEventOnView(mPopupZoomer, 30, 30);
-
-                // Wait for the animation to finish (if there is any).
-                mPopupZoomer.finishPendingDraws();
-
-                // The view should still be visible as no OnTapListener is set.
-                Assert.assertEquals(View.VISIBLE, mPopupZoomer.getVisibility());
-                Assert.assertTrue(mPopupZoomer.isShowing());
-            }
-        });
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Navigation"})
-    public void testHidePopupOnLosingFocus() throws Exception {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPopupZoomer.setBitmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8));
-                mPopupZoomer.show(new Rect(0, 0, 5, 5));
-
-                // Wait for the animation to finish.
-                mPopupZoomer.finishPendingDraws();
-
-                // The view should be visible.
-                Assert.assertEquals(View.VISIBLE, mPopupZoomer.getVisibility());
-                Assert.assertTrue(mPopupZoomer.isShowing());
-
-                // Simulate losing the focus.
-                mViewEventSink.onViewFocusChanged(false);
-
-                // Wait for the hide animation to finish.
-                mPopupZoomer.finishPendingDraws();
-
-                // Now that another view has been focused, the view should be invisible.
-                Assert.assertEquals(View.INVISIBLE, mPopupZoomer.getVisibility());
-                Assert.assertFalse(mPopupZoomer.isShowing());
-            }
-        });
-    }
-}
diff --git a/content/public/browser/child_process_termination_info.h b/content/public/browser/child_process_termination_info.h
index afd672e..0331f00 100644
--- a/content/public/browser/child_process_termination_info.h
+++ b/content/public/browser/child_process_termination_info.h
@@ -37,6 +37,9 @@
   // True if child service was explicitly killed by browser.
   bool was_killed_intentionally_by_browser = false;
 
+  // True if the child shut itself down cleanly by quitting the main runloop.
+  bool clean_exit = false;
+
   // Counts of remaining child processes with corresponding binding.
   int remaining_process_with_strong_binding = 0;
   int remaining_process_with_moderate_binding = 0;
diff --git a/content/public/browser/media_session.h b/content/public/browser/media_session.h
index da3e90d..a28f449 100644
--- a/content/public/browser/media_session.h
+++ b/content/public/browser/media_session.h
@@ -10,12 +10,6 @@
 #include "content/common/content_export.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 
-namespace blink {
-namespace mojom {
-enum class MediaSessionAction;
-}  // namespace mojom
-}  // namespace blink
-
 namespace content {
 
 class MediaSessionObserver;
@@ -52,7 +46,8 @@
   virtual bool IsActuallyPaused() const = 0;
 
   // Tell the media session a user action has performed.
-  virtual void DidReceiveAction(blink::mojom::MediaSessionAction action) = 0;
+  virtual void DidReceiveAction(
+      media_session::mojom::MediaSessionAction action) = 0;
 
   // Set the volume multiplier applied during ducking.
   virtual void SetDuckingVolumeMultiplier(double multiplier) = 0;
diff --git a/content/public/browser/media_session_observer.h b/content/public/browser/media_session_observer.h
index 9c60dde..6ebfc9e 100644
--- a/content/public/browser/media_session_observer.h
+++ b/content/public/browser/media_session_observer.h
@@ -12,11 +12,11 @@
 #include "content/common/content_export.h"
 #include "content/public/common/media_metadata.h"
 
-namespace blink {
+namespace media_session {
 namespace mojom {
 enum class MediaSessionAction;
 }  // namespace mojom
-}  // namespace blink
+}  // namespace media_session
 
 namespace content {
 
@@ -44,7 +44,7 @@
 
   // Called when the media session action list has changed.
   virtual void MediaSessionActionsChanged(
-      const std::set<blink::mojom::MediaSessionAction>& action) {}
+      const std::set<media_session::mojom::MediaSessionAction>& action) {}
 
  protected:
   // Create a MediaSessionObserver and start observing a session.
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index fdb2672..134e72b 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -732,6 +732,7 @@
     "//third_party/webrtc/media:rtc_media_base",
     "//third_party/webrtc/modules/audio_device",
     "//third_party/webrtc/modules/audio_processing",
+    "//third_party/webrtc/modules/audio_processing:api",
     "//third_party/webrtc/modules/audio_processing:audio_processing_statistics",
     "//third_party/webrtc/modules/audio_processing/aec_dump",
     "//third_party/webrtc/modules/video_coding:video_codec_interface",
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index a3da88a7..83b7b60 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -533,8 +533,9 @@
   DCHECK(extra_data);
   return CommonNavigationParams(
       info.url_request.Url(), referrer, extra_data->transition_type(),
-      navigation_type, true, info.replaces_current_history_item, GURL(), GURL(),
-      static_cast<PreviewsState>(info.url_request.GetPreviewsState()),
+      navigation_type, true,
+      info.frame_load_type == WebFrameLoadType::kReplaceCurrentItem, GURL(),
+      GURL(), static_cast<PreviewsState>(info.url_request.GetPreviewsState()),
       base::TimeTicks::Now(), info.url_request.HttpMethod().Latin1(),
       GetRequestBodyForWebURLRequest(info.url_request), source_location,
       false /* started_from_context_menu */, info.url_request.HasUserGesture(),
@@ -960,12 +961,13 @@
       common_params.input_start);
 
   if (common_params.source_location.has_value()) {
-    navigation_params->source_location.url =
+    blink::WebSourceLocation source_location;
+    source_location.url =
         WebString::FromLatin1(common_params.source_location->url);
-    navigation_params->source_location.line_number =
-        common_params.source_location->line_number;
-    navigation_params->source_location.column_number =
+    source_location.line_number = common_params.source_location->line_number;
+    source_location.column_number =
         common_params.source_location->column_number;
+    navigation_params->source_location = source_location;
   }
 
   navigation_params->is_user_activated =
@@ -3254,10 +3256,6 @@
                            std::move(subresource_overrides),
                            std::move(prefetch_loader_factory));
 
-  // Clear pending navigations which weren't sent to the browser because we
-  // did not get a didStartProvisionalLoad() notification for them.
-  pending_navigation_info_.reset(nullptr);
-
   // If the navigation is for "view source", the WebLocalFrame needs to be put
   // in a special mode.
   if (request_params.is_view_source)
@@ -4217,8 +4215,7 @@
 
 void RenderFrameImpl::DidStartProvisionalLoad(
     blink::WebDocumentLoader* document_loader,
-    blink::WebURLRequest& request,
-    mojo::ScopedMessagePipeHandle navigation_initiator_handle) {
+    blink::WebURLRequest& request) {
   // In fast/loader/stop-provisional-loads.html, we abort the load before this
   // callback is invoked.
   if (!document_loader)
@@ -4228,31 +4225,6 @@
                "RenderFrameImpl::didStartProvisionalLoad", "id", routing_id_,
                "url", document_loader->GetRequest().Url().GetString().Utf8());
 
-  // If we have a pending navigation to be sent to the browser send it here.
-  if (pending_navigation_info_.get()) {
-    NavigationPolicyInfo info(request);
-    info.navigation_type = pending_navigation_info_->navigation_type;
-    info.default_policy = pending_navigation_info_->policy;
-    info.replaces_current_history_item =
-        pending_navigation_info_->replaces_current_history_item;
-    info.is_history_navigation_in_new_child_frame =
-        pending_navigation_info_->history_navigation_in_new_child_frame;
-    info.is_client_redirect = pending_navigation_info_->client_redirect;
-    info.triggering_event_info =
-        pending_navigation_info_->triggering_event_info;
-    info.form = pending_navigation_info_->form;
-    info.source_location = pending_navigation_info_->source_location;
-    info.devtools_initiator_info =
-        pending_navigation_info_->devtools_initiator_info;
-    info.blob_url_token =
-        pending_navigation_info_->blob_url_token.PassInterface().PassHandle();
-    info.input_start = pending_navigation_info_->input_start;
-    info.href_translate = pending_navigation_info_->href_translate;
-
-    pending_navigation_info_.reset(nullptr);
-    BeginNavigation(info, std::move(navigation_initiator_handle));
-  }
-
   NavigationState* navigation_state =
       NavigationState::FromDocumentLoader(document_loader);
   for (auto& observer : observers_) {
@@ -5991,7 +5963,7 @@
 }
 
 WebNavigationPolicy RenderFrameImpl::DecidePolicyForNavigation(
-    const NavigationPolicyInfo& info) {
+    NavigationPolicyInfo& info) {
   // This method is only called for renderer initiated navigations, which
   // may have originated from a link-click, script, drag-n-drop operation, etc.
 
@@ -6161,8 +6133,8 @@
     // If the navigation is not synchronous, send it to the browser.  This
     // includes navigations with no request being sent to the network stack.
     if (!use_archive && IsURLHandledByNetworkStack(url)) {
-      pending_navigation_info_.reset(new PendingNavigationInfo(info));
-      return blink::kWebNavigationPolicyHandledByClient;
+      BeginNavigation(info);
+      return blink::kWebNavigationPolicyIgnore;
     } else {
       return blink::kWebNavigationPolicyCurrentTab;
     }
@@ -6425,7 +6397,8 @@
   params.blob_url_token =
       CloneBlobURLToken(info.blob_url_token.get()).PassHandle().release();
   params.should_replace_current_entry =
-      info.replaces_current_history_item && render_view_->history_list_length_;
+      info.frame_load_type == WebFrameLoadType::kReplaceCurrentItem &&
+      render_view_->history_list_length_;
   params.user_gesture = info.has_user_gesture;
   if (GetContentClient()->renderer()->AllowPopup())
     params.user_gesture = true;
@@ -6681,9 +6654,26 @@
 }
 }  // namespace
 
-void RenderFrameImpl::BeginNavigation(
-    const NavigationPolicyInfo& info,
-    mojo::ScopedMessagePipeHandle navigation_initiator_handle) {
+void RenderFrameImpl::BeginNavigation(NavigationPolicyInfo& info) {
+  auto navigation_params = std::make_unique<blink::WebNavigationParams>();
+  navigation_params->navigation_timings.input_start = info.input_start;
+  // We need the provider to be non-null, otherwise Blink crashes, even though
+  // the provider should not be used for any actual networking.
+  navigation_params->service_worker_network_provider =
+      BuildServiceWorkerNetworkProviderForNavigation(
+          nullptr /* request_params */,
+          nullptr /* controller_service_worker_info */);
+  if (!frame_->CreatePlaceholderDocumentLoader(
+          info.url_request, info.frame_load_type, info.navigation_type,
+          info.is_client_redirect, base::UnguessableToken::Create(),
+          std::move(navigation_params), BuildDocumentState())) {
+    return;
+  }
+
+  WebDocumentLoader* document_loader = frame_->GetProvisionalDocumentLoader();
+  NavigationState* navigation_state =
+      NavigationState::FromDocumentLoader(document_loader);
+
   browser_side_navigation_pending_ = true;
   browser_side_navigation_pending_url_ = info.url_request.Url();
 
@@ -6776,21 +6766,24 @@
 
   mojom::NavigationClientAssociatedPtrInfo navigation_client_info;
   if (IsPerNavigationMojoInterfaceEnabled()) {
-    WebDocumentLoader* document_loader = frame_->GetProvisionalDocumentLoader();
-    NavigationState* navigation_state =
-        NavigationState::FromDocumentLoader(document_loader);
     BindNavigationClient(mojo::MakeRequest(&navigation_client_info));
     navigation_state->set_navigation_client(std::move(navigation_client_impl_));
   }
 
   blink::mojom::NavigationInitiatorPtr initiator_ptr(
       blink::mojom::NavigationInitiatorPtrInfo(
-          std::move(navigation_initiator_handle), 0));
+          std::move(info.navigation_initiator_handle), 0));
 
   GetFrameHost()->BeginNavigation(
       MakeCommonNavigationParams(info, load_flags, info.input_start),
       std::move(begin_navigation_params), std::move(blob_url_token),
       std::move(navigation_client_info), std::move(initiator_ptr));
+
+  DCHECK(navigation_state->IsContentInitiated());
+  for (auto& observer : observers_) {
+    observer.DidStartProvisionalLoad(document_loader,
+                                     true /* is_content_initiated */);
+  }
 }
 
 void RenderFrameImpl::LoadDataURL(
@@ -7195,24 +7188,6 @@
   return provider->IsControlledByServiceWorker();
 }
 
-RenderFrameImpl::PendingNavigationInfo::PendingNavigationInfo(
-    const NavigationPolicyInfo& info)
-    : navigation_type(info.navigation_type),
-      policy(info.default_policy),
-      replaces_current_history_item(info.replaces_current_history_item),
-      history_navigation_in_new_child_frame(
-          info.is_history_navigation_in_new_child_frame),
-      client_redirect(info.is_client_redirect),
-      triggering_event_info(info.triggering_event_info),
-      form(info.form),
-      source_location(info.source_location),
-      devtools_initiator_info(info.devtools_initiator_info),
-      blob_url_token(CloneBlobURLToken(info.blob_url_token.get())),
-      input_start(info.input_start),
-      href_translate(info.href_translate) {}
-
-RenderFrameImpl::PendingNavigationInfo::~PendingNavigationInfo() = default;
-
 void RenderFrameImpl::BindWidget(mojom::WidgetRequest request) {
   GetRenderWidget()->SetWidgetBinding(std::move(request));
 }
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index d9c6f574..1df8017 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -646,14 +646,12 @@
                    mojo::ScopedMessagePipeHandle blob_url_token) override;
   void LoadErrorPage(int reason) override;
   blink::WebNavigationPolicy DecidePolicyForNavigation(
-      const NavigationPolicyInfo& info) override;
+      NavigationPolicyInfo& info) override;
   void WillSendSubmitEvent(const blink::WebFormElement& form) override;
   void DidCreateDocumentLoader(
       blink::WebDocumentLoader* document_loader) override;
-  void DidStartProvisionalLoad(
-      blink::WebDocumentLoader* document_loader,
-      blink::WebURLRequest& request,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle) override;
+  void DidStartProvisionalLoad(blink::WebDocumentLoader* document_loader,
+                               blink::WebURLRequest& request) override;
   void DidFailProvisionalLoad(const blink::WebURLError& error,
                               blink::WebHistoryCommitType commit_type) override;
   void DidCommitProvisionalLoad(
@@ -1178,9 +1176,7 @@
       const RequestNavigationParams& request_params);
 
   // Sends a FrameHostMsg_BeginNavigation to the browser
-  void BeginNavigation(
-      const NavigationPolicyInfo& info,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle);
+  void BeginNavigation(NavigationPolicyInfo& info);
 
   // Loads a data url.
   void LoadDataURL(
@@ -1569,33 +1565,6 @@
   // See BindingsPolicy for details.
   int enabled_bindings_ = 0;
 
-  // Contains information about a pending navigation to be sent to the browser.
-  // We save information about the navigation in decidePolicyForNavigation().
-  // The navigation is sent to the browser in didStartProvisionalLoad().
-  // Please see the BeginNavigation() for information.
-  struct PendingNavigationInfo {
-    blink::WebNavigationType navigation_type;
-    blink::WebNavigationPolicy policy;
-    bool replaces_current_history_item;
-    bool history_navigation_in_new_child_frame;
-    bool client_redirect;
-    blink::WebTriggeringEventInfo triggering_event_info;
-    blink::WebFormElement form;
-    blink::WebSourceLocation source_location;
-    blink::WebString devtools_initiator_info;
-    blink::mojom::BlobURLTokenPtr blob_url_token;
-    base::TimeTicks input_start;
-    blink::WebString href_translate;
-
-    explicit PendingNavigationInfo(const NavigationPolicyInfo& info);
-    ~PendingNavigationInfo();
-  };
-
-  // Contains information about a pending navigation to be sent to the browser.
-  // This state is allocated in decidePolicyForNavigation() and is used and
-  // released in didStartProvisionalLoad().
-  std::unique_ptr<PendingNavigationInfo> pending_navigation_info_;
-
   service_manager::BindSourceInfo browser_info_;
 
   mojom::FrameHostAssociatedPtr frame_host_ptr_;
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index be76db7..2786bacf 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -664,6 +664,8 @@
   request.SetFetchRedirectMode(network::mojom::FetchRedirectMode::kManual);
   request.SetFrameType(network::mojom::RequestContextFrameType::kTopLevel);
   request.SetRequestContext(blink::mojom::RequestContextType::INTERNAL);
+  request.SetRequestorOrigin(
+      blink::WebSecurityOrigin::Create(GURL("http://foo.com")));
   blink::WebLocalFrameClient::NavigationPolicyInfo policy_info(request);
   policy_info.navigation_type = blink::kWebNavigationTypeLinkClicked;
   policy_info.default_policy = blink::kWebNavigationPolicyCurrentTab;
@@ -671,7 +673,8 @@
       frame()->DecidePolicyForNavigation(policy_info);
   // If this is a renderer-initiated navigation that just begun, it should
   // stop and be sent to the browser.
-  EXPECT_EQ(blink::kWebNavigationPolicyHandledByClient, policy);
+  EXPECT_EQ(blink::kWebNavigationPolicyIgnore, policy);
+  EXPECT_TRUE(frame()->IsBrowserSideNavigationPending());
 
   // Verify that form posts to WebUI URLs will be sent to the browser process.
   blink::WebURLRequest form_request(GURL("chrome://foo"));
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 326b3be..edec4133 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
 import("//third_party/icu/config.gni")
@@ -326,7 +325,6 @@
     target = ":chromium_linker_test_apk__apk"
     output =
         "$root_gen_dir/content/shell/android/linker_test_apk/${target_name}.h"
-    exception_files = jni_exception_files
   }
 }
 
diff --git a/content/shell/test_runner/web_frame_test_client.cc b/content/shell/test_runner/web_frame_test_client.cc
index 85aa37e9..f16d0bd7 100644
--- a/content/shell/test_runner/web_frame_test_client.cc
+++ b/content/shell/test_runner/web_frame_test_client.cc
@@ -549,13 +549,8 @@
 }
 
 blink::WebNavigationPolicy WebFrameTestClient::DecidePolicyForNavigation(
-    const blink::WebLocalFrameClient::NavigationPolicyInfo& info) {
-  // PlzNavigate
-  // Navigation requests initiated by the renderer have checked navigation
-  // policy when the navigation was sent to the browser. Some layout tests
-  // expect that navigation policy is only checked once.
-  if (delegate_->IsNavigationInitiatedByRenderer(info.url_request))
-    return info.default_policy;
+    blink::WebLocalFrameClient::NavigationPolicyInfo& info) {
+  DCHECK(!delegate_->IsNavigationInitiatedByRenderer(info.url_request));
 
   if (test_runner()->shouldDumpNavigationPolicy()) {
     delegate_->PrintMessage("Default policy for navigation to '" +
diff --git a/content/shell/test_runner/web_frame_test_client.h b/content/shell/test_runner/web_frame_test_client.h
index aba6661..781c363 100644
--- a/content/shell/test_runner/web_frame_test_client.h
+++ b/content/shell/test_runner/web_frame_test_client.h
@@ -71,7 +71,7 @@
   void WillSendRequest(blink::WebURLRequest& request) override;
   void DidReceiveResponse(const blink::WebURLResponse& response) override;
   blink::WebNavigationPolicy DecidePolicyForNavigation(
-      const blink::WebLocalFrameClient::NavigationPolicyInfo& info) override;
+      blink::WebLocalFrameClient::NavigationPolicyInfo& info) override;
   void CheckIfAudioSinkExistsAndIsAuthorized(
       const blink::WebString& sink_id,
       std::unique_ptr<blink::WebSetSinkIdCallbacks> web_callbacks) override;
diff --git a/content/shell/test_runner/web_frame_test_proxy.cc b/content/shell/test_runner/web_frame_test_proxy.cc
index 6bb582aa..4e0797b2 100644
--- a/content/shell/test_runner/web_frame_test_proxy.cc
+++ b/content/shell/test_runner/web_frame_test_proxy.cc
@@ -247,7 +247,7 @@
 }
 
 blink::WebNavigationPolicy WebFrameTestProxy::DecidePolicyForNavigation(
-    const blink::WebLocalFrameClient::NavigationPolicyInfo& info) {
+    blink::WebLocalFrameClient::NavigationPolicyInfo& info) {
   blink::WebNavigationPolicy policy =
       test_client_->DecidePolicyForNavigation(info);
   if (policy == blink::kWebNavigationPolicyIgnore)
diff --git a/content/shell/test_runner/web_frame_test_proxy.h b/content/shell/test_runner/web_frame_test_proxy.h
index 114f14c..f4421ec 100644
--- a/content/shell/test_runner/web_frame_test_proxy.h
+++ b/content/shell/test_runner/web_frame_test_proxy.h
@@ -89,7 +89,7 @@
   void WillSendRequest(blink::WebURLRequest& request) override;
   void DidReceiveResponse(const blink::WebURLResponse& response) override;
   blink::WebNavigationPolicy DecidePolicyForNavigation(
-      const blink::WebLocalFrameClient::NavigationPolicyInfo& info) override;
+      blink::WebLocalFrameClient::NavigationPolicyInfo& info) override;
   void PostAccessibilityEvent(const blink::WebAXObject& object,
                               ax::mojom::Event event) override;
   void MarkWebAXObjectDirty(const blink::WebAXObject& object,
diff --git a/content/test/content_test_suite.cc b/content/test/content_test_suite.cc
index d93c9550b..b1044f5f 100644
--- a/content/test/content_test_suite.cc
+++ b/content/test/content_test_suite.cc
@@ -13,10 +13,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/test/test_content_client_initializer.h"
-#include "gpu/config/gpu_info_collector.h"
-#include "gpu/config/gpu_preferences.h"
-#include "gpu/config/gpu_util.h"
-#include "gpu/ipc/in_process_command_buffer.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 #include "media/base/media.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/init/gl_factory.h"
@@ -89,14 +86,9 @@
   bool is_child_process = command_line->HasSwitch(switches::kTestChildProcess);
   if (!is_child_process) {
     gl::GLSurfaceTestSupport::InitializeNoExtensionsOneOff();
-    gpu::GPUInfo gpu_info;
-    gpu::CollectGraphicsInfoForTesting(&gpu_info);
-    gpu::GpuFeatureInfo gpu_feature_info = gpu::ComputeGpuFeatureInfo(
-        gpu_info, gpu::GpuPreferences(), command_line, nullptr);
-    gpu::InProcessCommandBuffer::InitializeDefaultServiceForTesting(
-        gpu_feature_info);
+    auto* gpu_feature_info = gpu::GetTestGpuThreadHolder()->GetGpuFeatureInfo();
     gl::init::SetDisabledExtensionsPlatform(
-        gpu_feature_info.disabled_extensions);
+        gpu_feature_info->disabled_extensions);
     gl::init::InitializeExtensionSettingsOneOffPlatform();
   }
   testing::TestEventListeners& listeners =
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index be412e8..4f10b63 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -207,7 +207,7 @@
 }
 
 blink::WebNavigationPolicy TestRenderFrame::DecidePolicyForNavigation(
-    const blink::WebLocalFrameClient::NavigationPolicyInfo& info) {
+    blink::WebLocalFrameClient::NavigationPolicyInfo& info) {
   if (info.default_policy == blink::kWebNavigationPolicyCurrentTab &&
       ((GetWebFrame()->Parent() && info.form.IsNull()) ||
        next_request_url_override_.has_value())) {
diff --git a/content/test/test_render_frame.h b/content/test/test_render_frame.h
index 7f4f6e3..36234e07 100644
--- a/content/test/test_render_frame.h
+++ b/content/test/test_render_frame.h
@@ -57,7 +57,7 @@
       const std::vector<ui::ImeTextSpan>& ime_text_spans);
 
   blink::WebNavigationPolicy DecidePolicyForNavigation(
-      const blink::WebLocalFrameClient::NavigationPolicyInfo& info) override;
+      blink::WebLocalFrameClient::NavigationPolicyInfo& info) override;
 
   std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
   TakeLastCommitParams();
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index 2ea6f265..400fb0e 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "base/debug/alias.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -150,6 +152,7 @@
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnReceiveResponse(
     const network::ResourceResponseHead& head) {
   current_response_ = head;
+  on_receive_response_received_ = true;
   HandleResponseOrRedirectHeaders(
       base::BindRepeating(&InProgressRequest::ContinueToResponseStarted,
                           weak_factory_.GetWeakPtr()));
@@ -191,6 +194,13 @@
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::
     OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body) {
+  // TODO(https://crbug.com/882661): Remove this once the bug is fixed.
+  if (!on_receive_response_sent_) {
+    bool on_receive_response_received = on_receive_response_received_;
+    base::debug::Alias(&on_receive_response_received);
+    DEBUG_ALIAS_FOR_GURL(request_url, request_.url)
+    base::debug::DumpWithoutCrashing();
+  }
   target_client_->OnStartLoadingResponseBody(std::move(body));
 }
 
@@ -441,6 +451,7 @@
     // |FollowRedirect()|.
     proxied_client_binding_.Close();
     target_loader_.reset();
+    on_receive_response_received_ = false;
 
     ContinueToBeforeRedirect(redirect_info, net::OK);
     return;
@@ -452,6 +463,7 @@
 
   ExtensionWebRequestEventRouter::GetInstance()->OnResponseStarted(
       factory_->browser_context_, factory_->info_map_, &info_.value(), net::OK);
+  on_receive_response_sent_ = true;
   target_client_->OnReceiveResponse(current_response_);
 }
 
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
index 798b144..5334833 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -139,6 +139,10 @@
     // lifetime.
     base::Optional<net::AuthCredentials> auth_credentials_;
 
+    // TODO(https://crbug.com/882661): Remove this once the bug is fixed.
+    bool on_receive_response_received_ = false;
+    bool on_receive_response_sent_ = false;
+
     bool request_completed_ = false;
 
     base::WeakPtrFactory<InProgressRequest> weak_factory_;
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 7efb118..278d1f8 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -173,6 +173,7 @@
     ":gpu",
     ":webgpu",
     "//gpu/command_buffer/client:gles2_interface",
+    "//gpu/ipc:gpu_thread_holder",
   ]
   deps = [
     "//base/test:test_support",
diff --git a/gpu/command_buffer/service/skia_utils.cc b/gpu/command_buffer/service/skia_utils.cc
index a232ef6..202cd81 100644
--- a/gpu/command_buffer/service/skia_utils.cc
+++ b/gpu/command_buffer/service/skia_utils.cc
@@ -66,7 +66,9 @@
   switch (sk_color_type) {
     case kARGB_4444_SkColorType:
       if (internal_format != GL_RGBA4 && internal_format != GL_RGBA) {
-        LOG(ERROR) << "GetGrBackendTexture: color type mismatch.";
+        LOG(ERROR)
+            << "GetGrBackendTexture: color type mismatch. internal_format=0x"
+            << std::hex << internal_format;
         return false;
       }
       if (texture_info.fFormat == GL_RGBA)
@@ -74,15 +76,29 @@
       break;
     case kRGBA_8888_SkColorType:
       if (internal_format != GL_RGBA8_OES && internal_format != GL_RGBA) {
-        LOG(ERROR) << "GetGrBackendTexture: missing texture type info.";
+        LOG(ERROR)
+            << "GetGrBackendTexture: color type mismatch. internal_format=0x"
+            << std::hex << internal_format;
         return false;
       }
       if (texture_info.fFormat == GL_RGBA)
         texture_info.fFormat = GL_RGBA8_OES;
       break;
+    case kRGB_888x_SkColorType:
+      if (internal_format != GL_RGB8_OES && internal_format != GL_RGB) {
+        LOG(ERROR)
+            << "GetGrBackendTexture: color type mismatch. internal_format=0x"
+            << std::hex << internal_format;
+        return false;
+      }
+      if (texture_info.fFormat == GL_RGB)
+        texture_info.fFormat = GL_RGB8_OES;
+      break;
     case kBGRA_8888_SkColorType:
       if (internal_format != GL_BGRA_EXT && internal_format != GL_BGRA8_EXT) {
-        LOG(ERROR) << "GetGrBackendTexture: missing texture type info.";
+        LOG(ERROR)
+            << "GetGrBackendTexture: color type mismatch. internal_format=0x"
+            << std::hex << internal_format;
         return false;
       }
       if (texture_info.fFormat == GL_BGRA_EXT)
diff --git a/gpu/ipc/BUILD.gn b/gpu/ipc/BUILD.gn
index 1a98132b..53275ec5b 100644
--- a/gpu/ipc/BUILD.gn
+++ b/gpu/ipc/BUILD.gn
@@ -15,8 +15,6 @@
     "gpu_in_process_thread_service.h",
     "in_process_command_buffer.cc",
     "in_process_command_buffer.h",
-    "in_process_gpu_thread_holder.cc",
-    "in_process_gpu_thread_holder.h",
   ]
 
   defines = [ "GL_IN_PROCESS_CONTEXT_IMPLEMENTATION" ]
@@ -50,3 +48,29 @@
     "//ui/gl/init",
   ]
 }
+
+component("gpu_thread_holder") {
+  testonly = true
+
+  sources = [
+    "in_process_gpu_thread_holder.cc",
+    "in_process_gpu_thread_holder.h",
+    "test_gpu_thread_holder.cc",
+    "test_gpu_thread_holder.h",
+  ]
+
+  defines = [ "IS_GPU_THREAD_HOLDER_IMPL" ]
+
+  public_deps = [
+    "//gpu/config",
+  ]
+
+  deps = [
+    ":gl_in_process_context",
+    "//base",
+    "//gpu/command_buffer/service",
+    "//gpu/command_buffer/service:gles2",
+    "//gpu/ipc/host",
+    "//gpu/ipc/service",
+  ]
+}
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 6ee7cc7..9237e825 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -16,7 +16,6 @@
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/containers/queue.h"
-#include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
@@ -65,7 +64,6 @@
 #include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_client_ids.h"
 #include "gpu/ipc/host/gpu_memory_buffer_support.h"
-#include "gpu/ipc/in_process_gpu_thread_holder.h"
 #include "gpu/ipc/service/gpu_channel_manager_delegate.h"
 #include "gpu/ipc/service/image_transport_surface.h"
 #include "ui/gfx/geometry/size.h"
@@ -109,9 +107,6 @@
   return base::BindOnce(wrapper, std::move(task), result, completion);
 }
 
-base::LazyInstance<InProcessGpuThreadHolder>::DestructorAtExit
-    g_default_task_executer = LAZY_INSTANCE_INITIALIZER;
-
 class ScopedEvent {
  public:
   explicit ScopedEvent(base::WaitableEvent* event) : event_(event) {}
@@ -121,23 +116,6 @@
   base::WaitableEvent* event_;
 };
 
-// If |task_executer| is passed in then it will be returned, otherwise a default
-// task_executer will be constructed and returned.
-scoped_refptr<CommandBufferTaskExecutor> MaybeGetDefaultTaskExecutor(
-    scoped_refptr<CommandBufferTaskExecutor> task_executer) {
-  if (task_executer)
-    return task_executer;
-
-  // Call base::ThreadTaskRunnerHandle::IsSet() to ensure that it is
-  // instantiated before we create the GPU thread, otherwise shutdown order will
-  // delete the ThreadTaskRunnerHandle before the GPU thread's message loop,
-  // and when the message loop is shutdown, it will recreate
-  // ThreadTaskRunnerHandle, which will re-add a new task to the, AtExitManager,
-  // which causes a deadlock because it's already locked.
-  base::ThreadTaskRunnerHandle::IsSet();
-  return g_default_task_executer.Get().GetTaskExecutor();
-}
-
 }  // namespace
 
 class InProcessCommandBuffer::SharedImageInterface
@@ -208,7 +186,7 @@
     : command_buffer_id_(NextCommandBufferId()),
       flush_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                    base::WaitableEvent::InitialState::NOT_SIGNALED),
-      task_executor_(MaybeGetDefaultTaskExecutor(std::move(task_executer))),
+      task_executor_(std::move(task_executer)),
       shared_image_interface_(new SharedImageInterface(this)),
       fence_sync_wait_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                              base::WaitableEvent::InitialState::NOT_SIGNALED),
@@ -219,25 +197,13 @@
   // Detach gpu sequence checker because we want to bind it to the gpu sequence,
   // and not the current (client) sequence except for webview (see Initialize).
   DETACH_FROM_SEQUENCE(gpu_sequence_checker_);
+  DCHECK(task_executor_);
 }
 
 InProcessCommandBuffer::~InProcessCommandBuffer() {
   Destroy();
 }
 
-// static
-void InProcessCommandBuffer::InitializeDefaultServiceForTesting(
-    const GpuFeatureInfo& gpu_feature_info) {
-  // Call base::ThreadTaskRunnerHandle::IsSet() to ensure that it is
-  // instantiated before we create the GPU thread, otherwise shutdown order will
-  // delete the ThreadTaskRunnerHandle before the GPU thread's message loop,
-  // and when the message loop is shutdown, it will recreate
-  // ThreadTaskRunnerHandle, which will re-add a new task to the, AtExitManager,
-  // which causes a deadlock because it's already locked.
-  base::ThreadTaskRunnerHandle::IsSet();
-  g_default_task_executer.Get().SetGpuFeatureInfo(gpu_feature_info);
-}
-
 gpu::ServiceTransferCache* InProcessCommandBuffer::GetTransferCacheForTest()
     const {
   return static_cast<raster::RasterDecoder*>(decoder_.get())
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index 393317a..fa3e995b 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -178,13 +178,6 @@
   void SetUpdateVSyncParametersCallback(
       const UpdateVSyncParametersCallback& callback);
 
-  // Mostly the GpuFeatureInfo from GpuInit will be used to create a gpu thread
-  // service. In certain tests GpuInit is not part of the execution path, so
-  // the test suite need to compute it and pass it to the default service.
-  // See "gpu/ipc/in_process_command_buffer.cc".
-  static void InitializeDefaultServiceForTesting(
-      const GpuFeatureInfo& gpu_feature_info);
-
   gpu::ServiceTransferCache* GetTransferCacheForTest() const;
   int GetRasterDecoderIdForTest() const;
 
diff --git a/gpu/ipc/in_process_gpu_thread_holder.cc b/gpu/ipc/in_process_gpu_thread_holder.cc
index 6bd58a9c..8dea549 100644
--- a/gpu/ipc/in_process_gpu_thread_holder.cc
+++ b/gpu/ipc/in_process_gpu_thread_holder.cc
@@ -51,12 +51,6 @@
   return &gpu_feature_info_;
 }
 
-void InProcessGpuThreadHolder::SetGpuFeatureInfo(
-    const GpuFeatureInfo& gpu_feature_info) {
-  DCHECK(!task_executor_);
-  gpu_feature_info_ = gpu_feature_info;
-}
-
 scoped_refptr<CommandBufferTaskExecutor>
 InProcessGpuThreadHolder::GetTaskExecutor() {
   if (!task_executor_) {
diff --git a/gpu/ipc/in_process_gpu_thread_holder.h b/gpu/ipc/in_process_gpu_thread_holder.h
index 541f36d9..cdc98c3d 100644
--- a/gpu/ipc/in_process_gpu_thread_holder.h
+++ b/gpu/ipc/in_process_gpu_thread_holder.h
@@ -7,12 +7,12 @@
 
 #include <memory>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/threading/thread.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_preferences.h"
-#include "gpu/ipc/gl_in_process_context_export.h"
 
 namespace gpu {
 class CommandBufferTaskExecutor;
@@ -23,7 +23,7 @@
 // isn't a full GPU thread implementation and should only be used in tests. A
 // default GpuPreferences and GpuFeatureInfo will be constructed from the
 // command line when this class is first created.
-class GL_IN_PROCESS_CONTEXT_EXPORT InProcessGpuThreadHolder
+class COMPONENT_EXPORT(GPU_THREAD_HOLDER) InProcessGpuThreadHolder
     : public base::Thread {
  public:
   InProcessGpuThreadHolder();
@@ -37,11 +37,6 @@
   // called for the first time.
   GpuFeatureInfo* GetGpuFeatureInfo();
 
-  // Can only be called before GetTaskExecutor() is called for the first time.
-  // TODO(kylechar): Remove this and convert anything that uses it
-  // GetGpuFeatureInfo() instead.
-  void SetGpuFeatureInfo(const GpuFeatureInfo& gpu_feature_info);
-
   // Returns a task executor that runs commands on the GPU thread. The task
   // executor will be created the first time this is called.
   scoped_refptr<CommandBufferTaskExecutor> GetTaskExecutor();
diff --git a/gpu/ipc/test_gpu_thread_holder.cc b/gpu/ipc/test_gpu_thread_holder.cc
new file mode 100644
index 0000000..ab4c55e
--- /dev/null
+++ b/gpu/ipc/test_gpu_thread_holder.cc
@@ -0,0 +1,16 @@
+// 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 "gpu/ipc/test_gpu_thread_holder.h"
+
+#include "base/no_destructor.h"
+
+namespace gpu {
+
+InProcessGpuThreadHolder* GetTestGpuThreadHolder() {
+  static base::NoDestructor<InProcessGpuThreadHolder> instance;
+  return instance.get();
+}
+
+}  // namespace gpu
diff --git a/gpu/ipc/test_gpu_thread_holder.h b/gpu/ipc/test_gpu_thread_holder.h
new file mode 100644
index 0000000..75ae34c
--- /dev/null
+++ b/gpu/ipc/test_gpu_thread_holder.h
@@ -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.
+
+#ifndef GPU_IPC_TEST_GPU_THREAD_HOLDER_H_
+#define GPU_IPC_TEST_GPU_THREAD_HOLDER_H_
+
+#include "base/component_export.h"
+#include "gpu/ipc/in_process_gpu_thread_holder.h"
+
+namespace gpu {
+
+// Returns a global InProcessGpuThreadHolder instance that can be used to get a
+// task executor for use with InProcessCommandBuffer in tests. Any changes to
+// GpuPreferences or GpuFeatureInfo should be done during test suite
+// initialization before *any* tests run.
+COMPONENT_EXPORT(GPU_THREAD_HOLDER)
+InProcessGpuThreadHolder* GetTestGpuThreadHolder();
+
+}  // namespace gpu
+
+#endif  // GPU_IPC_TEST_GPU_THREAD_HOLDER_H_
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index de2ae81..60134c5 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -164,7 +164,6 @@
     "//components/prefs",
     "//components/prefs/ios",
     "//components/proxy_config",
-    "//components/signin/core/browser",
     "//components/suggestions",
     "//components/sync",
     "//components/url_formatter",
@@ -246,6 +245,7 @@
     "//ios/third_party/material_roboto_font_loader_ios",
     "//mojo/core/embedder",
     "//net",
+    "//services/identity/public/cpp:cpp",
     "//skia",
     "//ui/base",
     "//ui/gfx",
diff --git a/ios/chrome/app/DEPS b/ios/chrome/app/DEPS
index 302d8e25..2ede3f3c 100644
--- a/ios/chrome/app/DEPS
+++ b/ios/chrome/app/DEPS
@@ -17,7 +17,6 @@
   "+components/payments/core",
   "+components/prefs",
   "+components/reading_list/core",
-  "+components/signin/core/browser",
   "+components/suggestions",
   "+components/task_scheduler_util",
   "+components/url_formatter",
@@ -27,6 +26,7 @@
   "+ios/net",
   "+ios/public/provider/chrome",
   "+mojo/core/embedder/embedder.h",
+  "+services/identity/public/cpp/identity_manager.h",
   "+third_party/breakpad/breakpad/src/client/ios",
 
   # Strings and resources.
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index c394069..2a1d63f 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -36,7 +36,6 @@
 #include "components/payments/core/features.h"
 #include "components/prefs/ios/pref_observer_bridge.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/url_formatter/url_formatter.h"
 #include "components/web_resource/web_resource_pref_names.h"
 #import "ios/chrome/app/application_delegate/app_state.h"
@@ -102,7 +101,7 @@
 #include "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_delegate.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
 #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
@@ -155,6 +154,7 @@
 #include "ios/web/public/webui/web_ui_ios_controller_factory.h"
 #include "mojo/core/embedder/embedder.h"
 #import "net/base/mac/url_conversions.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -2593,9 +2593,9 @@
   ios::ChromeBrowserState* browserState = [self currentBrowserState];
   if (browserState->IsOffTheRecord())
     return nil;
-  SigninManager* signin_manager =
-      ios::SigninManagerFactory::GetForBrowserState(browserState);
-  std::string username = signin_manager->GetAuthenticatedAccountInfo().email;
+  identity::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForBrowserState(browserState);
+  std::string username = identity_manager->GetPrimaryAccountInfo().email;
   return username.empty() ? nil : base::SysUTF8ToNSString(username);
 }
 
diff --git a/ios/chrome/app/spotlight/base_spotlight_manager.mm b/ios/chrome/app/spotlight/base_spotlight_manager.mm
index 8a20fba..703641a4 100644
--- a/ios/chrome/app/spotlight/base_spotlight_manager.mm
+++ b/ios/chrome/app/spotlight/base_spotlight_manager.mm
@@ -11,7 +11,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "components/favicon/core/fallback_url_util.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
 #include "ios/chrome/grit/ios_strings.h"
diff --git a/ios/chrome/app/spotlight/spotlight_manager_unittest.mm b/ios/chrome/app/spotlight/spotlight_manager_unittest.mm
index 37099a1..89aea46 100644
--- a/ios/chrome/app/spotlight/spotlight_manager_unittest.mm
+++ b/ios/chrome/app/spotlight/spotlight_manager_unittest.mm
@@ -14,7 +14,7 @@
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/bookmarks/test/test_bookmark_client.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon/core/test/mock_favicon_service.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #import "ios/chrome/app/spotlight/bookmarks_spotlight_manager.h"
diff --git a/ios/chrome/browser/autocomplete/BUILD.gn b/ios/chrome/browser/autocomplete/BUILD.gn
index fd0a873a..95391e1 100644
--- a/ios/chrome/browser/autocomplete/BUILD.gn
+++ b/ios/chrome/browser/autocomplete/BUILD.gn
@@ -41,6 +41,7 @@
     "//ios/chrome/browser/web_state_list",
     "//ios/public/provider/chrome/browser",
     "//ios/web",
+    "//services/identity/public/cpp",
     "//url",
   ]
 }
diff --git a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
index 5b92bfe..997f57a 100644
--- a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
+++ b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
@@ -11,7 +11,6 @@
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/unified_consent/unified_consent_service.h"
 #include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
@@ -26,13 +25,14 @@
 #include "ios/chrome/browser/history/top_sites_factory.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_list.h"
 #include "ios/chrome/browser/unified_consent/unified_consent_service_factory.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/web/public/web_state/web_state.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -180,9 +180,9 @@
 }
 
 bool AutocompleteProviderClientImpl::IsAuthenticated() const {
-  SigninManagerBase* signin_manager =
-      ios::SigninManagerFactory::GetForBrowserState(browser_state_);
-  return signin_manager != nullptr && signin_manager->IsAuthenticated();
+  identity::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForBrowserState(browser_state_);
+  return identity_manager != nullptr && identity_manager->HasPrimaryAccount();
 }
 
 bool AutocompleteProviderClientImpl::IsUnifiedConsentGiven() const {
diff --git a/ios/chrome/browser/favicon/favicon_loader.mm b/ios/chrome/browser/favicon/favicon_loader.mm
index de20240..bb0945e 100644
--- a/ios/chrome/browser/favicon/favicon_loader.mm
+++ b/ios/chrome/browser/favicon/favicon_loader.mm
@@ -11,7 +11,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "components/favicon/core/fallback_url_util.h"
 #include "components/favicon/core/favicon_server_fetcher_params.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_callback.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
diff --git a/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.cc b/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.cc
index 5076c52..db78f6b 100644
--- a/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.cc
+++ b/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.cc
@@ -5,7 +5,7 @@
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 
 #include "base/memory/singleton.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/image_fetcher/ios/ios_image_decoder_impl.h"
 #include "components/keyed_service/core/service_access_type.h"
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
index 160f4f0..b26356c0 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
@@ -6,58 +6,63 @@
 
 #include <memory>
 
-#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/browser/account_info.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_configurator.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_consumer.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
 namespace {
-class SignInObserver;
+class IdentityManagerObserver;
 }  // namespace
 
 @interface BookmarkPromoController ()<SigninPromoViewConsumer> {
   bool _isIncognito;
   ios::ChromeBrowserState* _browserState;
-  std::unique_ptr<SignInObserver> _signinObserver;
+  std::unique_ptr<IdentityManagerObserver> _identityManagerObserver;
 }
 
 // Mediator to use for the sign-in promo view displayed in the bookmark view.
 @property(nonatomic, readwrite, strong)
     SigninPromoViewMediator* signinPromoViewMediator;
 
-// SignInObserver Callbacks
+// IdentityManagerObserver Callbacks
 
 // Called when a user signs into Google services such as sync.
-- (void)googleSigninSucceededWithAccountId:(const std::string&)account_id
-                                  username:(const std::string&)username;
+- (void)onPrimaryAccountSetWithAccountId:(const std::string&)account_id
+                                username:(const std::string&)username;
 
 // Called when the currently signed-in user for a user has been signed out.
-- (void)googleSignedOutWithAcountId:(const std::string&)account_id
-                           username:(const std::string&)username;
+- (void)onPrimaryAccountClearedWithAccountId:(const std::string&)account_id
+                                    username:(const std::string&)username;
 
 @end
 
 namespace {
-class SignInObserver : public SigninManagerBase::Observer {
+class IdentityManagerObserver : public identity::IdentityManager::Observer {
  public:
-  SignInObserver(BookmarkPromoController* controller)
+  IdentityManagerObserver(BookmarkPromoController* controller)
       : controller_(controller) {}
 
-  void GoogleSigninSucceeded(const std::string& account_id,
-                             const std::string& username) override {
-    [controller_ googleSigninSucceededWithAccountId:account_id
-                                           username:username];
+  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override {
+    [controller_
+        onPrimaryAccountSetWithAccountId:primary_account_info.account_id
+                                username:primary_account_info.email];
   }
 
-  void GoogleSignedOut(const std::string& account_id,
-                       const std::string& username) override {
-    [controller_ googleSignedOutWithAcountId:account_id username:username];
+  void OnPrimaryAccountCleared(
+      const AccountInfo& previous_primary_account_info) override {
+    [controller_
+        onPrimaryAccountClearedWithAccountId:previous_primary_account_info
+                                                 .account_id
+                                    username:previous_primary_account_info
+                                                 .email];
   }
 
  private:
@@ -83,10 +88,10 @@
     _isIncognito = browserState->IsOffTheRecord();
     if (!_isIncognito) {
       _browserState = browserState;
-      _signinObserver.reset(new SignInObserver(self));
-      SigninManager* signinManager =
-          ios::SigninManagerFactory::GetForBrowserState(_browserState);
-      signinManager->AddObserver(_signinObserver.get());
+      _identityManagerObserver.reset(new IdentityManagerObserver(self));
+      identity::IdentityManager* identityManager =
+          IdentityManagerFactory::GetForBrowserState(_browserState);
+      identityManager->AddObserver(_identityManagerObserver.get());
       _signinPromoViewMediator = [[SigninPromoViewMediator alloc]
           initWithBrowserState:_browserState
                    accessPoint:signin_metrics::AccessPoint::
@@ -103,9 +108,9 @@
   [_signinPromoViewMediator signinPromoViewRemoved];
   if (!_isIncognito) {
     DCHECK(_browserState);
-    SigninManager* signinManager =
-        ios::SigninManagerFactory::GetForBrowserState(_browserState);
-    signinManager->RemoveObserver(_signinObserver.get());
+    identity::IdentityManager* identityManager =
+        IdentityManagerFactory::GetForBrowserState(_browserState);
+    identityManager->RemoveObserver(_identityManagerObserver.get());
   }
 }
 
@@ -132,24 +137,24 @@
           shouldDisplaySigninPromoViewWithAccessPoint:
               signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER
                                          browserState:_browserState]) {
-    SigninManager* signinManager =
-        ios::SigninManagerFactory::GetForBrowserState(_browserState);
-    self.shouldShowSigninPromo = !signinManager->IsAuthenticated();
+    identity::IdentityManager* identityManager =
+        IdentityManagerFactory::GetForBrowserState(_browserState);
+    self.shouldShowSigninPromo = !identityManager->HasPrimaryAccount();
   }
 }
 
-#pragma mark - SignInObserver
+#pragma mark - IdentityManagerObserver
 
 // Called when a user signs into Google services such as sync.
-- (void)googleSigninSucceededWithAccountId:(const std::string&)account_id
-                                  username:(const std::string&)username {
+- (void)onPrimaryAccountSetWithAccountId:(const std::string&)account_id
+                                username:(const std::string&)username {
   if (!self.signinPromoViewMediator.isSigninInProgress)
     self.shouldShowSigninPromo = NO;
 }
 
 // Called when the currently signed-in user for a user has been signed out.
-- (void)googleSignedOutWithAcountId:(const std::string&)account_id
-                           username:(const std::string&)username {
+- (void)onPrimaryAccountClearedWithAccountId:(const std::string&)account_id
+                                    username:(const std::string&)username {
   [self updateShouldShowSigninPromo];
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_favicon_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_favicon_mediator.mm
index a2e35e14..befde10b 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_favicon_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_favicon_mediator.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_favicon_mediator.h"
 
 #include "base/bind.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/ntp_snippets/category.h"
 #include "components/ntp_snippets/content_suggestions_service.h"
 #include "ios/chrome/browser/application_context.h"
diff --git a/ios/chrome/browser/ui/favicon/favicon_attributes_provider.mm b/ios/chrome/browser/ui/favicon/favicon_attributes_provider.mm
index 9c2e566..1d70fed 100644
--- a/ios/chrome/browser/ui/favicon/favicon_attributes_provider.mm
+++ b/ios/chrome/browser/ui/favicon/favicon_attributes_provider.mm
@@ -10,7 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "components/favicon/core/fallback_url_util.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
 #include "ios/chrome/browser/favicon/large_icon_cache.h"
diff --git a/ios/chrome/browser/ui/history/BUILD.gn b/ios/chrome/browser/ui/history/BUILD.gn
index 6c4753c..bddcbf5 100644
--- a/ios/chrome/browser/ui/history/BUILD.gn
+++ b/ios/chrome/browser/ui/history/BUILD.gn
@@ -49,7 +49,6 @@
     "history_entry_item.mm",
     "history_entry_item_delegate.h",
     "history_entry_item_interface.h",
-    "history_image_data_source.h",
     "history_local_commands.h",
     "history_table_view_controller.h",
     "history_table_view_controller.mm",
diff --git a/ios/chrome/browser/ui/history/history_image_data_source.h b/ios/chrome/browser/ui/history/history_image_data_source.h
deleted file mode 100644
index 3c0539b..0000000
--- a/ios/chrome/browser/ui/history/history_image_data_source.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_IMAGE_DATA_SOURCE_H_
-#define IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_IMAGE_DATA_SOURCE_H_
-
-#import <UIKit/UIKit.h>
-
-class GURL;
-@class FaviconAttributes;
-
-// Protocol that the history UI uses to retrieve images.
-@protocol HistoryImageDataSource
-// Requests the receiver to provide a favicon image for |URL|. A
-// FaviconAttributes instance with non-nil properties is synchronously returned
-// for immediate use. |completion| is called asynchronously with a
-// FaviconAttribues instance if appropriate. For example, a default image may be
-// returned synchronously and the actual favicon returned asynchronously. In
-// another example, the image returned synchronously may be the actual favicon,
-// so there is no need to call the completion block.
-- (FaviconAttributes*)faviconForURL:(const GURL&)URL
-                         completion:(void (^)(FaviconAttributes*))completion;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_IMAGE_DATA_SOURCE_H_
diff --git a/ios/chrome/browser/ui/history/history_mediator.h b/ios/chrome/browser/ui/history/history_mediator.h
index 0cbe8b5..465c06f 100644
--- a/ios/chrome/browser/ui/history/history_mediator.h
+++ b/ios/chrome/browser/ui/history/history_mediator.h
@@ -7,13 +7,13 @@
 
 #import <Foundation/Foundation.h>
 
-#import "ios/chrome/browser/ui/history/history_image_data_source.h"
+#import "ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h"
 
 namespace ios {
 class ChromeBrowserState;
 }
 
-@interface HistoryMediator : NSObject<HistoryImageDataSource>
+@interface HistoryMediator : NSObject<TableViewFaviconDataSource>
 
 // The coordinator's BrowserState.
 @property(nonatomic, assign) ios::ChromeBrowserState* browserState;
diff --git a/ios/chrome/browser/ui/history/history_mediator.mm b/ios/chrome/browser/ui/history/history_mediator.mm
index 544a04e1..58f69446 100644
--- a/ios/chrome/browser/ui/history/history_mediator.mm
+++ b/ios/chrome/browser/ui/history/history_mediator.mm
@@ -38,7 +38,7 @@
   return self;
 }
 
-#pragma mark - HistoryImageDataSource
+#pragma mark - TableViewFaviconDataSource
 
 - (FaviconAttributes*)faviconForURL:(const GURL&)URL
                          completion:(void (^)(FaviconAttributes*))completion {
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.h b/ios/chrome/browser/ui/history/history_table_view_controller.h
index 0aab21168..23e32ef 100644
--- a/ios/chrome/browser/ui/history/history_table_view_controller.h
+++ b/ios/chrome/browser/ui/history/history_table_view_controller.h
@@ -14,7 +14,7 @@
 }
 
 @class ContextMenuCoordinator;
-@protocol HistoryImageDataSource;
+@protocol TableViewFaviconDataSource;
 @protocol HistoryLocalCommands;
 @protocol HistoryPresentationDelegate;
 @protocol UrlLoader;
@@ -34,7 +34,7 @@
 // Delegate used to make the Tab UI visible.
 @property(nonatomic, weak) id<HistoryPresentationDelegate> presentationDelegate;
 // Data source for favicon images.
-@property(nonatomic, weak) id<HistoryImageDataSource> imageDataSource;
+@property(nonatomic, weak) id<TableViewFaviconDataSource> imageDataSource;
 // Coordinator for displaying context menus for history entries.
 @property(nonatomic, strong) ContextMenuCoordinator* contextMenuCoordinator;
 
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.mm b/ios/chrome/browser/ui/history/history_table_view_controller.mm
index 140e525..efcbe37 100644
--- a/ios/chrome/browser/ui/history/history_table_view_controller.mm
+++ b/ios/chrome/browser/ui/history/history_table_view_controller.mm
@@ -23,7 +23,6 @@
 #include "ios/chrome/browser/ui/history/history_entry_inserter.h"
 #import "ios/chrome/browser/ui/history/history_entry_item.h"
 #import "ios/chrome/browser/ui/history/history_entry_item_delegate.h"
-#import "ios/chrome/browser/ui/history/history_image_data_source.h"
 #include "ios/chrome/browser/ui/history/history_local_commands.h"
 #import "ios/chrome/browser/ui/history/history_ui_constants.h"
 #include "ios/chrome/browser/ui/history/history_util.h"
@@ -32,6 +31,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_link_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
+#import "ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h"
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 #import "ios/chrome/browser/ui/util/pasteboard_util.h"
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_mediator_unittest.mm b/ios/chrome/browser/ui/reading_list/reading_list_mediator_unittest.mm
index 097c8b7c..9f4ddf2 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_mediator_unittest.mm
@@ -8,7 +8,7 @@
 
 #include "base/strings/sys_string_conversions.h"
 #include "base/test/simple_test_clock.h"
-#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon/core/large_icon_service_impl.h"
 #include "components/favicon/core/test/mock_favicon_service.h"
 #include "components/reading_list/core/reading_list_model_impl.h"
 #include "components/url_formatter/url_formatter.h"
diff --git a/ios/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
index 3d560ba..71cb6f5 100644
--- a/ios/chrome/browser/ui/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -46,7 +46,6 @@
     "recent_tabs_constants.h",
     "recent_tabs_constants.mm",
     "recent_tabs_consumer.h",
-    "recent_tabs_image_data_source.h",
     "recent_tabs_presentation_delegate.h",
     "recent_tabs_table_view_controller.h",
     "recent_tabs_table_view_controller.mm",
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.h b/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.h
index b9d73908..e87bbad 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.h
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.h
@@ -8,9 +8,9 @@
 #import <Foundation/Foundation.h>
 
 #import "ios/chrome/browser/ui/recent_tabs/closed_tabs_observer_bridge.h"
-#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_image_data_source.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/recent_tabs/synced_sessions_bridge.h"
+#import "ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h"
 
 namespace ios {
 class ChromeBrowserState;
@@ -26,7 +26,7 @@
 // accordingly.
 @interface RecentTabsMediator : NSObject<ClosedTabsObserving,
                                          RecentTabsTableViewControllerDelegate,
-                                         RecentTabsImageDataSource>
+                                         TableViewFaviconDataSource>
 
 // The consumer for this object. This can change during the lifetime of this
 // object and may be nil.
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.mm
index 277c7c6..0efb17d 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.mm
@@ -113,7 +113,7 @@
   [self.consumer setTabRestoreService:nullptr];
 }
 
-#pragma mark - RecentTabsImageDataSource
+#pragma mark - TableViewFaviconDataSource
 
 - (FaviconAttributes*)faviconForURL:(const GURL&)URL
                          completion:(void (^)(FaviconAttributes*))completion {
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h
index 0be4fae..0e8f2c8 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h
@@ -16,7 +16,7 @@
 @protocol RecentTabsTableViewControllerDelegate;
 @protocol RecentTabsPresentationDelegate;
 @protocol UrlLoader;
-@protocol RecentTabsImageDataSource;
+@protocol TableViewFaviconDataSource;
 
 @interface RecentTabsTableViewController
     : ChromeTableViewController<RecentTabsConsumer>
@@ -35,7 +35,7 @@
     presentationDelegate;
 
 // Data source for images.
-@property(nonatomic, weak) id<RecentTabsImageDataSource> imageDataSource;
+@property(nonatomic, weak) id<TableViewFaviconDataSource> imageDataSource;
 
 // Initializers.
 - (instancetype)init NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index e5e399e..b556228c 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -29,7 +29,6 @@
 #import "ios/chrome/browser/ui/commands/show_signin_command.h"
 #import "ios/chrome/browser/ui/context_menu/context_menu_coordinator.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_constants.h"
-#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_image_data_source.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_presentation_delegate.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller_delegate.h"
 #include "ios/chrome/browser/ui/recent_tabs/synced_sessions.h"
@@ -44,6 +43,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
+#import "ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 #import "ios/chrome/browser/ui/util/top_view_controller.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_manager.mm b/ios/chrome/browser/ui/settings/clear_browsing_data_manager.mm
index e87e2a8..61fb568 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_manager.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_manager.mm
@@ -16,7 +16,6 @@
 #include "components/google/core/common/google_util.h"
 #include "components/history/core/browser/web_history_service.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -26,7 +25,7 @@
 #include "ios/chrome/browser/experimental_flags.h"
 #include "ios/chrome/browser/feature_engagement/tracker_factory.h"
 #include "ios/chrome/browser/history/web_history_service_factory.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_detail_item.h"
@@ -50,6 +49,7 @@
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/images/branded_image_provider.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -264,9 +264,9 @@
 // Add footers about user's account data.
 - (void)addSyncProfileItemsToModel:(ListModel*)model {
   // Google Account footer.
-  SigninManager* signinManager =
-      ios::SigninManagerFactory::GetForBrowserState(self.browserState);
-  if (signinManager->IsAuthenticated()) {
+  identity::IdentityManager* identityManager =
+      IdentityManagerFactory::GetForBrowserState(self.browserState);
+  if (identityManager->HasPrimaryAccount()) {
     // TODO(crbug.com/650424): Footer items must currently go into a separate
     // section, to work around a drawing bug in MDC.
     [model addSectionWithIdentifier:SectionIdentifierGoogleAccount];
@@ -291,7 +291,7 @@
   }
 
   // If not signed in, no need to continue with profile syncing.
-  if (!signinManager->IsAuthenticated()) {
+  if (!identityManager->HasPrimaryAccount()) {
     return;
   }
 
@@ -595,9 +595,9 @@
       "History.ClearBrowsingData.HistoryNoticeShownInFooterWhenUpdated",
       _shouldShowNoticeAboutOtherFormsOfBrowsingHistory);
 
-  SigninManager* signinManager =
-      ios::SigninManagerFactory::GetForBrowserState(_browserState);
-  if (!signinManager->IsAuthenticated()) {
+  identity::IdentityManager* identityManager =
+      IdentityManagerFactory::GetForBrowserState(_browserState);
+  if (!identityManager->HasPrimaryAccount()) {
     return;
   }
 
diff --git a/ios/chrome/browser/ui/table_view/BUILD.gn b/ios/chrome/browser/ui/table_view/BUILD.gn
index cd0dd6c..69a9ebfd 100644
--- a/ios/chrome/browser/ui/table_view/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/BUILD.gn
@@ -8,6 +8,7 @@
     "chrome_table_view_consumer.h",
     "chrome_table_view_controller.h",
     "chrome_table_view_controller.mm",
+    "table_view_favicon_data_source.h",
     "table_view_model.h",
     "table_view_model.mm",
     "table_view_navigation_controller.h",
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_image_data_source.h b/ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h
similarity index 73%
rename from ios/chrome/browser/ui/recent_tabs/recent_tabs_image_data_source.h
rename to ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h
index c71eff46e..dfd4a68 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_image_data_source.h
+++ b/ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h
@@ -2,17 +2,16 @@
 // 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_RECENT_TABS_RECENT_TABS_IMAGE_DATA_SOURCE_H_
-#define IOS_CHROME_BROWSER_UI_RECENT_TABS_RECENT_TABS_IMAGE_DATA_SOURCE_H_
+#ifndef IOS_CHROME_BROWSER_UI_TABLE_VIEW_TABLE_VIEW_FAVICON_DATA_SOURCE_H_
+#define IOS_CHROME_BROWSER_UI_TABLE_VIEW_TABLE_VIEW_FAVICON_DATA_SOURCE_H_
 
 #import <UIKit/UIKit.h>
 
 class GURL;
 @class FaviconAttributes;
 
-// Protocol that the recent tabs UI uses to synchronously and asynchronously
-// get images.
-@protocol RecentTabsImageDataSource
+// Protocol that Tableview UI uses to retrieve favicons for its cells.
+@protocol TableViewFaviconDataSource<NSObject>
 // Requests the receiver to provide a favicon image for |URL|. A
 // FaviconAttributes instance with non-nil properties is synchronously returned
 // for immediate use. |completion| is called asynchronously with a
@@ -24,4 +23,4 @@
                          completion:(void (^)(FaviconAttributes*))completion;
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_RECENT_TABS_RECENT_TABS_IMAGE_DATA_SOURCE_H_
+#endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_TABLE_VIEW_FAVICON_DATA_SOURCE_H_
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 41bfa91..0d632ef7 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -259,16 +259,9 @@
 const base::Feature kMemoryPressureBasedSourceBufferGC{
     "MemoryPressureBasedSourceBufferGC", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enable MojoVideoDecoder.  On Android, we use this by default.  Elsewhere,
-// it's experimental.
-const base::Feature kMojoVideoDecoder {
-  "MojoVideoDecoder",
-#if defined(OS_ANDROID)
-      base::FEATURE_ENABLED_BY_DEFAULT
-#else
-      base::FEATURE_DISABLED_BY_DEFAULT
-#endif
-};
+// Enable MojoVideoDecoder, replacing GpuVideoDecoder.
+const base::Feature kMojoVideoDecoder{"MojoVideoDecoder",
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable The D3D11 Video decoder. Must also enable MojoVideoDecoder for
 // this to have any effect.
diff --git a/media/blink/url_index.cc b/media/blink/url_index.cc
index 626b272..281e884d 100644
--- a/media/blink/url_index.cc
+++ b/media/blink/url_index.cc
@@ -316,29 +316,33 @@
   } else {
     waiting_load_callbacks_.emplace_back(std::move(cb));
     if (waiting_load_callbacks_.size() == 1)
-      url_index_->WaitToLoad(this);
+      url_index_->WaitToLoad(this, true);
   }
 }
 
-void UrlData::LoadNow() {
+void UrlData::LoadNow(bool immediate) {
   // Move the callbacks into local variables in case
   // any of the callbacks decide to call WaitToLoad().
   std::vector<base::OnceClosure> waiting_load_callbacks;
   std::swap(waiting_load_callbacks, waiting_load_callbacks_);
-  for (auto& i : waiting_load_callbacks)
-    std::move(i).Run();
+  for (auto& i : waiting_load_callbacks) {
+    if (immediate) {
+      std::move(i).Run();
+    } else {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(i));
+    }
+  }
 }
 
-
-void UrlIndex::WaitToLoad(UrlData* url_data) {
+void UrlIndex::WaitToLoad(UrlData* url_data, bool immediate) {
   if (loading_.find(url_data) != loading_.end()) {
     // Already loading
-    url_data->LoadNow();
+    url_data->LoadNow(immediate);
     return;
   }
   if (loading_.size() < GetMaxParallelPreload()) {
     loading_.insert(url_data);
-    url_data->LoadNow();
+    url_data->LoadNow(immediate);
     return;
   }
   loading_queue_.push_back(url_data);
@@ -353,9 +357,9 @@
     auto url_data = loading_queue_.front();
     loading_queue_.pop_front();
     if (url_data->IsPreloading()) {
-      WaitToLoad(url_data.get());
+      WaitToLoad(url_data.get(), false);
     } else {
-      url_data->LoadNow();
+      url_data->LoadNow(false);
     }
   }
 }
diff --git a/media/blink/url_index.h b/media/blink/url_index.h
index 27a60dd..1ed80606c 100644
--- a/media/blink/url_index.h
+++ b/media/blink/url_index.h
@@ -202,7 +202,8 @@
   bool IsPreloading() const;
 
   // Called by url_index when it's time to fire callbacks sent to WaitToLoad().
-  void LoadNow();
+  // |immediate| is true if this call was not delayed in any way.
+  void LoadNow(bool immediate);
 
   void OnEmpty();
   void MergeFrom(const scoped_refptr<UrlData>& other);
@@ -332,7 +333,9 @@
 
   // Call url_data->LoadNow() when it's ok to start preloading.
   // Note that LoadNow may be called immediately.
-  void WaitToLoad(UrlData* url_data);
+  // |immediate| shold be true if this was called directly from
+  // UrlData::WaitToLoad.
+  void WaitToLoad(UrlData* url_data, bool immediate);
 
   // Let us know that |url_data| is done preloading. If other resources
   // are waiting, we will let one of them know it's ok to load now.
diff --git a/media/blink/url_index_unittest.cc b/media/blink/url_index_unittest.cc
index cf84153..391b055 100644
--- a/media/blink/url_index_unittest.cc
+++ b/media/blink/url_index_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/logging.h"
+#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_feature_list.h"
@@ -178,13 +179,16 @@
   AddToLoadQueue(a.get(), base::BindOnce(&SetBoolWhenCalled, &called));
   UrlData::UrlDataWithLoadingState url_data_with_loading_state;
   url_data_with_loading_state.SetUrlData(a);
+  base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(called);
   url_data_with_loading_state.SetLoadingState(
       UrlData::UrlDataWithLoadingState::LoadingState::kPreload);
   AddToLoading(a.get());
+  base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(called);
   url_data_with_loading_state.SetLoadingState(
       UrlData::UrlDataWithLoadingState::LoadingState::kHasPlayed);
+  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(called);
 }
 
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index d7dfc417..1b5d7847 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -770,11 +770,8 @@
       break;
     }
   }
-  if (!profile_supported) {
-    RETURN_AND_NOTIFY_ON_FAILURE(false,
-                                 "Unsupported h.264, vp8, or vp9 profile",
-                                 PLATFORM_FAILURE, false);
-  }
+  RETURN_ON_FAILURE(profile_supported, "Unsupported h.264, vp8, or vp9 profile",
+                    false);
 
   if (config.profile == VP9PROFILE_PROFILE2 ||
       config.profile == VP9PROFILE_PROFILE3 ||
@@ -819,47 +816,38 @@
         ::GetProcAddress(dxgi_manager_dll, "MFCreateDXGIDeviceManager"));
   }
 
-  RETURN_AND_NOTIFY_ON_FAILURE(make_context_current_cb_.Run(),
-                               "Failed to make context current",
-                               PLATFORM_FAILURE, false);
+  RETURN_ON_FAILURE(make_context_current_cb_.Run(),
+                    "Failed to make context current", false);
 
-  RETURN_AND_NOTIFY_ON_FAILURE(
+  RETURN_ON_FAILURE(
       gl::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle,
-      "EGL_ANGLE_surface_d3d_texture_2d_share_handle unavailable",
-      PLATFORM_FAILURE, false);
+      "EGL_ANGLE_surface_d3d_texture_2d_share_handle unavailable", false);
 
-  RETURN_AND_NOTIFY_ON_FAILURE(gl::GLFence::IsSupported(),
-                               "GL fences are unsupported", PLATFORM_FAILURE,
-                               false);
+  RETURN_ON_FAILURE(gl::GLFence::IsSupported(), "GL fences are unsupported",
+                    false);
 
   State state = GetState();
-  RETURN_AND_NOTIFY_ON_FAILURE((state == kUninitialized),
-                               "Initialize: invalid state: " << state,
-                               ILLEGAL_STATE, false);
+  RETURN_ON_FAILURE((state == kUninitialized),
+                    "Initialize: invalid state: " << state, false);
 
-  RETURN_AND_NOTIFY_ON_FAILURE(InitializeMediaFoundation(),
-                               "Could not initialize Media Foundartion",
-                               PLATFORM_FAILURE, false);
+  RETURN_ON_FAILURE(InitializeMediaFoundation(),
+                    "Could not initialize Media Foundartion", false);
 
   config_ = config;
 
-  RETURN_AND_NOTIFY_ON_FAILURE(InitDecoder(config.profile),
-                               "Failed to initialize decoder", PLATFORM_FAILURE,
-                               false);
+  RETURN_ON_FAILURE(InitDecoder(config.profile), "Failed to initialize decoder",
+                    false);
 
-  RETURN_AND_NOTIFY_ON_FAILURE(GetStreamsInfoAndBufferReqs(),
-                               "Failed to get input/output stream info.",
-                               PLATFORM_FAILURE, false);
+  RETURN_ON_FAILURE(GetStreamsInfoAndBufferReqs(),
+                    "Failed to get input/output stream info.", false);
 
-  RETURN_AND_NOTIFY_ON_FAILURE(
+  RETURN_ON_FAILURE(
       SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0),
-      "Send MFT_MESSAGE_NOTIFY_BEGIN_STREAMING notification failed",
-      PLATFORM_FAILURE, false);
+      "Send MFT_MESSAGE_NOTIFY_BEGIN_STREAMING notification failed", false);
 
-  RETURN_AND_NOTIFY_ON_FAILURE(
+  RETURN_ON_FAILURE(
       SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0),
-      "Send MFT_MESSAGE_NOTIFY_START_OF_STREAM notification failed",
-      PLATFORM_FAILURE, false);
+      "Send MFT_MESSAGE_NOTIFY_START_OF_STREAM notification failed", false);
 
   if (codec_ == kCodecH264)
     config_change_detector_.reset(new H264ConfigChangeDetector);
diff --git a/media/webrtc/BUILD.gn b/media/webrtc/BUILD.gn
index 99d8209..b2856da9 100644
--- a/media/webrtc/BUILD.gn
+++ b/media/webrtc/BUILD.gn
@@ -29,6 +29,7 @@
   deps = [
     "//base",
     "//third_party/webrtc/modules/audio_processing",
+    "//third_party/webrtc/modules/audio_processing:api",
     "//third_party/webrtc/modules/audio_processing:audio_processing_statistics",
     "//third_party/webrtc_overrides:init_webrtc",
   ]
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 6591df9..eac99e2 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -1478,7 +1478,7 @@
   AddAndNotifyObserversOfThroughput(throughput_observation);
 }
 
-void NetworkQualityEstimator::MaybeComputeEffectiveConnectionType() {
+bool NetworkQualityEstimator::ShouldComputeEffectiveConnectionType() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   const base::TimeTicks now = tick_clock_->NowTicks();
@@ -1488,27 +1488,49 @@
   // computation. Strict inequalities are used to ensure that effective
   // connection type is recomputed on connection change events even if the clock
   // has not updated.
-  if (now - last_effective_connection_type_computation_ <
-          effective_connection_type_recomputation_interval_ &&
-      last_connection_change_ < last_effective_connection_type_computation_ &&
-      // Recompute the effective connection type if the previously computed
-      // effective connection type was unknown.
-      effective_connection_type_ != EFFECTIVE_CONNECTION_TYPE_UNKNOWN &&
-      // Recompute the effective connection type if the number of samples
-      // available now are 50% more than the number of samples that were
-      // available when the effective connection type was last computed.
-      rtt_observations_size_at_last_ect_computation_ * 1.5 >=
-          (rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP]
-               .Size() +
-           rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
-               .Size()) &&
-      throughput_observations_size_at_last_ect_computation_ * 1.5 >=
-          http_downstream_throughput_kbps_observations_.Size() &&
-      (new_rtt_observations_since_last_ect_computation_ +
-       new_throughput_observations_since_last_ect_computation_) <
-          params_->count_new_observations_received_compute_ect()) {
-    return;
+  if (now - last_effective_connection_type_computation_ >=
+      effective_connection_type_recomputation_interval_) {
+    return true;
   }
+
+  if (last_connection_change_ >= last_effective_connection_type_computation_) {
+    return true;
+  }
+
+  // Recompute the effective connection type if the previously computed
+  // effective connection type was unknown.
+  if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
+    return true;
+  }
+
+  // Recompute the effective connection type if the number of samples
+  // available now are 50% more than the number of samples that were
+  // available when the effective connection type was last computed.
+  if (rtt_observations_size_at_last_ect_computation_ * 1.5 <
+      (rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP].Size() +
+       rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
+           .Size())) {
+    return true;
+  }
+
+  if (throughput_observations_size_at_last_ect_computation_ * 1.5 <
+      http_downstream_throughput_kbps_observations_.Size()) {
+    return true;
+  }
+
+  if ((new_rtt_observations_since_last_ect_computation_ +
+       new_throughput_observations_since_last_ect_computation_) >=
+      params_->count_new_observations_received_compute_ect()) {
+    return true;
+  }
+  return false;
+}
+
+void NetworkQualityEstimator::MaybeComputeEffectiveConnectionType() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!ShouldComputeEffectiveConnectionType())
+    return;
   ComputeEffectiveConnectionType();
 }
 
diff --git a/net/nqe/network_quality_estimator.h b/net/nqe/network_quality_estimator.h
index bae2d3c5..67accb8 100644
--- a/net/nqe/network_quality_estimator.h
+++ b/net/nqe/network_quality_estimator.h
@@ -461,8 +461,11 @@
   // Returns true only if the |request| can be used for RTT estimation.
   bool RequestProvidesRTTObservation(const URLRequest& request) const;
 
-  // Recomputes effective connection type, if it was computed more than the
-  // specified duration ago, or if there has been a connection change recently.
+  // Returns true if ECT should be recomputed.
+  bool ShouldComputeEffectiveConnectionType() const;
+
+  // Calls ShouldComputeEffectiveConnectionType() to determine if ECT needs to
+  // be computed. If so, it recomputes effective connection type.
   void MaybeComputeEffectiveConnectionType();
 
   // Notifies observers of a change in effective connection type.
diff --git a/services/media_session/public/mojom/media_session.mojom b/services/media_session/public/mojom/media_session.mojom
index e82b617..062b8b9 100644
--- a/services/media_session/public/mojom/media_session.mojom
+++ b/services/media_session/public/mojom/media_session.mojom
@@ -12,6 +12,17 @@
   kPlaying,
 };
 
+// Spec: https://wicg.github.io/mediasession/
+[Extensible]
+enum MediaSessionAction {
+  kPlay,
+  kPause,
+  kPreviousTrack,
+  kNextTrack,
+  kSeekBackward,
+  kSeekForward,
+};
+
 // Contains state information about a MediaSession.
 struct MediaSessionInfo {
   [Extensible]
diff --git a/services/network/proxy_resolving_socket_factory_mojo.cc b/services/network/proxy_resolving_socket_factory_mojo.cc
index e5f1a85..0841e60 100644
--- a/services/network/proxy_resolving_socket_factory_mojo.cc
+++ b/services/network/proxy_resolving_socket_factory_mojo.cc
@@ -32,9 +32,11 @@
     CreateProxyResolvingSocketCallback callback) {
   std::unique_ptr<net::StreamSocket> net_socket =
       factory_impl_.CreateSocket(url, options && options->use_tls);
-  if (options && options->fake_tls_handshake)
+  if (options && options->fake_tls_handshake) {
+    DCHECK(!options->use_tls);
     net_socket = std::make_unique<jingle_glue::FakeSSLClientSocket>(
         std::move(net_socket));
+  }
 
   auto socket = std::make_unique<ProxyResolvingSocketMojo>(
       std::move(net_socket),
diff --git a/services/network/public/mojom/proxy_resolving_socket.mojom b/services/network/public/mojom/proxy_resolving_socket.mojom
index 4f683280..f8e8b37 100644
--- a/services/network/public/mojom/proxy_resolving_socket.mojom
+++ b/services/network/public/mojom/proxy_resolving_socket.mojom
@@ -43,6 +43,7 @@
   // Tries to do a fake TLS handshake on the connection.
   // This is sometimes used with XMPP to pass through proxies.
   // See jingle_glue::FakeSSLClientSocket for more details.
+  // Should not be used with |use_tls| set to true.
   bool fake_tls_handshake = false;
 };
 
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.content_browsertests.filter b/testing/buildbot/filters/chromeos.single_process_mash.content_browsertests.filter
index c184647ef..8f666190 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.content_browsertests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.content_browsertests.filter
@@ -13,13 +13,3 @@
 -CaptureScreenshotTest.SetDefaultBackgroundColorOverride
 -CaptureScreenshotTest.TransparentScreenshots
 -CompositedScrollingBrowserTest.Scroll3DTransformedScroller
-
-
-# InputEventAckWaiter::Wait() times out,
-# or RenderWidgetHostMouseEventMonitor::EventWasReceived fails.
-# https://crbug.com/884360
--SitePerProcessHitTestBrowserTest.CrossProcessTooltipTest*
-
-
-# Flaky. https://crbug.com/889878
--SyntheticKeyEventTest.KeyboardEventAck
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
index 50fc0e2..95349d0 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
@@ -38,7 +38,6 @@
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.OverviewMode/1
 
 # TabDragging: crbug.com/890071
--TabDragging/DetachToBrowserInSeparateDisplayTabDragControllerTest.DragBrowserWindowWhenMajorityOfBoundsInSecondDisplay/0
 -TabDragging/DetachToBrowserTabDragControllerTest.DetachToOwnWindowWhileInImmersiveFullscreenMode/1
 -TabDragging/DetachToBrowserTabDragControllerTest.DragToSeparateWindow/1
 -TabDragging/DetachToBrowserTabDragControllerTest.DragWithMaskedWindows/0
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1778f97..c2d1bc91 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -779,10 +779,28 @@
             ],
             "experiments": [
                 {
+                    "name": "Enabled_AllSmallForms",
+                    "disable_features": [
+                        "AutofillEnforceMinRequiredFieldsForQuery"
+                    ]
+                },
+                {
                     "name": "Enabled_NoSmallAddressForms",
                     "disable_features": [
                         "AutofillEnforceMinRequiredFieldsForQuery"
                     ]
+                },
+                {
+                    "name": "Control_NoSmallForms",
+                    "enable_features": [
+                        "AutofillEnforceMinRequiredFieldsForQuery"
+                    ]
+                },
+                {
+                    "name": "Default_Enabled_NoSmallAddressForms",
+                    "disable_features": [
+                        "AutofillEnforceMinRequiredFieldsForQuery"
+                    ]
                 }
             ]
         }
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 38319f2..843b050a 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2548,7 +2548,6 @@
 crbug.com/467127 [ Mac Win ] external/wpt/css/css-flexbox/ttwf-reftest-flex-wrap.html [ Failure ]
 
 crbug.com/467127 external/wpt/css/css-flexbox/negative-margins-001.html [ Failure ]
-crbug.com/467127 external/wpt/css/css-flexbox/percentage-heights-003.html [ Failure ]
 crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a.xhtml [ Failure ]
 crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b.xhtml [ Failure ]
 crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml [ Failure ]
@@ -5438,10 +5437,6 @@
 ####crbug.com/898394 [ Linux ] virtual/android/url-bar/bottom-and-top-fixed-sticks-to-top.html [ Failure ]
 crbug.com/898378 [ Mac10.13 ] virtual/scroll_customization/fast/scroll-behavior/smooth-scroll/keyboard-scroll.html [ Timeout ]
 
-# Sheriff 2018-10-24
-crbug.com/889185 [ Win10 ] http/tests/images/feature-policy-image-policies-with-border-radius.html [ Failure ]
-crbug.com/889185 [ Win10 ] http/tests/images/feature-policy-legacy-formats.html [ Failure ]
-
 # Sheriff 2018-10-26
 ### See crbug.com/891427 comment near the top of this file:
 ####crbug.com/899087 [ Linux ] virtual/android/fullscreen/full-screen-iframe-allowed-video.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/css3/masking/clip-path-descendant-text-mutated-expected.html b/third_party/WebKit/LayoutTests/css3/masking/clip-path-descendant-text-mutated-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/masking/clip-path-descendant-text-mutated-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/css3/masking/clip-path-descendant-text-mutated.html b/third_party/WebKit/LayoutTests/css3/masking/clip-path-descendant-text-mutated.html
new file mode 100644
index 0000000..327648f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/masking/clip-path-descendant-text-mutated.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="../../resources/ahem.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<style>
+#target {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+  border-right: 100px solid red;
+  clip-path: url(#clip);
+}
+</style>
+<div id="target"></div>
+<svg>
+  <clipPath id="clip">
+    <text id="text" y="80" font-family="Ahem" font-size="100">XX</text>
+  </clipPath>
+</svg>
+<script>
+runAfterLayoutAndPaint(function() {
+  text.firstChild.data = 'X';
+}, true);
+</script>
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 210780aa..beddc1e 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
@@ -112462,6 +112462,11 @@
      {}
     ]
    ],
+   "content-security-policy/generic/cspro-not-enforced-in-worker.html.sub.headers": [
+    [
+     {}
+    ]
+   ],
    "content-security-policy/generic/fail-0_1.js": [
     [
      {}
@@ -112497,6 +112502,11 @@
      {}
     ]
    ],
+   "content-security-policy/generic/support/eval.js": [
+    [
+     {}
+    ]
+   ],
    "content-security-policy/generic/support/load_img_and_post_result_header.html": [
     [
      {}
@@ -112977,6 +112987,16 @@
      {}
     ]
    ],
+   "content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html.sub.headers": [
+    [
+     {}
+    ]
+   ],
+   "content-security-policy/script-src/eval-allowed-in-report-only-mode.html.sub.headers": [
+    [
+     {}
+    ]
+   ],
    "content-security-policy/script-src/externalScript.js": [
     [
      {}
@@ -113222,6 +113242,11 @@
      {}
     ]
    ],
+   "content-security-policy/securitypolicyviolation/idlharness.window-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "content-security-policy/securitypolicyviolation/img-src-redirect-upgrade-reporting.https.html.headers": [
     [
      {}
@@ -151122,56 +151147,11 @@
      {}
     ]
    ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp.html.headers": [
     [
      {}
     ]
    ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_1-1000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_1001-2000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_2001-3000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_3001-4000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_4001-5000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_5001-6000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_6001-7000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_7001-last-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-han.html.headers": [
     [
      {}
@@ -151187,56 +151167,11 @@
      {}
     ]
    ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form.html.headers": [
     [
      {}
     ]
    ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_1-1000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_1001-2000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_2001-3000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_3001-4000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_4001-5000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_5001-6000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_6001-7000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_7001-last-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-ascii-expected.txt": [
     [
      {}
@@ -151257,56 +151192,11 @@
      {}
     ]
    ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href.html.headers": [
     [
      {}
     ]
    ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_1-1000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_1001-2000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_2001-3000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_3001-4000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_4001-5000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_5001-6000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_6001-7000-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_7001-last-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encoder.js": [
     [
      {}
@@ -152797,6 +152687,11 @@
      {}
     ]
    ],
+   "feature-policy/feature-policy-header-policy-declined.https.sub.html.sub.headers": [
+    [
+     {}
+    ]
+   ],
    "feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html.sub.headers": [
     [
      {}
@@ -180627,6 +180522,11 @@
      {}
     ]
    ],
+   "webrtc-identity/RTCPeerConnection-constructor-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "webrtc-identity/RTCPeerConnection-getIdentityAssertion.sub-expected.txt": [
     [
      {}
@@ -181072,6 +180972,26 @@
      {}
     ]
    ],
+   "webrtc/legacy/README.txt": [
+    [
+     {}
+    ]
+   ],
+   "webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "webrtc/legacy/onaddstream.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "webrtc/protocol/README.txt": [
     [
      {}
@@ -196528,6 +196448,12 @@
      {}
     ]
    ],
+   "content-security-policy/generic/cspro-not-enforced-in-worker.html": [
+    [
+     "/content-security-policy/generic/cspro-not-enforced-in-worker.html",
+     {}
+    ]
+   ],
    "content-security-policy/generic/directive-name-case-insensitive.sub.html": [
     [
      "/content-security-policy/generic/directive-name-case-insensitive.sub.html",
@@ -197430,6 +197356,18 @@
      {}
     ]
    ],
+   "content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html": [
+    [
+     "/content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html",
+     {}
+    ]
+   ],
+   "content-security-policy/script-src/eval-allowed-in-report-only-mode.html": [
+    [
+     "/content-security-policy/script-src/eval-allowed-in-report-only-mode.html",
+     {}
+    ]
+   ],
    "content-security-policy/script-src/hash-always-converted-to-utf-8/iso-8859-1.html": [
     [
      "/content-security-policy/script-src/hash-always-converted-to-utf-8/iso-8859-1.html",
@@ -197802,6 +197740,12 @@
      {}
     ]
    ],
+   "content-security-policy/securitypolicyviolation/constructor-required-fields.html": [
+    [
+     "/content-security-policy/securitypolicyviolation/constructor-required-fields.html",
+     {}
+    ]
+   ],
    "content-security-policy/securitypolicyviolation/idlharness.window.js": [
     [
      "/content-security-policy/securitypolicyviolation/idlharness.window.html",
@@ -199136,6 +199080,12 @@
      {}
     ]
    ],
+   "css/CSS2/abspos/adjacent-to-relpos-inline-in-inline-that-had-block.html": [
+    [
+     "/css/CSS2/abspos/adjacent-to-relpos-inline-in-inline-that-had-block.html",
+     {}
+    ]
+   ],
    "css/CSS2/abspos/adjacent-to-relpos-inline-that-had-block.html": [
     [
      "/css/CSS2/abspos/adjacent-to-relpos-inline-that-had-block.html",
@@ -203372,6 +203322,12 @@
      {}
     ]
    ],
+   "css/css-position/inheritance.html": [
+    [
+     "/css/css-position/inheritance.html",
+     {}
+    ]
+   ],
    "css/css-position/position-sticky-bottom.html": [
     [
      "/css/css-position/position-sticky-bottom.html",
@@ -210914,6 +210870,12 @@
      {}
     ]
    ],
+   "css/motion/parsing/offset-rotate-computed.html": [
+    [
+     "/css/motion/parsing/offset-rotate-computed.html",
+     {}
+    ]
+   ],
    "css/motion/parsing/offset-rotate-parsing-invalid.html": [
     [
      "/css/motion/parsing/offset-rotate-parsing-invalid.html",
@@ -223458,6 +223420,12 @@
      {}
     ]
    ],
+   "feature-policy/feature-policy-header-policy-declined.https.sub.html": [
+    [
+     "/feature-policy/feature-policy-header-policy-declined.https.sub.html",
+     {}
+    ]
+   ],
    "feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html": [
     [
      "/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html",
@@ -271742,6 +271710,12 @@
      {}
     ]
    ],
+   "webrtc-identity/RTCPeerConnection-constructor.html": [
+    [
+     "/webrtc-identity/RTCPeerConnection-constructor.html",
+     {}
+    ]
+   ],
    "webrtc-identity/RTCPeerConnection-getIdentityAssertion.sub.html": [
     [
      "/webrtc-identity/RTCPeerConnection-getIdentityAssertion.sub.html",
@@ -271924,12 +271898,6 @@
      {}
     ]
    ],
-   "webrtc/RTCPeerConnection-createOffer-offerToReceive.html": [
-    [
-     "/webrtc/RTCPeerConnection-createOffer-offerToReceive.html",
-     {}
-    ]
-   ],
    "webrtc/RTCPeerConnection-createOffer.html": [
     [
      "/webrtc/RTCPeerConnection-createOffer.html",
@@ -272260,6 +272228,24 @@
      {}
     ]
    ],
+   "webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive.html": [
+    [
+     "/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive.html",
+     {}
+    ]
+   ],
+   "webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https.html": [
+    [
+     "/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https.html",
+     {}
+    ]
+   ],
+   "webrtc/legacy/onaddstream.https.html": [
+    [
+     "/webrtc/legacy/onaddstream.https.html",
+     {}
+    ]
+   ],
    "webrtc/no-media-call.html": [
     [
      "/webrtc/no-media-call.html",
@@ -295625,6 +295611,14 @@
    "b16eadaedc07ca9dbc60310d850d49ded370d22a",
    "testharness"
   ],
+  "content-security-policy/generic/cspro-not-enforced-in-worker.html": [
+   "784cdc88752f0c97226e59007c514d018f737aeb",
+   "testharness"
+  ],
+  "content-security-policy/generic/cspro-not-enforced-in-worker.html.sub.headers": [
+   "877e192bbff2204ddf56f83e411f17bfb6adab89",
+   "support"
+  ],
   "content-security-policy/generic/directive-name-case-insensitive.sub.html": [
    "c65c59fb23fdc6d21eefc090927c1cfd4cd6702a",
    "testharness"
@@ -295725,6 +295719,10 @@
    "4980937eab7f7d07c80104fa2e73371781366c76",
    "support"
   ],
+  "content-security-policy/generic/support/eval.js": [
+   "d8ba2a5589a1a98abee73dab586414f601aaf6f3",
+   "support"
+  ],
   "content-security-policy/generic/support/load_img_and_post_result_header.html": [
    "c7a2e75dba37c85ae1710d92a2a8071ea229bf85",
    "support"
@@ -296637,6 +296635,22 @@
    "cb762eff806849df46dc758ef7b98b63f27f54c9",
    "support"
   ],
+  "content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html": [
+   "5357aa2eef2a97122a24a4cced51d340b156995e",
+   "testharness"
+  ],
+  "content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html.sub.headers": [
+   "37a04b5fc2eb66cbe2e7b09f03b9762b65eb2cab",
+   "support"
+  ],
+  "content-security-policy/script-src/eval-allowed-in-report-only-mode.html": [
+   "eebc8f026f9f1b7387614c86f4d5d2af8ab29ddf",
+   "testharness"
+  ],
+  "content-security-policy/script-src/eval-allowed-in-report-only-mode.html.sub.headers": [
+   "b9b5d81acc3bc58dc6fdff2f436504810f6f4f6e",
+   "support"
+  ],
   "content-security-policy/script-src/externalScript.js": [
    "2920b03c9bc98d16d4c7ebefaf8bcef268c3796c",
    "support"
@@ -297081,6 +297095,14 @@
    "40c4865185a919135f1fa8b54167192a5773eaef",
    "testharness"
   ],
+  "content-security-policy/securitypolicyviolation/constructor-required-fields.html": [
+   "1260c491fc2501b080850b5e523a847d37200127",
+   "testharness"
+  ],
+  "content-security-policy/securitypolicyviolation/idlharness.window-expected.txt": [
+   "6b879c51dbfba7d4dc8e90752b339cca7d8686c3",
+   "support"
+  ],
   "content-security-policy/securitypolicyviolation/idlharness.window.js": [
    "fc5e65d6cfdc59fb8bd3c10e3ed358d5b7f8074d",
    "testharness"
@@ -300377,6 +300399,10 @@
    "4b1baff8b956d706b2f90e67277ccf3a7c4d3458",
    "testharness"
   ],
+  "css/CSS2/abspos/adjacent-to-relpos-inline-in-inline-that-had-block.html": [
+   "1859ff83001a23060284e2fa496ebc8f36738260",
+   "testharness"
+  ],
   "css/CSS2/abspos/adjacent-to-relpos-inline-that-had-block.html": [
    "293b88f52088a255bd854eac5577c214847dcd7c",
    "testharness"
@@ -338621,6 +338647,10 @@
    "9ccb822f107f429651c7949995412c9f2a80feea",
    "reftest"
   ],
+  "css/css-position/inheritance.html": [
+   "d75b28c1e0aa2698fe0c73965f4e7d1f06b16547",
+   "testharness"
+  ],
   "css/css-position/position-relative-table-left-ref.html": [
    "7c1193b80007d8e7f89b35400a6d2ea2266cb3ac",
    "support"
@@ -354430,7 +354460,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/pointer-events.html": [
-   "3e069e760456b024b3bbf7a6f4df8280597bad0d",
+   "091e985406b98b4a37f006310c56701282656e95",
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/position.html": [
@@ -357770,7 +357800,7 @@
    "reftest"
   ],
   "css/css-values/vh-support-margin.html": [
-   "06b2375a82d2ae0ce28d48a1df2e77d673f6f39f",
+   "57fce2064b0fde347eb806ca0e037aadc4232d27",
    "reftest"
   ],
   "css/css-values/vh-support-transform-origin.html": [
@@ -365877,6 +365907,10 @@
    "d40f70e8c0010cf3f860b3ad760dea5d7b64efdd",
    "testharness"
   ],
+  "css/motion/parsing/offset-rotate-computed.html": [
+   "a29d25ccdf2551503399b0a36089d463e481c0ed",
+   "testharness"
+  ],
   "css/motion/parsing/offset-rotate-parsing-invalid.html": [
    "4e5a9229a0c85b3a2e5bfe59719bad88245d65c5",
    "testharness"
@@ -375829,10 +375863,6 @@
    "f4bcd863ef54f9aaa907e5da4bf3c5857f5adb8f",
    "support"
   ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt": [
-   "2fd070340d6ad1679aab4e07ac9b8644571d7d18",
-   "support"
-  ],
   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp.html": [
    "cd8d41b5aa6e379c70503d4960d0928d0664b37c",
    "testharness"
@@ -375841,38 +375871,6 @@
    "547bbcb4505b462e578d225bea5243180ffbb374",
    "support"
   ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_1-1000-expected.txt": [
-   "6c759d9867f0518ade256b1321543956404b55dd",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_1001-2000-expected.txt": [
-   "60e7a6148d9e8608c19e05e49ba385210c2db047",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_2001-3000-expected.txt": [
-   "7b24f9e90e71857f6e183f30e0a5567572d6cb69",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_3001-4000-expected.txt": [
-   "6d596fe1676cd3bbf739e8d3121a5892d7d4f8d4",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_4001-5000-expected.txt": [
-   "0b0ed2eed8abb875d54a1a55ff6deb81069e95df",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_5001-6000-expected.txt": [
-   "8afe54236df2908432271539477c564212b88130",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_6001-7000-expected.txt": [
-   "ae45d73e26163f817966c4ca901941b62d9d1684",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp_7001-last-expected.txt": [
-   "250a51076c0a242ecb47d6c7f978d3d8a4de3898",
-   "support"
-  ],
   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-errors-han.html": [
    "d35ef5fe1d3c4f11874b48a72854033e5d0b8347",
    "testharness"
@@ -375901,10 +375899,6 @@
    "c95b13d1d65e1745567b74c48a03ad95e50779fd",
    "testharness"
   ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-expected.txt": [
-   "3b9ed4d957b0fa1fc18335d1c47de9a8761a87fe",
-   "support"
-  ],
   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form.html": [
    "3a1b3b91ea51026c1da5b4ce3f8d6ef0e71103a9",
    "testharness"
@@ -375913,38 +375907,6 @@
    "51324ea2d35c16b742244c242aaa8e13b93bbef4",
    "support"
   ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_1-1000-expected.txt": [
-   "072a3b6af9afea3ce5830317eb1b192796578504",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_1001-2000-expected.txt": [
-   "65245baee33f7f2d80ff88d65d3a44b1985b90b2",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_2001-3000-expected.txt": [
-   "23d4eb24f309f16e4d18887b7cd2217f434462c2",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_3001-4000-expected.txt": [
-   "92ec06660af2bd6a785507f3da26351e3225da28",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_4001-5000-expected.txt": [
-   "afcc6008934a385848db42a8cf2eb6bbd3c4e9e2",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_5001-6000-expected.txt": [
-   "26f2887f179b905f910333fd5622c825fdb99349",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_6001-7000-expected.txt": [
-   "168068042093210874c3a84647854778b345af1e",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form_7001-last-expected.txt": [
-   "5251ac5994e75afb64e0bea98f51b9df8c93b516",
-   "support"
-  ],
   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-ascii-expected.txt": [
    "0e437240fd55a9d38eb93472b4be85809142adff",
    "support"
@@ -375973,10 +375935,6 @@
    "51324ea2d35c16b742244c242aaa8e13b93bbef4",
    "support"
   ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href-expected.txt": [
-   "3b9ed4d957b0fa1fc18335d1c47de9a8761a87fe",
-   "support"
-  ],
   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href.html": [
    "08b8c2647cd0f13db94f6f0513393ae980a2fe30",
    "testharness"
@@ -375985,38 +375943,6 @@
    "51324ea2d35c16b742244c242aaa8e13b93bbef4",
    "support"
   ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_1-1000-expected.txt": [
-   "ce42d54939283629829f0c47197652e7ea0dc394",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_1001-2000-expected.txt": [
-   "61505b0118a2cedacb47b78eba7b63846396f79b",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_2001-3000-expected.txt": [
-   "9b228baab41c3524a480be81fd5e509ba4c039c0",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_3001-4000-expected.txt": [
-   "1b2a9a0d68a06eaf7e40febb1f54976831b627c0",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_4001-5000-expected.txt": [
-   "d85cfec14ea52ca16ef1eb9622029e40f55f08e1",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_5001-6000-expected.txt": [
-   "99fed3cd7f9940a6cf5cf384578e323e2640f78f",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_6001-7000-expected.txt": [
-   "6a81ea105b82789198c34567d1d0e38caf0332f1",
-   "support"
-  ],
-  "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-href_7001-last-expected.txt": [
-   "a0812cbd79747bb26b0f5903e4a9ac9b32fce09a",
-   "support"
-  ],
   "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encoder.js": [
    "9f07d0b6f409520d70975e022cda86bc260b992e",
    "support"
@@ -378425,6 +378351,14 @@
    "c2493a089031aa89fc6c689e0bdc1ea717da81c3",
    "support"
   ],
+  "feature-policy/feature-policy-header-policy-declined.https.sub.html": [
+   "b148df17caf1aba4bd76fef508e0e338add34df8",
+   "testharness"
+  ],
+  "feature-policy/feature-policy-header-policy-declined.https.sub.html.sub.headers": [
+   "4ac2a4a588c27f69d110a756e901111625ea9133",
+   "support"
+  ],
   "feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html": [
    "c025705a36b10e3830cf2f076b34e14f77737129",
    "testharness"
@@ -401086,7 +401020,7 @@
    "support"
   ],
   "interfaces/CSP.idl": [
-   "46fae543cb2d96dfdc0ee03d0470c53464564a80",
+   "4b53e3a3a40386d93abded001ac99bc04ea46b3e",
    "support"
   ],
   "interfaces/CSS-Parser-API.idl": [
@@ -401498,7 +401432,7 @@
    "support"
   ],
   "interfaces/screen-capture.idl": [
-   "b79d09d19dced9e3b77e791e2d543fc9f44742ee",
+   "f00660bd904e8ad3fcdf0fa9cf66c49e5d918767",
    "support"
   ],
   "interfaces/screen-orientation.idl": [
@@ -413482,7 +413416,7 @@
    "support"
   ],
   "payment-request/payment-request-abort-method.https.html": [
-   "64886a6b7cc91b3aa05a34e6f8650808d3f1ab0b",
+   "8e561b288d15fa30971dd1f88b930671c83a5887",
    "testharness"
   ],
   "payment-request/payment-request-canmakepayment-method-protection.https-expected.txt": [
@@ -413498,7 +413432,7 @@
    "support"
   ],
   "payment-request/payment-request-canmakepayment-method.https.html": [
-   "03a9c19fb8fd774ccea9cf2a9740be0971fa4910",
+   "f38caa00b9313dc80f5ab5180dc10e850eb57147",
    "testharness"
   ],
   "payment-request/payment-request-constructor-crash.https.html": [
@@ -414230,7 +414164,7 @@
    "testharness"
   ],
   "pointerlock/mouse_buttons_back_forward-manual.html": [
-   "06e0a5daee1c53da990b1290fc4e0951ca55c3c2",
+   "4515ebabcbd3dfef30b89512609078b7d89a2a92",
    "manual"
   ],
   "pointerlock/movementX_Y_basic-manual.html": [
@@ -423626,15 +423560,15 @@
    "support"
   ],
   "screen-capture/getdisplaymedia.https-expected.txt": [
-   "d73ca7830695ce5f3e8e6e668dab757f6c959e38",
+   "dbf3a26c6592968383774687e5239a1db60cb72d",
    "support"
   ],
   "screen-capture/getdisplaymedia.https.html": [
-   "06ab72d20bf2668dfc91096afb28c88bd100d2a0",
+   "36e0f22933a08921b7aea57f29191b477f767e1d",
    "testharness"
   ],
   "screen-capture/idlharness.window.js": [
-   "076e89f275c9e5c583780e83bd96f1f79ed80fdd",
+   "527565ea9632e69a6de62efd2be3729c974b93e4",
    "testharness"
   ],
   "screen-orientation/META.yml": [
@@ -425250,7 +425184,7 @@
    "testharness"
   ],
   "service-workers/service-worker/clients-matchall-include-uncontrolled.https.html": [
-   "210cbb042125b4032997c8ef35f7b2c091a1e4b9",
+   "d3b5a966d330412562fc6cbc614e4995f3d1260b",
    "testharness"
   ],
   "service-workers/service-worker/clients-matchall-on-evaluation.https.html": [
@@ -425830,7 +425764,7 @@
    "testharness"
   ],
   "service-workers/service-worker/postmessage-to-client.https.html": [
-   "15d2e889337078869e3c3e97d312649e6a8bd8b2",
+   "f834a4bffe20c69cbd57ae4c6bf922083d3554d6",
    "testharness"
   ],
   "service-workers/service-worker/postmessage.https.html": [
@@ -431362,7 +431296,7 @@
    "manual"
   ],
   "uievents/mouse/mouse_buttons_back_forward-manual.html": [
-   "7a676db4c4464eb74e95a19a3b98ac94f44b1c77",
+   "aa4e944b95cb2e0932ffa042c3f47ec1ccaf4653",
    "manual"
   ],
   "uievents/mouse/mouseevent_move_button-manual.html": [
@@ -434993,6 +434927,14 @@
    "900769b9d4334bb38d6413beea6a28235d8c927c",
    "support"
   ],
+  "webrtc-identity/RTCPeerConnection-constructor-expected.txt": [
+   "dc884ad040f8b716c034908d9cde69678f2c3c93",
+   "support"
+  ],
+  "webrtc-identity/RTCPeerConnection-constructor.html": [
+   "8498e6b35c9ff820c86773dd874930c5b012229e",
+   "testharness"
+  ],
   "webrtc-identity/RTCPeerConnection-getIdentityAssertion.sub-expected.txt": [
    "f3902089fe1c9958c8dd3ee598efb73a5b7ed4c4",
    "support"
@@ -435162,7 +435104,7 @@
    "testharness"
   ],
   "webrtc/RTCIceTransport.html": [
-   "17ae6dca016fcd08cbb34b4819129f6338de28c6",
+   "6a00e1c59e7183fd84712862b6c6b044d4be340d",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-add-track-no-deadlock.https.html": [
@@ -435182,7 +435124,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-addTrack.https.html": [
-   "2ec9a1f7d60d84fce6e5a8b240b0fadc3b1d910f",
+   "1550fc4f7943b79cc331d5c93ca6d5643793dead",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-addTransceiver.https-expected.txt": [
@@ -435206,15 +435148,15 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-connectionState.html": [
-   "d8e94112718155720f31622966b27760b653c399",
+   "7ef7e4a3bf2f30a221d1de2c09e5388f206afdc5",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-constructor-expected.txt": [
-   "05ef39e3e239ff04f77386a2e0fd79a0aae2aae2",
+   "3ad5c15da5ecd7cd91e0848480e4ead769752d04",
    "support"
   ],
   "webrtc/RTCPeerConnection-constructor.html": [
-   "e00d98709087eea37599e5c7d520b04cbb6e1e8f",
+   "abea2eb6761b779ba527b75660d0c2258c805ba1",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-createAnswer-expected.txt": [
@@ -435222,7 +435164,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-createAnswer.html": [
-   "abff1eb7329cceda286216b90531f997040ed474",
+   "030ced8c98f4c926c5af870bf16097f4f56a7d1e",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-createDataChannel-expected.txt": [
@@ -435234,19 +435176,15 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-createOffer-expected.txt": [
-   "e051cf4df94458bd814c5f91c7f390d2c5bfca48",
+   "3f7a47deb53e23de703ad4f4f3d5289b86d995d3",
    "support"
   ],
   "webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt": [
    "569d63c0e8236ce5c0a653a573b821bc933f1ce4",
    "support"
   ],
-  "webrtc/RTCPeerConnection-createOffer-offerToReceive.html": [
-   "dd1827a1c8d60abe67cad72a9ef3280003c792c0",
-   "testharness"
-  ],
   "webrtc/RTCPeerConnection-createOffer.html": [
-   "2fa3a05f60a1a53668e9978009958a19c57e4fc1",
+   "c56d45280ef6317bc0ef77b8ad71e9273c809863",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-generateCertificate-expected.txt": [
@@ -435286,7 +435224,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-helper.js": [
-   "0153247146b164c1f32246596a17ec58aeea28e0",
+   "8529fb4ef3cdf509a519608fbb4a5919c44f3e37",
    "support"
   ],
   "webrtc/RTCPeerConnection-iceConnectionState-expected.txt": [
@@ -435358,7 +435296,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-answer.html": [
-   "4a50456f21613a39946c54774377aef176c04486",
+   "b783d854738a926cfdf0ecc7ecd7897b5b86b200",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt": [
@@ -435366,7 +435304,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-offer.html": [
-   "9502f283f55081300f80a0f06650f28eb46ce506",
+   "49eb522881a9abb914566b42a5e7aae7063fdb8b",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-pranswer-expected.txt": [
@@ -435374,7 +435312,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-pranswer.html": [
-   "a145f95afa3521d6ec8446e12da41d19d338b9a7",
+   "1fbb30923f3d1e9abef6940a931a6d426ed01915",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt": [
@@ -435386,11 +435324,11 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription.html": [
-   "0ab2db1d56d4163aa088776c51d652cedece15a7",
+   "1030f494f2469c2fceaa618e2992397b5a4bbb8f",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-answer.html": [
-   "ff554478af833a2a9b56a7a863d37a0255bca7b6",
+   "cc5bd1e0807b71e0e96c368fe18ab6bd0c6d142d",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-expected.txt": [
@@ -435406,7 +435344,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-pranswer.html": [
-   "9197a359671bd1d8f28098b2a8db1b93ee181cee",
+   "c27ee20855bea03e4aa8a2ca80b90653f5e9d5b5",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt": [
@@ -435418,11 +435356,11 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt": [
-   "7c92a52eb5e10faa031b9ce672183b7177c6b988",
+   "cd124369de68d3cade98c8bdf4714f019d23858c",
    "support"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-rollback.html": [
-   "01969dce310c78561d97f22a0cc6da9390bf0c27",
+   "ec90e561cbcf76559356dcd46c77547fb8262dcb",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt": [
@@ -435430,11 +435368,11 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html": [
-   "7179f1ef78ccd5b5b956df6241d0b2b47c7425a4",
+   "c9d2d1282c7b2190f80506becebc48e32b3dc009",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription.html": [
-   "ad9cdff5fb0a4cf90e977fd0cdde7d3d685e9f4d",
+   "182abea403ed7bee6568aac402cbb6a8caac6159",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-track-stats.https-expected.txt": [
@@ -435546,7 +435484,7 @@
    "support"
   ],
   "webrtc/RTCRtpReceiver-getParameters.html": [
-   "97e5d10d1e53b18d3c9deca969d2046881418569",
+   "453844d5f0e63cf6690ca8eb5fe3335073d34921",
    "testharness"
   ],
   "webrtc/RTCRtpReceiver-getStats.https-expected.txt": [
@@ -435574,7 +435512,7 @@
    "support"
   ],
   "webrtc/RTCRtpSender-getStats.https.html": [
-   "18ab82555447194dbacfb07aa5715953c0f17dde",
+   "1af8285f4c325f38b322d26aa582f3bed7f70585",
    "testharness"
   ],
   "webrtc/RTCRtpSender-replaceTrack.https-expected.txt": [
@@ -435610,11 +435548,11 @@
    "testharness"
   ],
   "webrtc/RTCRtpTransceiver.https-expected.txt": [
-   "09b20d29848db8b5d2ed8d0de5e5431f194e07b8",
+   "4807af0735ee68ed4971525fe6d37079b3ae8a50",
    "support"
   ],
   "webrtc/RTCRtpTransceiver.https.html": [
-   "860f5de2d2aca4b3cbcc6e201c8dc774f154346f",
+   "2de9fb9f6ee4dbc555bf30ba46f21df30385dd5c",
    "testharness"
   ],
   "webrtc/RTCSctpTransport-constructor-expected.txt": [
@@ -435622,7 +435560,7 @@
    "support"
   ],
   "webrtc/RTCSctpTransport-constructor.html": [
-   "c415c3fe180b6718fa11c20b1bc9a1ecd88a4547",
+   "7d3df051d876e490c926639499c13ba0dcf8d30a",
    "testharness"
   ],
   "webrtc/RTCSctpTransport-maxMessageSize-expected.txt": [
@@ -435630,7 +435568,7 @@
    "support"
   ],
   "webrtc/RTCSctpTransport-maxMessageSize.html": [
-   "28d17eeaccf3f26aaca93c9e1f1eab9edc643aaa",
+   "9163a66af151e3e2f13ab7c4a40e56e3f162c97f",
    "testharness"
   ],
   "webrtc/RTCStats-helper.js": [
@@ -435670,7 +435608,7 @@
    "support"
   ],
   "webrtc/getstats.html": [
-   "937f54b74ec8266f890d60a4c67e75c3d6714826",
+   "053d9092ceb6f1ca6900d164ee10965c001f2a0c",
    "testharness"
   ],
   "webrtc/historical-expected.txt": [
@@ -435686,15 +435624,43 @@
    "support"
   ],
   "webrtc/idlharness.https.window.js": [
-   "3c57a022cabfbedcf4a015024ac88ecd3080faf6",
+   "6e9b7e9afa2a1a1ef0448307b2e39e7fbc7a358f",
    "testharness"
   ],
   "webrtc/interfaces.https-expected.txt": [
    "090c43bb61337bc8bc5157e35bacee922a5d05c8",
    "support"
   ],
+  "webrtc/legacy/README.txt": [
+   "8adbf6aa173949718a90bd54cc95361e1a8801ba",
+   "support"
+  ],
+  "webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt": [
+   "09d40c0ab084ee38a53e27b6b846c27ba4e91268",
+   "support"
+  ],
+  "webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive.html": [
+   "f710498e75f1be587c66d1d0dfe215cb136cc747",
+   "testharness"
+  ],
+  "webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt": [
+   "1c91b50028b83fa4e7f0e5a47ef83022082262a1",
+   "support"
+  ],
+  "webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https.html": [
+   "8c2975214c4dce28a41336de128e0c54c0800828",
+   "testharness"
+  ],
+  "webrtc/legacy/onaddstream.https-expected.txt": [
+   "7415aac47818b2bcbc775c770cc7cd53380bc83c",
+   "support"
+  ],
+  "webrtc/legacy/onaddstream.https.html": [
+   "5aa16b36a24e1bce3c286cc5aec249a891bf76f0",
+   "testharness"
+  ],
   "webrtc/no-media-call.html": [
-   "ff7ab53c111414f66566a4ebcffbb2e66a17a5f0",
+   "0f2e2a33e62b47292085c14ad36a253d800c5982",
    "testharness"
   ],
   "webrtc/promises-call.html": [
@@ -435706,7 +435672,7 @@
    "support"
   ],
   "webrtc/protocol/jsep-initial-offer.https.html": [
-   "d65ee5e9621af5760ee259b5e221ff590ea86a06",
+   "88bdfcfc2867335ca53c30f5637b38b3d12c64ca",
    "testharness"
   ],
   "webrtc/protocol/video-codecs.https.html": [
@@ -435742,19 +435708,19 @@
    "support"
   ],
   "websockets/Close-1000-reason.any.js": [
-   "1517db2e06c525f7e93e63bf062c608aec1deadb",
+   "e8b49b5875bafe8e4e29b275c8d4709fea1e352f",
    "testharness"
   ],
   "websockets/Close-1000.any.js": [
-   "e052f2385073cc0718532cc24c8a71dd7c75bd5e",
+   "bcfceffbd54d3e71f7e45b03f703f01f8ad370a4",
    "testharness"
   ],
   "websockets/Close-Reason-124Bytes.any.js": [
-   "97bc1bb224b8de671a2b0a1f8f7db1eb99c01585",
+   "357748a7c4df65f34c00fafa4a96a3a427fa2508",
    "testharness"
   ],
   "websockets/Close-reason-unpaired-surrogates.any.js": [
-   "119a32d0c931d1213c5e6b1dc8368c5647843f5e",
+   "0b2e6813cbcf106ed67020e142a9fbaeb2793842",
    "testharness"
   ],
   "websockets/Close-undefined.any.js": [
@@ -435762,7 +435728,7 @@
    "testharness"
   ],
   "websockets/Create-Secure-extensions-empty.any.js": [
-   "82b000dde728a26f325b9eaa9fdf3e3d24cc69eb",
+   "647b7f8ef414baf95c21b1a33921bc0de716a18d",
    "testharness"
   ],
   "websockets/Create-Secure-url-with-space-expected.txt": [
@@ -435770,43 +435736,43 @@
    "support"
   ],
   "websockets/Create-Secure-url-with-space.any-expected.txt": [
-   "ab892279c9bb6a030caa3c7bd2b42db930a8b680",
+   "a04297058f4b030d13dfa4e027a1e38de9bbc4dd",
    "support"
   ],
   "websockets/Create-Secure-url-with-space.any.js": [
-   "94265c6182c11e92a09e6b7e35a4a919215d8544",
+   "f6ca19e04726a428fd8c635dc65919ca953c0479",
    "testharness"
   ],
   "websockets/Create-Secure-url-with-space.any.worker-expected.txt": [
-   "ab892279c9bb6a030caa3c7bd2b42db930a8b680",
+   "a04297058f4b030d13dfa4e027a1e38de9bbc4dd",
    "support"
   ],
   "websockets/Create-Secure-valid-url-array-protocols.any.js": [
-   "fcaf8a36454d1cbd42826e5bfb8073acb6512f0d",
+   "b0ebe6a0dbe454e12a1040db90f4310c9bdeba3d",
    "testharness"
   ],
   "websockets/Create-Secure-valid-url-binaryType-blob.any.js": [
-   "fed88f5c2f2f51dca1405ae0e2929707c2ba4a70",
+   "a639ee88ab290241696b498bb46501e515ee1235",
    "testharness"
   ],
   "websockets/Create-Secure-valid-url-protocol-setCorrectly.any.js": [
-   "7ecd2959f76dc0572bb9df51a900cd69aee722fa",
+   "768e1bc46f6b81825ef997b4b8702b666207686c",
    "testharness"
   ],
   "websockets/Create-Secure-valid-url-protocol-string.any.js": [
-   "59c77c696acc1baa9116de5a8fc4e0ce6cfa7921",
+   "e1e661cf56771408bf8f8c038945431f5d315986",
    "testharness"
   ],
   "websockets/Create-Secure-valid-url.any.js": [
-   "6f1229e7616634bf7baf4c153e3261f0a29d5a3a",
+   "ab02a19e1374bf6561687f27594360bf50f71856",
    "testharness"
   ],
   "websockets/Create-Secure-verify-url-set-non-default-port.any.js": [
-   "755dbe2f3ffded950166a80f941d3e574adf814d",
+   "8ed985b58e2bd9fb33bfd39683a9f0504084fd62",
    "testharness"
   ],
   "websockets/Create-asciiSep-protocol-string.any.js": [
-   "cb3c3e4c96f1acad756677ea58c86a18f64cdda4",
+   "37657f18dfca8fee3157c48e88798bdbc41f68c3",
    "testharness"
   ],
   "websockets/Create-blocked-port-expected.txt": [
@@ -435830,11 +435796,11 @@
    "testharness"
   ],
   "websockets/Create-non-absolute-url.any.js": [
-   "369557ec3f526cbd1c4a5a8e01633f73a80d094a",
+   "9ac9707209f9b9bc6a22ea1637783c0aed8f2222",
    "testharness"
   ],
   "websockets/Create-nonAscii-protocol-string.any.js": [
-   "39be9f458051ffa2ea0f1cb17296c3e7e6495af2",
+   "631699413a357caf1ed950f460d484cfeda63ebd",
    "testharness"
   ],
   "websockets/Create-on-worker-shutdown.any-expected.txt": [
@@ -435850,7 +435816,7 @@
    "support"
   ],
   "websockets/Create-protocol-with-space.any.js": [
-   "b3c14d8e1717a120b71e72323ff5bde601d39419",
+   "18f6815a489a8af0b671d9111c11210f306d0567",
    "testharness"
   ],
   "websockets/Create-protocols-repeated-case-insensitive-expected.txt": [
@@ -435858,43 +435824,43 @@
    "support"
   ],
   "websockets/Create-protocols-repeated-case-insensitive.any-expected.txt": [
-   "2687b4033ee79abfb8d645e8cb0650d0434856b4",
+   "fe44654d1705354556ae79dcc1f9e13049837d18",
    "support"
   ],
   "websockets/Create-protocols-repeated-case-insensitive.any.js": [
-   "16f99759d18afcd4b097ea9282055b5c70f7034c",
+   "1d59015e05835074a9bf4642fe8a40850675bf64",
    "testharness"
   ],
   "websockets/Create-protocols-repeated-case-insensitive.any.worker-expected.txt": [
-   "2687b4033ee79abfb8d645e8cb0650d0434856b4",
+   "fe44654d1705354556ae79dcc1f9e13049837d18",
    "support"
   ],
   "websockets/Create-protocols-repeated.any.js": [
-   "624d45306a77ce6fc37b94d2fdd73410c96398d3",
+   "bdaf5606a8b16a4b9a65d0326ba1e0ab3eb330e2",
    "testharness"
   ],
   "websockets/Create-valid-url-array-protocols.any.js": [
-   "dde0303aa0fe3cb4af194e4f13824888f616ffe0",
+   "f8161334e5e0f3cd739daa4bf6ca3ed871d95b8a",
    "testharness"
   ],
   "websockets/Create-valid-url-protocol-empty.any.js": [
-   "8682e4a40181fa96f87a2f0fee6823aabadcbd0e",
+   "0bf2ff53bbc8e1fa84fa4b2728c7a3f24cd14ef8",
    "testharness"
   ],
   "websockets/Create-valid-url-protocol.any.js": [
-   "85e870fda2fc725fe2cdae24b298e27a90ed9611",
+   "2dcae27f1a66e52ee2674b7a08216bc5abdae87d",
    "testharness"
   ],
   "websockets/Create-valid-url.any.js": [
-   "9a43dcc5fdd15c04901738f764623c74dfde4e53",
+   "b84e2118a1d6d98b8b717f38360181b1100fb606",
    "testharness"
   ],
   "websockets/Create-verify-url-set-non-default-port.any.js": [
-   "5548fd134ca1971068dcde4a909d8345428b32c1",
+   "56116719682f11349e0bee4a12219c8a2f90905a",
    "testharness"
   ],
   "websockets/Create-wrong-scheme.any.js": [
-   "506f81c17035c15d563a0e6a7b51d4746f6653eb",
+   "78ff7394622edfce754d6649d478e547a77e57c5",
    "testharness"
   ],
   "websockets/META.yml": [
@@ -435910,63 +435876,63 @@
    "support"
   ],
   "websockets/Secure-Close-1000-reason.any.js": [
-   "4d3f67cf45a8caf0e63146f13899d59890704c0d",
+   "8f289875d9ff3e428ff34cd45a32888041dd80c9",
    "testharness"
   ],
   "websockets/Secure-Close-1000-verify-code.any.js": [
-   "87ba4079ccfef3b2bc6049e35ee6ae700f67aa3a",
+   "a812e0d62c8792945c6c62bafb5f591759a2f624",
    "testharness"
   ],
   "websockets/Secure-Close-1000.any.js": [
-   "67f4e054a3de4c39a31cf2ac9db1255d2dc88d01",
+   "2c7e5e9c63d53125ffe7a55929635ce334d988a8",
    "testharness"
   ],
   "websockets/Secure-Close-1005-verify-code.any.js": [
-   "a7c72eaf5157857f1b4075d8cbd5d02a62e4f8df",
+   "aa8e37ea2245cff6bee993a080aa437f1abe48e3",
    "testharness"
   ],
   "websockets/Secure-Close-1005.any.js": [
-   "ddb2c185854f41aabf3841b341fcf4fc10a74a4a",
+   "f5f2cbf6e6616e7c596789ba6bb1339fe358b0bc",
    "testharness"
   ],
   "websockets/Secure-Close-2999-reason.any.js": [
-   "0fa198eb39e04ba9836f897d30d81499b3743911",
+   "ee1c756694ba258d9c8f05f469c0e94f0e4aaf53",
    "testharness"
   ],
   "websockets/Secure-Close-3000-reason.any.js": [
-   "6640ddc17d4738376aab0e15a197719fbcee9068",
+   "d2266ddc0222efe2c2a0c589c65bace0264861f9",
    "testharness"
   ],
   "websockets/Secure-Close-3000-verify-code.any.js": [
-   "5b122d4c33f4665cd22e0952e4297951504d7a6d",
+   "bbd5bbb22a897ed303aa71dd174b12dfbe39301b",
    "testharness"
   ],
   "websockets/Secure-Close-4999-reason.any.js": [
-   "d57899eb3eed65a352eb8ac37f02ff8f5cc1ea52",
+   "b8429cf97b5b1654a0d93f53c4421435e132e514",
    "testharness"
   ],
   "websockets/Secure-Close-Reason-124Bytes.any.js": [
-   "826cb6eccf761af93bcf56bf9da19c9df0da758d",
+   "0786c295e760c3af2cb4994267974f62c6f004c1",
    "testharness"
   ],
   "websockets/Secure-Close-Reason-Unpaired-surrogates.any.js": [
-   "fdc62c465d34a1ca56475a4efac7131d48eacbc9",
+   "a7b9ea4bb6424160ced80e726cac835961a19b3b",
    "testharness"
   ],
   "websockets/Secure-Close-onlyReason.any.js": [
-   "79f79b5d833994ed7a89fa0f0dbeb5c0c8e7dcfd",
+   "029b8d8af8ef8517fad73cfeec84131aef8d1583",
    "testharness"
   ],
   "websockets/Secure-Close-readyState-Closed.any.js": [
-   "3279744848c0e5a983a2eeadd51e854743b59572",
+   "0dd46f14be148357c02e8878b77ddc66360b968c",
    "testharness"
   ],
   "websockets/Secure-Close-readyState-Closing.any.js": [
-   "b18347447a569dec7d81ec949d27787d543ce31c",
+   "690ca83a2b3fbc4a850626167395625b38da919b",
    "testharness"
   ],
   "websockets/Secure-Close-server-initiated-close.any.js": [
-   "8531b3140b1dcfe058a21a13341807b9de2323f1",
+   "800fbc40e1d15ef407ba824a6424de91dc22188c",
    "testharness"
   ],
   "websockets/Secure-Close-undefined.any.js": [
@@ -435974,123 +435940,123 @@
    "testharness"
   ],
   "websockets/Secure-Send-65K-data.any.js": [
-   "daa937af579a7b710fd4d4e74cf5840f0d30b88b",
+   "ca876534339d7b61eef58552bf8e028f3b9caa7b",
    "testharness"
   ],
   "websockets/Secure-Send-binary-65K-arraybuffer.any.js": [
-   "17859e5630d871d53ae165aece610555e8475820",
+   "0627e7554e6e77d2e4ce1bee28cb2d0022b4ae77",
    "testharness"
   ],
   "websockets/Secure-Send-binary-arraybuffer.any.js": [
-   "6e4c08d6901039f56625bd30c0f5d9f67547f8c8",
+   "5778a241fffd2c1c3eab0fff86a4b089c403d137",
    "testharness"
   ],
   "websockets/Secure-Send-binary-arraybufferview-float32.any.js": [
-   "9825d34d5c053070b51db467181b695205d0fe86",
+   "2672319d40e0dbe976386ce82543b63171526b5a",
    "testharness"
   ],
   "websockets/Secure-Send-binary-arraybufferview-float64.any.js": [
-   "4dcac40e37e251f5c1c64773ff7950f67fe6fb23",
+   "993d5fc07e7167939678496cbf4e7ce467161527",
    "testharness"
   ],
   "websockets/Secure-Send-binary-arraybufferview-int32.any.js": [
-   "655af21575bf4343ef318a7017269084338d624e",
+   "58fe02b2505902823fc94b020db0ceea1652a647",
    "testharness"
   ],
   "websockets/Secure-Send-binary-arraybufferview-uint16-offset-length.any.js": [
-   "16f050f3a649f1d2e4f677dc8420ce910b64a9ae",
+   "26231b411f0256c65c25de8b854688713e2d9b7e",
    "testharness"
   ],
   "websockets/Secure-Send-binary-arraybufferview-uint32-offset.any.js": [
-   "8976b3dc614116d2e76daa90b2eacb6115d1a36d",
+   "d2995ae539f156e01ba71b910346be2586ca7aaa",
    "testharness"
   ],
   "websockets/Secure-Send-binary-arraybufferview-uint8-offset-length.any.js": [
-   "9e9d1b5a825e1eafc912248a7d7a07ae7d4ee24e",
+   "90d9d9d51f85a58e48869fb0c2173c14808f2ff9",
    "testharness"
   ],
   "websockets/Secure-Send-binary-arraybufferview-uint8-offset.any.js": [
-   "f563cec9184a999ee1b695f3ab13d5786d2bfe1a",
+   "ce2e6081dd2bbacec91e588f024d8e5eb7967ad6",
    "testharness"
   ],
   "websockets/Secure-Send-binary-blob.any.js": [
-   "8bf0f12ed8267a9e63bae1387a98ac255db99c5f",
+   "f532dfbf83ab91f429eb793d5f95fe8d8856baa9",
    "testharness"
   ],
   "websockets/Secure-Send-data.any.js": [
-   "04c720de792d7cf7a3a14217b7bbea520a8c6a7e",
+   "78de87cc78be78c60237c24a0f1d744f5bdb1249",
    "testharness"
   ],
   "websockets/Secure-Send-null.any.js": [
-   "7c374c25c24eeb51b0d593b2e99988da1785bc17",
+   "bf644c6de880bd17a8781b5f20103a297424c9c7",
    "testharness"
   ],
   "websockets/Secure-Send-paired-surrogates.any.js": [
-   "073f06472982e45322f47a30358b80226db41799",
+   "f53b9d522acc50a6dd86c1269a828b2abe5ec094",
    "testharness"
   ],
   "websockets/Secure-Send-unicode-data.any.js": [
-   "a3518c12f7aabdf9b13b770f46b1fc7e9e36e083",
+   "7156698510b9a54f6c0369bb41306ac1a1717961",
    "testharness"
   ],
   "websockets/Secure-Send-unpaired-surrogates.any.js": [
-   "83f3e7bebc2034981e9cb996c7412152f41b232e",
+   "00d8589ed7fde5d9e2e6012c307ac3144a9acb12",
    "testharness"
   ],
   "websockets/Send-0byte-data.any.js": [
-   "131a19d77dd0b4f071b5aee9944d7552fa38f6ae",
+   "b335a1fc151655b598b878b020e40eee0fd36ab8",
    "testharness"
   ],
   "websockets/Send-65K-data.any.js": [
-   "172e6eed5ac7764a4c300e17fb7669ae1eaa89d0",
+   "ad74327fa8157c4235ced2459e73d5bc216dd5b3",
    "testharness"
   ],
   "websockets/Send-Unpaired-Surrogates.any.js": [
-   "65bb2b16ce843e897be6f691471d1d7afa1bcc99",
+   "820fb3115714a37d42df0bd3008daae1cd494b34",
    "testharness"
   ],
   "websockets/Send-before-open.any.js": [
-   "101a1a2ff60116038b269d902672a5829878a6db",
+   "b2f1ea9c3aa856e5e0ace17ff4ff3a69d6cf57fd",
    "testharness"
   ],
   "websockets/Send-binary-65K-arraybuffer.any.js": [
-   "f446a2579b82e8b64e3dd0bc49ed02d89128a8fa",
+   "394a902c8d70258e482c650baafd3509ee3a73d9",
    "testharness"
   ],
   "websockets/Send-binary-arraybuffer.any.js": [
-   "620514351f3c5987bc22e4b45d562d904627bbd0",
+   "24618293fb8ba537321bd4d7d3b552e00d2d0fa2",
    "testharness"
   ],
   "websockets/Send-binary-arraybufferview-int16-offset.any.js": [
-   "7022668e637bf98b15845f6f7505dec9421df055",
+   "a32b1391dc79693cf5779c00809650ee1782cc5e",
    "testharness"
   ],
   "websockets/Send-binary-arraybufferview-int8.any.js": [
-   "242c8c6ece8f6aeead8e84a81c9ff06a6642e255",
+   "d9e50b511d44164af55c486d1da78a19c4bb7eb1",
    "testharness"
   ],
   "websockets/Send-binary-blob.any.js": [
-   "ee6486ea129e2cd73f7fa81aadda1a8654fad91c",
+   "12d2a83785dde0eb752907a45992f8752d70102b",
    "testharness"
   ],
   "websockets/Send-data.any.js": [
-   "487393b582eddb3fc7627c1d4da379b6065d83a2",
+   "98a866b29493145b34738c2db187d670e7f27318",
    "testharness"
   ],
   "websockets/Send-data.worker.js": [
-   "f607fd7cf386783b0d6c058d309f7ef1d94c64ba",
+   "6d4f693d053266f9982ff2eabcac617d61019857",
    "testharness"
   ],
   "websockets/Send-null.any.js": [
-   "8d8d6b76133ebb97510b6002159eab8d05c3b1f0",
+   "ac1a1b29826652e35c384653aacf79cbee2de7c3",
    "testharness"
   ],
   "websockets/Send-paired-surrogates.any.js": [
-   "f12b0016eb0a91eb32fc76cd934d9f91d0f9035c",
+   "003b04010a8affbf61f32ab3a7974f5b278977a9",
    "testharness"
   ],
   "websockets/Send-unicode-data.any.js": [
-   "ce2b9a0e2405b2b9eed974c91e49b0b2e2c8b6cc",
+   "59292546db11f69b3b25ca7cce37f029f928ea33",
    "testharness"
   ],
   "websockets/basic-auth.any.js": [
@@ -436118,7 +436084,7 @@
    "testharness"
   ],
   "websockets/binaryType-wrong-value.any.js": [
-   "6030608c20176020aaed20c453978e38aa2c4456",
+   "61ac6a19ffd07bf6cb942bc325282ff0b902ace1",
    "testharness"
   ],
   "websockets/bufferedAmount-unchanged-by-sync-xhr.any.js": [
@@ -436726,7 +436692,7 @@
    "testharness"
   ],
   "websockets/opening-handshake/003-sets-origin.worker.js": [
-   "8746cca234ca0f26c39722da9e67ff944ed4501b",
+   "cc54f2b88416e6b2f665a576e32fd6ae301df54f",
    "testharness"
   ],
   "websockets/opening-handshake/003.html": [
@@ -443390,11 +443356,11 @@
    "testharness"
   ],
   "xhr/send-content-type-charset-expected.txt": [
-   "068a31bd05baa9c53eec185283b80b6960a053d3",
+   "6269b5656c3511cde1b84cb17c7e5aa344102612",
    "support"
   ],
   "xhr/send-content-type-charset.htm": [
-   "0a91e1fbd7e061df3123163e049cc8aab90a67e2",
+   "a968bb3f4d4e7f3bf67d517b921a436a7faa1aff",
    "testharness"
   ],
   "xhr/send-content-type-string.htm": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/idlharness.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/idlharness.window-expected.txt
new file mode 100644
index 0000000..6b879c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/idlharness.window-expected.txt
@@ -0,0 +1,44 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS SecurityPolicyViolationEvent interface: existence and properties of interface object
+PASS SecurityPolicyViolationEvent interface object length
+PASS SecurityPolicyViolationEvent interface object name
+PASS SecurityPolicyViolationEvent interface: existence and properties of interface prototype object
+PASS SecurityPolicyViolationEvent interface: existence and properties of interface prototype object's "constructor" property
+PASS SecurityPolicyViolationEvent interface: existence and properties of interface prototype object's @@unscopables property
+FAIL SecurityPolicyViolationEvent interface: attribute documentURL assert_true: The prototype object must have a property "documentURL" expected true got false
+PASS SecurityPolicyViolationEvent interface: attribute documentURI
+PASS SecurityPolicyViolationEvent interface: attribute referrer
+FAIL SecurityPolicyViolationEvent interface: attribute blockedURL assert_true: The prototype object must have a property "blockedURL" expected true got false
+PASS SecurityPolicyViolationEvent interface: attribute blockedURI
+PASS SecurityPolicyViolationEvent interface: attribute effectiveDirective
+PASS SecurityPolicyViolationEvent interface: attribute violatedDirective
+PASS SecurityPolicyViolationEvent interface: attribute originalPolicy
+PASS SecurityPolicyViolationEvent interface: attribute sourceFile
+PASS SecurityPolicyViolationEvent interface: attribute sample
+PASS SecurityPolicyViolationEvent interface: attribute disposition
+PASS SecurityPolicyViolationEvent interface: attribute statusCode
+FAIL SecurityPolicyViolationEvent interface: attribute lineno assert_true: The prototype object must have a property "lineno" expected true got false
+PASS SecurityPolicyViolationEvent interface: attribute lineNumber
+FAIL SecurityPolicyViolationEvent interface: attribute colno assert_true: The prototype object must have a property "colno" expected true got false
+PASS SecurityPolicyViolationEvent interface: attribute columnNumber
+PASS SecurityPolicyViolationEvent must be primary interface of new SecurityPolicyViolationEvent("securitypolicyviolation")
+PASS Stringification of new SecurityPolicyViolationEvent("securitypolicyviolation")
+FAIL SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "documentURL" with the proper type assert_inherits: property "documentURL" not found in prototype chain
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "documentURI" with the proper type
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "referrer" with the proper type
+FAIL SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "blockedURL" with the proper type assert_inherits: property "blockedURL" not found in prototype chain
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "blockedURI" with the proper type
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "effectiveDirective" with the proper type
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "violatedDirective" with the proper type
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "originalPolicy" with the proper type
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "sourceFile" with the proper type
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "sample" with the proper type
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "disposition" with the proper type
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "statusCode" with the proper type
+FAIL SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "lineno" with the proper type assert_inherits: property "lineno" not found in prototype chain
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "lineNumber" with the proper type
+FAIL SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "colno" with the proper type assert_inherits: property "colno" not found in prototype chain
+PASS SecurityPolicyViolationEvent interface: new SecurityPolicyViolationEvent("securitypolicyviolation") must inherit property "columnNumber" with the proper type
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-position/inheritance.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-position/inheritance.html
new file mode 100644
index 0000000..d75b28c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-position/inheritance.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Inheritance of CSS Positioned Layout properties</title>
+<link rel="help" href="https://drafts.csswg.org/css-position/#property-index">
+<meta name="assert" content="Properties inherit or not according to the spec.">
+<meta name="assert" content="Properties have initial values according to the spec.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+</head>
+<body>
+<div id="container">
+<div id="target"></div>
+</div>
+<style>
+#container, #target {
+  position: sticky;
+}
+</style>
+<script>
+assert_not_inherited('position', 'static', 'absolute');
+assert_not_inherited('top', 'auto', '10px');
+assert_not_inherited('right', 'auto', '10px');
+assert_not_inherited('bottom', 'auto', '10px');
+assert_not_inherited('left', 'auto', '10px');
+assert_not_inherited('inset-before', 'auto', '10px');
+assert_not_inherited('inset-after', 'auto', '10px');
+assert_not_inherited('inset-start', 'auto', '10px');
+assert_not_inherited('inset-end', 'auto', '10px');
+assert_not_inherited('z-index', 'auto', '123');
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-values/vh-support-margin.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-values/vh-support-margin.html
index 06b2375a..57fce20 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-values/vh-support-margin.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-values/vh-support-margin.html
@@ -13,7 +13,7 @@
 	<link
 		rel="author"
 		title="François REMY"
-		href="mailto:fremycompany.developer@yahoo.fr"
+		href="mailto:francois.remy.pub@outlook.com"
 	/ >
 
 	<link rel="help" href="http://www.w3.org/TR/css3-values/#viewport-relative-lengths">
@@ -25,7 +25,7 @@
 
 	<style type="text/css">
 
-			html, body { margin: 0px; padding: 0px; }
+			html, body { margin: 0px; padding: 0px; height: 100%; }
 
 			html { background: green; }
 			#target { background: red; width: 100%; height: 100%; margin-left: -100vw; margin-top: -100vh; }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-rotate-computed.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-rotate-computed.html
new file mode 100644
index 0000000..a29d25c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-rotate-computed.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Motion Path Module Level 1: getComputedValue().offsetRotate</title>
+<link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-rotate-property">
+<meta name="assert" content="offset-rotate reverse is auto 180deg.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("offset-rotate", "auto", "auto 0deg");
+test_computed_value("offset-rotate", "reverse", "auto 180deg");
+test_computed_value("offset-rotate", "calc(90deg - 0.5turn - 300grad + 0rad)", "-360deg");
+test_computed_value("offset-rotate", "auto 5turn", "auto 1800deg");
+test_computed_value("offset-rotate", "reverse -50grad", "auto 135deg");
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/CSP.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/CSP.idl
index 46fae54..4b53e3a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/CSP.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/CSP.idl
@@ -10,31 +10,34 @@
 [Constructor(DOMString type, optional SecurityPolicyViolationEventInit eventInitDict),
  Exposed=(Window,Worker)]
 interface SecurityPolicyViolationEvent : Event {
-    readonly    attribute USVString      documentURI;
+    readonly    attribute USVString      documentURL;
+    readonly    attribute USVString      documentURI; // historical alias of documentURL
     readonly    attribute USVString      referrer;
-    readonly    attribute USVString      blockedURI;
-    readonly    attribute DOMString      violatedDirective;
+    readonly    attribute USVString      blockedURL;
+    readonly    attribute USVString      blockedURI; // historical alias of blockedURL
     readonly    attribute DOMString      effectiveDirective;
+    readonly    attribute DOMString      violatedDirective; // historical alias of effectiveDirective
     readonly    attribute DOMString      originalPolicy;
     readonly    attribute USVString      sourceFile;
     readonly    attribute DOMString      sample;
     readonly    attribute SecurityPolicyViolationEventDisposition      disposition;
     readonly    attribute unsigned short statusCode;
-    readonly    attribute unsigned long  lineNumber;
-    readonly    attribute unsigned long  columnNumber;
+    readonly    attribute unsigned long  lineno;
+    readonly    attribute unsigned long  lineNumber; // historical alias of lineno
+    readonly    attribute unsigned long  colno;
+    readonly    attribute unsigned long  columnNumber; // historical alias of colno
 };
 
 dictionary SecurityPolicyViolationEventInit : EventInit {
-    required USVString      documentURI;
+    required USVString      documentURL;
              USVString      referrer = "";
-             USVString      blockedURI = "";
-    required DOMString      violatedDirective;
+             USVString      blockedURL = "";
     required DOMString      effectiveDirective;
     required DOMString      originalPolicy;
              USVString      sourceFile = "";
              DOMString      sample = "";
     required SecurityPolicyViolationEventDisposition disposition;
     required unsigned short statusCode;
-             unsigned long  lineNumber = 0;
-             unsigned long  columnNumber = 0;
+             unsigned long  lineno = 0;
+             unsigned long  colno = 0;
 };
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-abort-method.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-abort-method.https.html
index 64886a6b..8e561b2 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-abort-method.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-abort-method.https.html
@@ -48,7 +48,7 @@
   await promise_rejects(t, "AbortError", acceptPromise);
   // As request is now "closed", trying to show it will fail
   await promise_rejects(t, "InvalidStateError", request.show());
-});
+}, "The same request cannot be shown multiple times.");
 
 promise_test(async t => {
   // request is in "created" state.
@@ -70,7 +70,7 @@
   // The request is now "closed", so...
   await promise_rejects(t, "InvalidStateError", request.abort());
   await promise_rejects(t, "AbortError", acceptPromise);
-});
+}, "Aborting a request before it is shown doesn't prevent it from being shown later.");
 
 promise_test(async t => {
   const request = new PaymentRequest(defaultMethods, defaultDetails);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-canmakepayment-method.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-canmakepayment-method.https.html
index 03a9c19f..f38caa0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-canmakepayment-method.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-canmakepayment-method.https.html
@@ -8,7 +8,9 @@
 <script src="/resources/testdriver.js"></script>
 <script>
 const basicCard = Object.freeze({ supportedMethods: "basic-card" });
-const applePay = Object.freeze({ supportedMethods: "https://apple.com/apple-pay" });
+const applePay = Object.freeze({
+  supportedMethods: "https://apple.com/apple-pay",
+});
 const defaultMethods = Object.freeze([basicCard, applePay]);
 const defaultDetails = Object.freeze({
   total: {
@@ -20,15 +22,23 @@
   },
 });
 
+const unsupportedMethods = [
+  { supportedMethods: "this-is-not-supported" },
+  { supportedMethods: "https://not.supported" },
+];
+
 promise_test(async t => {
   const request = new PaymentRequest(defaultMethods, defaultDetails);
-  assert_true(await request.canMakePayment(), "one of the methods should be supported");
-}, `If payment method identifier and serialized parts are supported, resolve promise with true.`);
+  assert_true(
+    await request.canMakePayment(),
+    "one of the methods should be supported"
+  );
+}, `If payment method identifier are supported, resolve promise with true.`);
 
 promise_test(async t => {
   const request = new PaymentRequest(defaultMethods, defaultDetails);
   const acceptPromise = test_driver.bless("show payment request", () => {
-    request.show() // Sets state to "interactive"
+    request.show(); // Sets state to "interactive"
   });
   const canMakePaymentPromise = request.canMakePayment();
   try {
@@ -50,7 +60,7 @@
 promise_test(async t => {
   const request = new PaymentRequest(defaultMethods, defaultDetails);
   const acceptPromise = test_driver.bless("show payment request", () => {
-    request.show() // Sets state to "interactive"
+    request.show(); // Sets state to "interactive"
   });
   acceptPromise.catch(() => {}); // no-op, just to silence unhandled rejection in devtools.
   await request.abort(); // The state is now "closed"
@@ -91,52 +101,20 @@
 }, `If request.[[state]] is "created", then return a promise that resolves to true for known method.`);
 
 promise_test(async t => {
-  const unsupportedMethods = [
-    "this-is-not-supported",
-    "https://not.supported",
-    "e",
-    "n6jzof05mk2g4lhxr-u-q-w1-c-i-pa-ty-bdvs9-ho-ae7-p-md8-s-wq3-h-qd-e-q-sa",
-    "a-b-q-n-s-pw0",
-    "m-u",
-    "s-l5",
-    "k9-f",
-    "m-l",
-    "u4-n-t",
-    "i488jh6-g18-fck-yb-v7-i",
-    "x-x-t-t-c34-o",
-    "https://wpt",
-    "https://wpt.fyi/",
-    "https://wpt.fyi/payment",
-    "https://wpt.fyi/payment-request",
-    "https://wpt.fyi/payment-request?",
-    "https://wpt.fyi/payment-request?this=is",
-    "https://wpt.fyi/payment-request?this=is&totally",
-    "https://wpt.fyi:443/payment-request?this=is&totally",
-    "https://wpt.fyi:443/payment-request?this=is&totally#fine",
-    "https://:@wpt.fyi:443/payment-request?this=is&totally#👍",
-    " \thttps://wpt\n ",
-    "https://xn--c1yn36f",
-    "https://點看",
-  ];
-  for (const method of unsupportedMethods) {
-    try {
-      const request = new PaymentRequest(
-        [{ supportedMethods: method }],
-        defaultDetails
-      );
-      assert_false(
-        await request.canMakePayment(),
-        `method "${method}" must not be supported`
-      );
-    } catch (err) {
-      assert_equals(
-        err.name,
-        "NotAllowedError",
-        "if it throws, then it must be a NotAllowedError."
-      );
-    }
-  }
-}, `If payment method identifier is unknown, resolve promise with false.`);
+  const noneSupported = new PaymentRequest(
+    unsupportedMethods,
+    defaultDetails
+  ).canMakePayment();
+  assert_false(await noneSupported, `methods must not be supported`);
+}, "All methods are unsupported");
+
+promise_test(async t => {
+  const someSupported = new PaymentRequest(
+    [...unsupportedMethods, ...defaultMethods],
+    defaultDetails
+  ).canMakePayment();
+  assert_true(await someSupported, `At least one method is expected to be supported.`);
+}, `Mix of supported and unsupported methods, at least one method is supported.`);
 </script>
 
 <small>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-createPolicy-defaultTests.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-createPolicy-defaultTests.tentative.html
new file mode 100644
index 0000000..a6dd2d1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedTypePolicyFactory-createPolicy-defaultTests.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js" ></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/helper.sub.js"></script>
+<body>
+<script>
+  //Policy name test
+  test(t => {
+    assert_throws("InvalidStateError", _ => {
+      let policy = window.TrustedTypes.createPolicy('default', { createHTML: s => s } );
+    });
+  }, "default policy has to be exposed");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/Window-TrustedTypes.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/Window-TrustedTypes.tentative.html
index 8e20e49..5bbb435 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/Window-TrustedTypes.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/Window-TrustedTypes.tentative.html
@@ -8,4 +8,10 @@
     let factory = window.TrustedTypes;
     assert_true(factory instanceof TrustedTypePolicyFactory);
   }, "factory = window.TrustedTypes");
+
+  test(t => {
+    assert_throws(new TypeError(), _ => {
+      let factory = new TrustedTypePolicyFactory();
+    });
+  }, "factory construction fails");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-DOMParser-parseFromString.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-DOMParser-parseFromString.tentative.html
index ec19ebc..4446a58 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-DOMParser-parseFromString.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-DOMParser-parseFromString.tentative.html
@@ -30,4 +30,19 @@
       var doc = parser.parseFromString(null, "text/html");
     });
   }, "'document.innerText = null' throws");
+
+  // After default policy creation string assignment implicitly calls createHTML.
+  test(t => {
+    let p = window.TrustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true);
+    let parser = new DOMParser();
+    let doc = parser.parseFromString(INPUTS.HTML, "text/html");
+    assert_equals(doc.body.innerText, RESULTS.HTML);
+  }, "'document.innerText = string' assigned via default policy (successful HTML transformation).");
+
+  // After default policy creation null assignment implicitly calls createHTML.
+  test(t => {
+    var parser = new DOMParser();
+    var doc = parser.parseFromString(null, "text/html");
+    assert_equals(doc.body.innerText, "null");
+  }, "'document.innerText = null' assigned via default policy does not throw");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.tentative.html
index 99e187d..2554ce6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.tentative.html
@@ -53,4 +53,17 @@
       setInterval(null);
     });
   }, "`window.setInterval(null)` throws.");
+
+  // After default policy creation string assignment implicitly calls createScript.
+  test(t => {
+    let policy = window.TrustedTypes.createPolicy("default", { createScript: createScriptJS }, true);
+    setTimeout(INPUTS.SCRIPT);
+    setInterval(INPUTS.SCRIPT);
+  }, "`setTimeout(string)`, `setInterval(string)` via default policy (successful Script transformation).");
+
+  // After default policy creation null assignment implicitly calls createScript.
+  test(t => {
+    setTimeout(null);
+    setInterval(null);
+  }, "`setTimeout(null)`, `setInterval(null)` via default policy (successful Script transformation).");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Document-write.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Document-write.tentative.html
index 5e70397..0d9ff61 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Document-write.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Document-write.tentative.html
@@ -9,6 +9,14 @@
 </head>
 <body>
 <script>
+  // TrustedHTML assignments do not throw.
+  test(t => {
+    let p = createHTML_policy(window, 1);
+    let html = p.createHTML(INPUTS.HTML);
+    document.write(html);
+    assert_equals(document.body.innerText, RESULTS.HTML);
+  }, "document.write with html assigned via policy (successful URL transformation).");
+
   // String assignments throw.
   test(t => {
     assert_throws(new TypeError(), _ => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.tentative.html
index 4eee887..ae1ace69 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.tentative.html
@@ -90,6 +90,58 @@
     assert_equals(d.lastChild, null);
     assert_equals(d.nextSibling, null);
   }, "`insertAdjacentHTML(null)` throws.");
+
+  // After default policy creation string assignment implicitly calls createHTML.
+  test(t => {
+    let p = window.TrustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true);
+
+    var d = document.createElement('div');
+    container.appendChild(d);
+
+    d.insertAdjacentHTML('beforebegin', INPUTS.HTML);
+    assert_equals(d.previousSibling.nodeType, Node.TEXT_NODE);
+    assert_equals(d.previousSibling.data, RESULTS.HTML);
+
+    d.insertAdjacentHTML('afterbegin', INPUTS.HTML);
+    assert_equals(d.firstChild.nodeType, Node.TEXT_NODE);
+    assert_equals(d.firstChild.data, RESULTS.HTML);
+
+    d.insertAdjacentHTML('beforeend', INPUTS.HTML);
+    assert_equals(d.lastChild.nodeType, Node.TEXT_NODE);
+    assert_equals(d.lastChild.data, RESULTS.HTML);
+
+    d.insertAdjacentHTML('afterend', INPUTS.HTML);
+    assert_equals(d.nextSibling.nodeType, Node.TEXT_NODE);
+    assert_equals(d.nextSibling.data, RESULTS.HTML);
+
+    while (container.firstChild)
+      container.firstChild.remove();
+  }, "`insertAdjacentHTML(string)` assigned via default policy (successful HTML transformation).");
+
+   // After default policy creation null assignment implicitly calls createHTML.
+  test(t => {
+    var d = document.createElement('div');
+    container.appendChild(d);
+
+    d.insertAdjacentHTML('beforebegin', null);
+    assert_equals(d.previousSibling.nodeType, Node.TEXT_NODE);
+    assert_equals(d.previousSibling.data, "null");
+
+    d.insertAdjacentHTML('afterbegin', null);
+    assert_equals(d.firstChild.nodeType, Node.TEXT_NODE);
+    assert_equals(d.firstChild.data, "null");
+
+    d.insertAdjacentHTML('beforeend', null);
+    assert_equals(d.lastChild.nodeType, Node.TEXT_NODE);
+    assert_equals(d.lastChild.data, "null");
+
+    d.insertAdjacentHTML('afterend', null);
+    assert_equals(d.nextSibling.nodeType, Node.TEXT_NODE);
+    assert_equals(d.nextSibling.data, "null");
+
+    while (container.firstChild)
+      container.firstChild.remove();
+  }, "`insertAdjacentHTML(null)` assigned via default policy does not throw.");
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-outerHTML.tentative-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-outerHTML.tentative-expected.txt
new file mode 100644
index 0000000..b30ab10
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-outerHTML.tentative-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS outerHTML with html assigned via policy (successful HTML transformation).
+PASS `outerHTML = string` throws.
+PASS `outerHTML = null` throws.
+PASS `outerHTML = string` assigned via default policy (successful HTML transformation).
+FAIL `outerHTML = null` assigned via default policy does not throw assert_equals: expected "null" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-outerHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-outerHTML.tentative.html
index fde0c17..945e3dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-outerHTML.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-outerHTML.tentative.html
@@ -49,6 +49,30 @@
     while (container.firstChild)
       container.firstChild.remove();
   }, "`outerHTML = null` throws.");
+
+  // After default policy creation string assignment implicitly calls createHTML.
+  test(t => {
+    let p = window.TrustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true);
+
+    var d = document.createElement('div');
+    document.querySelector('#container').appendChild(d);
+    d.outerHTML = INPUTS.HTML;
+    assert_equals(container.innerText, RESULTS.HTML);
+
+    while (container.firstChild)
+      container.firstChild.remove();
+  }, "`outerHTML = string` assigned via default policy (successful HTML transformation).");
+
+  // After default policy creation null assignment implicitly calls createHTML.
+  test(t => {
+    var d = document.createElement('div');
+    container.appendChild(d);
+    d.outerHTML = null;
+    assert_equals(container.innerText, "null");
+
+    while (container.firstChild)
+      container.firstChild.remove();
+  }, "`outerHTML = null` assigned via default policy does not throw");
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-setAttribute.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-setAttribute.tentative.html
index 26aaa512..646997d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-setAttribute.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Element-setAttribute.tentative.html
@@ -10,7 +10,7 @@
 <body>
 <script>
   // TrustedURL Assignments
-  let testCases = [
+  const URLTestCases = [
     [ 'a', 'href' ],
     [ 'area', 'href' ],
     [ 'base', 'href' ],
@@ -26,7 +26,7 @@
     [ 'track', 'src' ]
   ];
 
-  testCases.forEach(c => {
+  URLTestCases.forEach(c => {
     test(t => {
       assert_element_accepts_trusted_url_explicit_set(window, c, t, c[0], c[1], RESULTS.URL);
       assert_throws_no_trusted_type_explicit_set(c[0], c[1], 'A string');
@@ -35,12 +35,12 @@
   });
 
   // TrustedScriptURL Assignments
-  let scriptTestCases = [
+  const scriptURLTestCases = [
     [ 'embed', 'src' ],
     [ 'script', 'src' ]
   ];
 
-  scriptTestCases.forEach(c => {
+  scriptURLTestCases.forEach(c => {
     test(t => {
       assert_element_accepts_trusted_script_url_explicit_set(window, c, t, c[0], c[1], RESULTS.SCRIPTURL);
       assert_throws_no_trusted_type_explicit_set(c[0], c[1], 'A string');
@@ -49,7 +49,7 @@
   });
 
   // TrustedHTML Assignments
-  let HTMLTestCases = [
+  const HTMLTestCases = [
     [ 'iframe', 'srcdoc' ]
   ];
 
@@ -61,6 +61,35 @@
     }, c[0] + "." + c[1] + " accepts only TrustedHTML");
   });
 
+  // After default policy creation string and null assignments implicitly call createXYZ
+  let p = window.TrustedTypes.createPolicy("default", { createURL: createURLJS, createScriptURL: createScriptURLJS, createHTML: createHTMLJS }, true);
+  URLTestCases.forEach(c => {
+    test(t => {
+      assert_element_accepts_trusted_type(c[0], c[1], INPUTS.URL, RESULTS.URL);
+
+      // Properties that actually parse the URLs will resort to the base URL
+      // when given a null or empty URL.
+      assert_element_accepts_trusted_type(c[0], c[1], null, "" + window.location);
+    }, c[0] + "." + c[1] + " accepts string and null after default policy was created.");
+  });
+
+  scriptURLTestCases.forEach(c => {
+    test(t => {
+      assert_element_accepts_trusted_type(c[0], c[1], INPUTS.SCRIPTURL, RESULTS.SCRIPTURL);
+
+      // Properties that actually parse the URLs will resort to the base URL
+      // when given a null or empty URL.
+      assert_element_accepts_trusted_type(c[0], c[1], null, "" + window.location);
+    }, c[0] + "." + c[1] + " accepts string and null after default policy was created.");
+  });
+
+  HTMLTestCases.forEach(c => {
+    test(t => {
+      assert_element_accepts_trusted_type(c[0], c[1], INPUTS.HTML, RESULTS.HTML);
+      assert_element_accepts_trusted_type(c[0], c[1], null, "null");
+    }, c[0] + "." + c[1] + " accepts string and null after default policy was created.");
+  });
+
   // Other attributes can be assigned with TrustedTypes or strings or null values
   test(t => {
     assert_element_accepts_trusted_url_explicit_set(window, 'arel', t, 'a', 'rel', RESULTS.URL);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative-expected.txt
new file mode 100644
index 0000000..224d66f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative-expected.txt
@@ -0,0 +1,37 @@
+This is a testharness.js-based test.
+PASS a.href accepts only TrustedURL
+PASS area.href accepts only TrustedURL
+PASS base.href accepts only TrustedURL
+PASS frame.src accepts only TrustedURL
+PASS iframe.src accepts only TrustedURL
+PASS img.src accepts only TrustedURL
+PASS input.src accepts only TrustedURL
+PASS link.href accepts only TrustedURL
+PASS video.src accepts only TrustedURL
+PASS object.data accepts only TrustedURL
+PASS object.codeBase accepts only TrustedURL
+PASS source.src accepts only TrustedURL
+PASS track.src accepts only TrustedURL
+PASS embed.src accepts only TrustedScriptURL
+PASS script.src accepts only TrustedScriptURL
+PASS div.innerHTML accepts only TrustedHTML
+PASS iframe.srcdoc accepts only TrustedHTML
+PASS a.href accepts string and null after default policy was created
+PASS area.href accepts string and null after default policy was created
+PASS base.href accepts string and null after default policy was created
+PASS frame.src accepts string and null after default policy was created
+PASS iframe.src accepts string and null after default policy was created
+PASS img.src accepts string and null after default policy was created
+PASS input.src accepts string and null after default policy was created
+PASS link.href accepts string and null after default policy was created
+PASS video.src accepts string and null after default policy was created
+PASS object.data accepts string and null after default policy was created
+PASS object.codeBase accepts string and null after default policy was created
+PASS source.src accepts string and null after default policy was created
+PASS track.src accepts string and null after default policy was created
+PASS embed.src accepts string and null after default policy was created
+PASS script.src accepts string and null after default policy was created
+FAIL div.innerHTML accepts string and null after default policy was created assert_equals: expected "null" but got ""
+PASS iframe.srcdoc accepts string and null after default policy was created
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative.html
index ceac828d..2a7edab9 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative.html
@@ -11,7 +11,7 @@
 <script>
   var testnb = 0;
   // TrustedURL Assignments
-  let testCases = [
+  const URLTestCases = [
     [ 'a', 'href' ],
     [ 'area', 'href' ],
     [ 'base', 'href' ],
@@ -27,7 +27,7 @@
     [ 'track', 'src' ]
   ];
 
-  testCases.forEach(c => {
+  URLTestCases.forEach(c => {
     test(t => {
       assert_element_accepts_trusted_url(window, ++testnb, t, c[0], c[1], RESULTS.URL);
       assert_throws_no_trusted_type(c[0], c[1], 'A string');
@@ -36,13 +36,13 @@
   });
 
   // TrustedScriptURL Assignments
-  let scriptTestCases = [
+  const scriptURLTestCases = [
     [ 'embed', 'src' ],
     [ 'script', 'src' ]
   ];
 
   testnb = 0;
-  scriptTestCases.forEach(c => {
+  scriptURLTestCases.forEach(c => {
     test(t => {
       assert_element_accepts_trusted_script_url(window, ++testnb, t, c[0], c[1], RESULTS.SCRIPTURL);
       assert_throws_no_trusted_type(c[0], c[1], 'A string');
@@ -51,7 +51,7 @@
   });
 
   // TrustedHTML Assignments
-  let HTMLTestCases = [
+  const HTMLTestCases = [
     [ 'div', 'innerHTML' ],
     [ 'iframe', 'srcdoc' ]
   ];
@@ -64,4 +64,29 @@
       assert_throws_no_trusted_type(c[0], c[1], null);
     }, c[0] + "." + c[1] + " accepts only TrustedHTML");
   });
+
+  // After default policy creation string and null assignments implicitly call createHTML
+  let p = window.TrustedTypes.createPolicy("default", { createURL: createURLJS, createScriptURL: createScriptURLJS, createHTML: createHTMLJS }, true);
+
+  URLTestCases.forEach(c => {
+    test(t => {
+      assert_element_accepts_trusted_type(c[0], c[1], INPUTS.URL, RESULTS.URL);
+      assert_element_accepts_trusted_type(c[0], c[1], null, "" + window.location);
+    }, c[0] + "." + c[1] + " accepts string and null after default policy was created");
+  });
+
+  scriptURLTestCases.forEach(c => {
+    test(t => {
+      assert_element_accepts_trusted_type(c[0], c[1], INPUTS.SCRIPTURL, RESULTS.SCRIPTURL);
+      assert_element_accepts_trusted_type(c[0], c[1], null, "" + window.location);
+    }, c[0] + "." + c[1] + " accepts string and null after default policy was created");
+  });
+
+
+  HTMLTestCases.forEach(c => {
+    test(t => {
+      assert_element_accepts_trusted_type(c[0], c[1], INPUTS.HTML, RESULTS.HTML);
+      assert_element_accepts_trusted_type(c[0], c[1], null, "null");
+    }, c[0] + "." + c[1] + " accepts string and null after default policy was created");
+  });
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-assign.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-assign.tentative.html
index c0817d6..4ad4af9 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-assign.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-assign.tentative.html
@@ -30,4 +30,20 @@
       location.assign(null);
     });
   }, "`location.assign = null` throws");
+
+  // Create default policy. Applies to all subsequent tests.
+  let p = window.TrustedTypes.createPolicy("default",
+      { createURL: createLocationURLJS }, true);
+
+  // After default policy creation string assignment implicitly calls createURL.
+  test(t => {
+    location.assign("abcdefg");
+    assert_true(location.href.endsWith("#abcdefg"));
+  }, "`location.assign = string` via default policy (successful URL transformation).");
+
+  // After default policy creation null assignment implicitly calls createURL.
+  test(t => {
+    location.assign(null);
+    assert_true(location.href.endsWith("#null"));
+  }, "`location.assign = null` via default policy does not throw.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-href.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-href.tentative.html
index 2a29e1e2..fd0a4e5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-href.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-href.tentative.html
@@ -30,4 +30,21 @@
       location.href = null;
     });
   }, "`location.href = null` throws");
+
+
+  // Create default policy. Applies to all subsequent tests.
+  let p = window.TrustedTypes.createPolicy("default",
+      { createURL: createLocationURLJS }, true);
+
+  // After default policy creation string assignment implicitly calls createURL.
+  test(t => {
+    location.href = "xxxx";
+    assert_true(location.href.endsWith("#xxxx"));
+  }, "`location.href = string` via default policy (successful URL transformation).");
+
+  // After default policy creation null assignment implicitly calls createURL.
+  test(t => {
+    location.href = null;
+    assert_true(location.href.endsWith("#null"));
+  }, "`location.href = null` assigned via default policy does not throw.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-replace.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-replace.tentative.html
index 8e534bf..9b8cabe5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-replace.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Location-replace.tentative.html
@@ -30,4 +30,20 @@
       location.replace(null);
     });
   }, "`location.replace = null` throws");
+
+  // Create default policy. Applies to all subsequent tests.
+  let p = window.TrustedTypes.createPolicy("default",
+      { createURL: createLocationURLJS }, true);
+
+  // After default policy creation string assignment implicitly calls createURL.
+  test(t => {
+    location.replace("potato");
+    assert_true(location.href.endsWith("#potato"));
+  }, "`location.replace = string` via default policy (successful URL transformation).");
+
+  // After default policy creation null assignment implicitly calls createURL.
+  test(t => {
+    location.replace(null);
+    assert_true(location.href.endsWith("#null"));
+  }, "`location.replace = null` via default policy (successful URL transformation).");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Range-createContextualFragment.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Range-createContextualFragment.tentative.html
index f44266a3..4919b7f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Range-createContextualFragment.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Range-createContextualFragment.tentative.html
@@ -33,4 +33,21 @@
       var result = range.createContextualFragment(null);
     });
   }, "`range.createContextualFragment(null)` throws.");
+
+  // After default policy creation string assignment implicitly calls createHTML
+  test(t => {
+    let p = window.TrustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true);
+    var range = document.createRange();
+    range.selectNodeContents(document.documentElement);
+    var result = range.createContextualFragment(INPUTS.HTML);
+    assert_equals(result.textContent, RESULTS.HTML);
+  }, "`range.createContextualFragment(string)` assigned via default policy (successful HTML transformation).");
+
+  // After default policy creation null assignment implicitly calls createHTML
+  test(t => {
+    var range = document.createRange();
+    range.selectNodeContents(document.documentElement);
+    var result = range.createContextualFragment(null);
+    assert_equals(result.textContent, "null");
+  }, "`range.createContextualFragment(null)` assigned via default policy does not throw.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Window-open.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Window-open.tentative.html
index 7b917db..fd30b6ed 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Window-open.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-Window-open.tentative.html
@@ -29,6 +29,14 @@
     });
   }
 
+  function testWindowDoesntThrow(t, url, expected, win) {
+    let child_window = win.open(url, "", "");
+    child_window.onload = t.step_func_done(_ => {
+      assert_equals(child_window.location.href, expected);
+      child_window.close();
+    });
+  }
+
   // TrustedURL assignments do not throw.
   test(t => {
     testWindowOpen(t, window, ++testnb);
@@ -55,6 +63,24 @@
   test(t => {
     testWindowThrows(t, null, document, ++testnb);
   }, "`document.open(null)` throws.");
+
+  // After default policy creation string assignment implicitly calls createURL.
+  let p = window.TrustedTypes.createPolicy("default", { createURL: createURLJS }, true);
+  test(t => {
+    testWindowDoesntThrow(t, INPUTS.URL, RESULTS.URL, window);
+  }, "'window.open(string)' assigned via default policy (successful URL transformation).");
+
+  test(t => {
+    testWindowDoesntThrow(t, INPUTS.URL, RESULTS.URL, document);
+  }, "'document.open(string)' assigned via default policy (successful URL transformation).");
+
+  test(t => {
+    testWindowDoesntThrow(t, null, "null", window);
+  }, "'window.open(null)' assigned via default policy does not throw.");
+
+  test(t => {
+    testWindowDoesntThrow(t, null, "null", document);
+  }, "'document.open(null)' assigned via default policy does not throw.");
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.sub.js b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.sub.js
index 75874e5..9ad6059 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.sub.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.sub.js
@@ -1,11 +1,11 @@
-var INPUTS = {
+const INPUTS = {
   HTML: "Hi, I want to be transformed!",
   SCRIPT: "Hi, I want to be transformed!",
   SCRIPTURL: "http://this.is.a.scripturl.test/",
   URL: "http://hello.i.am.an.url/"
 };
 
-var RESULTS = {
+const RESULTS = {
   HTML: "Quack, I want to be a duck!",
   SCRIPT: "Meow, I want to be a cat!",
   SCRIPTURL: "http://this.is.a.successful.test/",
@@ -31,6 +31,14 @@
       .replace("an.url", "successfully.transformed");
 }
 
+// When testing location.href (& friends), we have the problem that assigning
+// to the new location will navigate away from the test. To fix this, we'll
+// have a policy that will just stick the argument into the fragment identifier
+// of the current location.href.
+function createLocationURLJS(value) {
+  return location.href.replace(/#.*/g, "") + "#" + value;
+}
+
 function createHTML_policy(win, c) {
   return win.TrustedTypes.createPolicy('SomeHTMLPolicyName' + c, { createHTML: createHTMLJS });
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc-identity/RTCPeerConnection-constructor-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc-identity/RTCPeerConnection-constructor-expected.txt
new file mode 100644
index 0000000..dc884ad0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc-identity/RTCPeerConnection-constructor-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL RTCPeerConnection constructor throws if the given peerIdentity getter throws assert_throws: function "() => new RTCPeerConnection({ peerIdentity: toStringThrows })" did not throw
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc-identity/RTCPeerConnection-constructor.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc-identity/RTCPeerConnection-constructor.html
new file mode 100644
index 0000000..8498e6b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc-identity/RTCPeerConnection-constructor.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCPeerConnection constructor</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(() => {
+    const toStringThrows = { toString: function() { throw new Error; } };
+    assert_throws(new Error, () => new RTCPeerConnection({ peerIdentity: toStringThrows }));
+}, "RTCPeerConnection constructor throws if the given peerIdentity getter throws");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCIceTransport.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCIceTransport.html
index 17ae6dc..6a00e1c5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCIceTransport.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCIceTransport.html
@@ -58,7 +58,7 @@
     assert_true(dtlsTransport instanceof RTCDtlsTransport,
       'Expect sctp.transport to be an RTCDtlsTransport');
 
-    const iceTransport = dtlsTransport.transport;
+    const iceTransport = dtlsTransport.iceTransport;
     assert_true(iceTransport instanceof RTCIceTransport,
       'Expect dtlsTransport.transport to be an RTCIceTransport');
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-addTrack.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-addTrack.https.html
index 2ec9a1f..1550fc4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-addTrack.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-addTrack.https.html
@@ -276,19 +276,6 @@
             createAnswer to mark the corresponding media description as sendrecv
             or sendonly and add the MSID of the track added, as defined in [JSEP]
             (section 5.2.2. and section 5.3.2.).
-        9.  A track could have contents that are inaccessible to the application.
-            This can be due to being marked with a peerIdentity option or anything
-            that would make a track CORS cross-origin. These tracks can be supplied
-            to the addTrack method, and have an RTCRtpSender created for them, but
-            content must not be transmitted, unless they are also marked with
-            peerIdentity and they meet the requirements for sending (see isolated
-            streams and RTCPeerConnection).
-
-            All other tracks that are not accessible to the application must not be
-            sent to the peer, with silence (audio), black frames (video) or
-            equivalently absent content being sent in place of track content.
-
-            Note that this property can change over time.
 
     Non-Testable
       5.1.  addTrack
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-connectionState.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-connectionState.html
index d8e94112..7ef7e4a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-connectionState.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-connectionState.html
@@ -34,7 +34,7 @@
 
     5.5.  RTCDtlsTransport Interface
       interface RTCDtlsTransport {
-        readonly attribute RTCIceTransport       transport;
+        readonly attribute RTCIceTransport       iceTransport;
         readonly attribute RTCDtlsTransportState state;
         ...
       };
@@ -121,7 +121,7 @@
         assert_equals(dtlsTransport.state, 'connected',
           'Expect DTLS transport to be in connected state');
 
-        const iceTransport = dtlsTransport.transport
+        const iceTransport = dtlsTransport.iceTransport
         assert_true(iceTransport.state ===  'connected' ||
           iceTransport.state === 'completed',
           'Expect ICE transport to be in connected or completed state');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-constructor-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-constructor-expected.txt
index 05ef39e..3ad5c15 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-constructor-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-constructor-expected.txt
@@ -4,9 +4,6 @@
 PASS new RTCPeerConnection(null)
 PASS new RTCPeerConnection(undefined)
 PASS new RTCPeerConnection({})
-FAIL new RTCPeerConnection({ peerIdentity: toStringThrows }) assert_throws: function "function() {
-        eval(expr);
-      }" did not throw
 PASS new RTCPeerConnection({ certificates: null })
 PASS new RTCPeerConnection({ certificates: undefined })
 PASS new RTCPeerConnection({ certificates: [] })
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-constructor.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-constructor.html
index e00d987..abea2eb 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-constructor.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-constructor.html
@@ -23,9 +23,6 @@
   'undefined': false,
   '{}': false,
 
-  // peerIdentity
-  '{ peerIdentity: toStringThrows }': new Error,
-
   // certificates
   '{ certificates: null }': new TypeError,
   '{ certificates: undefined }': false,
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createAnswer.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createAnswer.html
index abff1eb..030ced8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createAnswer.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createAnswer.html
@@ -11,8 +11,7 @@
   // https://w3c.github.io/webrtc-pc/archives/20170515/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer()
-  //   generateAnswer()
+  //   generateDataChannelOffer()
 
   /*
    * 4.3.2. createAnswer()
@@ -39,7 +38,7 @@
 
     t.add_cleanup(() => pc.close());
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer => pc.setRemoteDescription(offer))
     .then(() => pc.createAnswer())
     .then(answer => {
@@ -56,7 +55,7 @@
 
     t.add_cleanup(() => pc.close());
 
-    return generateOffer({ pc, data: true })
+    return generateDataChannelOffer(pc)
     .then(offer => pc.setRemoteDescription(offer))
     .then(() => {
       pc.close();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
index e051cf4..3f7a47d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
@@ -3,8 +3,5 @@
 PASS createOffer() and then setLocalDescription() should succeed
 PASS createOffer() after connection is closed should reject with InvalidStateError
 FAIL When media stream is added when createOffer() is running in parallel, the result offer should contain the new media stream Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers assert_equals: Expect audio line to remain in created offer expected 1 but got 0
-FAIL createOffer() with offerToReceiveVideo should add video line to all subsequent created offers assert_equals: Expect video line to remain in created offer expected 1 but got 0
-FAIL createOffer() with offerToReceiveAudio:true then offerToReceiveVideo:true should have result offer with both audio and video line assert_equals: Expect audio line to remain in created offer expected 1 but got 0
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer.html
index 2fa3a05..c56d452 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer.html
@@ -11,8 +11,6 @@
   // https://w3c.github.io/webrtc-pc/archives/20170515/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer()
-  //   generateAnswer()
   //   countAudioLine()
   //   countVideoLine()
   //   assert_session_desc_similar()
@@ -49,7 +47,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveAudio: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setLocalDescription(offer)
       .then(() => {
@@ -113,93 +111,4 @@
    *    5.  If this inspection failed for any reason, reject p with a newly created
    *        OperationError and abort these steps.
    */
-
-  /*
-   *  4.3.3.2 Configuration data extensions
-   *  partial dictionary RTCOfferOptions
-   */
-
-  /*
-   *  offerToReceiveAudio of type boolean
-   *    When this is given a non-false value, no outgoing track of type
-   *    "audio" is attached to the PeerConnection, and the existing
-   *    localDescription (if any) doesn't contain any sendrecv or recv
-   *    audio media sections, createOffer() will behave as if
-   *    addTransceiver("audio") had been called once prior to the createOffer() call.
-   */
-  promise_test(t => {
-    const pc = new RTCPeerConnection();
-
-    t.add_cleanup(() => pc.close());
-
-    return pc.createOffer({ offerToReceiveAudio: true })
-    .then(offer1 => {
-      assert_equals(countAudioLine(offer1.sdp), 1,
-        'Expect created offer to have audio line');
-
-      // The first createOffer implicitly calls addTransceiver('audio'),
-      // so all following offers will also have audio media section
-      // in their SDP.
-      return pc.createOffer({ offerToReceiveAudio: false })
-      .then(offer2 => {
-        assert_equals(countAudioLine(offer2.sdp), 1,
-          'Expect audio line to remain in created offer');
-      })
-    });
-  }, 'createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers');
-
-  /*
-   *  offerToReceiveVideo of type boolean
-   *    When this is given a non-false value, and no outgoing track
-   *    of type "video" is attached to the PeerConnection, and the
-   *    existing localDescription (if any) doesn't contain any sendecv
-   *    or recv video media sections, createOffer() will behave as if
-   *    addTransceiver("video") had been called prior to the createOffer() call.
-   */
-  promise_test(t => {
-    const pc = new RTCPeerConnection();
-
-    t.add_cleanup(() => pc.close());
-
-    return pc.createOffer({ offerToReceiveVideo: true })
-    .then(offer1 => {
-      assert_equals(countVideoLine(offer1.sdp), 1,
-      'Expect created offer to have video line');
-
-      return pc.createOffer({ offerToReceiveVideo: false })
-      .then(offer2 => {
-        assert_equals(countVideoLine(offer2.sdp), 1,
-          'Expect video line to remain in created offer');
-      })
-    });
-  }, 'createOffer() with offerToReceiveVideo should add video line to all subsequent created offers');
-
-  promise_test(t => {
-    const pc = new RTCPeerConnection();
-
-    t.add_cleanup(() => pc.close());
-
-    return pc.createOffer({
-      offerToReceiveAudio: true,
-      offerToReceiveVideo: false
-    }).then(offer1 => {
-      assert_equals(countAudioLine(offer1.sdp), 1,
-        'Expect audio line to be found in created offer');
-
-      assert_equals(countVideoLine(offer1.sdp), 0,
-        'Expect video line to not found in create offer');
-
-      return pc.createOffer({
-        offerToReceiveAudio: false,
-        offerToReceiveVideo: true
-      }).then(offer2 => {
-        assert_equals(countAudioLine(offer2.sdp), 1,
-          'Expect audio line to remain in created offer');
-
-        assert_equals(countVideoLine(offer2.sdp), 1,
-          'Expect video line to be found in create offer');
-      })
-    });
-  }, 'createOffer() with offerToReceiveAudio:true then offerToReceiveVideo:true should have result offer with both audio and video line');
-
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-helper.js b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-helper.js
index 0153247..8529fb4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-helper.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-helper.js
@@ -103,61 +103,21 @@
     'Expect both session descriptions to have different count of media lines');
 }
 
-// Helper function to generate offer using a freshly created RTCPeerConnection
-// object with any audio, video, data media lines present
-function generateOffer(options={}) {
-  const {
-    audio = false,
-    video = false,
-    data = false,
-    pc,
-  } = options;
+async function generateDataChannelOffer(pc) {
+  pc.createDataChannel('test');
+  const offer = await pc.createOffer();
+  assert_equals(countApplicationLine(offer.sdp), 1, 'Expect m=application line to be present in generated SDP');
+  return offer;
+}
 
-  if (data) {
-    pc.createDataChannel('test');
-  }
-
-  const setup = {};
-
-  if (audio) {
-    setup.offerToReceiveAudio = true;
-  }
-
-  if (video) {
-    setup.offerToReceiveVideo = true;
-  }
-
-  return pc.createOffer(setup).then(offer => {
-    // Guard here to ensure that the generated offer really
-    // contain the number of media lines we want
-    const { sdp } = offer;
-
-    if(audio) {
-      assert_equals(countAudioLine(sdp), 1,
-        'Expect m=audio line to be present in generated SDP');
-    } else {
-      assert_equals(countAudioLine(sdp), 0,
-        'Expect m=audio line to be present in generated SDP');
+async function generateAudioReceiveOnlyOffer(pc)
+{
+    try {
+        pc.addTransceiver('audio', { direction: 'recvonly' });
+        return pc.createOffer();
+    } catch(e) {
+        return pc.createOffer({ offerToReceiveAudio: true });
     }
-
-    if(video) {
-      assert_equals(countVideoLine(sdp), 1,
-        'Expect m=video line to be present in generated SDP');
-    } else {
-      assert_equals(countVideoLine(sdp), 0,
-        'Expect m=video line to not present in generated SDP');
-    }
-
-    if(data) {
-      assert_equals(countApplicationLine(sdp), 1,
-        'Expect m=application line to be present in generated SDP');
-    } else {
-      assert_equals(countApplicationLine(sdp), 0,
-        'Expect m=application line to not present in generated SDP');
-    }
-
-    return offer;
-  });
 }
 
 async function generateVideoReceiveOnlyOffer(pc)
@@ -172,14 +132,12 @@
 
 // Helper function to generate answer based on given offer using a freshly
 // created RTCPeerConnection object
-function generateAnswer(offer) {
+async function generateAnswer(offer) {
   const pc = new RTCPeerConnection();
-  return pc.setRemoteDescription(offer)
-  .then(() => pc.createAnswer())
-  .then((answer) => {
-    pc.close();
-    return answer;
-  });
+  await pc.setRemoteDescription(offer);
+  const answer = await pc.createAnswer();
+  pc.close();
+  return answer;
 }
 
 // Run a test function that return a promise that should
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer.html
index 4a50456..b783d85 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer.html
@@ -11,7 +11,6 @@
   // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer
   //   generateAnswer
   //   assert_session_desc_similar
 
@@ -65,7 +64,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setRemoteDescription(offer)
       .then(() => pc.createAnswer())
@@ -97,7 +96,7 @@
 
     t.add_cleanup(() => pc.close());
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setRemoteDescription(offer)
       .then(() => pc.createAnswer())
@@ -128,7 +127,7 @@
 
     t.add_cleanup(() => pc.close());
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setRemoteDescription(offer)
       .then(() => generateAnswer(offer))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer.html
index 9502f28..49eb5228 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer.html
@@ -11,7 +11,7 @@
   // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer
+  //   generateDataChannelOffer
   //   assert_session_desc_not_similar
   //   assert_session_desc_similar
 
@@ -62,7 +62,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveAudio: true })
+    return generateAudioReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setLocalDescription(offer)
       .then(() => {
@@ -84,7 +84,7 @@
   promise_test(t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
-    return pc.createOffer({ offerToReceiveAudio: true })
+    return generateAudioReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setLocalDescription({ type: 'offer' })
       .then(() => {
@@ -109,7 +109,7 @@
 
     t.add_cleanup(() => pc2.close());
 
-    return generateOffer({ pc, data: true })
+    return generateDataChannelOffer(pc)
     .then(offer =>
       promise_rejects(t, 'InvalidModificationError',
         pc2.setLocalDescription(offer)));
@@ -122,9 +122,9 @@
     // with the first offer
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
-    return pc.createOffer({ offerToReceiveAudio: true })
+    return generateAudioReceiveOnlyOffer(pc)
     .then(offer1 =>
-      pc.createOffer({ offerToReceiveVideo: true })
+      generateVideoReceiveOnlyOffer(pc)
       .then(offer2 => {
         assert_session_desc_not_similar(offer1, offer2);
         return promise_rejects(t, 'InvalidModificationError',
@@ -139,11 +139,11 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveAudio: true })
+    return generateAudioReceiveOnlyOffer(pc)
     .then(offer1 =>
       pc.setLocalDescription(offer1)
       .then(() =>
-        pc.createOffer({ offerToReceiveVideo: true })
+        generateVideoReceiveOnlyOffer(pc)
         .then(offer2 =>
           pc.setLocalDescription(offer2)
           .then(offer2 => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer.html
index a145f95..1fbb309 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer.html
@@ -11,8 +11,6 @@
   // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer
-  //   generateAnswer
   //   assert_session_desc_similar
 
   /*
@@ -86,7 +84,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setRemoteDescription(offer)
       .then(() => pc.createAnswer())
@@ -119,7 +117,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setRemoteDescription(offer)
       .then(() => pc.createAnswer())
@@ -141,7 +139,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setRemoteDescription(offer)
       .then(() => pc.createAnswer())
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription.html
index 0ab2db1..1030f494 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription.html
@@ -11,7 +11,7 @@
   // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer
+  //   generateDataChannelOffer
   //   assert_session_desc_not_similar
   //   assert_session_desc_similar
 
@@ -50,14 +50,14 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveAudio: true })
+    return generateAudioReceiveOnlyOffer(pc)
     .then(offer1 =>
       pc.setLocalDescription(offer1)
       .then(() => generateAnswer(offer1))
       .then(answer => pc.setRemoteDescription(answer))
       .then(() => {
         pc.createDataChannel('test');
-        return pc.createOffer({ offerToReceiveVideo: true });
+        return generateVideoReceiveOnlyOffer(pc);
       })
       .then(offer2 =>
         pc.setLocalDescription(offer2)
@@ -79,12 +79,12 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return generateOffer({ pc, data: true })
+    return generateDataChannelOffer(pc)
     .then(offer => pc.setRemoteDescription(offer))
     .then(() => pc.createAnswer())
     .then(answer =>
       pc.setLocalDescription(answer)
-      .then(() => pc.createOffer({ offerToReceiveVideo: true }))
+      .then(() => generateVideoReceiveOnlyOffer(pc))
       .then(offer =>
         pc.setLocalDescription(offer)
         .then(() => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-answer.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-answer.html
index ff55447..cc5bd1e0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-answer.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-answer.html
@@ -11,7 +11,6 @@
   // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer()
   //   generateAnswer()
   //   assert_session_desc_similar()
 
@@ -64,7 +63,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setLocalDescription(offer)
       .then(() => generateAnswer(offer))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-pranswer.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-pranswer.html
index 9197a35..c27ee20 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-pranswer.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-pranswer.html
@@ -11,7 +11,6 @@
   // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer
   //   generateAnswer
   //   assert_session_desc_similar
 
@@ -86,7 +85,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setLocalDescription(offer)
       .then(() => generateAnswer(offer))
@@ -117,7 +116,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setLocalDescription(offer)
       .then(() => generateAnswer(offer))
@@ -139,7 +138,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return pc.createOffer({ offerToReceiveVideo: true })
+    return generateVideoReceiveOnlyOffer(pc)
     .then(offer =>
       pc.setLocalDescription(offer)
       .then(() => generateAnswer(offer))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt
index 7c92a52..cd12436 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL setRemoteDescription(rollback) in have-remote-offer state should revert to stable state Cannot read property 'createDataChannel' of undefined
+FAIL setRemoteDescription(rollback) in have-remote-offer state should revert to stable state promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
 FAIL setRemoteDescription(rollback) from stable state should reject with InvalidStateError assert_throws: function "function() { throw e }" threw object "TypeError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType." that is not a DOMException InvalidStateError: property "code" is equal to undefined, expected 11
 FAIL setRemoteDescription(rollback) should ignore invalid sdp content and succeed promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html
index 01969dc..ec90e56 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html
@@ -11,7 +11,7 @@
   // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer
+  //   generateDataChannelOffer
   //   assert_session_desc_similar
 
   /*
@@ -63,7 +63,7 @@
     const states = [];
     pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
 
-    return generateOffer({ data: true })
+    return generateDataChannelOffer(pc)
     .then(offer => pc.setRemoteDescription(offer))
     .then(() => {
       assert_equals(pc.signalingState, 'have-remote-offer');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html
index 7179f1e..c9d2d128 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html
@@ -115,9 +115,12 @@
                   'Second ontrack\'s stream ID matches local stream.');
     assert_array_equals(firstTrackEvent.streams, secondTrackEvent.streams,
                         'ontrack was fired with the same streams both times.');
-    assert_array_equals(firstTrackEvent.streams[0].getTracks(),
-                        [firstTrackEvent.track, secondTrackEvent.track],
-                        'The remote stream == [first track, second track].');
+
+    assert_equals(firstTrackEvent.streams[0].getTracks().length, 2, "stream should have two tracks");
+    assert_true(firstTrackEvent.streams[0].getTracks().includes(firstTrackEvent.track), "remoteStream should have the first track");
+    assert_true(firstTrackEvent.streams[0].getTracks().includes(secondTrackEvent.track), "remoteStream should have the second track");
+    assert_equals(ontrackEventsFired, 2, 'Unexpected number of track events.');
+
     assert_equals(ontrackEventsFired, 2, 'Unexpected number of track events.');
   }, 'addTrack() with two tracks and one stream makes ontrack fire twice with the tracks and shared stream.');
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription.html
index ad9cdff5..182abea 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription.html
@@ -91,7 +91,7 @@
       assert_equals(state, states[eventCount]);
     };
 
-    const offer = await pc.createOffer({ offerToReceiveAudio: true });
+    const offer = await generateAudioReceiveOnlyOffer(pc);
     assert_state('stable');
     await pc.setLocalDescription(offer);
     assert_state('have-local-offer');
@@ -111,10 +111,10 @@
     t.add_cleanup(() => pc.close());
     t.add_cleanup(() => pc2.close());
 
-    const offer1 = await pc2.createOffer({ offerToReceiveAudio: true });
+    const offer1 = await generateAudioReceiveOnlyOffer(pc2);
     await pc.setRemoteDescription(offer1);
     await pc.setLocalDescription(await pc.createAnswer());
-    const offer2 = await pc2.createOffer({ offerToReceiveVideo: true });
+    const offer2 = await generateVideoReceiveOnlyOffer(pc2);
     await pc.setRemoteDescription(offer2);
     assert_session_desc_not_similar(offer1, offer2);
     assert_session_desc_similar(pc.remoteDescription, offer2);
@@ -128,7 +128,7 @@
     t.add_cleanup(() => pc.close());
     t.add_cleanup(() => pc2.close());
 
-    const offer = await pc.createOffer({ offerToReceiveAudio: true });
+    const offer = await generateAudioReceiveOnlyOffer(pc);
     await pc.setLocalDescription(offer);
     await pc2.setRemoteDescription(offer);
     const answer = await pc2.createAnswer();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpReceiver-getParameters.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpReceiver-getParameters.html
index 97e5d10d..453844d5f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpReceiver-getParameters.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpReceiver-getParameters.html
@@ -36,7 +36,7 @@
   test(t => {
     const pc = new RTCPeerConnection();
     const { receiver } = pc.addTransceiver('audio');
-    const param = pc.getParameters();
+    const param = receiver.getParameters();
     validateReceiverRtpParameters(param);
   });
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpSender-getStats.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpSender-getStats.https.html
index 18ab825..1af8285f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpSender-getStats.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpSender-getStats.https.html
@@ -44,7 +44,11 @@
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
-    const { sender } = caller.addTransceiver('audio');
+
+    const stream = await getNoiseStream({audio:true});
+    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+    const [track] = stream.getTracks();
+    const { sender } = caller.addTransceiver(track);
 
     await doSignalingHandshake(caller, callee);
     const statsReport = await sender.getStats();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
index 09b20d2..4807af0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
@@ -4,10 +4,6 @@
 FAIL checkAddTransceiverWithTrack promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 FAIL checkAddTransceiverWithAddTrack assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:{}},stopped:false},{currentDirection:null,direction:\"sendrecv\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:{}},stopped:false}]" but got "[]"
 FAIL checkAddTransceiverWithDirection promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL checkAddTransceiverWithStream promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL checkAddTransceiverWithOfferToReceiveAudio assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false}]" but got "[]"
-FAIL checkAddTransceiverWithOfferToReceiveVideo assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
-FAIL checkAddTransceiverWithOfferToReceiveBoth assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false},{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
 FAIL checkAddTransceiverWithSetRemoteOfferSending promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 FAIL checkAddTransceiverWithSetRemoteOfferNoSend promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 FAIL checkAddTransceiverBadKind assert_true: addTransceiver("foo") throws a TypeError expected true got false
@@ -26,7 +22,6 @@
 FAIL checkAddTrackExistingTransceiverThenRemove promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 FAIL checkRemoveTrackNegotiation promise_test: Unhandled rejection with value: object "TypeError: Cannot set property 'direction' of undefined"
 FAIL checkMute promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'receiver' of undefined"
-FAIL checkOnAddStream assert_equals: expected "[{streams:[{}]},{streams:[{}]}]" but got "[]"
 FAIL checkStop promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'stop' of undefined"
 FAIL checkStopAfterCreateOffer promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'stop' of undefined"
 FAIL checkStopAfterSetLocalOffer promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'stop' of undefined"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpTransceiver.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpTransceiver.https.html
index 860f5de..2de9fb9f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpTransceiver.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCRtpTransceiver.https.html
@@ -305,125 +305,6 @@
       ]);
   };
 
-  const checkAddTransceiverWithStream = async t => {
-    const pc = new RTCPeerConnection();
-    t.add_cleanup(() => pc.close());
-
-    const audioStream = await navigator.mediaDevices.getUserMedia({audio: true});
-    const videoStream = await navigator.mediaDevices.getUserMedia({video: true});
-    t.add_cleanup(() => stopTracks(audioStream, videoStream));
-
-    const audio = audioStream.getAudioTracks()[0];
-    const video = videoStream.getVideoTracks()[0];
-
-    pc.addTransceiver(audio, {streams: [audioStream]});
-    pc.addTransceiver(video, {streams: [videoStream]});
-
-    hasProps(pc.getTransceivers(),
-      [
-        {
-          receiver: {track: {kind: "audio"}},
-          sender: {track: audio},
-          direction: "sendrecv",
-          mid: null,
-          currentDirection: null,
-          stopped: false
-        },
-        {
-          receiver: {track: {kind: "video"}},
-          sender: {track: video},
-          direction: "sendrecv",
-          mid: null,
-          currentDirection: null,
-          stopped: false
-        }
-      ]);
-
-    const offer = await pc.createOffer();
-    assert_true(offer.sdp.includes("a=msid:" + audioStream.id + " " + audio.id),
-      "offer contains the expected audio msid");
-    assert_true(offer.sdp.includes("a=msid:" + videoStream.id + " " + video.id),
-      "offer contains the expected video msid");
-  };
-
-  const checkAddTransceiverWithOfferToReceive = async (t, kinds) => {
-    const pc = new RTCPeerConnection();
-    t.add_cleanup(() => pc.close());
-
-    const propsToSet = kinds.map(kind => {
-      if (kind == "audio") {
-        return "offerToReceiveAudio";
-      } else if (kind == "video") {
-        return "offerToReceiveVideo";
-      }
-    });
-
-    const options = {};
-
-    for (const prop of propsToSet) {
-      options[prop] = true;
-    }
-
-    let offer = await pc.createOffer(options);
-
-    const expected = [];
-
-    if (options.offerToReceiveAudio) {
-      expected.push(
-        {
-          receiver: {track: {kind: "audio"}},
-          sender: {track: null},
-          direction: "recvonly",
-          mid: null,
-          currentDirection: null,
-          stopped: false
-        });
-    }
-
-    if (options.offerToReceiveVideo) {
-      expected.push(
-        {
-          receiver: {track: {kind: "video"}},
-          sender: {track: null},
-          direction: "recvonly",
-          mid: null,
-          currentDirection: null,
-          stopped: false
-        });
-    }
-
-    hasProps(pc.getTransceivers(), expected);
-
-    // Test offerToReceive: false
-    for (const prop of propsToSet) {
-      options[prop] = false;
-    }
-
-    // Check that sendrecv goes to sendonly
-    for (const transceiver of pc.getTransceivers()) {
-      transceiver.direction = "sendrecv";
-    }
-
-    for (const transceiverCheck of expected) {
-      transceiverCheck.direction = "sendonly";
-    }
-
-    offer = await pc.createOffer(options);
-    hasProps(pc.getTransceivers(), expected);
-
-    // Check that recvonly goes to inactive
-    for (const transceiver of pc.getTransceivers()) {
-      transceiver.direction = "recvonly";
-    }
-
-    for (const transceiverCheck of expected) {
-      transceiverCheck.direction = "inactive";
-    }
-
-    offer = await pc.createOffer(options);
-    hasProps(pc.getTransceivers(), expected);
-  };
-
   const checkAddTransceiverWithSetRemoteOfferSending = async t => {
     const pc1 = new RTCPeerConnection();
     const pc2 = new RTCPeerConnection();
@@ -1248,70 +1129,6 @@
     assert_equals(2, countUnmuteVideo2.count, "Got 2 unmute events for pc2's video track");
   };
 
-  const checkOnAddStream = async t => {
-    const pc1 = new RTCPeerConnection();
-    t.add_cleanup(() => pc1.close());
-    const stream1 = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
-    t.add_cleanup(() => stopTracks(stream1));
-    const audio1 = stream1.getAudioTracks()[0];
-    pc1.addTrack(audio1, stream1);
-    const video1 = stream1.getVideoTracks()[0];
-    pc1.addTrack(video1, stream1);
-
-    const pc2 = new RTCPeerConnection();
-    t.add_cleanup(() => pc2.close());
-    const stream2 = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
-    t.add_cleanup(() => stopTracks(stream2));
-    const audio2 = stream2.getAudioTracks()[0];
-    pc2.addTrack(audio2, stream2);
-    const video2 = stream2.getVideoTracks()[0];
-    pc2.addTrack(video2, stream2);
-
-    const offer = await pc1.createOffer();
-
-    let trackEventCollector = collectTrackEvents(pc2);
-    let addstreamEventCollector = collectEvents(pc2, "addstream", e => {
-      hasProps(e, {stream: {id: stream1.id}});
-      assert_equals(e.stream.getAudioTracks().length, 1, "One audio track");
-      assert_equals(e.stream.getVideoTracks().length, 1, "One video track");
-    });
-
-    await pc2.setRemoteDescription(offer);
-
-    let addstreamEvents = addstreamEventCollector.finish();
-    assert_equals(addstreamEvents.length, 1, "Should have 1 addstream event");
-
-    let trackEvents = trackEventCollector.finish();
-
-    hasProps(trackEvents,
-      [
-        {streams: [addstreamEvents[0].stream]},
-        {streams: [addstreamEvents[0].stream]}
-      ]);
-
-    await pc1.setLocalDescription(offer);
-    const answer = await pc2.createAnswer();
-
-    trackEventCollector = collectTrackEvents(pc1);
-    addstreamEventCollector = collectEvents(pc1, "addstream", e => {
-      hasProps(e, {stream: {id: stream2.id}});
-      assert_equals(e.stream.getAudioTracks().length, 1, "One audio track");
-      assert_equals(e.stream.getVideoTracks().length, 1, "One video track");
-    });
-
-    await pc1.setRemoteDescription(answer);
-    addstreamEvents = addstreamEventCollector.finish();
-    assert_equals(addstreamEvents.length, 1, "Should have 1 addstream event");
-
-    trackEvents = trackEventCollector.finish();
-
-    hasProps(trackEvents,
-      [
-        {streams: [addstreamEvents[0].stream]},
-        {streams: [addstreamEvents[0].stream]}
-      ]);
-  };
-
   const checkStop = async t => {
     const pc1 = new RTCPeerConnection();
     t.add_cleanup(() => pc1.close());
@@ -2312,16 +2129,6 @@
   checkAddTransceiverWithTrack,
   checkAddTransceiverWithAddTrack,
   checkAddTransceiverWithDirection,
-  checkAddTransceiverWithStream,
-  function checkAddTransceiverWithOfferToReceiveAudio(t) {
-    return checkAddTransceiverWithOfferToReceive(t, ["audio"]);
-  },
-  function checkAddTransceiverWithOfferToReceiveVideo(t) {
-    return checkAddTransceiverWithOfferToReceive(t, ["video"]);
-  },
-  function checkAddTransceiverWithOfferToReceiveBoth(t) {
-    return checkAddTransceiverWithOfferToReceive(t, ["audio", "video"]);
-  },
   checkAddTransceiverWithSetRemoteOfferSending,
   checkAddTransceiverWithSetRemoteOfferNoSend,
   checkAddTransceiverBadKind,
@@ -2340,7 +2147,6 @@
   checkAddTrackExistingTransceiverThenRemove,
   checkRemoveTrackNegotiation,
   checkMute,
-  checkOnAddStream,
   checkStop,
   checkStopAfterCreateOffer,
   checkStopAfterSetLocalOffer,
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCSctpTransport-constructor.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCSctpTransport-constructor.html
index c415c3fe..7d3df051 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCSctpTransport-constructor.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCSctpTransport-constructor.html
@@ -11,7 +11,7 @@
   // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
 
   // The following helper functions are called from RTCPeerConnection-helper.js:
-  //   generateOffer()
+  //   generateDataChannelOffer()
   //   generateAnswer()
 
   /*
@@ -69,7 +69,7 @@
     t.add_cleanup(() => pc.close());
     assert_equals(pc.sctp, null);
 
-    return generateOffer({ pc, data: true })
+    return generateDataChannelOffer(pc)
     .then(offer => pc.setRemoteDescription(offer))
     .then(() => pc.createAnswer())
     .then(answer => pc.setLocalDescription(answer))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCSctpTransport-maxMessageSize.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCSctpTransport-maxMessageSize.html
index 28d17ee..9163a66 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCSctpTransport-maxMessageSize.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCSctpTransport-maxMessageSize.html
@@ -49,7 +49,7 @@
   assert_equals(pc.sctp, null);
   let maxMessageSize;
 
-  return generateOffer({ pc, data: true })
+  return generateDataChannelOffer(pc)
   .then((offer) => {
     assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
       'SDP should have max-message-size attribute');
@@ -73,7 +73,7 @@
   t.add_cleanup(() => pc.close());
   assert_equals(pc.sctp, null);
 
-  return generateOffer({ pc, data: true })
+  return generateDataChannelOffer(pc)
   .then((offer) => {
     assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
       'SDP should have max-message-size attribute');
@@ -102,7 +102,7 @@
   t.add_cleanup(() => pc.close());
   assert_equals(pc.sctp, null);
 
-  return generateOffer({ pc, data: true })
+  return generateDataChannelOffer(pc)
   .then((offer) => {
     assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
       'SDP should have max-message-size attribute');
@@ -124,7 +124,7 @@
   t.add_cleanup(() => pc.close());
   assert_equals(pc.sctp, null);
 
-  return generateOffer({ pc, data: true })
+  return generateDataChannelOffer(pc)
   .then((offer) => {
     assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
       'SDP should have max-message-size attribute');
@@ -160,7 +160,7 @@
   assert_equals(pc.sctp, null);
   const largerThanCanSendSize = canSendSize + 1;
 
-  return generateOffer({ pc, data: true })
+  return generateDataChannelOffer(pc)
   .then((offer) => {
     assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
       'SDP should have max-message-size attribute');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/getstats.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/getstats.html
index 937f54b..053d9092 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/getstats.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/getstats.html
@@ -38,10 +38,6 @@
     }
   });
 
-  var onRemoteStream = test.step_func(function(event) {
-    assert_unreached('WebRTC received a stream when there was none');
-  });
-
   var getStatsRecordByType = function(stats, type) {
     for (let stat of stats.values()) {
       if (stat.type == type) {
@@ -91,7 +87,6 @@
 
     gSecondConnection = new RTCPeerConnection(null);
     gSecondConnection.onicecandidate = onIceCandidateToSecond;
-    gSecondConnection.onaddstream = onRemoteStream;
 
     // The createDataChannel is necessary and sufficient to make
     // sure the ICE connection be attempted.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window.js
index 3c57a022..6e9b7e9a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window.js
@@ -67,7 +67,9 @@
       'Expect sctpTransport.transport to be instance of RTCDtlsTransport');
     idlTestObjects.dtlsTransport = dtlsTransport;
 
-    const iceTransport = dtlsTransport.transport;
+    const iceTransport = dtlsTransport.iceTransport;
+    assert_true(iceTransport instanceof RTCIceTransport,
+      'Expect sctpTransport.transport to be instance of RTCDtlsTransport');
     idlTestObjects.iceTransport = iceTransport;
   });
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/README.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/README.txt
new file mode 100644
index 0000000..8adbf6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/README.txt
@@ -0,0 +1,2 @@
+This directory contains files that test for behavior relevant to webrtc,
+particularly defined in https://w3c.github.io/webrtc-pc/#legacy-interface-extensions
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
new file mode 100644
index 0000000..09d40c0a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+FAIL createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers assert_equals: Expect audio line to remain in created offer expected 1 but got 0
+FAIL createOffer() with offerToReceiveVideo should add video line to all subsequent created offers assert_equals: Expect video line to remain in created offer expected 1 but got 0
+FAIL createOffer() with offerToReceiveAudio:true, then with offerToReceiveVideo:true, should have result offer with both audio and video line assert_equals: Expect audio line to remain in created offer expected 1 but got 0
+PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
+FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect pc to have one transceiver expected 1 but got 0
+PASS createOffer() with offerToReceiveVideo set to false should not create a transceiver
+FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers assert_equals: Expect pc to have two transceivers expected 2 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive.html
similarity index 66%
rename from third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive.html
rename to third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive.html
index dd1827a..f710498 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive.html
@@ -4,10 +4,99 @@
 <link rel="help" href="https://w3c.github.io/webrtc-pc/#legacy-configuration-extensions">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="RTCPeerConnection-helper.js"></script>
+<script src="../RTCPeerConnection-helper.js"></script>
 <script>
   'use strict';
 
+  /*
+   *  4.3.3.2 Configuration data extensions
+   *  partial dictionary RTCOfferOptions
+   */
+
+  /*
+   *  offerToReceiveAudio of type boolean
+   *    When this is given a non-false value, no outgoing track of type
+   *    "audio" is attached to the PeerConnection, and the existing
+   *    localDescription (if any) doesn't contain any sendrecv or recv
+   *    audio media sections, createOffer() will behave as if
+   *    addTransceiver("audio") had been called once prior to the createOffer() call.
+   */
+  promise_test(t => {
+    const pc = new RTCPeerConnection();
+
+    t.add_cleanup(() => pc.close());
+
+    return pc.createOffer({ offerToReceiveAudio: true })
+    .then(offer1 => {
+      assert_equals(countAudioLine(offer1.sdp), 1,
+        'Expect created offer to have audio line');
+
+      // The first createOffer implicitly calls addTransceiver('audio'),
+      // so all following offers will also have audio media section
+      // in their SDP.
+      return pc.createOffer({ offerToReceiveAudio: false })
+      .then(offer2 => {
+        assert_equals(countAudioLine(offer2.sdp), 1,
+          'Expect audio line to remain in created offer');
+      })
+    });
+  }, 'createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers');
+
+  /*
+   *  offerToReceiveVideo of type boolean
+   *    When this is given a non-false value, and no outgoing track
+   *    of type "video" is attached to the PeerConnection, and the
+   *    existing localDescription (if any) doesn't contain any sendecv
+   *    or recv video media sections, createOffer() will behave as if
+   *    addTransceiver("video") had been called prior to the createOffer() call.
+   */
+  promise_test(t => {
+    const pc = new RTCPeerConnection();
+
+    t.add_cleanup(() => pc.close());
+
+    return pc.createOffer({ offerToReceiveVideo: true })
+    .then(offer1 => {
+      assert_equals(countVideoLine(offer1.sdp), 1,
+      'Expect created offer to have video line');
+
+      return pc.createOffer({ offerToReceiveVideo: false })
+      .then(offer2 => {
+        assert_equals(countVideoLine(offer2.sdp), 1,
+          'Expect video line to remain in created offer');
+      })
+    });
+  }, 'createOffer() with offerToReceiveVideo should add video line to all subsequent created offers');
+
+  promise_test(t => {
+    const pc = new RTCPeerConnection();
+
+    t.add_cleanup(() => pc.close());
+
+    return pc.createOffer({
+      offerToReceiveAudio: true,
+      offerToReceiveVideo: false
+    }).then(offer1 => {
+      assert_equals(countAudioLine(offer1.sdp), 1,
+        'Expect audio line to be found in created offer');
+
+      assert_equals(countVideoLine(offer1.sdp), 0,
+        'Expect video line to not be found in create offer');
+
+      return pc.createOffer({
+        offerToReceiveAudio: false,
+        offerToReceiveVideo: true
+      }).then(offer2 => {
+        assert_equals(countAudioLine(offer2.sdp), 1,
+          'Expect audio line to remain in created offer');
+
+        assert_equals(countVideoLine(offer2.sdp), 1,
+          'Expect video line to be found in create offer');
+      })
+    });
+  }, 'createOffer() with offerToReceiveAudio:true, then with offerToReceiveVideo:true, should have result offer with both audio and video line');
+
+
   // Run some tests for both audio and video kinds
   ['audio', 'video'].forEach((kind) => {
     const capsKind = kind[0].toUpperCase() + kind.slice(1);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
new file mode 100644
index 0000000..1c91b50
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL checkAddTransceiverWithStream promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL checkAddTransceiverWithOfferToReceiveAudio assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false}]" but got "[]"
+FAIL checkAddTransceiverWithOfferToReceiveVideo assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
+FAIL checkAddTransceiverWithOfferToReceiveBoth assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false},{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https.html
new file mode 100644
index 0000000..8c29752
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https.html
@@ -0,0 +1,169 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCRtpTransceiver with OfferToReceive legacy options</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../RTCPeerConnection-helper.js"></script>
+<script>
+  'use strict';
+
+  const stopTracks = (...streams) => {
+    streams.forEach(stream => stream.getTracks().forEach(track => track.stop()));
+  };
+
+  // comparable() - produces copy of object that is JSON comparable.
+  // o = original object (required)
+  // t = template of what to examine. Useful if o is non-enumerable (optional)
+
+  const comparable = (o, t = o) => {
+    if (typeof o != 'object' || !o) {
+      return o;
+    }
+    if (Array.isArray(t) && Array.isArray(o)) {
+      return o.map((n, i) => comparable(n, t[i]));
+    }
+    return Object.keys(t).sort()
+        .reduce((r, key) => (r[key] = comparable(o[key], t[key]), r), {});
+  };
+
+  const stripKeyQuotes = s => s.replace(/"(\w+)":/g, "$1:");
+
+  const hasProps = (observed, expected) => {
+    const observable = comparable(observed, expected);
+    assert_equals(stripKeyQuotes(JSON.stringify(observable)),
+       stripKeyQuotes(JSON.stringify(comparable(expected))));
+  };
+
+  const checkAddTransceiverWithStream = async t => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+
+    const audioStream = await navigator.mediaDevices.getUserMedia({audio: true});
+    const videoStream = await navigator.mediaDevices.getUserMedia({video: true});
+    t.add_cleanup(() => stopTracks(audioStream, videoStream));
+
+    const audio = audioStream.getAudioTracks()[0];
+    const video = videoStream.getVideoTracks()[0];
+
+    pc.addTransceiver(audio, {streams: [audioStream]});
+    pc.addTransceiver(video, {streams: [videoStream]});
+
+    hasProps(pc.getTransceivers(),
+      [
+        {
+          receiver: {track: {kind: "audio"}},
+          sender: {track: audio},
+          direction: "sendrecv",
+          mid: null,
+          currentDirection: null,
+          stopped: false
+        },
+        {
+          receiver: {track: {kind: "video"}},
+          sender: {track: video},
+          direction: "sendrecv",
+          mid: null,
+          currentDirection: null,
+          stopped: false
+        }
+      ]);
+
+    const offer = await pc.createOffer();
+    assert_true(offer.sdp.includes("a=msid:" + audioStream.id + " " + audio.id),
+      "offer contains the expected audio msid");
+    assert_true(offer.sdp.includes("a=msid:" + videoStream.id + " " + video.id),
+      "offer contains the expected video msid");
+  };
+
+  const checkAddTransceiverWithOfferToReceive = async (t, kinds) => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+
+    const propsToSet = kinds.map(kind => {
+      if (kind == "audio") {
+        return "offerToReceiveAudio";
+      } else if (kind == "video") {
+        return "offerToReceiveVideo";
+      }
+    });
+
+    const options = {};
+
+    for (const prop of propsToSet) {
+      options[prop] = true;
+    }
+
+    let offer = await pc.createOffer(options);
+
+    const expected = [];
+
+    if (options.offerToReceiveAudio) {
+      expected.push(
+        {
+          receiver: {track: {kind: "audio"}},
+          sender: {track: null},
+          direction: "recvonly",
+          mid: null,
+          currentDirection: null,
+          stopped: false
+        });
+    }
+
+    if (options.offerToReceiveVideo) {
+      expected.push(
+        {
+          receiver: {track: {kind: "video"}},
+          sender: {track: null},
+          direction: "recvonly",
+          mid: null,
+          currentDirection: null,
+          stopped: false
+        });
+    }
+
+    hasProps(pc.getTransceivers(), expected);
+
+    // Test offerToReceive: false
+    for (const prop of propsToSet) {
+      options[prop] = false;
+    }
+
+    // Check that sendrecv goes to sendonly
+    for (const transceiver of pc.getTransceivers()) {
+      transceiver.direction = "sendrecv";
+    }
+
+    for (const transceiverCheck of expected) {
+      transceiverCheck.direction = "sendonly";
+    }
+
+    offer = await pc.createOffer(options);
+    hasProps(pc.getTransceivers(), expected);
+
+    // Check that recvonly goes to inactive
+    for (const transceiver of pc.getTransceivers()) {
+      transceiver.direction = "recvonly";
+    }
+
+    for (const transceiverCheck of expected) {
+      transceiverCheck.direction = "inactive";
+    }
+
+    offer = await pc.createOffer(options);
+    hasProps(pc.getTransceivers(), expected);
+  };
+
+const tests = [
+  checkAddTransceiverWithStream,
+  function checkAddTransceiverWithOfferToReceiveAudio(t) {
+    return checkAddTransceiverWithOfferToReceive(t, ["audio"]);
+  },
+  function checkAddTransceiverWithOfferToReceiveVideo(t) {
+    return checkAddTransceiverWithOfferToReceive(t, ["video"]);
+  },
+  function checkAddTransceiverWithOfferToReceiveBoth(t) {
+    return checkAddTransceiverWithOfferToReceive(t, ["audio", "video"]);
+  }
+].forEach(test => promise_test(test, test.name));
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/onaddstream.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
new file mode 100644
index 0000000..7415aac
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught Error: assert_true: Transceiver is set on event expected true got false
+FAIL Check onaddstream assert_equals: expected "[{streams:[{}]},{streams:[{}]}]" but got "[]"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/onaddstream.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/onaddstream.https.html
new file mode 100644
index 0000000..5aa16b36
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/legacy/onaddstream.https.html
@@ -0,0 +1,153 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>onaddstream tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  'use strict';
+
+  const stopTracks = (...streams) => {
+    streams.forEach(stream => stream.getTracks().forEach(track => track.stop()));
+  };
+
+  const collectEvents = (target, name, check) => {
+    const events = [];
+    const handler = e => {
+      check(e);
+      events.push(e);
+    };
+
+    target.addEventListener(name, handler);
+
+    const finishCollecting = () => {
+      target.removeEventListener(name, handler);
+      return events;
+    };
+
+    return {finish: finishCollecting};
+  };
+
+  const collectAddTrackEvents = stream => {
+    const checkEvent = e => {
+      assert_true(e.track instanceof MediaStreamTrack, "Track is set on event");
+      assert_true(stream.getTracks().includes(e.track),
+        "track in addtrack event is in the stream");
+    };
+    return collectEvents(stream, "addtrack", checkEvent);
+  };
+
+  const collectRemoveTrackEvents = stream => {
+    const checkEvent = e => {
+      assert_true(e.track instanceof MediaStreamTrack, "Track is set on event");
+      assert_true(!stream.getTracks().includes(e.track),
+        "track in removetrack event is not in the stream");
+    };
+    return collectEvents(stream, "removetrack", checkEvent);
+  };
+
+  const collectTrackEvents = pc => {
+    const checkEvent = e => {
+      assert_true(e.track instanceof MediaStreamTrack, "Track is set on event");
+      assert_true(e.receiver instanceof RTCRtpReceiver, "Receiver is set on event");
+      assert_true(e.transceiver instanceof RTCRtpTransceiver, "Transceiver is set on event");
+      assert_true(Array.isArray(e.streams), "Streams is set on event");
+      e.streams.forEach(stream => {
+        assert_true(stream.getTracks().includes(e.track),
+           "Each stream in event contains the track");
+      });
+      assert_equals(e.receiver, e.transceiver.receiver,
+                    "Receiver belongs to transceiver");
+      assert_equals(e.track, e.receiver.track,
+                    "Track belongs to receiver");
+    };
+
+    return collectEvents(pc, "track", checkEvent);
+  };
+
+  // comparable() - produces copy of object that is JSON comparable.
+  // o = original object (required)
+  // t = template of what to examine. Useful if o is non-enumerable (optional)
+
+  const comparable = (o, t = o) => {
+    if (typeof o != 'object' || !o) {
+      return o;
+    }
+    if (Array.isArray(t) && Array.isArray(o)) {
+      return o.map((n, i) => comparable(n, t[i]));
+    }
+    return Object.keys(t).sort()
+        .reduce((r, key) => (r[key] = comparable(o[key], t[key]), r), {});
+  };
+
+  const stripKeyQuotes = s => s.replace(/"(\w+)":/g, "$1:");
+
+  const hasProps = (observed, expected) => {
+    const observable = comparable(observed, expected);
+    assert_equals(stripKeyQuotes(JSON.stringify(observable)),
+       stripKeyQuotes(JSON.stringify(comparable(expected))));
+  };
+
+  promise_test(async t => {
+    const pc1 = new RTCPeerConnection();
+    t.add_cleanup(() => pc1.close());
+    const stream1 = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
+    t.add_cleanup(() => stopTracks(stream1));
+    const audio1 = stream1.getAudioTracks()[0];
+    pc1.addTrack(audio1, stream1);
+    const video1 = stream1.getVideoTracks()[0];
+    pc1.addTrack(video1, stream1);
+
+    const pc2 = new RTCPeerConnection();
+    t.add_cleanup(() => pc2.close());
+    const stream2 = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
+    t.add_cleanup(() => stopTracks(stream2));
+    const audio2 = stream2.getAudioTracks()[0];
+    pc2.addTrack(audio2, stream2);
+    const video2 = stream2.getVideoTracks()[0];
+    pc2.addTrack(video2, stream2);
+
+    const offer = await pc1.createOffer();
+
+    let trackEventCollector = collectTrackEvents(pc2);
+    let addstreamEventCollector = collectEvents(pc2, "addstream", e => {
+      hasProps(e, {stream: {id: stream1.id}});
+      assert_equals(e.stream.getAudioTracks().length, 1, "One audio track");
+      assert_equals(e.stream.getVideoTracks().length, 1, "One video track");
+    });
+
+    await pc2.setRemoteDescription(offer);
+
+    let addstreamEvents = addstreamEventCollector.finish();
+    assert_equals(addstreamEvents.length, 1, "Should have 1 addstream event");
+
+    let trackEvents = trackEventCollector.finish();
+
+    hasProps(trackEvents,
+      [
+        {streams: [addstreamEvents[0].stream]},
+        {streams: [addstreamEvents[0].stream]}
+      ]);
+
+    await pc1.setLocalDescription(offer);
+    const answer = await pc2.createAnswer();
+
+    trackEventCollector = collectTrackEvents(pc1);
+    addstreamEventCollector = collectEvents(pc1, "addstream", e => {
+      hasProps(e, {stream: {id: stream2.id}});
+      assert_equals(e.stream.getAudioTracks().length, 1, "One audio track");
+      assert_equals(e.stream.getVideoTracks().length, 1, "One video track");
+    });
+
+    await pc1.setRemoteDescription(answer);
+    addstreamEvents = addstreamEventCollector.finish();
+    assert_equals(addstreamEvents.length, 1, "Should have 1 addstream event");
+
+    trackEvents = trackEventCollector.finish();
+
+    hasProps(trackEvents,
+      [
+        {streams: [addstreamEvents[0].stream]},
+        {streams: [addstreamEvents[0].stream]}
+      ]);
+  },"Check onaddstream");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/no-media-call.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/no-media-call.html
index ff7ab53..0f2e2a33 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/no-media-call.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/no-media-call.html
@@ -124,8 +124,6 @@
     gSecondConnection.onicecandidate = onIceCandidateToSecond;
     gSecondConnection.oniceconnectionstatechange = onIceConnectionStateChange;
 
-    // The offerToReceiveVideo is necessary and sufficient to make
-    // an actual connection.
     generateVideoReceiveOnlyOffer(gFirstConnection)
       .then(onOfferCreated, failed('createOffer'));
   });
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/jsep-initial-offer.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/jsep-initial-offer.https.html
index d65ee5e..88bdfcfc2 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/jsep-initial-offer.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/jsep-initial-offer.https.html
@@ -11,7 +11,7 @@
   // draft-ietf-rtcweb-jsep-24 section 5.2.1
   promise_test(async t => {
     const pc = new RTCPeerConnection();
-    const offer = await pc.createOffer({offerToReceiveVideo: true});
+    const offer = await generateVideoReceiveOnlyOffer(pc);
     let offer_lines = offer.sdp.split('\r\n');
     // The first 3 lines are dictated by JSEP.
     assert_equals(offer_lines[0], "v=0");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-1000-reason.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-1000-reason.any.js
index 1517db2..e8b49b58 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-1000-reason.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-1000-reason.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create WebSocket - Close the Connection - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create WebSocket - Close the Connection - close(1000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
+var testOpen = async_test("Create WebSocket - Close the Connection - Connection should be opened");
+var testClose = async_test("Create WebSocket - Close the Connection - close(1000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
 
 var wsocket = CreateWebSocket(false, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-1000.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-1000.any.js
index e052f23..bcfceff 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-1000.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-1000.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create WebSocket - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create WebSocket - Close the Connection - close(1000) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
+var testOpen = async_test("Create WebSocket - Connection should be opened");
+var testClose = async_test("Create WebSocket - Close the Connection - close(1000) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
 
 var wsocket = CreateWebSocket(false, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-Reason-124Bytes.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-Reason-124Bytes.any.js
index 97bc1bb..357748a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-Reason-124Bytes.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-Reason-124Bytes.any.js
@@ -1,6 +1,6 @@
 // META: script=websocket.sub.js
 
-var test = async_test("W3C WebSocket API - Create WebSocket - Close the Connection - close(code, 'reason more than 123 bytes') - SYNTAX_ERR is thrown");
+var test = async_test("Create WebSocket - Close the Connection - close(code, 'reason more than 123 bytes') - SYNTAX_ERR is thrown");
 
 var wsocket = CreateWebSocket(false, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-reason-unpaired-surrogates.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-reason-unpaired-surrogates.any.js
index 119a32d..0b2e681 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-reason-unpaired-surrogates.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Close-reason-unpaired-surrogates.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get opened");
-var testClose = async_test("W3C WebSocket API - Create WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get closed");
+var testOpen = async_test("Create WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get opened");
+var testClose = async_test("Create WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get closed");
 
 var wsocket = CreateWebSocket(false, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-extensions-empty.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-extensions-empty.any.js
index 82b000dd..647b7f8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-extensions-empty.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-extensions-empty.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - wsocket.extensions should be set to '' after connection is established - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - wsocket.extensions should be set to '' after connection is established - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - wsocket.extensions should be set to '' after connection is established - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - wsocket.extensions should be set to '' after connection is established - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any-expected.txt
index ab89227..a042970 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL W3C WebSocket API - Create Secure WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown assert_throws: function "function() {
+FAIL Create Secure WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown assert_throws: function "function() {
     wsocket = CreateWebSocketWithSpaceInUrl(spaceUrl)
   }" did not throw
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any.js
index 94265c6..f6ca19e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any.js
@@ -6,4 +6,4 @@
   assert_throws("SYNTAX_ERR", function() {
     wsocket = CreateWebSocketWithSpaceInUrl(spaceUrl)
   });
-}, "W3C WebSocket API - Create Secure WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown")
+}, "Create Secure WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any.worker-expected.txt
index ab89227..a042970 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any.worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-url-with-space.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL W3C WebSocket API - Create Secure WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown assert_throws: function "function() {
+FAIL Create Secure WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown assert_throws: function "function() {
     wsocket = CreateWebSocketWithSpaceInUrl(spaceUrl)
   }" did not throw
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-array-protocols.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-array-protocols.any.js
index fcaf8a3..b0ebe6a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-array-protocols.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-array-protocols.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and array of protocol strings - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Pass a valid URL and array of protocol strings - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, true);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-binaryType-blob.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-binaryType-blob.any.js
index fed88f5c..a639ee88 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-binaryType-blob.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-binaryType-blob.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - wsocket.binaryType should be set to 'blob' after connection is established - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - wsocket.binaryType should be set to 'blob' after connection is established - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - wsocket.binaryType should be set to 'blob' after connection is established - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - wsocket.binaryType should be set to 'blob' after connection is established - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-protocol-setCorrectly.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-protocol-setCorrectly.any.js
index 7ecd295..768e1bc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-protocol-setCorrectly.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-protocol-setCorrectly.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and protocol string - protocol should be set correctly - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and protocol string - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Pass a valid URL and protocol string - protocol should be set correctly - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Pass a valid URL and protocol string - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, true, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-protocol-string.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-protocol-string.any.js
index 59c77c6..e1e661c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-protocol-string.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url-protocol-string.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Check readyState is 1");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and protocol string - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Check readyState is 1");
+var testClose = async_test("Create Secure WebSocket - Pass a valid URL and protocol string - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, true, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url.any.js
index 6f1229e7..ab02a19 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-valid-url.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Pass a valid URL  - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Pass a valid URL - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Pass a valid URL  - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Pass a valid URL - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-verify-url-set-non-default-port.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-verify-url-set-non-default-port.any.js
index 755dbe2..8ed985b5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-verify-url-set-non-default-port.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-Secure-verify-url-set-non-default-port.any.js
@@ -4,4 +4,4 @@
   var urlNonDefaultPort = "wss://" + __SERVER__NAME + ":" + __NEW__SECURE__PORT + "/" + __PATH;
   var wsocket = new WebSocket(urlNonDefaultPort);
   assert_equals(wsocket.url, urlNonDefaultPort, "wsocket.url is set correctly");
-}, "W3C WebSocket API - Create Secure WebSocket - wsocket.url should be set correctly")
+}, "Create Secure WebSocket - wsocket.url should be set correctly")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-asciiSep-protocol-string.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-asciiSep-protocol-string.any.js
index cb3c3e4..37657f1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-asciiSep-protocol-string.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-asciiSep-protocol-string.any.js
@@ -6,4 +6,4 @@
   assert_throws("SYNTAX_ERR", function() {
     wsocket = CreateWebSocketWithAsciiSep(asciiWithSep)
   });
-}, "W3C WebSocket API - Create WebSocket - Pass a valid URL and a protocol string with an ascii separator character - SYNTAX_ERR is thrown")
+}, "Create WebSocket - Pass a valid URL and a protocol string with an ascii separator character - SYNTAX_ERR is thrown")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-non-absolute-url.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-non-absolute-url.any.js
index 369557e..9ac9707 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-non-absolute-url.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-non-absolute-url.any.js
@@ -5,4 +5,4 @@
   assert_throws("SYNTAX_ERR", function() {
     wsocket = CreateWebSocketNonAbsolute()
   });
-}, "W3C WebSocket API - Create WebSocket - Pass a non absolute URL - SYNTAX_ERR is thrown")
+}, "Create WebSocket - Pass a non absolute URL - SYNTAX_ERR is thrown")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-nonAscii-protocol-string.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-nonAscii-protocol-string.any.js
index 39be9f458..6316994 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-nonAscii-protocol-string.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-nonAscii-protocol-string.any.js
@@ -6,4 +6,4 @@
   assert_throws("SYNTAX_ERR", function() {
     wsocket = CreateWebSocketNonAsciiProtocol(nonAsciiProtocol)
   });
-}, "W3C WebSocket API - Create WebSocket - Pass a valid URL and a protocol string with non-ascii values - SYNTAX_ERR is thrown")
+}, "Create WebSocket - Pass a valid URL and a protocol string with non-ascii values - SYNTAX_ERR is thrown")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocol-with-space.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocol-with-space.any.js
index b3c14d8e..18f6815 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocol-with-space.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocol-with-space.any.js
@@ -5,4 +5,4 @@
   assert_throws("SYNTAX_ERR", function() {
     wsocket = CreateWebSocketWithSpaceInProtocol("ec ho")
   });
-}, "W3C WebSocket API - Create WebSocket - Pass a valid URL and a protocol string with a space in it - SYNTAX_ERR is thrown")
+}, "Create WebSocket - Pass a valid URL and a protocol string with a space in it - SYNTAX_ERR is thrown")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt
index 2687b40..fe44654 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL W3C WebSocket API - Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown assert_throws: function "function() {
+FAIL Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown assert_throws: function "function() {
     wsocket = CreateWebSocketWithRepeatedProtocolsCaseInsensitive()
   }" did not throw
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.js
index 16f9975..1d59015 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.js
@@ -5,4 +5,4 @@
   assert_throws("SYNTAX_ERR", function() {
     wsocket = CreateWebSocketWithRepeatedProtocolsCaseInsensitive()
   });
-}, "W3C WebSocket API - Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown")
+}, "Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.worker-expected.txt
index 2687b40..fe44654 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL W3C WebSocket API - Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown assert_throws: function "function() {
+FAIL Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown assert_throws: function "function() {
     wsocket = CreateWebSocketWithRepeatedProtocolsCaseInsensitive()
   }" did not throw
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated.any.js
index 624d453..bdaf560 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-protocols-repeated.any.js
@@ -5,4 +5,4 @@
   assert_throws("SYNTAX_ERR", function() {
     wsocket = CreateWebSocketWithRepeatedProtocols()
   });
-}, "W3C WebSocket API - Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values - SYNTAX_ERR is thrown")
+}, "Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values - SYNTAX_ERR is thrown")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-array-protocols.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-array-protocols.any.js
index dde0303a..f816133 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-array-protocols.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-array-protocols.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed");
+var testOpen = async_test("Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be opened");
+var testClose = async_test("Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed");
 
 var wsocket = CreateWebSocket(false, false, true);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-protocol-empty.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-protocol-empty.any.js
index 8682e4a..0bf2ff5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-protocol-empty.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-protocol-empty.any.js
@@ -4,4 +4,4 @@
   var wsocket = CreateWebSocket(false, true, false);
   assert_equals(wsocket.protocol, "", "protocol should be empty");
   wsocket.close();
-}, "W3C WebSocket API - Create WebSocket - wsocket.protocol should be empty before connection is established")
+}, "Create WebSocket - wsocket.protocol should be empty before connection is established")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-protocol.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-protocol.any.js
index 85e870f..2dcae27 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-protocol.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url-protocol.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create WebSocket - Pass a valid URL and a protocol string - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create WebSocket - Pass a valid URL and a protocol string - Connection should be closed");
+var testOpen = async_test("Create WebSocket - Pass a valid URL and a protocol string - Connection should be opened");
+var testClose = async_test("Create WebSocket - Pass a valid URL and a protocol string - Connection should be closed");
 
 var wsocket = CreateWebSocket(false, true, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url.any.js
index 9a43dcc..b84e2118 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-valid-url.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create WebSocket - Pass a valid URL - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create WebSocket - Pass a valid URL - Connection should be closed");
+var testOpen = async_test("Create WebSocket - Pass a valid URL - Connection should be opened");
+var testClose = async_test("Create WebSocket - Pass a valid URL - Connection should be closed");
 
 var wsocket = CreateWebSocket(false, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-verify-url-set-non-default-port.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-verify-url-set-non-default-port.any.js
index 5548fd1..5611671 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-verify-url-set-non-default-port.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-verify-url-set-non-default-port.any.js
@@ -4,4 +4,4 @@
   var urlNonDefaultPort = "ws://" + __SERVER__NAME + ":" + __NEW__PORT + "/" + __PATH;
   var wsocket = new WebSocket(urlNonDefaultPort);
   assert_equals(wsocket.url, urlNonDefaultPort, "wsocket.url is set correctly");
-}, "W3C WebSocket API - Create WebSocket - wsocket.url should be set correctly");
+}, "Create WebSocket - wsocket.url should be set correctly");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-wrong-scheme.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-wrong-scheme.any.js
index 506f81c..78ff739 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-wrong-scheme.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Create-wrong-scheme.any.js
@@ -5,4 +5,4 @@
   assert_throws("SYNTAX_ERR", function() {
     wsocket = CreateWebSocketNonWsScheme()
   });
-}, "W3C WebSocket API - Create WebSocket - Pass a URL with a non ws/wss scheme - SYNTAX_ERR is thrown")
+}, "Create WebSocket - Pass a URL with a non ws/wss scheme - SYNTAX_ERR is thrown")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000-reason.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000-reason.any.js
index 4d3f67c..8f28987 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000-reason.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000-reason.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(1000, reason) - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(1000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - close(1000, reason) - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - close(1000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000-verify-code.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000-verify-code.any.js
index 87ba407..a812e0d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000-verify-code.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000-verify-code.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(1000, reason) - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(1000, reason) - event.code == 1000 and event.reason = 'Clean Close'");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - close(1000, reason) - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - close(1000, reason) - event.code == 1000 and event.reason = 'Clean Close'");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000.any.js
index 67f4e05..2c7e5e9c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1000.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(1000) - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(1000) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - close(1000) - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - close(1000) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1005-verify-code.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1005-verify-code.any.js
index a7c72eaf..aa8e37ea 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1005-verify-code.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1005-verify-code.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close() - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close() - return close code is 1005 - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - close() - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - close() - return close code is 1005 - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1005.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1005.any.js
index ddb2c18..f5f2cbf6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1005.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-1005.any.js
@@ -1,6 +1,6 @@
 // META: script=websocket.sub.js
 
-var test = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(1005) - see '7.1.5.  The WebSocket Connection Close Code' in http://www.ietf.org/rfc/rfc6455.txt");
+var test = async_test("Create Secure WebSocket - Close the Connection - close(1005) - see '7.1.5.  The WebSocket Connection Close Code' in http://www.ietf.org/rfc/rfc6455.txt");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-2999-reason.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-2999-reason.any.js
index 0fa198eb..ee1c7566 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-2999-reason.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-2999-reason.any.js
@@ -1,6 +1,6 @@
 // META: script=websocket.sub.js
 
-var test = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(2999, reason) - INVALID_ACCESS_ERR is thrown");
+var test = async_test("Create Secure WebSocket - Close the Connection - close(2999, reason) - INVALID_ACCESS_ERR is thrown");
 
 var wsocket = CreateWebSocket(true, false, false);
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-3000-reason.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-3000-reason.any.js
index 6640ddc..d2266dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-3000-reason.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-3000-reason.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(3000, reason) - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(3000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - close(3000, reason) - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - close(3000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-3000-verify-code.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-3000-verify-code.any.js
index 5b122d4c3..bbd5bbb 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-3000-verify-code.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-3000-verify-code.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(3000, reason) - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(3000, reason) - verify return code is 3000 - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - close(3000, reason) - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - close(3000, reason) - verify return code is 3000 - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-4999-reason.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-4999-reason.any.js
index d57899e..b8429cf 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-4999-reason.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-4999-reason.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(4999, reason) - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(4999, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - close(4999, reason) - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - close(4999, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-Reason-124Bytes.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-Reason-124Bytes.any.js
index 826cb6ec..0786c29 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-Reason-124Bytes.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-Reason-124Bytes.any.js
@@ -1,6 +1,6 @@
 // META: script=websocket.sub.js
 
-var test = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(code, 'reason more than 123 bytes') - SYNTAX_ERR is thrown");
+var test = async_test("Create Secure WebSocket - Close the Connection - close(code, 'reason more than 123 bytes') - SYNTAX_ERR is thrown");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-Reason-Unpaired-surrogates.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-Reason-Unpaired-surrogates.any.js
index fdc62c4..a7b9ea4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-Reason-Unpaired-surrogates.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-Reason-Unpaired-surrogates.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get closed");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-onlyReason.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-onlyReason.any.js
index 79f79b5..029b8d8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-onlyReason.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-onlyReason.any.js
@@ -1,6 +1,6 @@
 // META: script=websocket.sub.js
 
-var test = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - close(only reason) - INVALID_ACCESS_ERR is thrown");
+var test = async_test("Create Secure WebSocket - Close the Connection - close(only reason) - INVALID_ACCESS_ERR is thrown");
 
 var wsocket = CreateWebSocket(true, false, false);
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-readyState-Closed.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-readyState-Closed.any.js
index 32797448..0dd46f1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-readyState-Closed.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-readyState-Closed.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Close the Connection - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Close the Connection - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-readyState-Closing.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-readyState-Closing.any.js
index b183474..690ca83 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-readyState-Closing.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-readyState-Closing.any.js
@@ -1,6 +1,6 @@
 // META: script=websocket.sub.js
 
-var test = async_test("W3C WebSocket API - Create Secure WebSocket - Close the Connection - readyState should be in CLOSING state just before onclose is called");
+var test = async_test("Create Secure WebSocket - Close the Connection - readyState should be in CLOSING state just before onclose is called");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-server-initiated-close.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-server-initiated-close.any.js
index 8531b31..800fbc4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-server-initiated-close.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Close-server-initiated-close.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create Secure WebSocket - Server initiated Close - Client sends back a CLOSE - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create Secure WebSocket - Server initiated Close - Client sends back a CLOSE - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
+var testOpen = async_test("Create Secure WebSocket - Server initiated Close - Client sends back a CLOSE - Connection should be opened");
+var testClose = async_test("Create Secure WebSocket - Server initiated Close - Client sends back a CLOSE - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 var isOpenCalled = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-65K-data.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-65K-data.any.js
index daa937a..ca87653 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-65K-data.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-65K-data.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send  65K data on a Secure WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send 65K data on a Secure WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send 65K data on a Secure WebSocket - Connection should be closed");
+var testOpen = async_test("Send  65K data on a Secure WebSocket - Connection should be opened");
+var testMessage = async_test("Send 65K data on a Secure WebSocket - Message should be received");
+var testClose = async_test("Send 65K data on a Secure WebSocket - Connection should be closed");
 
 var data = "";
 var wsocket = CreateWebSocket(true, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-65K-arraybuffer.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-65K-arraybuffer.any.js
index 17859e5..0627e75 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-65K-arraybuffer.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-65K-arraybuffer.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send 65K binary data on a Secure WebSocket - ArrayBuffer - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send 65K binary data on a Secure WebSocket - ArrayBuffer - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send 65K binary data on a Secure WebSocket - ArrayBuffer - Connection should be closed");
+var testOpen = async_test("Send 65K binary data on a Secure WebSocket - ArrayBuffer - Connection should be opened");
+var testMessage = async_test("Send 65K binary data on a Secure WebSocket - ArrayBuffer - Message should be received");
+var testClose = async_test("Send 65K binary data on a Secure WebSocket - ArrayBuffer - Connection should be closed");
 
 var data = "";
 var datasize = 65000;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybuffer.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybuffer.any.js
index 6e4c08d..5778a24 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybuffer.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybuffer.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a Secure WebSocket - ArrayBuffer - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a Secure WebSocket - ArrayBuffer - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a Secure WebSocket - ArrayBuffer - Connection should be closed");
+var testOpen = async_test("Send binary data on a Secure WebSocket - ArrayBuffer - Connection should be opened");
+var testMessage = async_test("Send binary data on a Secure WebSocket - ArrayBuffer - Message should be received");
+var testClose = async_test("Send binary data on a Secure WebSocket - ArrayBuffer - Connection should be closed");
 
 var data = "";
 var datasize = 15;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-float32.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-float32.any.js
index 9825d34..2672319 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-float32.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-float32.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Float32Array - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Float32Array - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Float32Array - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Float32Array - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Float32Array - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Float32Array - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-float64.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-float64.any.js
index 4dcac40..993d5fc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-float64.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-float64.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Float64Array - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Float64Array - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Float64Array - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Float64Array - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Float64Array - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Float64Array - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-int32.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-int32.any.js
index 655af21..58fe02b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-int32.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-int32.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int32Array - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int32Array - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int32Array - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Int32Array - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Int32Array - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Int32Array - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint16-offset-length.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint16-offset-length.any.js
index 16f050f..26231b41 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint16-offset-length.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint16-offset-length.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint32-offset.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint32-offset.any.js
index 8976b3dc..d2995ae5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint32-offset.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint32-offset.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint8-offset-length.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint8-offset-length.any.js
index 9e9d1b5..90d9d9d51 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint8-offset-length.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint8-offset-length.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint8-offset.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint8-offset.any.js
index f563cec..ce2e608 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint8-offset.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-arraybufferview-uint8-offset.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-blob.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-blob.any.js
index 8bf0f12e..f532dfb 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-blob.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-binary-blob.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a Secure WebSocket - Blob - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a Secure WebSocket - Blob - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a Secure WebSocket - Blob - Connection should be closed");
+var testOpen = async_test("Send binary data on a Secure WebSocket - Blob - Connection should be opened");
+var testMessage = async_test("Send binary data on a Secure WebSocket - Blob - Message should be received");
+var testClose = async_test("Send binary data on a Secure WebSocket - Blob - Connection should be closed");
 
 var data = "";
 var datasize = 65000;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-data.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-data.any.js
index 04c720d..78de87c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-data.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-data.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send  data on a Secure WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send data on a Secure WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send data on a Secure WebSocket - Connection should be closed");
+var testOpen = async_test("Send  data on a Secure WebSocket - Connection should be opened");
+var testMessage = async_test("Send data on a Secure WebSocket - Message should be received");
+var testClose = async_test("Send data on a Secure WebSocket - Connection should be closed");
 
 var data = "Message to send";
 var wsocket = CreateWebSocket(true, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-null.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-null.any.js
index 7c374c2..bf644c6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-null.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-null.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send null data on a Secure WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send null data on a Secure WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send null data on a Secure WebSocket - Connection should be closed");
+var testOpen = async_test("Send null data on a Secure WebSocket - Connection should be opened");
+var testMessage = async_test("Send null data on a Secure WebSocket - Message should be received");
+var testClose = async_test("Send null data on a Secure WebSocket - Connection should be closed");
 
 var data = null;
 var nullReturned = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-paired-surrogates.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-paired-surrogates.any.js
index 073f064..f53b9d52 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-paired-surrogates.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-paired-surrogates.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send paired surrogates data on a Secure WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send paired surrogates data on a Secure WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send paired surrogates data on a Secure WebSocket - Connection should be closed");
+var testOpen = async_test("Send paired surrogates data on a Secure WebSocket - Connection should be opened");
+var testMessage = async_test("Send paired surrogates data on a Secure WebSocket - Message should be received");
+var testClose = async_test("Send paired surrogates data on a Secure WebSocket - Connection should be closed");
 
 var data = "\uD801\uDC07";
 var wsocket = CreateWebSocket(true, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-unicode-data.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-unicode-data.any.js
index a3518c1..7156698 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-unicode-data.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-unicode-data.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send  unicode data on a Secure WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send unicode data on a Secure WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send unicode data on a Secure WebSocket - Connection should be closed");
+var testOpen = async_test("Send  unicode data on a Secure WebSocket - Connection should be opened");
+var testMessage = async_test("Send unicode data on a Secure WebSocket - Message should be received");
+var testClose = async_test("Send unicode data on a Secure WebSocket - Connection should be closed");
 
 var data = "¥¥¥¥¥¥";
 var wsocket = CreateWebSocket(true, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-unpaired-surrogates.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-unpaired-surrogates.any.js
index 83f3e7b..00d8589e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-unpaired-surrogates.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Secure-Send-unpaired-surrogates.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send unpaired surrogates on a Secure WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send unpaired surrogates on a Secure WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send unpaired surrogates on a Secure WebSocket - Connection should be closed");
+var testOpen = async_test("Send unpaired surrogates on a Secure WebSocket - Connection should be opened");
+var testMessage = async_test("Send unpaired surrogates on a Secure WebSocket - Message should be received");
+var testClose = async_test("Send unpaired surrogates on a Secure WebSocket - Connection should be closed");
 
 var data = "\uD807";
 var replacementChar = "\uFFFD";
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-0byte-data.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-0byte-data.any.js
index 131a19d7..b335a1fc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-0byte-data.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-0byte-data.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send 0 byte data on a WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send 0 byte data on a WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send 0 byte data on a WebSocket - Connection should be closed");
+var testOpen = async_test("Send 0 byte data on a WebSocket - Connection should be opened");
+var testMessage = async_test("Send 0 byte data on a WebSocket - Message should be received");
+var testClose = async_test("Send 0 byte data on a WebSocket - Connection should be closed");
 
 var data = "";
 var wsocket = CreateWebSocket(false, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-65K-data.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-65K-data.any.js
index 172e6ee..ad74327 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-65K-data.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-65K-data.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send 65K data on a WebSocket -  Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send 65K data on a WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send 65K data on a WebSocket - Connection should be closed");
+var testOpen = async_test("Send 65K data on a WebSocket -  Connection should be opened");
+var testMessage = async_test("Send 65K data on a WebSocket - Message should be received");
+var testClose = async_test("Send 65K data on a WebSocket - Connection should be closed");
 
 var data = "";
 var wsocket = CreateWebSocket(false, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-Unpaired-Surrogates.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-Unpaired-Surrogates.any.js
index 65bb2b1..820fb31 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-Unpaired-Surrogates.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-Unpaired-Surrogates.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send unpaired surrogates on a WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send unpaired surrogates on a WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send unpaired surrogates on a WebSocket - Connection should be closed");
+var testOpen = async_test("Send unpaired surrogates on a WebSocket - Connection should be opened");
+var testMessage = async_test("Send unpaired surrogates on a WebSocket - Message should be received");
+var testClose = async_test("Send unpaired surrogates on a WebSocket - Connection should be closed");
 
 var data = "\uD807";
 var replacementChar = "\uFFFD";
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-before-open.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-before-open.any.js
index 101a1a2..b2f1ea9c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-before-open.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-before-open.any.js
@@ -5,4 +5,4 @@
   assert_throws("INVALID_STATE_ERR", function() {
     wsocket.send("Message to send")
   });
-}, "W3C WebSocket API - Send data on a WebSocket before connection is opened - INVALID_STATE_ERR is returned")
+}, "Send data on a WebSocket before connection is opened - INVALID_STATE_ERR is returned")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-65K-arraybuffer.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-65K-arraybuffer.any.js
index f446a25..394a902c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-65K-arraybuffer.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-65K-arraybuffer.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send 65K binary data on a WebSocket - ArrayBuffer - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send 65K binary data on a WebSocket - ArrayBuffer - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send 65K binary data on a WebSocket - ArrayBuffer - Connection should be closed");
+var testOpen = async_test("Send 65K binary data on a WebSocket - ArrayBuffer - Connection should be opened");
+var testMessage = async_test("Send 65K binary data on a WebSocket - ArrayBuffer - Message should be received");
+var testClose = async_test("Send 65K binary data on a WebSocket - ArrayBuffer - Connection should be closed");
 
 var data = "";
 var datasize = 65000;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybuffer.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybuffer.any.js
index 6205143..24618293 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybuffer.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybuffer.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBuffer - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBuffer - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBuffer - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBuffer - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBuffer - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBuffer - Connection should be closed");
 
 var data = "";
 var datasize = 15;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.js
index 7022668..a32b139 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybufferview-int8.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybufferview-int8.any.js
index 242c8c6..d9e50b51 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybufferview-int8.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-arraybufferview-int8.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int8Array - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int8Array - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - ArrayBufferView - Int8Array - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - ArrayBufferView - Int8Array - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - ArrayBufferView - Int8Array - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - ArrayBufferView - Int8Array - Connection should be closed");
 
 var data = "";
 var datasize = 8;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-blob.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-blob.any.js
index ee6486e..12d2a837 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-blob.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-binary-blob.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send binary data on a WebSocket - Blob - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send binary data on a WebSocket - Blob - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send binary data on a WebSocket - Blob - Connection should be closed");
+var testOpen = async_test("Send binary data on a WebSocket - Blob - Connection should be opened");
+var testMessage = async_test("Send binary data on a WebSocket - Blob - Message should be received");
+var testClose = async_test("Send binary data on a WebSocket - Blob - Connection should be closed");
 
 var data = "";
 var datasize = 65000;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-data.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-data.any.js
index 487393b..98a866b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-data.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-data.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send data on a WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send data on a WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send data on a WebSocket - Connection should be closed");
+var testOpen = async_test("Send data on a WebSocket - Connection should be opened");
+var testMessage = async_test("Send data on a WebSocket - Message should be received");
+var testClose = async_test("Send data on a WebSocket - Connection should be closed");
 
 var data = "Message to send";
 var wsocket = CreateWebSocket(false, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-data.worker.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-data.worker.js
index f607fd7..6d4f693d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-data.worker.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-data.worker.js
@@ -16,6 +16,6 @@
             done();
     }), true);
 
-}, "W3C WebSocket API - Send data on a WebSocket in a Worker")
+}, "Send data on a WebSocket in a Worker")
 
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-null.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-null.any.js
index 8d8d6b7..ac1a1b29 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-null.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-null.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send null data on a WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send null data on a WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send null data on a WebSocket - Connection should be closed");
+var testOpen = async_test("Send null data on a WebSocket - Connection should be opened");
+var testMessage = async_test("Send null data on a WebSocket - Message should be received");
+var testClose = async_test("Send null data on a WebSocket - Connection should be closed");
 
 var data = null;
 var nullReturned = false;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-paired-surrogates.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-paired-surrogates.any.js
index f12b001..003b0401 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-paired-surrogates.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-paired-surrogates.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send (paired surrogates) data on a WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send (paired surrogates) data on a WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send (paired surrogates) data on a WebSocket - Connection should be closed");
+var testOpen = async_test("Send (paired surrogates) data on a WebSocket - Connection should be opened");
+var testMessage = async_test("Send (paired surrogates) data on a WebSocket - Message should be received");
+var testClose = async_test("Send (paired surrogates) data on a WebSocket - Connection should be closed");
 
 var data = "\uD801\uDC07";
 var wsocket = CreateWebSocket(false, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-unicode-data.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-unicode-data.any.js
index ce2b9a0e..5929254 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-unicode-data.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/Send-unicode-data.any.js
@@ -1,8 +1,8 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Send unicode data on a WebSocket - Connection should be opened");
-var testMessage = async_test("W3C WebSocket API - Send unicode data on a WebSocket - Message should be received");
-var testClose = async_test("W3C WebSocket API - Send unicode data on a WebSocket - Connection should be closed");
+var testOpen = async_test("Send unicode data on a WebSocket - Connection should be opened");
+var testMessage = async_test("Send unicode data on a WebSocket - Message should be received");
+var testClose = async_test("Send unicode data on a WebSocket - Connection should be closed");
 
 var data = "¥¥¥¥¥¥";
 var wsocket = CreateWebSocket(false, false, false);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/binaryType-wrong-value.any.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/binaryType-wrong-value.any.js
index 6030608..61ac6a1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/binaryType-wrong-value.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/binaryType-wrong-value.any.js
@@ -1,7 +1,7 @@
 // META: script=websocket.sub.js
 
-var testOpen = async_test("W3C WebSocket API - Create WebSocket - set binaryType to something other than blob or arraybuffer - SYNTAX_ERR is returned - Connection should be opened");
-var testClose = async_test("W3C WebSocket API - Create WebSocket - set binaryType to something other than blob or arraybuffer - SYNTAX_ERR is returned - Connection should be closed");
+var testOpen = async_test("Create WebSocket - set binaryType to something other than blob or arraybuffer - SYNTAX_ERR is returned - Connection should be opened");
+var testClose = async_test("Create WebSocket - set binaryType to something other than blob or arraybuffer - SYNTAX_ERR is returned - Connection should be closed");
 
 var wsocket = CreateWebSocket(true, false, false);
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/websockets/opening-handshake/003-sets-origin.worker.js b/third_party/WebKit/LayoutTests/external/wpt/websockets/opening-handshake/003-sets-origin.worker.js
index 8746cca..cc54f2b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/websockets/opening-handshake/003-sets-origin.worker.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/websockets/opening-handshake/003-sets-origin.worker.js
@@ -14,5 +14,5 @@
     ws.close();
   })
   ws.onerror = ws.onclose = t.unreached_func();
-}, "W3C WebSocket API - origin set in a Worker");
+}, "origin set in a Worker");
 done();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/xhr/send-content-type-charset-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/xhr/send-content-type-charset-expected.txt
index 068a31b..6269b565 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/xhr/send-content-type-charset-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/xhr/send-content-type-charset-expected.txt
@@ -12,7 +12,8 @@
 PASS charset given but wrong, fix it (known MIME, actual charset)
 FAIL Multiple non-UTF-8 charset parameters deduplicate, bogus parameter dropped assert_equals: expected "text/x-pink-unicorn;charset=UTF-8" but got "text/x-pink-unicorn; charset=UTF-8; charset=UTF-8; notrelated; charset=UTF-8"
 PASS No content type set, give MIME and charset
-FAIL charset with space that is UTF-8 does not change assert_equals: expected "text/plain;charset= utf-8" but got "text/plain;charset= UTF-8"
+FAIL charset with leading space that is UTF-8 does change assert_equals: expected "text/plain;charset=UTF-8" but got "text/plain;charset= UTF-8"
+FAIL charset with trailing space that is UTF-8 does not change assert_equals: expected "text/plain;charset=utf-8 ;x=x" but got "text/plain;charset=UTF-8 ;x=x"
 FAIL charset in double quotes that is UTF-8 does not change assert_equals: expected "text/plain;charset=\"utf-8\"" but got "text/plain;charset=\"UTF-8\""
 FAIL charset in double quotes with space assert_equals: expected "text/plain;charset=UTF-8" but got "text/plain;charset=\" UTF-8\""
 FAIL charset in double quotes with backslashes that is UTF-8 does not change assert_equals: expected "text/plain;charset=\"u\\t\f-8\"" but got "text/plain;charset=\"UTF-8\""
diff --git a/third_party/WebKit/LayoutTests/external/wpt/xhr/send-content-type-charset.htm b/third_party/WebKit/LayoutTests/external/wpt/xhr/send-content-type-charset.htm
index 0a91e1f..a968bb3f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/xhr/send-content-type-charset.htm
+++ b/third_party/WebKit/LayoutTests/external/wpt/xhr/send-content-type-charset.htm
@@ -88,8 +88,12 @@
       )
       request(
         "text/plain;charset= utf-8",
-        "text/plain;charset= utf-8",
-        "charset with space that is UTF-8 does not change")
+        "text/plain;charset=UTF-8",
+        "charset with leading space that is UTF-8 does change")
+      request(
+        "text/plain;charset=utf-8 ;x=x",
+        "text/plain;charset=utf-8 ;x=x",
+        "charset with trailing space that is UTF-8 does not change");
       request(
         "text/plain;charset=\"utf-8\"",
         "text/plain;charset=\"utf-8\"",
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt
index 411e588..e1600da8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt
@@ -58,7 +58,7 @@
         columnNumber : 0
         consumedCacheSize : <number>
         lineNumber : 0
-        notStreamedReason : "resource no longer alive"
+        notStreamedReason : "already used streamed data"
         streamed : <boolean>
         url : .../devtools/resources/v8-cache-script.js
     }
diff --git a/third_party/WebKit/LayoutTests/media/mediasession/mojo/callback-alive-after-gc.html b/third_party/WebKit/LayoutTests/media/mediasession/mojo/callback-alive-after-gc.html
index 5c5680a..7f516044 100644
--- a/third_party/WebKit/LayoutTests/media/mediasession/mojo/callback-alive-after-gc.html
+++ b/third_party/WebKit/LayoutTests/media/mediasession/mojo/callback-alive-after-gc.html
@@ -4,6 +4,7 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/gc.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/media_session/public/mojom/media_session.mojom.js"></script>
 <script src="file:///gen/third_party/blink/public/platform/modules/mediasession/media_session.mojom.js"></script>
 <script src="resources/mediasessionservice-mock.js"></script>
 <script src="resources/utils.js"></script>
@@ -16,7 +17,7 @@
   mock.setClientCallback(_ => {
     gc();
     setTimeout(_ => {
-      mock.getClient().didReceiveAction(MediaSessionAction.PLAY);
+      mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kPlay);
     });
   });
   window.navigator.mediaSession.setActionHandler("play", _ => { t.done(); });
diff --git a/third_party/WebKit/LayoutTests/media/mediasession/mojo/media-control-action-reaches-client.html b/third_party/WebKit/LayoutTests/media/mediasession/mojo/media-control-action-reaches-client.html
index 77aa6c1..4640c19 100644
--- a/third_party/WebKit/LayoutTests/media/mediasession/mojo/media-control-action-reaches-client.html
+++ b/third_party/WebKit/LayoutTests/media/mediasession/mojo/media-control-action-reaches-client.html
@@ -3,6 +3,7 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/media_session/public/mojom/media_session.mojom.js"></script>
 <script src="file:///gen/third_party/blink/public/platform/modules/mediasession/media_session.mojom.js"></script>
 <script src="resources/mediasessionservice-mock.js"></script>
 <script src="resources/utils.js"></script>
@@ -42,12 +43,12 @@
   window.navigator.mediaSession.setActionHandler(
       "seekforward", t.step_func(checkExpectation.bind(null, t, "seekforward")));
 
-  mock.getClient().didReceiveAction(MediaSessionAction.PLAY);
-  mock.getClient().didReceiveAction(MediaSessionAction.PAUSE);
-  mock.getClient().didReceiveAction(MediaSessionAction.PREVIOUS_TRACK);
-  mock.getClient().didReceiveAction(MediaSessionAction.NEXT_TRACK);
-  mock.getClient().didReceiveAction(MediaSessionAction.SEEK_BACKWARD);
-  mock.getClient().didReceiveAction(MediaSessionAction.SEEK_FORWARD);
+  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kPlay);
+  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kPause);
+  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kPreviousTrack);
+  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kNextTrack);
+  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kSeekBackward);
+  mock.getClient().didReceiveAction(mediaSession.mojom.MediaSessionAction.kSeekForward);
 }
 
 // Use async_test to do asynchronous setup since setup() only works for
diff --git a/third_party/WebKit/LayoutTests/media/mediasession/mojo/media-control-set-handler-notifies-service.html b/third_party/WebKit/LayoutTests/media/mediasession/mojo/media-control-set-handler-notifies-service.html
index 12db1a48..2806f8ce 100644
--- a/third_party/WebKit/LayoutTests/media/mediasession/mojo/media-control-set-handler-notifies-service.html
+++ b/third_party/WebKit/LayoutTests/media/mediasession/mojo/media-control-set-handler-notifies-service.html
@@ -3,6 +3,7 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/media_session/public/mojom/media_session.mojom.js"></script>
 <script src="file:///gen/third_party/blink/public/platform/modules/mediasession/media_session.mojom.js"></script>
 <script src="resources/mediasessionservice-mock.js"></script>
 <script src="resources/utils.js"></script>
@@ -16,24 +17,24 @@
 function getExpectations() {
   if (!expectations) {
     expectations = [
-      [ MediaSessionAction.PLAY, true ],
-      [ MediaSessionAction.PAUSE, true ],
-      [ MediaSessionAction.PREVIOUS_TRACK, true ],
-      [ MediaSessionAction.NEXT_TRACK, true ],
-      [ MediaSessionAction.SEEK_BACKWARD, true ],
-      [ MediaSessionAction.SEEK_FORWARD, true ],
-      [ MediaSessionAction.PLAY, false ],
-      [ MediaSessionAction.PAUSE, false ],
-      [ MediaSessionAction.PREVIOUS_TRACK, false ],
-      [ MediaSessionAction.NEXT_TRACK, false ],
-      [ MediaSessionAction.SEEK_BACKWARD, false ],
-      [ MediaSessionAction.SEEK_FORWARD, false ],
-      [ MediaSessionAction.PLAY, true ],
-      [ MediaSessionAction.PAUSE, true ],
-      [ MediaSessionAction.PREVIOUS_TRACK, true ],
-      [ MediaSessionAction.NEXT_TRACK, true ],
-      [ MediaSessionAction.SEEK_BACKWARD, true ],
-      [ MediaSessionAction.SEEK_FORWARD, true ],
+      [ mediaSession.mojom.MediaSessionAction.kPlay, true ],
+      [ mediaSession.mojom.MediaSessionAction.kPause, true ],
+      [ mediaSession.mojom.MediaSessionAction.kPreviousTrack, true ],
+      [ mediaSession.mojom.MediaSessionAction.kNextTrack, true ],
+      [ mediaSession.mojom.MediaSessionAction.kSeekBackward, true ],
+      [ mediaSession.mojom.MediaSessionAction.kSeekForward, true ],
+      [ mediaSession.mojom.MediaSessionAction.kPlay, false ],
+      [ mediaSession.mojom.MediaSessionAction.kPause, false ],
+      [ mediaSession.mojom.MediaSessionAction.kPreviousTrack, false ],
+      [ mediaSession.mojom.MediaSessionAction.kNextTrack, false ],
+      [ mediaSession.mojom.MediaSessionAction.kSeekBackward, false ],
+      [ mediaSession.mojom.MediaSessionAction.kSeekForward, false ],
+      [ mediaSession.mojom.MediaSessionAction.kPlay, true ],
+      [ mediaSession.mojom.MediaSessionAction.kPause, true ],
+      [ mediaSession.mojom.MediaSessionAction.kPreviousTrack, true ],
+      [ mediaSession.mojom.MediaSessionAction.kNextTrack, true ],
+      [ mediaSession.mojom.MediaSessionAction.kSeekBackward, true ],
+      [ mediaSession.mojom.MediaSessionAction.kSeekForward, true ],
     ];
   }
   return expectations;
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png b/third_party/WebKit/LayoutTests/platform/linux/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.png b/third_party/WebKit/LayoutTests/platform/linux/http/tests/images/feature-policy-legacy-formats-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/http/tests/images/feature-policy-legacy-formats-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-unoptimized-images-cached-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/http/tests/images/feature-policy-unoptimized-images-cached-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/images/feature-policy-unoptimized-images-cached-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/http/tests/images/feature-policy-unoptimized-images-cached-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png b/third_party/WebKit/LayoutTests/platform/win/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-legacy-formats-expected.png b/third_party/WebKit/LayoutTests/platform/win/http/tests/images/feature-policy-legacy-formats-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-legacy-formats-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/http/tests/images/feature-policy-legacy-formats-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-unoptimized-images-cached-image-expected.png b/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-unoptimized-images-cached-image-expected.png
deleted file mode 100644
index 44616b9..0000000
--- a/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-unoptimized-images-cached-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
index 39aed2a..3ee7bf7 100644
--- a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
@@ -3,8 +3,5 @@
 PASS createOffer() and then setLocalDescription() should succeed
 PASS createOffer() after connection is closed should reject with InvalidStateError
 FAIL When media stream is added when createOffer() is running in parallel, the result offer should contain the new media stream assert_equals: Expect m=audio line to be found in offer SDP expected 1 but got 0
-PASS createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers
-PASS createOffer() with offerToReceiveVideo should add video line to all subsequent created offers
-PASS createOffer() with offerToReceiveAudio:true then offerToReceiveVideo:true should have result offer with both audio and video line
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
index 1f3c3e8..e81abfd 100644
--- a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL RTCRtpReceiver.prototype.getParameters pc.getParameters is not a function
+FAIL RTCRtpReceiver.prototype.getParameters receiver.getParameters is not a function
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
index 0ad7465..f7302af8 100644
--- a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
+FAIL sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.remoteId to be string expected "string" but got "undefined"
 FAIL sender.getStats() via addTrack should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.remoteId to be string expected "string" but got "undefined"
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
new file mode 100644
index 0000000..9714f8a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers
+PASS createOffer() with offerToReceiveVideo should add video line to all subsequent created offers
+PASS createOffer() with offerToReceiveAudio:true, then with offerToReceiveVideo:true, should have result offer with both audio and video line
+PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
+FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+PASS offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists
+FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" assert_equals: Expect transceiver to have "inactive" direction expected "inactive" but got "recvonly"
+FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+PASS createOffer() with offerToReceiveVideo set to false should not create a transceiver
+FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+PASS offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists
+FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" assert_equals: Expect transceiver to have "inactive" direction expected "inactive" but got "recvonly"
+FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers assert_equals: Expect pc to have two transceivers expected 2 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
new file mode 100644
index 0000000..0485f08a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS checkAddTransceiverWithStream
+FAIL checkAddTransceiverWithOfferToReceiveAudio assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false}]" but got "[]"
+FAIL checkAddTransceiverWithOfferToReceiveVideo assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
+FAIL checkAddTransceiverWithOfferToReceiveBoth assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false},{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/onaddstream.https-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
new file mode 100644
index 0000000..6742df7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS Check onaddstream
+Harness: the test ran to completion.
+
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index f72390c..4a51d63 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -536,7 +536,7 @@
 }
 
 android_aar_prebuilt("com_google_android_play_core_java") {
-  aar_path = "libs/com_google_android_play_core/core-1.3.0.aar"
+  aar_path = "libs/com_google_android_play_core/core-1.3.5.aar"
   info_path =
       "libs/com_google_android_play_core/com_google_android_play_core.info"
 }
diff --git a/third_party/android_deps/libs/com_google_android_play_core/README.chromium b/third_party/android_deps/libs/com_google_android_play_core/README.chromium
index 1ff27de..c4cd62d25 100644
--- a/third_party/android_deps/libs/com_google_android_play_core/README.chromium
+++ b/third_party/android_deps/libs/com_google_android_play_core/README.chromium
@@ -1,7 +1,7 @@
 Name: 
 Short Name: core
 URL: https://developers.google.com/android/guides/setup
-Version: 1.3.0
+Version: 1.3.5
 License: Android Software Development Kit License
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/android_deps/libs/com_google_android_play_core/cipd.yaml b/third_party/android_deps/libs/com_google_android_play_core/cipd.yaml
index 2800ffe4..0f0ef5e2 100644
--- a/third_party/android_deps/libs/com_google_android_play_core/cipd.yaml
+++ b/third_party/android_deps/libs/com_google_android_play_core/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:1.3.0-cr0
+# cipd create --pkg-def cipd.yaml -tag version:1.3.5-cr0
 package: chromium/third_party/android_deps/libs/com_google_android_play_core
 description: ""
 data:
-- file: core-1.3.0.aar
+- file: core-1.3.5.aar
diff --git a/third_party/blink/common/frame/user_activation_state.cc b/third_party/blink/common/frame/user_activation_state.cc
index ff8b4fa..ace8e45 100644
--- a/third_party/blink/common/frame/user_activation_state.cc
+++ b/third_party/blink/common/frame/user_activation_state.cc
@@ -9,8 +9,7 @@
 // The expiry time should be long enough to allow network round trips even in a
 // very slow connection (to support xhr-like calls with user activation), yet
 // not too long to make an "unattneded" page feel activated.
-constexpr base::TimeDelta kActivationLifespan =
-    base::TimeDelta::FromSeconds(30);
+constexpr base::TimeDelta kActivationLifespan = base::TimeDelta::FromSeconds(1);
 
 void UserActivationState::Activate() {
   has_been_active_ = true;
diff --git a/third_party/blink/common/frame/user_activation_state_unittest.cc b/third_party/blink/common/frame/user_activation_state_unittest.cc
index 1968ed1..30bc7b47 100644
--- a/third_party/blink/common/frame/user_activation_state_unittest.cc
+++ b/third_party/blink/common/frame/user_activation_state_unittest.cc
@@ -65,12 +65,12 @@
   user_activation_state.Activate();
 
   // Right before activation expiry, both bits remain set.
-  AdvanceClock(base::TimeDelta::FromSeconds(29));
+  AdvanceClock(base::TimeDelta::FromMilliseconds(999));
   EXPECT_TRUE(user_activation_state.HasBeenActive());
   EXPECT_TRUE(user_activation_state.IsActive());
 
   // Right after activation expiry, only the transient bit gets reset.
-  AdvanceClock(base::TimeDelta::FromSeconds(1));
+  AdvanceClock(base::TimeDelta::FromMilliseconds(1));
   EXPECT_TRUE(user_activation_state.HasBeenActive());
   EXPECT_FALSE(user_activation_state.IsActive());
 }
@@ -94,17 +94,17 @@
 
   // An activation is consumable before expiry.
   user_activation_state.Activate();
-  AdvanceClock(base::TimeDelta::FromSeconds(5));
+  AdvanceClock(base::TimeDelta::FromMilliseconds(900));
   EXPECT_TRUE(user_activation_state.ConsumeIfActive());
 
   // An activation is not consumable after expiry.
   user_activation_state.Activate();
-  AdvanceClock(base::TimeDelta::FromSeconds(30));
+  AdvanceClock(base::TimeDelta::FromSeconds(1));
   EXPECT_FALSE(user_activation_state.ConsumeIfActive());
 
   // Consecutive activations within expiry is consumable only once.
   user_activation_state.Activate();
-  AdvanceClock(base::TimeDelta::FromSeconds(5));
+  AdvanceClock(base::TimeDelta::FromMilliseconds(900));
   user_activation_state.Activate();
   EXPECT_TRUE(user_activation_state.ConsumeIfActive());
   EXPECT_FALSE(user_activation_state.ConsumeIfActive());
@@ -112,7 +112,7 @@
   // Non-consecutive activations within expiry is consumable separately.
   user_activation_state.Activate();
   EXPECT_TRUE(user_activation_state.ConsumeIfActive());
-  AdvanceClock(base::TimeDelta::FromSeconds(5));
+  AdvanceClock(base::TimeDelta::FromSeconds(900));
   user_activation_state.Activate();
   EXPECT_TRUE(user_activation_state.ConsumeIfActive());
 }
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index a8e923c..86b2c69 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -729,6 +729,7 @@
   ]
   public_deps = [
     "//mojo/public/mojom/base",
+    "//services/media_session/public/mojom",
     "//ui/gfx/geometry/mojo",
     "//url/mojom:url_mojom_gurl",
   ]
diff --git a/third_party/blink/public/platform/modules/mediasession/media_session.mojom b/third_party/blink/public/platform/modules/mediasession/media_session.mojom
index a053820..f4261da0 100644
--- a/third_party/blink/public/platform/modules/mediasession/media_session.mojom
+++ b/third_party/blink/public/platform/modules/mediasession/media_session.mojom
@@ -5,21 +5,10 @@
 module blink.mojom;
 
 import "mojo/public/mojom/base/string16.mojom";
+import "services/media_session/public/mojom/media_session.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "url/mojom/url.mojom";
 
-// Spec: https://wicg.github.io/mediasession/
-enum MediaSessionAction {
-     PLAY,
-     PAUSE,
-     PREVIOUS_TRACK,
-     NEXT_TRACK,
-     SEEK_BACKWARD,
-     SEEK_FORWARD,
-
-     LAST = SEEK_FORWARD,
-};
-
 enum MediaSessionPlaybackState {
      NONE,
      PAUSED,
@@ -46,7 +35,7 @@
 interface MediaSessionClient {
   // Notifies the Blink side that a MediaSessionAction has been fired from the
   // UI or the platform.
-  DidReceiveAction(MediaSessionAction action);
+  DidReceiveAction(media_session.mojom.MediaSessionAction action);
 };
 
 interface MediaSessionService {
@@ -64,9 +53,10 @@
   // Notifies the browser that the event handler for |action| has been set,
   // browser needs to show a media button in the UI or register listeners to the
   // platform.
-  EnableAction(MediaSessionAction action);
+  EnableAction(media_session.mojom.MediaSessionAction action);
+
   // Notifies the browser that the event handler for |action| has been set,
   // browser needs to hide the media button in the UI and unregister listeners
   // from the platform.
-  DisableAction(MediaSessionAction action);
+  DisableAction(media_session.mojom.MediaSessionAction action);
 };
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 81cbe555..449a339 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -347,6 +347,19 @@
   // in the other process.
   virtual void MarkAsLoading() = 0;
 
+  // Marks the frame as loading and creates a placeholder document loader.
+  // This placeholder informs Blink that the navigation is ongoing, while it
+  // is actually being handled by the client.
+  // TODO(dgozman): remove this together with placeholder document loader.
+  virtual bool CreatePlaceholderDocumentLoader(
+      const WebURLRequest&,
+      WebFrameLoadType,
+      WebNavigationType,
+      bool is_client_redirect,
+      const base::UnguessableToken& devtools_navigation_token,
+      std::unique_ptr<WebNavigationParams>,
+      std::unique_ptr<WebDocumentLoader::ExtraData>) = 0;
+
   // Orientation Changes ----------------------------------------------------
 
   // Notify the frame that the screen orientation has changed.
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 61e8f45..c4491a2 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -64,6 +64,7 @@
 #include "third_party/blink/public/web/web_dom_message_event.h"
 #include "third_party/blink/public/web/web_form_element.h"
 #include "third_party/blink/public/web/web_frame.h"
+#include "third_party/blink/public/web/web_frame_load_type.h"
 #include "third_party/blink/public/web/web_frame_owner_properties.h"
 #include "third_party/blink/public/web/web_global_object_reuse_policy.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
@@ -341,7 +342,7 @@
     WebNavigationType navigation_type;
     WebNavigationPolicy default_policy;
     bool has_user_gesture;
-    bool replaces_current_history_item;
+    WebFrameLoadType frame_load_type;
     bool is_history_navigation_in_new_child_frame;
     bool is_client_redirect;
     WebTriggeringEventInfo triggering_event_info;
@@ -353,6 +354,7 @@
     mojo::ScopedMessagePipeHandle blob_url_token;
     base::TimeTicks input_start;
     WebString href_translate;
+    mojo::ScopedMessagePipeHandle navigation_initiator_handle;
 
     // Specify whether or not a MHTML Archive can be used to load a subframe
     // resource instead of doing a network request.
@@ -364,7 +366,7 @@
           navigation_type(kWebNavigationTypeOther),
           default_policy(kWebNavigationPolicyIgnore),
           has_user_gesture(false),
-          replaces_current_history_item(false),
+          frame_load_type(WebFrameLoadType::kStandard),
           is_history_navigation_in_new_child_frame(false),
           is_client_redirect(false),
           triggering_event_info(WebTriggeringEventInfo::kUnknown),
@@ -374,7 +376,7 @@
   };
 
   virtual WebNavigationPolicy DecidePolicyForNavigation(
-      const NavigationPolicyInfo& info) {
+      NavigationPolicyInfo& info) {
     return info.default_policy;
   }
 
@@ -406,10 +408,8 @@
   virtual void DidCreateDocumentLoader(WebDocumentLoader*) {}
 
   // A new provisional load has been started.
-  virtual void DidStartProvisionalLoad(
-      WebDocumentLoader* document_loader,
-      WebURLRequest& request,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle) {}
+  virtual void DidStartProvisionalLoad(WebDocumentLoader* document_loader,
+                                       WebURLRequest& request) {}
 
   // The provisional load failed. The WebHistoryCommitType is the commit type
   // that would have been used had the load succeeded.
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 3ab4c5bd..5603ba5 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/optional.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
 #include "third_party/blink/public/platform/web_source_location.h"
 #include "third_party/blink/public/web/web_navigation_timings.h"
@@ -19,7 +20,7 @@
 // DocumentLoader and used by the embedder.
 struct WebNavigationParams {
   WebNavigationTimings navigation_timings;
-  WebSourceLocation source_location;
+  base::Optional<WebSourceLocation> source_location;
   bool is_user_activated = false;
   std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
       service_worker_network_provider;
diff --git a/third_party/blink/public/web/web_navigation_policy.h b/third_party/blink/public/web/web_navigation_policy.h
index b4abcb9c..6d37980d 100644
--- a/third_party/blink/public/web/web_navigation_policy.h
+++ b/third_party/blink/public/web/web_navigation_policy.h
@@ -41,7 +41,6 @@
   kWebNavigationPolicyNewForegroundTab,
   kWebNavigationPolicyNewWindow,
   kWebNavigationPolicyNewPopup,
-  kWebNavigationPolicyHandledByClient,
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_element_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_element_custom.cc
index 6184abd..ee66f61 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_element_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_element_custom.cc
@@ -37,7 +37,7 @@
   if (value->IsNull()) {
     cpp_value.SetString(String());
   } else {
-    V8StringOrTrustedHTML::ToImpl(info.GetIsolate(), value, cpp_value,
+    V8StringOrTrustedHTML::ToImpl(isolate, value, cpp_value,
                                   UnionTypeConversionMode::kNotNullable,
                                   exception_state);
   }
@@ -70,7 +70,7 @@
   if (value->IsNull()) {
     cpp_value.SetString(String());
   } else {
-    V8StringOrTrustedHTML::ToImpl(info.GetIsolate(), value, cpp_value,
+    V8StringOrTrustedHTML::ToImpl(isolate, value, cpp_value,
                                   UnionTypeConversionMode::kNotNullable,
                                   exception_state);
   }
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_shadow_root_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_shadow_root_custom.cc
index 45240052..d99ee7b 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_shadow_root_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_shadow_root_custom.cc
@@ -37,7 +37,7 @@
   if (value->IsNull()) {
     cpp_value.SetString(String());
   } else {
-    V8StringOrTrustedHTML::ToImpl(info.GetIsolate(), value, cpp_value,
+    V8StringOrTrustedHTML::ToImpl(isolate, value, cpp_value,
                                   UnionTypeConversionMode::kNotNullable,
                                   exception_state);
   }
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
index 9ca324ff..40a98c8 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -14,7 +14,7 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
-#include "third_party/blink/renderer/core/script/classic_pending_script.h"
+#include "third_party/blink/renderer/core/loader/resource/script_resource.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
@@ -343,7 +343,7 @@
   return resource_buffer_size >= small_script_threshold_;
 }
 
-void ScriptStreamer::NotifyAppendData(ScriptResource* resource) {
+void ScriptStreamer::NotifyAppendData() {
   DCHECK(IsMainThread());
   if (streaming_suppressed_)
     return;
@@ -351,8 +351,8 @@
     // Even if the first data chunk is small, the script can still be big
     // enough - wait until the next data chunk comes before deciding whether
     // to start the streaming.
-    DCHECK(resource->ResourceBuffer());
-    if (!HasEnoughDataForStreaming(resource->ResourceBuffer()->size()))
+    DCHECK(script_resource_->ResourceBuffer());
+    if (!HasEnoughDataForStreaming(script_resource_->ResourceBuffer()->size()))
       return;
     have_enough_data_for_streaming_ = true;
 
@@ -360,8 +360,8 @@
       // Check for BOM (byte order marks), because that might change our
       // understanding of the data encoding.
       char maybe_bom[kMaximumLengthOfBOM] = {};
-      if (!resource->ResourceBuffer()->GetBytes(maybe_bom,
-                                                kMaximumLengthOfBOM)) {
+      if (!script_resource_->ResourceBuffer()->GetBytes(maybe_bom,
+                                                        kMaximumLengthOfBOM)) {
         NOTREACHED();
         return;
       }
@@ -369,7 +369,7 @@
       std::unique_ptr<TextResourceDecoder> decoder(
           TextResourceDecoder::Create(TextResourceDecoderOptions(
               TextResourceDecoderOptions::kPlainTextContent,
-              WTF::TextEncoding(resource->Encoding()))));
+              WTF::TextEncoding(script_resource_->Encoding()))));
       decoder->CheckForBOM(maybe_bom, kMaximumLengthOfBOM);
 
       // The encoding may change when we see the BOM. Check for BOM now
@@ -441,7 +441,7 @@
 
   }
   if (stream_)
-    stream_->DidReceiveData(resource, this);
+    stream_->DidReceiveData(script_resource_, this);
 }
 
 void ScriptStreamer::NotifyFinished() {
@@ -490,10 +490,10 @@
 }
 
 ScriptStreamer::ScriptStreamer(
-    ClassicPendingScript* script,
+    ScriptResource* script_resource,
     v8::ScriptCompiler::CompileOptions compile_options,
     scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner)
-    : pending_script_(script),
+    : script_resource_(script_resource),
       detached_(false),
       stream_(nullptr),
       loading_finished_(false),
@@ -502,8 +502,8 @@
       streaming_suppressed_(false),
       suppressed_reason_(kInvalid),
       compile_options_(compile_options),
-      script_url_string_(script->GetResource()->Url().Copy().GetString()),
-      script_resource_identifier_(script->GetResource()->Identifier()),
+      script_url_string_(script_resource->Url().Copy().GetString()),
+      script_resource_identifier_(script_resource->Identifier()),
       // Unfortunately there's no dummy encoding value in the enum; let's use
       // one we don't stream.
       encoding_(v8::ScriptCompiler::StreamedSource::TWO_BYTE),
@@ -517,7 +517,7 @@
 }
 
 void ScriptStreamer::Trace(blink::Visitor* visitor) {
-  visitor->Trace(pending_script_);
+  visitor->Trace(script_resource_);
 }
 
 void ScriptStreamer::StreamingComplete() {
@@ -550,64 +550,32 @@
   if (!IsFinished())
     return;
 
-  pending_script_->StreamingFinished();
+  script_resource_->StreamingFinished();
 }
 
-void ScriptStreamer::StartStreaming(
-    ClassicPendingScript* script,
+ScriptStreamer* ScriptStreamer::Create(
+    ScriptResource* resource,
     scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner,
     NotStreamingReason* not_streaming_reason) {
   DCHECK(IsMainThread());
   *not_streaming_reason = kInvalid;
-  ScriptResource* resource = ToScriptResource(script->GetResource());
   if (!resource->Url().ProtocolIsInHTTPFamily()) {
     *not_streaming_reason = kNotHTTP;
-    return;
-  }
-  if (resource->IsCacheValidator()) {
-    // This happens e.g., during reloads. We're actually not going to load
-    // the current Resource of the ClassicPendingScript but switch to another
-    // Resource -> don't stream.
-    *not_streaming_reason = kReload;
-    return;
+    return nullptr;
   }
   if (resource->IsLoaded() && !resource->ResourceBuffer()) {
     // This happens for already loaded resources, e.g. if resource
     // validation fails. In that case, the loading subsystem will discard
     // the resource buffer.
     *not_streaming_reason = kNoResourceBuffer;
-    return;
+    return nullptr;
   }
   // We cannot filter out short scripts, even if we wait for the HTTP headers
   // to arrive: the Content-Length HTTP header is not sent for chunked
   // downloads.
 
-  ScriptStreamer* streamer =
-      new ScriptStreamer(script, v8::ScriptCompiler::kNoCompileOptions,
-                         std::move(loading_task_runner));
-
-  // If this script was ready when streaming began, no callbacks will be
-  // received to populate the data for the ScriptStreamer, so send them now.
-  // Note that this script may be processing an asynchronous cache hit, in
-  // which case ScriptResource::IsLoaded() will be true, but ready_state_ will
-  // not be kReadyStreaming. In that case, ScriptStreamer can listen to the
-  // async callbacks generated by the cache hit.
-  if (script->IsReady()) {
-    DCHECK(resource->IsLoaded());
-    streamer->NotifyAppendData(resource);
-    if (streamer->StreamingSuppressed()) {
-      *not_streaming_reason = streamer->StreamingSuppressedReason();
-      return;
-    }
-  }
-
-  // The Resource might go out of scope if the script is no longer needed.
-  // This makes ClassicPendingScript notify the ScriptStreamer when it is
-  // destroyed.
-  script->SetStreamer(streamer);
-
-  if (script->IsReady())
-    streamer->NotifyFinished();
+  return new ScriptStreamer(resource, v8::ScriptCompiler::kNoCompileOptions,
+                            std::move(loading_task_runner));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.h b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
index 5e79b9f1..1a9c3b49 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
@@ -17,12 +17,11 @@
 
 namespace blink {
 
-class ClassicPendingScript;
 class ScriptResource;
 class SourceStream;
 
 // ScriptStreamer streams incomplete script data to V8 so that it can be parsed
-// while it's loaded. ClassicPendingScript holds a reference to ScriptStreamer.
+// while it's loaded. ScriptResource holds a reference to ScriptStreamer.
 // At the moment, ScriptStreamer is only used for parser blocking scripts; this
 // means that the Document stays stable and no other scripts are executing
 // while we're streaming. It is possible, though, that Document and the
@@ -40,18 +39,21 @@
   enum NotStreamingReason {
     kAlreadyLoaded,  // DEPRECATED
     kNotHTTP,
-    kReload,
+    kRevalidate,
     kContextNotValid,  // DEPRECATED
     kEncodingNotSupported,
+    // TODO(leszeks): Deprecate once scheduled streaming is on by default
     kThreadBusy,
     kV8CannotStream,
     kScriptTooSmall,
     kNoResourceBuffer,
     kHasCodeCache,
-    kStreamerNotReadyOnGetSource,
+    kStreamerNotReadyOnGetSource,  // DEPRECATED
     kInlineScript,
     kDidntTryToStartStreaming,
     kErrorOccurred,
+    kStreamingDisabled,
+    kSecondScriptResourceUse,
     kWorkerTopLevelScript,
 
     // Pseudo values that should never be seen in reported metrics
@@ -62,11 +64,11 @@
   ~ScriptStreamer();
   void Trace(blink::Visitor*);
 
-  // Launches a task (on a background thread) which will stream the given
-  // ClassicPendingScript into V8 as it loads.
-  static void StartStreaming(ClassicPendingScript*,
-                             scoped_refptr<base::SingleThreadTaskRunner>,
-                             NotStreamingReason* not_streaming_reason);
+  // Create a script streamer which will stream the given ScriptResource into V8
+  // as it loads.
+  static ScriptStreamer* Create(ScriptResource*,
+                                scoped_refptr<base::SingleThreadTaskRunner>,
+                                NotStreamingReason* not_streaming_reason);
 
   // Returns false if we cannot stream the given encoding.
   static bool ConvertEncoding(const char* encoding_name,
@@ -99,8 +101,12 @@
     return suppressed_reason_;
   }
 
-  // Called by ClassicPendingScript when data arrives from the network.
-  void NotifyAppendData(ScriptResource*);
+  // Called by ScriptResource when data arrives from the network.
+  void NotifyAppendData();
+  // Called by ScriptResource when loading has completed.
+  //
+  // Should not be called synchronously, as it can trigger script resource
+  // client callbacks.
   void NotifyFinished();
 
   // Called by ScriptStreamingTask when it has streamed all data to V8 and V8
@@ -123,17 +129,21 @@
   // Maximum size of the BOM marker.
   static constexpr size_t kMaximumLengthOfBOM = 4;
 
-  ScriptStreamer(ClassicPendingScript*,
+  ScriptStreamer(ScriptResource*,
                  v8::ScriptCompiler::CompileOptions,
                  scoped_refptr<base::SingleThreadTaskRunner>);
 
   void Prefinalize();
 
+  // Should not be called synchronously, as it can trigger script resource
+  // client callbacks.
   void StreamingComplete();
+  // Should not be called synchronously, as it can trigger script resource
+  // client callbacks.
   void NotifyFinishedToClient();
   bool HasEnoughDataForStreaming(size_t resource_buffer_size);
 
-  Member<ClassicPendingScript> pending_script_;
+  Member<ScriptResource> script_resource_;
   // Whether ScriptStreamer is detached from the Resource. In those cases, the
   // script data is not needed any more, and the client won't get notified
   // when the loading and streaming are done.
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
index 7d033b09..3ee30642 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
@@ -159,10 +159,7 @@
 TEST_F(ScriptStreamingTest, CompilingStreamedScript) {
   // Test that we can successfully compile a streamed script.
   V8TestingScope scope;
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
 
@@ -199,10 +196,7 @@
   // the V8 side typically finished before loading finishes: make sure we
   // handle it gracefully.
   V8TestingScope scope;
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
   AppendData("function foo() {");
@@ -240,10 +234,7 @@
   // Test that the upper layers (PendingScript and up) can be ramped down
   // while streaming is ongoing, and ScriptStreamer handles it gracefully.
   V8TestingScope scope;
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
   AppendData("function foo() {");
@@ -264,14 +255,11 @@
   EXPECT_FALSE(client->Finished());
 }
 
-TEST_F(ScriptStreamingTest, DataAfterCancellingStreaming) {
+TEST_F(ScriptStreamingTest, DataAfterDisposingPendingScript) {
   // Test that the upper layers (PendingScript and up) can be ramped down
   // before streaming is started, and ScriptStreamer handles it gracefully.
   V8TestingScope scope;
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
 
@@ -291,6 +279,7 @@
                               // holding on to it here.
 
   // Make sure the streaming starts.
+  AppendData(resource, "function foo() {");
   AppendPadding(resource);
   resource.Clear();
 
@@ -306,10 +295,7 @@
   // upper layer (ScriptResourceClient) should get a notification when the
   // script is loaded.
   V8TestingScope scope;
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
   AppendData("function foo() {");
@@ -338,10 +324,7 @@
   // (ScriptResourceClient) should be notified when an empty script has been
   // loaded.
   V8TestingScope scope;
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
 
@@ -360,10 +343,7 @@
   V8TestingScope scope;
   ScriptStreamer::SetSmallScriptThresholdForTesting(100);
 
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
 
@@ -385,10 +365,7 @@
   V8TestingScope scope;
   ScriptStreamer::SetSmallScriptThresholdForTesting(100);
 
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
 
@@ -424,10 +401,7 @@
   V8TestingScope scope;
   GetResource()->SetEncodingForTest("windows-1252");
 
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
 
@@ -465,10 +439,7 @@
   // This encoding is wrong on purpose.
   GetResource()->SetEncodingForTest("windows-1252");
 
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
 
@@ -500,10 +471,7 @@
 // A test for crbug.com/711703. Should not crash.
 TEST_F(ScriptStreamingTest, GarbageCollectDuringStreaming) {
   V8TestingScope scope;
-  ScriptStreamer::NotStreamingReason reason;
-  ScriptStreamer::StartStreaming(GetPendingScript(), loading_task_runner_,
-                                 &reason);
-  GetPendingScript()->SetNotStreamingReasonForTest(reason);
+  GetResource()->StartStreaming(loading_task_runner_);
 
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
@@ -515,6 +483,32 @@
       BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
 }
 
+TEST_F(ScriptStreamingTest, ResourceSetRevalidatingRequest) {
+  V8TestingScope scope;
+  GetResource()->StartStreaming(loading_task_runner_);
+
+  TestPendingScriptClient* client = new TestPendingScriptClient;
+  GetPendingScript()->WatchForLoad(client);
+
+  // Kick the streaming off.
+  AppendData("function foo() {");
+  AppendPadding();
+  AppendData("}");
+  Finish();
+  ProcessTasksUntilStreamingComplete();
+
+  // Second start streaming should fail.
+  EXPECT_FALSE(GetResource()->StartStreaming(loading_task_runner_));
+
+  ResourceRequest request(GetResource()->Url());
+  GetResource()->SetRevalidatingRequest(request);
+
+  // The next streaming should still fail, but the reason should be
+  // "kRevalidate".
+  EXPECT_FALSE(GetResource()->StartStreaming(loading_task_runner_));
+  EXPECT_EQ(GetResource()->NoStreamerReason(), ScriptStreamer::kRevalidate);
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
index 1c6fa9b..b4cc23f 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
@@ -871,13 +871,13 @@
   return iterator_getter->IsFunction();
 }
 
-v8::Isolate* ToIsolate(ExecutionContext* context) {
+v8::Isolate* ToIsolate(const ExecutionContext* context) {
   if (context && context->IsDocument())
     return V8PerIsolateData::MainThreadIsolate();
   return v8::Isolate::GetCurrent();
 }
 
-v8::Isolate* ToIsolate(LocalFrame* frame) {
+v8::Isolate* ToIsolate(const LocalFrame* frame) {
   DCHECK(frame);
   return frame->GetWindowProxyManager()->GetIsolate();
 }
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h
index dfdca27..db89c04 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h
@@ -439,8 +439,8 @@
                                            v8::Local<v8::Value>,
                                            ExceptionState&);
 
-CORE_EXPORT v8::Isolate* ToIsolate(ExecutionContext*);
-CORE_EXPORT v8::Isolate* ToIsolate(LocalFrame*);
+CORE_EXPORT v8::Isolate* ToIsolate(const ExecutionContext*);
+CORE_EXPORT v8::Isolate* ToIsolate(const LocalFrame*);
 
 CORE_EXPORT DOMWindow* ToDOMWindow(v8::Isolate*, v8::Local<v8::Value>);
 CORE_EXPORT LocalDOMWindow* ToLocalDOMWindow(v8::Local<v8::Context>);
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
index edbdba2..f3a4ce2 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
@@ -93,11 +93,15 @@
   }
 
   ScriptResource* CreateEmptyResource() {
-    return ScriptResource::CreateForTest(NullURL(), UTF8Encoding());
+    ScriptResource* resource =
+        ScriptResource::CreateForTest(NullURL(), UTF8Encoding());
+    resource->SetClientIsWaitingForFinished();
+    return resource;
   }
 
   ScriptResource* CreateResource(const WTF::TextEncoding& encoding) {
     ScriptResource* resource = ScriptResource::CreateForTest(Url(), encoding);
+    resource->SetClientIsWaitingForFinished();
     String code = Code();
     ResourceResponse response(Url());
     response.SetHTTPStatusCode(200);
diff --git a/third_party/blink/renderer/core/editing/link_selection_test.cc b/third_party/blink/renderer/core/editing/link_selection_test.cc
index b7fa8985..a067492 100644
--- a/third_party/blink/renderer/core/editing/link_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/link_selection_test.cc
@@ -114,7 +114,7 @@
 class TestFrameClient : public frame_test_helpers::TestWebFrameClient {
  public:
   WebNavigationPolicy DecidePolicyForNavigation(
-      const NavigationPolicyInfo& info) override {
+      NavigationPolicyInfo& info) override {
     last_policy_ = info.default_policy;
     return kWebNavigationPolicyIgnore;
   }
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 939e764..18cfe765 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -419,13 +419,11 @@
 
 void LocalFrameClientImpl::DispatchDidStartProvisionalLoad(
     DocumentLoader* loader,
-    ResourceRequest& request,
-    mojo::ScopedMessagePipeHandle navigation_initiator_handle) {
+    const ResourceRequest& request) {
   if (web_frame_->Client()) {
     WrappedResourceRequest wrapped_request(request);
     web_frame_->Client()->DidStartProvisionalLoad(
-        WebDocumentLoaderImpl::FromDocumentLoader(loader), wrapped_request,
-        std::move(navigation_initiator_handle));
+        WebDocumentLoaderImpl::FromDocumentLoader(loader), wrapped_request);
   }
 }
 
@@ -493,7 +491,7 @@
     WebNavigationType type,
     NavigationPolicy policy,
     bool has_transient_activation,
-    bool replaces_current_history_item,
+    WebFrameLoadType frame_load_type,
     bool is_client_redirect,
     WebTriggeringEventInfo triggering_event_info,
     HTMLFormElement* form,
@@ -501,7 +499,8 @@
         should_check_main_world_content_security_policy,
     mojom::blink::BlobURLTokenPtr blob_url_token,
     base::TimeTicks input_start_time,
-    const String& href_translate) {
+    const String& href_translate,
+    mojom::blink::NavigationInitiatorPtr navigation_initiator) {
   if (!web_frame_->Client())
     return kNavigationPolicyIgnore;
 
@@ -516,7 +515,7 @@
   // TODO(dgozman): remove this after some Canary coverage.
   CHECK(!web_document_loader || !web_document_loader->GetExtraData());
   navigation_info.has_user_gesture = has_transient_activation;
-  navigation_info.replaces_current_history_item = replaces_current_history_item;
+  navigation_info.frame_load_type = frame_load_type;
   navigation_info.is_client_redirect = is_client_redirect;
   navigation_info.triggering_event_info = triggering_event_info;
   navigation_info.should_check_main_world_content_security_policy =
@@ -526,6 +525,8 @@
           : kWebContentSecurityPolicyDispositionDoNotCheck;
   navigation_info.blob_url_token = blob_url_token.PassInterface().PassHandle();
   navigation_info.input_start = input_start_time;
+  navigation_info.navigation_initiator_handle =
+      navigation_initiator.PassInterface().PassHandle();
 
   // Can be null.
   LocalFrame* local_parent_frame = GetLocalParentFrame(web_frame_);
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index 7856af0..542dab1 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -96,10 +96,8 @@
                                        WebHistoryCommitType,
                                        bool content_initiated) override;
   void DispatchWillCommitProvisionalLoad() override;
-  void DispatchDidStartProvisionalLoad(
-      DocumentLoader*,
-      ResourceRequest&,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle) override;
+  void DispatchDidStartProvisionalLoad(DocumentLoader*,
+                                       const ResourceRequest&) override;
   void DispatchDidReceiveTitle(const String&) override;
   void DispatchDidChangeIcons(IconType) override;
   void DispatchDidCommitLoad(HistoryItem*,
@@ -119,14 +117,15 @@
       WebNavigationType,
       NavigationPolicy,
       bool has_transient_activation,
-      bool should_replace_current_entry,
+      WebFrameLoadType,
       bool is_client_redirect,
       WebTriggeringEventInfo,
       HTMLFormElement*,
       ContentSecurityPolicyDisposition should_bypass_main_world_csp,
       mojom::blink::BlobURLTokenPtr,
       base::TimeTicks input_start_time,
-      const String& href_translate) override;
+      const String& href_translate,
+      mojom::blink::NavigationInitiatorPtr) override;
   void DispatchWillSendSubmitEvent(HTMLFormElement*) override;
   void DidStartLoading() override;
   void DidStopLoading() override;
diff --git a/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc b/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc
index 2e017cd..5ff778a 100644
--- a/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc
+++ b/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc
@@ -73,10 +73,8 @@
 class SubresourceFilteringWebFrameClient
     : public frame_test_helpers::TestWebFrameClient {
  public:
-  void DidStartProvisionalLoad(
-      WebDocumentLoader* data_source,
-      WebURLRequest& request,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle) override {
+  void DidStartProvisionalLoad(WebDocumentLoader* data_source,
+                               WebURLRequest& request) override {
     // Normally, the filter should be set when the load is committed. For
     // the sake of this test, however, inject it earlier to verify that it
     // is not consulted for the main resource load.
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 bb2920f..8a70e30 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -4474,7 +4474,7 @@
 
   // frame_test_helpers::TestWebFrameClient:
   WebNavigationPolicy DecidePolicyForNavigation(
-      const NavigationPolicyInfo& info) override {
+      NavigationPolicyInfo& info) override {
     EXPECT_FALSE(info.is_client_redirect);
     return kWebNavigationPolicyCurrentTab;
   }
@@ -7380,7 +7380,7 @@
 
   // frame_test_helpers::TestWebFrameClient:
   WebNavigationPolicy DecidePolicyForNavigation(
-      const NavigationPolicyInfo& info) override {
+      NavigationPolicyInfo& info) override {
     if (ignore_navigations_) {
       decide_policy_call_count_++;
       return kWebNavigationPolicyIgnore;
@@ -7874,10 +7874,8 @@
   ~TestHistoryChildWebFrameClient() override = default;
 
   // frame_test_helpers::TestWebFrameClient:
-  void DidStartProvisionalLoad(
-      WebDocumentLoader* document_loader,
-      WebURLRequest& request,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle) override {
+  void DidStartProvisionalLoad(WebDocumentLoader* document_loader,
+                               WebURLRequest& request) override {
     replaces_current_history_item_ =
         document_loader->ReplacesCurrentHistoryItem();
   }
@@ -10700,10 +10698,7 @@
     EXPECT_EQ(0, callback_count_++);
     frame_test_helpers::TestWebFrameClient::DidStartLoading();
   }
-  void DidStartProvisionalLoad(
-      WebDocumentLoader*,
-      WebURLRequest&,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle) override {
+  void DidStartProvisionalLoad(WebDocumentLoader*, WebURLRequest&) override {
     EXPECT_EQ(1, callback_count_++);
   }
   void DidCommitProvisionalLoad(const WebHistoryItem&,
@@ -12458,10 +12453,15 @@
     return CreateLocalChild(*parent, scope, child_client_);
   }
   WebNavigationPolicy DecidePolicyForNavigation(
-      const NavigationPolicyInfo& info) override {
+      NavigationPolicyInfo& info) override {
     if (child_client_ || KURL(info.url_request.Url()) == BlankURL())
       return kWebNavigationPolicyCurrentTab;
-    return kWebNavigationPolicyHandledByClient;
+
+    Frame()->CreatePlaceholderDocumentLoader(
+        info.url_request, info.frame_load_type, info.navigation_type,
+        info.is_client_redirect, base::UnguessableToken::Create(), nullptr,
+        nullptr);
+    return kWebNavigationPolicyIgnore;
   }
 
  private:
@@ -12481,9 +12481,9 @@
   WebURLRequest request(ToKURL(base_url_ + "fallback.html"));
   main_frame->StartNavigation(request);
 
-  // Because the child frame will be HandledByClient, the main frame will not
-  // finish loading, so frame_test_helpers::PumpPendingRequestsForFrameToLoad
-  // doesn't work here.
+  // Because the child frame will have placeholder document loader, the main
+  // frame will not finish loading, so
+  // frame_test_helpers::PumpPendingRequestsForFrameToLoad doesn't work here.
   Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
 
   // Overwrite the client-handled child frame navigation with about:blank.
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 0947dd3b..d41000b23 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -3417,7 +3417,7 @@
       base_url);
 
   // Make a request from a local frame.
-  WebURLRequest web_url_request_with_target_start;
+  WebURLRequest web_url_request_with_target_start(KURL("about:blank"));
   LocalFrame* local_frame =
       ToWebLocalFrameImpl(web_view_impl->MainFrame()->FirstChild())->GetFrame();
   FrameLoadRequest request_with_target_start(
@@ -3437,7 +3437,7 @@
   frame->SetName("_start");
 
   // Make a request that will open a new window
-  WebURLRequest web_url_request;
+  WebURLRequest web_url_request(KURL("about:blank"));
   FrameLoadRequest request(nullptr, web_url_request.ToResourceRequest(),
                            "_blank");
   ToLocalFrame(web_view_impl->GetPage()->MainFrame())
@@ -3448,7 +3448,7 @@
 
   // Make a request from the new window that will navigate the original window.
   // The original window should be focused.
-  WebURLRequest web_url_request_with_target_start;
+  WebURLRequest web_url_request_with_target_start(KURL("about:blank"));
   FrameLoadRequest request_with_target_start(
       nullptr, web_url_request_with_target_start.ToResourceRequest(), "_start");
   ToLocalFrame(static_cast<WebViewImpl*>(client.CreatedWebView())
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index e998db49..a8bd493 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -34,6 +34,7 @@
 #include <memory>
 
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "third_party/blink/public/mojom/frame/navigation_initiator.mojom-blink.h"
 #include "third_party/blink/public/platform/web_content_security_policy_struct.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_effective_connection_type.h"
@@ -43,6 +44,7 @@
 #include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/public/platform/web_worker_fetch_context.h"
+#include "third_party/blink/public/web/web_frame_load_type.h"
 #include "third_party/blink/public/web/web_global_object_reuse_policy.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
 #include "third_party/blink/public/web/web_navigation_params.h"
@@ -131,10 +133,8 @@
                                                WebHistoryCommitType,
                                                bool content_initiated) {}
   virtual void DispatchWillCommitProvisionalLoad() = 0;
-  virtual void DispatchDidStartProvisionalLoad(
-      DocumentLoader*,
-      ResourceRequest&,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle) = 0;
+  virtual void DispatchDidStartProvisionalLoad(DocumentLoader*,
+                                               const ResourceRequest&) = 0;
   virtual void DispatchDidReceiveTitle(const String&) = 0;
   virtual void DispatchDidChangeIcons(IconType) = 0;
   virtual void DispatchDidCommitLoad(HistoryItem*,
@@ -155,7 +155,7 @@
       WebNavigationType,
       NavigationPolicy,
       bool has_transient_activation,
-      bool should_replace_current_entry,
+      WebFrameLoadType,
       bool is_client_redirect,
       WebTriggeringEventInfo,
       HTMLFormElement*,
@@ -163,7 +163,8 @@
           should_check_main_world_content_security_policy,
       mojom::blink::BlobURLTokenPtr,
       base::TimeTicks input_start_time,
-      const String& href_translate) = 0;
+      const String& href_translate,
+      mojom::blink::NavigationInitiatorPtr) = 0;
 
   virtual void DispatchWillSendSubmitEvent(HTMLFormElement*) = 0;
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 7cc9eb37..79848505 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -2245,6 +2245,24 @@
   GetFrame()->Loader().MarkAsLoading();
 }
 
+bool WebLocalFrameImpl::CreatePlaceholderDocumentLoader(
+    const WebURLRequest& request,
+    WebFrameLoadType frame_load_type,
+    WebNavigationType navigation_type,
+    bool is_client_redirect,
+    const base::UnguessableToken& devtools_navigation_token,
+    std::unique_ptr<WebNavigationParams> navigation_params,
+    std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) {
+  DCHECK(!request.IsNull());
+  DCHECK(!request.Url().ProtocolIs("javascript"));
+  return GetFrame()->Loader().CreatePlaceholderDocumentLoader(
+      request.ToResourceRequest(),
+      is_client_redirect ? ClientRedirectPolicy::kClientRedirect
+                         : ClientRedirectPolicy::kNotClientRedirect,
+      devtools_navigation_token, frame_load_type, navigation_type,
+      std::move(navigation_params), std::move(extra_data));
+}
+
 void WebLocalFrameImpl::SendOrientationChangeEvent() {
   if (!GetFrame())
     return;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 80fa1cf..281cf46 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -307,6 +307,14 @@
                          const WebSourceLocation&) override;
   void ClientDroppedNavigation() override;
   void MarkAsLoading() override;
+  bool CreatePlaceholderDocumentLoader(
+      const WebURLRequest&,
+      WebFrameLoadType,
+      WebNavigationType,
+      bool is_client_redirect,
+      const base::UnguessableToken& devtools_navigation_token,
+      std::unique_ptr<WebNavigationParams>,
+      std::unique_ptr<WebDocumentLoader::ExtraData>) override;
   void SendOrientationChangeEvent() override;
   WebSandboxFlags EffectiveSandboxFlags() const override;
   void DidCallAddSearchProvider() override;
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index d60cd4b..26bdc001 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -386,7 +386,7 @@
   if (!child_frame)
     return false;
 
-  ResourceRequest request(url);
+  ResourceRequest request(url.IsNull() ? BlankURL() : url);
   ReferrerPolicy policy = ReferrerPolicyAttribute();
   request.SetReferrerPolicy(policy);
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 7e258b3..a8d694f 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -371,8 +371,8 @@
   switch (reason) {
     case ScriptStreamer::kNotHTTP:
       return "not http/https protocol";
-    case ScriptStreamer::kReload:
-      return "reload event";
+    case ScriptStreamer::kRevalidate:
+      return "revalidation event";
     case ScriptStreamer::kContextNotValid:
       return "script context not valid";
     case ScriptStreamer::kEncodingNotSupported:
@@ -395,6 +395,10 @@
       return "start streaming not called";
     case ScriptStreamer::kErrorOccurred:
       return "an error occurred";
+    case ScriptStreamer::kStreamingDisabled:
+      return "already disabled streaming";
+    case ScriptStreamer::kSecondScriptResourceUse:
+      return "already used streamed data";
     case ScriptStreamer::kWorkerTopLevelScript:
       return "worker top-level scripts are not streamable";
     case ScriptStreamer::kAlreadyLoaded:
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
index ae2a595d..bb0d359 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/core/layout/pointer_events_hit_rules.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h"
+#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
 #include "third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h"
 #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
@@ -114,7 +115,8 @@
     LayoutInvalidationReasonForTracing reason) {
   descendant_text_nodes_.clear();
   SetNeedsPositioningValuesUpdate();
-  SetNeedsLayoutAndFullPaintInvalidation(reason);
+  // TODO(fs): Restore the passing of |reason| here.
+  LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(*this);
 }
 
 void LayoutSVGText::SubtreeChildWasAdded() {
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index b78b408..4f1c8f4e 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -172,8 +172,8 @@
     }
   }
 
-  if (navigation_params) {
-    WebSourceLocation& location = navigation_params->source_location;
+  if (navigation_params && navigation_params->source_location.has_value()) {
+    WebSourceLocation& location = navigation_params->source_location.value();
     source_location_ = SourceLocation::Create(
         location.url, location.line_number, location.column_number, nullptr);
   }
@@ -266,7 +266,8 @@
       break;
     case ResourceType::kScript:
       params.SetRequestContext(mojom::RequestContextType::SCRIPT);
-      resource = ScriptResource::Fetch(params, Fetcher(), nullptr);
+      resource = ScriptResource::Fetch(params, Fetcher(), nullptr,
+                                       ScriptResource::kAllowStreaming);
       break;
     case ResourceType::kCSSStyleSheet:
       resource = CSSStyleSheetResource::Fetch(params, Fetcher(), nullptr);
diff --git a/third_party/blink/renderer/core/loader/empty_clients.cc b/third_party/blink/renderer/core/loader/empty_clients.cc
index 98b50d6c..9caf891f 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.cc
+++ b/third_party/blink/renderer/core/loader/empty_clients.cc
@@ -101,14 +101,15 @@
     WebNavigationType,
     NavigationPolicy,
     bool,
-    bool,
+    WebFrameLoadType,
     bool,
     WebTriggeringEventInfo,
     HTMLFormElement*,
     ContentSecurityPolicyDisposition,
     mojom::blink::BlobURLTokenPtr,
     base::TimeTicks,
-    const String&) {
+    const String&,
+    mojom::blink::NavigationInitiatorPtr) {
   return kNavigationPolicyIgnore;
 }
 
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 4124d9a..2392108 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -250,10 +250,8 @@
 
   void DispatchDidHandleOnloadEvents() override {}
   void DispatchWillCommitProvisionalLoad() override {}
-  void DispatchDidStartProvisionalLoad(
-      DocumentLoader*,
-      ResourceRequest&,
-      mojo::ScopedMessagePipeHandle navigation_initiator_handle) override {}
+  void DispatchDidStartProvisionalLoad(DocumentLoader*,
+                                       const ResourceRequest&) override {}
   void DispatchDidReceiveTitle(const String&) override {}
   void DispatchDidChangeIcons(IconType) override {}
   void DispatchDidCommitLoad(HistoryItem*,
@@ -267,20 +265,22 @@
   void DispatchDidFinishLoad() override {}
   void DispatchDidChangeThemeColor() override {}
 
-  NavigationPolicy DecidePolicyForNavigation(const ResourceRequest&,
-                                             Document* origin_document,
-                                             DocumentLoader*,
-                                             WebNavigationType,
-                                             NavigationPolicy,
-                                             bool,
-                                             bool,
-                                             bool,
-                                             WebTriggeringEventInfo,
-                                             HTMLFormElement*,
-                                             ContentSecurityPolicyDisposition,
-                                             mojom::blink::BlobURLTokenPtr,
-                                             base::TimeTicks,
-                                             const String&) override;
+  NavigationPolicy DecidePolicyForNavigation(
+      const ResourceRequest&,
+      Document* origin_document,
+      DocumentLoader*,
+      WebNavigationType,
+      NavigationPolicy,
+      bool,
+      WebFrameLoadType,
+      bool,
+      WebTriggeringEventInfo,
+      HTMLFormElement*,
+      ContentSecurityPolicyDisposition,
+      mojom::blink::BlobURLTokenPtr,
+      base::TimeTicks,
+      const String&,
+      mojom::blink::NavigationInitiatorPtr) override;
 
   void DispatchWillSendSubmitEvent(HTMLFormElement*) override;
 
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index c8bde7ef..be7a5ad 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -793,7 +793,6 @@
   CHECK(!IsBackForwardLoadType(frame_load_type));
   DCHECK(passed_request.TriggeringEventInfo() !=
          WebTriggeringEventInfo::kUnknown);
-  DCHECK(policy != kNavigationPolicyHandledByClient);
 
   DCHECK(frame_->GetDocument());
   if (HTMLFrameOwnerElement* element = frame_->DeprecatedLocalOwner())
@@ -889,18 +888,15 @@
       frame_->IsMainFrame() ? network::mojom::RequestContextFrameType::kTopLevel
                             : network::mojom::RequestContextFrameType::kNested);
 
-  mojo::ScopedMessagePipeHandle navigation_initiator_handle;
+  mojom::blink::NavigationInitiatorPtr navigation_initiator;
   if (origin_document && origin_document->GetContentSecurityPolicy()
                              ->ExperimentalFeaturesEnabled()) {
     WebContentSecurityPolicyList initiator_csp =
         origin_document->GetContentSecurityPolicy()
             ->ExposeForNavigationalChecks();
     resource_request.SetInitiatorCSP(initiator_csp);
-    mojom::blink::NavigationInitiatorPtr navigation_initiator;
     auto request = mojo::MakeRequest(&navigation_initiator);
     origin_document->BindNavigationInitiatorRequest(std::move(request));
-    navigation_initiator_handle =
-        navigation_initiator.PassInterface().PassHandle();
   }
 
   // Record the latest requiredCSP value that will be used when sending this
@@ -944,13 +940,12 @@
 
   policy = Client()->DecidePolicyForNavigation(
       resource_request, origin_document, nullptr /* document_loader */,
-      navigation_type, policy, has_transient_activation,
-      frame_load_type == WebFrameLoadType::kReplaceCurrentItem,
+      navigation_type, policy, has_transient_activation, frame_load_type,
       request.ClientRedirect() == ClientRedirectPolicy::kClientRedirect,
       request.TriggeringEventInfo(), request.Form(),
       request.ShouldCheckMainWorldContentSecurityPolicy(),
       request.GetBlobURLToken(), request.GetInputStartTime(),
-      request.HrefTranslate().GetString());
+      request.HrefTranslate().GetString(), std::move(navigation_initiator));
 
   // 'beforeunload' can be fired above, which can detach this frame from inside
   // the event handler.
@@ -960,38 +955,10 @@
   if (policy == kNavigationPolicyIgnore)
     return;
 
-  if (policy == kNavigationPolicyCurrentTab) {
-    CommitNavigation(resource_request, SubstituteData(),
-                     request.ClientRedirect(), base::UnguessableToken::Create(),
-                     frame_load_type, nullptr, nullptr, nullptr);
-    return;
-  }
-
-  DCHECK(policy == kNavigationPolicyHandledByClient);
-  if (!CancelProvisionalLoaderForNewNavigation(
-          true /* cancel_scheduled_navigations */)) {
-    return;
-  }
-
-  provisional_document_loader_ = CreateDocumentLoader(
-      resource_request, SubstituteData(), request.ClientRedirect(),
-      base::UnguessableToken::Create(), frame_load_type, navigation_type,
-      nullptr /* navigation_params */, nullptr /* extra_data */);
-
-  provisional_document_loader_->AppendRedirect(
-      provisional_document_loader_->Url());
-  frame_->GetFrameScheduler()->DidStartProvisionalLoad(frame_->IsMainFrame());
-
-  // TODO(ananta):
-  // We should get rid of the dependency on the DocumentLoader in consumers of
-  // the DidStartProvisionalLoad() notification.
-  Client()->DispatchDidStartProvisionalLoad(
-      provisional_document_loader_, resource_request,
-      std::move(navigation_initiator_handle));
-  probe::didStartProvisionalLoad(frame_);
-  virtual_time_pauser_.PauseVirtualTime();
-  DCHECK(provisional_document_loader_);
-  TakeObjectSnapshot();
+  DCHECK(policy == kNavigationPolicyCurrentTab);
+  CommitNavigation(resource_request, SubstituteData(), request.ClientRedirect(),
+                   base::UnguessableToken::Create(), frame_load_type, nullptr,
+                   nullptr, nullptr);
 }
 
 void FrameLoader::CommitNavigation(
@@ -1082,8 +1049,7 @@
 
   frame_->GetFrameScheduler()->DidStartProvisionalLoad(frame_->IsMainFrame());
   Client()->DispatchDidStartProvisionalLoad(provisional_document_loader_,
-                                            resource_request,
-                                            mojo::ScopedMessagePipeHandle());
+                                            resource_request);
   probe::didStartProvisionalLoad(frame_);
   virtual_time_pauser_.PauseVirtualTime();
 
@@ -1139,6 +1105,32 @@
   return mojom::CommitResult::Ok;
 }
 
+bool FrameLoader::CreatePlaceholderDocumentLoader(
+    const ResourceRequest& resource_request,
+    ClientRedirectPolicy client_redirect_policy,
+    const base::UnguessableToken& devtools_navigation_token,
+    WebFrameLoadType frame_load_type,
+    WebNavigationType navigation_type,
+    std::unique_ptr<WebNavigationParams> navigation_params,
+    std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) {
+  if (!CancelProvisionalLoaderForNewNavigation(
+          true /* cancel_scheduled_navigations */)) {
+    return false;
+  }
+
+  provisional_document_loader_ = CreateDocumentLoader(
+      resource_request, SubstituteData(), client_redirect_policy,
+      devtools_navigation_token, frame_load_type, navigation_type,
+      std::move(navigation_params), std::move(extra_data));
+  provisional_document_loader_->AppendRedirect(
+      provisional_document_loader_->Url());
+  frame_->GetFrameScheduler()->DidStartProvisionalLoad(frame_->IsMainFrame());
+  probe::didStartProvisionalLoad(frame_);
+  virtual_time_pauser_.PauseVirtualTime();
+  TakeObjectSnapshot();
+  return true;
+}
+
 SubstituteData FrameLoader::DefaultSubstituteDataForURL(const KURL& url) {
   if (!ShouldTreatURLAsSrcdocDocument(url))
     return SubstituteData();
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index 3e0b3e3..9cfcee46 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -132,6 +132,22 @@
       bool has_event,
       std::unique_ptr<WebDocumentLoader::ExtraData> extra_data = nullptr);
 
+  // Called when the browser process is handling the navigation, to
+  // create a "placeholder" document loader and mark the frame as loading.
+  // This placeholder document loader will be later abandoned, and only
+  // lives temporarily so that the rest of Blink code knows the navigation
+  // is in place.
+  // See DocumentLoader::devtools_navigation_token_ for documentation on
+  // the token.
+  bool CreatePlaceholderDocumentLoader(
+      const ResourceRequest&,
+      ClientRedirectPolicy,
+      const base::UnguessableToken& devtools_navigation_token,
+      WebFrameLoadType,
+      WebNavigationType,
+      std::unique_ptr<WebNavigationParams>,
+      std::unique_ptr<WebDocumentLoader::ExtraData>);
+
   // This runs the "stop document loading" algorithm in HTML:
   // https://html.spec.whatwg.org/C/browsing-the-web.html#stop-document-loading
   // Note, this function only cancels ongoing navigation handled through
diff --git a/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
index d236602..9b0da3f 100644
--- a/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
@@ -27,7 +27,8 @@
   if (FetchIfLayeredAPI(fetch_params))
     return;
 
-  ScriptResource::Fetch(fetch_params, fetcher_, this);
+  ScriptResource::Fetch(fetch_params, fetcher_, this,
+                        ScriptResource::kNoStreaming);
 }
 
 void DocumentModuleScriptFetcher::NotifyFinished(Resource* resource) {
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
index e11bb7a9..d2b1e4ad 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
@@ -217,8 +217,10 @@
   TestModuleScriptLoaderClient* client = new TestModuleScriptLoaderClient;
   TestFetchDataURL(ModuleScriptCustomFetchType::kNone, client);
 
-  EXPECT_TRUE(client->WasNotifyFinished())
-      << "ModuleScriptLoader should finish synchronously.";
+  // TODO(leszeks): This should finish synchronously, but currently due
+  // to the script resource/script streamer interaction, it does not.
+  RunUntilIdle();
+  EXPECT_TRUE(client->WasNotifyFinished());
   ASSERT_TRUE(client->GetModuleScript());
   EXPECT_FALSE(client->GetModuleScript()->HasEmptyRecord());
   EXPECT_FALSE(client->GetModuleScript()->HasParseError());
@@ -272,8 +274,11 @@
   TestModuleScriptLoaderClient* client = new TestModuleScriptLoaderClient;
   TestInvalidSpecifier(ModuleScriptCustomFetchType::kNone, client);
 
-  EXPECT_TRUE(client->WasNotifyFinished())
-      << "ModuleScriptLoader should finish synchronously.";
+  // TODO(leszeks): This should finish synchronously, but currently due
+  // to the script resource/script streamer interaction, it does not.
+  RunUntilIdle();
+  EXPECT_TRUE(client->WasNotifyFinished());
+
   ASSERT_TRUE(client->GetModuleScript());
   EXPECT_TRUE(client->GetModuleScript()->HasEmptyRecord());
   EXPECT_TRUE(client->GetModuleScript()->HasParseError());
@@ -313,8 +318,10 @@
   TestModuleScriptLoaderClient* client = new TestModuleScriptLoaderClient;
   TestFetchInvalidURL(ModuleScriptCustomFetchType::kNone, client);
 
-  EXPECT_TRUE(client->WasNotifyFinished())
-      << "ModuleScriptLoader should finish synchronously.";
+  // TODO(leszeks): This should finish synchronously, but currently due
+  // to the script resource/script streamer interaction, it does not.
+  RunUntilIdle();
+  EXPECT_TRUE(client->WasNotifyFinished());
   EXPECT_FALSE(client->GetModuleScript());
 }
 
@@ -355,6 +362,9 @@
   EXPECT_FALSE(client->WasNotifyFinished())
       << "ModuleScriptLoader unexpectedly finished synchronously.";
   platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+  // TODO(leszeks): This should finish synchronously, but currently due
+  // to the script resource/script streamer interaction, it does not.
+  RunUntilIdle();
 
   EXPECT_TRUE(client->WasNotifyFinished());
   EXPECT_TRUE(client->GetModuleScript());
diff --git a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
index 3367f0c..807d9b3 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
@@ -33,7 +33,8 @@
   // Step 13.2. "Fetch request, and asynchronously wait to run the remaining
   // steps as part of fetch's process response for the response response." [spec
   // text]
-  ScriptResource::Fetch(fetch_params, global_scope_->EnsureFetcher(), this);
+  ScriptResource::Fetch(fetch_params, global_scope_->EnsureFetcher(), this,
+                        ScriptResource::kNoStreaming);
 }
 
 void WorkerModuleScriptFetcher::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.cc
index 7a59931..74b7850 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.cc
@@ -38,7 +38,8 @@
   // need to handle that case, maybe by having a way to restart fetches in a
   // different global scope?
   url_ = fetch_params.Url();
-  ScriptResource::Fetch(fetch_params, fetcher_.Get(), this);
+  ScriptResource::Fetch(fetch_params, fetcher_.Get(), this,
+                        ScriptResource::kNoStreaming);
 }
 
 void WorkletModuleScriptFetcher::NotifyFinished(Resource* resource) {
diff --git a/third_party/blink/renderer/core/loader/navigation_policy.cc b/third_party/blink/renderer/core/loader/navigation_policy.cc
index 887f7a82..cb4fa0f 100644
--- a/third_party/blink/renderer/core/loader/navigation_policy.cc
+++ b/third_party/blink/renderer/core/loader/navigation_policy.cc
@@ -203,7 +203,5 @@
                    kNavigationPolicyNewForegroundTab);
 STATIC_ASSERT_ENUM(kWebNavigationPolicyNewWindow, kNavigationPolicyNewWindow);
 STATIC_ASSERT_ENUM(kWebNavigationPolicyNewPopup, kNavigationPolicyNewPopup);
-STATIC_ASSERT_ENUM(kWebNavigationPolicyHandledByClient,
-                   kNavigationPolicyHandledByClient);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/navigation_policy.h b/third_party/blink/renderer/core/loader/navigation_policy.h
index 3ac25806..1b56851 100644
--- a/third_party/blink/renderer/core/loader/navigation_policy.h
+++ b/third_party/blink/renderer/core/loader/navigation_policy.h
@@ -46,7 +46,6 @@
   kNavigationPolicyNewForegroundTab,
   kNavigationPolicyNewWindow,
   kNavigationPolicyNewPopup,
-  kNavigationPolicyHandledByClient,
 };
 
 // Returns a NavigationPolicy to use for starting a navigation
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource.cc b/third_party/blink/renderer/core/loader/resource/script_resource.cc
index 3662eb4..157e66b 100644
--- a/third_party/blink/renderer/core/loader/resource/script_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/script_resource.cc
@@ -71,13 +71,33 @@
 
 ScriptResource* ScriptResource::Fetch(FetchParameters& params,
                                       ResourceFetcher* fetcher,
-                                      ResourceClient* client) {
+                                      ResourceClient* client,
+                                      StreamingAllowed streaming_allowed) {
   DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
             network::mojom::RequestContextFrameType::kNone);
   DCHECK(IsRequestContextSupported(
       params.GetResourceRequest().GetRequestContext()));
-  return ToScriptResource(
+  ScriptResource* resource = ToScriptResource(
       fetcher->RequestResource(params, ScriptResourceFactory(), client));
+
+  if (streaming_allowed != kAllowStreaming) {
+    // Advance the |streaming_state_| to kStreamingNotAllowed by calling
+    // SetClientIsWaitingForFinished unless it is explicitly allowed.'
+    //
+    // Do this in a task rather than directly to make sure that we don't call
+    // the finished callbacks of other clients synchronously.
+
+    // TODO(leszeks): Previous behaviour, without script streaming, was to
+    // synchronously notify the given client, with the assumption that other
+    // clients were already finished. If this behaviour becomes necessary, we
+    // would have to either check that streaming wasn't started (if that would
+    // be a logic error), or cancel any existing streaming.
+    fetcher->Context().GetLoadingTaskRunner()->PostTask(
+        FROM_HERE, WTF::Bind(&ScriptResource::SetClientIsWaitingForFinished,
+                             WrapWeakPersistent(resource)));
+  }
+
+  return resource;
 }
 
 ScriptResource::ScriptResource(
@@ -91,6 +111,11 @@
 
 ScriptResource::~ScriptResource() = default;
 
+void ScriptResource::Trace(blink::Visitor* visitor) {
+  visitor->Trace(streamer_);
+  TextResource::Trace(visitor);
+}
+
 void ScriptResource::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail,
                                   WebProcessMemoryDump* memory_dump) const {
   Resource::OnMemoryDump(level_of_detail, memory_dump);
@@ -102,7 +127,7 @@
 }
 
 const ParkableString& ScriptResource::SourceText() {
-  DCHECK(IsLoaded());
+  CHECK(IsFinishedInternal());
 
   if (source_text_.IsNull() && Data()) {
     String source_text = DecodedText();
@@ -135,9 +160,27 @@
 
 void ScriptResource::DestroyDecodedDataForFailedRevalidation() {
   source_text_ = ParkableString();
+  // Make sure there's no streaming.
+  DCHECK(!streamer_);
+  DCHECK_EQ(streaming_state_, StreamingState::kStreamingNotAllowed);
   SetDecodedSize(0);
 }
 
+void ScriptResource::SetRevalidatingRequest(const ResourceRequest& request) {
+  CHECK_EQ(streaming_state_, StreamingState::kFinishedNotificationSent);
+  if (streamer_) {
+    CHECK(streamer_->IsStreamingFinished());
+    streamer_ = nullptr;
+  }
+  // Revalidation requests don't actually load the current Resource, so disable
+  // streaming.
+  not_streaming_reason_ = ScriptStreamer::kRevalidate;
+  streaming_state_ = StreamingState::kStreamingNotAllowed;
+  CheckStreamingState();
+
+  TextResource::SetRevalidatingRequest(request);
+}
+
 AccessControlStatus ScriptResource::CalculateAccessControlStatus() const {
   DCHECK_NE(GetResponse().GetType(), network::mojom::FetchResponseType::kError);
   return GetResponse().IsCORSSameOrigin() ? kSharableCrossOrigin
@@ -151,7 +194,197 @@
   if (HasClientsOrObservers())
     return false;
 
+  // Do not revalidate until streaming is complete.
+  if (!IsFinishedInternal())
+    return false;
+
   return Resource::CanUseCacheValidator();
 }
 
+void ScriptResource::NotifyDataReceived(const char* data, size_t size) {
+  CheckStreamingState();
+  if (streamer_) {
+    DCHECK_EQ(streaming_state_, StreamingState::kStreaming);
+    streamer_->NotifyAppendData();
+  }
+  TextResource::NotifyDataReceived(data, size);
+}
+
+void ScriptResource::NotifyFinished() {
+  DCHECK(IsLoaded());
+  switch (streaming_state_) {
+    case StreamingState::kCanStartStreaming:
+      // Do nothing, expect either a StartStreaming() call to transition us to
+      // kStreaming, or an SetClientIsWaitingForFinished() call to transition us
+      // into kStreamingNotAllowed. These will then transition again since
+      // IsLoaded will be true.
+      break;
+    case StreamingState::kStreaming:
+      AdvanceStreamingState(StreamingState::kWaitingForStreamingToEnd);
+      DCHECK(streamer_);
+      streamer_->NotifyFinished();
+      // Don't call the base NotifyFinished until streaming finishes too (which
+      // might happen immediately in the above ScriptStreamer::NotifyFinished
+      // call)
+      break;
+    case StreamingState::kStreamingNotAllowed:
+      AdvanceStreamingState(StreamingState::kFinishedNotificationSent);
+      TextResource::NotifyFinished();
+      break;
+    case StreamingState::kWaitingForStreamingToEnd:
+    case StreamingState::kFinishedNotificationSent:
+      // Not possible.
+      CHECK(false);
+      break;
+  }
+}
+
+bool ScriptResource::IsFinishedInternal() const {
+  CheckStreamingState();
+  return streaming_state_ == StreamingState::kFinishedNotificationSent;
+}
+
+void ScriptResource::StreamingFinished() {
+  CHECK(streamer_);
+  CHECK_EQ(streaming_state_, StreamingState::kWaitingForStreamingToEnd);
+  AdvanceStreamingState(StreamingState::kFinishedNotificationSent);
+  TextResource::NotifyFinished();
+}
+
+bool ScriptResource::StartStreaming(
+    scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner) {
+  CheckStreamingState();
+
+  if (streamer_) {
+    return !streamer_->IsStreamingFinished();
+  }
+
+  if (streaming_state_ != StreamingState::kCanStartStreaming) {
+    return false;
+  }
+
+  // Don't bother streaming if there was an error, it won't work anyway.
+  if (ErrorOccurred()) {
+    return false;
+  }
+
+  CHECK(!IsCacheValidator());
+
+  streamer_ =
+      ScriptStreamer::Create(this, loading_task_runner, &not_streaming_reason_);
+  if (streamer_) {
+    AdvanceStreamingState(StreamingState::kStreaming);
+
+    // If there is any data already, send it to the streamer.
+    if (Data()) {
+      // Note that we don't need to iterate through the segments of the data, as
+      // the streamer will do that itself.
+      CHECK_GT(Data()->size(), 0u);
+      streamer_->NotifyAppendData();
+    }
+    // If the we're is already loaded, notify the streamer about that too.
+    if (IsLoaded()) {
+      AdvanceStreamingState(StreamingState::kWaitingForStreamingToEnd);
+
+      // Do this in a task rather than directly to make sure that we don't call
+      // the finished callback in the same stack as starting streaming -- this
+      // can cause issues with the client expecting to be not finished when
+      // starting streaming (e.g. ClassicPendingScript::IsReady == false), but
+      // ending up finished by the end of this method.
+      loading_task_runner->PostTask(FROM_HERE,
+                                    WTF::Bind(&ScriptStreamer::NotifyFinished,
+                                              WrapPersistent(streamer_.Get())));
+    }
+  }
+
+  CheckStreamingState();
+  return streamer_ && !streamer_->IsStreamingFinished();
+}
+
+void ScriptResource::SetClientIsWaitingForFinished() {
+  // No-op if streaming already started or finished.
+  CheckStreamingState();
+  if (streaming_state_ != StreamingState::kCanStartStreaming)
+    return;
+
+  AdvanceStreamingState(StreamingState::kStreamingNotAllowed);
+  not_streaming_reason_ = ScriptStreamer::kStreamingDisabled;
+  // Trigger the finished notification if needed.
+  if (IsLoaded()) {
+    AdvanceStreamingState(StreamingState::kFinishedNotificationSent);
+    TextResource::NotifyFinished();
+  }
+}
+
+ScriptStreamer* ScriptResource::TakeStreamer() {
+  CHECK(IsFinishedInternal());
+  if (!streamer_)
+    return nullptr;
+
+  ScriptStreamer* streamer = streamer_;
+  streamer_ = nullptr;
+  not_streaming_reason_ = ScriptStreamer::kSecondScriptResourceUse;
+  return streamer;
+}
+
+void ScriptResource::AdvanceStreamingState(StreamingState new_state) {
+  switch (streaming_state_) {
+    case StreamingState::kCanStartStreaming:
+      CHECK(new_state == StreamingState::kStreaming ||
+            new_state == StreamingState::kStreamingNotAllowed);
+      break;
+    case StreamingState::kStreaming:
+      CHECK(streamer_);
+      CHECK_EQ(new_state, StreamingState::kWaitingForStreamingToEnd);
+      break;
+    case StreamingState::kWaitingForStreamingToEnd:
+      CHECK(streamer_);
+      CHECK_EQ(new_state, StreamingState::kFinishedNotificationSent);
+      break;
+    case StreamingState::kStreamingNotAllowed:
+      CHECK_EQ(new_state, StreamingState::kFinishedNotificationSent);
+      break;
+    case StreamingState::kFinishedNotificationSent:
+      CHECK(false);
+      break;
+  }
+
+  streaming_state_ = new_state;
+  CheckStreamingState();
+}
+
+void ScriptResource::CheckStreamingState() const {
+  // TODO(leszeks): Eventually convert these CHECKs into DCHECKs once the logic
+  // is a bit more baked in.
+  switch (streaming_state_) {
+    case StreamingState::kCanStartStreaming:
+      CHECK(!streamer_);
+      break;
+    case StreamingState::kStreaming:
+      CHECK(streamer_);
+      CHECK(!streamer_->IsFinished());
+      // kStreaming can be entered both when loading (if streaming is started
+      // before load completes) or when loaded (if streaming is started after
+      // load completes). In the latter case, the state will almost immediately
+      // advance to kWaitingForStreamingToEnd.
+      CHECK(IsLoaded() || IsLoading());
+      break;
+    case StreamingState::kWaitingForStreamingToEnd:
+      CHECK(streamer_);
+      CHECK(!streamer_->IsFinished());
+      CHECK(IsLoaded());
+      break;
+    case StreamingState::kStreamingNotAllowed:
+      CHECK(!streamer_);
+      // TODO(leszeks): We could CHECK(!IsLoaded()) if not for the immediate
+      // kCanStartStreaming -> kStreamingNotAllowed -> kFinishedNotificationSent
+      // transition in SetClientIsWaitingForFinished when IsLoaded.
+      break;
+    case StreamingState::kFinishedNotificationSent:
+      CHECK(!streamer_ || streamer_->IsFinished());
+      CHECK(IsLoaded());
+      break;
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource.h b/third_party/blink/renderer/core/loader/resource/script_resource.h
index 0c4d7ef..8a8aa5ff 100644
--- a/third_party/blink/renderer/core/loader/resource/script_resource.h
+++ b/third_party/blink/renderer/core/loader/resource/script_resource.h
@@ -28,6 +28,7 @@
 
 #include <memory>
 
+#include "third_party/blink/renderer/bindings/core/v8/script_streamer.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/loader/resource/text_resource.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string.h"
@@ -43,11 +44,34 @@
 class KURL;
 class ResourceFetcher;
 
+// ScriptResource is a resource representing a JavaScript script. It is only
+// used for "classic" scripts, i.e. not modules.
+//
+// In addition to loading the script, a ScriptResource can optionally stream the
+// script to the JavaScript parser/compiler, using a ScriptStreamer. In this
+// case, clients of the ScriptResource will not receive the finished
+// notification until the streaming completes.
+//
+// See also:
+// https://docs.google.com/document/d/143GOPl_XVgLPFfO-31b_MdBcnjklLEX2OIg_6eN6fQ4
 class CORE_EXPORT ScriptResource final : public TextResource {
  public:
+  // For scripts fetched with kAllowStreaming, the ScriptResource expects users
+  // to call StartStreaming to start streaming the loaded data, and
+  // SetClientIsWaitingForFinished when they actually want the data to be
+  // available for execute. Note that StartStreaming can fail, so the client of
+  // an unfinished resource has to call SetClientIsWaitingForFinished to
+  // guarantee that it receives a finished callback.
+  //
+  // Scripts fetched with kNoStreaming will (asynchronously) call
+  // SetClientIsWaitingForFinished on the resource, so the user does not have to
+  // call it again. This is effectively the "legacy" behaviour.
+  enum StreamingAllowed { kNoStreaming, kAllowStreaming };
+
   static ScriptResource* Fetch(FetchParameters&,
                                ResourceFetcher*,
-                               ResourceClient*);
+                               ResourceClient*,
+                               StreamingAllowed);
 
   // Public for testing
   static ScriptResource* CreateForTest(const KURL& url,
@@ -63,24 +87,96 @@
 
   ~ScriptResource() override;
 
+  void Trace(blink::Visitor*) override;
+
   void OnMemoryDump(WebMemoryDumpLevelOfDetail,
                     WebProcessMemoryDump*) const override;
 
-  void DestroyDecodedDataForFailedRevalidation() override;
-
   void SetSerializedCachedMetadata(const char*, size_t) override;
 
+  // Returns true if streaming was successfully started (or if an active
+  // streamer is already running)
+  //
+  // TODO(leszeks): This value is only used for work stealing, so make this
+  // function return void if work stealing is removed.
+  bool StartStreaming(
+      scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner);
+
+  // State that a client of the script resource will no longer try to start
+  // streaming, and is now waiting for the resource to call the client's finish
+  // callback (regardless of whether the resource is finished loading or
+  // finished streaming). Specifically, it causes the kCanStartStreaming to
+  // kStreamingNotAllowed transition. Streaming cannot be started after this is
+  // called.
+  //
+  // If the resource is already streaming, this will be a no-op, and the client
+  // will still only get the finished notification when the streaming completes.
+  //
+  // This function should never be called synchronously (except by
+  // NotifyFinished) as it can trigger all clients' finished callbacks, which in
+  // turn can invoke JavaScript execution.
+  //
+  // TODO(leszeks): Eventually Fetch (with streaming allowed) will be the only
+  // way of starting streaming, and SetClientIsWaitingForFinished will not be
+  // part of the public interface.
+  void SetClientIsWaitingForFinished();
+
+  // Called (only) by ScriptStreamer when streaming completes.
+  //
+  // This function should never be called synchronously as it can trigger all
+  // clients' finished callbacks, which in turn can invoke JavaScript execution.
+  void StreamingFinished();
+
   const ParkableString& SourceText();
 
   AccessControlStatus CalculateAccessControlStatus() const;
 
   SingleCachedMetadataHandler* CacheHandler();
 
+  // Gets the script streamer from the ScriptResource, clearing the resource's
+  // streamer so that it cannot be used twice.
+  ScriptStreamer* TakeStreamer();
+
+  ScriptStreamer::NotStreamingReason NoStreamerReason() const {
+    return not_streaming_reason_;
+  }
+
+  // Used in DCHECKs
+  bool HasStreamer() { return !!streamer_; }
+  bool HasFinishedStreamer() { return streamer_ && streamer_->IsFinished(); }
+
+  // Visible for tests.
+  void SetRevalidatingRequest(const ResourceRequest&) override;
+
  protected:
   CachedMetadataHandler* CreateCachedMetadataHandler(
       std::unique_ptr<CachedMetadataSender> send_callback) override;
 
+  void DestroyDecodedDataForFailedRevalidation() override;
+
+  void NotifyDataReceived(const char* data, size_t size) override;
+
+  // ScriptResources are considered finished when either:
+  //   1. Loading + streaming completes, or
+  //   2. Loading completes + streaming was never started + someone called
+  //      "SetClientIsWaitingForFinished" to block streaming from ever starting.
+  void NotifyFinished() override;
+  bool IsFinishedInternal() const override;
+
  private:
+  // Valid state transitions:
+  //
+  // kCanStartStreaming -> kStreaming -> kWaitingForStreamingToEnd
+  //                                                -> kFinishedNotificationSent
+  // kCanStartStreaming -> kStreamingNotAllowed -> kFinishedNotificationSent
+  enum class StreamingState {
+    kCanStartStreaming,         // Streaming can be started.
+    kStreamingNotAllowed,       // Streaming can no longer be started.
+    kStreaming,                 // Both loading the resource and streaming.
+    kWaitingForStreamingToEnd,  // Resource loaded but streaming not complete.
+    kFinishedNotificationSent   // Everything complete and finish sent.
+  };
+
   class ScriptResourceFactory : public ResourceFactory {
    public:
     ScriptResourceFactory()
@@ -101,7 +197,16 @@
 
   bool CanUseCacheValidator() const override;
 
+  void AdvanceStreamingState(StreamingState new_state);
+
+  // Check that invariants for the state hold.
+  void CheckStreamingState() const;
+
   ParkableString source_text_;
+  Member<ScriptStreamer> streamer_;
+  ScriptStreamer::NotStreamingReason not_streaming_reason_ =
+      ScriptStreamer::kDidntTryToStartStreaming;
+  StreamingState streaming_state_ = StreamingState::kCanStartStreaming;
 };
 
 DEFINE_RESOURCE_TYPE_CASTS(Script);
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.cc b/third_party/blink/renderer/core/script/classic_pending_script.cc
index 4fea45c..f43f869 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.cc
+++ b/third_party/blink/renderer/core/script/classic_pending_script.cc
@@ -55,7 +55,12 @@
   // <spec step="2">Set request's client to settings object.</spec>
   //
   // Note: |element_document| corresponds to the settings object.
-  ScriptResource::Fetch(params, element_document.Fetcher(), pending_script);
+  //
+  // We allow streaming, as WatchForLoad() is always called when the script
+  // needs to execute and the ScriptResource is not finished, so
+  // SetClientIsWaitingForFinished is always set on the resource.
+  ScriptResource::Fetch(params, element_document.Fetcher(), pending_script,
+                        ScriptResource::kAllowStreaming);
   pending_script->CheckState();
   return pending_script;
 }
@@ -88,10 +93,7 @@
       is_external_(is_external),
       ready_state_(is_external ? kWaitingForResource : kReady),
       integrity_failure_(false),
-      is_currently_streaming_(false),
-      not_streamed_reason_(is_external
-                               ? ScriptStreamer::kDidntTryToStartStreaming
-                               : ScriptStreamer::kInlineScript) {
+      is_currently_streaming_(false) {
   CHECK(GetElement());
   MemoryCoordinator::Instance().RegisterClient(this);
 }
@@ -102,7 +104,6 @@
   // TODO(hiroshige): Turn these CHECK()s into DCHECK() before going to beta.
   CHECK(GetElement());
   CHECK_EQ(is_external_, !!GetResource());
-  CHECK(GetResource() || !streamer_);
 }
 
 namespace {
@@ -190,50 +191,25 @@
   MemoryCoordinator::Instance().UnregisterClient(this);
   ClearResource();
   integrity_failure_ = false;
-  CancelStreaming();
 }
 
-void ClassicPendingScript::StreamingFinished() {
-  CheckState();
-  DCHECK(streamer_);  // Should only be called by ScriptStreamer.
-  DCHECK(IsCurrentlyStreaming());
-
-  if (ready_state_ == kWaitingForStreaming) {
-    FinishWaitingForStreaming();
-  } else if (ready_state_ == kReadyStreaming) {
-    FinishReadyStreaming();
-  } else {
-    NOTREACHED();
+void ClassicPendingScript::WatchForLoad(PendingScriptClient* client) {
+  if (is_external_) {
+    // Once we are watching the ClassicPendingScript for load, we won't ever
+    // try to start streaming this resource via this ClassicPendingScript, so
+    // we mark the resource to instead get a finished notification when loading
+    // (rather than streaming) completes.
+    //
+    // Do this in a task rather than directly to make sure the IsReady state
+    // of PendingScript does not change during this call.
+    GetElement()
+        ->GetDocument()
+        .GetTaskRunner(TaskType::kNetworking)
+        ->PostTask(FROM_HERE,
+                   WTF::Bind(&ScriptResource::SetClientIsWaitingForFinished,
+                             WrapPersistent(ToScriptResource(GetResource()))));
   }
-
-  DCHECK(!IsCurrentlyStreaming());
-}
-
-void ClassicPendingScript::FinishWaitingForStreaming() {
-  CheckState();
-  DCHECK(GetResource());
-  DCHECK_EQ(ready_state_, kWaitingForStreaming);
-
-  bool error_occurred = GetResource()->ErrorOccurred() || integrity_failure_;
-  AdvanceReadyState(error_occurred ? kErrorOccurred : kReady);
-}
-
-void ClassicPendingScript::FinishReadyStreaming() {
-  CheckState();
-  DCHECK(GetResource());
-  DCHECK_EQ(ready_state_, kReadyStreaming);
-  AdvanceReadyState(kReady);
-}
-
-void ClassicPendingScript::CancelStreaming() {
-  if (!streamer_)
-    return;
-
-  streamer_->Cancel();
-  streamer_ = nullptr;
-  streamer_done_.Reset();
-  is_currently_streaming_ = false;
-  DCHECK(!IsCurrentlyStreaming());
+  PendingScript::WatchForLoad(client);
 }
 
 void ClassicPendingScript::NotifyFinished(Resource* resource) {
@@ -260,6 +236,7 @@
   //
   // See https://crbug.com/500701 for more information.
   CheckState();
+  DCHECK(GetResource());
   ScriptElementBase* element = GetElement();
   if (element) {
     SubresourceIntegrityHelper::DoReport(element->GetDocument(),
@@ -283,24 +260,11 @@
                                        options_, cross_origin);
   }
 
-  // We are now waiting for script streaming to finish.
-  // If there is no script streamer, this step completes immediately.
-  AdvanceReadyState(kWaitingForStreaming);
-  if (streamer_)
-    streamer_->NotifyFinished();
-  else
-    FinishWaitingForStreaming();
-}
-
-void ClassicPendingScript::DataReceived(Resource* resource,
-                                        const char*,
-                                        size_t) {
-  if (streamer_)
-    streamer_->NotifyAppendData(ToScriptResource(resource));
+  bool error_occurred = GetResource()->ErrorOccurred() || integrity_failure_;
+  AdvanceReadyState(error_occurred ? kErrorOccurred : kReady);
 }
 
 void ClassicPendingScript::Trace(blink::Visitor* visitor) {
-  visitor->Trace(streamer_);
   ResourceClient::Trace(visitor);
   MemoryCoordinatorClient::Trace(visitor);
   PendingScript::Trace(visitor);
@@ -347,9 +311,9 @@
                                             GetElement()->GetDocument());
     }
 
-    DCHECK(!streamer_);
-    DCHECK_EQ(not_streamed_reason_, ScriptStreamer::kInlineScript);
-    RecordStreamingHistogram(GetSchedulingType(), false, not_streamed_reason_);
+    DCHECK(!GetResource());
+    RecordStreamingHistogram(GetSchedulingType(), false,
+                             ScriptStreamer::kInlineScript);
 
     ScriptSourceCode source_code(source_text_for_inline_script_,
                                  source_location_type_, cache_handler,
@@ -370,25 +334,26 @@
 
   // Check if we can use the script streamer.
   bool streamer_ready = false;
-  ScriptStreamer::NotStreamingReason not_streamed_reason = not_streamed_reason_;
-  if (streamer_) {
+  ScriptStreamer::NotStreamingReason not_streamed_reason =
+      resource->NoStreamerReason();
+  ScriptStreamer* streamer = resource->TakeStreamer();
+  if (streamer) {
     DCHECK_EQ(not_streamed_reason, ScriptStreamer::kInvalid);
-    if (streamer_->StreamingSuppressed()) {
-      not_streamed_reason = streamer_->StreamingSuppressedReason();
+    if (streamer->StreamingSuppressed()) {
+      not_streamed_reason = streamer->StreamingSuppressedReason();
     } else if (ready_state_ == kErrorOccurred) {
       not_streamed_reason = ScriptStreamer::kErrorOccurred;
-    } else if (ready_state_ == kReadyStreaming) {
-      not_streamed_reason = ScriptStreamer::kStreamerNotReadyOnGetSource;
     } else {
       // Streamer can be used to compile script.
-      DCHECK_EQ(ready_state_, kReady);
+      CHECK_EQ(ready_state_, kReady);
+      not_streamed_reason = ScriptStreamer::kInvalid;
       streamer_ready = true;
     }
   }
   RecordStreamingHistogram(GetSchedulingType(), streamer_ready,
                            not_streamed_reason);
 
-  ScriptSourceCode source_code(streamer_ready ? streamer_ : nullptr, resource,
+  ScriptSourceCode source_code(streamer_ready ? streamer : nullptr, resource,
                                not_streamed_reason);
   // The base URL for external classic script is
   //
@@ -399,21 +364,6 @@
                                resource->CalculateAccessControlStatus());
 }
 
-void ClassicPendingScript::SetStreamer(ScriptStreamer* streamer) {
-  DCHECK(streamer);
-  DCHECK(!streamer_);
-  DCHECK(!IsWatchingForLoad() || ready_state_ != kWaitingForResource);
-  DCHECK(!streamer->IsFinished());
-  DCHECK(ready_state_ == kWaitingForResource || ready_state_ == kReady);
-
-  streamer_ = streamer;
-  is_currently_streaming_ = true;
-  if (streamer && ready_state_ == kReady)
-    AdvanceReadyState(kReadyStreaming);
-
-  CheckState();
-}
-
 bool ClassicPendingScript::IsReady() const {
   CheckState();
   return ready_state_ >= kReady;
@@ -422,21 +372,12 @@
 void ClassicPendingScript::AdvanceReadyState(ReadyState new_ready_state) {
   // We will allow exactly these state transitions:
   //
-  // kWaitingForResource -> kWaitingForStreaming -> [kReady, kErrorOccurred]
-  // kReady -> kReadyStreaming -> kReady
+  // kWaitingForResource -> [kReady, kErrorOccurred]
   switch (ready_state_) {
     case kWaitingForResource:
-      CHECK_EQ(new_ready_state, kWaitingForStreaming);
-      break;
-    case kWaitingForStreaming:
       CHECK(new_ready_state == kReady || new_ready_state == kErrorOccurred);
       break;
     case kReady:
-      CHECK_EQ(new_ready_state, kReadyStreaming);
-      break;
-    case kReadyStreaming:
-      CHECK_EQ(new_ready_state, kReady);
-      break;
     case kErrorOccurred:
       NOTREACHED();
       break;
@@ -445,6 +386,8 @@
   bool old_is_ready = IsReady();
   ready_state_ = new_ready_state;
 
+  ScriptResource* resource = ToScriptResource(GetResource());
+
   // Did we transition into a 'ready' state?
   if (IsReady() && !old_is_ready && IsWatchingForLoad())
     PendingScriptFinished();
@@ -467,17 +410,16 @@
   // To help diagnose crbug.com/78426, we'll temporarily add some DCHECKs
   // that are a subset of the DCHECKs below:
   if (IsCurrentlyStreaming()) {
-    DCHECK(streamer_);
-    DCHECK(!streamer_->IsFinished());
+    DCHECK(resource->HasStreamer());
+    DCHECK(!resource->HasFinishedStreamer());
   }
 
   // IsCurrentlyStreaming should match what streamer_ thinks.
-  DCHECK_EQ(IsCurrentlyStreaming(), streamer_ && !streamer_->IsFinished());
+  DCHECK_EQ(IsCurrentlyStreaming(),
+            resource->HasStreamer() && !resource->HasFinishedStreamer());
   // IsCurrentlyStreaming should match the ready_state_.
   DCHECK_EQ(IsCurrentlyStreaming(),
-            ready_state_ == kReadyStreaming ||
-                (streamer_ && (ready_state_ == kWaitingForResource ||
-                               ready_state_ == kWaitingForStreaming)));
+            resource->HasStreamer() && ready_state_ == kWaitingForResource);
   // We can only have a streamer_done_ callback if we are actually streaming.
   DCHECK(IsCurrentlyStreaming() || !streamer_done_);
 }
@@ -503,27 +445,6 @@
   if (!document || !document->GetFrame())
     return false;
 
-  ScriptState* script_state = ToScriptStateForMainWorld(document->GetFrame());
-  if (!script_state)
-    return false;
-
-  // To support streaming re-try, we'll clear the existing streamer if
-  // it exists; it claims to be finished; but it's finished because streaming
-  // has been suppressed.
-  if (streamer_ && streamer_->StreamingSuppressed() &&
-      streamer_->IsFinished()) {
-    DCHECK_EQ(ready_state_, kReady);
-    DCHECK(!streamer_done_);
-    DCHECK(!IsCurrentlyStreaming());
-    streamer_.Clear();
-  }
-
-  if (streamer_)
-    return false;
-
-  // The two checks above should imply that we're not presently streaming.
-  DCHECK(!IsCurrentlyStreaming());
-
   // Parser blocking scripts tend to do a lot of work in the 'finished'
   // callbacks, while async + in-order scripts all do control-like activities
   // (like posting new tasks). Use the 'control' queue only for control tasks.
@@ -532,17 +453,19 @@
                        ? TaskType::kNetworking
                        : TaskType::kNetworkingControl;
 
-  DCHECK(!streamer_);
   DCHECK(!IsCurrentlyStreaming());
   DCHECK(!streamer_done_);
-  ScriptStreamer::StartStreaming(this, document->GetTaskRunner(task_type),
-                                 &not_streamed_reason_);
-  DCHECK(streamer_ || not_streamed_reason_ != ScriptStreamer::kInvalid);
-  bool success = streamer_ && !streamer_->IsStreamingFinished();
+
+  ReadyState ready_state_before_stream = ready_state_;
+  bool success = ToScriptResource(GetResource())
+                     ->StartStreaming(document->GetTaskRunner(task_type));
+  // We have to make sure that the ready state is not changed by starting
+  // streaming, just in case we're relying on IsReady being false.
+  CHECK_EQ(ready_state_before_stream, ready_state_);
 
   // If we have successfully started streaming, we are required to call the
   // callback.
-  DCHECK_EQ(success, IsCurrentlyStreaming());
+  is_currently_streaming_ = success;
   if (success)
     streamer_done_ = std::move(done);
   return success;
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.h b/third_party/blink/renderer/core/script/classic_pending_script.h
index 98777d1..6655de5 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.h
+++ b/third_party/blink/renderer/core/script/classic_pending_script.h
@@ -59,6 +59,8 @@
     return blink::ScriptType::kClassic;
   }
 
+  void WatchForLoad(PendingScriptClient*) override;
+
   ClassicScript* GetSource(const KURL& document_url) const override;
   bool IsReady() const override;
   bool IsExternal() const override { return is_external_; }
@@ -75,12 +77,10 @@
  private:
   // See AdvanceReadyState implementation for valid state transitions.
   enum ReadyState {
-    // These states are considered "not ready".
+    // This state is considered "not ready".
     kWaitingForResource,
-    kWaitingForStreaming,
     // These states are considered "ready".
     kReady,
-    kReadyStreaming,
     kErrorOccurred,
   };
 
@@ -95,16 +95,11 @@
   // appropriate.
   void AdvanceReadyState(ReadyState);
 
-  // Handle the end of streaming.
-  void FinishWaitingForStreaming();
-  void FinishReadyStreaming();
-  void CancelStreaming();
   void CheckState() const override;
 
   // ResourceClient
   void NotifyFinished(Resource*) override;
   String DebugName() const override { return "PendingScript"; }
-  void DataReceived(Resource*, const char*, size_t) override;
 
   static void RecordStreamingHistogram(
       ScriptSchedulingType type,
@@ -134,7 +129,6 @@
   // The request is intervened by document.write() intervention.
   bool intervened_ = false;
 
-  Member<ScriptStreamer> streamer_;
   base::OnceClosure streamer_done_;
 
   // This flag tracks whether streamer_ is currently streaming. It is used
diff --git a/third_party/blink/renderer/core/script/document_write_intervention.cc b/third_party/blink/renderer/core/script/document_write_intervention.cc
index 9e9553c..ed287c4 100644
--- a/third_party/blink/renderer/core/script/document_write_intervention.cc
+++ b/third_party/blink/renderer/core/script/document_write_intervention.cc
@@ -219,7 +219,8 @@
       resource->Url(), element_document.GetSecurityOrigin(), cross_origin,
       resource->Encoding(), FetchParameters::kIdleLoad);
   AddHeader(&params);
-  ScriptResource::Fetch(params, element_document.Fetcher(), nullptr);
+  ScriptResource::Fetch(params, element_document.Fetcher(), nullptr,
+                        ScriptResource::kNoStreaming);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/pending_script.h b/third_party/blink/renderer/core/script/pending_script.h
index d031cc7..02cf4944 100644
--- a/third_party/blink/renderer/core/script/pending_script.h
+++ b/third_party/blink/renderer/core/script/pending_script.h
@@ -76,7 +76,7 @@
     return parser_blocking_load_start_time_;
   }
 
-  void WatchForLoad(PendingScriptClient*);
+  virtual void WatchForLoad(PendingScriptClient*);
   void StopWatchingForLoad();
   void PendingScriptFinished();
 
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.cc b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.cc
index 501f51b..e011cb6d 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.cc
@@ -31,13 +31,39 @@
 TrustedHTML* TrustedTypePolicy::createHTML(ScriptState* script_state,
                                            const String& input,
                                            ExceptionState& exception_state) {
+  return CreateHTML(script_state->GetIsolate(), input, exception_state);
+}
+
+TrustedScript* TrustedTypePolicy::createScript(
+    ScriptState* script_state,
+    const String& input,
+    ExceptionState& exception_state) {
+  return CreateScript(script_state->GetIsolate(), input, exception_state);
+}
+
+TrustedScriptURL* TrustedTypePolicy::createScriptURL(
+    ScriptState* script_state,
+    const String& input,
+    ExceptionState& exception_state) {
+  return CreateScriptURL(script_state->GetIsolate(), input, exception_state);
+}
+
+TrustedURL* TrustedTypePolicy::createURL(ScriptState* script_state,
+                                         const String& input,
+                                         ExceptionState& exception_state) {
+  return CreateURL(script_state->GetIsolate(), input, exception_state);
+}
+
+TrustedHTML* TrustedTypePolicy::CreateHTML(v8::Isolate* isolate,
+                                           const String& input,
+                                           ExceptionState& exception_state) {
   if (!policy_options_->createHTML()) {
     exception_state.ThrowTypeError(
         "Policy " + name_ +
         "'s TrustedTypePolicyOptions did not specify a 'createHTML' member.");
     return nullptr;
   }
-  v8::TryCatch try_catch(script_state->GetIsolate());
+  v8::TryCatch try_catch(isolate);
   String html;
   if (!policy_options_->createHTML()->Invoke(nullptr, input).To(&html)) {
     DCHECK(try_catch.HasCaught());
@@ -47,8 +73,8 @@
   return TrustedHTML::Create(html);
 }
 
-TrustedScript* TrustedTypePolicy::createScript(
-    ScriptState* script_state,
+TrustedScript* TrustedTypePolicy::CreateScript(
+    v8::Isolate* isolate,
     const String& input,
     ExceptionState& exception_state) {
   if (!policy_options_->createScript()) {
@@ -57,7 +83,7 @@
         "'s TrustedTypePolicyOptions did not specify a 'createScript' member.");
     return nullptr;
   }
-  v8::TryCatch try_catch(script_state->GetIsolate());
+  v8::TryCatch try_catch(isolate);
   String script;
   if (!policy_options_->createScript()->Invoke(nullptr, input).To(&script)) {
     DCHECK(try_catch.HasCaught());
@@ -67,8 +93,8 @@
   return TrustedScript::Create(script);
 }
 
-TrustedScriptURL* TrustedTypePolicy::createScriptURL(
-    ScriptState* script_state,
+TrustedScriptURL* TrustedTypePolicy::CreateScriptURL(
+    v8::Isolate* isolate,
     const String& input,
     ExceptionState& exception_state) {
   if (!policy_options_->createScriptURL()) {
@@ -77,7 +103,7 @@
                                    "specify a 'createScriptURL' member.");
     return nullptr;
   }
-  v8::TryCatch try_catch(script_state->GetIsolate());
+  v8::TryCatch try_catch(isolate);
   String script_url;
   if (!policy_options_->createScriptURL()
            ->Invoke(nullptr, input)
@@ -89,7 +115,7 @@
   return TrustedScriptURL::Create(KURL(script_url));
 }
 
-TrustedURL* TrustedTypePolicy::createURL(ScriptState* script_state,
+TrustedURL* TrustedTypePolicy::CreateURL(v8::Isolate* isolate,
                                          const String& input,
                                          ExceptionState& exception_state) {
   if (!policy_options_->createURL()) {
@@ -98,7 +124,7 @@
         "'s TrustedTypePolicyOptions did not specify a 'createURL' member.");
     return nullptr;
   }
-  v8::TryCatch try_catch(script_state->GetIsolate());
+  v8::TryCatch try_catch(isolate);
   String url;
   if (!policy_options_->createURL()->Invoke(nullptr, input).To(&url)) {
     DCHECK(try_catch.HasCaught());
@@ -120,4 +146,5 @@
   visitor->Trace(policy_options_);
   ScriptWrappable::Trace(visitor);
 }
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
index d340b79f..4c9f8e0 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "v8/include/v8.h"
 
 namespace blink {
 
@@ -27,6 +28,15 @@
                                    TrustedTypePolicyOptions*,
                                    bool exposed);
 
+  TrustedHTML* CreateHTML(v8::Isolate*, const String&, ExceptionState&);
+  TrustedScript* CreateScript(v8::Isolate*, const String&, ExceptionState&);
+  TrustedScriptURL* CreateScriptURL(v8::Isolate*,
+                                    const String&,
+                                    ExceptionState&);
+  TrustedURL* CreateURL(v8::Isolate*, const String&, ExceptionState&);
+
+  // IDL generates calls with ScriptState*, which contains the Isolate*.
+  // These methods all call the Isolate* variant.
   TrustedHTML* createHTML(ScriptState*, const String&, ExceptionState&);
   TrustedScript* createScript(ScriptState*, const String&, ExceptionState&);
   TrustedScriptURL* createScriptURL(ScriptState*,
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
index f33290ea..502ef389 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.cc
@@ -34,10 +34,15 @@
   // TODO(orsibatiz): After policy naming rules are estabilished, check for the
   // policy_name to be according to them.
   if (policy_map_.Contains(policy_name)) {
-    exception_state.ThrowTypeError("Policy with name" + policy_name +
+    exception_state.ThrowTypeError("Policy with name " + policy_name +
                                    " already exists.");
     return nullptr;
   }
+  if (policy_name == "default" && !exposed) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "The default policy must be exposed.");
+    return nullptr;
+  }
   TrustedTypePolicy* policy = TrustedTypePolicy::Create(
       policy_name, const_cast<TrustedTypePolicyOptions*>(policy_options),
       exposed);
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
index 93ccd48..c7331c5 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
@@ -9,10 +9,14 @@
 #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script_url.h"
 #include "third_party/blink/renderer/bindings/core/v8/usv_string_or_trusted_url.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_html.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_script.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_script_url.h"
+#include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h"
+#include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_url.h"
 
 namespace blink {
@@ -68,17 +72,34 @@
          RuntimeEnabledFeatures::TrustedDOMTypesEnabled());
   DCHECK(!string_or_trusted_html.IsNull());
 
-  if (!string_or_trusted_html.IsTrustedHTML() && doc &&
-      doc->RequireTrustedTypes()) {
+  bool require_trusted_type = doc && doc->RequireTrustedTypes();
+  if (!require_trusted_type && string_or_trusted_html.IsString()) {
+    return string_or_trusted_html.GetAsString();
+  }
+
+  if (string_or_trusted_html.IsTrustedHTML()) {
+    return string_or_trusted_html.GetAsTrustedHTML()->toString();
+  }
+
+  TrustedTypePolicy* default_policy =
+      doc->ExecutingWindow()->trustedTypes()->getExposedPolicy("default");
+  if (!default_policy) {
     exception_state.ThrowTypeError(
         "This document requires `TrustedHTML` assignment.");
     return g_empty_string;
   }
 
-  String markup = string_or_trusted_html.IsString()
-                      ? string_or_trusted_html.GetAsString()
-                      : string_or_trusted_html.GetAsTrustedHTML()->toString();
-  return markup;
+  TrustedHTML* result = default_policy->CreateHTML(
+      ToIsolate(doc), string_or_trusted_html.GetAsString(), exception_state);
+  if (exception_state.HadException()) {
+    exception_state.ClearException();
+    exception_state.ThrowTypeError(
+        "This document requires `TrustedHTML` assignment and 'default' policy "
+        "failed to execute.");
+    return g_empty_string;
+  }
+
+  return result->toString();
 }
 
 String GetStringFromTrustedScript(
@@ -89,18 +110,35 @@
          RuntimeEnabledFeatures::TrustedDOMTypesEnabled());
   DCHECK(!string_or_trusted_script.IsNull());
 
-  if (!string_or_trusted_script.IsTrustedScript() && doc &&
-      doc->RequireTrustedTypes()) {
+  bool require_trusted_type = doc && doc->RequireTrustedTypes();
+  if (!require_trusted_type && string_or_trusted_script.IsString()) {
+    return string_or_trusted_script.GetAsString();
+  }
+
+  if (string_or_trusted_script.IsTrustedScript()) {
+    return string_or_trusted_script.GetAsTrustedScript()->toString();
+  }
+
+  TrustedTypePolicy* default_policy =
+      doc->ExecutingWindow()->trustedTypes()->getExposedPolicy("default");
+  if (!default_policy) {
     exception_state.ThrowTypeError(
         "This document requires `TrustedScript` assignment.");
     return g_empty_string;
   }
 
-  String markup =
-      string_or_trusted_script.IsString()
-          ? string_or_trusted_script.GetAsString()
-          : string_or_trusted_script.GetAsTrustedScript()->toString();
-  return markup;
+  TrustedScript* result = default_policy->CreateScript(
+      ToIsolate(doc), string_or_trusted_script.GetAsString(), exception_state);
+  if (exception_state.HadException()) {
+    exception_state.ClearException();
+    exception_state.ThrowTypeError(
+        "This document requires `TrustedScript` assignment and 'default' "
+        "policy "
+        "failed to execute.");
+    return g_empty_string;
+  }
+
+  return result->toString();
 }
 
 String GetStringFromTrustedScriptURL(
@@ -111,18 +149,37 @@
          RuntimeEnabledFeatures::TrustedDOMTypesEnabled());
   DCHECK(!string_or_trusted_script_url.IsNull());
 
-  if (!string_or_trusted_script_url.IsTrustedScriptURL() && doc &&
-      doc->RequireTrustedTypes()) {
+  bool require_trusted_type = doc && doc->RequireTrustedTypes();
+  if (!require_trusted_type && string_or_trusted_script_url.IsString()) {
+    return string_or_trusted_script_url.GetAsString();
+  }
+
+  if (string_or_trusted_script_url.IsTrustedScriptURL()) {
+    return string_or_trusted_script_url.GetAsTrustedScriptURL()->toString();
+  }
+
+  TrustedTypePolicy* default_policy =
+      doc->ExecutingWindow()->trustedTypes()->getExposedPolicy("default");
+  if (!default_policy) {
     exception_state.ThrowTypeError(
         "This document requires `TrustedScriptURL` assignment.");
     return g_empty_string;
   }
 
-  String markup =
-      string_or_trusted_script_url.IsString()
-          ? string_or_trusted_script_url.GetAsString()
-          : string_or_trusted_script_url.GetAsTrustedScriptURL()->toString();
-  return markup;
+  TrustedScriptURL* result = default_policy->CreateScriptURL(
+      ToIsolate(doc), string_or_trusted_script_url.GetAsString(),
+      exception_state);
+
+  if (exception_state.HadException()) {
+    exception_state.ClearException();
+    exception_state.ThrowTypeError(
+        "This document requires `TrustedScriptURL` assignment and 'default' "
+        "policy "
+        "failed to execute.");
+    return g_empty_string;
+  }
+
+  return result->toString();
 }
 
 String GetStringFromTrustedURL(USVStringOrTrustedURL string_or_trusted_url,
@@ -132,16 +189,33 @@
          RuntimeEnabledFeatures::TrustedDOMTypesEnabled());
   DCHECK(!string_or_trusted_url.IsNull());
 
-  if (!string_or_trusted_url.IsTrustedURL() && doc &&
-      doc->RequireTrustedTypes()) {
+  bool require_trusted_type = doc && doc->RequireTrustedTypes();
+  if (!require_trusted_type && string_or_trusted_url.IsUSVString()) {
+    return string_or_trusted_url.GetAsUSVString();
+  }
+
+  if (string_or_trusted_url.IsTrustedURL()) {
+    return string_or_trusted_url.GetAsTrustedURL()->toString();
+  }
+
+  TrustedTypePolicy* default_policy =
+      doc->ExecutingWindow()->trustedTypes()->getExposedPolicy("default");
+  if (!default_policy) {
     exception_state.ThrowTypeError(
         "This document requires `TrustedURL` assignment.");
     return g_empty_string;
   }
 
-  String markup = string_or_trusted_url.IsUSVString()
-                      ? string_or_trusted_url.GetAsUSVString()
-                      : string_or_trusted_url.GetAsTrustedURL()->toString();
-  return markup;
+  TrustedURL* result = default_policy->CreateURL(
+      ToIsolate(doc), string_or_trusted_url.GetAsUSVString(), exception_state);
+  if (exception_state.HadException()) {
+    exception_state.ClearException();
+    exception_state.ThrowTypeError(
+        "This document requires `TrustedURL` assignment and 'default' policy "
+        "failed to execute.");
+    return g_empty_string;
+  }
+
+  return result->toString();
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
index 40d3c24..f17780d 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script_url.h"
 #include "third_party/blink/renderer/bindings/core/v8/usv_string_or_trusted_url.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_html.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_script.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_script_url.h"
@@ -37,11 +38,14 @@
 
 void GetStringFromTrustedHTMLThrows(
     const StringOrTrustedHTML& string_or_trusted_html) {
-  Document* document = Document::CreateForTest();
-  document->SetRequireTrustedTypes();
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder->GetDocument();
+  document.SetRequireTrustedTypes();
+  V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
-  String s = GetStringFromTrustedHTML(string_or_trusted_html, document,
+  String s = GetStringFromTrustedHTML(string_or_trusted_html, &document,
                                       exception_state);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kTypeError, exception_state.CodeAs<ESErrorType>());
@@ -50,11 +54,14 @@
 
 void GetStringFromTrustedScriptThrows(
     const StringOrTrustedScript& string_or_trusted_script) {
-  Document* document = Document::CreateForTest();
-  document->SetRequireTrustedTypes();
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder->GetDocument();
+  document.SetRequireTrustedTypes();
+  V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
-  String s = GetStringFromTrustedScript(string_or_trusted_script, document,
+  String s = GetStringFromTrustedScript(string_or_trusted_script, &document,
                                         exception_state);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kTypeError, exception_state.CodeAs<ESErrorType>());
@@ -63,12 +70,15 @@
 
 void GetStringFromTrustedScriptURLThrows(
     const StringOrTrustedScriptURL& string_or_trusted_script_url) {
-  Document* document = Document::CreateForTest();
-  document->SetRequireTrustedTypes();
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder->GetDocument();
+  document.SetRequireTrustedTypes();
+  V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
   String s = GetStringFromTrustedScriptURL(string_or_trusted_script_url,
-                                           document, exception_state);
+                                           &document, exception_state);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kTypeError, exception_state.CodeAs<ESErrorType>());
   exception_state.ClearException();
@@ -76,12 +86,15 @@
 
 void GetStringFromTrustedURLThrows(
     const USVStringOrTrustedURL& string_or_trusted_url) {
-  Document* document = Document::CreateForTest();
-  document->SetRequireTrustedTypes();
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder->GetDocument();
+  document.SetRequireTrustedTypes();
+  V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   ASSERT_FALSE(exception_state.HadException());
-  String s =
-      GetStringFromTrustedURL(string_or_trusted_url, document, exception_state);
+  String s = GetStringFromTrustedURL(string_or_trusted_url, &document,
+                                     exception_state);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kTypeError, exception_state.CodeAs<ESErrorType>());
   exception_state.ClearException();
@@ -103,10 +116,13 @@
 void GetStringFromTrustedHTMLWorks(
     const StringOrTrustedHTML& string_or_trusted_html,
     String expected) {
-  Document* document = Document::CreateForTest();
-  document->SetRequireTrustedTypes();
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder->GetDocument();
+  document.SetRequireTrustedTypes();
+  V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
-  String s = GetStringFromTrustedHTML(string_or_trusted_html, document,
+  String s = GetStringFromTrustedHTML(string_or_trusted_html, &document,
                                       exception_state);
   ASSERT_EQ(s, expected);
 }
@@ -114,10 +130,13 @@
 void GetStringFromTrustedScriptWorks(
     const StringOrTrustedScript& string_or_trusted_script,
     String expected) {
-  Document* document = Document::CreateForTest();
-  document->SetRequireTrustedTypes();
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder->GetDocument();
+  document.SetRequireTrustedTypes();
+  V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
-  String s = GetStringFromTrustedScript(string_or_trusted_script, document,
+  String s = GetStringFromTrustedScript(string_or_trusted_script, &document,
                                         exception_state);
   ASSERT_EQ(s, expected);
 }
@@ -125,22 +144,28 @@
 void GetStringFromTrustedScriptURLWorks(
     const StringOrTrustedScriptURL& string_or_trusted_script_url,
     String expected) {
-  Document* document = Document::CreateForTest();
-  document->SetRequireTrustedTypes();
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder->GetDocument();
+  document.SetRequireTrustedTypes();
+  V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
   String s = GetStringFromTrustedScriptURL(string_or_trusted_script_url,
-                                           document, exception_state);
+                                           &document, exception_state);
   ASSERT_EQ(s, expected);
 }
 
 void GetStringFromTrustedURLWorks(
     const USVStringOrTrustedURL& string_or_trusted_url,
     String expected) {
-  Document* document = Document::CreateForTest();
-  document->SetRequireTrustedTypes();
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder->GetDocument();
+  document.SetRequireTrustedTypes();
+  V8TestingScope scope;
   DummyExceptionStateForTesting exception_state;
-  String s =
-      GetStringFromTrustedURL(string_or_trusted_url, document, exception_state);
+  String s = GetStringFromTrustedURL(string_or_trusted_url, &document,
+                                     exception_state);
   ASSERT_EQ(s, expected);
 }
 
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkerCacheViews.js b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkerCacheViews.js
index 6766eb1..a478eb0b 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkerCacheViews.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkerCacheViews.js
@@ -360,7 +360,7 @@
     } else if (columnId === 'responseType') {
       if (this._responseType === 'opaqueResponse') {
         const opaque = UI.XLink.create(
-            'https://developers.google.com/web/tools/workbox/guides/storage-quota#beware_of_opaque_responses',
+            'https://developers.google.com/web/tools/chrome-devtools/progressive-web-apps#opaque-responses',
             ls`opaque`);
         opaque.title = ls`As a security consideration, an opaque response potentially takes ` +
             ls`up far more cache space than its content length`;
diff --git a/third_party/blink/renderer/devtools/scripts/compile_frontend.py b/third_party/blink/renderer/devtools/scripts/compile_frontend.py
index 223b0142..da85cb7 100755
--- a/third_party/blink/renderer/devtools/scripts/compile_frontend.py
+++ b/third_party/blink/renderer/devtools/scripts/compile_frontend.py
@@ -216,7 +216,7 @@
     if match:
         major = int(match.group(1))
         minor = int(match.group(2))
-        is_ok = major >= required_major and minor >= required_minor
+        is_ok = major > required_major or major == required_major and minor >= required_minor
     if is_ok:
         exec_command = [java_path, '-Xms1024m', '-server', '-XX:+TieredCompilation']
         check_server_proc = popen(exec_command + ['-version'])
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index de3dc6a..91fa414 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -196,7 +196,9 @@
     "peerconnection/adapters/test/mock_ice_transport_adapter_cross_thread_factory.h",
     "peerconnection/adapters/test/mock_p2p_quic_packet_transport.h",
     "peerconnection/adapters/test/mock_p2p_quic_stream.h",
+    "peerconnection/adapters/test/mock_p2p_quic_stream_delegate.h",
     "peerconnection/adapters/test/mock_p2p_quic_transport.h",
+    "peerconnection/adapters/test/mock_p2p_quic_transport_delegate.h",
     "peerconnection/adapters/test/mock_p2p_quic_transport_factory.h",
     "peerconnection/testing/internals_rtc_certificate.cc",
     "peerconnection/testing/internals_rtc_certificate.h",
@@ -309,6 +311,7 @@
     "payments/payment_test_helper.cc",
     "payments/payment_test_helper.h",
     "payments/payments_validators_test.cc",
+    "peerconnection/adapters/p2p_quic_stream_unittest.cc",
     "peerconnection/adapters/p2p_quic_transport_test.cc",
     "peerconnection/rtc_data_channel_test.cc",
     "peerconnection/rtc_ice_transport_test.cc",
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index f7b83a0..8c4fc0f 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -9,36 +9,18 @@
 #include <memory>
 
 #include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/numerics/checked_math.h"
 #include "third_party/blink/renderer/core/css/cssom/css_url_image_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
-#include "third_party/blink/renderer/core/html/canvas/image_data.h"
 #include "third_party/blink/renderer/core/html/canvas/text_metrics.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
-#include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h"
 #include "third_party/blink/renderer/core/svg/svg_image_element.h"
-#include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
-#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_gradient.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_pattern.h"
-#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/path_2d.h"
-#include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
-#include "third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h"
-#include "third_party/blink/renderer/platform/graphics/color.h"
-#include "third_party/blink/renderer/platform/graphics/image.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
-#include "third_party/blink/renderer/platform/histogram.h"
-#include "third_party/blink/renderer/platform/wtf/math_extras.h"
 
 namespace blink {
 
@@ -1291,30 +1273,50 @@
           : CanvasRenderingContext2DState::kNonOpaqueImage);
 
   ValidateStateStack();
-
+  bool source_is_canvas = false;
   if (!IsPaint2D()) {
-    std::string histogram_name = "Blink.Canvas.DrawImage.Duration.";
+    std::string image_source_name;
     if (image_source->IsCanvasElement()) {
-      histogram_name.append("Canvas.");
+      image_source_name = "Canvas";
+      source_is_canvas = true;
     } else if (image_source->IsCSSImageValue()) {
-      histogram_name.append("CssImage.");
+      image_source_name = "CssImage";
     } else if (image_source->IsImageElement()) {
-      histogram_name.append("ImageElement.");
+      image_source_name = "ImageElement";
     } else if (image_source->IsImageBitmap()) {
-      histogram_name.append("ImageBitmap.");
+      image_source_name = "ImageBitmap";
     } else if (image_source->IsOffscreenCanvas()) {
-      histogram_name.append("OffscreenCanvas.");
+      image_source_name = "OffscreenCanvas";
+      source_is_canvas = true;
     } else if (image_source->IsSVGSource()) {
-      histogram_name.append("SVG.");
+      image_source_name = "SVG";
     } else if (image_source->IsVideoElement()) {
-      histogram_name.append("Video.");
+      image_source_name = "Video";
     } else {  // Unknown source.
-      histogram_name.append("Unknown.");
+      image_source_name = "Unknown";
     }
-    histogram_name.append(
-        CanCreateCanvas2dResourceProvider() && IsAccelerated() ? "GPU" : "CPU");
+
+    std::string duration_histogram_name =
+        "Blink.Canvas.DrawImage.Duration." + image_source_name;
+    std::string size_histogram_name =
+        "Blink.Canvas.DrawImage.SqrtNumberOfPixels." + image_source_name;
+
+    if (CanCreateCanvas2dResourceProvider() && IsAccelerated()) {
+      if (source_is_canvas)
+        size_histogram_name.append(".GPU");
+      duration_histogram_name.append(".GPU");
+    } else {
+      if (source_is_canvas)
+        size_histogram_name.append(".CPU");
+      duration_histogram_name.append(".CPU");
+    }
+
+    int sqrt_pixels = std::sqrt(dst_rect.Width() * dst_rect.Height());
     base::TimeDelta elapsed = TimeTicks::Now() - start_time;
-    UmaHistogramMicrosecondsTimes(histogram_name, elapsed);
+
+    base::UmaHistogramMicrosecondsTimes(duration_histogram_name, elapsed);
+    base::UmaHistogramCustomCounts(size_histogram_name, sqrt_pixels, 1, 5000,
+                                   50);
   }
 }
 
@@ -1667,11 +1669,11 @@
     int scaled_time = getScaledElapsedTime(
         image_data_rect.Width(), image_data_rect.Height(), start_time);
     if (CanCreateCanvas2dResourceProvider() && IsAccelerated()) {
-      UMA_HISTOGRAM_COUNTS_1000("Blink.Canvas.GetImageDataScaledDuration.GPU",
-                                scaled_time);
+      base::UmaHistogramCounts1000(
+          "Blink.Canvas.GetImageDataScaledDuration.GPU", scaled_time);
     } else {
-      UMA_HISTOGRAM_COUNTS_1000("Blink.Canvas.GetImageDataScaledDuration.CPU",
-                                scaled_time);
+      base::UmaHistogramCounts1000(
+          "Blink.Canvas.GetImageDataScaledDuration.CPU", scaled_time);
     }
   }
 
@@ -1773,11 +1775,11 @@
     int scaled_time =
         getScaledElapsedTime(dest_rect.Width(), dest_rect.Height(), start_time);
     if (CanCreateCanvas2dResourceProvider() && IsAccelerated()) {
-      UMA_HISTOGRAM_COUNTS_1000("Blink.Canvas.PutImageDataScaledDuration.GPU",
-                                scaled_time);
+      base::UmaHistogramCounts1000(
+          "Blink.Canvas.PutImageDataScaledDuration.GPU", scaled_time);
     } else {
-      UMA_HISTOGRAM_COUNTS_1000("Blink.Canvas.PutImageDataScaledDuration.CPU",
-                                scaled_time);
+      base::UmaHistogramCounts1000(
+          "Blink.Canvas.PutImageDataScaledDuration.CPU", scaled_time);
     }
   }
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index 29fb2e2d..208b4e5a3 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -5,24 +5,15 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_BASE_RENDERING_CONTEXT_2D_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_BASE_RENDERING_CONTEXT_2D_H_
 
-#include <utility>
-
 #include "third_party/blink/renderer/bindings/modules/v8/canvas_image_source.h"
 #include "third_party/blink/renderer/bindings/modules/v8/string_or_canvas_gradient_or_canvas_pattern.h"
 #include "third_party/blink/renderer/core/geometry/dom_matrix.h"
-#include "third_party/blink/renderer/core/geometry/dom_matrix_2d_init.h"
 #include "third_party/blink/renderer/core/html/canvas/image_data.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_gradient.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h"
-#include "third_party/blink/renderer/platform/graphics/color_behavior.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
-#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
-#include "third_party/skia/include/effects/SkComposeImageFilter.h"
 
 namespace blink {
 class CanvasImageSource;
diff --git a/third_party/blink/renderer/modules/mediasession/media_session.cc b/third_party/blink/renderer/modules/mediasession/media_session.cc
index 697e0f4..d232feb 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_session.cc
@@ -20,7 +20,7 @@
 
 namespace {
 
-using ::blink::mojom::blink::MediaSessionAction;
+using ::media_session::mojom::blink::MediaSessionAction;
 
 const AtomicString& MojomActionToActionName(MediaSessionAction action) {
   DEFINE_STATIC_LOCAL(const AtomicString, play_action_name, ("play"));
@@ -35,17 +35,17 @@
                       ("seekforward"));
 
   switch (action) {
-    case MediaSessionAction::PLAY:
+    case MediaSessionAction::kPlay:
       return play_action_name;
-    case MediaSessionAction::PAUSE:
+    case MediaSessionAction::kPause:
       return pause_action_name;
-    case MediaSessionAction::PREVIOUS_TRACK:
+    case MediaSessionAction::kPreviousTrack:
       return previous_track_action_name;
-    case MediaSessionAction::NEXT_TRACK:
+    case MediaSessionAction::kNextTrack:
       return next_track_action_name;
-    case MediaSessionAction::SEEK_BACKWARD:
+    case MediaSessionAction::kSeekBackward:
       return seek_backward_action_name;
-    case MediaSessionAction::SEEK_FORWARD:
+    case MediaSessionAction::kSeekForward:
       return seek_forward_action_name;
     default:
       NOTREACHED();
@@ -56,17 +56,17 @@
 base::Optional<MediaSessionAction> ActionNameToMojomAction(
     const String& action_name) {
   if ("play" == action_name)
-    return MediaSessionAction::PLAY;
+    return MediaSessionAction::kPlay;
   if ("pause" == action_name)
-    return MediaSessionAction::PAUSE;
+    return MediaSessionAction::kPause;
   if ("previoustrack" == action_name)
-    return MediaSessionAction::PREVIOUS_TRACK;
+    return MediaSessionAction::kPreviousTrack;
   if ("nexttrack" == action_name)
-    return MediaSessionAction::NEXT_TRACK;
+    return MediaSessionAction::kNextTrack;
   if ("seekbackward" == action_name)
-    return MediaSessionAction::SEEK_BACKWARD;
+    return MediaSessionAction::kSeekBackward;
   if ("seekforward" == action_name)
-    return MediaSessionAction::SEEK_FORWARD;
+    return MediaSessionAction::kSeekForward;
 
   NOTREACHED();
   return base::nullopt;
@@ -213,7 +213,7 @@
 }
 
 void MediaSession::DidReceiveAction(
-    blink::mojom::blink::MediaSessionAction action) {
+    media_session::mojom::blink::MediaSessionAction action) {
   Document* document = To<Document>(GetExecutionContext());
   std::unique_ptr<UserGestureIndicator> gesture_indicator =
       LocalFrame::NotifyUserActivation(document ? document->GetFrame()
diff --git a/third_party/blink/renderer/modules/mediasession/media_session.h b/third_party/blink/renderer/modules/mediasession/media_session.h
index 45c9a2f..303f763 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session.h
+++ b/third_party/blink/renderer/modules/mediasession/media_session.h
@@ -62,7 +62,8 @@
   void NotifyActionChange(const String& action, ActionChangeType);
 
   // blink::mojom::blink::MediaSessionClient implementation.
-  void DidReceiveAction(blink::mojom::blink::MediaSessionAction) override;
+  void DidReceiveAction(
+      media_session::mojom::blink::MediaSessionAction) override;
 
   // Returns null when the ExecutionContext is not document.
   mojom::blink::MediaSessionService* GetService();
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h
index e6833dc..9824069 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_STREAM_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_STREAM_H_
 
+#include <stdint.h>
+#include <vector>
+
 namespace blink {
 
 // The bidirectional QUIC stream object to be used by the RTCQuicStream Web
@@ -13,7 +16,8 @@
 // Lifetime: The P2PQuicStream is owned by the P2PQuicTransport, and can be
 // deleted after the stream is closed for reading and writing. This can happen
 // in 3 ways: 1) OnRemoteReset has been fired. 2) Calling Reset(). 3) Both
-// Finish() has been called and OnRemoteFinish has been fired.
+// a FIN bit has been sent with WriteData(_, true) and OnRemoteFinish has been
+// fired.
 class P2PQuicStream {
  public:
   // Receives callbacks for receiving RST_STREAM frames or a STREAM_FRAME with
@@ -29,13 +33,21 @@
     // deleted by the quic::QuicSession.
     virtual void OnRemoteReset() {}
 
-    // Called when the P2PQuicStream has consumed all incoming data from the
-    // remote side up to the FIN bit. Consuming means that the data is marked
-    // as consumed by quic::QuicStreamSequencer, but the data has not
-    // necessarily been read by the application. If the stream has already
-    // finished writing, then upon consuming the FIN bit the stream can no
-    // longer read or write and is deleted by the quic::QuicSession.
-    virtual void OnRemoteFinish() {}
+    // Called when the P2PQuicStream has received data from the remote side.
+    // If |fin| is set to true that means that the FIN bit has been received
+    // and the Delegate will no longer receive data with OnDataReceived.
+    // If the stream has already finished writing, then upon receiving the FIN
+    // bit the stream can no longer read or write and is deleted by the
+    // quic::QuicSession.
+    virtual void OnDataReceived(std::vector<uint8_t> data, bool fin) {}
+
+    // Called when data written with WriteData() has been consumed by QUIC.
+    //
+    // This will happen immediately after calling WriteData(), unless QUIC has
+    // buffered the data in which case it will be fired when the stream is no
+    // longer write blocked and the data is consumed. |amount| specifies how
+    // much data was consumed in bytes.
+    virtual void OnWriteDataConsumed(uint32_t amount) {}
   };
 
   virtual ~P2PQuicStream() = default;
@@ -48,18 +60,24 @@
   // received from the remote side, because the local stream is already closed.
   virtual void Reset() = 0;
 
-  // Sends a stream frame with the FIN bit set, which notifies the remote side
-  // that this stream is done writing. The stream can no longer write after
-  // calling this function. If the stream has already received a FIN bit, this
-  // will close the stream for reading & writing and it will be deleted by the
-  // quic::QuicSession.
-  virtual void Finish() = 0;
+  // Marks received data of size |amount| as being consumed by the Delegate.
+  // This is used in conjuction with Delegate::OnDataReceived, to let the
+  // P2PQuicStream know that received data has been consumed. This allows the
+  // P2PQuicStream to send back pressure to the send side, if the Delegate
+  // cannot receive more data.
+  virtual void MarkReceivedDataConsumed(uint32_t amount) = 0;
+
+  // Writes |data| to a STREAM frame and gives it to QUIC to be buffered or sent
+  // to the remote endpoint. Once that data has been sent Delegate::OnDataSent()
+  // will be fired. Specifying |fin| to true will mark the STREAM frame with the
+  // FIN bit set, which notifies the remote side that this stream is done
+  // writing. After sending the FIN bit, the P2PQuicStream can no longer write.
+  // Once the P2PQuicStream has sent AND received the FIN bit it will be closed
+  // for reading and writing and deleted by the quic::QuicSession.
+  virtual void WriteData(std::vector<uint8_t> data, bool fin) = 0;
 
   // Sets the delegate object, which must outlive the P2PQuicStream.
   virtual void SetDelegate(Delegate* delegate) = 0;
-
-  // TODO:(https://crbug.com/874296): Create functions for reading and writing,
-  // specifically for waitForReadable/waitForWriteable.
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.cc
index ce6c4f4..150acf5b 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.cc
@@ -9,17 +9,83 @@
 namespace blink {
 
 P2PQuicStreamImpl::P2PQuicStreamImpl(quic::QuicStreamId id,
-                                     quic::QuicSession* session)
-    : quic::QuicStream(id, session, /*is_static=*/false, quic::BIDIRECTIONAL) {}
+                                     quic::QuicSession* session,
+                                     uint32_t delegate_read_buffer_size,
+                                     uint32_t write_buffer_size)
+    : quic::QuicStream(id, session, /*is_static=*/false, quic::BIDIRECTIONAL),
+      delegate_read_buffer_size_(delegate_read_buffer_size),
+      write_buffer_size_(write_buffer_size) {
+  DCHECK_GT(delegate_read_buffer_size_, 0u);
+  DCHECK_GT(write_buffer_size_, 0u);
+}
 
 P2PQuicStreamImpl::~P2PQuicStreamImpl() {}
 
 void P2PQuicStreamImpl::OnDataAvailable() {
-  // We just drop the data by marking all data as immediately consumed.
-  sequencer()->MarkConsumed(sequencer()->ReadableBytes());
-  if (sequencer()->IsClosed()) {
-    // This means all data has been consumed up to the FIN bit.
-    OnFinRead();
+  DCHECK(delegate_);
+  if (!sequencer()->HasBytesToRead() && sequencer()->IsClosed()) {
+    // We have consumed all data from the sequencer up to the FIN bit. This can
+    // only occur by receiving an empty STREAM frame with the FIN bit set.
+    quic::QuicStream::OnFinRead();
+    delegate_->OnDataReceived(std::vector<uint8_t>(), /*fin=*/true);
+    consumed_fin_ = true;
+  }
+
+  uint32_t delegate_read_buffer_available =
+      delegate_read_buffer_size_ - delegate_read_buffered_amount_;
+  uint32_t total_read_amount =
+      std::min(static_cast<uint32_t>(sequencer()->ReadableBytes()),
+               delegate_read_buffer_available);
+  // Nothing to do if the delegate's read buffer can't fit anymore data,
+  // or the sequencer doesn't have any data available to be read.
+  if (total_read_amount == 0 || consumed_fin_) {
+    return;
+  }
+  std::vector<uint8_t> data(total_read_amount);
+  uint32_t current_data_offset = 0;
+  struct iovec iov;
+
+  // Read data from the quic::QuicStreamSequencer until we have exhausted the
+  // data, or have read at least the amount of the delegate's read buffer size.
+  while (sequencer()->GetReadableRegion(&iov)) {
+    uint32_t read_amount = static_cast<uint32_t>(iov.iov_len);
+    if (read_amount == 0) {
+      // Read everything available from the quic::QuicStreamSequencer.
+      DCHECK_EQ(current_data_offset, total_read_amount);
+      break;
+    }
+    // Limit the |consume_amount| by the amount available in the delegate's read
+    // buffer.
+    uint32_t consume_amount = std::min(
+        read_amount, delegate_read_buffer_available - current_data_offset);
+    memcpy(data.data() + current_data_offset, iov.iov_base, consume_amount);
+    sequencer()->MarkConsumed(consume_amount);
+    current_data_offset += consume_amount;
+    if (read_amount > consume_amount) {
+      // The delegate cannot store more data in its read buffer.
+      DCHECK_EQ(current_data_offset, total_read_amount);
+      break;
+    }
+  }
+
+  bool fin = !sequencer()->HasBytesToRead() && sequencer()->IsClosed();
+  delegate_read_buffered_amount_ += data.size();
+  DCHECK(delegate_read_buffer_size_ >= delegate_read_buffered_amount_);
+  if (fin) {
+    quic::QuicStream::OnFinRead();
+    consumed_fin_ = fin;
+  }
+  delegate_->OnDataReceived(std::move(data), fin);
+}
+
+void P2PQuicStreamImpl::OnStreamDataConsumed(size_t bytes_consumed) {
+  DCHECK(delegate_);
+  // We should never consume more than has been written.
+  DCHECK_GE(write_buffered_amount_, bytes_consumed);
+  QuicStream::OnStreamDataConsumed(bytes_consumed);
+  if (bytes_consumed > 0) {
+    write_buffered_amount_ -= bytes_consumed;
+    delegate_->OnWriteDataConsumed(bytes_consumed);
   }
 }
 
@@ -32,10 +98,23 @@
   quic::QuicStream::Reset(quic::QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
 }
 
-void P2PQuicStreamImpl::Finish() {
-  // Should never call Finish twice.
-  DCHECK(!fin_sent());
-  quic::QuicStream::WriteOrBufferData("", /*fin=*/true, nullptr);
+void P2PQuicStreamImpl::MarkReceivedDataConsumed(uint32_t amount) {
+  DCHECK_GE(delegate_read_buffered_amount_, amount);
+  delegate_read_buffered_amount_ -= amount;
+  if (sequencer()->HasBytesToRead() || !consumed_fin_) {
+    OnDataAvailable();
+  }
+}
+
+void P2PQuicStreamImpl::WriteData(std::vector<uint8_t> data, bool fin) {
+  // It is up to the delegate to not write more data than the
+  // |write_buffer_size_|.
+  DCHECK_GE(write_buffer_size_, data.size() + write_buffered_amount_);
+  write_buffered_amount_ += data.size();
+  QuicStream::WriteOrBufferData(
+      quic::QuicStringPiece(reinterpret_cast<const char*>(data.data()),
+                            data.size()),
+      fin, nullptr);
 }
 
 void P2PQuicStreamImpl::SetDelegate(P2PQuicStream::Delegate* delegate) {
@@ -43,8 +122,6 @@
 }
 
 void P2PQuicStreamImpl::OnStreamReset(const quic::QuicRstStreamFrame& frame) {
-  // TODO(https://crbug.com/874296): If we get an incoming stream we need to
-  // make sure that the delegate is set before we have incoming data.
   DCHECK(delegate_);
   // Calling this on the QuicStream will ensure that the stream is closed
   // for reading and writing and we send a RST frame to the remote side if
@@ -53,14 +130,17 @@
   delegate_->OnRemoteReset();
 }
 
-void P2PQuicStreamImpl::OnFinRead() {
-  // TODO(https://crbug.com/874296): If we get an incoming stream we need to
-  // make sure that the delegate is set before we have incoming data.
-  DCHECK(delegate_);
-  // Calling this on the QuicStream ensures that the stream is closed
-  // for reading.
-  quic::QuicStream::OnFinRead();
-  delegate_->OnRemoteFinish();
+void P2PQuicStreamImpl::OnClose() {
+  closed_ = true;
+  quic::QuicStream::OnClose();
+}
+
+bool P2PQuicStreamImpl::IsClosedForTesting() {
+  return closed_;
+}
+
+uint32_t P2PQuicStreamImpl::DelegateReadBufferedAmountForTesting() {
+  return delegate_read_buffered_amount_;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h
index 81cb68d..5d39fe4 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h
@@ -14,10 +14,28 @@
 class MODULES_EXPORT P2PQuicStreamImpl final : public P2PQuicStream,
                                                public quic::QuicStream {
  public:
-  P2PQuicStreamImpl(quic::QuicStreamId id, quic::QuicSession* session);
+  P2PQuicStreamImpl(quic::QuicStreamId id,
+                    quic::QuicSession* session,
+                    uint32_t delegate_read_buffer_size,
+                    uint32_t write_buffer_size);
   ~P2PQuicStreamImpl() override;
+  // P2PQuicStream overrides
+  void SetDelegate(P2PQuicStream::Delegate* delegate) override;
 
-  // QuicStream overrides.
+  void Reset() override;
+
+  void WriteData(std::vector<uint8_t> data, bool fin) override;
+
+  void MarkReceivedDataConsumed(uint32_t amount) override;
+
+  // For testing purposes. This is returns true after quic::QuicStream::OnClose
+  bool IsClosedForTesting();
+
+  // For testing purposes. This exposes the amount of received data that the
+  // P2PQuicStream is aware is buffered by the delegate.
+  uint32_t DelegateReadBufferedAmountForTesting();
+
+  // quic::QuicStream overrides.
   //
   // Right now this marks the data as consumed and drops it.
   // TODO(https://crbug.com/874296): We need to update this function for
@@ -25,31 +43,54 @@
   // busy. See:
   // https://w3c.github.io/webrtc-quic/#dom-rtcquicstream-waitforreadable
   void OnDataAvailable() override;
-
-  // P2PQuicStream overrides
-  void SetDelegate(P2PQuicStream::Delegate* delegate) override;
-
-  void Reset() override;
-
-  void Finish() override;
-
-  // quic::QuicStream overrides
-  //
   // Called by the quic::QuicSession when receiving a RST_STREAM frame from the
   // remote side. This closes the stream for reading & writing (if not already
   // closed), and sends a RST_STREAM frame if one has not been sent yet.
   void OnStreamReset(const quic::QuicRstStreamFrame& frame) override;
+  //  Called by the quic::QuicSession. This means the stream is closed for
+  //  reading
+  // and writing, and can now be deleted by the quic::QuicSession.
+  void OnClose() override;
 
-  // Called when the stream has finished consumed data up to the FIN bit from
-  // the quic::QuicStreamSequencer. This will close the underlying QuicStream
-  // for reading. This can be called either by the P2PQuicStreamImpl when
-  // reading data, or by the quic::QuicStreamSequencer if we're done reading &
-  // receive a stream frame with the FIN bit.
-  void OnFinRead() override;
+ protected:
+  // quic::QuicStream overrides.
+  //
+  // Called when written data (from WriteData()) is consumed by QUIC. This means
+  // the data has either been sent across the wire, or it has been turned into a
+  // packet and queued if the socket is unexpectedly blocked.
+  void OnStreamDataConsumed(size_t bytes_consumed) override;
 
  private:
   using quic::QuicStream::Reset;
+
+  // Outlives the P2PQuicStreamImpl.
   Delegate* delegate_;
+
+  // The read buffer size of the delegate. The |delegate_read_buffered_amount_|
+  // must never exceed this value (enforced by the P2PQuicStreamImpl).
+  const uint32_t delegate_read_buffer_size_;
+  // The maximum size allowed to be buffered write side. The
+  // |write_buffered_amount_| must never exceed this value, and it is up
+  // to the delegate to enforce this.
+  const uint32_t write_buffer_size_;
+  // How much total data has been received and given to the delegate,
+  // but not yet consumed by the delegate. This value gets increased when data
+  // is received from the QUIC library in OnDataAvailable() and and decreased
+  // when the delegate updates that data has been read with
+  // MarkReceivedDataConsumed().
+  uint32_t delegate_read_buffered_amount_ = 0;
+  // How much data is buffered by the QUIC library, but has not yet
+  // been sent. This value gets increased when WriteData() is called
+  // and decreased when OnDataConsumed() gets called by the QUIC library,
+  // due to the data being sent.
+  uint32_t write_buffered_amount_ = 0;
+
+  // Set after OnClose gets called.
+  bool closed_ = false;
+
+  // This is set after the sequencer is closed due to the P2PQuicStream
+  // consuming all of the sequencer's data up to the FIN bit.
+  bool consumed_fin_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc
new file mode 100644
index 0000000..cc5be70
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc
@@ -0,0 +1,426 @@
+// 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 "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h"
+#include "net/test/gtest_util.h"
+#include "net/third_party/quic/core/quic_data_writer.h"
+#include "net/third_party/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream_delegate.h"
+
+namespace blink {
+
+namespace {
+
+using testing::_;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+
+const uint32_t kWriteBufferSize = 1024;
+const uint32_t kDelegateReadBufferSize = 1024;
+const quic::QuicStreamId kStreamId = 5;
+const std::string kSomeData = "howdy";
+
+}  // namespace
+
+// Unit tests for the P2PQuicStream, using a mock QuicSession, which allows
+// us to isolate testing the behaviors of reading a writing.
+class P2PQuicStreamTest : public testing::Test {
+ public:
+  P2PQuicStreamTest()
+      : connection_(
+            new quic::test::MockQuicConnection(&connection_helper_,
+                                               &alarm_factory_,
+                                               quic::Perspective::IS_CLIENT)),
+        session_(connection_) {
+    session_.Initialize();
+    // DCHECKS get hit when the clock is at 0.
+    connection_helper_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1));
+  }
+
+  ~P2PQuicStreamTest() override {}
+
+  void InitializeStream(
+      uint32_t delegate_read_buffer_size = kDelegateReadBufferSize,
+      uint32_t write_buffer_size = kWriteBufferSize) {
+    stream_ = new P2PQuicStreamImpl(
+        kStreamId, &session_, delegate_read_buffer_size, write_buffer_size);
+    stream_->SetDelegate(&delegate_);
+    // The session takes the ownership of the stream.
+    session_.ActivateStream(std::unique_ptr<P2PQuicStreamImpl>(stream_));
+  }
+
+  quic::test::MockQuicConnectionHelper connection_helper_;
+  quic::test::MockAlarmFactory alarm_factory_;
+  // Owned by the |session_|.
+  quic::test::MockQuicConnection* connection_;
+  // The MockQuicSession allows us to see data that is being written and control
+  // whether the data is being "sent across" or blocked.
+  quic::test::MockQuicSession session_;
+  MockP2PQuicStreamDelegate delegate_;
+  // Owned by |session_|.
+  P2PQuicStreamImpl* stream_;
+};
+
+TEST_F(P2PQuicStreamTest, StreamSendsFinAndCanNoLongerWrite) {
+  InitializeStream();
+  EXPECT_CALL(session_, WritevData(stream_, kStreamId, _, _, _))
+      .WillOnce(Invoke(quic::test::MockQuicSession::ConsumeData));
+
+  stream_->WriteData({}, /*fin=*/true);
+
+  EXPECT_TRUE(stream_->fin_sent());
+  EXPECT_TRUE(stream_->write_side_closed());
+  EXPECT_FALSE(stream_->reading_stopped());
+}
+
+TEST_F(P2PQuicStreamTest, StreamResetSendsRst) {
+  InitializeStream();
+  EXPECT_CALL(session_, SendRstStream(kStreamId, _, _));
+  stream_->Reset();
+  EXPECT_TRUE(stream_->rst_sent());
+}
+
+// Tests that when a stream receives a stream frame with the FIN bit set it
+// will fire the appropriate callback and close the stream for reading.
+TEST_F(P2PQuicStreamTest, StreamOnStreamFrameWithFin) {
+  InitializeStream();
+  EXPECT_CALL(delegate_, OnDataReceived(_, /*fin=*/true));
+
+  quic::QuicStreamFrame fin_frame(kStreamId, /*fin=*/true, 0, 0);
+  stream_->OnStreamFrame(fin_frame);
+
+  EXPECT_TRUE(stream_->reading_stopped());
+  EXPECT_FALSE(stream_->write_side_closed());
+}
+
+// Tests that when a stream receives a stream frame with the FIN bit set after
+// it has written the FIN bit, then the stream will close.
+TEST_F(P2PQuicStreamTest, StreamClosedAfterSendingThenReceivingFin) {
+  InitializeStream();
+  EXPECT_CALL(session_, WritevData(stream_, kStreamId, _, _, _))
+      .WillOnce(Invoke(quic::test::MockQuicSession::ConsumeData));
+  stream_->WriteData({}, /*fin=*/true);
+  EXPECT_FALSE(stream_->IsClosedForTesting());
+
+  quic::QuicStreamFrame fin_frame(stream_->id(), /*fin=*/true, 0, 0);
+  stream_->OnStreamFrame(fin_frame);
+
+  EXPECT_TRUE(stream_->reading_stopped());
+  EXPECT_TRUE(stream_->write_side_closed());
+  EXPECT_TRUE(stream_->IsClosedForTesting());
+}
+
+// Tests that when a stream writes a FIN bit after receiving a stream frame with
+// the FIN bit then the stream will close.
+TEST_F(P2PQuicStreamTest, StreamClosedAfterReceivingThenSendingFin) {
+  InitializeStream();
+  quic::QuicStreamFrame fin_frame(stream_->id(), /*fin=*/true, 0, 0);
+  stream_->OnStreamFrame(fin_frame);
+  EXPECT_FALSE(stream_->IsClosedForTesting());
+
+  EXPECT_CALL(session_, WritevData(stream_, kStreamId, _, _, _))
+      .WillOnce(Invoke(quic::test::MockQuicSession::ConsumeData));
+
+  stream_->WriteData({}, /*fin=*/true);
+
+  EXPECT_TRUE(stream_->IsClosedForTesting());
+}
+
+// Tests that when a stream writes some data with the FIN bit set, and receives
+// data with the FIN bit set it will become closed.
+TEST_F(P2PQuicStreamTest, StreamClosedAfterWritingAndReceivingDataWithFin) {
+  InitializeStream();
+  EXPECT_CALL(session_, WritevData(stream_, kStreamId,
+                                   /*write_length=*/kSomeData.size(), _, _))
+      .WillOnce(Invoke(quic::test::MockQuicSession::ConsumeData));
+  stream_->WriteData(std::vector<uint8_t>(kSomeData.begin(), kSomeData.end()),
+                     /*fin=*/true);
+  EXPECT_FALSE(stream_->IsClosedForTesting());
+
+  quic::QuicStreamFrame fin_frame_with_data(stream_->id(), /*fin=*/true, 0,
+                                            kSomeData);
+  stream_->OnStreamFrame(fin_frame_with_data);
+
+  EXPECT_TRUE(stream_->reading_stopped());
+  EXPECT_TRUE(stream_->write_side_closed());
+  EXPECT_TRUE(stream_->IsClosedForTesting());
+}
+
+// Tests that when a stream receives a RST_STREAM frame it will fire the
+// appropriate callback and the stream will become closed.
+TEST_F(P2PQuicStreamTest, StreamClosedAfterReceivingReset) {
+  InitializeStream();
+  EXPECT_CALL(delegate_, OnRemoteReset());
+
+  quic::QuicRstStreamFrame rst_frame(quic::kInvalidControlFrameId, kStreamId,
+                                     quic::QUIC_STREAM_CANCELLED, 0);
+  stream_->OnStreamReset(rst_frame);
+
+  EXPECT_TRUE(stream_->IsClosedForTesting());
+}
+
+// Tests that data written to the P2PQuicStream will appropriately get written
+// to the underlying QUIC library.
+TEST_F(P2PQuicStreamTest, StreamWritesData) {
+  InitializeStream();
+  EXPECT_CALL(session_, WritevData(stream_, kStreamId,
+                                   /*write_length=*/kSomeData.size(), _, _))
+      .WillOnce(Invoke([](quic::QuicStream* stream, quic::QuicStreamId id,
+                          size_t write_length, quic::QuicStreamOffset offset,
+                          quic::StreamSendingState state) {
+        // quic::QuicSession::WritevData does not pass the data. The data is
+        // saved to the stream, so we must grab it before it's consumed, in
+        // order to check that it's what was written.
+        std::string data_consumed_by_quic(write_length, 'a');
+        quic::QuicDataWriter writer(write_length, &data_consumed_by_quic[0],
+                                    quic::NETWORK_BYTE_ORDER);
+        stream->WriteStreamData(offset, write_length, &writer);
+
+        EXPECT_EQ(kSomeData, data_consumed_by_quic);
+        EXPECT_EQ(quic::StreamSendingState::NO_FIN, state);
+        return quic::QuicConsumedData(
+            write_length, state != quic::StreamSendingState::NO_FIN);
+      }));
+  EXPECT_CALL(delegate_, OnWriteDataConsumed(kSomeData.size()));
+
+  stream_->WriteData(std::vector<uint8_t>(kSomeData.begin(), kSomeData.end()),
+                     /*fin=*/false);
+}
+
+// Tests that data written to the P2PQuicStream will appropriately get written
+// to the underlying QUIC library with the FIN bit set.
+TEST_F(P2PQuicStreamTest, StreamWritesDataWithFin) {
+  InitializeStream();
+  EXPECT_CALL(session_, WritevData(stream_, kStreamId,
+                                   /*write_length=*/kSomeData.size(), _, _))
+      .WillOnce(Invoke([](quic::QuicStream* stream, quic::QuicStreamId id,
+                          size_t write_length, quic::QuicStreamOffset offset,
+                          quic::StreamSendingState state) {
+        // WritevData does not pass the data. The data is saved to the stream,
+        // so we must grab it before it's consumed, in order to check that it's
+        // what was written.
+        std::string data_consumed_by_quic(write_length, 'a');
+        quic::QuicDataWriter writer(write_length, &data_consumed_by_quic[0],
+                                    quic::NETWORK_BYTE_ORDER);
+        stream->WriteStreamData(offset, write_length, &writer);
+
+        EXPECT_EQ(kSomeData, data_consumed_by_quic);
+        EXPECT_EQ(quic::StreamSendingState::FIN, state);
+        return quic::QuicConsumedData(
+            write_length, state != quic::StreamSendingState::NO_FIN);
+      }));
+  EXPECT_CALL(delegate_, OnWriteDataConsumed(kSomeData.size()));
+
+  stream_->WriteData(std::vector<uint8_t>(kSomeData.begin(), kSomeData.end()),
+                     /*fin=*/true);
+}
+
+// Tests that when written data is not consumed by QUIC (due to buffering),
+// the OnWriteDataConsumed will not get fired.
+TEST_F(P2PQuicStreamTest, StreamWritesDataAndNotConsumedByQuic) {
+  InitializeStream();
+  EXPECT_CALL(delegate_, OnWriteDataConsumed(_)).Times(0);
+  EXPECT_CALL(session_, WritevData(stream_, kStreamId,
+                                   /*write_length=*/kSomeData.size(), _, _))
+      .WillOnce(Invoke([](quic::QuicStream* stream, quic::QuicStreamId id,
+                          size_t write_length, quic::QuicStreamOffset offset,
+                          quic::StreamSendingState state) {
+        // We mock that the QUIC library is not consuming the data, meaning it's
+        // being buffered. In this case, the OnWriteDataConsumed() callback
+        // should not be called.
+        return quic::QuicConsumedData(/*bytes_consumed=*/0,
+                                      quic::StreamSendingState::NO_FIN);
+      }));
+
+  stream_->WriteData(std::vector<uint8_t>(kSomeData.begin(), kSomeData.end()),
+                     /*fin=*/true);
+}
+
+// Tests that OnWriteDataConsumed() is fired with the amount consumed by QUIC.
+// This tests the case when amount consumed by QUIC is less than what is written
+// with P2PQuicStream::WriteData. This can happen when QUIC is receiving back
+// pressure from the receive side, and its "send window" is smaller than the
+// amount attempted to be written.
+TEST_F(P2PQuicStreamTest, StreamWritesDataAndPartiallyConsumedByQuic) {
+  InitializeStream();
+  size_t amount_consumed_by_quic = 2;
+  EXPECT_CALL(delegate_, OnWriteDataConsumed(amount_consumed_by_quic));
+  EXPECT_CALL(session_, WritevData(stream_, kStreamId,
+                                   /*write_length=*/kSomeData.size(), _, _))
+      .WillOnce(Invoke([&amount_consumed_by_quic](
+                           quic::QuicStream* stream, quic::QuicStreamId id,
+                           size_t write_length, quic::QuicStreamOffset offset,
+                           quic::StreamSendingState state) {
+        // We mock that the QUIC library is only consuming some of the data,
+        // meaning the rest is being buffered.
+        return quic::QuicConsumedData(
+            /*bytes_consumed=*/amount_consumed_by_quic,
+            quic::StreamSendingState::NO_FIN);
+      }));
+
+  stream_->WriteData(std::vector<uint8_t>(kSomeData.begin(), kSomeData.end()),
+                     /*fin=*/true);
+}
+
+// Tests if a P2PQuicStream receives data it will appropriately fire the
+// OnDataReceived callback to the delegate.
+TEST_F(P2PQuicStreamTest, StreamReceivesData) {
+  InitializeStream();
+  std::string data = "some_data";
+  quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0, data);
+
+  EXPECT_CALL(delegate_,
+              OnDataReceived(std::vector<uint8_t>(data.begin(), data.end()),
+                             /*fin=*/false));
+
+  stream_->OnStreamFrame(stream_frame);
+}
+
+// Tests that when received data is marked consumed it is appropriately
+// reflected in the P2PQuicStream's view of the delegate read buffer size.
+TEST_F(P2PQuicStreamTest, MarkConsumedData) {
+  InitializeStream();
+  std::string data = "some_data";
+  quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0, data);
+
+  EXPECT_CALL(delegate_, OnDataReceived(_, _));
+  stream_->OnStreamFrame(stream_frame);
+  // At this point the application has received data but not marked is as
+  // consumed, so from the P2PQuicStream perspective that data has been
+  // buffered.
+  EXPECT_EQ(data.size(), stream_->DelegateReadBufferedAmountForTesting());
+
+  stream_->MarkReceivedDataConsumed(data.size());
+  EXPECT_EQ(0u, stream_->DelegateReadBufferedAmountForTesting());
+}
+
+// Tests that if the delegate's read buffer is "full" from the
+// P2PQuicStream's perspective, then getting more data will not fire the
+// OnDataReceived callback.
+TEST_F(P2PQuicStreamTest, StreamReceivesDataWithFullReadBuffer) {
+  std::string data = "some_data";
+  // The P2PQuicStream is created with a delegate read buffer size equal
+  // to the size of the data that is being received.
+  InitializeStream(/*delegate_read_buffer_size=*/data.size());
+  quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0, data);
+
+  EXPECT_CALL(delegate_, OnDataReceived(_, _)).Times(1);
+  stream_->OnStreamFrame(stream_frame);
+  EXPECT_EQ(data.size(), stream_->DelegateReadBufferedAmountForTesting());
+
+  // Delegate read buffer is now full. Receiving more data should not fire the
+  // callback.
+  std::string more_data = "data2";
+  quic::QuicStreamFrame new_stream_frame(stream_->id(), /*fin=*/false,
+                                         /*offset=*/data.size(), more_data);
+  stream_->OnStreamFrame(new_stream_frame);
+  EXPECT_EQ(data.size(), stream_->DelegateReadBufferedAmountForTesting());
+}
+
+// Tests that if the delegate's read buffer is "full" from the
+// P2PQuicStream's perspective, and then getting an empty STREAM frame with the
+// FIN bit set, will fire Delegate::OnDataReceived with fin set to true.
+TEST_F(P2PQuicStreamTest, StreamReceivesFinWithFullReadBuffer) {
+  std::string data = "some_data";
+  // The P2PQuicStream is created with a delegate read buffer size equal
+  // to the size of the data that is being received.
+  InitializeStream(/*delegate_read_buffer_size=*/data.size());
+  quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0, data);
+
+  EXPECT_CALL(delegate_,
+              OnDataReceived(std::vector<uint8_t>(data.begin(), data.end()),
+                             /*fin=*/false))
+      .Times(1);
+  stream_->OnStreamFrame(stream_frame);
+  EXPECT_EQ(data.size(), stream_->DelegateReadBufferedAmountForTesting());
+
+  // Delegate read buffer is now full, but because the STREAM frame with the FIN
+  // bit doesn't contain any data, it means that all data has been consumed from
+  // the sequencer up to the FIN bit. This fires OnDataReceived with the
+  // fin=true.
+  EXPECT_CALL(delegate_, OnDataReceived(_, true)).Times(1);
+  std::string more_data = "data2";
+  quic::QuicStreamFrame new_stream_frame(stream_->id(), /*fin=*/true,
+                                         /*offset=*/data.size(), 0);
+  stream_->OnStreamFrame(new_stream_frame);
+  EXPECT_EQ(data.size(), stream_->DelegateReadBufferedAmountForTesting());
+}
+
+// Tests that when the delegate's read buffer is "full" from the
+// P2PQuicStream's perspective, the Delegate::OnDataReceived callback is
+// fired after the received data is marked as consumed by the delegate.
+TEST_F(P2PQuicStreamTest, StreamDataConsumedWithFullDelegateReadBuffer) {
+  std::string data = "some_data";
+  // The P2PQuicStream is created with a delegate read buffer size equal
+  // to the size of the data that is being received.
+  InitializeStream(/*delegate_read_buffer_size=*/data.size());
+  quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0, data);
+
+  EXPECT_CALL(delegate_,
+              OnDataReceived(std::vector<uint8_t>(data.begin(), data.end()),
+                             /*fin=*/false))
+      .Times(1);
+  stream_->OnStreamFrame(stream_frame);
+  EXPECT_EQ(data.size(), stream_->DelegateReadBufferedAmountForTesting());
+
+  // Delegate read buffer is now full. Receiving more data should not fire the
+  // callback.
+  std::string more_data = "data2";
+  quic::QuicStreamFrame new_stream_frame(stream_->id(), /*fin=*/true,
+                                         /*offset=*/data.size(), more_data);
+  stream_->OnStreamFrame(new_stream_frame);
+  EXPECT_EQ(data.size(), stream_->DelegateReadBufferedAmountForTesting());
+
+  // Marking the original data as consumed should fire the new data to be
+  // received.
+  EXPECT_CALL(delegate_, OnDataReceived(std::vector<uint8_t>(more_data.begin(),
+                                                             more_data.end()),
+                                        /*fin=*/true))
+      .Times(1);
+  stream_->MarkReceivedDataConsumed(data.size());
+  EXPECT_EQ(more_data.size(), stream_->DelegateReadBufferedAmountForTesting());
+}
+
+// Tests that when receiving more data than available in the delegate read
+// buffer, that the delegate will get an OnDataReceived callback for the amount
+// available in its buffer. Then later when the delegate marks the data as
+// consumed it will get another OnDataReceived callback.
+TEST_F(P2PQuicStreamTest, StreamReceivesMoreDataThanDelegateReadBufferSize) {
+  std::string data = "somedata";
+  // The P2PQuicStream is created with a delegate read buffer size equal
+  // to half of the data being sent.
+  InitializeStream(/*delegate_read_buffer_size=*/4);
+  quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/true, 0, data);
+
+  // Upon receiving the stream frame the Delegate should receive "some", because
+  // that's all it has space to buffer.
+  EXPECT_CALL(delegate_, OnDataReceived(std::vector<uint8_t>(data.begin(),
+                                                             data.begin() + 4),
+                                        /*fin=*/false))
+      .Times(1);
+  stream_->OnStreamFrame(stream_frame);
+  EXPECT_EQ(4u, stream_->DelegateReadBufferedAmountForTesting());
+
+  // Upon consuming 2 bytes of data, the delegate should receive the next part
+  // of the message - "da".
+  EXPECT_CALL(delegate_, OnDataReceived(std::vector<uint8_t>(data.begin() + 4,
+                                                             data.begin() + 6),
+                                        /*fin=*/false))
+      .Times(1);
+  stream_->MarkReceivedDataConsumed(2);
+  EXPECT_EQ(4u, stream_->DelegateReadBufferedAmountForTesting());
+
+  // After consuming 4 bytes of data (all received data thus far), the delegate
+  // should receive the next part of the message - "ta" and the FIN bit.
+  EXPECT_CALL(delegate_,
+              OnDataReceived(std::vector<uint8_t>(data.begin() + 6, data.end()),
+                             /*fin=*/true))
+      .Times(1);
+  stream_->MarkReceivedDataConsumed(4);
+  // Just the last data received ("ta") is held in the delegate's read buffer.
+  EXPECT_EQ(2u, stream_->DelegateReadBufferedAmountForTesting());
+}
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
index a6c67fe..87876d5 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
@@ -22,13 +22,19 @@
       P2PQuicTransport::Delegate* const delegate_in,
       P2PQuicPacketTransport* const packet_transport_in,
       const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>
-          certificates_in)
+          certificates_in,
+      uint32_t stream_delegate_read_buffer_size_in,
+      uint32_t stream_write_buffer_size_in)
       : packet_transport(packet_transport_in),
         certificates(certificates_in),
-        delegate(delegate_in) {
+        delegate(delegate_in),
+        stream_delegate_read_buffer_size(stream_delegate_read_buffer_size_in),
+        stream_write_buffer_size(stream_write_buffer_size_in) {
     DCHECK_GT(certificates.size(), 0u);
     DCHECK(packet_transport);
     DCHECK(delegate);
+    DCHECK_GT(stream_delegate_read_buffer_size, 0u);
+    DCHECK_GT(stream_write_buffer_size, 0u);
   }
   P2PQuicTransportConfig(const P2PQuicTransportConfig&) = delete;
   P2PQuicTransportConfig& operator=(const P2PQuicTransportConfig&) = delete;
@@ -51,6 +57,15 @@
   // to listen and respond to a crypto handshake upon construction.
   // This will NOT start a handshake.
   bool can_respond_to_crypto_handshake = true;
+  // The amount that the delegate can store in its read buffer. This is a
+  // mandatory field that must be set to ensure that the
+  // P2PQuicStream::Delegate will not give the delegate more data than it can
+  // store.
+  const uint32_t stream_delegate_read_buffer_size;
+  // The amount that the P2PQuicStream will allow to buffer. This is a mandatory
+  // field that must be set to ensure that the client of the P2PQuicStream does
+  // not write more data than can be buffered.
+  const uint32_t stream_write_buffer_size;
 };
 
 // For creating a P2PQuicTransport. This factory should be injected into
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
index 4054e0c6..6460c57 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
@@ -151,11 +151,16 @@
                        : quic::Perspective::IS_CLIENT),
       packet_transport_(p2p_transport_config.packet_transport),
       delegate_(p2p_transport_config.delegate),
-      clock_(clock) {
+      clock_(clock),
+      stream_delegate_read_buffer_size_(
+          p2p_transport_config.stream_delegate_read_buffer_size),
+      stream_write_buffer_size_(p2p_transport_config.stream_write_buffer_size) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(delegate_);
   DCHECK(clock_);
   DCHECK(packet_transport_);
+  DCHECK_GT(stream_delegate_read_buffer_size_, 0u);
+  DCHECK_GT(stream_write_buffer_size_, 0u);
   DCHECK_GT(p2p_transport_config.certificates.size(), 0u);
   if (p2p_transport_config.can_respond_to_crypto_handshake) {
     InitializeCryptoStream();
@@ -250,7 +255,8 @@
   DCHECK(crypto_stream_);
   DCHECK(IsEncryptionEstablished());
   DCHECK(!IsClosed());
-  return new P2PQuicStreamImpl(id, this);
+  return new P2PQuicStreamImpl(id, this, stream_delegate_read_buffer_size_,
+                               stream_write_buffer_size_);
 }
 
 void P2PQuicTransportImpl::InitializeCryptoStream() {
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
index 2764b717..e54d28b 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
@@ -170,6 +170,11 @@
   // Owned by whatever creates the P2PQuicTransportImpl. The |clock_| needs to
   // outlive the P2PQuicTransportImpl.
   quic::QuicClock* clock_ = nullptr;
+  // The size of the stream delegate's read buffer, used when creating
+  // P2PQuicStreams.
+  uint32_t stream_delegate_read_buffer_size_;
+  // Determines the size of the write buffer when P2PQuicStreams.
+  uint32_t stream_write_buffer_size_;
 
   THREAD_CHECKER(thread_checker_);
 };
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
index c889ea9..45c52ae 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
@@ -10,6 +10,8 @@
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory_impl.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream_delegate.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport_delegate.h"
 #include "third_party/webrtc/rtc_base/rtccertificate.h"
 #include "third_party/webrtc/rtc_base/sslfingerprint.h"
 #include "third_party/webrtc/rtc_base/sslidentity.h"
@@ -18,133 +20,84 @@
 
 namespace {
 
-// The types of callbacks that can be fired on a P2PQuicTransport::Delegate.
-enum class TransportCallbackType {
-  kNone,
-  kOnRemoteStopped,
-  kOnConnectionFailed,
-  kOnConnected,
-  kOnStream
-};
+using testing::_;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using ::testing::MakePolymorphicAction;
+using ::testing::PolymorphicAction;
 
-// The types of callbacks that can be fired on a P2PQuicStream::Delegate.
-enum class StreamCallbackType { kNone, kOnRemoteReset, kOnRemoteFinish };
+const std::string kTriggerRemoteStreamPhrase = "open sesame";
+const uint32_t kWriteBufferSize = 100 * 1024;
+const uint32_t kDelegateReadBufferSize = 100 * 1024;
 
-// The QuicStreamDelegate implements counters for callbacks. It can also set
-// expectations for a specific callback. When an expectation is set the
-// quic::TestTaskRunner drives the test until the callbacks have been fired, and
-// we are no longer expecting the callback.
-class QuicStreamDelegateForTesting final : public P2PQuicStream::Delegate {
+// A custom gmock Action that fires the given callback. This is used in
+// conjuction with the CallbackRunLoop in order to drive the TestTaskRunner
+// until callbacks are fired. For example:
+//   CallbackRunLoop run_loop(runner());
+//   EXPECT_CALL(&object, foo())
+//       .WillOnce(FireCallback(run_loop.CreateCallback()));
+//   run_loop.RunUntilCallbacksFired(task_runner);
+class FireCallbackAction {
  public:
-  ~QuicStreamDelegateForTesting() override {}
+  FireCallbackAction(base::RepeatingCallback<void()> callback)
+      : callback_(callback) {}
 
-  void OnRemoteReset() override {
-    if (callback_type_expected_ == StreamCallbackType::kOnRemoteReset) {
-      callback_type_expected_ = StreamCallbackType::kNone;
-    }
-    remote_reset_count_++;
-  };
-
-  void OnRemoteFinish() override {
-    if (callback_type_expected_ == StreamCallbackType::kOnRemoteFinish) {
-      callback_type_expected_ = StreamCallbackType::kNone;
-    }
-    remote_finish_count_++;
-  };
-
-  int remote_reset_count() { return remote_reset_count_; }
-
-  int remote_finish_count() { return remote_finish_count_; }
-
-  // Sets the type of callback expected to be called.
-  void ExpectCallback(StreamCallbackType callback_type) {
-    callback_type_expected_ = callback_type;
-  }
-
-  // Returns if we are expecting a callback that hasn't been fired yet.
-  bool IsExpectingCallback() const {
-    return callback_type_expected_ != StreamCallbackType::kNone;
+  template <typename Result, typename ArgumentTuple>
+  Result Perform(const ArgumentTuple& args) const {
+    callback_.Run();
   }
 
  private:
-  int remote_reset_count_ = 0;
-  int remote_finish_count_ = 0;
-  StreamCallbackType callback_type_expected_ = StreamCallbackType::kNone;
+  base::RepeatingCallback<void()> callback_;
 };
 
-// Implements counters for callbacks. It can also set expectations for a
-// specific callback. When an expectation is set the quic::TestTaskRunner
-// drives the test until the callbacks have been fired, and we are no longer
-// expecting the callback.
+// Returns the custom gmock PolymorphicAction created from the
+// FireCallbackAction above.
+PolymorphicAction<FireCallbackAction> FireCallback(
+    base::RepeatingCallback<void()> callback) {
+  return MakePolymorphicAction(FireCallbackAction(callback));
+}
+
+// A helper object that can drive a TestTaskRunner's tasks, until
+// callbacks are fired.
 //
-// TODO(https://crbug.com/874296): If these files get moved to the platform
-// directory we will run the tests in a different test environment. In that case
-// it will make more sense to use the TestCompletionCallback and the RunLoop for
-// driving the test.
-class QuicTransportDelegateForTest final : public P2PQuicTransport::Delegate {
+// TODO(https://crbug.com/874296): If the test files get moved to the platform
+// directory we will run the tests in a different test environment. In that
+// case it will make more sense to use the TestCompletionCallback and the
+// RunLoop for driving the test.
+class CallbackRunLoop {
  public:
-  ~QuicTransportDelegateForTest() override {}
-  void OnRemoteStopped() override {
-    if (callback_type_expected_ == TransportCallbackType::kOnRemoteStopped) {
-      callback_type_expected_ = TransportCallbackType::kNone;
+  CallbackRunLoop(scoped_refptr<net::test::TestTaskRunner> task_runner)
+      : task_runner_(task_runner) {}
+
+  // Drives the run loop until all created callbacks have been fired.
+  // This is done using the |task_runner_|, which runs the tasks
+  // in the correct order and then advances the quic::MockClock to the time the
+  // task is run.
+  void RunUntilCallbacksFired() {
+    while (callback_counter_ != 0) {
+      ASSERT_GT(task_runner_->GetPostedTasks().size(), 0u);
+      task_runner_->RunNextTask();
     }
-    stopped_count_++;
   }
 
-  void OnConnectionFailed(const std::string& error_details,
-                          bool from_remote) override {
-    if (callback_type_expected_ == TransportCallbackType::kOnConnectionFailed) {
-      callback_type_expected_ = TransportCallbackType::kNone;
-    }
-    connection_failed_count_++;
+  // Creates a callback and increments the |callback_counter_|. The callback,
+  // when fired, will decrement the counter. This callback must only
+  // be Run() once (it is a RepeatingCallback because MakePolymorphicAction()
+  // requires that the action is COPYABLE).
+  base::RepeatingCallback<void()> CreateCallback() {
+    callback_counter_++;
+    return base::BindRepeating(&CallbackRunLoop::OnCallbackFired,
+                               base::Unretained(this));
   }
 
-  void OnConnected() override {
-    if (callback_type_expected_ == TransportCallbackType::kOnConnected) {
-      callback_type_expected_ = TransportCallbackType::kNone;
-    }
-    connected_count_++;
-  }
-
-  // We store the remotely created stream.
-  void OnStream(P2PQuicStream* stream) override {
-    if (callback_type_expected_ == TransportCallbackType::kOnStream) {
-      callback_type_expected_ = TransportCallbackType::kNone;
-    }
-    streams_.push_back(static_cast<P2PQuicStreamImpl*>(stream));
-    on_stream_count_++;
-  }
-
-  int stopped_count() const { return stopped_count_; }
-
-  int connection_failed_count() const { return connection_failed_count_; }
-
-  int connected_count() const { return connected_count_; }
-
-  int on_stream_count() const { return on_stream_count_; }
-
-  // Sets the type of callback expected to be called.
-  void ExpectCallback(TransportCallbackType callback_type) {
-    callback_type_expected_ = callback_type;
-  }
-
-  // Returns if we are expecting a callback that hasn't been fired yet.
-  bool IsExpectingCallback() const {
-    return callback_type_expected_ != TransportCallbackType::kNone;
-  }
-
-  std::vector<P2PQuicStreamImpl*> streams() const { return streams_; }
-
  private:
-  TransportCallbackType callback_type_expected_ = TransportCallbackType::kNone;
-  int stopped_count_ = 0;
-  int connection_failed_count_ = 0;
-  int connected_count_ = 0;
-  int on_stream_count_ = 0;
-  // The delegates created for each stream as a result of the remote side
-  // creating streams and sending data (triggering OnStream). P2PQuicStreamsImpl
-  // are owned by the P2PQuicTransport.
-  std::vector<P2PQuicStreamImpl*> streams_;
+  void OnCallbackFired() { callback_counter_--; }
+
+  scoped_refptr<net::test::TestTaskRunner> task_runner_;
+  // Incremented when a callback is created and decremented when the returned
+  // callback is later Run().
+  size_t callback_counter_ = 0;
 };
 
 // This is a fake packet transport to be used by the P2PQuicTransportImpl. It
@@ -271,12 +224,15 @@
   quic::MockClock* clock_;
 };
 
-// A helper class to bundle test objects together.
+// A helper class to bundle test objects together. It keeps track of the
+// P2PQuicTransport, P2PQuicStream and the associated delegate objects. This
+// also keeps track of when callbacks are expected on the delegate objects,
+// which allows running the TestTaskRunner tasks until they have been fired.
 class QuicPeerForTest {
  public:
   QuicPeerForTest(
       std::unique_ptr<FakePacketTransport> packet_transport,
-      std::unique_ptr<QuicTransportDelegateForTest> quic_transport_delegate,
+      std::unique_ptr<MockP2PQuicTransportDelegate> quic_transport_delegate,
       std::unique_ptr<P2PQuicTransportImpl> quic_transport,
       rtc::scoped_refptr<rtc::RTCCertificate> certificate)
       : packet_transport_(std::move(packet_transport)),
@@ -284,9 +240,28 @@
         quic_transport_(std::move(quic_transport)),
         certificate_(certificate) {}
 
+  // A helper that creates a stream and creates and attaches a delegate.
+  void CreateStreamWithDelegate() {
+    stream_ = quic_transport_->CreateStream();
+    stream_delegate_ = std::make_unique<MockP2PQuicStreamDelegate>();
+    stream_->SetDelegate(stream_delegate_.get());
+    stream_id_ = stream_->id();
+  }
+
+  // When a remote stream is created via P2PQuicTransport::Delegate::OnStream,
+  // this is called to set the stream.
+  void SetStreamAndDelegate(
+      P2PQuicStreamImpl* stream,
+      std::unique_ptr<MockP2PQuicStreamDelegate> stream_delegate) {
+    DCHECK(stream);
+    stream_ = stream;
+    stream_id_ = stream->id();
+    stream_delegate_ = std::move(stream_delegate);
+  }
+
   FakePacketTransport* packet_transport() { return packet_transport_.get(); }
 
-  QuicTransportDelegateForTest* quic_transport_delegate() {
+  MockP2PQuicTransportDelegate* quic_transport_delegate() {
     return quic_transport_delegate_.get();
   }
 
@@ -294,9 +269,26 @@
 
   rtc::scoped_refptr<rtc::RTCCertificate> certificate() { return certificate_; }
 
+  P2PQuicStreamImpl* stream() const { return stream_; }
+
+  MockP2PQuicStreamDelegate* stream_delegate() const {
+    return stream_delegate_.get();
+  }
+
+  quic::QuicStreamId stream_id() const { return stream_id_; }
+
  private:
   std::unique_ptr<FakePacketTransport> packet_transport_;
-  std::unique_ptr<QuicTransportDelegateForTest> quic_transport_delegate_;
+  std::unique_ptr<MockP2PQuicTransportDelegate> quic_transport_delegate_;
+  // The corresponding delegate to |stream_|.
+  std::unique_ptr<MockP2PQuicStreamDelegate> stream_delegate_ = nullptr;
+  // Created as a result of CreateStreamWithDelegate() or RemoteStreamCreated().
+  // Owned by the |quic_transport_|.
+  P2PQuicStreamImpl* stream_ = nullptr;
+  // The corresponding ID for |stream_|. This can be used to check if the stream
+  // is closed at the transport level (after the stream object could be
+  // deleted).
+  quic::QuicStreamId stream_id_;
   std::unique_ptr<P2PQuicTransportImpl> quic_transport_;
   rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
 };
@@ -381,9 +373,9 @@
     quic_transport_factory_ = std::make_unique<P2PQuicTransportFactoryImpl>(
         &clock_, std::unique_ptr<net::QuicChromiumAlarmFactory>(alarm_factory));
 
-    std::unique_ptr<FakePacketTransport> client_packet_transport =
+    auto client_packet_transport =
         std::make_unique<FakePacketTransport>(alarm_factory, &clock_);
-    std::unique_ptr<FakePacketTransport> server_packet_transport =
+    auto server_packet_transport =
         std::make_unique<FakePacketTransport>(alarm_factory, &clock_);
     // Connect the transports so that they can speak to each other.
     client_packet_transport->ConnectPeerTransport(
@@ -393,14 +385,13 @@
     rtc::scoped_refptr<rtc::RTCCertificate> client_cert =
         CreateTestCertificate();
 
-    std::unique_ptr<QuicTransportDelegateForTest>
-        client_quic_transport_delegate =
-            std::make_unique<QuicTransportDelegateForTest>();
+    auto client_quic_transport_delegate =
+        std::make_unique<MockP2PQuicTransportDelegate>();
     std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> client_certificates;
     client_certificates.push_back(client_cert);
-    P2PQuicTransportConfig client_config(client_quic_transport_delegate.get(),
-                                         client_packet_transport.get(),
-                                         client_certificates);
+    P2PQuicTransportConfig client_config(
+        client_quic_transport_delegate.get(), client_packet_transport.get(),
+        client_certificates, kDelegateReadBufferSize, kWriteBufferSize);
     client_config.is_server = false;
     client_config.can_respond_to_crypto_handshake =
         can_respond_to_crypto_handshake;
@@ -418,17 +409,16 @@
         std::move(client_quic_transport_delegate),
         std::move(client_quic_transport), client_cert);
 
-    std::unique_ptr<QuicTransportDelegateForTest>
-        server_quic_transport_delegate =
-            std::make_unique<QuicTransportDelegateForTest>();
+    auto server_quic_transport_delegate =
+        std::make_unique<MockP2PQuicTransportDelegate>();
 
     rtc::scoped_refptr<rtc::RTCCertificate> server_cert =
         CreateTestCertificate();
     std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> server_certificates;
     server_certificates.push_back(server_cert);
-    P2PQuicTransportConfig server_config(server_quic_transport_delegate.get(),
-                                         server_packet_transport.get(),
-                                         server_certificates);
+    P2PQuicTransportConfig server_config(
+        server_quic_transport_delegate.get(), server_packet_transport.get(),
+        server_certificates, kDelegateReadBufferSize, kWriteBufferSize);
     server_config.is_server = true;
     server_config.can_respond_to_crypto_handshake =
         can_respond_to_crypto_handshake;
@@ -466,26 +456,6 @@
     server_peer_->quic_transport()->InitializeCryptoStream();
   }
 
-  // Drives the test until we are't expecting any more callbacks to be fired.
-  // This is done using the net::test::TestTaskRunner, which runs the tasks
-  // in the correct order and then advances the quic::MockClock to the time the
-  // task is run.
-  void RunUntilCallbacksFired() {
-    while (server_peer_->quic_transport_delegate()->IsExpectingCallback() ||
-           client_peer_->quic_transport_delegate()->IsExpectingCallback() ||
-           ExpectingStreamCallback()) {
-      // We shouldn't enter a case where we are expecting a callback
-      // and we're out of tasks to run.
-      ASSERT_GT(runner_->GetPostedTasks().size(), 0u);
-      runner_->RunNextTask();
-    }
-  }
-
-  bool ExpectingStreamCallback() {
-    return streams_setup_ && (client_stream_delegate_->IsExpectingCallback() ||
-                              server_stream_delegate_->IsExpectingCallback());
-  }
-
   // Drives the test by running the current tasks that are posted.
   void RunCurrentTasks() {
     size_t posted_tasks_size = runner_->GetPostedTasks().size();
@@ -513,51 +483,60 @@
 
   // Sets up an initial handshake and connection between peers.
   void Connect() {
-    client_peer_->quic_transport_delegate()->ExpectCallback(
-        TransportCallbackType::kOnConnected);
-    server_peer_->quic_transport_delegate()->ExpectCallback(
-        TransportCallbackType::kOnConnected);
+    CallbackRunLoop run_loop(runner());
+
+    EXPECT_CALL(*client_peer_->quic_transport_delegate(), OnConnected())
+        .WillOnce(FireCallback(run_loop.CreateCallback()));
+    EXPECT_CALL(*server_peer_->quic_transport_delegate(), OnConnected())
+        .WillOnce(FireCallback(run_loop.CreateCallback()));
+
     StartHandshake();
-    RunUntilCallbacksFired();
-    ExpectSecureConnection();
+    run_loop.RunUntilCallbacksFired();
   }
 
   // Creates a P2PQuicStreamImpl on both the client and server side that are
-  // connected to each other.
+  // connected to each other. The client's stream is created with
+  // P2PQuicTransport::CreateStream, while the server's stream is initiated from
+  // the remote (client) side, with P2PQuicStream::Delegate::OnStream. This
+  // allows us to test at an integration level with connected streams.
   void SetupConnectedStreams() {
+    CallbackRunLoop run_loop(runner());
     // We must already have a secure connection before streams are created.
     ASSERT_TRUE(client_peer_->quic_transport()->IsEncryptionEstablished());
     ASSERT_TRUE(server_peer_->quic_transport()->IsEncryptionEstablished());
 
-    client_stream_ = client_peer_->quic_transport()->CreateStream();
-    ASSERT_TRUE(client_stream_);
-    client_stream_id_ = client_stream_->id();
-    client_stream_delegate_ = std::make_unique<QuicStreamDelegateForTesting>();
-    client_stream_->SetDelegate(client_stream_delegate_.get());
+    client_peer_->CreateStreamWithDelegate();
+    ASSERT_TRUE(client_peer_->stream());
+    ASSERT_TRUE(client_peer_->stream_delegate());
 
     // Send some data to trigger the remote side (server side) to get an
-    // incoming stream.
-    server_peer_->quic_transport_delegate()->ExpectCallback(
-        TransportCallbackType::kOnStream);
-    client_stream_->WriteOrBufferData("hello", false, nullptr);
-    RunUntilCallbacksFired();
+    // incoming stream. We capture the stream and set it's delegate when
+    // OnStream gets called on the mock object.
+    base::RepeatingCallback<void()> callback = run_loop.CreateCallback();
+    QuicPeerForTest* server_peer_ptr = server_peer_.get();
+    MockP2PQuicStreamDelegate* stream_delegate =
+        new MockP2PQuicStreamDelegate();
+    P2PQuicStream* server_stream;
+    EXPECT_CALL(*server_peer_->quic_transport_delegate(), OnStream(_))
+        .WillOnce(Invoke([&callback, &server_stream,
+                          &stream_delegate](P2PQuicStream* stream) {
+          stream->SetDelegate(stream_delegate);
+          server_stream = stream;
+          callback.Run();
+        }));
 
-    ASSERT_EQ(1u, server_peer_->quic_transport()->GetNumActiveStreams());
-    ASSERT_EQ(1u, client_peer_->quic_transport()->GetNumActiveStreams());
-    ASSERT_EQ(1u, server_peer_->quic_transport_delegate()->streams().size());
-    server_stream_ = server_peer_->quic_transport_delegate()->streams()[0];
-    ASSERT_TRUE(server_stream_);
-    server_stream_id_ = server_stream_->id();
-    server_stream_delegate_ = std::make_unique<QuicStreamDelegateForTesting>();
-    server_stream_->SetDelegate(server_stream_delegate_.get());
-    streams_setup_ = true;
-  }
-
-  void ExpectSecureConnection() {
-    EXPECT_TRUE(client_peer_->quic_transport()->IsEncryptionEstablished());
-    EXPECT_TRUE(client_peer_->quic_transport()->IsCryptoHandshakeConfirmed());
-    EXPECT_TRUE(server_peer_->quic_transport()->IsCryptoHandshakeConfirmed());
-    EXPECT_TRUE(server_peer_->quic_transport()->IsEncryptionEstablished());
+    client_peer_->stream()->WriteData(
+        std::vector<uint8_t>(kTriggerRemoteStreamPhrase.begin(),
+                             kTriggerRemoteStreamPhrase.end()),
+        /*fin=*/false);
+    run_loop.RunUntilCallbacksFired();
+    // Set the stream and delegate to the |server_peer_|, so that it can be
+    // accessed by tests later.
+    server_peer_ptr->SetStreamAndDelegate(
+        static_cast<P2PQuicStreamImpl*>(server_stream),
+        std::unique_ptr<MockP2PQuicStreamDelegate>(stream_delegate));
+    ASSERT_TRUE(client_peer_->stream());
+    ASSERT_TRUE(client_peer_->stream_delegate());
   }
 
   void ExpectConnectionNotEstablished() {
@@ -567,65 +546,27 @@
     EXPECT_FALSE(server_peer_->quic_transport()->IsEncryptionEstablished());
   }
 
-  // Test that the callbacks were called appropriately after a successful
-  // crypto handshake.
-  void ExpectSuccessfulHandshake() {
-    EXPECT_EQ(1, client_peer_->quic_transport_delegate()->connected_count());
-    EXPECT_EQ(0, client_peer_->quic_transport_delegate()->stopped_count());
-    EXPECT_EQ(
-        0, client_peer_->quic_transport_delegate()->connection_failed_count());
-
-    EXPECT_EQ(1, server_peer_->quic_transport_delegate()->connected_count());
-    EXPECT_EQ(0, server_peer_->quic_transport_delegate()->stopped_count());
-    EXPECT_EQ(
-        0, server_peer_->quic_transport_delegate()->connection_failed_count());
-  }
-
   void ExpectTransportsClosed() {
     EXPECT_TRUE(client_peer_->quic_transport()->IsClosed());
     EXPECT_TRUE(server_peer_->quic_transport()->IsClosed());
   }
 
+  // Expects that streams of both the server and client transports are
+  // closed.
   void ExpectStreamsClosed() {
-    ASSERT_TRUE(streams_setup_);
     EXPECT_EQ(0u, client_peer_->quic_transport()->GetNumActiveStreams());
-    EXPECT_TRUE(
-        client_peer_->quic_transport()->IsClosedStream(client_stream_id_));
+    EXPECT_TRUE(client_peer_->quic_transport()->IsClosedStream(
+        client_peer()->stream_id()));
+
     EXPECT_EQ(0u, server_peer_->quic_transport()->GetNumActiveStreams());
-    EXPECT_TRUE(
-        server_peer()->quic_transport()->IsClosedStream(server_stream_id_));
+    EXPECT_TRUE(server_peer()->quic_transport()->IsClosedStream(
+        server_peer()->stream_id()));
   }
 
   // Exposes these private functions to the test.
   bool IsClientClosed() { return client_peer_->quic_transport()->IsClosed(); }
   bool IsServerClosed() { return server_peer_->quic_transport()->IsClosed(); }
 
-  // Tests that the callbacks were appropriately called after the client
-  // stops the connection. Only the server should receive the OnRemoteStopped()
-  // callback.
-  void ExpectClientStopped() {
-    ExpectTransportsClosed();
-    EXPECT_EQ(0, client_peer_->quic_transport_delegate()->stopped_count());
-    EXPECT_EQ(
-        0, client_peer_->quic_transport_delegate()->connection_failed_count());
-    EXPECT_EQ(1, server_peer_->quic_transport_delegate()->stopped_count());
-    EXPECT_EQ(
-        0, server_peer_->quic_transport_delegate()->connection_failed_count());
-  }
-
-  // Tests that the callbacks were appropriately called after the server
-  // stops the connection. Only the client should receive the OnRemoteStopped()
-  // callback.
-  void ExpectServerStopped() {
-    ExpectTransportsClosed();
-    EXPECT_EQ(1, client_peer_->quic_transport_delegate()->stopped_count());
-    EXPECT_EQ(
-        0, client_peer_->quic_transport_delegate()->connection_failed_count());
-    EXPECT_EQ(0, server_peer_->quic_transport_delegate()->stopped_count());
-    EXPECT_EQ(
-        0, server_peer_->quic_transport_delegate()->connection_failed_count());
-  }
-
   QuicPeerForTest* client_peer() { return client_peer_.get(); }
 
   quic::QuicConnection* client_connection() {
@@ -638,21 +579,7 @@
     return server_peer_->quic_transport()->connection();
   }
 
-  P2PQuicStreamImpl* server_stream() { return server_stream_; }
-
-  P2PQuicStreamImpl* client_stream() { return client_stream_; }
-
-  quic::QuicStreamId server_stream_id() { return server_stream_id_; }
-
-  quic::QuicStreamId client_stream_id() { return client_stream_id_; }
-
-  QuicStreamDelegateForTesting* server_stream_delegate() {
-    return server_stream_delegate_.get();
-  }
-
-  QuicStreamDelegateForTesting* client_stream_delegate() {
-    return client_stream_delegate_.get();
-  }
+  scoped_refptr<net::test::TestTaskRunner> runner() { return runner_; }
 
  private:
   quic::MockClock clock_;
@@ -664,17 +591,6 @@
   std::unique_ptr<P2PQuicTransportFactoryImpl> quic_transport_factory_;
   std::unique_ptr<QuicPeerForTest> client_peer_;
   std::unique_ptr<QuicPeerForTest> server_peer_;
-
-  // Stream objects, which are created with SetupConnectedStream().
-  bool streams_setup_ = false;
-  std::unique_ptr<QuicStreamDelegateForTesting> client_stream_delegate_;
-  std::unique_ptr<QuicStreamDelegateForTesting> server_stream_delegate_;
-  // The P2PQuicStreamImpls are owned by the P2PQuicTransport.
-  P2PQuicStreamImpl* client_stream_ = nullptr;
-  P2PQuicStreamImpl* server_stream_ = nullptr;
-  // We cache the values before the streams are potentially closed and deleted.
-  quic::QuicStreamId server_stream_id_;
-  quic::QuicStreamId client_stream_id_;
 };
 
 // Tests that we can connect two quic transports.
@@ -682,31 +598,42 @@
   Initialize();
   Connect();
 
-  ExpectSuccessfulHandshake();
+  EXPECT_TRUE(client_peer()->quic_transport()->IsEncryptionEstablished());
+  EXPECT_TRUE(client_peer()->quic_transport()->IsCryptoHandshakeConfirmed());
+  EXPECT_TRUE(server_peer()->quic_transport()->IsCryptoHandshakeConfirmed());
+  EXPECT_TRUE(server_peer()->quic_transport()->IsEncryptionEstablished());
 }
 
 // Tests the standard case for the server side closing the connection.
 TEST_F(P2PQuicTransportTest, ServerStops) {
   Initialize();
   Connect();
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnRemoteStopped);
-  server_peer()->quic_transport()->Stop();
-  RunUntilCallbacksFired();
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .Times(0);
 
-  ExpectServerStopped();
+  server_peer()->quic_transport()->Stop();
+  run_loop.RunUntilCallbacksFired();
+
+  ExpectTransportsClosed();
 }
 
 // Tests the standard case for the client side closing the connection.
 TEST_F(P2PQuicTransportTest, ClientStops) {
   Initialize();
   Connect();
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnRemoteStopped);
-  client_peer()->quic_transport()->Stop();
-  RunUntilCallbacksFired();
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .Times(0);
 
-  ExpectClientStopped();
+  client_peer()->quic_transport()->Stop();
+  run_loop.RunUntilCallbacksFired();
+
+  ExpectTransportsClosed();
 }
 
 // Tests that if either side tries to close the connection a second time, it
@@ -714,29 +641,37 @@
 TEST_F(P2PQuicTransportTest, StopAfterStopped) {
   Initialize();
   Connect();
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnRemoteStopped);
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
   client_peer()->quic_transport()->Stop();
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
+
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .Times(0);
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .Times(0);
+
   client_peer()->quic_transport()->Stop();
   server_peer()->quic_transport()->Stop();
   RunCurrentTasks();
 
-  ExpectClientStopped();
+  ExpectTransportsClosed();
 }
 
 // Tests that when the client closes the connection the subsequent call to
-// Start() will be ignored.
+// StartHandshake() will be ignored.
 TEST_F(P2PQuicTransportTest, ClientStopsBeforeClientStarts) {
   Initialize();
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnRemoteStopped);
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
   client_peer()->quic_transport()->Stop();
   StartHandshake();
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectConnectionNotEstablished();
-  ExpectClientStopped();
+  ExpectTransportsClosed();
 }
 
 // Tests that if the server closes the connection before the client starts the
@@ -744,29 +679,37 @@
 // ignored.
 TEST_F(P2PQuicTransportTest, ServerStopsBeforeClientStarts) {
   Initialize();
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnRemoteStopped);
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnRemoteStopped())
+      .Times(0);
+
   server_peer()->quic_transport()->Stop();
   StartHandshake();
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectConnectionNotEstablished();
-  ExpectServerStopped();
+  ExpectTransportsClosed();
 }
 
 // Tests that when the server's connection fails and then a handshake is
 // attempted the transports will not become connected.
 TEST_F(P2PQuicTransportTest, ClientConnectionClosesBeforeHandshake) {
   Initialize();
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
   client_connection()->CloseConnection(
       quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
       quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
   StartHandshake();
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectConnectionNotEstablished();
 }
@@ -775,15 +718,19 @@
 // attempted the transports will not become connected.
 TEST_F(P2PQuicTransportTest, ServerConnectionClosesBeforeHandshake) {
   Initialize();
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
   server_connection()->CloseConnection(
       quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
       quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
   StartHandshake();
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectConnectionNotEstablished();
 }
@@ -791,17 +738,17 @@
 // Tests that the appropriate callbacks are fired when the handshake fails.
 TEST_F(P2PQuicTransportTest, HandshakeFailure) {
   InitializeWithFailingProofVerification();
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
-  StartHandshake();
-  RunUntilCallbacksFired();
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
 
-  EXPECT_EQ(
-      1, client_peer()->quic_transport_delegate()->connection_failed_count());
-  EXPECT_EQ(
-      1, server_peer()->quic_transport_delegate()->connection_failed_count());
+  StartHandshake();
+  run_loop.RunUntilCallbacksFired();
+
   ExpectConnectionNotEstablished();
   ExpectTransportsClosed();
 }
@@ -811,21 +758,21 @@
 TEST_F(P2PQuicTransportTest, ClientConnectionFailureAfterConnected) {
   Initialize();
   Connect();
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, /*from_remote=*/false))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, /*from_remote=*/true))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
   // Close the connection with an internal QUIC error.
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
   client_connection()->CloseConnection(
       quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
       quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectTransportsClosed();
-  EXPECT_EQ(
-      1, client_peer()->quic_transport_delegate()->connection_failed_count());
-  EXPECT_EQ(
-      1, server_peer()->quic_transport_delegate()->connection_failed_count());
 }
 
 // Tests that the appropriate callbacks are fired when the server's connection
@@ -833,21 +780,20 @@
 TEST_F(P2PQuicTransportTest, ServerConnectionFailureAfterConnected) {
   Initialize();
   Connect();
-  // Close the connection with an internal QUIC error.
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, /*from_remote=*/true))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, /*from_remote=*/false))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
   server_connection()->CloseConnection(
       quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
       quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectTransportsClosed();
-  EXPECT_EQ(
-      1, client_peer()->quic_transport_delegate()->connection_failed_count());
-  EXPECT_EQ(
-      1, server_peer()->quic_transport_delegate()->connection_failed_count());
 }
 
 // Tests that closing the connection with no ACK frame does not make any
@@ -855,39 +801,41 @@
 TEST_F(P2PQuicTransportTest, ConnectionFailureNoAckFrame) {
   Initialize();
   Connect();
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
   client_connection()->CloseConnection(
       quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
       quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK);
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectTransportsClosed();
-  EXPECT_EQ(
-      1, client_peer()->quic_transport_delegate()->connection_failed_count());
-  EXPECT_EQ(
-      1, server_peer()->quic_transport_delegate()->connection_failed_count());
 }
 
 // Tests that a silent failure will only close on one side.
 TEST_F(P2PQuicTransportTest, ConnectionSilentFailure) {
   Initialize();
   Connect();
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnConnectionFailed);
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .Times(0);
+
   client_connection()->CloseConnection(
       quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
       quic::ConnectionCloseBehavior::SILENT_CLOSE);
-  RunUntilCallbacksFired();
+  run_loop.RunUntilCallbacksFired();
 
   EXPECT_TRUE(IsClientClosed());
-  EXPECT_EQ(
-      1, client_peer()->quic_transport_delegate()->connection_failed_count());
   EXPECT_FALSE(IsServerClosed());
-  EXPECT_EQ(
-      0, server_peer()->quic_transport_delegate()->connection_failed_count());
 }
 
 // Tests that the client transport can create a stream and an incoming stream
@@ -895,23 +843,35 @@
 TEST_F(P2PQuicTransportTest, ClientCreatesStream) {
   Initialize();
   Connect();
-  P2PQuicStreamImpl* client_stream =
-      client_peer()->quic_transport()->CreateStream();
+  CallbackRunLoop run_loop(runner());
+  client_peer()->CreateStreamWithDelegate();
+  ASSERT_TRUE(client_peer()->stream());
+
   RunCurrentTasks();
 
-  ASSERT_TRUE(client_stream);
   EXPECT_TRUE(client_peer()->quic_transport()->HasOpenDynamicStreams());
-  EXPECT_EQ(0, server_peer()->quic_transport_delegate()->on_stream_count());
   EXPECT_FALSE(server_peer()->quic_transport()->HasOpenDynamicStreams());
 
   // After sending data across it will trigger a stream to be created on the
   // server side.
-  server_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnStream);
-  client_stream->WriteOrBufferData("hello", false, nullptr);
-  RunUntilCallbacksFired();
+  MockP2PQuicStreamDelegate server_stream_delegate;
+  base::RepeatingCallback<void()> callback = run_loop.CreateCallback();
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnStream(_))
+      .WillOnce(
+          Invoke([&callback, &server_stream_delegate](P2PQuicStream* stream) {
+            ASSERT_TRUE(stream);
+            // The Delegate must get immediately set to a new incoming stream.
+            stream->SetDelegate(&server_stream_delegate);
+            // Allows the run loop to run until this is fired.
+            callback.Run();
+          }));
 
-  EXPECT_EQ(1, server_peer()->quic_transport_delegate()->on_stream_count());
+  client_peer()->stream()->WriteData(
+      std::vector<uint8_t>(kTriggerRemoteStreamPhrase.begin(),
+                           kTriggerRemoteStreamPhrase.end()),
+      /*fin=*/false);
+  run_loop.RunUntilCallbacksFired();
+
   EXPECT_TRUE(server_peer()->quic_transport()->HasOpenDynamicStreams());
 }
 
@@ -920,23 +880,35 @@
 TEST_F(P2PQuicTransportTest, ServerCreatesStream) {
   Initialize();
   Connect();
-  P2PQuicStreamImpl* server_stream =
-      server_peer()->quic_transport()->CreateStream();
+  CallbackRunLoop run_loop(runner());
+  server_peer()->CreateStreamWithDelegate();
+  ASSERT_TRUE(server_peer()->stream());
+
   RunCurrentTasks();
 
-  ASSERT_TRUE(server_stream);
   EXPECT_TRUE(server_peer()->quic_transport()->HasOpenDynamicStreams());
-  EXPECT_EQ(0, client_peer()->quic_transport_delegate()->on_stream_count());
   EXPECT_FALSE(client_peer()->quic_transport()->HasOpenDynamicStreams());
 
   // After sending data across it will trigger a stream to be created on the
-  // client side.
-  client_peer()->quic_transport_delegate()->ExpectCallback(
-      TransportCallbackType::kOnStream);
-  server_stream->WriteOrBufferData("hello", false, nullptr);
-  RunUntilCallbacksFired();
+  // server side.
+  MockP2PQuicStreamDelegate client_stream_delegate;
+  base::RepeatingCallback<void()> callback = run_loop.CreateCallback();
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(), OnStream(_))
+      .WillOnce(
+          Invoke([&callback, &client_stream_delegate](P2PQuicStream* stream) {
+            ASSERT_TRUE(stream);
+            // The Delegate must get immediately set to a new incoming stream.
+            stream->SetDelegate(&client_stream_delegate);
+            // Allows the run loop to run until this is fired.
+            callback.Run();
+          }));
 
-  EXPECT_EQ(1, client_peer()->quic_transport_delegate()->on_stream_count());
+  server_peer()->stream()->WriteData(
+      std::vector<uint8_t>(kTriggerRemoteStreamPhrase.begin(),
+                           kTriggerRemoteStreamPhrase.end()),
+      /*fin=*/false);
+  run_loop.RunUntilCallbacksFired();
+
   EXPECT_TRUE(client_peer()->quic_transport()->HasOpenDynamicStreams());
 }
 
@@ -976,11 +948,13 @@
   Initialize();
   Connect();
   SetupConnectedStreams();
+  CallbackRunLoop run_loop(runner());
 
-  server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteReset);
-  client_stream()->Reset();
-  RunUntilCallbacksFired();
+  EXPECT_CALL(*server_peer()->stream_delegate(), OnRemoteReset())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
 
+  client_peer()->stream()->Reset();
+  run_loop.RunUntilCallbacksFired();
   ExpectStreamsClosed();
 }
 
@@ -990,188 +964,171 @@
   Initialize();
   Connect();
   SetupConnectedStreams();
+  CallbackRunLoop run_loop(runner());
 
-  client_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteReset);
-  server_stream()->Reset();
-  RunUntilCallbacksFired();
+  EXPECT_CALL(*client_peer()->stream_delegate(), OnRemoteReset())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
+  server_peer()->stream()->Reset();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectStreamsClosed();
 }
 
-// Tests the basic case for calling Finish() on both sides.
-TEST_F(P2PQuicTransportTest, StreamFinishHandshake) {
+// Tests the basic case for sending a FIN bit on both sides.
+TEST_F(P2PQuicTransportTest, StreamClosedAfterSendingAndReceivingFin) {
   Initialize();
   Connect();
   SetupConnectedStreams();
+  CallbackRunLoop run_loop(runner());
 
-  server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteFinish);
-  client_stream()->Finish();
-  RunUntilCallbacksFired();
+  EXPECT_CALL(*server_peer()->stream_delegate(),
+              OnDataReceived(_, /*fin=*/true))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
+  client_peer()->stream()->WriteData({}, /*fin=*/true);
+  run_loop.RunUntilCallbacksFired();
 
   ASSERT_EQ(1u, server_peer()->quic_transport()->GetNumActiveStreams());
   ASSERT_EQ(1u, client_peer()->quic_transport()->GetNumActiveStreams());
-  EXPECT_EQ(0, client_stream_delegate()->remote_finish_count());
-  EXPECT_TRUE(client_stream()->write_side_closed());
-  EXPECT_FALSE(client_stream()->reading_stopped());
-  EXPECT_FALSE(server_stream()->write_side_closed());
-  EXPECT_TRUE(server_stream()->reading_stopped());
-  EXPECT_FALSE(
-      server_peer()->quic_transport()->IsClosedStream(server_stream_id()));
-  EXPECT_FALSE(
-      client_peer()->quic_transport()->IsClosedStream(client_stream_id()));
+  EXPECT_TRUE(client_peer()->stream()->write_side_closed());
+  EXPECT_FALSE(client_peer()->stream()->reading_stopped());
+  EXPECT_FALSE(server_peer()->stream()->write_side_closed());
+  EXPECT_TRUE(server_peer()->stream()->reading_stopped());
+  EXPECT_FALSE(server_peer()->quic_transport()->IsClosedStream(
+      server_peer()->stream_id()));
+  EXPECT_FALSE(client_peer()->quic_transport()->IsClosedStream(
+      client_peer()->stream_id()));
 
-  client_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteFinish);
-  server_stream()->Finish();
-  RunUntilCallbacksFired();
+  EXPECT_CALL(*client_peer()->stream_delegate(),
+              OnDataReceived(_, /*fin=*/true))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
+  server_peer()->stream()->WriteData({}, /*fin=*/true);
+  run_loop.RunUntilCallbacksFired();
+
   // This is required so that the client acks the FIN back to the server side
   // and the server side removes its zombie streams.
   RunCurrentTasks();
 
   ASSERT_EQ(0u, server_peer()->quic_transport()->GetNumActiveStreams());
   ASSERT_EQ(0u, client_peer()->quic_transport()->GetNumActiveStreams());
-  EXPECT_EQ(1, server_stream_delegate()->remote_finish_count());
-  EXPECT_EQ(1, client_stream_delegate()->remote_finish_count());
-  EXPECT_TRUE(
-      server_peer()->quic_transport()->IsClosedStream(server_stream_id()));
-  EXPECT_TRUE(
-      client_peer()->quic_transport()->IsClosedStream(client_stream_id()));
+  EXPECT_TRUE(server_peer()->quic_transport()->IsClosedStream(
+      server_peer()->stream_id()));
+  EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(
+      client_peer()->stream_id()));
 }
 
-// Tests that if a Reset() is called after Finish(), both sides close down
-// properly.
-TEST_F(P2PQuicTransportTest, StreamResetAfterFinish) {
+// Tests that if a Reset() is called after sending a FIN bit, both sides close
+// down properly.
+TEST_F(P2PQuicTransportTest, StreamResetAfterSendingFin) {
   Initialize();
   Connect();
   SetupConnectedStreams();
+  CallbackRunLoop run_loop(runner());
 
-  server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteFinish);
-  client_stream()->Finish();
-  RunUntilCallbacksFired();
+  EXPECT_CALL(*server_peer()->stream_delegate(),
+              OnDataReceived(_, /*fin=*/true))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
 
-  server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteReset);
-  client_stream()->Reset();
-  RunUntilCallbacksFired();
+  client_peer()->stream()->WriteData({}, /*fin=*/true);
+  run_loop.RunUntilCallbacksFired();
+
+  EXPECT_CALL(*server_peer()->stream_delegate(), OnRemoteReset())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*client_peer()->stream_delegate(), OnRemoteReset()).Times(0);
+
+  client_peer()->stream()->Reset();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectStreamsClosed();
-  EXPECT_EQ(0, client_stream_delegate()->remote_reset_count());
 }
 
 // Tests that if a Reset() is called after receiving a stream frame with the FIN
 // bit set from the remote side, both sides close down properly.
-TEST_F(P2PQuicTransportTest, StreamResetAfterRemoteFinish) {
+TEST_F(P2PQuicTransportTest, StreamResetAfterReceivingFin) {
   Initialize();
   Connect();
   SetupConnectedStreams();
+  CallbackRunLoop run_loop(runner());
 
-  server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteFinish);
-  client_stream()->Finish();
-  RunUntilCallbacksFired();
+  EXPECT_CALL(*server_peer()->stream_delegate(),
+              OnDataReceived(_, /*fin=*/true))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
 
-  client_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteReset);
+  client_peer()->stream()->WriteData({}, /*fin=*/true);
+  run_loop.RunUntilCallbacksFired();
+
+  EXPECT_CALL(*client_peer()->stream_delegate(), OnRemoteReset())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->stream_delegate(), OnRemoteReset()).Times(0);
+
   // The server stream has received its FIN bit from the remote side, and
   // responds with a Reset() to close everything down.
-  server_stream()->Reset();
-  RunUntilCallbacksFired();
+  server_peer()->stream()->Reset();
+  run_loop.RunUntilCallbacksFired();
 
   ExpectStreamsClosed();
-  EXPECT_EQ(0, server_stream_delegate()->remote_reset_count());
 }
 
-// The following unit tests are more isolated to the P2PQuicStreamImpl
-// implementation. They only test a stream's behavior on one side (not any
-// interactions between two connected streams).
-
-TEST_F(P2PQuicTransportTest, StreamFinishSendsFinAndCanNoLongerWrite) {
+// Tests that when data is sent on a stream it is received on the other end.
+TEST_F(P2PQuicTransportTest, StreamDataSentThenReceivedOnRemoteSide) {
   Initialize();
   Connect();
-  P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
+  SetupConnectedStreams();
+  CallbackRunLoop run_loop(runner());
+  std::string message = "howdy partner";
 
-  stream->Finish();
-  EXPECT_TRUE(stream->fin_sent());
-  EXPECT_TRUE(stream->write_side_closed());
-  EXPECT_FALSE(stream->reading_stopped());
+  EXPECT_CALL(*server_peer()->stream_delegate(),
+              OnDataReceived(
+                  std::vector<uint8_t>(message.begin(), message.end()), false))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*client_peer()->stream_delegate(),
+              OnWriteDataConsumed(message.size()))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
+  client_peer()->stream()->WriteData(
+      std::vector<uint8_t>(message.begin(), message.end()), /* fin= */ false);
+  run_loop.RunUntilCallbacksFired();
 }
 
-TEST_F(P2PQuicTransportTest, StreamResetSendsRstAndBecomesClosed) {
+// Tests that if both sides have a stream that sends data and FIN bit
+// they both close down for reading and writing properly.
+TEST_F(P2PQuicTransportTest, StreamDataSentWithFinClosesStreams) {
   Initialize();
   Connect();
+  SetupConnectedStreams();
+  CallbackRunLoop run_loop(runner());
+  std::string server_message = "some server data";
+  std::string client_message = "client data";
 
-  P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
-  quic::QuicStreamId stream_id = stream->id();
+  EXPECT_CALL(*server_peer()->stream_delegate(),
+              OnDataReceived(std::vector<uint8_t>(client_message.begin(),
+                                                  client_message.end()),
+                             true))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->stream_delegate(),
+              OnWriteDataConsumed(server_message.size()))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
 
-  stream->Reset();
+  EXPECT_CALL(*client_peer()->stream_delegate(),
+              OnDataReceived(std::vector<uint8_t>(server_message.begin(),
+                                                  server_message.end()),
+                             true))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*client_peer()->stream_delegate(),
+              OnWriteDataConsumed(client_message.size()))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
 
-  EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(stream_id));
-}
+  client_peer()->stream()->WriteData(
+      std::vector<uint8_t>(client_message.begin(), client_message.end()),
+      /*fin=*/true);
+  server_peer()->stream()->WriteData(
+      std::vector<uint8_t>(server_message.begin(), server_message.end()),
+      /*fin=*/true);
+  run_loop.RunUntilCallbacksFired();
 
-// Tests that when a stream receives a stream frame with the FIN bit set it
-// will fire the appropriate callback and close the stream for reading.
-TEST_F(P2PQuicTransportTest, StreamOnStreamFrameWithFin) {
-  Initialize();
-  Connect();
-  P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
-  QuicStreamDelegateForTesting stream_delegate;
-  stream->SetDelegate(&stream_delegate);
-
-  quic::QuicStreamFrame fin_frame(stream->id(), /*fin=*/true, 0, 0);
-  stream->OnStreamFrame(fin_frame);
-  EXPECT_EQ(1, stream_delegate.remote_finish_count());
-  EXPECT_TRUE(stream->reading_stopped());
-  EXPECT_FALSE(stream->write_side_closed());
-}
-
-// Tests that when a stream receives a stream frame with the FIN bit set after
-// it has called Finish(), then the stream will close.
-TEST_F(P2PQuicTransportTest, StreamClosedAfterReceivesFin) {
-  Initialize();
-  Connect();
-  P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
-  quic::QuicStreamId stream_id = stream->id();
-  QuicStreamDelegateForTesting stream_delegate;
-  stream->SetDelegate(&stream_delegate);
-
-  stream->Finish();
-  EXPECT_FALSE(client_peer()->quic_transport()->IsClosedStream(stream_id));
-  quic::QuicStreamFrame fin_frame(stream->id(), /*fin=*/true, 0, 0);
-  stream->OnStreamFrame(fin_frame);
-
-  EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(stream_id));
-}
-
-// Tests that when a stream calls Finish() after receiving a stream frame with
-// the FIN bit then the stream will close.
-TEST_F(P2PQuicTransportTest, StreamClosedAfterFinish) {
-  Initialize();
-  Connect();
-  P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
-  quic::QuicStreamId stream_id = stream->id();
-  QuicStreamDelegateForTesting stream_delegate;
-  stream->SetDelegate(&stream_delegate);
-
-  quic::QuicStreamFrame fin_frame(stream->id(), /*fin=*/true, 0, 0);
-  stream->OnStreamFrame(fin_frame);
-  EXPECT_FALSE(client_peer()->quic_transport()->IsClosedStream(stream_id));
-  stream->Finish();
-
-  EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(stream_id));
-}
-
-// Tests that when a stream receives a RST_STREAM frame it will fire the
-// appropriate callback and the stream will become closed.
-TEST_F(P2PQuicTransportTest, StreamClosedAfterReceivingReset) {
-  Initialize();
-  Connect();
-  P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
-  quic::QuicStreamId stream_id = stream->id();
-  QuicStreamDelegateForTesting stream_delegate;
-  stream->SetDelegate(&stream_delegate);
-
-  quic::QuicRstStreamFrame rst_frame(quic::kInvalidControlFrameId, stream_id,
-                                     quic::QUIC_STREAM_CANCELLED, 0);
-  stream->OnStreamReset(rst_frame);
-
-  EXPECT_EQ(1, stream_delegate.remote_reset_count());
-  EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(stream_id));
+  ExpectStreamsClosed();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc
index 71545c64..25f2393 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc
@@ -49,10 +49,13 @@
   Delete();
 }
 
+// TODO(https://crbug.com/874296): When the blink binding (RTCQuicStream) is
+// updated to support reading/writing, remove this function.
 void QuicStreamHost::Finish() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(p2p_stream_);
-  p2p_stream_->Finish();
+  std::vector<uint8_t> data;
+  p2p_stream_->WriteData(data, true);
   writeable_ = false;
   if (!readable_ && !writeable_) {
     Delete();
@@ -67,8 +70,14 @@
   Delete();
 }
 
-void QuicStreamHost::OnRemoteFinish() {
+// TODO(https://crbug.com/874296): When the blink binding (RTCQuicStream) is
+// updated to support reading/writing, update this function to do more than just
+// call OnRemoteFinish.
+void QuicStreamHost::OnDataReceived(std::vector<uint8_t> data, bool fin) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (!fin) {
+    return;
+  }
   PostCrossThreadTask(
       *proxy_thread(), FROM_HERE,
       CrossThreadBind(&QuicStreamProxy::OnRemoteFinish, stream_proxy_));
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h
index baab847..6733e2f 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h
@@ -64,7 +64,7 @@
 
   // P2PQuicStream::Delegate overrides.
   void OnRemoteReset() override;
-  void OnRemoteFinish() override;
+  void OnDataReceived(std::vector<uint8_t> data, bool fin) override;
 
   // Up reference. Owned by QuicTransportProxy.
   QuicTransportHost* transport_host_ = nullptr;
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
index cc99a06..caef635 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
@@ -44,9 +44,14 @@
   DCHECK(ice_transport_host);
   DCHECK(!ice_transport_host_);
   ice_transport_host_ = ice_transport_host;
+  // TODO(https://crbug.com/874296): Pass through values for read and write
+  // stream buffer sizes in the P2PQuicTransportConfig. Currently this is just
+  // set to the same size as the QUIC receive window size (24 MB).
+  uint32_t stream_buffer_size = 24 * 1024 * 1024;
   P2PQuicTransportConfig config(
       this, ice_transport_host->ConnectConsumer(this)->packet_transport(),
-      certificates);
+      certificates, /*stream_delegate_read_buffer_size_in=*/stream_buffer_size,
+      /*stream_write_buffer_size_in=*/stream_buffer_size);
   config.is_server = (perspective == quic::Perspective::IS_SERVER);
   quic_transport_ =
       quic_transport_factory_->CreateQuicTransport(std::move(config));
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream.h b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream.h
index 3329963d..9cfc631 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream.h
@@ -14,8 +14,9 @@
  public:
   // P2PQuicStream overrides.
   MOCK_METHOD0(Reset, void());
-  MOCK_METHOD0(Finish, void());
   MOCK_METHOD1(SetDelegate, void(Delegate*));
+  MOCK_METHOD2(WriteData, void(std::vector<uint8_t>, bool));
+  MOCK_METHOD1(MarkReceivedDataConsumed, void(uint32_t));
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream_delegate.h b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream_delegate.h
new file mode 100644
index 0000000..840ecfa
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream_delegate.h
@@ -0,0 +1,24 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_TEST_MOCK_P2P_QUIC_STREAM_DELEGATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_TEST_MOCK_P2P_QUIC_STREAM_DELEGATE_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h"
+
+namespace blink {
+
+class MockP2PQuicStreamDelegate
+    : public testing::NiceMock<P2PQuicStream::Delegate> {
+ public:
+  // P2PQuicStream::Delegate overrides.
+  MOCK_METHOD1(OnWriteDataConsumed, void(uint32_t));
+  MOCK_METHOD0(OnRemoteReset, void());
+  MOCK_METHOD2(OnDataReceived, void(std::vector<uint8_t>, bool));
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_TEST_MOCK_P2P_QUIC_STREAM_DELEGATE_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport_delegate.h b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport_delegate.h
new file mode 100644
index 0000000..9c0efc5
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport_delegate.h
@@ -0,0 +1,25 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_TEST_MOCK_P2P_QUIC_TRANSPORT_DELEGATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_TEST_MOCK_P2P_QUIC_TRANSPORT_DELEGATE_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h"
+
+namespace blink {
+
+class MockP2PQuicTransportDelegate
+    : public testing::NiceMock<P2PQuicTransport::Delegate> {
+ public:
+  // P2PQuicTransport::Delegate overrides.
+  MOCK_METHOD0(OnRemoteStopped, void());
+  MOCK_METHOD2(OnConnectionFailed, void(const std::string&, bool));
+  MOCK_METHOD0(OnConnected, void());
+  MOCK_METHOD1(OnStream, void(P2PQuicStream*));
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_TEST_MOCK_P2P_QUIC_TRANSPORT_DELEGATE_H_
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.cc b/third_party/blink/renderer/modules/webaudio/audio_node.cc
index cb5f48b3b..9fa74340 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_node.cc
@@ -76,8 +76,6 @@
 
 AudioHandler::~AudioHandler() {
   DCHECK(IsMainThread());
-  // dispose() should be called.
-  DCHECK(!GetNode());
   InstanceCounters::DecrementCounter(InstanceCounters::kAudioHandlerCounter);
 #if DEBUG_AUDIONODE_REFERENCES
   --node_count_[GetNodeType()];
@@ -111,7 +109,6 @@
   Context()->GetDeferredTaskHandler().RemoveAutomaticPullNode(this);
   for (auto& output : outputs_)
     output->Dispose();
-  node_ = nullptr;
 }
 
 AudioNode* AudioHandler::GetNode() const {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.h b/third_party/blink/renderer/modules/webaudio/audio_node.h
index de1563cf..763d7ed 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_node.h
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
 #include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -108,10 +109,11 @@
   // Do not release resources used by an audio rendering thread in dispose().
   virtual void Dispose();
 
-  // GetNode() returns a valid object until dispose() is called.  This returns
-  // nullptr after dispose().  We must not call GetNode() in an audio rendering
-  // thread.
+  // GetNode() returns a valid object until the AudioNode is collected on the
+  // main thread, and nullptr thereafter. We must not call GetNode() in an audio
+  // rendering thread.
   AudioNode* GetNode() const;
+
   // context() returns a valid object until the BaseAudioContext dies, and
   // returns nullptr otherwise.  This always returns a valid object in an audio
   // rendering thread, and inside dispose().  We must not call context() in the
@@ -264,11 +266,8 @@
   volatile bool is_initialized_;
   NodeType node_type_;
 
-  // The owner AudioNode.  This untraced member is safe because dispose() is
-  // called before the AudioNode death, and it clears |node_|.  Do not access
-  // |node_| directly, use GetNode() instead.
-  // See http://crbug.com/404527 for the detail.
-  UntracedMember<AudioNode> node_;
+  // The owner AudioNode. Accessed only on the main thread.
+  const WeakPersistent<AudioNode> node_;
 
   // This untraced member is safe because this is cleared for all of live
   // AudioHandlers when the BaseAudioContext dies.  Do not access m_context
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 7897a73..95ddf43d 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -754,7 +754,6 @@
   canvas_image_provider_.reset();
   xform_canvas_ = nullptr;
   surface_ = nullptr;
-  single_buffer_ = nullptr;
 }
 
 uint32_t CanvasResourceProvider::ContentUniqueID() const {
@@ -790,7 +789,7 @@
   // Need to check HasOneRef() because if there are outstanding references to
   // the resource, it cannot be safely recycled.
   if (resource->HasOneRef() && resource_recycling_enabled_)
-    recycled_resources_.push_back(std::move(resource));
+    canvas_resources_.push_back(std::move(resource));
 }
 
 void CanvasResourceProvider::SetResourceRecyclingEnabled(bool value) {
@@ -800,29 +799,27 @@
 }
 
 void CanvasResourceProvider::ClearRecycledResources() {
-  recycled_resources_.clear();
+  canvas_resources_.clear();
 }
 
 scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() {
+  if (canvas_resources_.IsEmpty())
+    canvas_resources_.push_back(CreateResource());
+
   if (IsSingleBuffered()) {
-    if (!single_buffer_)
-      single_buffer_ = CreateResource();
-    return single_buffer_;
+    DCHECK_EQ(canvas_resources_.size(), 1u);
+    return canvas_resources_.back();
   }
-  if (recycled_resources_.size()) {
-    scoped_refptr<CanvasResource> resource =
-        std::move(recycled_resources_.back());
-    recycled_resources_.pop_back();
-    return resource;
-  }
-  return CreateResource();
+
+  scoped_refptr<CanvasResource> resource = std::move(canvas_resources_.back());
+  canvas_resources_.pop_back();
+  return resource;
 }
 
 void CanvasResourceProvider::TryEnableSingleBuffering() {
   if (IsSingleBuffered() || !SupportsSingleBuffering())
     return;
   SetResourceRecyclingEnabled(false);
-  is_single_buffered_ = true;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
index f6e1c72..9266694 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -92,9 +92,9 @@
       base::WeakPtr<CanvasResourceDispatcher>,
       bool is_origin_top_left = true);
 
-  // Use this method for capturing a frame that is intended to be displayed via
-  // the compositor. Cases that need to acquire a snaptshot that is not destined
-  // to be transfered via TransferableResource should call Snapshot() instead.
+  // Use Snapshot() for capturing a frame that is intended to be displayed via
+  // the compositor. Cases that are destined to be transferred via a
+  // TransferableResource should call ProduceFrame() instead.
   virtual scoped_refptr<CanvasResource> ProduceFrame() = 0;
   scoped_refptr<StaticBitmapImage> Snapshot();
 
@@ -119,11 +119,10 @@
   // Indicates that the compositing path is single buffered, meaning that
   // ProduceFrame() return a reference to the same resource each time, which
   // implies that Producing an animation frame may overwrite the resource used
-  // by the previous frame. This results in graphics updates skipping the
-  // queue, thus reducing latency, but with the possible side effects of
-  // tearring (in cases where the resource is scanned out directly) and
-  // irregular frame rate.
-  bool IsSingleBuffered() { return is_single_buffered_; }
+  // by the previous frame. This results in graphics updates skipping the queue,
+  // thus reducing latency, but with the possible side effects of tearing (in
+  // cases where the resource is scanned out directly) and irregular frame rate.
+  bool IsSingleBuffered() { return !resource_recycling_enabled_; }
 
   // Attempt to enable single buffering mode on this resource provider.  May
   // fail if the CanvasResourcePRovider subclass does not support this mode of
@@ -226,12 +225,11 @@
       cc::PaintImage::kInvalidContentId;
   uint32_t snapshot_sk_image_id_ = 0u;
 
-  WTF::Vector<scoped_refptr<CanvasResource>> recycled_resources_;
+  // When and if |resource_recycling_enabled_| is false, |canvas_resources_|
+  // will only hold one CanvasResource at most.
+  WTF::Vector<scoped_refptr<CanvasResource>> canvas_resources_;
   bool resource_recycling_enabled_ = true;
 
-  bool is_single_buffered_ = false;
-  scoped_refptr<CanvasResource> single_buffer_;
-
   base::WeakPtrFactory<CanvasResourceProvider> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CanvasResourceProvider);
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index afbf3b6c..18adfcb9 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -466,9 +466,15 @@
         color_buffer_for_mailbox->produce_sync_token, gfx::Size(size_),
         is_overlay_candidate);
     out_resource->color_space = sampler_color_space_;
-    out_resource->format = viz::RGBA_8888;
-    if (use_half_float_storage_)
-      out_resource->format = viz::RGBA_F16;
+    if (allocate_alpha_channel_) {
+      if (use_half_float_storage_)
+        out_resource->format = viz::RGBA_F16;
+      else
+        out_resource->format = viz::RGBA_8888;
+    } else {
+      DCHECK(!use_half_float_storage_);
+      out_resource->format = viz::RGBX_8888;
+    }
 
     // This holds a ref on the DrawingBuffer that will keep it alive until the
     // mailbox is released (and while the release callback is running).
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index d441b762..192d1cd 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -272,7 +272,7 @@
 }
 
 void Resource::NotifyFinished() {
-  DCHECK(IsLoaded());
+  CHECK(IsFinishedInternal());
 
   ResourceClientWalker<ResourceClient> w(clients_);
   while (ResourceClient* c = w.Next()) {
@@ -299,6 +299,10 @@
       data_ = SharedBuffer::Create(data, length);
     SetEncodedSize(data_->size());
   }
+  NotifyDataReceived(data, length);
+}
+
+void Resource::NotifyDataReceived(const char* data, size_t length) {
   ResourceClientWalker<ResourceClient> w(Clients());
   while (ResourceClient* c = w.Next())
     c->DataReceived(this, data, length);
@@ -618,7 +622,7 @@
   }
   if (!HasClient(c))
     return;
-  if (IsLoaded()) {
+  if (IsFinishedInternal()) {
     c->NotifyFinished(this);
     if (clients_.Contains(c)) {
       finished_clients_.insert(c);
@@ -690,6 +694,12 @@
 
   WillAddClientOrObserver();
   finish_observers_.insert(client);
+  // Despite these being "Finish" observers, what they actually care about is
+  // whether the resource is "Loaded", not "Finished" (e.g. link onload). Hence
+  // we check IsLoaded directly here, rather than IsFinishedInternal.
+  //
+  // TODO(leszeks): Either rename FinishObservers to LoadedObservers, or the
+  // NotifyFinished method of ResourceClient to NotifyProcessed (or similar).
   if (IsLoaded())
     TriggerNotificationForFinishObservers(task_runner);
 }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index 9dd91a5..966e271 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -431,6 +431,19 @@
  protected:
   Resource(const ResourceRequest&, ResourceType, const ResourceLoaderOptions&);
 
+  // Returns true if the resource has finished any processing it wanted to do
+  // after loading. Should only be used to decide whether to call
+  // NotifyFinished.
+  //
+  // By default this is the same as being loaded (i.e. no processing), but it is
+  // used by ScriptResource to signal that streaming JavaScript compilation
+  // completed. Note that classes overloading this method should also overload
+  // NotifyFinished to not call Resource::NotifyFinished until this value
+  // becomes true.
+  // TODO(hiroshige): Remove this when ScriptResourceContent is introduced.
+  virtual bool IsFinishedInternal() const { return IsLoaded(); }
+
+  virtual void NotifyDataReceived(const char* data, size_t size);
   virtual void NotifyFinished();
 
   void MarkClientFinished(ResourceClient*);
diff --git a/third_party/blink/tools/blinkpy/common/net/luci_auth.py b/third_party/blink/tools/blinkpy/common/net/luci_auth.py
new file mode 100644
index 0000000..da6190f
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/common/net/luci_auth.py
@@ -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.
+
+"""An interface to luci-auth.
+
+The main usage is to get the OAuth access token for the service account on LUCI.
+"""
+
+
+class LuciAuth(object):
+
+    def __init__(self, host):
+        self._host = host
+
+    def get_access_token(self):
+        # ScriptError will be raised if luci-auth fails.
+        output = self._host.executive.run_command(['luci-auth', 'token'])
+        return output.strip()
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index a8b3b475..ad84c2b 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -86,6 +86,9 @@
             # //base/allocator/partition_allocator/oom_callback.h.
             'base::SetPartitionAllocOomCallback',
 
+            # //base/metrics/histogram_functions.h
+            'base::UmaHistogram.+',
+
             # //base/metrics/field_trial_params.h.
             'base::GetFieldTrialParamValueByFeature',
             'base::GetFieldTrialParamByFeatureAsBool',
diff --git a/third_party/blink/tools/blinkpy/w3c/import_notifier.py b/third_party/blink/tools/blinkpy/w3c/import_notifier.py
index 92f32ff3..2e7846b9 100644
--- a/third_party/blink/tools/blinkpy/w3c/import_notifier.py
+++ b/third_party/blink/tools/blinkpy/w3c/import_notifier.py
@@ -16,6 +16,7 @@
 import logging
 import re
 
+from blinkpy.common.net.luci_auth import LuciAuth
 from blinkpy.common.path_finder import PathFinder
 from blinkpy.w3c.common import WPT_GH_URL
 from blinkpy.w3c.directory_owners_extractor import DirectoryOwnersExtractor
@@ -35,6 +36,7 @@
         self.git = chromium_git
         self.local_wpt = local_wpt
 
+        self._monorail_api = MonorailAPI
         self.default_port = host.port_factory.get()
         self.finder = PathFinder(host.filesystem)
         self.owners_extractor = DirectoryOwnersExtractor(host.filesystem)
@@ -56,9 +58,8 @@
             patchset: The patchset number of the import CL (a string).
             dry_run: If True, no bugs will be actually filed to crbug.com.
             service_account_key_json: The path to a JSON private key of a
-                service account for accessing Monorail. If None, try to load
-                from the default location, i.e. the path stored in the
-                environment variable GOOGLE_APPLICATION_CREDENTIALS.
+                service account for accessing Monorail. If None, try to get an
+                access token from luci-auth.
 
         Note: "test names" are paths of the tests relative to LayoutTests.
         """
@@ -265,7 +266,10 @@
             _log.info('[%d] Filed bug: %s', index, MonorailIssue.crbug_link(response['id']))
 
     def _get_monorail_api(self, service_account_key_json):
-        return MonorailAPI(service_account_key_json=service_account_key_json)
+        if service_account_key_json:
+            return self._monorail_api(service_account_key_json=service_account_key_json)
+        token = LuciAuth(self.host).get_access_token()
+        return self._monorail_api(access_token=token)
 
 
 class TestFailure(object):
diff --git a/third_party/blink/tools/blinkpy/w3c/import_notifier_unittest.py b/third_party/blink/tools/blinkpy/w3c/import_notifier_unittest.py
index 99eb8a1c..d4a231b4 100644
--- a/third_party/blink/tools/blinkpy/w3c/import_notifier_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/import_notifier_unittest.py
@@ -199,6 +199,18 @@
         self.notifier._get_monorail_api = unreachable  # pylint: disable=protected-access
         self.notifier.file_bugs([], True)
 
+    def test_file_bugs_calls_luci_auth(self):
+        test = self
+
+        class FakeAPI(object):
+            def __init__(self, service_account_key_json=None, access_token=None):
+                test.assertIsNone(service_account_key_json)
+                test.assertEqual(access_token, 'MOCK output of child process')
+
+        self.notifier._monorail_api = FakeAPI  # pylint: disable=protected-access
+        self.notifier.file_bugs([], False)
+        self.assertEqual(self.host.executive.calls, [['luci-auth', 'token']])
+
 
 class TestFailureTest(unittest.TestCase):
 
diff --git a/third_party/blink/tools/blinkpy/w3c/monorail.py b/third_party/blink/tools/blinkpy/w3c/monorail.py
index e6536ee..1ffa923 100644
--- a/third_party/blink/tools/blinkpy/w3c/monorail.py
+++ b/third_party/blink/tools/blinkpy/w3c/monorail.py
@@ -98,14 +98,14 @@
         'https://monorail-prod.appspot.com/_ah/api/discovery/v1/apis/'
         '{api}/{apiVersion}/rest')
 
-    def __init__(self, service_account_key_json=None):
+    def __init__(self, service_account_key_json=None, access_token=None):
         """Initializes a MonorailAPI instance.
 
         Args:
             service_account_key_json: The path to a JSON private key of a
-                service account for accessing Monorail. If None, try to load
-                from the default location, i.e. the path stored in the
-                environment variable GOOGLE_APPLICATION_CREDENTIALS.
+                service account for accessing Monorail. If None, use access_token.
+            access_token: An OAuth access token. If None, fall back to Google
+                application default credentials.
         """
         # Make it easier to mock out the two libraries in the future.
         # Dependencies managed by wpt-import.vpython - pylint: disable=import-error,no-member
@@ -114,8 +114,13 @@
         import oauth2client.client
         self._oauth2_client = oauth2client.client
 
+        # TODO(robertma): Deprecate the JSON key support once BuildBot is gone.
         if service_account_key_json:
             credentials = self._oauth2_client.GoogleCredentials.from_stream(service_account_key_json)
+        elif access_token:
+            credentials = self._oauth2_client.AccessTokenCredentials(
+                access_token=access_token,
+                user_agent='blinkpy/1.0')
         else:
             credentials = self._oauth2_client.GoogleCredentials.get_application_default()
 
diff --git a/tools/android/roll/android_deps/build.gradle b/tools/android/roll/android_deps/build.gradle
index b5a6f30e..74bae36 100644
--- a/tools/android/roll/android_deps/build.gradle
+++ b/tools/android/roll/android_deps/build.gradle
@@ -39,7 +39,7 @@
     compile "com.google.android.gms:play-services-vision:${gmsVersion}"
     compile "com.google.android.gms:play-services-fido:${gmsVersion}"
 
-    compile "com.google.android.play:core:1.3.0"
+    compile "com.google.android.play:core:1.3.5"
 
     // Support v4 libraries
     def supportLibVersion = '27.0.0'
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 5aa6520..b557ffb9 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -781,6 +781,14 @@
               'i686': 'x86',
           }[target_arch]])
 
+      # NDK r16 "helpfully" installs libc++ as libstdc++ "so the compiler will
+      # pick it up by default". Only these days, the compiler tries to find
+      # libc++ instead. See https://crbug.com/902270.
+      shutil.copy(os.path.join(toolchain_dir, 'sysroot/usr/lib/libstdc++.a'),
+                  os.path.join(toolchain_dir, 'sysroot/usr/lib/libc++.a'))
+      shutil.copy(os.path.join(toolchain_dir, 'sysroot/usr/lib/libstdc++.so'),
+                  os.path.join(toolchain_dir, 'sysroot/usr/lib/libc++.so'))
+
       # Build compiler-rt runtimes needed for Android in a separate build tree.
       build_dir = os.path.join(LLVM_BUILD_DIR, 'android-' + target_arch)
       if not os.path.exists(build_dir):
diff --git a/tools/grit/grit/grit_runner.py b/tools/grit/grit/grit_runner.py
index bcd96c3..270cc48 100755
--- a/tools/grit/grit/grit_runner.py
+++ b/tools/grit/grit/grit_runner.py
@@ -211,7 +211,12 @@
     return 0
   else:
     options = Options()
-    args = options.ReadOptions(args)  # args may be shorter after this
+    try:
+      args = options.ReadOptions(args)  # args may be shorter after this
+    except getopt.GetoptError as e:
+      print "grit:", str(e)
+      print "Try running 'grit help' for valid options."
+      return 1
     if not args:
       print "No tool provided.  Try running 'grit help' for a list of tools."
       return 2
@@ -237,13 +242,18 @@
     if options.hash:
       grit.extern.FP.UseUnsignedFingerPrintFromModule(options.hash)
 
-    toolobject = _GetToolInfo(tool)[_FACTORY]()
-    if options.profile_dest:
-      import hotshot
-      prof = hotshot.Profile(options.profile_dest)
-      return prof.runcall(toolobject.Run, options, args[1:])
-    else:
-      return toolobject.Run(options, args[1:])
+    try:
+      toolobject = _GetToolInfo(tool)[_FACTORY]()
+      if options.profile_dest:
+        import hotshot
+        prof = hotshot.Profile(options.profile_dest)
+        return prof.runcall(toolobject.Run, options, args[1:])
+      else:
+        return toolobject.Run(options, args[1:])
+    except getopt.GetoptError as e:
+      print "grit: %s: %s" % (tool, str(e))
+      print "Try running 'grit help %s' for valid options." % (tool,)
+      return 1
 
 
 if __name__ == '__main__':
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7f5fc0b..06309d9 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -36461,7 +36461,7 @@
 <enum name="NotStreamingReason">
   <int value="0" label="DEPRECATED: Already loaded"/>
   <int value="1" label="Not HTTP"/>
-  <int value="2" label="Reload"/>
+  <int value="2" label="Revalidation"/>
   <int value="3" label="Context not valid"/>
   <int value="4" label="Encoding not supported"/>
   <int value="5" label="Thread busy"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 39f750d..ecc0149 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -8893,6 +8893,16 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Blink.Canvas.DrawImage.SqrtNumberOfPixels"
+    units="sqrt(pixels)" expires_after="2019-12-31">
+  <owner>fserb@chromium.org</owner>
+  <owner>davidqu@chromium.org</owner>
+  <summary>
+    Stores the square root of the number of pixels drawn into a Canvas.
+    Different histograms per CanvasImageSource.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Canvas.GetImageData" units="microseconds">
   <obsolete>
     Replaced with Blink.Canvas.GetImageDataScaledDuration in 10/2018.
@@ -76604,6 +76614,14 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordProtection.PageZoomFactor">
+  <owner>drubery@chromium.org</owner>
+  <summary>
+    Reports the zoom factor for a login page, when the protected password is
+    entered.
+  </summary>
+</histogram>
+
 <histogram base="true" name="PasswordProtection.PasswordAlertModeOutcome"
     enum="PasswordProtectionRequestOutcome">
   <owner>jialiul@chromium.org</owner>
@@ -85178,6 +85196,62 @@
   </summary>
 </histogram>
 
+<histogram name="ProtoDB.DestroySuccess" enum="BooleanSuccess">
+  <owner>thildebr@chromium.org</owner>
+  <summary>Whether a ProtoDB Destroy call was successful or not.</summary>
+</histogram>
+
+<histogram name="ProtoDB.GetErrorStatus" enum="LevelDBStatus">
+  <owner>thildebr@chromium.org</owner>
+  <summary>
+    The LevelDB Status returned from a failed ProtoDatabase Get call.
+  </summary>
+</histogram>
+
+<histogram name="ProtoDB.GetFound" enum="Boolean">
+  <owner>thildebr@chromium.org</owner>
+  <summary>Whether a ProtoDB Get call found what was requested.</summary>
+</histogram>
+
+<histogram name="ProtoDB.GetSuccess" enum="BooleanSuccess">
+  <owner>thildebr@chromium.org</owner>
+  <summary>Whether a ProtoDB Get call was successful or not.</summary>
+</histogram>
+
+<histogram name="ProtoDB.InitStatus" enum="LevelDBStatus">
+  <owner>thildebr@chromium.org</owner>
+  <summary>The LevelDB Status from a ProtoDatabase Init call.</summary>
+</histogram>
+
+<histogram name="ProtoDB.LoadEntriesSuccess" enum="BooleanSuccess">
+  <owner>thildebr@chromium.org</owner>
+  <summary>Whether a ProtoDB LoadEntries call was successful or not.</summary>
+</histogram>
+
+<histogram name="ProtoDB.LoadKeysAndEntriesSuccess" enum="BooleanSuccess">
+  <owner>thildebr@chromium.org</owner>
+  <summary>
+    Whether a ProtoDB LoadKeysAndEntries call was successful or not.
+  </summary>
+</histogram>
+
+<histogram name="ProtoDB.LoadKeysSuccess" enum="BooleanSuccess">
+  <owner>thildebr@chromium.org</owner>
+  <summary>Whether a ProtoDB LoadKeys call was successful or not.</summary>
+</histogram>
+
+<histogram name="ProtoDB.UpdateErrorStatus" enum="LevelDBStatus">
+  <owner>thildebr@chromium.org</owner>
+  <summary>
+    The LevelDB Status returned from a failed Protodatabase UpdateEntries call.
+  </summary>
+</histogram>
+
+<histogram name="ProtoDB.UpdateSuccess" enum="BooleanSuccess">
+  <owner>thildebr@chromium.org</owner>
+  <summary>Whether a ProtoDB UpdateEntries call was successful or not.</summary>
+</histogram>
+
 <histogram name="ProximityAuth.BleWeaveConnectionResult"
     enum="ProximityAuth_BleWeaveConnectionResult">
   <owner>hansberry@chromium.org</owner>
@@ -89972,7 +90046,7 @@
 
 <histogram name="SafeBrowsing.ContentsSize.Height" units="DIPs"
     expires_after="M73">
-  <owner>jialiul@chrormium.org</owner>
+  <owner>jialiul@chromium.org</owner>
   <owner>nparker@chromium.org</owner>
   <summary>
     Records the height of content area when the user opens a new browser window
@@ -89982,7 +90056,7 @@
 
 <histogram name="SafeBrowsing.ContentsSize.Width" units="DIPs"
     expires_after="M73">
-  <owner>jialiul@chrormium.org</owner>
+  <owner>jialiul@chromium.org</owner>
   <owner>nparker@chromium.org</owner>
   <summary>
     Records the width of content area when the user opens a new browser window
@@ -90076,6 +90150,32 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.FontSize.Default" units="pts" expires_after="M73">
+  <owner>drubery@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>Records the default font size on user startup.</summary>
+</histogram>
+
+<histogram name="SafeBrowsing.FontSize.DefaultFixed" units="pts"
+    expires_after="M73">
+  <owner>drubery@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>Records the default fixed font size on user startup.</summary>
+</histogram>
+
+<histogram name="SafeBrowsing.FontSize.Minimum" units="pts" expires_after="M73">
+  <owner>drubery@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>Records the minimum font size on user startup.</summary>
+</histogram>
+
+<histogram name="SafeBrowsing.FontSize.MinimumLogical" units="pts"
+    expires_after="M73">
+  <owner>drubery@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>Records the minimum logical font size on user startup.</summary>
+</histogram>
+
 <histogram name="SafeBrowsing.GetV4HashHttpResponseOrErrorCode"
     enum="CombinedHttpResponseAndNetErrorCode">
   <obsolete>
@@ -124957,6 +125057,20 @@
   <affected-histogram name="WebRTC.Stun.BatchSuccessPercent.UnknownNAT"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="BlinkCanvasDrawImageSizeType" separator=".">
+  <suffix name="Canvas.CPU"/>
+  <suffix name="Canvas.GPU"/>
+  <suffix name="CssImage"/>
+  <suffix name="ImageBitmap"/>
+  <suffix name="ImageElement"/>
+  <suffix name="OffscreenCanvas.CPU"/>
+  <suffix name="OffscreenCanvas.GPU"/>
+  <suffix name="SVG"/>
+  <suffix name="Unknown"/>
+  <suffix name="Video"/>
+  <affected-histogram name="Blink.Canvas.DrawImage.SqrtNumberOfPixels"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="BlinkCanvasDrawImageType" separator=".">
   <suffix base="true" name="Canvas"/>
   <suffix base="true" name="CssImage"/>
@@ -128621,6 +128735,16 @@
   <affected-histogram name="LevelDB.ApproximateMemoryUse"/>
   <affected-histogram name="LevelDB.ApproximateMemTableMemoryUse"/>
   <affected-histogram name="LevelDB.Open"/>
+  <affected-histogram name="ProtoDB.DestroySuccess"/>
+  <affected-histogram name="ProtoDB.GetErrorStatus"/>
+  <affected-histogram name="ProtoDB.GetFound"/>
+  <affected-histogram name="ProtoDB.GetSuccess"/>
+  <affected-histogram name="ProtoDB.InitStatus"/>
+  <affected-histogram name="ProtoDB.LoadEntriesSuccess"/>
+  <affected-histogram name="ProtoDB.LoadKeysAndEntriesSuccess"/>
+  <affected-histogram name="ProtoDB.LoadKeysSuccess"/>
+  <affected-histogram name="ProtoDB.UpdateErrorStatus"/>
+  <affected-histogram name="ProtoDB.UpdateSuccess"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="LevelDBEnvBackupRestore" separator="">
diff --git a/tools/perf/benchmarks/rendering.py b/tools/perf/benchmarks/rendering.py
index e2951f2..949ec6df 100644
--- a/tools/perf/benchmarks/rendering.py
+++ b/tools/perf/benchmarks/rendering.py
@@ -4,17 +4,16 @@
 from core import perf_benchmark
 
 import page_sets
-from measurements import rendering
 from telemetry import benchmark
 from telemetry import story as story_module
-
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.web_perf import timeline_based_measurement
 
 @benchmark.Info(emails=['sadrul@chromium.org', 'vmiura@chromium.org'],
                 documentation_url='https://bit.ly/rendering-benchmarks',
                 component='Internals>GPU>Metrics')
 class RenderingDesktop(perf_benchmark.PerfBenchmark):
 
-  test = rendering.Rendering
   SUPPORTED_PLATFORMS = [story_module.expectations.ALL_DESKTOP]
 
   @classmethod
@@ -31,13 +30,18 @@
   def CreateStorySet(self, options):
     return page_sets.RenderingStorySet(platform='desktop')
 
+  def CreateCoreTimelineBasedMeasurementOptions(self):
+    category_filter = chrome_trace_category_filter.CreateLowOverheadFilter()
+    options = timeline_based_measurement.Options(category_filter)
+    options.SetTimelineBasedMetrics(['renderingMetric'])
+    return options
+
 
 @benchmark.Info(emails=['sadrul@chromium.org', 'vmiura@chromium.org'],
                 documentation_url='https://bit.ly/rendering-benchmarks',
                 component='Internals>GPU>Metrics')
 class RenderingMobile(perf_benchmark.PerfBenchmark):
 
-  test = rendering.Rendering
   SUPPORTED_PLATFORMS = [story_module.expectations.ALL_MOBILE]
 
   @classmethod
@@ -53,3 +57,9 @@
 
   def CreateStorySet(self, options):
     return page_sets.RenderingStorySet(platform='mobile')
+
+  def CreateCoreTimelineBasedMeasurementOptions(self):
+    category_filter = chrome_trace_category_filter.CreateLowOverheadFilter()
+    options = timeline_based_measurement.Options(category_filter)
+    options.SetTimelineBasedMetrics(['renderingMetric'])
+    return options
diff --git a/tools/perf/contrib/cluster_telemetry/rendering_ct.py b/tools/perf/contrib/cluster_telemetry/rendering_ct.py
index eee6d160..35c33ba 100644
--- a/tools/perf/contrib/cluster_telemetry/rendering_ct.py
+++ b/tools/perf/contrib/cluster_telemetry/rendering_ct.py
@@ -4,9 +4,10 @@
 
 from contrib.cluster_telemetry import ct_benchmarks_util
 from contrib.cluster_telemetry import page_set
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.web_perf import timeline_based_measurement
 
 from core import perf_benchmark
-from measurements import rendering
 
 def ScrollToEndOfPage(action_runner):
   action_runner.Wait(1)
@@ -19,8 +20,6 @@
 
   options = {'upload_results': True}
 
-  test = rendering.Rendering
-
   @classmethod
   def Name(cls):
     return 'rendering.cluster_telemetry'
@@ -37,3 +36,9 @@
     return page_set.CTPageSet(
         options.urls_list, options.user_agent, options.archive_data_file,
         run_page_interaction_callback=ScrollToEndOfPage)
+
+  def CreateCoreTimelineBasedMeasurementOptions(self):
+    category_filter = chrome_trace_category_filter.CreateLowOverheadFilter()
+    options = timeline_based_measurement.Options(category_filter)
+    options.SetTimelineBasedMetrics(['renderingMetric'])
+    return options
diff --git a/tools/perf/contrib/cluster_telemetry/repaint.py b/tools/perf/contrib/cluster_telemetry/repaint.py
index e7d0fd8..ed88aa6 100644
--- a/tools/perf/contrib/cluster_telemetry/repaint.py
+++ b/tools/perf/contrib/cluster_telemetry/repaint.py
@@ -5,16 +5,15 @@
 from contrib.cluster_telemetry import ct_benchmarks_util
 from contrib.cluster_telemetry import page_set
 from contrib.cluster_telemetry import repaint_helpers
+from telemetry.timeline import chrome_trace_category_filter
+from telemetry.web_perf import timeline_based_measurement
 
 from core import perf_benchmark
-from measurements import rendering
 
 
 class RepaintCT(perf_benchmark.PerfBenchmark):
   """Measures repaint performance for Cluster Telemetry."""
 
-  test = rendering.Rendering
-
   @classmethod
   def Name(cls):
     return 'repaint_ct'
@@ -41,3 +40,9 @@
     return page_set.CTPageSet(
         options.urls_list, options.user_agent, options.archive_data_file,
         run_page_interaction_callback=repaint_helpers.WaitThenRepaint)
+
+  def CreateCoreTimelineBasedMeasurementOptions(self):
+    category_filter = chrome_trace_category_filter.CreateLowOverheadFilter()
+    options = timeline_based_measurement.Options(category_filter)
+    options.SetTimelineBasedMetrics(['renderingMetric'])
+    return options
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 9c094e8..249da7e 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -258,6 +258,7 @@
 crbug.com/869118 [ Linux ] system_health.memory_desktop/long_running:tools:gmail-background [ Skip ]
 crbug.com/836447 [ ChromeOS ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
 crbug.com/799734 [ Win ] system_health.memory_desktop/browse:media:tumblr [ Skip ]
+crbug.com/899887 [ Linux ] system_health.memory_desktop/browse:social:facebook_infinite_scroll:2018 [ Skip ]
 
 # Benchmark: system_health.memory_mobile
 crbug.com/900909 [ Android ] system_health.memory_mobile/browse:chrome:omnibox [ Skip ]
diff --git a/tools/perf/measurements/rendering.py b/tools/perf/measurements/rendering.py
deleted file mode 100644
index 36481f0..0000000
--- a/tools/perf/measurements/rendering.py
+++ /dev/null
@@ -1,106 +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.
-
-from telemetry.page import legacy_page_test
-from telemetry.timeline import model as model_module
-from telemetry.value import trace
-from telemetry.web_perf import smooth_gesture_util
-from telemetry.web_perf import timeline_interaction_record as tir_module
-from telemetry.timeline import tracing_config
-
-from metrics import timeline
-from measurements import rendering_util
-
-
-def _CollectRecordsFromRendererThreads(model, renderer_thread):
-  records = []
-  for event in renderer_thread.async_slices:
-    if tir_module.IsTimelineInteractionRecord(event.name):
-      interaction = tir_module.TimelineInteractionRecord.FromAsyncEvent(event)
-      # Adjust the interaction record to match the synthetic gesture
-      # controller if needed.
-      interaction = (
-          smooth_gesture_util.GetAdjustedInteractionIfContainGesture(
-              model, interaction))
-      records.append(interaction)
-  return records
-
-
-class Rendering(legacy_page_test.LegacyPageTest):
-
-  def __init__(self):
-    super(Rendering, self).__init__()
-    self._results = None
-
-  @classmethod
-  def CustomizeBrowserOptions(cls, options):
-    options.AppendExtraBrowserArgs('--enable-gpu-benchmarking')
-    options.AppendExtraBrowserArgs('--touch-events=enabled')
-
-  def WillNavigateToPage(self, page, tab):
-    config = tracing_config.TracingConfig()
-    config.enable_chrome_trace = True
-    config.enable_platform_display_trace = True
-
-    # Basic categories for smoothness.
-    config.chrome_trace_config.SetLowOverheadFilter()
-
-    # Extra categories from commandline flag.
-    if self.options and self.options.extra_chrome_categories:
-      config.chrome_trace_config.category_filter.AddFilterString(
-          self.options.extra_chrome_categories)
-
-    tab.browser.platform.tracing_controller.StartTracing(config)
-
-  def ValidateAndMeasurePage(self, _, tab, results):
-    self._results = results
-    tab.browser.platform.tracing_controller.telemetry_info = (
-        results.telemetry_info)
-    trace_result = tab.browser.platform.tracing_controller.StopTracing()
-
-    # TODO(charliea): This is part of a three-sided Chromium/Telemetry patch
-    # where we're changing the return type of StopTracing from a TraceValue to a
-    # (TraceValue, nonfatal_exception_list) tuple. Once the tuple return value
-    # lands in Chromium, the non-tuple logic should be deleted.
-    if isinstance(trace_result, tuple):
-      trace_result = trace_result[0]
-
-    trace_value = trace.TraceValue(
-        results.current_page, trace_result,
-        file_path=results.telemetry_info.trace_local_path,
-        remote_path=results.telemetry_info.trace_remote_path,
-        upload_bucket=results.telemetry_info.upload_bucket,
-        cloud_url=results.telemetry_info.trace_remote_url)
-    results.AddValue(trace_value)
-
-    model = model_module.TimelineModel(trace_result)
-    renderer_thread = model.GetFirstRendererThread(tab.id)
-    records = _CollectRecordsFromRendererThreads(model, renderer_thread)
-
-    thread_times_metric = timeline.ThreadTimesTimelineMetric()
-    thread_times_metric.AddResults(model, renderer_thread, records, results)
-
-    rendering_util.AddTBMv2RenderingMetrics(
-        trace_value, results, import_experimental_metrics=True)
-
-  def DidRunPage(self, platform):
-    if platform.tracing_controller.is_tracing_running:
-      trace_result = platform.tracing_controller.StopTracing()
-      if self._results:
-
-        # TODO(charliea): This is part of a three-sided Chromium/Telemetry patch
-        # where we're changing the return type of StopTracing from a TraceValue
-        # to a (TraceValue, nonfatal_exception_list) tuple. Once the tuple
-        # return value lands in Chromium, the non-tuple logic should be deleted.
-        if isinstance(trace_result, tuple):
-          trace_result = trace_result[0]
-
-        trace_value = trace.TraceValue(
-            self._results.current_page, trace_result,
-            file_path=self._results.telemetry_info.trace_local_path,
-            remote_path=self._results.telemetry_info.trace_remote_path,
-            upload_bucket=self._results.telemetry_info.upload_bucket,
-            cloud_url=self._results.telemetry_info.trace_remote_url)
-
-        self._results.AddValue(trace_value)
diff --git a/tools/perf/measurements/rendering_unittest.py b/tools/perf/measurements/rendering_unittest.py
deleted file mode 100644
index f2db811..0000000
--- a/tools/perf/measurements/rendering_unittest.py
+++ /dev/null
@@ -1,41 +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.
-
-from telemetry.testing import options_for_unittests
-from telemetry.testing import page_test_test_case
-from telemetry.util import wpr_modes
-
-from measurements import rendering
-from measurements import rendering_util
-
-RENDERING_THREAD_GROUPS = ['GPU', 'IO', 'browser', 'display_compositor',
-                           'other', 'raster', 'renderer_compositor',
-                           'renderer_main', 'total_all', 'total_fast_path']
-
-class RenderingUnitTest(page_test_test_case.PageTestTestCase):
-  """Test for rendering measurement
-
-     Runs rendering measurement on a simple page and verifies the existence of
-     all metrics in the results. The correctness of metrics is not tested here.
-  """
-
-  def setUp(self):
-    self._options = options_for_unittests.GetCopy()
-    self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
-    self._options.pageset_repeat = 2
-
-  def testRendering(self):
-    ps = self.CreateStorySetFromFileInUnittestDataDir('scrollable_page.html')
-    results = self.RunMeasurement(
-        rendering.Rendering(), ps, options=self._options)
-    self.assertFalse(results.had_failures)
-    stat = rendering_util.ExtractStat(results)
-
-    self.assertGreater(stat['frame_times'].count, 1)
-    self.assertGreater(stat['percentage_smooth'].count, 1)
-    for thread_group in RENDERING_THREAD_GROUPS:
-      # We should have at least two sample values for each metric, since
-      # pageset_repeat is 2.
-      histogram_name = 'thread_%s_cpu_time_per_frame' % thread_group
-      self.assertGreater(stat[histogram_name].count, 1)
diff --git a/tools/perf/measurements/rendering_util.py b/tools/perf/measurements/rendering_util.py
deleted file mode 100644
index abea7ab..0000000
--- a/tools/perf/measurements/rendering_util.py
+++ /dev/null
@@ -1,42 +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.
-
-from tracing.metrics import metric_runner
-from tracing.value import histogram as histogram_module
-
-def AddTBMv2RenderingMetrics(trace_value, results, import_experimental_metrics):
-  mre_result = metric_runner.RunMetric(
-      trace_value.filename, metrics=['renderingMetric'],
-      extra_import_options={'trackDetailedModelStats': True},
-      report_progress=False, canonical_url=results.telemetry_info.trace_url)
-
-  for f in mre_result.failures:
-    results.Fail(f.stack)
-
-  existing_values = {v.name for v in results.all_page_specific_values}
-  histograms = []
-  for histogram in mre_result.pairs.get('histograms', []):
-    name = histogram.get('name', '')
-    if ((import_experimental_metrics or name.find('_tbmv2') < 0) and
-        not name in existing_values):
-      histograms.append(histogram)
-  results.ImportHistogramDicts(histograms, import_immediately=False)
-
-def ExtractStat(results):
-  stat = {}
-  for histogram_dict in results.AsHistogramDicts():
-    # It would be nicer if instead of converting results._histograms to dicts
-    # and then parsing them back in the following line, results had a getter
-    # returning results._histograms. But, since this is a temporary code that
-    # will be deleted after transitioning Smoothness to TBMv2, we don't change
-    # page_test_results.py for a temporary usecase.
-    if 'name' in histogram_dict:
-      histogram = histogram_module.Histogram.FromDict(histogram_dict)
-      if histogram.running is None:
-        continue
-      if histogram.name in stat:
-        stat[histogram.name] = stat[histogram.name].Merge(histogram.running)
-      else:
-        stat[histogram.name] = histogram.running
-  return stat
diff --git a/tools/perf/metrics/timeline.py b/tools/perf/metrics/timeline.py
deleted file mode 100644
index 675ad65..0000000
--- a/tools/perf/metrics/timeline.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import collections
-
-from telemetry.util.statistics import DivideIfPossibleOrZero
-from telemetry.value import scalar
-from telemetry.web_perf.metrics import timeline_based_metric
-
-
-# We want to generate a consistent picture of our thread usage, despite
-# having several process configurations (in-proc-gpu/single-proc).
-# Since we can't isolate renderer threads in single-process mode, we
-# always sum renderer-process threads' times. We also sum all io-threads
-# for simplicity.
-TimelineThreadCategories = {
-    "Chrome_InProcGpuThread": "GPU",
-    "CrGpuMain": "GPU",
-    "VizCompositorThread": "display_compositor",
-    "CrBrowserMain": "browser",
-    "Browser Compositor": "browser",
-    "CrRendererMain": "renderer_main",
-    "Compositor": "renderer_compositor",
-    "IOThread": "IO",
-    "CompositorTileWorker": "raster",
-    "DummyThreadName1": "other",
-    "DummyThreadName2": "total_fast_path",
-    "DummyThreadName3": "total_all"
-}
-
-_MatchBySubString = ["IOThread", "CompositorTileWorker"]
-
-AllThreads = TimelineThreadCategories.values()
-NoThreads = []
-FastPathThreads = [
-  "GPU", "display_compositor", "renderer_compositor", "browser", "IO"]
-
-ReportMainThreadOnly = ["renderer_main"]
-ReportSilkDetails = ["renderer_main"]
-
-# TODO(epenner): Thread names above are likely fairly stable but trace names
-# could change. We should formalize these traces to keep this robust.
-OverheadTraceCategory = "trace_event_overhead"
-OverheadTraceName = "overhead"
-FrameTraceName = "Graphics.Pipeline"
-FrameTraceStepName = "GenerateCompositorFrame"
-FrameTraceThreadName = "renderer_compositor"
-
-
-def Rate(numerator, denominator):
-  return DivideIfPossibleOrZero(numerator, denominator)
-
-
-def ThreadCategoryName(thread_name):
-  thread_category = "other"
-  for substring, category in TimelineThreadCategories.iteritems():
-    if substring in _MatchBySubString and substring in thread_name:
-      thread_category = category
-  if thread_name in TimelineThreadCategories:
-    thread_category = TimelineThreadCategories[thread_name]
-  return thread_category
-
-
-def ThreadTasksResultName(thread_category):
-  return "tasks_per_frame_" + thread_category
-
-
-def ThreadMeanFrameTimeResultName(thread_category):
-  return "mean_frame_time_" + thread_category
-
-
-def ThreadDetailResultName(thread_category, detail):
-  detail_sanitized = detail.replace(".", "_")
-  return "thread_" + thread_category + "|" + detail_sanitized
-
-
-class ResultsForThread(object):
-
-  def __init__(self, model, record_ranges, name):
-    self.model = model
-    self.toplevel_slices = []
-    self.all_slices = []
-    self.name = name
-    self.record_ranges = record_ranges
-    self.all_action_time = \
-        sum([record_range.bounds for record_range in self.record_ranges])
-
-  def SlicesInActions(self, slices):
-    slices_in_actions = []
-    for event in slices:
-      for record_range in self.record_ranges:
-        if record_range.ContainsInterval(event.start, event.end):
-          slices_in_actions.append(event)
-          break
-    return slices_in_actions
-
-  def AppendThreadSlices(self, thread):
-    self.all_slices.extend(self.SlicesInActions(thread.all_slices))
-    self.toplevel_slices.extend(self.SlicesInActions(thread.toplevel_slices))
-
-  # Reports cpu-time per interval and tasks per interval.
-  def AddResults(self, num_intervals, results):
-    tasks_per_interval = Rate(len(self.toplevel_slices), num_intervals)
-    results.AddValue(scalar.ScalarValue(
-        results.current_page,
-        ThreadTasksResultName(self.name),
-        "tasks", tasks_per_interval))
-
-  def AddDetailedResults(self, num_intervals, results):
-    slices_by_category = collections.defaultdict(list)
-    for s in self.all_slices:
-      slices_by_category[s.category].append(s)
-    all_self_times = []
-    for category, slices_in_category in slices_by_category.iteritems():
-      self_time = sum([x.self_time for x in slices_in_category])
-      all_self_times.append(self_time)
-      self_time_per_interval = Rate(self_time, num_intervals)
-      results.AddValue(scalar.ScalarValue(
-          results.current_page,
-          ThreadDetailResultName(self.name, category),
-          "ms",
-          self_time_per_interval))
-    all_measured_time = sum(all_self_times)
-    idle_time = max(0, self.all_action_time - all_measured_time)
-    idle_time_per_interval = Rate(idle_time, num_intervals)
-    results.AddValue(scalar.ScalarValue(
-        results.current_page,
-        ThreadDetailResultName(self.name, "idle"),
-        "ms",
-        idle_time_per_interval))
-
-  def CountTracesWithNameAndArg(self, trace_name, step):
-    count = 0
-    for event in self.all_slices:
-      if trace_name in event.name and event.args and event.args["step"] == step:
-        count += 1
-    return count
-
-
-class ThreadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric):
-
-  def __init__(self):
-    super(ThreadTimesTimelineMetric, self).__init__()
-    # Minimal traces, for minimum noise in CPU-time measurements.
-    self.results_to_report = AllThreads
-    self.details_to_report = NoThreads
-
-  def AddResults(self, model, _, interaction_records, results):
-    # Set up each thread category for consistent results.
-    thread_category_results = {}
-    for name in TimelineThreadCategories.values():
-      thread_category_results[name] = ResultsForThread(
-          model, [r.GetBounds() for r in interaction_records], name)
-
-    # Group the slices by their thread category.
-    for thread in model.GetAllThreads():
-      thread_category = ThreadCategoryName(thread.name)
-      thread_category_results[thread_category].AppendThreadSlices(thread)
-
-    # Group all threads.
-    for thread in model.GetAllThreads():
-      thread_category_results["total_all"].AppendThreadSlices(thread)
-
-    # Also group fast-path threads.
-    for thread in model.GetAllThreads():
-      if ThreadCategoryName(thread.name) in FastPathThreads:
-        thread_category_results["total_fast_path"].AppendThreadSlices(thread)
-
-    # Calculate the interaction's number of frames.
-    frame_rate_thread = thread_category_results[FrameTraceThreadName]
-    num_frames = frame_rate_thread.CountTracesWithNameAndArg(FrameTraceName,
-        FrameTraceStepName)
-
-    # Report the desired results and details for each interval type.
-    for thread_results in thread_category_results.values():
-      if thread_results.name in self.results_to_report:
-        thread_results.AddResults(num_frames, results)
-      # TODO(nduca): When generic results objects are done, this special case
-      # can be replaced with a generic UI feature.
-      if thread_results.name in self.details_to_report:
-        thread_results.AddDetailedResults(num_frames, results)
-
-    # Report mean frame time for the frame rate thread. We could report other
-    # frame rates (e.g. renderer_main) but this might get confusing.
-    mean_frame_time = Rate(frame_rate_thread.all_action_time, num_frames)
-    results.AddValue(scalar.ScalarValue(
-        results.current_page,
-        ThreadMeanFrameTimeResultName(FrameTraceThreadName),
-        "ms", mean_frame_time))
diff --git a/tools/perf/metrics/timeline_unittest.py b/tools/perf/metrics/timeline_unittest.py
deleted file mode 100644
index 383f42d..0000000
--- a/tools/perf/metrics/timeline_unittest.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import unittest
-
-from telemetry.testing import test_page_test_results
-from telemetry.timeline import model as model_module
-from telemetry.web_perf import timeline_interaction_record as tir_module
-
-from metrics import timeline
-
-
-def _GetInteractionRecord(start, end):
-  return tir_module.TimelineInteractionRecord('test-record', start, end)
-
-
-class ThreadTimesTimelineMetricUnittest(unittest.TestCase):
-
-  def GetResults(self, metric, model, renderer_thread, interaction_record):
-    results = test_page_test_results.TestPageTestResults(self)
-    metric.AddResults(model, renderer_thread, interaction_record,
-                      results)
-    return results
-
-  def testResults(self):
-    model = model_module.TimelineModel()
-    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
-    renderer_main.name = 'CrRendererMain'
-
-    metric = timeline.ThreadTimesTimelineMetric()
-    metric.details_to_report = timeline.ReportMainThreadOnly
-    results = self.GetResults(metric, model, renderer_main.parent,
-                              [_GetInteractionRecord(1, 2)])
-
-    # Test that all result thread categories exist
-    for name in timeline.TimelineThreadCategories.values():
-      results.GetPageSpecificValueNamed(timeline.ThreadTasksResultName(name))
-
-  def testBasic(self):
-    model = model_module.TimelineModel()
-    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
-    renderer_main.name = 'CrRendererMain'
-
-    # Create two frame swaps (Results times should be divided by two) for
-    # an interaction that lasts 20 milliseconds.
-    cc_main = model.GetOrCreateProcess(1).GetOrCreateThread(3)
-    cc_main.name = 'Compositor'
-    cc_main.BeginSlice('cc_cat', timeline.FrameTraceName, 10, 10,
-        args={'step': 'GenerateCompositorFrame'})
-    cc_main.EndSlice(11, 11)
-    cc_main.BeginSlice('cc_cat', timeline.FrameTraceName, 12, 12,
-        args={'step': 'GenerateCompositorFrame'})
-    cc_main.EndSlice(13, 13)
-
-    # [      X       ]   [ Z ]
-    #      [  Y  ]
-    renderer_main.BeginSlice('cat1', 'X', 10, 0)
-    renderer_main.BeginSlice('cat2', 'Y', 15, 5)
-    renderer_main.EndSlice(16, 5.5)
-    renderer_main.EndSlice(30, 19.5)
-    renderer_main.BeginSlice('cat1', 'Z', 31, 20)
-    renderer_main.BeginSlice('cat1', 'Z', 33, 21)
-    model.FinalizeImport()
-
-    # Exclude 'Z' using an action-range.
-    metric = timeline.ThreadTimesTimelineMetric()
-    metric.details_to_report = timeline.ReportMainThreadOnly
-    results = self.GetResults(metric, model, renderer_main.parent,
-                              [_GetInteractionRecord(10, 30)])
-
-    # Test for the results we expect.
-    main_thread = 'renderer_main'
-    cc_thread = 'renderer_compositor'
-    assert_results = [
-        (timeline.ThreadMeanFrameTimeResultName(cc_thread), 'ms', 10.0),
-        (timeline.ThreadTasksResultName(main_thread), 'tasks', 0.5),
-        (timeline.ThreadTasksResultName(cc_thread), 'tasks', 1.0),
-        (timeline.ThreadDetailResultName(main_thread, 'cat1'), 'ms', 9.5),
-        (timeline.ThreadDetailResultName(main_thread, 'cat2'), 'ms', 0.5),
-        (timeline.ThreadDetailResultName(main_thread, 'idle'), 'ms', 0),
-    ]
-    for name, unit, value in assert_results:
-      results.AssertHasPageSpecificScalarValue(name, unit, value)
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 0fb85221..03c3b55 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -2412,6 +2412,20 @@
             top_level->GetHost()->GetBoundsInPixels());
 }
 
+TEST_F(WindowTreeClientTestHighDPI, HostCtorInitializesDisplayScale) {
+  // WindowTreeHost's ctor attempts to initialize the display scale factor.
+  WindowTreeHostMus window_tree_host(
+      CreateInitParamsForTopLevel(window_tree_client_impl()));
+  EXPECT_EQ(2.0f, window_tree_host.device_scale_factor());
+  // This test sets pixel bounds before calling WindowTreeHost::InitHost() to
+  // simulate circumstances similar to <http://crbug.com/899084>. Pixel bounds
+  // should still be scaled to DIP bounds for the window tree before InitHost.
+  gfx::Rect bounds(2, 4, 60, 80);
+  gfx::Rect pixel_bounds = gfx::ConvertRectToPixel(2.0f, bounds);
+  window_tree_host.SetBoundsInPixels(pixel_bounds);
+  EXPECT_EQ(bounds, window_tree()->last_set_window_bounds());
+}
+
 TEST_F(WindowTreeClientTestHighDPI, ObservedInputEventsInDip) {
   display::Screen* screen = display::Screen::GetScreen();
   const display::Display primary_display = screen->GetPrimaryDisplay();
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index 41c376e..d8e79f8 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -348,6 +348,8 @@
   if (!window_)
     window_ = new Window(nullptr);
   display::Screen::GetScreen()->AddObserver(this);
+  auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(window_);
+  device_scale_factor_ = display.device_scale_factor();
 }
 
 void WindowTreeHost::IntializeDeviceScaleFactor(float device_scale_factor) {
diff --git a/ui/compositor/test/in_process_context_provider.cc b/ui/compositor/test/in_process_context_provider.cc
index 0cd4452..7d95731 100644
--- a/ui/compositor/test/in_process_context_provider.cc
+++ b/ui/compositor/test/in_process_context_provider.cc
@@ -17,6 +17,7 @@
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/command_buffer/common/skia_utils.h"
 #include "gpu/ipc/gl_in_process_context.h"
+#include "gpu/ipc/test_gpu_thread_holder.h"
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
@@ -98,8 +99,7 @@
 
   context_ = std::make_unique<gpu::GLInProcessContext>();
   bind_result_ = context_->Initialize(
-      nullptr,  /* service */
-      nullptr,  /* surface */
+      gpu::GetTestGpuThreadHolder()->GetTaskExecutor(), nullptr, /* surface */
       !window_, /* is_offscreen */
       window_, attribs_, gpu::SharedMemoryLimits(), gpu_memory_buffer_manager_,
       image_factory_, base::ThreadTaskRunnerHandle::Get());
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 06e2cff..ddd7047 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -1214,12 +1214,40 @@
   SetDefaultMultiDisplayModeForCurrentDisplays(UNIFIED);
 }
 
-const Display* DisplayManager::GetPrimaryMirroringDisplayForUnifiedDesktop()
-    const {
+Display DisplayManager::GetMirroringDisplayForUnifiedDesktop(
+    DisplayPositionInUnifiedMatrix cell_position) const {
   if (!IsInUnifiedMode())
-    return nullptr;
+    return Display();
 
-  return &software_mirroring_display_list_[0];
+  DCHECK(!current_unified_desktop_matrix_.empty());
+
+  const size_t rows = current_unified_desktop_matrix_.size();
+  const size_t columns = current_unified_desktop_matrix_[0].size();
+
+  int64_t display_id = kInvalidDisplayId;
+  switch (cell_position) {
+    case DisplayPositionInUnifiedMatrix::kTopLeft:
+      display_id = current_unified_desktop_matrix_[0][0];
+      break;
+
+    case DisplayPositionInUnifiedMatrix::kTopRight:
+      display_id = current_unified_desktop_matrix_[0][columns - 1];
+      break;
+
+    case DisplayPositionInUnifiedMatrix::kBottomLeft:
+      display_id = current_unified_desktop_matrix_[rows - 1][0];
+      break;
+  }
+
+  DCHECK_NE(display_id, kInvalidDisplayId);
+
+  for (auto& display : software_mirroring_display_list_) {
+    if (display.id() == display_id)
+      return display;
+  }
+
+  NOTREACHED();
+  return Display();
 }
 
 int DisplayManager::GetMirroringDisplayRowIndexInUnifiedMatrix(
diff --git a/ui/display/manager/display_manager.h b/ui/display/manager/display_manager.h
index 6f0d784..86c8115 100644
--- a/ui/display/manager/display_manager.h
+++ b/ui/display/manager/display_manager.h
@@ -375,11 +375,11 @@
   // current mode to Unified Desktop.
   void SetUnifiedDesktopMatrix(const UnifiedDesktopLayoutMatrix& matrix);
 
-  // In Unified Desktop mode, we consider the first mirroring display to be the
-  // primary. It's also the top-left display in the layout matrix, and it's
-  // where the shelf is placed.
-  // This returns nullptr if we're not in unified desktop mode.
-  const Display* GetPrimaryMirroringDisplayForUnifiedDesktop() const;
+  // Returns the Unified Desktop mode mirroring display according to the
+  // supplied |cell_position| in the matrix. Returns invalid display if we're
+  // not in Unified mode.
+  Display GetMirroringDisplayForUnifiedDesktop(
+      DisplayPositionInUnifiedMatrix cell_position) const;
 
   // Returns the index of the row in the Unified Mode layout matrix which
   // contains the display with |display_id|.
diff --git a/ui/display/unified_desktop_utils.h b/ui/display/unified_desktop_utils.h
index 7c120e0..426b323 100644
--- a/ui/display/unified_desktop_utils.h
+++ b/ui/display/unified_desktop_utils.h
@@ -12,6 +12,13 @@
 
 namespace display {
 
+// Defines a display position in the unified display matrix.
+enum class DisplayPositionInUnifiedMatrix {
+  kTopLeft,
+  kTopRight,
+  kBottomLeft,
+};
+
 // Type of the matrix that represents the display layout in Unified Desktop
 // mode. The ID of a display is placed in a cell in the matrix where that
 // display is desired to be placed in the actual layout.