diff --git a/DEPS b/DEPS
index 7ed11363b..1e46bf2 100644
--- a/DEPS
+++ b/DEPS
@@ -105,7 +105,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': 'cf7258abac8980323362b65161700b5260f7f3a8',
+  'skia_revision': '767fddfcce612c1618c1b6f6dbac4d88565f0b13',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -165,7 +165,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': 'f5981fb3c2b045ad90b1418a134cc49e76260d9d',
+  'catapult_revision': '5314945fa47aafd1edb930ace2590bdc7c874632',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -552,7 +552,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '25c5ebd6302c7ed72f9475934ca8cacbbf5edbe9',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '937745b816068ad929ea185686c1627fe1b8b220',
       'condition': 'checkout_linux',
   },
 
@@ -1049,7 +1049,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7ca87fb1d3da3b3d2060886e8c58e726d74c8219',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b336c2784f5e1e6e2f59e62a18b2d0e21a555b41',
+    Var('webrtc_git') + '/src.git' + '@' + 'e9721f2f08307e67c602a3c614ec144c5fb7e5c2',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/ash/shelf/shelf_application_menu_model_unittest.cc b/ash/shelf/shelf_application_menu_model_unittest.cc
index 0ea0e54..27e3245b 100644
--- a/ash/shelf/shelf_application_menu_model_unittest.cc
+++ b/ash/shelf/shelf_application_menu_model_unittest.cc
@@ -50,11 +50,13 @@
   base::string16 title = base::ASCIIToUTF16("title");
   ShelfApplicationMenuModel menu(title, std::vector<mojom::MenuItemPtr>(),
                                  nullptr);
-  // Expect the title.
-  ASSERT_EQ(static_cast<int>(1), menu.GetItemCount());
-  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(0));
-  EXPECT_EQ(title, menu.GetLabelAt(0));
-  EXPECT_FALSE(menu.IsEnabledAt(0));
+  // Expect the title with separators.
+  ASSERT_EQ(static_cast<int>(3), menu.GetItemCount());
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(0));
+  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(1));
+  EXPECT_EQ(title, menu.GetLabelAt(1));
+  EXPECT_FALSE(menu.IsEnabledAt(1));
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(2));
 }
 
 // Verifies the menu contents given a non-empty item list.
@@ -74,23 +76,23 @@
   ShelfApplicationMenuModel menu(title, std::move(items), nullptr);
   ShelfApplicationMenuModelTestAPI menu_test_api(&menu);
 
-  // Expect the title and the enabled items.
-  ASSERT_EQ(static_cast<int>(5), menu.GetItemCount());
-
-  // The label title should not be enabled.
-  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(0));
-  EXPECT_EQ(title, menu.GetLabelAt(0));
-  EXPECT_FALSE(menu.IsEnabledAt(0));
-
+  // Expect the title with separators, the enabled items, and another separator.
+  ASSERT_EQ(static_cast<int>(7), menu.GetItemCount());
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(0));
   EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(1));
-  EXPECT_EQ(title1, menu.GetLabelAt(1));
-  EXPECT_TRUE(menu.IsEnabledAt(1));
-  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(2));
-  EXPECT_EQ(title2, menu.GetLabelAt(2));
-  EXPECT_TRUE(menu.IsEnabledAt(2));
+  EXPECT_EQ(title, menu.GetLabelAt(1));
+  EXPECT_FALSE(menu.IsEnabledAt(1));
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(2));
   EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(3));
-  EXPECT_EQ(title3, menu.GetLabelAt(3));
+  EXPECT_EQ(title1, menu.GetLabelAt(3));
   EXPECT_TRUE(menu.IsEnabledAt(3));
+  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(4));
+  EXPECT_EQ(title2, menu.GetLabelAt(4));
+  EXPECT_TRUE(menu.IsEnabledAt(4));
+  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(5));
+  EXPECT_EQ(title3, menu.GetLabelAt(5));
+  EXPECT_TRUE(menu.IsEnabledAt(5));
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(6));
 }
 
 // Verifies RecordMenuItemSelectedMetrics uses the correct histogram buckets.
diff --git a/ash/shelf/shelf_context_menu_model_unittest.cc b/ash/shelf/shelf_context_menu_model_unittest.cc
index ee70b604..16ddc589 100644
--- a/ash/shelf/shelf_context_menu_model_unittest.cc
+++ b/ash/shelf/shelf_context_menu_model_unittest.cc
@@ -126,53 +126,33 @@
 
   // Check the shelf auto-hide behavior and menu interaction.
   ShelfContextMenuModel menu1(MenuItemList(), nullptr, primary_id);
+  EXPECT_FALSE(menu1.IsItemCheckedAt(0));
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
   menu1.ActivatedAt(0);
   EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
 
-  // Recreate the menu, auto-hide should still be enabled.
+  // This menu shows auto-hide enabled; check alignment and menu interaction.
   ShelfContextMenuModel menu2(MenuItemList(), nullptr, primary_id);
-  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
-
-  // By default the shelf should be on bottom, shelf alignment options in order:
-  // Left, Bottom, Right. Bottom should be checked.
+  EXPECT_TRUE(menu2.IsItemCheckedAt(0));
   ui::MenuModel* submenu = menu2.GetSubmenuModelAt(1);
   EXPECT_TRUE(submenu->IsItemCheckedAt(1));
   EXPECT_EQ(SHELF_ALIGNMENT_BOTTOM, shelf->alignment());
-
-  // Activate the left shelf alignment option.
   submenu->ActivatedAt(0);
   EXPECT_EQ(SHELF_ALIGNMENT_LEFT, shelf->alignment());
 
-  // Recreate the menu, it should show left alignment checked.
+  // This menu shows left alignment; check wallpaper item interaction.
   ShelfContextMenuModel menu3(MenuItemList(), nullptr, primary_id);
   submenu = menu3.GetSubmenuModelAt(1);
   EXPECT_TRUE(submenu->IsItemCheckedAt(0));
-
   TestWallpaperControllerClient client;
   Shell::Get()->wallpaper_controller()->SetClientForTesting(
       client.CreateInterfacePtr());
   EXPECT_EQ(0u, client.open_count());
-
-  // Click the third option, wallpaper picker. It should open.
   menu3.ActivatedAt(2);
   Shell::Get()->wallpaper_controller()->FlushForTesting();
   EXPECT_EQ(1u, client.open_count());
 }
 
-// Tests that a desktop context menu shows the default options.
-TEST_F(ShelfContextMenuModelTest, DesktopMenu) {
-  MenuItemList items;
-  // Pass a null delegate to indicate the menu is not for an application.
-  ShelfContextMenuModel menu(std::move(items), nullptr,
-                             GetPrimaryDisplay().id());
-
-  ASSERT_EQ(3, menu.GetItemCount());
-  EXPECT_EQ(CommandId::MENU_AUTO_HIDE, menu.GetCommandIdAt(0));
-  EXPECT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu.GetCommandIdAt(1));
-  EXPECT_EQ(CommandId::MENU_CHANGE_WALLPAPER, menu.GetCommandIdAt(2));
-}
-
 // Tests the prepending of custom items in a shelf context menu.
 TEST_F(ShelfContextMenuModelTest, CustomItems) {
   // Make a list of custom items with a variety of values.
@@ -183,7 +163,9 @@
   item->label = base::ASCIIToUTF16("item");
   item->enabled = true;
   items.push_back(std::move(item));
-
+  mojom::MenuItemPtr separator(mojom::MenuItem::New());
+  separator->type = ui::MenuModel::TYPE_SEPARATOR;
+  items.push_back(std::move(separator));
   mojom::MenuItemPtr check(mojom::MenuItem::New());
   check->type = ui::MenuModel::TYPE_CHECK;
   check->command_id = 999;
@@ -191,7 +173,6 @@
   check->enabled = true;
   check->checked = false;
   items.push_back(std::move(check));
-
   mojom::MenuItemPtr radio(mojom::MenuItem::New());
   radio->type = ui::MenuModel::TYPE_RADIO;
   radio->command_id = 1337;
@@ -200,35 +181,38 @@
   radio->checked = true;
   items.push_back(std::move(radio));
 
+  // Ensure the menu model's prepended contents match the items above.
   TestShelfItemDelegate delegate;
   ShelfContextMenuModel menu(std::move(items), &delegate,
                              GetPrimaryDisplay().id());
-
-  // Ensure the menu model's prepended contents match the items above. Because
-  // the delegate is not null, the menu is interpreted as an application menu.
-  // It will not have the desktop menu options which are autohide, shelf
-  // position, and wallpaper picker.
-  ASSERT_EQ(3, menu.GetItemCount());
+  ASSERT_EQ(7, menu.GetItemCount());
 
   EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(0));
   EXPECT_EQ(123, menu.GetCommandIdAt(0));
   EXPECT_EQ(base::ASCIIToUTF16("item"), menu.GetLabelAt(0));
   EXPECT_TRUE(menu.IsEnabledAt(0));
 
-  EXPECT_EQ(ui::MenuModel::TYPE_CHECK, menu.GetTypeAt(1));
-  EXPECT_EQ(999, menu.GetCommandIdAt(1));
-  EXPECT_EQ(base::ASCIIToUTF16("check"), menu.GetLabelAt(1));
-  EXPECT_TRUE(menu.IsEnabledAt(1));
-  EXPECT_FALSE(menu.IsItemCheckedAt(1));
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(1));
 
-  EXPECT_EQ(ui::MenuModel::TYPE_RADIO, menu.GetTypeAt(2));
-  EXPECT_EQ(1337, menu.GetCommandIdAt(2));
-  EXPECT_EQ(base::ASCIIToUTF16("radio"), menu.GetLabelAt(2));
-  EXPECT_FALSE(menu.IsEnabledAt(2));
-  EXPECT_TRUE(menu.IsItemCheckedAt(2));
+  EXPECT_EQ(ui::MenuModel::TYPE_CHECK, menu.GetTypeAt(2));
+  EXPECT_EQ(999, menu.GetCommandIdAt(2));
+  EXPECT_EQ(base::ASCIIToUTF16("check"), menu.GetLabelAt(2));
+  EXPECT_TRUE(menu.IsEnabledAt(2));
+  EXPECT_FALSE(menu.IsItemCheckedAt(2));
+
+  EXPECT_EQ(ui::MenuModel::TYPE_RADIO, menu.GetTypeAt(3));
+  EXPECT_EQ(1337, menu.GetCommandIdAt(3));
+  EXPECT_EQ(base::ASCIIToUTF16("radio"), menu.GetLabelAt(3));
+  EXPECT_FALSE(menu.IsEnabledAt(3));
+  EXPECT_TRUE(menu.IsItemCheckedAt(3));
+
+  // The default contents should appear at the bottom.
+  EXPECT_EQ(CommandId::MENU_AUTO_HIDE, menu.GetCommandIdAt(4));
+  EXPECT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu.GetCommandIdAt(5));
+  EXPECT_EQ(CommandId::MENU_CHANGE_WALLPAPER, menu.GetCommandIdAt(6));
 
   // Invoking a custom item should execute the command id on the delegate.
-  menu.ActivatedAt(1);
+  menu.ActivatedAt(2);
   EXPECT_EQ(999, delegate.last_executed_command());
 }
 
@@ -275,54 +259,44 @@
   EXPECT_NE(-1, secondary_menu.GetIndexOfCommandId(CommandId::MENU_AUTO_HIDE));
 }
 
-// Tests that the autohide and alignment menu options are not included in tablet
-// mode.
-TEST_F(ShelfContextMenuModelTest, ExcludeClamshellOptionsOnTabletMode) {
+TEST_F(ShelfContextMenuModelTest, DisableAutoHideOptionOnTabletMode) {
   TabletModeController* tablet_mode_controller =
       Shell::Get()->tablet_mode_controller();
   int64_t primary_id = GetPrimaryDisplay().id();
 
-  // In tablet mode, the wallpaper picker should be the only option because the
-  // other options are disabled.
+  // Tests that in tablet mode, shelf auto-hide option is disabled.
   tablet_mode_controller->EnableTabletModeWindowManager(true);
   ShelfContextMenuModel menu1(MenuItemList(), nullptr, primary_id);
-  EXPECT_EQ(1, menu1.GetItemCount());
-  EXPECT_EQ(ShelfContextMenuModel::MENU_CHANGE_WALLPAPER,
-            menu1.GetCommandIdAt(0));
+  ASSERT_EQ(CommandId::MENU_AUTO_HIDE, menu1.GetCommandIdAt(0));
+  EXPECT_FALSE(menu1.IsEnabledAt(0));
+  EXPECT_TRUE(menu1.IsVisibleAt(0));
 
-  // Test that a menu shown out of tablet mode includes all three options:
-  // MENU_AUTO_HIDE, MENU_ALIGNMENT_MENU, and MENU_CHANGE_WALLPAPER.
+  // Tests that exiting tablet mode reenables the auto-hide context menu item.
   tablet_mode_controller->EnableTabletModeWindowManager(false);
   ShelfContextMenuModel menu2(MenuItemList(), nullptr, primary_id);
-  EXPECT_EQ(3, menu2.GetItemCount());
-
-  // Test the auto hide option.
-  EXPECT_EQ(ShelfContextMenuModel::MENU_AUTO_HIDE, menu2.GetCommandIdAt(0));
+  ASSERT_EQ(CommandId::MENU_AUTO_HIDE, menu2.GetCommandIdAt(0));
   EXPECT_TRUE(menu2.IsEnabledAt(0));
+  EXPECT_TRUE(menu2.IsVisibleAt(0));
+}
 
-  // Test the shelf alignment menu option.
-  EXPECT_EQ(ShelfContextMenuModel::MENU_ALIGNMENT_MENU,
-            menu2.GetCommandIdAt(1));
+TEST_F(ShelfContextMenuModelTest, DisableAlignmentMenuOnTabletMode) {
+  TabletModeController* tablet_mode_controller =
+      Shell::Get()->tablet_mode_controller();
+  int64_t primary_id = GetPrimaryDisplay().id();
+
+  // Tests that in tablet mode, shelf alignment menu is disabled.
+  tablet_mode_controller->EnableTabletModeWindowManager(true);
+  ShelfContextMenuModel menu1(MenuItemList(), nullptr, primary_id);
+  ASSERT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu1.GetCommandIdAt(1));
+  EXPECT_FALSE(menu1.IsEnabledAt(1));
+  EXPECT_TRUE(menu1.IsVisibleAt(1));
+
+  // Tests that exiting tablet mode reenables the shelf alignment menu.
+  tablet_mode_controller->EnableTabletModeWindowManager(false);
+  ShelfContextMenuModel menu2(MenuItemList(), nullptr, primary_id);
+  ASSERT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu2.GetCommandIdAt(1));
   EXPECT_TRUE(menu2.IsEnabledAt(1));
-
-  // Test the shelf alignment submenu.
-  ui::MenuModel* submenu = menu2.GetSubmenuModelAt(1);
-  EXPECT_EQ(ShelfContextMenuModel::MENU_ALIGNMENT_LEFT,
-            submenu->GetCommandIdAt(0));
-  EXPECT_TRUE(submenu->IsEnabledAt(0));
-
-  EXPECT_EQ(ShelfContextMenuModel::MENU_ALIGNMENT_BOTTOM,
-            submenu->GetCommandIdAt(1));
-  EXPECT_TRUE(submenu->IsEnabledAt(1));
-
-  EXPECT_EQ(ShelfContextMenuModel::MENU_ALIGNMENT_RIGHT,
-            submenu->GetCommandIdAt(2));
-  EXPECT_TRUE(submenu->IsEnabledAt(2));
-
-  // Test the wallpaper picker option.
-  EXPECT_EQ(ShelfContextMenuModel::MENU_CHANGE_WALLPAPER,
-            menu2.GetCommandIdAt(2));
-  EXPECT_TRUE(menu2.IsEnabledAt(2));
+  EXPECT_TRUE(menu2.IsVisibleAt(1));
 }
 
 TEST_F(ShelfContextMenuModelTest, CommandIdsMatchEnumsForHistograms) {
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index e5410bb..ea1eff12 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -56,6 +56,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_mock_time_message_loop_task_runner.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/test/aura_test_base.h"
@@ -1993,6 +1994,33 @@
   EXPECT_FALSE(test_api_->CloseMenu());
 }
 
+// Tests that the app list button shows a context menu on right click when
+// touchable app context menus are not enabled.
+TEST_F(ShelfViewTest, AppListButtonShowsContextMenu) {
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  AppListButton* app_list_button = shelf_view_->GetAppListButton();
+  generator->MoveMouseTo(app_list_button->GetBoundsInScreen().CenterPoint());
+  generator->PressRightButton();
+  EXPECT_TRUE(test_api_->CloseMenu());
+}
+
+// Tests that a ShelfButton ink drop highlight is set to ACTIVATED when a menu
+// is shown by mouse.
+TEST_F(ShelfViewTest, ShelfButtonShowsInkDropHighlightOnMenuShow) {
+  const ShelfID id_0 = AddApp();
+  ShelfButton* button_0 = GetButtonByID(id_0);
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->MoveMouseTo(button_0->GetBoundsInScreen().CenterPoint());
+  generator->PressRightButton();
+  EXPECT_EQ(views::InkDropState::ACTIVATED,
+            button_0->GetInkDropForTesting()->GetTargetInkDropState());
+
+  // Close the menu, the InkDropState should transition to HIDDEN.
+  EXPECT_TRUE(test_api_->CloseMenu());
+  EXPECT_EQ(views::InkDropState::HIDDEN,
+            button_0->GetInkDropForTesting()->GetTargetInkDropState());
+}
+
 // Tests that ShelfWindowWatcher buttons show a context menu on right click.
 TEST_F(ShelfViewTest, ShelfWindowWatcherButtonShowsContextMenu) {
   ui::test::EventGenerator* generator = GetEventGenerator();
@@ -2740,10 +2768,7 @@
                                mouse_location, ui::EventTimeForNow(),
                                ui::EF_LEFT_MOUSE_BUTTON, 0);
   button->OnMouseReleased(release_event);
-  EXPECT_EQ(views::InkDropState::ACTIVATED,
-            browser_button_ink_drop_->GetTargetInkDropState());
-  EXPECT_TRUE(test_api_->CloseMenu());
-
+  test_api_->CloseMenu();
   EXPECT_EQ(views::InkDropState::HIDDEN,
             browser_button_ink_drop_->GetTargetInkDropState());
   EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
@@ -2910,9 +2935,14 @@
   ASSERT_TRUE(test_api_->IsShowingOverflowBubble());
 }
 
+#if defined(OS_LINUX)
+#define MAYBE_MouseContextMenu DISABLED_MouseContextMenu
+#else
+#define MAYBE_MouseContextMenu MouseContextMenu
+#endif
 // Tests ink drop state transitions for the overflow button when the user
 // right clicks on the button to show the context menu.
-TEST_F(OverflowButtonInkDropTest, MouseContextMenu) {
+TEST_F(OverflowButtonInkDropTest, MAYBE_MouseContextMenu) {
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->MoveMouseTo(GetScreenPointInsideOverflowButton());
 
@@ -3221,9 +3251,14 @@
   EXPECT_FALSE(test_api_->IsShowingOverflowBubble());
 }
 
+#if defined(OS_LINUX)
+#define MAYBE_MouseContextMenu DISABLED_MouseContextMenu
+#else
+#define MAYBE_MouseContextMenu MouseContextMenu
+#endif
 // Tests ink drop state transitions for the overflow button when it is active
 // and the user right clicks on the button to show the context menu.
-TEST_F(OverflowButtonActiveInkDropTest, MouseContextMenu) {
+TEST_F(OverflowButtonActiveInkDropTest, MAYBE_MouseContextMenu) {
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->MoveMouseTo(GetScreenPointInsideOverflowButton());
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 272a365..5ea64942 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -910,6 +910,7 @@
     "task/task_traits.cc",
     "task/task_traits.h",
     "task/task_traits_details.h",
+    "task/task_traits_extension.h",
     "task_runner.cc",
     "task_runner.h",
     "task_runner_util.h",
@@ -2186,6 +2187,17 @@
   }
 }
 
+source_set("base_unittests_tasktraits") {
+  testonly = true
+  sources = [
+    "task/test_task_traits_extension.cc",
+    "task/test_task_traits_extension.h",
+  ]
+  deps = [
+    ":base",
+  ]
+}
+
 test("base_unittests") {
   sources = [
     "allocator/allocator_interception_mac_unittest.mm",
@@ -2430,6 +2442,7 @@
     "task/task_scheduler/test_utils.cc",
     "task/task_scheduler/test_utils.h",
     "task/task_scheduler/tracked_ref_unittest.cc",
+    "task/task_traits_extension_unittest.cc",
     "task/task_traits_unittest.cc",
     "task_runner_util_unittest.cc",
     "template_util_unittest.cc",
@@ -2530,6 +2543,7 @@
 
   deps = [
     ":base",
+    ":base_unittests_tasktraits",
     ":i18n",
     "//base/allocator:buildflags",
     "//base/test:native_library_test_utils",
@@ -2777,12 +2791,14 @@
       "metrics/histogram_unittest.nc",
       "optional_unittest.nc",
       "strings/string16_unittest.nc",
+      "task/task_traits_extension_unittest.nc",
       "task/task_traits_unittest.nc",
       "thread_annotations_unittest.nc",
     ]
 
     deps = [
       ":base",
+      ":base_unittests_tasktraits",
       "//base/test:run_all_unittests",
       "//testing/gtest",
     ]
diff --git a/base/android/proguard/enable_obfuscation.flags b/base/android/proguard/enable_obfuscation.flags
index 11bc240f..030d77f1 100644
--- a/base/android/proguard/enable_obfuscation.flags
+++ b/base/android/proguard/enable_obfuscation.flags
@@ -5,4 +5,4 @@
 # As of August 11, 2016, obfuscation was found to save 660kb on our .dex size
 # and 53kb memory/process (through shrinking method/string counts).
 -renamesourcefileattribute PG
--repackageclasses ""
+-repackageclasses ''
diff --git a/base/message_loop/message_pump_mac.h b/base/message_loop/message_pump_mac.h
index 61079cd..fa88c3a 100644
--- a/base/message_loop/message_pump_mac.h
+++ b/base/message_loop/message_pump_mac.h
@@ -80,15 +80,6 @@
 
 class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
  public:
-  // Mask that determines which modes to use. Exposed for tests.
-  enum { kCommonModeMask = 0x1, kAllModesMask = 0x1f };
-
-  // Modes to use for MessagePumpNSApplication that are considered "safe".
-  // Currently just common and exclusive modes. Ideally, messages would be
-  // pumped in all modes, but that interacts badly with app modal dialogs (e.g.
-  // NSAlert). Exposed for tests.
-  enum { kNSApplicationModalSafeModeMask = 0x13 };
-
   // MessagePump:
   void Run(Delegate* delegate) override;
   void ScheduleWork() override;
@@ -137,7 +128,7 @@
   class ScopedModeEnabler;
 
   // The maximum number of run loop modes that can be monitored.
-  static constexpr int kNumModes = 5;
+  static constexpr int kNumModes = 4;
 
   // Marking timers as invalid at the right time helps significantly reduce
   // power use (see the comment in RunDelayedWorkTimer()), however there is no
diff --git a/base/message_loop/message_pump_mac.mm b/base/message_loop/message_pump_mac.mm
index 3ef79d6..fb25201 100644
--- a/base/message_loop/message_pump_mac.mm
+++ b/base/message_loop/message_pump_mac.mm
@@ -30,6 +30,14 @@
 
 namespace {
 
+// Mask that determines which modes to use.
+enum { kCommonModeMask = 0x1, kAllModesMask = 0xf };
+
+// Modes to use for MessagePumpNSApplication that are considered "safe".
+// Currently just common and exclusive modes. Ideally, messages would be pumped
+// in all modes, but that interacts badly with app modal dialogs (e.g. NSAlert).
+enum { kNSApplicationModalSafeModeMask = 0x3 };
+
 void NoOp(void* info) {
 }
 
@@ -148,9 +156,6 @@
 
         // Process work when AppKit is highlighting an item on the main menubar.
         CFSTR("NSUnhighlightMenuRunLoopMode"),
-
-        // Process work when AppKit is animating the menu bar.
-        CFSTR("NSAnimationRunLoopMode"),
     };
     static_assert(arraysize(modes) == kNumModes, "mode size mismatch");
     static_assert((1 << kNumModes) - 1 == kAllModesMask,
@@ -757,18 +762,16 @@
 
 ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() {
   DCHECK(g_app_pump);
-  DCHECK_EQ(MessagePumpCFRunLoopBase::kNSApplicationModalSafeModeMask,
-            g_app_pump->GetModeMask());
+  DCHECK_EQ(kNSApplicationModalSafeModeMask, g_app_pump->GetModeMask());
   // Pumping events in private runloop modes is known to interact badly with
   // app modal windows like NSAlert.
   if (![NSApp modalWindow])
-    g_app_pump->SetModeMask(MessagePumpCFRunLoopBase::kAllModesMask);
+    g_app_pump->SetModeMask(kAllModesMask);
 }
 
 ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() {
   DCHECK(g_app_pump);
-  g_app_pump->SetModeMask(
-      MessagePumpCFRunLoopBase::kNSApplicationModalSafeModeMask);
+  g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask);
 }
 
 int ScopedPumpMessagesInPrivateModes::GetModeMaskForTest() {
diff --git a/base/message_loop/message_pump_mac_unittest.mm b/base/message_loop/message_pump_mac_unittest.mm
index c30b2d4..6b63aa1 100644
--- a/base/message_loop/message_pump_mac_unittest.mm
+++ b/base/message_loop/message_pump_mac_unittest.mm
@@ -16,6 +16,14 @@
 - (void)runTestThenCloseAlert:(NSAlert*)alert;
 @end
 
+namespace {
+
+// Internal constants from message_pump_mac.mm.
+constexpr int kAllModesMask = 0xf;
+constexpr int kNSApplicationModalSafeModeMask = 0x3;
+
+}  // namespace
+
 namespace base {
 
 class TestMessagePumpCFRunLoopBase {
@@ -184,8 +192,7 @@
   {
     base::ScopedPumpMessagesInPrivateModes allow_private;
     // No modal window, so all modes should be pumped.
-    EXPECT_EQ(MessagePumpCFRunLoopBase::kAllModesMask,
-              allow_private.GetModeMaskForTest());
+    EXPECT_EQ(kAllModesMask, allow_private.GetModeMaskForTest());
   }
 
   base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
@@ -209,7 +216,7 @@
   {
     base::ScopedPumpMessagesInPrivateModes allow_private;
     // With a modal window, only safe modes should be pumped.
-    EXPECT_EQ(base::MessagePumpCFRunLoopBase::kNSApplicationModalSafeModeMask,
+    EXPECT_EQ(kNSApplicationModalSafeModeMask,
               allow_private.GetModeMaskForTest());
   }
   [[alert buttons][0] performClick:nil];
diff --git a/base/task/task_traits.h b/base/task/task_traits.h
index 7727365..0d5e16f 100644
--- a/base/task/task_traits.h
+++ b/base/task/task_traits.h
@@ -8,10 +8,14 @@
 #include <stdint.h>
 
 #include <iosfwd>
+#include <tuple>
 #include <type_traits>
+#include <utility>
 
 #include "base/base_export.h"
+#include "base/logging.h"
 #include "base/task/task_traits_details.h"
+#include "base/task/task_traits_extension.h"
 #include "build/build_config.h"
 
 namespace base {
@@ -149,33 +153,58 @@
   // constexpr base::TaskTraits other_user_visible_may_block_traits = {
   //     base::MayBlock(), base::TaskPriority::USER_VISIBLE};
   template <class... ArgTypes,
-            class CheckArgumentsAreValid = internal::InitTypes<
-                decltype(ValidTrait(std::declval<ArgTypes>()))...>>
+            class CheckArgumentsAreValidBaseTraits = std::enable_if_t<
+                trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
   constexpr TaskTraits(ArgTypes... args)
       : priority_set_explicitly_(
-            internal::HasArgOfType<TaskPriority, ArgTypes...>::value),
-        priority_(internal::GetValueFromArgList(
-            internal::EnumArgGetter<TaskPriority, TaskPriority::USER_VISIBLE>(),
+            trait_helpers::HasArgOfType<TaskPriority, ArgTypes...>::value),
+        priority_(trait_helpers::GetValueFromArgList(
+            trait_helpers::EnumArgGetter<TaskPriority,
+                                         TaskPriority::USER_VISIBLE>(),
             args...)),
         shutdown_behavior_set_explicitly_(
-            internal::HasArgOfType<TaskShutdownBehavior, ArgTypes...>::value),
-        shutdown_behavior_(internal::GetValueFromArgList(
-            internal::EnumArgGetter<TaskShutdownBehavior,
-                                    TaskShutdownBehavior::SKIP_ON_SHUTDOWN>(),
+            trait_helpers::HasArgOfType<TaskShutdownBehavior,
+                                        ArgTypes...>::value),
+        shutdown_behavior_(trait_helpers::GetValueFromArgList(
+            trait_helpers::EnumArgGetter<
+                TaskShutdownBehavior,
+                TaskShutdownBehavior::SKIP_ON_SHUTDOWN>(),
             args...)),
-        may_block_(internal::GetValueFromArgList(
-            internal::BooleanArgGetter<MayBlock>(),
+        may_block_(trait_helpers::GetValueFromArgList(
+            trait_helpers::BooleanArgGetter<MayBlock>(),
             args...)),
-        with_base_sync_primitives_(internal::GetValueFromArgList(
-            internal::BooleanArgGetter<WithBaseSyncPrimitives>(),
+        with_base_sync_primitives_(trait_helpers::GetValueFromArgList(
+            trait_helpers::BooleanArgGetter<WithBaseSyncPrimitives>(),
             args...)) {}
 
+  // Construct TaskTraits with extension traits. See task_traits_extension.h.
+  template <class... ArgTypes,
+            class AvoidConstructorRedeclaration = void,
+            class CheckArgsContainNonBaseTrait = std::enable_if_t<
+                !trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
+  constexpr TaskTraits(ArgTypes... args)
+      // Select those arguments that are valid base TaskTraits and forward them
+      // to the above constructor via a helper constructor.
+      : TaskTraits(std::forward_as_tuple(args...),
+                   trait_helpers::SelectIndices<
+                       trait_helpers::ValidTraitTester<ValidTrait>::IsValid,
+                       ArgTypes...>{}) {
+    // Select all other arguments and try to create an extension with them.
+    extension_ = MakeTaskTraitsExtensionHelper(
+        std::forward_as_tuple(args...),
+        trait_helpers::SelectIndices<
+            trait_helpers::ValidTraitTester<ValidTrait>::IsInvalid,
+            ArgTypes...>{});
+  }
+
   constexpr TaskTraits(const TaskTraits& other) = default;
   TaskTraits& operator=(const TaskTraits& other) = default;
 
   // Returns TaskTraits constructed by combining |left| and |right|. If a trait
   // is specified in both |left| and |right|, the returned TaskTraits will have
-  // the value from |right|.
+  // the value from |right|. Note that extension traits are not merged: any
+  // extension traits in |left| are discarded if extension traits are present in
+  // |right|.
   static constexpr TaskTraits Override(const TaskTraits& left,
                                        const TaskTraits& right) {
     return TaskTraits(left, right);
@@ -207,6 +236,16 @@
     return with_base_sync_primitives_;
   }
 
+  uint8_t extension_id() const { return extension_.extension_id; }
+
+  // Access the extension data by parsing it into the provided extension type.
+  // See task_traits_extension.h for requirements on the extension type.
+  template <class TaskTraitsExtension>
+  const TaskTraitsExtension GetExtension() const {
+    DCHECK_EQ(TaskTraitsExtension::kExtensionId, extension_.extension_id);
+    return TaskTraitsExtension::Parse(extension_);
+  }
+
  private:
   constexpr TaskTraits(const TaskTraits& left, const TaskTraits& right)
       : priority_set_explicitly_(left.priority_set_explicitly_ ||
@@ -221,7 +260,21 @@
                                : left.shutdown_behavior_),
         may_block_(left.may_block_ || right.may_block_),
         with_base_sync_primitives_(left.with_base_sync_primitives_ ||
-                                   right.with_base_sync_primitives_) {}
+                                   right.with_base_sync_primitives_),
+        extension_(right.extension_.extension_id !=
+                           TaskTraitsExtensionStorage::kInvalidExtensionId
+                       ? right.extension_
+                       : left.extension_) {}
+
+  // Helper constructor which selects those arguments from |args| that are
+  // indicated by the index_sequence and forwards them to the public
+  // constructor. Due to filtering, the indices may be non-contiguous.
+  template <class... ArgTypes, std::size_t... Indices>
+  constexpr TaskTraits(std::tuple<ArgTypes...> args,
+                       std::index_sequence<Indices...>)
+      : TaskTraits(
+            std::get<Indices>(std::forward<std::tuple<ArgTypes...>>(args))...) {
+  }
 
   bool priority_set_explicitly_;
   TaskPriority priority_;
@@ -229,6 +282,8 @@
   TaskShutdownBehavior shutdown_behavior_;
   bool may_block_;
   bool with_base_sync_primitives_;
+
+  TaskTraitsExtensionStorage extension_;
 };
 
 // Returns string literals for the enums defined in this file. These methods
diff --git a/base/task/task_traits_details.h b/base/task/task_traits_details.h
index 4d18071..11001e7 100644
--- a/base/task/task_traits_details.h
+++ b/base/task/task_traits_details.h
@@ -5,11 +5,12 @@
 #ifndef BASE_TASK_TASK_TRAITS_DETAILS_H_
 #define BASE_TASK_TASK_TRAITS_DETAILS_H_
 
+#include <tuple>
 #include <type_traits>
 #include <utility>
 
 namespace base {
-namespace internal {
+namespace trait_helpers {
 
 // HasArgOfType<CheckedType, ArgTypes...>::value is true iff a type in ArgTypes
 // matches CheckedType.
@@ -116,13 +117,89 @@
   constexpr ValueType GetDefaultValue() const { return DefaultValue; }
 };
 
-// Allows instantiation of multiple types in one statement. Used to prevent
-// instantiation of the constructor of TaskTraits with inappropriate argument
-// types.
-template <class...>
-struct InitTypes {};
+// Tests whether a given trait type is valid or invalid by testing whether it is
+// convertible to the provided ValidTraits type. To use, define a ValidTraits
+// type like this:
+//
+// struct ValidTraits {
+//   ValidTraits(MyTrait) {}
+//   ...
+// };
+template <class ValidTraits>
+struct ValidTraitTester {
+  template <class TraitType>
+  struct IsValid : std::is_convertible<TraitType, ValidTraits> {};
 
-}  // namespace internal
+  template <class TraitType>
+  struct IsInvalid
+      : std::conditional_t<std::is_convertible<TraitType, ValidTraits>::value,
+                           std::false_type,
+                           std::true_type> {};
+};
+
+// Tests if a given trait type is valid according to the provided ValidTraits.
+template <class ValidTraits, class TraitType>
+struct IsValidTrait
+    : ValidTraitTester<ValidTraits>::template IsValid<TraitType> {};
+
+// Tests whether multiple given traits types are all valid according to the
+// provided ValidTraits.
+template <class ValidTraits, class...>
+struct AreValidTraits : std::true_type {};
+
+template <class ValidTraits, class NextType, class... Rest>
+struct AreValidTraits<ValidTraits, NextType, Rest...>
+    : std::conditional<IsValidTrait<ValidTraits, NextType>::value,
+                       AreValidTraits<ValidTraits, Rest...>,
+                       std::false_type>::type {};
+
+// Helper struct that recursively builds up an index_sequence containing all
+// those indexes of elements in Args for which the |Predicate<Arg>::value| is
+// true.
+template <template <class> class Predicate,
+          std::size_t CurrentIndex,
+          class Output,
+          class... Args>
+struct SelectIndicesHelper;
+
+template <template <class> class Predicate,
+          std::size_t CurrentIndex,
+          std::size_t... Indices,
+          class First,
+          class... Rest>
+struct SelectIndicesHelper<Predicate,
+                           CurrentIndex,
+                           std::index_sequence<Indices...>,
+                           First,
+                           Rest...>
+    : std::conditional_t<
+          Predicate<First>::value,
+          // Push the index into the sequence and recurse.
+          SelectIndicesHelper<Predicate,
+                              CurrentIndex + 1,
+                              std::index_sequence<Indices..., CurrentIndex>,
+                              Rest...>,
+          // Skip the index and recurse.
+          SelectIndicesHelper<Predicate,
+                              CurrentIndex + 1,
+                              std::index_sequence<Indices...>,
+                              Rest...>> {};
+
+template <template <class> class Predicate,
+          std::size_t CurrentIndex,
+          class Sequence>
+struct SelectIndicesHelper<Predicate, CurrentIndex, Sequence> {
+  using type = Sequence;
+};
+
+// Selects the indices of elements in the |Args| list for which
+// |Predicate<Arg>::value| is |true|.
+template <template <class> class Predicate, class... Args>
+using SelectIndices =
+    typename SelectIndicesHelper<Predicate, 0, std::index_sequence<>, Args...>::
+        type;
+
+}  // namespace trait_helpers
 }  // namespace base
 
 #endif  // BASE_TASK_TASK_TRAITS_DETAILS_H_
diff --git a/base/task/task_traits_extension.h b/base/task/task_traits_extension.h
new file mode 100644
index 0000000..2147cd5c
--- /dev/null
+++ b/base/task/task_traits_extension.h
@@ -0,0 +1,192 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_TASK_TRAITS_EXTENSION_H_
+#define BASE_TASK_TASK_TRAITS_EXTENSION_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <tuple>
+#include <utility>
+
+#include "base/base_export.h"
+
+namespace base {
+
+// Embedders can attach additional traits to a TaskTraits object in a way that
+// is opaque to base. These extension traits can then be specified along the
+// base traits when constructing the TaskTraits object. They are then stored and
+// propagated with the TaskTraits object.
+//
+// To support constexpr-compatible construction, extension traits are stored in
+// a fixed-size byte array in the TaskTraits object and serialized into and
+// parsed from this storage by an embedder-provided extension class and
+// MakeTaskTraitsExtension() template function. The embedder can later access
+// the extension traits via TaskTraits::GetExtension<[ExtensionClass]>().
+//
+// A TaskTraits extension class needs to specify publicly:
+//  (1) -- static constexpr uint8_t kExtensionId.
+//      This field's value identifies the type of the extension uniquely within
+//      each process. The embedder is responsible for ensuring uniqueness and
+//      can assign values between kFirstEmbedderExtensionId and kMaxExtensionId
+//      of TaskTraitsExtensionStorage::ExtensionId.
+//  (2) -- static const [ExtensionClass] Parse(
+//      --     const base::TaskTraitsExtensionStorage& extension).
+//      Parses and constructs an extension object from the provided storage.
+//
+// For each TaskTraits extension class, the embedder has to provide a
+// corresponding MakeTaskTraitsExtension definition inside the same namespace
+// as its extension traits:
+//  (3) -- template <...>
+//      -- constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension(
+//      --     ArgTypes&&... args).
+//      Constructs and serializes an extension with the given arguments into
+//      a TaskTraitsExtensionStorage and returns it. Should only accept valid
+//      arguments for the extension.
+//
+// EXAMPLE (see also base/task/test_task_traits_extension.h):
+// --------
+//
+// namespace my_embedder {
+// enum class MyExtensionTrait {kMyValue1, kMyValue2};
+//
+// class MyTaskTraitsExtension {
+//  public:
+//   static constexpr uint8_t kExtensionId =
+//       TaskTraitsExtensionStorage::kFirstEmbedderExtensionId;
+//
+//   struct ValidTrait {
+//     ValidTrait(MyExtensionTrait) {}
+//   };
+//
+//   // Constructor that accepts only valid traits as specified by ValidTraits.
+//   template <class... ArgTypes,
+//             class CheckArgumentsAreValid = std::enable_if_t<
+//                 base::trait_helpers::AreValidTraits<
+//                     ValidTrait, ArgTypes...>::value>>
+//   constexpr MyTaskTraitsExtension(ArgTypes... args)
+//       : my_trait_(base::trait_helpers::GetValueFromArgList(
+//             base::trait_helpers::EnumArgGetter<MyExtensionTrait,
+//                                                MyExtensionTrait::kValue1>(),
+//             args...)) {}
+//
+//   // Serializes MyTaskTraitsExtension into a storage object and returns it.
+//   constexpr base::TaskTraitsExtensionStorage Serialize() const {
+//     // Note: can't use reinterpret_cast or placement new because neither are
+//     // constexpr-compatible.
+//     return {kExtensionId, {{static_cast<uint8_t>(my_trait_)}}};
+//   }
+//
+//   // Creates a MyTaskTraitsExtension by parsing it from a storage object.
+//   static const MyTaskTraitsExtension Parse(
+//       const base::TaskTraitsExtensionStorage& extension) {
+//     return MyTaskTraitsExtension(
+//         static_cast<MyExtensionTrait>(extension.data[0]));
+//   }
+//
+//   constexpr MyExtensionTrait my_trait() const { return my_trait_; }
+//
+//  private:
+//   MyExtensionTrait my_trait_;
+// };
+//
+// // Creates a MyTaskTraitsExtension for the provided |args| and serializes it
+// // into |extension|. Accepts only valid arguments for the
+// // MyTaskTraitsExtension() constructor.
+// template <class... ArgTypes,
+//           class = std::enable_if_t<
+//               base::trait_helpers::AreValidTraits<
+//                   MyTaskTraitsExtension::ValidTrait, ArgTypes...>::value>>
+// constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension(
+//     ArgTypes&&... args) {
+//   return MyTaskTraitsExtension(std::forward<ArgTypes>(args)...).Serialize();
+// }
+// }  // namespace my_embedder
+//
+// // Construction of TaskTraits with extension traits.
+// constexpr TaskTraits t1 = {my_embedder::MyExtensionTrait::kValueB};
+// constexpr TaskTraits t2 = {base::MayBlock(),
+//                            my_embedder::MyExtensionTrait::kValueA};
+//
+// // Extension traits can also be specified directly when posting a task.
+// base::PostTaskWithTraits(FROM_HERE,
+//                          {my_embedder::MyExtensionTrait::kValueB},
+//                          base::BindOnce(...));
+
+// Stores extension traits opaquely inside a fixed-size data array. We store
+// this data directly (rather than in a separate object on the heap) to support
+// constexpr-compatible TaskTraits construction.
+struct BASE_EXPORT TaskTraitsExtensionStorage {
+  static constexpr size_t kStorageSize = 8;  // bytes
+
+  inline constexpr TaskTraitsExtensionStorage();
+  inline constexpr TaskTraitsExtensionStorage(
+      uint8_t extension_id_in,
+      const std::array<uint8_t, kStorageSize>& data_in);
+  inline constexpr TaskTraitsExtensionStorage(
+      uint8_t extension_id_in,
+      std::array<uint8_t, kStorageSize>&& data_in);
+
+  inline constexpr TaskTraitsExtensionStorage(
+      const TaskTraitsExtensionStorage& other);
+  inline TaskTraitsExtensionStorage& operator=(
+      const TaskTraitsExtensionStorage& other) = default;
+
+  enum ExtensionId : uint8_t {
+    kInvalidExtensionId = 0,
+    // The embedder is responsible for assigning the remaining values uniquely.
+    kFirstEmbedderExtensionId = 1,
+    kMaxExtensionId = 255
+  };
+
+  // Identifies the type of extension. See ExtensionId enum above.
+  uint8_t extension_id;
+
+  // Serialized extension data.
+  std::array<uint8_t, kStorageSize> data;
+};
+
+// TODO(https://crbug.com/874482): These constructors need to be "inline" but
+// defined outside the class above, because the chromium-style clang plugin
+// doesn't exempt constexpr constructors at the moment.
+inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage()
+    : extension_id(kInvalidExtensionId), data{} {}
+
+inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage(
+    uint8_t extension_id_in,
+    const std::array<uint8_t, kStorageSize>& data_in)
+    : extension_id(extension_id_in), data(data_in) {}
+
+inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage(
+    uint8_t extension_id_in,
+    std::array<uint8_t, kStorageSize>&& data_in)
+    : extension_id(extension_id_in), data(std::move(data_in)) {}
+
+inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage(
+    const TaskTraitsExtensionStorage& other) = default;
+
+// Default implementation of MakeTaskTraitsExtension template function, which
+// doesn't accept any traits and does not create an extension.
+template <class Unused = void>
+constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtension(
+    TaskTraitsExtensionStorage& storage) {
+  return TaskTraitsExtensionStorage();
+}
+
+// Forwards those arguments from |args| that are indicated by the index_sequence
+// to a MakeTaskTraitsExtension() template function, which is provided by the
+// embedder in an unknown namespace; its resolution relies on argument-dependent
+// lookup. Due to filtering, the provided indices may be non-contiguous.
+template <class... ArgTypes, std::size_t... Indices>
+constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtensionHelper(
+    std::tuple<ArgTypes...> args,
+    std::index_sequence<Indices...>) {
+  return MakeTaskTraitsExtension(
+      std::get<Indices>(std::forward<std::tuple<ArgTypes...>>(args))...);
+}
+
+}  // namespace base
+
+#endif  // BASE_TASK_TASK_TRAITS_EXTENSION_H_
diff --git a/base/task/task_traits_extension_unittest.cc b/base/task/task_traits_extension_unittest.cc
new file mode 100644
index 0000000..39033aa
--- /dev/null
+++ b/base/task/task_traits_extension_unittest.cc
@@ -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.
+
+#include "base/task/task_traits.h"
+
+#include "base/task/test_task_traits_extension.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(TaskTraitsExtensionTest, CreateWithOneExtensionTrait) {
+  constexpr TaskTraits traits = {TestExtensionEnumTrait::kB};
+
+  EXPECT_EQ(traits.GetExtension<TestTaskTraitsExtension>().enum_trait(),
+            TestExtensionEnumTrait::kB);
+  EXPECT_FALSE(traits.GetExtension<TestTaskTraitsExtension>().bool_trait());
+}
+
+TEST(TaskTraitsExtensionTest, CreateWithMultipleExtensionTraits) {
+  constexpr TaskTraits traits = {TestExtensionEnumTrait::kB,
+                                 TestExtensionBoolTrait()};
+
+  EXPECT_EQ(traits.GetExtension<TestTaskTraitsExtension>().enum_trait(),
+            TestExtensionEnumTrait::kB);
+  EXPECT_TRUE(traits.GetExtension<TestTaskTraitsExtension>().bool_trait());
+}
+
+TEST(TaskTraitsExtensionTest, CreateWithBaseAndExtensionTraits) {
+  constexpr TaskTraits traits = {TaskPriority::USER_BLOCKING,
+                                 TestExtensionEnumTrait::kC,
+                                 TestExtensionBoolTrait()};
+
+  EXPECT_EQ(traits.priority(), TaskPriority::USER_BLOCKING);
+  EXPECT_EQ(traits.GetExtension<TestTaskTraitsExtension>().enum_trait(),
+            TestExtensionEnumTrait::kC);
+  EXPECT_TRUE(traits.GetExtension<TestTaskTraitsExtension>().bool_trait());
+}
+
+}  // namespace base
diff --git a/base/task/task_traits_extension_unittest.nc b/base/task/task_traits_extension_unittest.nc
new file mode 100644
index 0000000..aa74c33
--- /dev/null
+++ b/base/task/task_traits_extension_unittest.nc
@@ -0,0 +1,28 @@
+// 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.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/task/task_traits.h"
+
+#include "base/task/test_task_traits_extension.h"
+
+namespace base {
+
+#if defined(NCTEST_TASK_TRAITS_EXTENSION_MULTIPLE_BASE_TRAITS)  // [r"Multiple arguments of the same type were provided to the constructor of TaskTraits."]
+constexpr TaskTraits traits = {MayBlock(), MayBlock()};
+#elif defined(NCTEST_TASK_TRAITS_EXTENSION_MULTIPLE_EXTENSION_TRAITS)  // [r"Multiple arguments of the same type were provided to the constructor of TaskTraits."]
+constexpr TaskTraits traits = {TestExtensionEnumTrait::kB, TestExtensionEnumTrait::kC};
+#elif defined(NCTEST_TASK_TRAITS_EXTENSION_INVALID_TYPE)  // [r"no matching function for call to 'MakeTaskTraitsExtension'"]
+constexpr TaskTraits traits = {TestExtensionEnumTrait::kB, 123};
+#elif defined(NCTEST_TASK_TRAITS_EXTENSION_TOO_MUCH_DATA_FOR_STORAGE)  // [r"no matching constructor for initialization of 'base::TaskTraitsExtensionStorage'"]
+constexpr TaskTraitsExtensionStorage TestSerializeTaskTraitsWithTooMuchData() {
+  constexpr std::array<uint8_t, TaskTraitsExtensionStorage::kStorageSize + 1>
+      data = {};
+  return {TaskTraitsExtensionStorage::kFirstEmbedderExtensionId, data};
+}
+#endif
+
+}  // namespace base
diff --git a/base/task/task_traits_unittest.nc b/base/task/task_traits_unittest.nc
index aa13b54..8eaafa2 100644
--- a/base/task/task_traits_unittest.nc
+++ b/base/task/task_traits_unittest.nc
@@ -24,7 +24,7 @@
 constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN,
                                MayBlock(),
                                TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
-#elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE)  // [r"no matching constructor for initialization of 'const base::TaskTraits'"]
+#elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE)  // [r"no matching function for call to 'MakeTaskTraitsExtension'"]
 constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, true};
 #endif
 
diff --git a/base/task/test_task_traits_extension.cc b/base/task/test_task_traits_extension.cc
new file mode 100644
index 0000000..7a704bc
--- /dev/null
+++ b/base/task/test_task_traits_extension.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task/test_task_traits_extension.h"
+
+namespace base {
+
+// static
+constexpr uint8_t TestTaskTraitsExtension::kExtensionId;
+
+}  // namespace base
diff --git a/base/task/test_task_traits_extension.h b/base/task/test_task_traits_extension.h
new file mode 100644
index 0000000..ec12361
--- /dev/null
+++ b/base/task/test_task_traits_extension.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_TEST_TASK_TRAITS_EXTENSION_H_
+#define BASE_TASK_TEST_TASK_TRAITS_EXTENSION_H_
+
+#include <utility>
+
+#include "base/task/task_traits.h"
+
+namespace base {
+
+enum class TestExtensionEnumTrait { kA, kB, kC };
+struct TestExtensionBoolTrait {};
+
+// Example TaskTraits extension for use in tests.
+class TestTaskTraitsExtension {
+ public:
+  static constexpr uint8_t kExtensionId =
+      TaskTraitsExtensionStorage::kFirstEmbedderExtensionId;
+
+  struct ValidTrait {
+    ValidTrait(TestExtensionEnumTrait) {}
+    ValidTrait(TestExtensionBoolTrait) {}
+  };
+
+  template <class... ArgTypes,
+            class CheckArgumentsAreValid = std::enable_if_t<
+                trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
+  constexpr TestTaskTraitsExtension(ArgTypes... args)
+      : enum_trait_(trait_helpers::GetValueFromArgList(
+            trait_helpers::EnumArgGetter<TestExtensionEnumTrait,
+                                         TestExtensionEnumTrait::kA>(),
+            args...)),
+        bool_trait_(trait_helpers::GetValueFromArgList(
+            trait_helpers::BooleanArgGetter<TestExtensionBoolTrait>(),
+            args...)) {}
+
+  constexpr TaskTraitsExtensionStorage Serialize() const {
+    return {kExtensionId, {{static_cast<uint8_t>(enum_trait_), bool_trait_}}};
+  }
+
+  static const TestTaskTraitsExtension Parse(
+      const TaskTraitsExtensionStorage& extension) {
+    if (extension.data[1]) {
+      return TestTaskTraitsExtension(
+          static_cast<TestExtensionEnumTrait>(extension.data[0]),
+          TestExtensionBoolTrait());
+    } else {
+      return TestTaskTraitsExtension(
+          static_cast<TestExtensionEnumTrait>(extension.data[0]));
+    }
+  }
+
+  constexpr TestExtensionEnumTrait enum_trait() const { return enum_trait_; }
+  constexpr bool bool_trait() const { return bool_trait_; }
+
+ private:
+  TestExtensionEnumTrait enum_trait_;
+  bool bool_trait_;
+};
+
+template <class... ArgTypes,
+          class = std::enable_if_t<
+              trait_helpers::AreValidTraits<TestTaskTraitsExtension::ValidTrait,
+                                            ArgTypes...>::value>>
+constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtension(
+    ArgTypes&&... args) {
+  return TestTaskTraitsExtension(std::forward<ArgTypes>(args)...).Serialize();
+}
+
+}  // namespace base
+
+#endif  // BASE_TASK_TEST_TASK_TRAITS_EXTENSION_H_
diff --git a/base/time/time.h b/base/time/time.h
index 344178a..f4c2f93f 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -106,14 +106,6 @@
 
 // TimeDelta ------------------------------------------------------------------
 
-// Represents a duration of time. Both positive and negative durations are
-// allowed; the latter being a duration whose start time is after its end time.
-//
-// When dealing with a value outside the representable range, TimeDelta will
-// saturate to infinity (both positive and negative). Once saturated,
-// mathematical operations on the TimeDelta will follow standard infinity
-// mathematics (e.g. subtracting a finite value from TimeDelta::Max() will just
-// result in TimeDelta::Max()).
 class BASE_EXPORT TimeDelta {
  public:
   constexpr TimeDelta() : delta_(0) {}
@@ -148,17 +140,18 @@
   }
 
   // Returns the maximum time delta, which should be greater than any reasonable
-  // time delta we might compare it to.
+  // time delta we might compare it to. Adding or subtracting the maximum time
+  // delta to a time or another time delta has an undefined result.
   static constexpr TimeDelta Max();
 
   // Returns the minimum time delta, which should be less than than any
-  // reasonable time delta we might compare it to.
+  // reasonable time delta we might compare it to. Adding or subtracting the
+  // minimum time delta to a time or another time delta has an undefined result.
   static constexpr TimeDelta Min();
 
   // Returns the internal numeric value of the TimeDelta object. Please don't
   // use this and do arithmetic on it, as it is more error prone than using the
-  // provided operators. In particular, it does not preserve TimeDelta's
-  // saturation mathematics.
+  // provided operators.
   // For serializing, use FromInternalValue to reconstitute.
   //
   // DEPRECATED - Do not use in new code. http://crbug.com/634507
@@ -166,8 +159,6 @@
 
   // Returns the magnitude (absolute value) of this TimeDelta.
   constexpr TimeDelta magnitude() const {
-    if (is_min())
-      return Max();
     // Some toolchains provide an incomplete C++11 implementation and lack an
     // int64_t overload for std::abs().  The following is a simple branchless
     // implementation:
@@ -218,39 +209,9 @@
   // __builtin_(add|sub)_overflow in safe_math_clang_gcc_impl.h :
   // https://chromium-review.googlesource.com/c/chromium/src/+/873352#message-59594ab70827795a67e0780404adf37b4b6c2f14
   TimeDelta operator+(TimeDelta other) const {
-    // Cannot add positive/negative infinity together.
-    DCHECK(!(is_max() && other.is_min()) && !(is_min() && other.is_max()));
-
-    // Anything + positive-infinity == positive-infinity.
-    if (is_max() || other.is_max())
-      return Max();
-
-    // Anything + negative-infinity == negative-infinity.
-    if (is_min() || other.is_min())
-      return Min();
-
-    // In the finite case, SaturatedAdd gives the behavior we want (including
-    // overflow protection).
     return TimeDelta(time_internal::SaturatedAdd(*this, other.delta_));
   }
-
   TimeDelta operator-(TimeDelta other) const {
-    // Infinities can be subtracted from one another, but only if they have
-    // opposing signs (e.g. infinity - infinity doesn't make sense.)
-    DCHECK(!(is_max() && other.is_max()) && !(is_min() && other.is_min()));
-
-    // positive-infinity - anything == positive infinity, and
-    // anything - negative-infinity == positive infinity.
-    if (is_max() || other.is_min())
-      return Max();
-
-    // negative-infinity - anything == negative infinity, and
-    // anything - positive-infinity == negative infinity.
-    if (is_min() || other.is_max())
-      return Min();
-
-    // In the finite case, SaturatedSub gives the behavior we want (including
-    // overflow protection).
     return TimeDelta(time_internal::SaturatedSub(*this, other.delta_));
   }
 
@@ -260,55 +221,33 @@
   TimeDelta& operator-=(TimeDelta other) {
     return *this = (*this - other);
   }
-
-  constexpr TimeDelta operator-() const {
-    if (is_max())
-      return Min();
-    if (is_min())
-      return Max();
-    return TimeDelta(-delta_);
-  }
+  constexpr TimeDelta operator-() const { return TimeDelta(-delta_); }
 
   // Computations with numeric types. operator*() isn't constexpr because of a
   // limitation around __builtin_mul_overflow (but operator/(1.0/a) works for
   // |a|'s of "reasonable" size -- i.e. that don't risk overflow).
   template <typename T>
   TimeDelta operator*(T a) const {
-    // Multiplying infinity by 0 is undefined.
-    DCHECK(a != 0 || (!is_max() && !is_min()));
-
-    // For the infinity cases: if both operands are positive or both are
-    // negative then the result is positive, otherwise the result is negative.
-    // In math, that's XOR.
-    if (is_max() || is_min())
-      return (delta_ > 0) ^ (a > 0) ? Min() : Max();
-
-    // For the non-infinity cases, we need to take care to avoid overflow.
     CheckedNumeric<int64_t> rv(delta_);
     rv *= a;
     if (rv.IsValid())
       return TimeDelta(rv.ValueOrDie());
-    // We know this overflows to positive or negative infinity, so the logic is
-    // the same as above; both operands positive or both negative results in
-    // positive infinity, otherwise negative infinity.
-    return (delta_ > 0) ^ (a > 0) ? Min() : Max();
+    // Matched sign overflows. Mismatched sign underflows.
+    if ((delta_ < 0) ^ (a < 0))
+      return TimeDelta(std::numeric_limits<int64_t>::min());
+    return TimeDelta(std::numeric_limits<int64_t>::max());
   }
   template <typename T>
   constexpr TimeDelta operator/(T a) const {
-    // For the infinity cases: if both operands are positive or both are
-    // negative then the result is positive, otherwise the result is negative.
-    // In math, that's XOR.
-    if (is_max() || is_min())
-      return (delta_ > 0) ^ (a >= 0) ? Min() : Max();
-
     CheckedNumeric<int64_t> rv(delta_);
     rv /= a;
     if (rv.IsValid())
       return TimeDelta(rv.ValueOrDie());
-    // We know this overflows to positive or negative infinity, so the logic is
-    // the same as above; both operands positive or both negative results in
-    // positive infinity, otherwise negative infinity.
-    return (delta_ > 0) ^ (a >= 0) ? Min() : Max();
+    // Matched sign overflows. Mismatched sign underflows.
+    // Special case to catch divide by zero.
+    if ((delta_ < 0) ^ (a <= 0))
+      return TimeDelta(std::numeric_limits<int64_t>::min());
+    return TimeDelta(std::numeric_limits<int64_t>::max());
   }
   template <typename T>
   TimeDelta& operator*=(T a) {
@@ -319,33 +258,8 @@
     return *this = (*this / a);
   }
 
-  constexpr int64_t operator/(TimeDelta a) const {
-    // Dividing infinity by infinity (either positive or negative) is undefined.
-    DCHECK((!is_max() && !is_min()) || (!a.is_max() && !a.is_min()));
-
-    // For an infinity numerator: if both operands are positive or both are
-    // negative then the result is positive, otherwise the result is negative.
-    // In math, that's XOR.
-    if (is_max() || is_min()) {
-      return (delta_ > 0) ^ (a.delta_ >= 0)
-                 ? std::numeric_limits<int64_t>::min()
-                 : std::numeric_limits<int64_t>::max();
-    }
-
-    // For an infinity denominator: its 0.
-    if (a.is_max() || a.is_min())
-      return 0;
-
-    return delta_ / a.delta_;
-  }
-
+  constexpr int64_t operator/(TimeDelta a) const { return delta_ / a.delta_; }
   constexpr TimeDelta operator%(TimeDelta a) const {
-    // infinity modulo x is not defined.
-    DCHECK(!is_max() && !is_min());
-
-    // This works for the infinite cases because our values for 'infinity' are
-    // the largest/smallest values possible for an int64_t, and therefore (x %
-    // infinity == x) will hold.
     return TimeDelta(delta_ % a.delta_);
   }
 
@@ -482,8 +396,6 @@
   }
 
   // Return a new time modified by some delta.
-  //
-  // Warning: Adding or subtracting a saturated TimeDelta is undefined.
   TimeClass operator+(TimeDelta delta) const {
     return TimeClass(time_internal::SaturatedAdd(delta, us_));
   }
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index f28fe74..5dc8888 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -1257,9 +1257,6 @@
   static_assert(TimeDelta::FromMicroseconds(max_int64_minus_one) ==
                     TimeDelta::FromMicroseconds(min_int64_plus_two).magnitude(),
                 "");
-
-  static_assert(TimeDelta::Max() == TimeDelta::Max().magnitude(), "");
-  static_assert(TimeDelta::Max() == TimeDelta::Min().magnitude(), "");
 }
 
 TEST(TimeDelta, ZeroMinMax) {
@@ -1452,104 +1449,6 @@
             (2 * TimeDelta::FromMilliseconds(1000)));
 }
 
-TEST(TimeDelta, NumericOperatorsSaturated) {
-  // Anything + positive-infinity == positive-infinity.
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Max() + TimeDelta::Max());
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Max() + TimeDelta::FromSeconds(10));
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Max() + TimeDelta::FromSeconds(-10));
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::FromSeconds(10) + TimeDelta::Max());
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::FromSeconds(-10) + TimeDelta::Max());
-
-  // Anything + negative-infinity == negative-infinity.
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Min() + TimeDelta::Min());
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Min() + TimeDelta::FromSeconds(10));
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Min() + TimeDelta::FromSeconds(-10));
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::FromSeconds(10) + TimeDelta::Min());
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::FromSeconds(-10) + TimeDelta::Min());
-
-  // positive-infinity - anything == positive infinity, and
-  // anything - negative-infinity == positive infinity.
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Max() - TimeDelta::FromSeconds(10));
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Max() - TimeDelta::FromSeconds(-10));
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::FromSeconds(10) - TimeDelta::Min());
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::FromSeconds(-10) - TimeDelta::Min());
-
-  // negative-infinity - anything == negative infinity, and
-  // anything - positive-infinity == negative infinity.
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Min() - TimeDelta::FromSeconds(10));
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Min() - TimeDelta::FromSeconds(-10));
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::FromSeconds(10) - TimeDelta::Max());
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::FromSeconds(-10) - TimeDelta::Max());
-
-  // Negation.
-  static_assert(-TimeDelta::Max() == TimeDelta::Min(), "");
-  static_assert(-TimeDelta::Min() == TimeDelta::Max(), "");
-
-  // positive-infinity * positive == positive-infinity
-  // positive-infinity * negative == negative-infinity
-  // negative-infinity * positive == negative-infinity
-  // negative-infinity * negative == positive-infinity
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Max() * 150);
-  EXPECT_EQ(TimeDelta::Max(),
-            TimeDelta::Max() * std::numeric_limits<int64_t>::max());
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Max() * -150);
-  EXPECT_EQ(TimeDelta::Min(),
-            TimeDelta::Max() * std::numeric_limits<int64_t>::min());
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Min() * 150);
-  EXPECT_EQ(TimeDelta::Min(),
-            TimeDelta::Min() * std::numeric_limits<int64_t>::max());
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Min() * -150);
-  EXPECT_EQ(TimeDelta::Max(),
-            TimeDelta::Min() * std::numeric_limits<int64_t>::min());
-
-  // positive-infinity / positive == positive-infinity
-  // positive-infinity / negative == negative-infinity
-  // negative-infinity / positive == negative-infinity
-  // negative-infinity / negative == positive-infinity
-  // positive-infinity / 0 == positive-infinity
-  // negative-infinity / 0 == negative-infinity
-  // +finite / 0 == positive-infinity
-  // -finite / 0 == negative-infinity
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Max() / 150);
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Max() / -150);
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Min() / 150);
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Min() / -150);
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::Max() / 0);
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::Min() / 0);
-  EXPECT_EQ(TimeDelta::Max(), TimeDelta::FromSeconds(10) / 0);
-  EXPECT_EQ(TimeDelta::Min(), TimeDelta::FromSeconds(-10) / 0);
-
-  // Special case operator/ for TimeDelta.
-  // positive-infinity / positive == positive-infinity
-  // positive-infinity / negative == negative-infinity
-  // negative-infinity / positive == negative-infinity
-  // negative-infinity / negative == positive-infinity
-  // positive-infinity / 0 == positive-infinity
-  // negative-infinity / 0 == negative-infinity
-  // anything / positive-infinity == 0
-  // anything / negative-infinity == 0
-  EXPECT_EQ(std::numeric_limits<int64_t>::max(),
-            TimeDelta::Max() / base::TimeDelta::FromSeconds(10));
-  EXPECT_EQ(std::numeric_limits<int64_t>::min(),
-            TimeDelta::Max() / base::TimeDelta::FromSeconds(-10));
-  EXPECT_EQ(std::numeric_limits<int64_t>::min(),
-            TimeDelta::Min() / base::TimeDelta::FromSeconds(10));
-  EXPECT_EQ(std::numeric_limits<int64_t>::max(),
-            TimeDelta::Min() / base::TimeDelta::FromSeconds(-10));
-  EXPECT_EQ(std::numeric_limits<int64_t>::max(),
-            TimeDelta::Max() / TimeDelta());
-  EXPECT_EQ(std::numeric_limits<int64_t>::min(),
-            TimeDelta::Min() / TimeDelta());
-  EXPECT_EQ(0, TimeDelta::FromSeconds(10) / TimeDelta::Max());
-  EXPECT_EQ(0, TimeDelta::FromSeconds(10) / TimeDelta::Min());
-
-  // x % positive-infinity == x
-  // x % negative-infinity == x
-  const TimeDelta kTenSeconds = TimeDelta::FromSeconds(10);
-  EXPECT_EQ(kTenSeconds, kTenSeconds % TimeDelta::Max());
-  EXPECT_EQ(kTenSeconds, kTenSeconds % TimeDelta::Min());
-}
-
 // Basic test of operators between TimeDeltas (without overflow -- next test
 // handles overflow).
 TEST(TimeDelta, TimeDeltaOperators) {
@@ -1571,10 +1470,10 @@
   // evaluation at the same time.
   static_assert(TimeDelta::Max().is_max(), "");
   static_assert(-TimeDelta::Max() < TimeDelta(), "");
+  static_assert(-TimeDelta::Max() > TimeDelta::Min(), "");
   static_assert(TimeDelta() > -TimeDelta::Max(), "");
 
-  TimeDelta large_delta =
-      TimeDelta::FromMicroseconds(std::numeric_limits<int64_t>::max() - 1);
+  TimeDelta large_delta = TimeDelta::Max() - TimeDelta::FromMilliseconds(1);
   TimeDelta large_negative = -large_delta;
   EXPECT_GT(TimeDelta(), large_negative);
   EXPECT_FALSE(large_delta.is_max());
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index fdc7f092..90a461ac 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -35,13 +35,16 @@
     for line in in_fd:
       if line[:1] != ' ':
         out_fd.write(line)
+  out_fd.flush()
 
 
 def _ParseOptions(args):
   parser = optparse.OptionParser()
   build_utils.AddDepfileOption(parser)
   parser.add_option('--proguard-path',
-                    help='Path to the proguard executable.')
+                    help='Path to the proguard.jar to use.')
+  parser.add_option('--r8-path',
+                    help='Path to the R8.jar to use.')
   parser.add_option('--input-paths',
                     help='Paths to the .jar files proguard should run on.')
   parser.add_option('--output-path', help='Path to the generated .jar file.')
@@ -53,9 +56,6 @@
                          'included by --proguard-configs, but that should '
                          'not actually be included.')
   parser.add_option('--mapping', help='Path to proguard mapping to apply.')
-  parser.add_option('--is-test', action='store_true',
-      help='If true, extra proguard options for instrumentation tests will be '
-      'added.')
   parser.add_option('--classpath', action='append',
                     help='Classpath for proguard.')
   parser.add_option('--enable-dangerous-optimizations', action='store_true',
@@ -82,6 +82,29 @@
   return options
 
 
+def _CreateR8Command(options):
+  # TODO: R8 needs -applymapping equivalent.
+  cmd = [
+    'java', '-jar', options.r8_path,
+    '--no-desugaring',
+    '--classfile',
+    '--output', options.output_path,
+    '--pg-map-output', options.output_path + '.mapping',
+  ]
+
+  classpath = [
+      p for p in set(options.classpath) if p not in options.input_paths
+  ]
+  for lib in classpath:
+    cmd += ['--lib', lib]
+
+  for config_file in options.proguard_configs:
+    cmd += ['--pg-conf', config_file]
+
+  cmd += options.input_paths
+  return cmd
+
+
 def main(args):
   args = build_utils.ExpandFileArgs(args)
   options = _ParseOptions(args)
@@ -101,30 +124,38 @@
   if not options.enable_dangerous_optimizations:
     proguard.disable_optimizations(_DANGEROUS_OPTIMIZATIONS)
 
-  # Do not consider the temp file as an input since its name is random.
-  input_paths = proguard.GetInputs()
+  # TODO(agrieve): Remove proguard usages.
+  if options.r8_path:
+    cmd = _CreateR8Command(options)
+    build_utils.CheckOutput(cmd)
+    build_utils.WriteDepfile(options.depfile, options.output_path,
+                             inputs=proguard.GetDepfileDeps(),
+                             add_pydeps=False)
+  else:
+    # Do not consider the temp file as an input since its name is random.
+    input_paths = proguard.GetInputs()
 
-  with tempfile.NamedTemporaryFile() as f:
-    if options.mapping:
-      input_paths.append(options.mapping)
-      # Maintain only class name mappings in the .mapping file in order to work
-      # around what appears to be a ProGuard bug in -applymapping:
-      #     method 'int closed()' is not being kept as 'a', but remapped to 'c'
-      _RemoveMethodMappings(options.mapping, f)
-      proguard.mapping(f.name)
+    with tempfile.NamedTemporaryFile() as f:
+      if options.mapping:
+        input_paths.append(options.mapping)
+        # Maintain only class name mappings in the .mapping file in order to
+        # work around what appears to be a ProGuard bug in -applymapping:
+        #     method 'int close()' is not being kept as 'a', but remapped to 'c'
+        _RemoveMethodMappings(options.mapping, f)
+        proguard.mapping(f.name)
 
-    input_strings = proguard.build()
-    if f.name in input_strings:
-      input_strings[input_strings.index(f.name)] = '$M'
+      input_strings = proguard.build()
+      if f.name in input_strings:
+        input_strings[input_strings.index(f.name)] = '$M'
 
-    build_utils.CallAndWriteDepfileIfStale(
-        proguard.CheckOutput,
-        options,
-        input_paths=input_paths,
-        input_strings=input_strings,
-        output_paths=proguard.GetOutputs(),
-        depfile_deps=proguard.GetDepfileDeps(),
-        add_pydeps=False)
+      build_utils.CallAndWriteDepfileIfStale(
+          proguard.CheckOutput,
+          options,
+          input_paths=input_paths,
+          input_strings=input_strings,
+          output_paths=proguard.GetOutputs(),
+          depfile_deps=proguard.GetDepfileDeps(),
+          add_pydeps=False)
 
 
 if __name__ == '__main__':
diff --git a/build/android/gyp/util/proguard_util.py b/build/android/gyp/util/proguard_util.py
index fd657e2..8a6e785 100644
--- a/build/android/gyp/util/proguard_util.py
+++ b/build/android/gyp/util/proguard_util.py
@@ -165,7 +165,7 @@
     # Quite useful for auditing proguard flags.
     for config in sorted(self._configs):
       out.write('#' * 80 + '\n')
-      out.write(config + '\n')
+      out.write('# ' + config + '\n')
       out.write('#' * 80 + '\n')
       with open(config) as config_file:
         contents = config_file.read().rstrip()
@@ -175,9 +175,9 @@
       out.write(contents)
       out.write('\n\n')
     out.write('#' * 80 + '\n')
-    out.write('Command-line\n')
+    out.write('# Command-line\n')
     out.write('#' * 80 + '\n')
-    out.write(' '.join(cmd) + '\n')
+    out.write('# ' + ' '.join(cmd) + '\n')
 
   def CheckOutput(self):
     cmd = self.build()
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index cbfcfff98..875e354 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -163,9 +163,6 @@
     # The password for the keystore to use for signing builds.
     android_keystore_password = default_android_keystore_password
 
-    # Enables verbose proguard output (summaries and unfiltered output).
-    proguard_verbose = false
-
     # Java debug on Android. Having this on enables multidexing, and turning it
     # off will enable proguard.
     is_java_debug = is_debug
@@ -207,6 +204,14 @@
 
     # Turns off android lint. Useful for prototyping or for faster local builds.
     disable_android_lint = false
+
+    # Location of aapt2 binary used for app bundles. For now, a more recent version
+    # than the one distributed with the Android SDK is required.
+    android_sdk_tools_bundle_aapt2 =
+        "//third_party/android_build_tools/aapt2/aapt2"
+
+    # Path to r8.jar. If specified, will be used instead of ProGuard for optimization.
+    experimental_r8_path = ""
   }
 
   # We need a second declare_args block to make sure we are using the overridden
@@ -344,13 +349,6 @@
   if (android_libcpp_lib_dir == "") {
     android_libcpp_lib_dir = "${android_libcpp_root}/libs/${android_app_abi}"
   }
-
-  declare_args() {
-    # Location of aapt2 binary used for app bundles. For now, a more recent version
-    # than the one distributed with the Android SDK is required.
-    android_sdk_tools_bundle_aapt2 =
-        "//third_party/android_build_tools/aapt2/aapt2"
-  }
 }
 
 declare_args() {
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 54a5850..15aa173 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -909,10 +909,7 @@
       depfile = "${target_gen_dir}/${target_name}.d"
       outputs = [
         _output_jar_path,
-        "$_output_jar_path.flags",
         "$_output_jar_path.mapping",
-        "$_output_jar_path.seeds",
-        "$_output_jar_path.usage",
       ]
       _rebased_build_config = rebase_path(invoker.build_config, root_build_dir)
       args = [
@@ -925,8 +922,11 @@
         "--classpath",
         "@FileArg($_rebased_build_config:deps_info:proguard_classpath_jars)",
       ]
-      if (proguard_verbose) {
-        args += [ "--verbose" ]
+      if (experimental_r8_path != "") {
+        args += [
+          "--r8-path",
+          rebase_path(experimental_r8_path, root_build_dir),
+        ]
       }
       if (defined(invoker.args)) {
         args += invoker.args
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index e2191b9..f0da799 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -3639,6 +3639,9 @@
   #    proguard_jar_path: Optional. Path to custom proguard jar used for
   #      proguarding.
   #
+  #    enable_multidex: Optional. Enable multidexing of optimized modules jars
+  #      when using synchronized proguarding.
+  #
   # Example:
   #   android_app_bundle("chrome_public_bundle") {
   #      base_module_target = "//chrome/android:chrome_public_apk"
@@ -3675,6 +3678,11 @@
 
     _proguard_enabled =
         defined(invoker.proguard_enabled) && invoker.proguard_enabled
+    _enable_multidex =
+        defined(invoker.enable_multidex) && invoker.enable_multidex
+    assert(
+        _proguard_enabled || !_enable_multidex,
+        "Bundle only adds dexing step if synchronized proguarding is enabled.")
 
     # Make build config, which is required for synchronized proguarding.
     _module_targets = []
@@ -3725,8 +3733,13 @@
 
         _module_final_dex_target = "${target_name}__${_module.name}__dex"
         _module_final_dex_target_dep = ":$_module_final_dex_target"
-        _module_final_dex_path =
-            "$target_gen_dir/$target_name/${_module.name}/classes.dex"
+        if (_enable_multidex) {
+          _module_final_dex_path =
+              "$target_gen_dir/$target_name/${_module.name}/classes.dex.zip"
+        } else {
+          _module_final_dex_path =
+              "$target_gen_dir/$target_name/${_module.name}/classes.dex"
+        }
         _module_final_dex_path_file_arg =
             rebase_path(_module_final_dex_path, root_build_dir)
 
@@ -3756,6 +3769,14 @@
 
           # http://crbug.com/725224. Fix for bots running out of memory.
           use_pool = true
+
+          if (_enable_multidex) {
+            enable_multidex = _enable_multidex
+            extra_main_dex_proguard_config =
+                "$_module_target_gen_dir/$_module_target_name/" +
+                "$_module_target_name.resources.main-dex-proguard.txt"
+            deps += [ "${_module_target}__compile_resources" ]
+          }
         }
       } else {
         _module_final_dex_target_dep = "${_module_target}__final_dex"
diff --git a/build/config/mac/rules.gni b/build/config/mac/rules.gni
index 9872661..d9f4b6e 100644
--- a/build/config/mac/rules.gni
+++ b/build/config/mac/rules.gni
@@ -360,6 +360,10 @@
     # TODO(sdefresne): should we have a framework_dirs similar to lib_dirs
     # and include_dirs to avoid duplicate values on the command-line.
     visibility = [ ":$_framework_target" ]
+    cflags = [
+      "-F",
+      rebase_path("$root_out_dir/.", root_build_dir),
+    ]
     ldflags = [
       "-F",
       rebase_path("$root_out_dir/.", root_build_dir),
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 807f140..500ab89 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -201,7 +201,7 @@
 
   // Returns true if |viewport_point| hits a wheel event handler region that
   // could block scrolling.
-  virtual bool HasWheelEventHandlerAt(
+  virtual bool HasBlockingWheelEventHandlerAt(
       const gfx::Point& viewport_point) const = 0;
 
   // It returns the type of a touch start or move event listener at
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 74e3caa..1fa5e4f 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -785,7 +785,7 @@
                      : InputHandler::TouchStartOrMoveEventListenerType::HANDLER;
 }
 
-bool LayerTreeHostImpl::HasWheelEventHandlerAt(
+bool LayerTreeHostImpl::HasBlockingWheelEventHandlerAt(
     const gfx::Point& viewport_point) const {
   gfx::PointF device_viewport_point = gfx::ScalePoint(
       gfx::PointF(viewport_point), active_tree_->device_scale_factor());
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 8518f7b..42583361 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -279,7 +279,8 @@
   EventListenerTypeForTouchStartOrMoveAt(
       const gfx::Point& viewport_port,
       TouchAction* out_touch_action) override;
-  bool HasWheelEventHandlerAt(const gfx::Point& viewport_point) const override;
+  bool HasBlockingWheelEventHandlerAt(
+      const gfx::Point& viewport_point) const override;
   std::unique_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
       ui::LatencyInfo* latency) override;
   ScrollElasticityHelper* CreateScrollElasticityHelper() override;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 033e52c..d58f6a9f 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -1136,8 +1136,8 @@
 
   // LTHI should know the wheel event handler region and only block mouse events
   // in that region.
-  EXPECT_TRUE(host_impl_->HasWheelEventHandlerAt(gfx::Point(10, 10)));
-  EXPECT_FALSE(host_impl_->HasWheelEventHandlerAt(gfx::Point(30, 30)));
+  EXPECT_TRUE(host_impl_->HasBlockingWheelEventHandlerAt(gfx::Point(10, 10)));
+  EXPECT_FALSE(host_impl_->HasBlockingWheelEventHandlerAt(gfx::Point(30, 30)));
 
   // But they don't influence the actual handling of the scroll gestures.
   InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index c6c32ac..39b59af 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1454,16 +1454,13 @@
     }
 
     version_name = chrome_version_name
+    enable_multidex = true
   }
 }
 
 monochrome_public_apk_or_module_tmpl("monochrome_public_apk") {
   apk_name = "MonochromePublic"
   target_type = "android_apk"
-  if (!is_java_debug) {
-    # TODO(crbug.com/868770): Address proguard version skew.
-    proguard_jar_path = "//third_party/proguard/lib/proguard.6.0.3.jar"
-  }
 }
 
 monochrome_public_apk_or_module_tmpl("monochrome_public_base_module") {
@@ -1730,8 +1727,7 @@
   bundle_name = "MonochromePublicBundle"
   base_module_target = ":monochrome_public_base_module"
   if (!is_java_debug) {
-    # TODO(crbug.com/868770): Address proguard version skew.
     proguard_enabled = true
-    proguard_jar_path = "//third_party/proguard/lib/proguard.6.0.3.jar"
+    enable_multidex = true
   }
 }
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 353441c..4d20dc4 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -87,6 +87,12 @@
   _is_modern = defined(invoker.is_modern) && invoker.is_modern
   assert(_is_modern || !_is_modern)  # Mark as used.
 
+  if (defined(invoker.enable_multidex)) {
+    _enable_multidex = invoker.enable_multidex
+  } else {
+    _enable_multidex = is_java_debug || multidex_in_release
+  }
+
   target(_target_type, target_name) {
     forward_variables_from(invoker, "*")
     exclude_xxxhdpi = true
@@ -112,20 +118,22 @@
       aapt_locale_whitelist = locales - android_chrome_omitted_locales
     }
 
-    if (is_java_debug || multidex_in_release) {
+    if (_enable_multidex) {
       enable_multidex = true
-      if (!defined(negative_main_dex_globs)) {
-        negative_main_dex_globs = [
-          "*ApplicationStatus*",  # Doesn't work in non-browser process.
-          "*ChromeActivity*",  # Pulls in the world, so ensure it doesn't slip in.
-          "*GoogleApiAvailability*",  # Play Services only in browser process.
-          "*R\$*",  # Should not use resources from non-browser process.
-        ]
-      }
+      if (_target_type == "android_apk") {
+        if (!defined(negative_main_dex_globs)) {
+          negative_main_dex_globs = [
+            "*ApplicationStatus*",  # Doesn't work in non-browser process.
+            "*ChromeActivity*",  # Pulls in the world, so ensure it doesn't slip in.
+            "*GoogleApiAvailability*",  # Play Services only in browser process.
+            "*R\$*",  # Should not use resources from non-browser process.
+          ]
+        }
 
-      # Allow targets to append to the default list.
-      if (defined(extra_negative_main_dex_globs)) {
-        negative_main_dex_globs += extra_negative_main_dex_globs
+        # Allow targets to append to the default list.
+        if (defined(extra_negative_main_dex_globs)) {
+          negative_main_dex_globs += extra_negative_main_dex_globs
+        }
       }
     }
     if (!is_java_debug) {
@@ -180,6 +188,11 @@
 # The equivalent of chrome_common_apk_or_module_tmpl for all builds of
 # monochrome.
 template("monochrome_public_common_apk_or_module_tmpl") {
+  if (defined(invoker.enable_multidex)) {
+    _enable_multidex = invoker.enable_multidex
+  } else {
+    _enable_multidex = is_java_debug || multidex_in_release
+  }
   chrome_public_common_apk_or_module_tmpl(target_name) {
     # Always build 64-bit //android_webview:monochrome because Chrome runs
     # in 32-bit mode.
@@ -231,7 +244,7 @@
       "//chrome/android/monochrome:monochrome_license_provider_java",
     ]
 
-    if ((is_java_debug || multidex_in_release) &&
+    if (_enable_multidex && invoker.target_type == "android_apk" &&
         !defined(invoker.negative_main_dex_globs)) {
       # WebView pulls play services into the main dex.
       negative_main_dex_globs = [
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
index 895ffbd4..f5abe230 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
@@ -13,8 +13,8 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -24,15 +24,15 @@
  * {@link ViewPager}. It instantiates the tab views based on the layout they provide.
  */
 class AccessoryPagerAdapter extends PagerAdapter
-        implements ListModelChangeProcessor.ViewBinder<SimpleListObservable<Tab>, ViewPager> {
-    private final SimpleListObservable<Tab> mTabList;
+        implements ListModelChangeProcessor.ViewBinder<ListModel<Tab>, ViewPager> {
+    private final ListModel<Tab> mTabList;
     private final Map<Tab, ViewGroup> mViews;
 
     /**
      * Creates the PagerAdapter that populates a ViewPager based on a held list of tabs.
      * @param tabList The list that contains the tabs to instantiate.
      */
-    public AccessoryPagerAdapter(SimpleListObservable<Tab> tabList) {
+    public AccessoryPagerAdapter(ListModel<Tab> tabList) {
         mTabList = tabList;
         mViews = new HashMap<>(mTabList.size());
     }
@@ -89,20 +89,17 @@
     }
 
     @Override
-    public void onItemsInserted(
-            SimpleListObservable<Tab> model, ViewPager view, int index, int count) {
+    public void onItemsInserted(ListModel<Tab> model, ViewPager view, int index, int count) {
         notifyDataSetChanged();
     }
 
     @Override
-    public void onItemsRemoved(
-            SimpleListObservable<Tab> model, ViewPager view, int index, int count) {
+    public void onItemsRemoved(ListModel<Tab> model, ViewPager view, int index, int count) {
         notifyDataSetChanged();
     }
 
     @Override
-    public void onItemsChanged(
-            SimpleListObservable<Tab> model, ViewPager view, int index, int count) {
+    public void onItemsChanged(ListModel<Tab> model, ViewPager view, int index, int count) {
         notifyDataSetChanged();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetModel.java
index 95b6c8a..c6885bc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetModel.java
@@ -5,8 +5,8 @@
 package org.chromium.chrome.browser.autofill.keyboard_accessory;
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.PropertyObservable;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 
 /**
  * This model holds all view state of the accessory sheet.
@@ -24,9 +24,9 @@
     private int mActiveTabIndex = NO_ACTIVE_TAB;
     private boolean mVisible;
     private int mHeight;
-    private final SimpleListObservable<Tab> mTabList = new SimpleListObservable<>();
+    private final ListModel<Tab> mTabList = new ListModel<>();
 
-    SimpleListObservable<Tab> getTabList() {
+    ListModel<Tab> getTabList() {
         return mTabList;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
index 3d09371..661890a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
@@ -11,9 +11,9 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.modelutil.PropertyObservable;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -280,7 +280,7 @@
      * @param suggestionList The list containing all suggestions.
      */
     static void recordSheetSuggestions(
-            @AccessoryTabType int tabType, SimpleListObservable<Item> suggestionList) {
+            @AccessoryTabType int tabType, ListModel<Item> suggestionList) {
         int interactiveSuggestions = 0;
         for (int i = 0; i < suggestionList.size(); ++i) {
             if (suggestionList.get(i).getType() == ItemType.SUGGESTION) ++interactiveSuggestions;
@@ -297,8 +297,7 @@
     }
 
     private static boolean hasAtLeastOneActionOfType(
-            SimpleListObservable<KeyboardAccessoryData.Action> actionList,
-            @AccessoryAction int... types) {
+            ListModel<KeyboardAccessoryData.Action> actionList, @AccessoryAction int... types) {
         Set<Integer> typeList = new HashSet<>(types.length);
         for (@AccessoryAction int type : types) typeList.add(type);
         for (KeyboardAccessoryData.Action action : actionList) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModel.java
index bfa59f3..69db3e93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModel.java
@@ -7,9 +7,9 @@
 import android.support.annotation.Nullable;
 import android.support.design.widget.TabLayout;
 
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.modelutil.PropertyObservable;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -37,15 +37,15 @@
         }
     }
 
-    private SimpleListObservable<KeyboardAccessoryData.Action> mActionListObservable;
-    private SimpleListObservable<KeyboardAccessoryData.Tab> mTabListObservable;
+    private ListModel<KeyboardAccessoryData.Action> mActionListObservable;
+    private ListModel<KeyboardAccessoryData.Tab> mTabListObservable;
     private boolean mVisible;
     private @Nullable Integer mActiveTab;
     private TabLayout.OnTabSelectedListener mTabSelectionCallbacks;
 
     KeyboardAccessoryModel() {
-        mActionListObservable = new SimpleListObservable<>();
-        mTabListObservable = new SimpleListObservable<>();
+        mActionListObservable = new ListModel<>();
+        mTabListObservable = new ListModel<>();
     }
 
     void addActionListObserver(ListObservable.ListObserver<Void> observer) {
@@ -56,7 +56,7 @@
         mActionListObservable.set(actions);
     }
 
-    SimpleListObservable<KeyboardAccessoryData.Action> getActionList() {
+    ListModel<KeyboardAccessoryData.Action> getActionList() {
         return mActionListObservable;
     }
 
@@ -72,7 +72,7 @@
         mTabListObservable.remove(tab);
     }
 
-    SimpleListObservable<KeyboardAccessoryData.Tab> getTabList() {
+    ListModel<KeyboardAccessoryData.Tab> getTabList() {
         return mTabListObservable;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java
index d693560..fd0f97f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java
@@ -15,8 +15,8 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryModel.PropertyKey;
 import org.chromium.chrome.browser.modelutil.LazyViewBinderAdapter;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 
 /**
  * Observes {@link KeyboardAccessoryModel} changes (like a newly available tab) and triggers the
@@ -59,11 +59,10 @@
     }
 
     static class TabViewBinder
-            implements ListModelChangeProcessor
-                               .ViewBinder<SimpleListObservable<Tab>, KeyboardAccessoryView> {
+            implements ListModelChangeProcessor.ViewBinder<ListModel<Tab>, KeyboardAccessoryView> {
         @Override
         public void onItemsInserted(
-                SimpleListObservable<Tab> model, KeyboardAccessoryView view, int index, int count) {
+                ListModel<Tab> model, KeyboardAccessoryView view, int index, int count) {
             assert count > 0 : "Tried to insert invalid amount of tabs - must be at least one.";
             while (count-- > 0) {
                 Tab tab = model.get(index);
@@ -74,7 +73,7 @@
 
         @Override
         public void onItemsRemoved(
-                SimpleListObservable<Tab> model, KeyboardAccessoryView view, int index, int count) {
+                ListModel<Tab> model, KeyboardAccessoryView view, int index, int count) {
             assert count > 0 : "Tried to remove invalid amount of tabs - must be at least one.";
             while (count-- > 0) {
                 view.removeTabAt(index++);
@@ -83,12 +82,12 @@
 
         @Override
         public void onItemsChanged(
-                SimpleListObservable<Tab> model, KeyboardAccessoryView view, int index, int count) {
+                ListModel<Tab> model, KeyboardAccessoryView view, int index, int count) {
             // TODO(fhorschig): Implement fine-grained, ranged changes should the need arise.
             updateAllTabs(view, model);
         }
 
-        void updateAllTabs(KeyboardAccessoryView view, SimpleListObservable<Tab> model) {
+        void updateAllTabs(KeyboardAccessoryView view, ListModel<Tab> model) {
             view.clearTabs();
             for (int i = 0; i < model.size(); ++i) {
                 Tab tab = model.get(i);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
index f163784..b87d7a0e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
@@ -16,8 +16,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.PasswordAccessorySheetViewBinder.ItemViewHolder;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
 
 /**
@@ -26,7 +26,7 @@
  */
 public class PasswordAccessorySheetCoordinator implements KeyboardAccessoryData.Tab.Listener {
     private final Context mContext;
-    private final SimpleListObservable<Item> mModel = new SimpleListObservable<>();
+    private final ListModel<Item> mModel = new ListModel<>();
     private final KeyboardAccessoryData.Observer<Item> mMediator = (t, items) -> mModel.set(items);
 
     private final KeyboardAccessoryData.Tab mTab;
@@ -87,12 +87,11 @@
 
     /**
      * Creates an adapter to an {@link PasswordAccessorySheetViewBinder} that is wired
-     * up to the model change processor which listens to the {@link SimpleListObservable<Item>}.
-     * @param model the {@link SimpleListObservable<Item>} the adapter gets its data from.
+     * up to the model change processor which listens to the {@link ListModel <Item>}.
+     * @param model the {@link ListModel <Item>} the adapter gets its data from.
      * @return Returns a fully initialized and wired adapter to a PasswordAccessorySheetViewBinder.
      */
-    static RecyclerViewAdapter<ItemViewHolder, Void> createAdapter(
-            SimpleListObservable<Item> model) {
+    static RecyclerViewAdapter<ItemViewHolder, Void> createAdapter(ListModel<Item> model) {
         return new RecyclerViewAdapter<>(
                 new SimpleRecyclerViewMcp<>(model, Item::getType, ItemViewHolder::bind),
                 ItemViewHolder::create);
@@ -117,7 +116,7 @@
     }
 
     @VisibleForTesting
-    SimpleListObservable<Item> getModelForTesting() {
+    ListModel<Item> getModelForTesting() {
         return mModel;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
index 7f1e7c6..70d6b1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
@@ -21,11 +21,11 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 
 /**
- * This stateless class provides methods to bind the items in a {@link SimpleListObservable<Item>}
+ * This stateless class provides methods to bind the items in a {@link ListModel <Item>}
  * to the {@link RecyclerView} used as view of the Password accessory sheet component.
  */
 class PasswordAccessorySheetViewBinder {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
index c22cc74..3f875fbb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
@@ -13,8 +13,8 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
 
 /**
@@ -23,7 +23,7 @@
  */
 public class ChipsCoordinator implements ChipsProvider.Observer {
     private final ChipsProvider mProvider;
-    private final SimpleListObservable<Chip> mModel = new SimpleListObservable<>();
+    private final ListModel<Chip> mModel = new ListModel<>();
     private final RecyclerView mView;
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListModel.java
new file mode 100644
index 0000000..5b0aa33
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListModel.java
@@ -0,0 +1,81 @@
+// 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.download.home.list;
+
+import android.support.annotation.Nullable;
+import android.support.v7.util.BatchingListUpdateCallback;
+import android.support.v7.util.ListUpdateCallback;
+
+import org.chromium.chrome.browser.modelutil.ListModel;
+
+/**
+ * Helper class to batch updates to ListModel before notifying observers.
+ * @see BatchingListUpdateCallback
+ * @param <T> The object type that this class manages in a list.
+ */
+public abstract class BatchListModel<T> extends ListModel<T> {
+    final BatchingListUpdateCallback mBatchingCallback;
+
+    /** Creates a new BatchListModel instance. */
+    public BatchListModel() {
+        mBatchingCallback = new BatchingListUpdateCallback(new ListUpdateCallback() {
+            @Override
+            public void onInserted(int position, int count) {
+                super_notifyItemRangeInserted(position, count);
+            }
+
+            @Override
+            public void onRemoved(int position, int count) {
+                super_notifyItemRangeRemoved(position, count);
+            }
+
+            @Override
+            public void onMoved(int fromPosition, int toPosition) {
+                assert false : "ListUpdateCallback#onMoved() is not supported by ListObservable.";
+            }
+
+            @Override
+            public void onChanged(int position, int count, @Nullable Object payload) {
+                assert payload == null;
+                super_notifyItemRangeChanged(position, count);
+            }
+        });
+    }
+
+    /**
+     * Dispatches any outstanding batched updates to observers.
+     * @see BatchingListUpdateCallback#dispatchLastEvent()
+     */
+    public void dispatchLastEvent() {
+        mBatchingCallback.dispatchLastEvent();
+    }
+
+    @Override
+    protected void notifyItemRangeInserted(int index, int count) {
+        mBatchingCallback.onInserted(index, count);
+    }
+
+    @Override
+    protected void notifyItemRangeRemoved(int index, int count) {
+        mBatchingCallback.onRemoved(index, count);
+    }
+
+    @Override
+    protected void notifyItemRangeChanged(int index, int count, @Nullable Void payload) {
+        mBatchingCallback.onChanged(index, count, null);
+    }
+
+    private void super_notifyItemRangeInserted(int index, int count) {
+        super.notifyItemRangeInserted(index, count);
+    }
+
+    private void super_notifyItemRangeRemoved(int index, int count) {
+        super.notifyItemRangeRemoved(index, count);
+    }
+
+    private void super_notifyItemRangeChanged(int index, int count) {
+        super.notifyItemRangeChanged(index, count, null);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListObservable.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListObservable.java
deleted file mode 100644
index 462155d..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListObservable.java
+++ /dev/null
@@ -1,81 +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.
-
-package org.chromium.chrome.browser.download.home.list;
-
-import android.support.annotation.Nullable;
-import android.support.v7.util.BatchingListUpdateCallback;
-import android.support.v7.util.ListUpdateCallback;
-
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
-
-/**
- * Helper class to batch updates to SimpleListObservable before notifying observers.
- * @see BatchingListUpdateCallback
- * @param <T> The object type that this class manages in a list.
- */
-public abstract class BatchListObservable<T> extends SimpleListObservable<T> {
-    final BatchingListUpdateCallback mBatchingCallback;
-
-    /** Creates a new BatchListObservable instance. */
-    public BatchListObservable() {
-        mBatchingCallback = new BatchingListUpdateCallback(new ListUpdateCallback() {
-            @Override
-            public void onInserted(int position, int count) {
-                super_notifyItemRangeInserted(position, count);
-            }
-
-            @Override
-            public void onRemoved(int position, int count) {
-                super_notifyItemRangeRemoved(position, count);
-            }
-
-            @Override
-            public void onMoved(int fromPosition, int toPosition) {
-                assert false : "ListUpdateCallback#onMoved() is not supported by ListObservable.";
-            }
-
-            @Override
-            public void onChanged(int position, int count, @Nullable Object payload) {
-                assert payload == null;
-                super_notifyItemRangeChanged(position, count);
-            }
-        });
-    }
-
-    /**
-     * Dispatches any outstanding batched updates to observers.
-     * @see BatchingListUpdateCallback#dispatchLastEvent()
-     */
-    public void dispatchLastEvent() {
-        mBatchingCallback.dispatchLastEvent();
-    }
-
-    @Override
-    protected void notifyItemRangeInserted(int index, int count) {
-        mBatchingCallback.onInserted(index, count);
-    }
-
-    @Override
-    protected void notifyItemRangeRemoved(int index, int count) {
-        mBatchingCallback.onRemoved(index, count);
-    }
-
-    @Override
-    protected void notifyItemRangeChanged(int index, int count, @Nullable Void payload) {
-        mBatchingCallback.onChanged(index, count, null);
-    }
-
-    private void super_notifyItemRangeInserted(int index, int count) {
-        super.notifyItemRangeInserted(index, count);
-    }
-
-    private void super_notifyItemRangeRemoved(int index, int count) {
-        super.notifyItemRangeRemoved(index, count);
-    }
-
-    private void super_notifyItemRangeChanged(int index, int count) {
-        super.notifyItemRangeChanged(index, count, null);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java
index 154ccd0..96db81d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java
@@ -8,10 +8,10 @@
 
 /**
  * This model represents the data required to build a list UI around a set of {@link ListItem}s.
- * This includes (1) a {@link BatchListObservable} implementation and (2) exposing a
+ * This includes (1) a {@link BatchListModel} implementation and (2) exposing a
  * {@link PropertyModel} for shared item properties and general list information.
  */
-class ListItemModel extends BatchListObservable<ListItem> {
+class ListItemModel extends BatchListModel<ListItem> {
     private final PropertyModel mListProperties = new PropertyModel(ListProperties.ALL_KEYS);
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModel.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModel.java
new file mode 100644
index 0000000..9929017
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModel.java
@@ -0,0 +1,14 @@
+// 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.modelutil;
+
+/**
+ * Base class for a {@link ListObservable} containing a {@link SimpleList} of items.
+ * It allows models to compose different ListObservables.
+ * Under the hood this class is just a shorthand for {@link ListModelBase} with a
+ * {@link Void} partial change notification payload type, for list types {@code T} that don't
+ * support partial change notification.
+ * @param <T> The object type that this class manages in a list.
+ */
+public class ListModel<T> extends ListModelBase<T, Void> {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelBase.java
new file mode 100644
index 0000000..fdafc09
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelBase.java
@@ -0,0 +1,146 @@
+// 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.modelutil;
+
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Base class for a {@link ListObservable} containing a {@link SimpleList} of items that support
+ * sending partial change notifications. If the list item type does not support partial change
+ * notifications, use the {@link ListModel} subclass.
+ * It allows models to compose different ListObservables.
+ * @param <T> The object type that this class manages in a list.
+ * @param <P> The payload type for partial change notifications.
+ */
+public class ListModelBase<T, P> extends ListObservableImpl<P> implements SimpleList<T> {
+    private final List<T> mItems = new ArrayList<>();
+
+    /**
+     * Returns the item at the given position.
+     * @param index The position to get the item from.
+     * @return Returns the found item.
+     */
+    @Override
+    public T get(int index) {
+        return mItems.get(index);
+    }
+
+    @Override
+    public int size() {
+        return mItems.size();
+    }
+
+    @NonNull
+    @Override
+    public Iterator<T> iterator() {
+        return mItems.iterator();
+    }
+
+    /**
+     * Appends a given {@code item} to the last position of the held {@link List}.
+     * Notifies observers about the inserted item.
+     * @param item The item to be stored.
+     */
+    public void add(T item) {
+        mItems.add(item);
+        notifyItemInserted(mItems.size() - 1);
+    }
+
+    /**
+     * Appends all given {@code items} to the last position of the held {@link List}.
+     * Notifies observers about the inserted items.
+     * @param items The items to be stored.
+     */
+    public void addAll(Collection<T> items) {
+        int insertionIndex = mItems.size();
+        mItems.addAll(items);
+        notifyItemRangeInserted(insertionIndex, items.size());
+    }
+
+    /**
+     * Removes a given item from the held {@link List}. Notifies observers about the removal.
+     * @param item The item to be removed.
+     */
+    public void remove(T item) {
+        int position = mItems.indexOf(item);
+        removeAt(position);
+    }
+
+    /**
+     * Removes an item by position from the held {@link List}. Notifies observers about the removal.
+     * @param position The position of the item to be removed.
+     * @return The item that has been removed.
+     */
+    public T removeAt(int position) {
+        T item = mItems.remove(position);
+        notifyItemRemoved(position);
+        return item;
+    }
+
+    /**
+     * Removes a range of {@code count} consecutive items from the held {@link List}, starting at
+     * {@code startPosition}. Notifies observers about the removal.
+     * @param startPosition The start position of the range of items to be removed.
+     * @param count The number of items to be removed.
+     */
+    public void removeRange(int startPosition, int count) {
+        mItems.subList(startPosition, startPosition + count).clear();
+        notifyItemRangeRemoved(startPosition, count);
+    }
+
+    /**
+     * Convenience method to replace all held items with the given array of items.
+     * @param newItems The array of items that should replace all held items.
+     * @see #set(Collection)
+     */
+    public void set(T[] newItems) {
+        set(Arrays.asList(newItems));
+    }
+
+    /**
+     * Replaces all held items with the given collection of items, notifying observers about the
+     * resulting insertions, deletions, changes, or combinations thereof.
+     * @param newItems The collection of items that should replace all held items.
+     */
+    public void set(Collection<T> newItems) {
+        int oldSize = mItems.size();
+        int newSize = newItems.size();
+
+        mItems.clear();
+        mItems.addAll(newItems);
+
+        int min = Math.min(oldSize, newSize);
+        if (min > 0) notifyItemRangeChanged(0, min);
+
+        if (newSize > oldSize) {
+            notifyItemRangeInserted(min, newSize - oldSize);
+        } else if (newSize < oldSize) {
+            notifyItemRangeRemoved(min, oldSize - newSize);
+        }
+    }
+
+    /**
+     * Replaces a single {@code item} at the given {@code index}.
+     * @param index The index of the item to be replaced.
+     * @param item The item to be replaced.
+     */
+    public void update(int index, T item) {
+        mItems.set(index, item);
+        notifyItemRangeChanged(index, 1);
+    }
+
+    /**
+     * @return The position of the given {@code item} in the held {@link List}.
+     */
+    public int indexOf(Object item) {
+        return mItems.indexOf(item);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java
index 7cbcae0..316b0b6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java
@@ -11,7 +11,7 @@
  * their items. Note also that this class on purpose does not expose an item type (it only exposes a
  * <i>payload</i> type for partial change notifications), nor does it give access to the list
  * contents. This is because the list might not be homogeneous -- it could represent items of vastly
- * different types that don't share a common base class. Use the {@link SimpleListObservable}
+ * different types that don't share a common base class. Use the {@link ListModel}
  * subclass for homogeneous lists.
  * @param <P> The parameter type for the payload for partial updates. Use {@link Void} for
  *         implementations that don't support partial updates.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java
new file mode 100644
index 0000000..bffbd7e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java
@@ -0,0 +1,54 @@
+// 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.modelutil;
+
+import android.support.annotation.Nullable;
+
+import java.util.Collection;
+
+/**
+ * Represents a list of (property-)observable items, and notifies about changes to any of its items.
+ *
+ * @param <T> The type of item in the list.
+ * @param <P> The property key type for {@code T} to be used as payload for partial updates.
+ */
+public class PropertyListModel<T extends PropertyObservable<P>, P> extends ListModelBase<T, P> {
+    private final PropertyObservable.PropertyObserver<P> mPropertyObserver =
+            this::onPropertyChanged;
+
+    @Override
+    public void add(T item) {
+        super.add(item);
+        item.addObserver(mPropertyObserver);
+    }
+
+    @Override
+    public void remove(T item) {
+        item.removeObserver(mPropertyObserver);
+        super.remove(item);
+    }
+
+    @Override
+    public void update(int index, T item) {
+        get(index).removeObserver(mPropertyObserver);
+        super.update(index, item);
+        item.addObserver(mPropertyObserver);
+    }
+
+    @Override
+    public void set(Collection<T> newItems) {
+        for (T item : this) {
+            item.removeObserver(mPropertyObserver);
+        }
+        super.set(newItems);
+        for (T item : newItems) {
+            item.addObserver(mPropertyObserver);
+        }
+    }
+
+    private void onPropertyChanged(PropertyObservable<P> source, @Nullable P propertyKey) {
+        notifyItemChanged(indexOf(source), propertyKey);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListObservable.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListObservable.java
deleted file mode 100644
index 57ee39b..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListObservable.java
+++ /dev/null
@@ -1,54 +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.
-package org.chromium.chrome.browser.modelutil;
-
-import android.support.annotation.Nullable;
-
-import java.util.Collection;
-
-/**
- * Represents a list of (property-)observable items, and notifies about changes to any of its items.
- *
- * @param <T> The type of item in the list.
- * @param <P> The property key type for {@code T} to be used as payload for partial updates.
- */
-public class PropertyListObservable<T extends PropertyObservable<P>, P>
-        extends SimpleListObservableBase<T, P> {
-    private final PropertyObservable.PropertyObserver<P> mPropertyObserver =
-            this::onPropertyChanged;
-
-    @Override
-    public void add(T item) {
-        super.add(item);
-        item.addObserver(mPropertyObserver);
-    }
-
-    @Override
-    public void remove(T item) {
-        item.removeObserver(mPropertyObserver);
-        super.remove(item);
-    }
-
-    @Override
-    public void update(int index, T item) {
-        get(index).removeObserver(mPropertyObserver);
-        super.update(index, item);
-        item.addObserver(mPropertyObserver);
-    }
-
-    @Override
-    public void set(Collection<T> newItems) {
-        for (T item : this) {
-            item.removeObserver(mPropertyObserver);
-        }
-        super.set(newItems);
-        for (T item : newItems) {
-            item.addObserver(mPropertyObserver);
-        }
-    }
-
-    private void onPropertyChanged(PropertyObservable<P> source, @Nullable P propertyKey) {
-        notifyItemChanged(indexOf(source), propertyKey);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleListObservable.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleListObservable.java
deleted file mode 100644
index 85455cd..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleListObservable.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-package org.chromium.chrome.browser.modelutil;
-
-/**
- * Base class for a {@link ListObservable} containing a {@link SimpleList} of items.
- * It allows models to compose different ListObservables.
- * Under the hood this class is just a shorthand for {@link SimpleListObservableBase} with a
- * {@link Void} partial change notification payload type, for list types {@code T} that don't
- * support partial change notification.
- * @param <T> The object type that this class manages in a list.
- */
-public class SimpleListObservable<T> extends SimpleListObservableBase<T, Void> {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleListObservableBase.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleListObservableBase.java
deleted file mode 100644
index 2b539f3..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleListObservableBase.java
+++ /dev/null
@@ -1,146 +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.
-
-package org.chromium.chrome.browser.modelutil;
-
-import android.support.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Base class for a {@link ListObservable} containing a {@link SimpleList} of items that support
- * sending partial change notifications. If the list item type does not support partial change
- * notifications, use the {@link SimpleListObservable} subclass.
- * It allows models to compose different ListObservables.
- * @param <T> The object type that this class manages in a list.
- * @param <P> The payload type for partial change notifications.
- */
-public class SimpleListObservableBase<T, P> extends ListObservableImpl<P> implements SimpleList<T> {
-    private final List<T> mItems = new ArrayList<>();
-
-    /**
-     * Returns the item at the given position.
-     * @param index The position to get the item from.
-     * @return Returns the found item.
-     */
-    @Override
-    public T get(int index) {
-        return mItems.get(index);
-    }
-
-    @Override
-    public int size() {
-        return mItems.size();
-    }
-
-    @NonNull
-    @Override
-    public Iterator<T> iterator() {
-        return mItems.iterator();
-    }
-
-    /**
-     * Appends a given {@code item} to the last position of the held {@link List}.
-     * Notifies observers about the inserted item.
-     * @param item The item to be stored.
-     */
-    public void add(T item) {
-        mItems.add(item);
-        notifyItemInserted(mItems.size() - 1);
-    }
-
-    /**
-     * Appends all given {@code items} to the last position of the held {@link List}.
-     * Notifies observers about the inserted items.
-     * @param item The items to be stored.
-     */
-    public void addAll(Collection<T> items) {
-        int insertionIndex = mItems.size();
-        mItems.addAll(items);
-        notifyItemRangeInserted(insertionIndex, items.size());
-    }
-
-    /**
-     * Removes a given item from the held {@link List}. Notifies observers about the removal.
-     * @param item The item to be removed.
-     */
-    public void remove(T item) {
-        int position = mItems.indexOf(item);
-        removeAt(position);
-    }
-
-    /**
-     * Removes an item by position from the held {@link List}. Notifies observers about the removal.
-     * @param position The position of the item to be removed.
-     * @return The item that has been removed.
-     */
-    public T removeAt(int position) {
-        T item = mItems.remove(position);
-        notifyItemRemoved(position);
-        return item;
-    }
-
-    /**
-     * Removes a range of {@code count} consecutive items from the held {@link List}, starting at
-     * {@code startPosition}. Notifies observers about the removal.
-     * @param startPosition The start position of the range of items to be removed.
-     * @param count The number of items to be removed.
-     */
-    public void removeRange(int startPosition, int count) {
-        mItems.subList(startPosition, startPosition + count).clear();
-        notifyItemRangeRemoved(startPosition, count);
-    }
-
-    /**
-     * Convenience method to replace all held items with the given array of items.
-     * @param newItems The array of items that should replace all held items.
-     * @see #set(Collection)
-     */
-    public void set(T[] newItems) {
-        set(Arrays.asList(newItems));
-    }
-
-    /**
-     * Replaces all held items with the given collection of items, notifying observers about the
-     * resulting insertions, deletions, changes, or combinations thereof.
-     * @param newItems The collection of items that should replace all held items.
-     */
-    public void set(Collection<T> newItems) {
-        int oldSize = mItems.size();
-        int newSize = newItems.size();
-
-        mItems.clear();
-        mItems.addAll(newItems);
-
-        int min = Math.min(oldSize, newSize);
-        if (min > 0) notifyItemRangeChanged(0, min);
-
-        if (newSize > oldSize) {
-            notifyItemRangeInserted(min, newSize - oldSize);
-        } else if (newSize < oldSize) {
-            notifyItemRangeRemoved(min, oldSize - newSize);
-        }
-    }
-
-    /**
-     * Replaces a single {@code item} at the given {@code index}.
-     * @param index The index of the item to be replaced.
-     * @param item The item to be replaced.
-     */
-    public void update(int index, T item) {
-        mItems.set(index, item);
-        notifyItemRangeChanged(index, 1);
-    }
-
-    /**
-     * @return The position of the given {@code item} in the held {@link List}.
-     */
-    public int indexOf(Object item) {
-        return mItems.indexOf(item);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java
index bcfe7c62..080c63f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java
@@ -34,7 +34,7 @@
      *         the default view type.
      * @param viewBinder The {@link ViewBinder} binding this adapter to the view holder.
      */
-    public SimpleRecyclerViewMcp(SimpleListObservable<T> model,
+    public SimpleRecyclerViewMcp(ListModel<T> model,
             @Nullable ItemViewTypeCallback<T> itemViewTypeCallback, ViewBinder<T, VH> viewBinder) {
         super(itemViewTypeCallback,
                 (holder, item, payload) -> viewBinder.onBindViewHolder(holder, item), model);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java
index 9fb7576..e2412ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java
@@ -23,7 +23,7 @@
     private final ViewBinder<T, VH, P> mViewBinder;
 
     public SimpleRecyclerViewMcpBase(@Nullable ItemViewTypeCallback<T> itemViewTypeCallback,
-            ViewBinder<T, VH, P> viewBinder, SimpleListObservableBase<T, P> model) {
+            ViewBinder<T, VH, P> viewBinder, ListModelBase<T, P> model) {
         mItemViewTypeCallback = itemViewTypeCallback;
         mViewBinder = viewBinder;
         mModel = model;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
index 3eec5419..e8aae53 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -10,9 +10,9 @@
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.modelutil.ListModelBase;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyListObservable;
-import org.chromium.chrome.browser.modelutil.SimpleListObservableBase;
+import org.chromium.chrome.browser.modelutil.PropertyListModel;
 import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcpBase;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
@@ -52,8 +52,8 @@
     private final SuggestionsSource mSuggestionsSource;
     private final SuggestionsRanker mSuggestionsRanker;
 
-    private final PropertyListObservable<SnippetArticle, PartialBindCallback> mSuggestions =
-            new PropertyListObservable<>();
+    private final PropertyListModel<SnippetArticle, PartialBindCallback> mSuggestions =
+            new PropertyListModel<>();
 
     // Children
     private final SectionHeader mHeader;
@@ -122,12 +122,12 @@
     private static class SuggestionsList extends SimpleRecyclerViewMcpBase<SnippetArticle,
             NewTabPageViewHolder, PartialBindCallback> {
         private final SuggestionsSource mSuggestionsSource;
-        private final SimpleListObservableBase<SnippetArticle, PartialBindCallback> mSuggestions;
+        private final ListModelBase<SnippetArticle, PartialBindCallback> mSuggestions;
 
         private boolean mIsDestroyed;
 
         public SuggestionsList(SuggestionsSource suggestionsSource,
-                PropertyListObservable<SnippetArticle, PartialBindCallback> suggestions,
+                PropertyListModel<SnippetArticle, PartialBindCallback> suggestions,
                 ViewBinder<SnippetArticle, NewTabPageViewHolder, PartialBindCallback> viewBinder) {
             super(ignored -> ItemViewType.SNIPPET, viewBinder, suggestions);
             mSuggestionsSource = suggestionsSource;
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 72941c4..8c76d95 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -483,7 +483,7 @@
   "java/src/org/chromium/chrome/browser/download/home/glue/ThumbnailRequestGlue.java",
   "java/src/org/chromium/chrome/browser/download/home/list/CalendarFactory.java",
   "java/src/org/chromium/chrome/browser/download/home/list/CalendarUtils.java",
-  "java/src/org/chromium/chrome/browser/download/home/list/BatchListObservable.java",
+  "java/src/org/chromium/chrome/browser/download/home/list/BatchListModel.java",
   "java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java",
   "java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java",
   "java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java",
@@ -821,18 +821,18 @@
   "java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java",
   "java/src/org/chromium/chrome/browser/modelutil/ForwardingListObservable.java",
   "java/src/org/chromium/chrome/browser/modelutil/LazyViewBinderAdapter.java",
+  "java/src/org/chromium/chrome/browser/modelutil/ListModel.java",
+  "java/src/org/chromium/chrome/browser/modelutil/ListModelBase.java",
   "java/src/org/chromium/chrome/browser/modelutil/ListModelChangeProcessor.java",
   "java/src/org/chromium/chrome/browser/modelutil/ListObservable.java",
   "java/src/org/chromium/chrome/browser/modelutil/ListObservableImpl.java",
   "java/src/org/chromium/chrome/browser/modelutil/PropertyKey.java",
-  "java/src/org/chromium/chrome/browser/modelutil/PropertyListObservable.java",
+  "java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java",
   "java/src/org/chromium/chrome/browser/modelutil/PropertyModel.java",
   "java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java",
   "java/src/org/chromium/chrome/browser/modelutil/PropertyObservable.java",
   "java/src/org/chromium/chrome/browser/modelutil/RecyclerViewAdapter.java",
   "java/src/org/chromium/chrome/browser/modelutil/SimpleList.java",
-  "java/src/org/chromium/chrome/browser/modelutil/SimpleListObservable.java",
-  "java/src/org/chromium/chrome/browser/modelutil/SimpleListObservableBase.java",
   "java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java",
   "java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java",
   "java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewTest.java
index 3a8e49be..be8994eb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewTest.java
@@ -28,7 +28,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content.browser.test.util.Criteria;
@@ -43,7 +43,7 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class PasswordAccessorySheetViewTest {
-    private SimpleListObservable<Item> mModel;
+    private ListModel<Item> mModel;
     private AtomicReference<RecyclerView> mView = new AtomicReference<>();
 
     @Rule
@@ -75,7 +75,7 @@
 
     @Before
     public void setUp() throws InterruptedException {
-        mModel = new SimpleListObservable<>();
+        mModel = new ListModel<>();
         mActivityTestRule.startMainActivityOnBlankPage();
         openLayoutInAccessorySheet(
                 R.layout.password_accessory_sheet, new KeyboardAccessoryData.Tab.Listener() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/OverviewListLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/OverviewListLayoutTest.java
index 0b94a72..ca16c51 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/OverviewListLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/OverviewListLayoutTest.java
@@ -31,6 +31,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelListItem;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -149,8 +150,12 @@
 
     private CharSequence getTabTitleOfListItem(int index) {
         View childView = getListItem(index);
-        TextView childTextView =
-                (TextView) childView.findViewById(org.chromium.chrome.R.id.tab_title);
+        TextView childTextView;
+        if (FeatureUtilities.isChromeModernDesignEnabled()) {
+            childTextView = (TextView) childView.findViewById(org.chromium.chrome.R.id.title);
+        } else {
+            childTextView = (TextView) childView.findViewById(org.chromium.chrome.R.id.tab_title);
+        }
         return childTextView.getText();
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
index 7807a5c1..f0df2ea 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
@@ -40,8 +40,8 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.PropertyProvider;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.test.util.browser.Features;
@@ -181,10 +181,10 @@
                 new PropertyProvider<>(GENERATE_PASSWORD_AUTOMATIC);
         PropertyProvider<Action> secondTabProvider =
                 new PropertyProvider<>(GENERATE_PASSWORD_AUTOMATIC);
-        SimpleListObservable<Action> keyboardActions = mediator.getKeyboardAccessory()
-                                                               .getMediatorForTesting()
-                                                               .getModelForTesting()
-                                                               .getActionList();
+        ListModel<Action> keyboardActions = mediator.getKeyboardAccessory()
+                                                    .getMediatorForTesting()
+                                                    .getModelForTesting()
+                                                    .getActionList();
         keyboardActions.addObserver(mMockItemListObserver);
 
         // Simulate opening a new tab which automatically triggers the registration:
@@ -443,4 +443,4 @@
         }
         mediator.getTabObserverForTesting().onDestroyed(tabToBeClosed);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
index eab2172..b3b6593 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
@@ -27,8 +27,8 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.asynctask.CustomShadowAsyncTask;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item;
+import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.SimpleListObservable;
 
 /**
  * Controller tests for the password accessory sheet.
@@ -43,7 +43,7 @@
     private ListObservable.ListObserver<Void> mMockItemListObserver;
 
     private PasswordAccessorySheetCoordinator mCoordinator;
-    private SimpleListObservable<Item> mModel;
+    private ListModel<Item> mModel;
 
     @Before
     public void setUp() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java
index 727c11af..89ea3457 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java
@@ -21,7 +21,7 @@
 import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
 
 /**
- * Basic test ensuring the {@link SimpleListObservable} notifies listeners properly.
+ * Basic test ensuring the {@link ListModel} notifies listeners properly.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
@@ -29,7 +29,7 @@
     @Mock
     private ListObserver<Void> mObserver;
 
-    private SimpleListObservable<Integer> mIntegerList = new SimpleListObservable<>();
+    private ListModel<Integer> mIntegerList = new ListModel<>();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index add49b1..965836a 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-70.0.3523.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-70.0.3524.2_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 51effba..0f4b800e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1582,8 +1582,8 @@
     "usb/usb_chooser_controller.h",
     "usb/usb_tab_helper.cc",
     "usb/usb_tab_helper.h",
-    "usb/web_usb_chooser_service.cc",
-    "usb/web_usb_chooser_service.h",
+    "usb/web_usb_chooser.cc",
+    "usb/web_usb_chooser.h",
     "usb/web_usb_histograms.cc",
     "usb/web_usb_histograms.h",
     "usb/web_usb_permission_provider.cc",
@@ -2294,8 +2294,8 @@
       "android/trusted_cdn.cc",
       "android/trusted_cdn.h",
       "android/url_utilities.cc",
-      "android/usb/web_usb_chooser_service_android.cc",
-      "android/usb/web_usb_chooser_service_android.h",
+      "android/usb/web_usb_chooser_android.cc",
+      "android/usb/web_usb_chooser_android.h",
       "android/warmup_manager.cc",
       "android/web_contents_factory.cc",
       "android/webapk/chrome_webapk_host.cc",
@@ -2961,8 +2961,8 @@
       "upgrade_detector/upgrade_detector.cc",
       "upgrade_detector/upgrade_detector.h",
       "upgrade_detector/upgrade_observer.h",
-      "usb/web_usb_chooser_service_desktop.cc",
-      "usb/web_usb_chooser_service_desktop.h",
+      "usb/web_usb_chooser_desktop.cc",
+      "usb/web_usb_chooser_desktop.h",
       "usb/web_usb_detector.cc",
       "usb/web_usb_detector.h",
     ]
diff --git a/chrome/browser/android/usb/web_usb_chooser_android.cc b/chrome/browser/android/usb/web_usb_chooser_android.cc
new file mode 100644
index 0000000..8fd1a197
--- /dev/null
+++ b/chrome/browser/android/usb/web_usb_chooser_android.cc
@@ -0,0 +1,32 @@
+// 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/android/usb/web_usb_chooser_android.h"
+
+#include <utility>
+
+#include "chrome/browser/ui/android/usb_chooser_dialog_android.h"
+#include "chrome/browser/usb/usb_chooser_controller.h"
+
+WebUsbChooserAndroid::WebUsbChooserAndroid(
+    content::RenderFrameHost* render_frame_host)
+    : WebUsbChooser(render_frame_host), weak_factory_(this) {}
+
+WebUsbChooserAndroid::~WebUsbChooserAndroid() {}
+
+void WebUsbChooserAndroid::ShowChooser(
+    std::unique_ptr<UsbChooserController> controller) {
+  dialog_ = UsbChooserDialogAndroid::Create(
+      render_frame_host(), std::move(controller),
+      base::BindOnce(&WebUsbChooserAndroid::OnDialogClosed,
+                     base::Unretained(this)));
+}
+
+void WebUsbChooserAndroid::OnDialogClosed() {
+  dialog_.reset();
+}
+
+base::WeakPtr<WebUsbChooser> WebUsbChooserAndroid::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
diff --git a/chrome/browser/android/usb/web_usb_chooser_android.h b/chrome/browser/android/usb/web_usb_chooser_android.h
new file mode 100644
index 0000000..e76f9f57
--- /dev/null
+++ b/chrome/browser/android/usb/web_usb_chooser_android.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef CHROME_BROWSER_ANDROID_USB_WEB_USB_CHOOSER_ANDROID_H_
+#define CHROME_BROWSER_ANDROID_USB_WEB_USB_CHOOSER_ANDROID_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/usb/web_usb_chooser.h"
+
+class UsbChooserController;
+class UsbChooserDialogAndroid;
+
+// Android implementation of the WebUsbChooser interface.
+// This interface can be used by a webpage to request permission from user
+// to access a certain device.
+class WebUsbChooserAndroid : public WebUsbChooser {
+ public:
+  explicit WebUsbChooserAndroid(content::RenderFrameHost* render_frame_host);
+  ~WebUsbChooserAndroid() override;
+
+  // WebUsbChooser implementation
+  void ShowChooser(std::unique_ptr<UsbChooserController> controller) override;
+  base::WeakPtr<WebUsbChooser> GetWeakPtr() override;
+
+ private:
+  void OnDialogClosed();
+
+  // Only a single dialog can be shown at a time.
+  std::unique_ptr<UsbChooserDialogAndroid> dialog_;
+
+  base::WeakPtrFactory<WebUsbChooserAndroid> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebUsbChooserAndroid);
+};
+
+#endif  // CHROME_BROWSER_ANDROID_USB_WEB_USB_CHOOSER_ANDROID_H_
diff --git a/chrome/browser/android/usb/web_usb_chooser_service_android.cc b/chrome/browser/android/usb/web_usb_chooser_service_android.cc
deleted file mode 100644
index 539f47c8..0000000
--- a/chrome/browser/android/usb/web_usb_chooser_service_android.cc
+++ /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.
-
-#include "chrome/browser/android/usb/web_usb_chooser_service_android.h"
-
-#include <utility>
-
-#include "chrome/browser/ui/android/usb_chooser_dialog_android.h"
-#include "chrome/browser/usb/usb_chooser_controller.h"
-#include "content/public/browser/browser_thread.h"
-
-WebUsbChooserServiceAndroid::WebUsbChooserServiceAndroid(
-    content::RenderFrameHost* render_frame_host)
-    : WebUsbChooserService(render_frame_host) {}
-
-WebUsbChooserServiceAndroid::~WebUsbChooserServiceAndroid() {}
-
-void WebUsbChooserServiceAndroid::ShowChooser(
-    std::unique_ptr<UsbChooserController> controller) {
-  dialog_ = UsbChooserDialogAndroid::Create(
-      render_frame_host(), std::move(controller),
-      base::BindOnce(&WebUsbChooserServiceAndroid::OnDialogClosed,
-                     base::Unretained(this)));
-}
-
-void WebUsbChooserServiceAndroid::OnDialogClosed() {
-  dialog_.reset();
-}
diff --git a/chrome/browser/android/usb/web_usb_chooser_service_android.h b/chrome/browser/android/usb/web_usb_chooser_service_android.h
deleted file mode 100644
index 00ab9f1..0000000
--- a/chrome/browser/android/usb/web_usb_chooser_service_android.h
+++ /dev/null
@@ -1,38 +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.
-
-#ifndef CHROME_BROWSER_ANDROID_USB_WEB_USB_CHOOSER_SERVICE_ANDROID_H_
-#define CHROME_BROWSER_ANDROID_USB_WEB_USB_CHOOSER_SERVICE_ANDROID_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "chrome/browser/usb/web_usb_chooser_service.h"
-
-class UsbChooserController;
-class UsbChooserDialogAndroid;
-
-// Implementation of the public device::usb::ChooserService interface.
-// This interface can be used by a webpage to request permission from user
-// to access a certain device.
-class WebUsbChooserServiceAndroid : public WebUsbChooserService {
- public:
-  explicit WebUsbChooserServiceAndroid(
-      content::RenderFrameHost* render_frame_host);
-  ~WebUsbChooserServiceAndroid() override;
-
-  // WebUsbChooserService implementation
-  void ShowChooser(std::unique_ptr<UsbChooserController> controller) override;
-
- private:
-  void OnDialogClosed();
-
-  // Only a single dialog can be shown at a time.
-  std::unique_ptr<UsbChooserDialogAndroid> dialog_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebUsbChooserServiceAndroid);
-};
-
-#endif  // CHROME_BROWSER_ANDROID_USB_WEB_USB_CHOOSER_SERVICE_ANDROID_H_
diff --git a/chrome/browser/android/vr/BUILD.gn b/chrome/browser/android/vr/BUILD.gn
index 3d80b3b..d161966 100644
--- a/chrome/browser/android/vr/BUILD.gn
+++ b/chrome/browser/android/vr/BUILD.gn
@@ -32,6 +32,8 @@
     "metrics_util_android.cc",
     "metrics_util_android.h",
     "register_jni.h",
+    "render_loop_factory.cc",
+    "render_loop_factory.h",
     "scoped_gpu_trace.cc",
     "scoped_gpu_trace.h",
     "vr_controller.cc",
diff --git a/chrome/browser/android/vr/gvr_controller_delegate.cc b/chrome/browser/android/vr/gvr_controller_delegate.cc
index 94d77c30..6dc08616 100644
--- a/chrome/browser/android/vr/gvr_controller_delegate.cc
+++ b/chrome/browser/android/vr/gvr_controller_delegate.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "chrome/browser/android/vr/gl_browser_interface.h"
+#include "chrome/browser/android/vr/vr_controller.h"
 #include "chrome/browser/vr/input_event.h"
 #include "chrome/browser/vr/model/controller_model.h"
 #include "chrome/browser/vr/pose_util.h"
@@ -18,10 +19,9 @@
 
 namespace vr {
 
-GvrControllerDelegate::GvrControllerDelegate(
-    std::unique_ptr<VrController> controller,
-    GlBrowserInterface* browser)
-    : controller_(std::move(controller)), browser_(browser) {}
+GvrControllerDelegate::GvrControllerDelegate(gvr::GvrApi* gvr_api,
+                                             GlBrowserInterface* browser)
+    : controller_(std::make_unique<VrController>(gvr_api)), browser_(browser) {}
 
 GvrControllerDelegate::~GvrControllerDelegate() = default;
 
diff --git a/chrome/browser/android/vr/gvr_controller_delegate.h b/chrome/browser/android/vr/gvr_controller_delegate.h
index 932ef92..2e0eed1 100644
--- a/chrome/browser/android/vr/gvr_controller_delegate.h
+++ b/chrome/browser/android/vr/gvr_controller_delegate.h
@@ -11,6 +11,10 @@
 #include "chrome/browser/android/vr/vr_controller.h"
 #include "chrome/browser/vr/controller_delegate.h"
 
+namespace gvr {
+class GvrApi;
+}
+
 namespace vr {
 
 class GestureDetector;
@@ -18,8 +22,7 @@
 
 class GvrControllerDelegate : public ControllerDelegate {
  public:
-  GvrControllerDelegate(std::unique_ptr<VrController> controller,
-                        GlBrowserInterface* browser);
+  GvrControllerDelegate(gvr::GvrApi* gvr_api, GlBrowserInterface* browser);
   ~GvrControllerDelegate() override;
 
   // ControllerDelegate implementation.
diff --git a/chrome/browser/android/vr/gvr_keyboard_delegate.h b/chrome/browser/android/vr/gvr_keyboard_delegate.h
index 8c5457f..aac426c 100644
--- a/chrome/browser/android/vr/gvr_keyboard_delegate.h
+++ b/chrome/browser/android/vr/gvr_keyboard_delegate.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_ANDROID_VR_GVR_KEYBOARD_DELEGATE_H_
 #define CHROME_BROWSER_ANDROID_VR_GVR_KEYBOARD_DELEGATE_H_
 
+#include <memory>
+
 #include "base/callback.h"
 #include "base/macros.h"
 #include "chrome/browser/vr/keyboard_delegate.h"
@@ -13,8 +15,6 @@
 
 namespace vr {
 
-struct TextInputInfo;
-
 class GvrKeyboardDelegate : public KeyboardDelegate {
  public:
   // Constructs a GvrKeyboardDelegate by dynamically loading the GVR keyboard
@@ -22,12 +22,11 @@
   static std::unique_ptr<GvrKeyboardDelegate> Create();
   ~GvrKeyboardDelegate() override;
 
-  void SetUiInterface(KeyboardUiInterface* ui);
-
   typedef int32_t EventType;
   typedef base::RepeatingCallback<void(EventType)> OnEventCallback;
 
   // KeyboardDelegate implementation.
+  void SetUiInterface(KeyboardUiInterface* ui) override;
   void OnBeginFrame() override;
   void ShowKeyboard() override;
   void HideKeyboard() override;
@@ -41,9 +40,8 @@
   bool SupportsSelection() override;
   void OnButtonDown(const gfx::PointF& position) override;
   void OnButtonUp(const gfx::PointF& position) override;
-
   // Called to update GVR keyboard with the given text input info.
-  void UpdateInput(const TextInputInfo& info);
+  void UpdateInput(const TextInputInfo& info) override;
 
  private:
   GvrKeyboardDelegate();
diff --git a/chrome/browser/android/vr/render_loop_factory.cc b/chrome/browser/android/vr/render_loop_factory.cc
new file mode 100644
index 0000000..3f7570cc
--- /dev/null
+++ b/chrome/browser/android/vr/render_loop_factory.cc
@@ -0,0 +1,72 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/vr/render_loop_factory.h"
+
+#include <utility>
+
+#include "chrome/browser/android/vr/gvr_controller_delegate.h"
+#include "chrome/browser/android/vr/gvr_keyboard_delegate.h"
+#include "chrome/browser/android/vr/vr_gl_thread.h"
+#include "chrome/browser/android/vr/vr_shell_gl.h"
+#include "chrome/browser/vr/sounds_manager_audio_delegate.h"
+#include "chrome/browser/vr/text_input_delegate.h"
+#include "chrome/browser/vr/ui_factory.h"
+
+namespace vr {
+
+RenderLoopFactory::Params::Params(
+    gvr::GvrApi* gvr_api,
+    const UiInitialState& ui_initial_state,
+    bool reprojected_rendering,
+    bool daydream_support,
+    bool pause_content,
+    bool low_density,
+    base::WaitableEvent* gl_surface_created_event,
+    base::OnceCallback<gfx::AcceleratedWidget()> surface_callback)
+    : gvr_api(gvr_api),
+      ui_initial_state(ui_initial_state),
+      reprojected_rendering(reprojected_rendering),
+      daydream_support(daydream_support),
+      pause_content(pause_content),
+      low_density(low_density),
+      gl_surface_created_event(gl_surface_created_event),
+      surface_callback(std::move(surface_callback)) {}
+
+RenderLoopFactory::Params::~Params() = default;
+
+std::unique_ptr<VrShellGl> RenderLoopFactory::Create(
+    VrGLThread* vr_gl_thread,
+    std::unique_ptr<Params> params) {
+  DCHECK(params);
+  auto keyboard_delegate = GvrKeyboardDelegate::Create();
+  auto text_input_delegate = std::make_unique<TextInputDelegate>();
+  if (!keyboard_delegate) {
+    params->ui_initial_state.needs_keyboard_update = true;
+  } else {
+    text_input_delegate->SetUpdateInputCallback(
+        base::BindRepeating(&KeyboardDelegate::UpdateInput,
+                            base::Unretained(keyboard_delegate.get())));
+  }
+  auto audio_delegate = std::make_unique<SoundsManagerAudioDelegate>();
+  auto ui = UiFactory::Create(
+      vr_gl_thread, vr_gl_thread, std::move(keyboard_delegate),
+      std::move(text_input_delegate), std::move(audio_delegate),
+      params->ui_initial_state);
+  auto controller_delegate =
+      std::make_unique<GvrControllerDelegate>(params->gvr_api, vr_gl_thread);
+  auto vr_shell_gl = std::make_unique<VrShellGl>(
+      vr_gl_thread, std::move(ui), std::move(controller_delegate),
+      params->gvr_api, params->reprojected_rendering, params->daydream_support,
+      params->ui_initial_state.in_web_vr, params->pause_content,
+      params->low_density);
+  vr_gl_thread->task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&VrShellGl::Init, vr_shell_gl->GetWeakPtr(),
+                     base::Unretained(params->gl_surface_created_event),
+                     base::Passed(std::move(params->surface_callback))));
+  return vr_shell_gl;
+}
+
+}  // namespace vr
diff --git a/chrome/browser/android/vr/render_loop_factory.h b/chrome/browser/android/vr/render_loop_factory.h
new file mode 100644
index 0000000..ea6adb0
--- /dev/null
+++ b/chrome/browser/android/vr/render_loop_factory.h
@@ -0,0 +1,57 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_VR_RENDER_LOOP_FACTORY_H_
+#define CHROME_BROWSER_ANDROID_VR_RENDER_LOOP_FACTORY_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "chrome/browser/vr/ui_initial_state.h"
+#include "chrome/browser/vr/vr_export.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gvr {
+class GvrApi;
+}
+
+namespace base {
+class WaitableEvent;
+}
+
+namespace vr {
+
+class VrGLThread;
+class VrShellGl;
+
+class VR_EXPORT RenderLoopFactory {
+ public:
+  struct VR_EXPORT Params {
+    Params(gvr::GvrApi* gvr_api,
+           const UiInitialState& ui_initial_state,
+           bool reprojected_rendering,
+           bool daydream_support,
+           bool pause_content,
+           bool low_density,
+           base::WaitableEvent* gl_surface_created_event,
+           base::OnceCallback<gfx::AcceleratedWidget()> surface_callback);
+    ~Params();
+    gvr::GvrApi* gvr_api;
+    UiInitialState ui_initial_state;
+    bool reprojected_rendering;
+    bool daydream_support;
+    bool pause_content;
+    bool low_density;
+    base::WaitableEvent* gl_surface_created_event;
+    base::OnceCallback<gfx::AcceleratedWidget()> surface_callback;
+  };
+
+  // TODO(acondor): Build an instance of RenderLoop owning VrShellGl.
+  static std::unique_ptr<VrShellGl> Create(VrGLThread* vr_gl_thread,
+                                           std::unique_ptr<Params> params);
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_ANDROID_VR_RENDER_LOOP_FACTORY_H_
diff --git a/chrome/browser/android/vr/vr_controller.cc b/chrome/browser/android/vr/vr_controller.cc
index 4e72be2..c0c6bc9 100644
--- a/chrome/browser/android/vr/vr_controller.cc
+++ b/chrome/browser/android/vr/vr_controller.cc
@@ -52,13 +52,11 @@
 
 }  // namespace
 
-VrController::VrController(gvr_context* gvr_context)
-    : previous_button_states_{0} {
+VrController::VrController(gvr::GvrApi* gvr_api)
+    : gvr_api_(gvr_api), previous_button_states_{0} {
   DVLOG(1) << __FUNCTION__ << "=" << this;
-  CHECK(gvr_context != nullptr) << "invalid gvr_context";
   controller_api_ = std::make_unique<gvr::ControllerApi>();
   controller_state_ = std::make_unique<gvr::ControllerState>();
-  gvr_api_ = gvr::GvrApi::WrapNonOwned(gvr_context);
 
   int32_t options = gvr::ControllerApi::DefaultOptions();
 
@@ -69,7 +67,7 @@
   options |= GVR_CONTROLLER_ENABLE_GYRO;
   options |= GVR_CONTROLLER_ENABLE_ACCEL;
 
-  CHECK(controller_api_->Init(options, gvr_context));
+  CHECK(controller_api_->Init(options, gvr_api_->cobj()));
   controller_api_->Resume();
 
   handedness_ = gvr_api_->GetUserPrefs().GetControllerHandedness();
diff --git a/chrome/browser/android/vr/vr_controller.h b/chrome/browser/android/vr/vr_controller.h
index 9c16ecba..061a66c 100644
--- a/chrome/browser/android/vr/vr_controller.h
+++ b/chrome/browser/android/vr/vr_controller.h
@@ -27,6 +27,7 @@
 
 namespace gvr {
 class ControllerState;
+class GvrApi;
 }
 
 namespace vr {
@@ -37,7 +38,7 @@
 class VrController : public PlatformController {
  public:
   // Controller API entry point.
-  explicit VrController(gvr_context* gvr_context);
+  explicit VrController(gvr::GvrApi* gvr_api);
   ~VrController() override;
 
   // Must be called when the Activity gets OnResume().
@@ -97,7 +98,7 @@
   // The last controller state (updated once per frame).
   std::unique_ptr<gvr::ControllerState> controller_state_;
 
-  std::unique_ptr<gvr::GvrApi> gvr_api_;
+  gvr::GvrApi* gvr_api_;
 
   std::unique_ptr<GestureDetector> gesture_detector_;
 
diff --git a/chrome/browser/android/vr/vr_gl_thread.cc b/chrome/browser/android/vr/vr_gl_thread.cc
index 84c252b..4ec68a7 100644
--- a/chrome/browser/android/vr/vr_gl_thread.cc
+++ b/chrome/browser/android/vr/vr_gl_thread.cc
@@ -27,25 +27,27 @@
 VrGLThread::VrGLThread(
     const base::WeakPtr<VrShell>& weak_vr_shell,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
-    base::WaitableEvent* gl_surface_created_event,
     gvr_context* gvr_api,
     const UiInitialState& ui_initial_state,
     bool reprojected_rendering,
     bool daydream_support,
     bool pause_content,
     bool low_density,
+    base::WaitableEvent* gl_surface_created_event,
     base::OnceCallback<gfx::AcceleratedWidget()> surface_callback)
     : base::android::JavaHandlerThread("VrShellGL"),
       weak_vr_shell_(weak_vr_shell),
       main_thread_task_runner_(std::move(main_thread_task_runner)),
-      gl_surface_created_event_(gl_surface_created_event),
-      gvr_api_(gvr_api),
-      ui_initial_state_(ui_initial_state),
-      reprojected_rendering_(reprojected_rendering),
-      daydream_support_(daydream_support),
-      pause_content_(pause_content),
-      low_density_(low_density),
-      surface_callback_(std::move(surface_callback)) {}
+      gvr_api_(gvr::GvrApi::WrapNonOwned(gvr_api)),
+      factory_params_(std::make_unique<RenderLoopFactory::Params>(
+          gvr_api_.get(),
+          ui_initial_state,
+          reprojected_rendering,
+          daydream_support,
+          pause_content,
+          low_density,
+          gl_surface_created_event,
+          std::move(surface_callback))) {}
 
 VrGLThread::~VrGLThread() {
   Stop();
@@ -61,40 +63,11 @@
 }
 
 void VrGLThread::Init() {
-  keyboard_delegate_ = GvrKeyboardDelegate::Create();
-  text_input_delegate_ = std::make_unique<TextInputDelegate>();
-  if (!keyboard_delegate_.get())
-    ui_initial_state_.needs_keyboard_update = true;
-
-  audio_delegate_ = std::make_unique<SoundsManagerAudioDelegate>();
-
-  auto ui = UiFactory::Create(this, this, keyboard_delegate_.get(),
-                              text_input_delegate_.get(), audio_delegate_.get(),
-                              ui_initial_state_);
-  text_input_delegate_->SetRequestFocusCallback(base::BindRepeating(
-      &UiInterface::RequestFocus, base::Unretained(ui.get())));
-  text_input_delegate_->SetRequestUnfocusCallback(base::BindRepeating(
-      &UiInterface::RequestUnfocus, base::Unretained(ui.get())));
-  if (keyboard_delegate_.get()) {
-    keyboard_delegate_->SetUiInterface(ui.get());
-    text_input_delegate_->SetUpdateInputCallback(
-        base::BindRepeating(&GvrKeyboardDelegate::UpdateInput,
-                            base::Unretained(keyboard_delegate_.get())));
-  }
-
-  vr_shell_gl_ = std::make_unique<VrShellGl>(
-      this, std::move(ui), gvr_api_, reprojected_rendering_, daydream_support_,
-      ui_initial_state_.in_web_vr, pause_content_, low_density_);
-
+  vr_shell_gl_ = RenderLoopFactory::Create(this, std::move(factory_params_));
   weak_browser_ui_ = vr_shell_gl_->GetBrowserUiWeakPtr();
-  task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&VrShellGl::Init, vr_shell_gl_->GetWeakPtr(),
-                                base::Unretained(gl_surface_created_event_),
-                                base::Passed(std::move(surface_callback_))));
 }
 
 void VrGLThread::CleanUp() {
-  audio_delegate_.reset();
   vr_shell_gl_.reset();
 }
 
diff --git a/chrome/browser/android/vr/vr_gl_thread.h b/chrome/browser/android/vr/vr_gl_thread.h
index 3d5db8f3..31408f4 100644
--- a/chrome/browser/android/vr/vr_gl_thread.h
+++ b/chrome/browser/android/vr/vr_gl_thread.h
@@ -13,25 +13,27 @@
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/android/vr/gl_browser_interface.h"
 #include "chrome/browser/android/vr/gvr_keyboard_delegate.h"
+#include "chrome/browser/android/vr/render_loop_factory.h"
 #include "chrome/browser/vr/browser_ui_interface.h"
 #include "chrome/browser/vr/model/omnibox_suggestions.h"
 #include "chrome/browser/vr/model/sound_id.h"
 #include "chrome/browser/vr/platform_input_handler.h"
 #include "chrome/browser/vr/text_input_delegate.h"
 #include "chrome/browser/vr/ui_browser_interface.h"
-#include "chrome/browser/vr/ui_initial_state.h"
 #include "chrome/browser/vr/ui_test_input.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
-#include "ui/gfx/native_widget_types.h"
 
 namespace base {
 class Version;
 class WaitableEvent;
 }  // namespace base
 
+namespace gvr {
+class GvrApi;
+}
+
 namespace vr {
 
-class AudioDelegate;
 class VrInputConnection;
 class VrShell;
 class VrShellGl;
@@ -45,13 +47,13 @@
   VrGLThread(
       const base::WeakPtr<VrShell>& weak_vr_shell,
       scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
-      base::WaitableEvent* gl_surface_created_event,
       gvr_context* gvr_api,
       const UiInitialState& ui_initial_state,
       bool reprojected_rendering,
       bool daydream_support,
       bool pause_content,
       bool low_density,
+      base::WaitableEvent* gl_surface_created_event,
       base::OnceCallback<gfx::AcceleratedWidget()> surface_callback);
 
   ~VrGLThread() override;
@@ -156,12 +158,6 @@
   bool OnMainThread() const;
   bool OnGlThread() const;
 
-  // Created on GL thread.
-  std::unique_ptr<VrShellGl> vr_shell_gl_;
-  std::unique_ptr<GvrKeyboardDelegate> keyboard_delegate_;
-  std::unique_ptr<TextInputDelegate> text_input_delegate_;
-  std::unique_ptr<AudioDelegate> audio_delegate_;
-
   base::WeakPtr<VrShell> weak_vr_shell_;
   base::WeakPtr<BrowserUiInterface> weak_browser_ui_;
   base::WeakPtr<VrInputConnection> weak_input_connection_;
@@ -170,16 +166,14 @@
   // VrGlThread. So it is safe to use raw pointer here.
   VrInputConnection* input_connection_ = nullptr;
 
-  // This state is used for initializing vr_shell_gl_.
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
-  base::WaitableEvent* gl_surface_created_event_;
-  gvr_context* gvr_api_;
-  UiInitialState ui_initial_state_;
-  bool reprojected_rendering_;
-  bool daydream_support_;
-  bool pause_content_;
-  bool low_density_;
-  base::OnceCallback<gfx::AcceleratedWidget()> surface_callback_;
+
+  // Created on GL thread.
+  std::unique_ptr<VrShellGl> vr_shell_gl_;
+  std::unique_ptr<gvr::GvrApi> gvr_api_;
+
+  // This state is used for initializing the RenderLoop.
+  std::unique_ptr<RenderLoopFactory::Params> factory_params_;
 
   DISALLOW_COPY_AND_ASSIGN(VrGLThread);
 };
diff --git a/chrome/browser/android/vr/vr_shell.cc b/chrome/browser/android/vr/vr_shell.cc
index b80e4e4e..fced933 100644
--- a/chrome/browser/android/vr/vr_shell.cc
+++ b/chrome/browser/android/vr/vr_shell.cc
@@ -6,6 +6,7 @@
 
 #include <android/native_window_jni.h>
 
+#include <algorithm>
 #include <string>
 #include <utility>
 
@@ -168,10 +169,10 @@
       base::BindOnce(&VrShell::GetRenderSurface, base::Unretained(this));
 
   gl_thread_ = std::make_unique<VrGLThread>(
-      weak_ptr_factory_.GetWeakPtr(), main_thread_task_runner_,
-      &gl_surface_created_event_, gvr_api, ui_initial_state,
-      reprojected_rendering_, HasDaydreamSupport(env), pause_content,
-      low_density, std::move(surface_callback));
+      weak_ptr_factory_.GetWeakPtr(), main_thread_task_runner_, gvr_api,
+      ui_initial_state, reprojected_rendering_, HasDaydreamSupport(env),
+      pause_content, low_density, &gl_surface_created_event_,
+      std::move(surface_callback));
   ui_ = gl_thread_.get();
   toolbar_ = std::make_unique<ToolbarHelper>(ui_, this);
   autocomplete_controller_ =
@@ -331,8 +332,7 @@
 
 void VrShell::PostToGlThread(const base::Location& from_here,
                              base::OnceClosure task) {
-  gl_thread_->message_loop()->task_runner()->PostTask(from_here,
-                                                      std::move(task));
+  gl_thread_->task_runner()->PostTask(from_here, std::move(task));
 }
 
 void VrShell::Navigate(GURL url, NavigationMethod method) {
diff --git a/chrome/browser/android/vr/vr_shell_gl.cc b/chrome/browser/android/vr/vr_shell_gl.cc
index 43de3cd..3d2f556d 100644
--- a/chrome/browser/android/vr/vr_shell_gl.cc
+++ b/chrome/browser/android/vr/vr_shell_gl.cc
@@ -9,7 +9,6 @@
 
 #include "base/android/android_hardware_buffer_compat.h"
 #include "base/android/jni_android.h"
-#include "base/bind_helpers.h"
 #include "base/callback_helpers.h"
 #include "base/containers/queue.h"
 #include "base/metrics/field_trial_params.h"
@@ -21,7 +20,6 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "chrome/browser/android/vr/gl_browser_interface.h"
-#include "chrome/browser/android/vr/gvr_controller_delegate.h"
 #include "chrome/browser/android/vr/gvr_util.h"
 #include "chrome/browser/android/vr/mailbox_to_surface_bridge.h"
 #include "chrome/browser/android/vr/metrics_util_android.h"
@@ -29,6 +27,7 @@
 #include "chrome/browser/android/vr/vr_controller.h"
 #include "chrome/browser/android/vr/vr_shell.h"
 #include "chrome/browser/vr/assets_loader.h"
+#include "chrome/browser/vr/controller_delegate.h"
 #include "chrome/browser/vr/gl_texture_location.h"
 #include "chrome/browser/vr/metrics/session_metrics_helper.h"
 #include "chrome/browser/vr/model/assets.h"
@@ -186,15 +185,21 @@
 
 VrShellGl::VrShellGl(GlBrowserInterface* browser,
                      std::unique_ptr<UiInterface> ui,
-                     gvr_context* gvr_api,
+                     std::unique_ptr<ControllerDelegate> controller_delegate,
+                     gvr::GvrApi* gvr_api,
                      bool reprojected_rendering,
                      bool daydream_support,
                      bool start_in_web_vr_mode,
                      bool pause_content,
                      bool low_density)
-    : RenderLoop(std::move(ui), browser, this, kWebVRSlidingAverageSize),
+    : RenderLoop(std::move(ui),
+                 this,
+                 std::move(controller_delegate),
+                 browser,
+                 kWebVRSlidingAverageSize),
       webvr_vsync_align_(
           base::FeatureList::IsEnabled(features::kWebVrVsyncAlign)),
+      gvr_api_(gvr_api),
       low_density_(low_density),
       web_vr_mode_(start_in_web_vr_mode),
       surfaceless_rendering_(reprojected_rendering),
@@ -212,9 +217,7 @@
       webvr_acquire_time_(kWebVRSlidingAverageSize),
       webvr_submit_time_(kWebVRSlidingAverageSize),
       weak_ptr_factory_(this) {
-  GvrInit(gvr_api);
-  set_controller_delegate(std::make_unique<GvrControllerDelegate>(
-      std::make_unique<VrController>(gvr_api), browser_));
+  GvrInit();
 }
 
 VrShellGl::~VrShellGl() {
@@ -239,20 +242,21 @@
     ForceExitVr();
     return;
   }
+  scoped_refptr<gl::GLSurface> surface;
   if (window) {
     DCHECK(!surfaceless_rendering_);
-    surface_ = gl::init::CreateViewGLSurface(window);
+    surface = gl::init::CreateViewGLSurface(window);
   } else {
     DCHECK(surfaceless_rendering_);
-    surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
+    surface = gl::init::CreateOffscreenGLSurface(gfx::Size());
   }
-  if (!surface_.get()) {
+  if (!surface.get()) {
     LOG(ERROR) << "gl::init::CreateOffscreenGLSurface failed";
     ForceExitVr();
     return;
   }
 
-  if (!BaseCompositorDelegate::Initialize(surface_)) {
+  if (!BaseCompositorDelegate::Initialize(surface)) {
     ForceExitVr();
     return;
   }
@@ -865,9 +869,7 @@
   ui_->OnWebVrTimeoutImminent();
 }
 
-void VrShellGl::GvrInit(gvr_context* gvr_api) {
-  gvr_api_ = gvr::GvrApi::WrapNonOwned(gvr_api);
-
+void VrShellGl::GvrInit() {
   MetricsUtilAndroid::LogVrViewerType(gvr_api_->GetViewerType());
 
   cardboard_ =
@@ -1180,7 +1182,7 @@
     WebXrFrame* frame = webxr_.GetProcessingFrame();
     render_info_.head_pose = frame->head_pose;
   } else {
-    device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(),
+    device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_,
                                                  &render_info_.head_pose);
   }
 
@@ -1534,8 +1536,7 @@
   // No need to swap buffers for surfaceless rendering.
   if (!surfaceless_rendering_) {
     // TODO(mthiesse): Support asynchronous SwapBuffers.
-    TRACE_EVENT0("gpu", "VrShellGl::SwapBuffers");
-    surface_->SwapBuffers(base::DoNothing());
+    SwapSurfaceBuffers();
   }
 
   // At this point, ShouldDrawWebVr and webvr_frame_processing_ may have become
@@ -1859,7 +1860,7 @@
     // When drawing WebVR, controller input doesn't need to be synchronized with
     // rendering as WebVR uses the gamepad api. To ensure we always handle input
     // like app button presses, process the controller here.
-    device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(),
+    device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_,
                                                  &render_info_.head_pose);
     input_states_.push_back(
         ProcessControllerInputForWebXr(render_info_, frame_time));
@@ -2040,7 +2041,7 @@
   gfx::Transform head_mat;
   TRACE_EVENT_BEGIN0("gpu", "VrShellGl::GetVRPosePtrWithNeckModel");
   device::mojom::VRPosePtr pose =
-      device::GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), &head_mat,
+      device::GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_, &head_mat,
                                                      prediction_nanos);
   TRACE_EVENT_END0("gpu", "VrShellGl::GetVRPosePtrWithNeckModel");
 
diff --git a/chrome/browser/android/vr/vr_shell_gl.h b/chrome/browser/android/vr/vr_shell_gl.h
index 2637a228..fbf79d7 100644
--- a/chrome/browser/android/vr/vr_shell_gl.h
+++ b/chrome/browser/android/vr/vr_shell_gl.h
@@ -59,6 +59,7 @@
 namespace vr {
 
 class BrowserUiInterface;
+class ControllerDelegate;
 class FPSMeter;
 class GlBrowserInterface;
 class MailboxToSurfaceBridge;
@@ -103,7 +104,8 @@
  public:
   VrShellGl(GlBrowserInterface* browser,
             std::unique_ptr<UiInterface> ui,
-            gvr_context* gvr_api,
+            std::unique_ptr<ControllerDelegate> controller_delegate,
+            gvr::GvrApi* gvr_api,
             bool reprojected_rendering,
             bool daydream_support,
             bool start_in_web_vr_mode,
@@ -163,7 +165,7 @@
 
  private:
   void InitializeGl(gfx::AcceleratedWidget surface);
-  void GvrInit(gvr_context* gvr_api);
+  void GvrInit();
 
   device::mojom::XRPresentationTransportOptionsPtr
   GetWebVrFrameTransportOptions(
@@ -284,7 +286,7 @@
   std::unique_ptr<gl::ScopedJavaSurface> ui_surface_;
   std::unique_ptr<gl::ScopedJavaSurface> content_overlay_surface_;
 
-  std::unique_ptr<gvr::GvrApi> gvr_api_;
+  gvr::GvrApi* gvr_api_;
   gvr::BufferViewportList viewport_list_;
   Viewport main_viewport_;
   Viewport webvr_viewport_;
diff --git a/chrome/browser/apps/BUILD.gn b/chrome/browser/apps/BUILD.gn
new file mode 100644
index 0000000..52efd75
--- /dev/null
+++ b/chrome/browser/apps/BUILD.gn
@@ -0,0 +1,21 @@
+# 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.
+
+import("//extensions/buildflags/buildflags.gni")
+
+# TODO(loyso): Remove this assertion. apps/ will be based off-extensions.
+assert(enable_extensions)
+
+source_set("apps") {
+  sources = [
+    "apps_launch.cc",
+    "apps_launch.h",
+  ]
+
+  deps = [
+    "//chrome/browser/apps/platform_apps",
+  ]
+
+  configs += [ "//build/config/compiler:wexit_time_destructors" ]
+}
diff --git a/chrome/browser/apps/apps_launch.cc b/chrome/browser/apps/apps_launch.cc
new file mode 100644
index 0000000..fe2677a
--- /dev/null
+++ b/chrome/browser/apps/apps_launch.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/apps_launch.h"
+
+#include "chrome/browser/apps/platform_apps/platform_app_launch.h"
+
+namespace apps {
+
+bool OpenApplicationWindow(Profile* profile,
+                           const std::string& app_id,
+                           const base::CommandLine& command_line,
+                           const base::FilePath& current_directory) {
+  // TODO(loyso): Redirect to web_app::WebAppProvider for BMO apps (PWAs).
+  return OpenExtensionApplicationWindow(profile, app_id, command_line,
+                                        current_directory);
+}
+
+bool OpenApplicationTab(Profile* profile, const std::string& app_id) {
+  // TODO(loyso): Redirect to web_app::WebAppProvider for BMO apps (Shortcuts).
+  return OpenExtensionApplicationTab(profile, app_id);
+}
+
+bool OpenApplicationWithReenablePrompt(
+    Profile* profile,
+    const std::string& app_id,
+    const base::CommandLine& command_line,
+    const base::FilePath& current_directory) {
+  return OpenExtensionApplicationWithReenablePrompt(
+      profile, app_id, command_line, current_directory);
+}
+
+bool OpenAppShortcutWindow(Profile* profile, const GURL& url) {
+  return OpenExtensionAppShortcutWindow(profile, url);
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/apps_launch.h b/chrome/browser/apps/apps_launch.h
new file mode 100644
index 0000000..498be0d
--- /dev/null
+++ b/chrome/browser/apps/apps_launch.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_APPS_LAUNCH_H_
+#define CHROME_BROWSER_APPS_APPS_LAUNCH_H_
+
+#include <string>
+
+class GURL;
+class Profile;
+
+namespace base {
+class CommandLine;
+class FilePath;
+}  // namespace base
+
+namespace apps {
+
+// Returns true if |app_id| was successfully opened in a window, and false
+// otherwise.
+bool OpenApplicationWindow(Profile* profile,
+                           const std::string& app_id,
+                           const base::CommandLine& command_line,
+                           const base::FilePath& current_directory);
+
+// Returns true if |app_id| was successfully opened in a tab, and false
+// otherwise.
+bool OpenApplicationTab(Profile* profile, const std::string& app_id);
+
+// Tries to open |app_id|, and prompts the user if the app is disabled. Returns
+// true if the app was successfully opened and false otherwise.
+bool OpenApplicationWithReenablePrompt(Profile* profile,
+                                       const std::string& app_id,
+                                       const base::CommandLine& command_line,
+                                       const base::FilePath& current_directory);
+
+// Returns true if |url| was successfully opened in a window, and false
+// otherwise.
+bool OpenAppShortcutWindow(Profile* profile, const GURL& url);
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APPS_LAUNCH_H_
diff --git a/chrome/browser/apps/platform_apps/BUILD.gn b/chrome/browser/apps/platform_apps/BUILD.gn
index b9486ce5..05382206a 100644
--- a/chrome/browser/apps/platform_apps/BUILD.gn
+++ b/chrome/browser/apps/platform_apps/BUILD.gn
@@ -22,6 +22,8 @@
     "browser_context_keyed_service_factories.h",
     "install_chrome_app.cc",
     "install_chrome_app.h",
+    "platform_app_launch.cc",
+    "platform_app_launch.h",
     "platform_app_navigation_redirector.cc",
     "platform_app_navigation_redirector.h",
     "shortcut_manager.cc",
diff --git a/chrome/browser/apps/platform_apps/platform_app_launch.cc b/chrome/browser/apps/platform_apps/platform_app_launch.cc
new file mode 100644
index 0000000..91c6020
--- /dev/null
+++ b/chrome/browser/apps/platform_apps/platform_app_launch.cc
@@ -0,0 +1,151 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/platform_apps/platform_app_launch.h"
+
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/launch_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/extensions/app_launch_params.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/common/extensions/extension_metrics.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+
+namespace apps {
+
+namespace {
+
+// Return true if launch for |app_id| is allowed.  Set
+// |out_app| to the app to open, and |out_launch_container|
+// to the type of window into which the app should be open.
+bool GetAppLaunchContainer(Profile* profile,
+                           const std::string& app_id,
+                           const extensions::Extension** out_app,
+                           extensions::LaunchContainer* out_launch_container) {
+  const extensions::Extension* app =
+      extensions::ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(
+          app_id);
+  // The app with id |app_id| may have been uninstalled.
+  if (!app)
+    return false;
+
+  // Don't launch platform apps in incognito mode.
+  if (profile->IsOffTheRecord() && app->is_platform_app())
+    return false;
+
+  // Look at preferences to find the right launch container. If no
+  // preference is set, launch as a window.
+  extensions::LaunchContainer launch_container = extensions::GetLaunchContainer(
+      extensions::ExtensionPrefs::Get(profile), app);
+
+  if (!extensions::util::IsNewBookmarkAppsEnabled() &&
+      !extensions::HasPreferredLaunchContainer(
+          extensions::ExtensionPrefs::Get(profile), app)) {
+    launch_container = extensions::LAUNCH_CONTAINER_WINDOW;
+  }
+
+  *out_app = app;
+  *out_launch_container = launch_container;
+  return true;
+}
+
+const extensions::Extension* GetPlatformApp(Profile* profile,
+                                            const std::string& app_id) {
+  const extensions::Extension* app =
+      extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
+          app_id, extensions::ExtensionRegistry::EVERYTHING);
+  return app && app->is_platform_app() ? app : nullptr;
+}
+
+void RecordCmdLineAppHistogram(extensions::Manifest::Type app_type) {
+  extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_CMD_LINE_APP,
+                                  app_type);
+}
+
+}  // namespace
+
+bool OpenExtensionApplicationWindow(Profile* profile,
+                                    const std::string& app_id,
+                                    const base::CommandLine& command_line,
+                                    const base::FilePath& current_directory) {
+  extensions::LaunchContainer launch_container;
+  const extensions::Extension* app;
+  if (!GetAppLaunchContainer(profile, app_id, &app, &launch_container))
+    return false;
+
+  if (launch_container == extensions::LAUNCH_CONTAINER_TAB)
+    return false;
+
+  RecordCmdLineAppHistogram(app->GetType());
+
+  ::AppLaunchParams params(profile, app, launch_container,
+                           WindowOpenDisposition::NEW_WINDOW,
+                           extensions::SOURCE_COMMAND_LINE);
+  params.command_line = command_line;
+  params.current_directory = current_directory;
+  content::WebContents* tab_in_app_window = ::OpenApplication(params);
+
+  // Platform apps fire off a launch event which may or may not open a window.
+  return tab_in_app_window != nullptr || ::CanLaunchViaEvent(app);
+}
+
+bool OpenExtensionApplicationTab(Profile* profile, const std::string& app_id) {
+  extensions::LaunchContainer launch_container;
+  const extensions::Extension* app;
+  if (!GetAppLaunchContainer(profile, app_id, &app, &launch_container))
+    return false;
+
+  // If the user doesn't want to open a tab, fail.
+  if (launch_container != extensions::LAUNCH_CONTAINER_TAB)
+    return false;
+
+  RecordCmdLineAppHistogram(app->GetType());
+
+  content::WebContents* app_tab = ::OpenApplication(
+      ::AppLaunchParams(profile, app, extensions::LAUNCH_CONTAINER_TAB,
+                        WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                        extensions::SOURCE_COMMAND_LINE));
+  return app_tab != nullptr;
+}
+
+bool OpenExtensionApplicationWithReenablePrompt(
+    Profile* profile,
+    const std::string& app_id,
+    const base::CommandLine& command_line,
+    const base::FilePath& current_directory) {
+  const extensions::Extension* app = GetPlatformApp(profile, app_id);
+  if (!app)
+    return false;
+
+  RecordCmdLineAppHistogram(extensions::Manifest::TYPE_PLATFORM_APP);
+  ::AppLaunchParams params(profile, app, extensions::LAUNCH_CONTAINER_NONE,
+                           WindowOpenDisposition::NEW_WINDOW,
+                           extensions::SOURCE_COMMAND_LINE);
+  params.command_line = command_line;
+  params.current_directory = current_directory;
+  ::OpenApplicationWithReenablePrompt(params);
+  return true;
+}
+
+bool OpenExtensionAppShortcutWindow(Profile* profile, const GURL& url) {
+  const extensions::Extension* app = extensions::ExtensionRegistry::Get(profile)
+                                         ->enabled_extensions()
+                                         .GetAppByURL(url);
+  if (app) {
+    RecordCmdLineAppHistogram(app->GetType());
+  } else {
+    extensions::RecordAppLaunchType(
+        extension_misc::APP_LAUNCH_CMD_LINE_APP_LEGACY,
+        extensions::Manifest::TYPE_HOSTED_APP);
+  }
+
+  content::WebContents* app_tab = ::OpenAppShortcutWindow(profile, url);
+  return app_tab != nullptr;
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/platform_apps/platform_app_launch.h b/chrome/browser/apps/platform_apps/platform_app_launch.h
new file mode 100644
index 0000000..c2c2922
--- /dev/null
+++ b/chrome/browser/apps/platform_apps/platform_app_launch.h
@@ -0,0 +1,49 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_PLATFORM_APPS_PLATFORM_APP_LAUNCH_H_
+#define CHROME_BROWSER_APPS_PLATFORM_APPS_PLATFORM_APP_LAUNCH_H_
+
+#include <string>
+
+class GURL;
+class Profile;
+
+namespace base {
+class CommandLine;
+class FilePath;
+}  // namespace base
+
+namespace apps {
+
+// Tries to open an application window. If the app is specified to start in a
+// tab returns false to specify default processing. Returns true if |app_id| was
+// successfully opened in a window, and false otherwise.
+bool OpenExtensionApplicationWindow(Profile* profile,
+                                    const std::string& app_id,
+                                    const base::CommandLine& command_line,
+                                    const base::FilePath& current_directory);
+
+// If the user set a pref indicating that the app should open in a tab, open an
+// application tab. Returns true if |app_id| was successfully opened in a tab,
+// and false otherwise.
+bool OpenExtensionApplicationTab(Profile* profile, const std::string& app_id);
+
+// Tries to open |app_id|, and prompts the user if the app is disabled. Returns
+// true if the app was successfully opened and false otherwise.
+// Handles the case If |app_id| is a disabled or terminated platform app.
+bool OpenExtensionApplicationWithReenablePrompt(
+    Profile* profile,
+    const std::string& app_id,
+    const base::CommandLine& command_line,
+    const base::FilePath& current_directory);
+
+// Tries to open an application window by app's |url|.
+// Returns true if |url| was successfully opened in a window, and false
+// otherwise.
+bool OpenExtensionAppShortcutWindow(Profile* profile, const GURL& url);
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_PLATFORM_APPS_PLATFORM_APP_LAUNCH_H_
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 06d2a77..cc934951 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -248,7 +248,6 @@
 #include "content/public/common/url_loader_throttle.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/web_preferences.h"
-#include "device/usb/public/mojom/chooser_service.mojom.h"
 #include "device/vr/buildflags/buildflags.h"
 #include "extensions/buildflags/buildflags.h"
 #include "google_apis/gaia/gaia_urls.h"
@@ -4564,32 +4563,6 @@
   tab_helper->CreateWebUsbService(render_frame_host, std::move(request));
 }
 
-void ChromeContentBrowserClient::CreateUsbChooserService(
-    content::RenderFrameHost* render_frame_host,
-    device::mojom::UsbChooserServiceRequest request) {
-  if (!base::FeatureList::IsEnabled(features::kWebUsb))
-    return;
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  // WebUSB is not supported in Apps/Extensions. https://crbug.com/770896
-  if (render_frame_host->GetSiteInstance()->GetSiteURL().SchemeIs(
-          extensions::kExtensionScheme)) {
-    return;
-  }
-#endif
-
-  WebContents* web_contents =
-      WebContents::FromRenderFrameHost(render_frame_host);
-  if (!web_contents) {
-    NOTREACHED();
-    return;
-  }
-
-  UsbTabHelper* tab_helper =
-      UsbTabHelper::GetOrCreateForWebContents(web_contents);
-  tab_helper->CreateChooserService(render_frame_host, std::move(request));
-}
-
 std::unique_ptr<content::AuthenticatorRequestClientDelegate>
 ChromeContentBrowserClient::GetWebAuthenticationRequestDelegate(
     content::RenderFrameHost* render_frame_host) {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 62b57ea..d41a2a79 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -458,9 +458,6 @@
   void CreateWebUsbService(
       content::RenderFrameHost* render_frame_host,
       mojo::InterfaceRequest<blink::mojom::WebUsbService> request) override;
-  void CreateUsbChooserService(
-      content::RenderFrameHost* render_frame_host,
-      device::mojom::UsbChooserServiceRequest request) override;
   bool ShowPaymentHandlerWindow(
       content::BrowserContext* browser_context,
       const GURL& url,
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index 8e4df9d..3bb2623 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/grit/generated_resources.h"
 #include "extensions/browser/device_local_account_util.h"
 #include "extensions/common/extension.h"
@@ -648,6 +649,10 @@
 // contained in |kSafePermissionStrings| or |kSafePermissionDicts|.  Otherwise
 // returns false and logs all reasons for failure.
 bool IsSafeForPublicSession(const extensions::Extension* extension) {
+  // If Public Session restrictions are not enabled, just return true.
+  if (!profiles::ArePublicSessionRestrictionsEnabled())
+    return true;
+
   bool safe = true;
   if (!extension->is_extension() &&
       !extension->is_hosted_app() &&
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc
index 52087b65..93383e3 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
 #include "base/values.h"
+#include "chromeos/login/scoped_test_public_session_login_state.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_constants.h"
@@ -92,6 +93,8 @@
 TEST(DeviceLocalAccountManagementPolicyProviderTest, PublicSession) {
   DeviceLocalAccountManagementPolicyProvider
       provider(policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION);
+  // Set the login state to a public session.
+  ScopedTestPublicSessionLoginState login_state;
 
   // Verify that if an extension's location has been whitelisted for use in
   // public sessions, the extension can be installed.
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 5470174..300dcbb 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -214,8 +214,14 @@
                       TestCase("imageOpenGalleryOpenDrive"),
                       TestCase("imageOpenGalleryOpenDrive").EnableDriveFs()));
 
+// NaCl fails to compile zip plugin.pexe too often on ASAN, crbug.com/867738
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ZipFiles DISABLED_ZipFiles
+#else
+#define MAYBE_ZipFiles ZipFiles
+#endif
 WRAPPED_INSTANTIATE_TEST_CASE_P(
-    ZipFiles, /* zip_files.js */
+    MAYBE_ZipFiles, /* zip_files.js */
     FilesAppBrowserTest,
     ::testing::Values(ZipCase("zipFileOpenDownloads").InGuestMode(),
                       ZipCase("zipFileOpenDownloads"),
diff --git a/chrome/browser/chromeos/input_method/DEPS b/chrome/browser/chromeos/input_method/DEPS
index 000cc4c..22b3126 100644
--- a/chrome/browser/chromeos/input_method/DEPS
+++ b/chrome/browser/chromeos/input_method/DEPS
@@ -19,14 +19,6 @@
     "+ash/shell.h",
     "+ash/wm/window_util.h",
   ],
-  # TODO(mash): Fix these. https://crbug.com/756059
-  "input_method_engine\.cc": [
-    "+ash/shell.h",
-  ],
-  "mode_indicator_controller\.cc": [
-    "+ash/shell.h",
-    "+ash/wm/window_util.h",
-  ],
 
   # TODO(erikwright): Bring this list to zero.
   # Do not add to the list of temporarily-allowed dependencies below,
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.cc b/chrome/browser/chromeos/input_method/input_method_engine.cc
index 2691f04..bb2e129 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine.cc
@@ -8,7 +8,6 @@
 #include <memory>
 #include <utility>
 
-#include "ash/shell.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
@@ -331,10 +330,13 @@
   if (event->key_code() == ui::VKEY_UNKNOWN)
     event->set_key_code(ui::DomKeycodeToKeyboardCode(code));
 
-  ui::EventSink* sink =
-      ash::Shell::GetPrimaryRootWindow()->GetHost()->event_sink();
-  ui::EventDispatchDetails details = sink->OnEventFromSource(event);
-  return !details.dispatcher_destroyed;
+  ui::IMEInputContextHandlerInterface* input_context =
+      ui::IMEBridge::Get()->GetInputContextHandler();
+  if (!input_context)
+    return false;
+
+  input_context->SendKeyEvent(event);
+  return true;
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc b/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
index 3950b8f..4f331abc 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
@@ -44,7 +44,6 @@
     "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpToUpperIME";
 const char kAPIArgumentIMEID[] =
     "_ext_ime_iafoklpfplgfnoimmaejoeondnjnlcfpAPIArgumentIME";
-const char kExtensionID[] = "iafoklpfplgfnoimmaejoeondnjnlcfp";
 
 // InputMethod extension should work on 1)normal extension, 2)normal extension
 // in incognito mode 3)component extension.
@@ -465,30 +464,18 @@
         "    requestId : '0',"
         "    key : 'z',"
         "    code : 'KeyZ',"
-        "  },{"
-        "    type : 'keyup',"
-        "    requestId : '1',"
-        "    key : 'z',"
-        "    code : 'KeyZ',"
         "  }]"
         "});";
 
-    ExtensionTestMessageListener keyevent_listener_down(
-        std::string("onKeyEvent:") + kExtensionID +
-        ":keydown:z:KeyZ:false:false:false:false",
-        false);
-    ExtensionTestMessageListener keyevent_listener_up(
-        std::string("onKeyEvent:") + kExtensionID +
-        ":keyup:z:KeyZ:false:false:false:false",
-        false);
-
     ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
                                        send_key_events_test_script));
 
-    ASSERT_TRUE(keyevent_listener_down.WaitUntilSatisfied());
-    EXPECT_TRUE(keyevent_listener_down.was_satisfied());
-    ASSERT_TRUE(keyevent_listener_up.WaitUntilSatisfied());
-    EXPECT_TRUE(keyevent_listener_up.was_satisfied());
+    const ui::KeyEvent& key_event = mock_input_context->last_sent_key_event();
+    EXPECT_EQ(ui::ET_KEY_PRESSED, key_event.type());
+    EXPECT_EQ(L'z', key_event.GetCharacter());
+    EXPECT_EQ(ui::DomCode::US_Z, key_event.code());
+    EXPECT_EQ(ui::VKEY_Z, key_event.key_code());
+    EXPECT_EQ(0, key_event.flags());
   }
   {
     SCOPED_TRACE("sendKeyEvents test with keyCode");
@@ -499,12 +486,6 @@
         "chrome.input.ime.sendKeyEvents({"
         "  contextID: engineBridge.getFocusedContextID().contextID,"
         "  keyData : [{"
-        "    type : 'keydown',"
-        "    requestId : '2',"
-        "    key : 'a',"
-        "    code : 'KeyQ',"
-        "    keyCode : 0x41,"
-        "  },{"
         "    type : 'keyup',"
         "    requestId : '3',"
         "    key : 'a',"
@@ -513,22 +494,15 @@
         "  }]"
         "});";
 
-    ExtensionTestMessageListener keyevent_listener_down(
-        std::string("onKeyEvent:") + kExtensionID +
-        ":keydown:a:KeyQ:false:false:false:false",
-        false);
-    ExtensionTestMessageListener keyevent_listener_up(
-        std::string("onKeyEvent:") + kExtensionID +
-        ":keyup:a:KeyQ:false:false:false:false",
-        false);
-
     ASSERT_TRUE(content::ExecuteScript(host->host_contents(),
                                        send_key_events_test_script));
 
-    ASSERT_TRUE(keyevent_listener_down.WaitUntilSatisfied());
-    EXPECT_TRUE(keyevent_listener_down.was_satisfied());
-    ASSERT_TRUE(keyevent_listener_up.WaitUntilSatisfied());
-    EXPECT_TRUE(keyevent_listener_up.was_satisfied());
+    const ui::KeyEvent& key_event = mock_input_context->last_sent_key_event();
+    EXPECT_EQ(ui::ET_KEY_RELEASED, key_event.type());
+    EXPECT_EQ(L'a', key_event.GetCharacter());
+    EXPECT_EQ(ui::DomCode::US_Q, key_event.code());
+    EXPECT_EQ(ui::VKEY_A, key_event.key_code());
+    EXPECT_EQ(0, key_event.flags());
   }
   {
     SCOPED_TRACE("setComposition test");
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 1ee40bfa..ae0f0ba 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -13,7 +13,6 @@
 #include <utility>
 
 #include "ash/public/cpp/ash_features.h"
-#include "ash/public/interfaces/ime_info.mojom.h"
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/hash.h"
diff --git a/chrome/browser/chromeos/login/saml/saml_offline_signin_limiter_unittest.cc b/chrome/browser/chromeos/login/saml/saml_offline_signin_limiter_unittest.cc
index 58467cd3..2623264 100644
--- a/chrome/browser/chromeos/login/saml/saml_offline_signin_limiter_unittest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_offline_signin_limiter_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop_current.h"
-#include "base/run_loop.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/clock.h"
@@ -27,10 +26,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::_;
 using testing::Mock;
 using testing::Return;
 using testing::Sequence;
+using testing::_;
 
 namespace chromeos {
 
@@ -127,8 +126,6 @@
 
 void SAMLOfflineSigninLimiterTest::TearDown() {
   SAMLOfflineSigninLimiterFactory::SetClockForTesting(NULL);
-  // Clear any PostAfterStartupTask's to avoid hanging.
-  base::RunLoop().RunUntilIdle();
 }
 
 TEST_F(SAMLOfflineSigninLimiterTest, NoSAMLDefaultLimit) {
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
index dc64996..371b9e4 100644
--- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
+++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
@@ -19,14 +19,13 @@
 #include "base/stl_util.h"
 #include "base/task/post_task.h"
 #include "base/threading/thread_checker.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
-#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_provider.h"
+#include "chrome/browser/chromeos/settings/install_attributes.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -270,9 +269,7 @@
 }
 
 bool OwnerSettingsServiceChromeOS::IsOwner() {
-  if (g_browser_process->platform_part()
-          ->browser_policy_connector_chromeos()
-          ->IsEnterpriseManaged()) {
+  if (InstallAttributes::Get()->IsEnterpriseManaged()) {
     return false;
   }
   return OwnerSettingsService::IsOwner();
@@ -280,9 +277,7 @@
 
 void OwnerSettingsServiceChromeOS::IsOwnerAsync(
     const IsOwnerCallback& callback) {
-  if (g_browser_process->platform_part()
-          ->browser_policy_connector_chromeos()
-          ->IsEnterpriseManaged()) {
+  if (InstallAttributes::Get()->IsEnterpriseManaged()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(callback, false));
     return;
diff --git a/chrome/browser/chromeos/policy/blocking_login_browsertest.cc b/chrome/browser/chromeos/policy/blocking_login_browsertest.cc
index ecc8b4b2..63a436c1 100644
--- a/chrome/browser/chromeos/policy/blocking_login_browsertest.cc
+++ b/chrome/browser/chromeos/policy/blocking_login_browsertest.cc
@@ -10,13 +10,11 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/screens/gaia_view.h"
 #include "chrome/browser/chromeos/login/test/oobe_base_test.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
-#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/install_attributes.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
@@ -134,15 +132,10 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  policy::BrowserPolicyConnectorChromeOS* browser_policy_connector() {
-    return g_browser_process->platform_part()
-        ->browser_policy_connector_chromeos();
-  }
-
   void EnrollDevice(const std::string& domain) {
     base::RunLoop loop;
     InstallAttributes::LockResult result;
-    browser_policy_connector()->GetInstallAttributes()->LockDevice(
+    InstallAttributes::Get()->LockDevice(
         policy::DEVICE_MODE_ENTERPRISE, domain, std::string(), "100200300",
         base::Bind(&CopyLockResult, &loop, &result));
     loop.Run();
@@ -242,7 +235,7 @@
   // Verify that there isn't a logged in user when the test starts.
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
   EXPECT_FALSE(user_manager->IsUserLoggedIn());
-  EXPECT_FALSE(browser_policy_connector()->IsEnterpriseManaged());
+  EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
   EXPECT_FALSE(profile_added_);
 
   // Enroll the device, if enrollment is enabled for this test instance.
@@ -250,9 +243,8 @@
     EnrollDevice(kDomain);
 
     EXPECT_FALSE(user_manager->IsUserLoggedIn());
-    EXPECT_TRUE(browser_policy_connector()->IsEnterpriseManaged());
-    EXPECT_EQ(kDomain,
-              browser_policy_connector()->GetEnterpriseEnrollmentDomain());
+    EXPECT_TRUE(InstallAttributes::Get()->IsEnterpriseManaged());
+    EXPECT_EQ(kDomain, InstallAttributes::Get()->GetDomain());
     EXPECT_FALSE(profile_added_);
     RunUntilIdle();
     EXPECT_FALSE(
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index f585014..80d05dc2 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -524,6 +524,11 @@
     device_local_account_policy_.policy_data().set_public_key_version(1);
     device_local_account_policy_.payload().mutable_userdisplayname()->set_value(
         kDisplayName1);
+
+    // Don't enable new managed sessions, use old public sessions.
+    device_local_account_policy_.payload()
+        .mutable_devicelocalaccountmanagedsessionenabled()
+        ->set_value(false);
   }
 
   void BuildDeviceLocalAccountPolicy() {
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
index d8c50a2d4..a684b613 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
@@ -20,18 +20,17 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/policy/affiliated_cloud_policy_invalidator.h"
 #include "chrome/browser/chromeos/policy/device_local_account.h"
 #include "chrome/browser/chromeos/policy/device_local_account_external_data_service.h"
 #include "chrome/browser/chromeos/policy/device_local_account_policy_store.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
-#include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chromeos/chromeos_paths.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "chromeos/settings/cros_settings_provider.h"
+#include "components/policy/core/common/chrome_schema.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
@@ -163,7 +162,7 @@
   // starts using it.
   schema_registry_.RegisterComponent(
       PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()),
-      g_browser_process->browser_policy_connector()->GetChromeSchema());
+      policy::GetChromeSchema());
   schema_registry_.SetAllDomainsReady();
 }
 
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_unittest.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_unittest.cc
index c1b71cb..742018b7 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_unittest.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chrome/test/base/testing_browser_process.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
index 7346738f9..9006734 100644
--- a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/message_loop/message_loop_current.h"
 #include "base/run_loop.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
@@ -300,8 +301,13 @@
   }
 };
 
+#if defined(OS_CHROMEOS)
+#define MAYBE_AllowedInPublicSession DISABLED_AllowedInPublicSession
+#else
+#define MAYBE_AllowedInPublicSession AllowedInPublicSession
+#endif
 IN_PROC_BROWSER_TEST_F(PolicyProvidedTrustRootsPublicSessionTest,
-                       AllowedInPublicSession) {
+                       MAYBE_AllowedInPublicSession) {
   StartLogin();
   WaitForSessionStart();
 
diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.cc b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
index c429fb3..b9c82c17 100644
--- a/chrome/browser/chromeos/settings/device_settings_test_helper.cc
+++ b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
-#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_api.cc b/chrome/browser/extensions/api/content_settings/content_settings_api.cc
index b361c7ba..77ef667 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_api.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_api.cc
@@ -11,6 +11,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
@@ -31,6 +32,7 @@
 #include "components/content_settings/core/browser/content_settings_utils.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/plugin_service.h"
 #include "content/public/common/webplugininfo.h"
@@ -273,6 +275,18 @@
     return RespondNow(Error(pref_keys::kIncognitoSessionOnlyErrorMessage));
   }
 
+  size_t num_values = 0;
+  int histogram_value =
+      ContentSettingTypeToHistogramValue(content_type, &num_values);
+  if (primary_pattern != secondary_pattern &&
+      secondary_pattern != ContentSettingsPattern::Wildcard()) {
+    UMA_HISTOGRAM_EXACT_LINEAR("ContentSettings.ExtensionEmbeddedSettingSet",
+                               histogram_value, num_values);
+  } else {
+    UMA_HISTOGRAM_EXACT_LINEAR("ContentSettings.ExtensionNonEmbeddedSettingSet",
+                               histogram_value, num_values);
+  }
+
   scoped_refptr<ContentSettingsStore> store =
       ContentSettingsService::Get(browser_context())->content_settings_store();
   store->SetExtensionContentSetting(extension_id(), primary_pattern,
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
index 2699c31..7273162 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
@@ -8,6 +8,7 @@
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -20,6 +21,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/prefs/pref_service.h"
@@ -359,4 +361,33 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest,
+                       EmbeddedSettingsMetric) {
+  base::HistogramTester histogram_tester;
+  const char kExtensionPath[] = "content_settings/embeddedsettingsmetric";
+  EXPECT_TRUE(RunExtensionSubtest(kExtensionPath, "test.html")) << message_;
+
+  size_t num_values = 0;
+  int javascript_type = ContentSettingTypeToHistogramValue(
+      CONTENT_SETTINGS_TYPE_IMAGES, &num_values);
+  int geolocation_type = ContentSettingTypeToHistogramValue(
+      CONTENT_SETTINGS_TYPE_GEOLOCATION, &num_values);
+  int cookies_type = ContentSettingTypeToHistogramValue(
+      CONTENT_SETTINGS_TYPE_COOKIES, &num_values);
+
+  histogram_tester.ExpectBucketCount(
+      "ContentSettings.ExtensionEmbeddedSettingSet", javascript_type, 1);
+  histogram_tester.ExpectBucketCount(
+      "ContentSettings.ExtensionEmbeddedSettingSet", geolocation_type, 1);
+  histogram_tester.ExpectTotalCount(
+      "ContentSettings.ExtensionEmbeddedSettingSet", 2);
+
+  histogram_tester.ExpectBucketCount(
+      "ContentSettings.ExtensionNonEmbeddedSettingSet", javascript_type, 1);
+  histogram_tester.ExpectBucketCount(
+      "ContentSettings.ExtensionNonEmbeddedSettingSet", cookies_type, 1);
+  histogram_tester.ExpectTotalCount(
+      "ContentSettings.ExtensionNonEmbeddedSettingSet", 2);
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc b/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
index 856b74e..4554ed65 100644
--- a/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
+++ b/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc
@@ -273,6 +273,11 @@
   VerifyMenuItem("parent", top_level_model_, top_level_index(),
                  ui::MenuModel::TYPE_SUBMENU, true);
 
+  // Since the extension submenu is shown, the previous separator should be in
+  // the model.
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR,
+            top_level_model_->GetTypeAt(top_level_index() - 1));
+
   ui::MenuModel* submodel =
       top_level_model_->GetSubmenuModelAt(top_level_index());
   ASSERT_TRUE(submodel);
@@ -301,6 +306,11 @@
   VerifyMenuItem("parent", top_level_model_, top_level_index(),
                  ui::MenuModel::TYPE_SUBMENU, true);
 
+  // Since the extension submenu is shown, the previous separator should be in
+  // the model.
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR,
+            top_level_model_->GetTypeAt(top_level_index() - 1));
+
   ui::MenuModel* submodel =
       top_level_model_->GetSubmenuModelAt(top_level_index());
   ASSERT_TRUE(submodel);
@@ -329,6 +339,11 @@
   VerifyMenuItem(extension()->name(), top_level_model_, top_level_index(),
                  ui::MenuModel::TYPE_SUBMENU, true);
 
+  // Since the extension submenu is shown, the previous separator should be in
+  // the model.
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR,
+            top_level_model_->GetTypeAt(top_level_index() - 1));
+
   ui::MenuModel* submodel =
       top_level_model_->GetSubmenuModelAt(top_level_index());
   ASSERT_TRUE(submodel);
@@ -363,6 +378,11 @@
   VerifyMenuItem(extension()->name(), top_level_model_, top_level_index(),
                  ui::MenuModel::TYPE_SUBMENU, true);
 
+  // Since the extension submenu is shown, the previous separator should be in
+  // the model.
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR,
+            top_level_model_->GetTypeAt(top_level_index() - 1));
+
   ui::MenuModel* submodel =
       top_level_model_->GetSubmenuModelAt(top_level_index());
   ASSERT_TRUE(submodel);
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager.cc b/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
index f923cc5..037e29ae3 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
@@ -86,11 +86,15 @@
     return;
   }
 
+  network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_info;
+  content::BrowserContext::GetDefaultStoragePartition(browser_context_)
+      ->GetURLLoaderFactoryForBrowserProcess()
+      ->Clone(mojo::MakeRequest(&url_loader_factory_info));
+
   scoped_refptr<Operation> operation(new WriteFromUrlOperation(
       weak_factory_.GetWeakPtr(), CreateConnector(), extension_id,
-      content::BrowserContext::GetDefaultStoragePartition(browser_context_)
-          ->GetURLRequestContext(),
-      url, hash, device_path, GetAssociatedDownloadFolder()));
+      std::move(url_loader_factory_info), url, hash, device_path,
+      GetAssociatedDownloadFolder()));
   operations_[extension_id] = operation;
   operation->PostTask(base::BindOnce(&Operation::Start, operation));
 
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
index cedfb9b..ea2c523 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
@@ -9,6 +9,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_fetcher.h"
+#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
@@ -20,7 +22,7 @@
     base::WeakPtr<OperationManager> manager,
     std::unique_ptr<service_manager::Connector> connector,
     const ExtensionId& extension_id,
-    net::URLRequestContextGetter* request_context,
+    network::mojom::URLLoaderFactoryPtrInfo factory_info,
     GURL url,
     const std::string& hash,
     const std::string& device_path,
@@ -30,7 +32,7 @@
                 extension_id,
                 device_path,
                 download_folder),
-      request_context_(request_context),
+      url_loader_factory_ptr_info_(std::move(factory_info)),
       url_(url),
       hash_(hash),
       download_continuation_() {}
@@ -110,49 +112,53 @@
             "Not implemented, considered not useful."
         })");
 
-  // Store the URL fetcher on this object so that it is destroyed before this
-  // object is.
-  url_fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET, this,
-                                         traffic_annotation);
+  auto request = std::make_unique<network::ResourceRequest>();
+  request->url = GURL(url_);
+  simple_url_loader_ =
+      network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
 
-  url_fetcher_->SetRequestContext(request_context_);
-  url_fetcher_->SaveResponseToFileAtPath(image_path_, task_runner());
+  simple_url_loader_->SetOnDownloadProgressCallback(base::BindRepeating(
+      &WriteFromUrlOperation::OnDataDownloaded, base::Unretained(this)));
+  simple_url_loader_->SetOnResponseStartedCallback(base::BindOnce(
+      &WriteFromUrlOperation::OnResponseStarted, base::Unretained(this)));
 
   AddCleanUpFunction(
-      base::BindOnce(&WriteFromUrlOperation::DestroyUrlFetcher, this));
+      base::BindOnce(&WriteFromUrlOperation::DestroySimpleURLLoader, this));
 
-  url_fetcher_->Start();
+  network::mojom::URLLoaderFactoryPtr url_loader_factory_ptr;
+  url_loader_factory_ptr.Bind(std::move(url_loader_factory_ptr_info_));
+
+  simple_url_loader_->DownloadToFile(
+      url_loader_factory_ptr.get(),
+      base::BindOnce(&WriteFromUrlOperation::OnSimpleLoaderComplete,
+                     base::Unretained(this)),
+      image_path_);
 }
 
-void WriteFromUrlOperation::DestroyUrlFetcher() { url_fetcher_.reset(); }
-
-void WriteFromUrlOperation::OnURLFetchUploadProgress(
-    const net::URLFetcher* source,
-    int64_t current,
-    int64_t total) {
-  // No-op
+void WriteFromUrlOperation::DestroySimpleURLLoader() {
+  simple_url_loader_.reset();
 }
 
-void WriteFromUrlOperation::OnURLFetchDownloadProgress(
-    const net::URLFetcher* source,
-    int64_t current,
-    int64_t total,
-    int64_t current_network_bytes) {
+void WriteFromUrlOperation::OnResponseStarted(
+    const GURL& final_url,
+    const network::ResourceResponseHead& response_head) {
+  total_response_bytes_ = response_head.content_length;
+}
+
+void WriteFromUrlOperation::OnDataDownloaded(uint64_t current) {
   DCHECK(IsRunningInCorrectSequence());
 
-  if (IsCancelled()) {
-    url_fetcher_.reset(NULL);
-  }
+  if (IsCancelled())
+    DestroySimpleURLLoader();
 
-  int progress = (kProgressComplete * current) / total;
+  int progress = (kProgressComplete * current) / total_response_bytes_;
 
   SetProgress(progress);
 }
 
-void WriteFromUrlOperation::OnURLFetchComplete(const net::URLFetcher* source) {
+void WriteFromUrlOperation::OnSimpleLoaderComplete(base::FilePath file_path) {
   DCHECK(IsRunningInCorrectSequence());
-
-  if (source->GetStatus().is_success() && source->GetResponseCode() == 200) {
+  if (!file_path.empty()) {
     SetProgress(kProgressComplete);
 
     std::move(download_continuation_).Run();
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h
index b2c5a9c..757fc07 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h
@@ -8,13 +8,13 @@
 #include <stdint.h>
 
 #include "chrome/browser/extensions/api/image_writer_private/operation.h"
-#include "net/url_request/url_fetcher_delegate.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "url/gurl.h"
 
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-}  // namespace net
+namespace network {
+struct ResourceResponseHead;
+class SimpleURLLoader;
+}  // namespace network
 
 namespace extensions {
 namespace image_writer {
@@ -22,12 +22,12 @@
 class OperationManager;
 
 // Encapsulates a write of an image accessed via URL.
-class WriteFromUrlOperation : public Operation, public net::URLFetcherDelegate {
+class WriteFromUrlOperation : public Operation {
  public:
   WriteFromUrlOperation(base::WeakPtr<OperationManager> manager,
                         std::unique_ptr<service_manager::Connector> connector,
                         const ExtensionId& extension_id,
-                        net::URLRequestContextGetter* request_context,
+                        network::mojom::URLLoaderFactoryPtrInfo factory_info,
                         GURL url,
                         const std::string& hash,
                         const std::string& storage_unit_id,
@@ -52,33 +52,24 @@
   void VerifyDownload(base::OnceClosure continuation);
 
  private:
-  // Destroys the URLFetcher.  The URLFetcher needs to be destroyed on the same
-  // thread it was created on.  The Operation may be deleted on the UI thread
-  // and so we must first delete the URLFetcher on the FILE thread.
-  void DestroyUrlFetcher();
-
-  // URLFetcherDelegate implementation.
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
-  void OnURLFetchDownloadProgress(const net::URLFetcher* source,
-                                  int64_t current,
-                                  int64_t total,
-                                  int64_t current_network_bytes) override;
-  void OnURLFetchUploadProgress(const net::URLFetcher* source,
-                                int64_t current,
-                                int64_t total) override;
-
+  void DestroySimpleURLLoader();
+  void OnResponseStarted(const GURL& final_url,
+                         const network::ResourceResponseHead& response_head);
+  void OnDataDownloaded(uint64_t current);
+  void OnSimpleLoaderComplete(base::FilePath file_path);
   void VerifyDownloadCompare(base::OnceClosure continuation,
                              const std::string& download_hash);
   void VerifyDownloadComplete(base::OnceClosure continuation);
 
   // Arguments
-  net::URLRequestContextGetter* request_context_;
+  network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_ptr_info_;
   GURL url_;
   const std::string hash_;
 
   // Local state
-  std::unique_ptr<net::URLFetcher> url_fetcher_;
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
   base::OnceClosure download_continuation_;
+  int total_response_bytes_ = -1;
 };
 
 } // namespace image_writer
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
index 76c89a15..20f3c870 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
 #include "net/url_request/test_url_request_interceptor.h"
 #include "services/service_manager/public/cpp/connector.h"
 
@@ -35,16 +36,17 @@
 // the current path to the image file.
 class WriteFromUrlOperationForTest : public WriteFromUrlOperation {
  public:
-  WriteFromUrlOperationForTest(base::WeakPtr<OperationManager> manager,
-                               const ExtensionId& extension_id,
-                               net::URLRequestContextGetter* request_context,
-                               GURL url,
-                               const std::string& hash,
-                               const std::string& storage_unit_id)
+  WriteFromUrlOperationForTest(
+      base::WeakPtr<OperationManager> manager,
+      const ExtensionId& extension_id,
+      network::mojom::URLLoaderFactoryPtrInfo factory_info,
+      GURL url,
+      const std::string& hash,
+      const std::string& storage_unit_id)
       : WriteFromUrlOperation(manager,
                               /*connector=*/nullptr,
                               extension_id,
-                              request_context,
+                              std::move(factory_info),
                               url,
                               hash,
                               storage_unit_id,
@@ -112,10 +114,15 @@
   scoped_refptr<WriteFromUrlOperationForTest> CreateOperation(
       const GURL& url,
       const std::string& hash) {
+    network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_ptr_info;
+    content::BrowserContext::GetDefaultStoragePartition(&test_profile_)
+        ->GetURLLoaderFactoryForBrowserProcess()
+        ->Clone(mojo::MakeRequest(&url_loader_factory_ptr_info));
+
     scoped_refptr<WriteFromUrlOperationForTest> operation(
         new WriteFromUrlOperationForTest(
             manager_.AsWeakPtr(), kDummyExtensionId,
-            test_profile_.GetRequestContext(), url, hash,
+            std::move(url_loader_factory_ptr_info), url, hash,
             test_utils_.GetDevicePath().AsUTF8Unsafe()));
     operation->Start();
     return operation;
@@ -182,10 +189,6 @@
 
   EXPECT_CALL(
       manager_,
-      OnProgress(kDummyExtensionId, image_writer_api::STAGE_DOWNLOAD, _))
-      .Times(AtLeast(1));
-  EXPECT_CALL(
-      manager_,
       OnProgress(kDummyExtensionId, image_writer_api::STAGE_DOWNLOAD, 0))
       .Times(AnyNumber());
   EXPECT_CALL(
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index d14a433..06d2a25 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -218,9 +218,16 @@
   ASSERT_TRUE(RunExtensionTest("webnavigation/api")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, GetFrame) {
+// Flaky on Windows. See http://crbug.com/874782
+#if defined(OS_WIN)
+#define MAYBE_GetFrame DISABLED_GetFrame
+#else
+#define MAYBE_GetFrame GetFrame
+#endif
+IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, MAYBE_GetFrame) {
   ASSERT_TRUE(RunExtensionTest("webnavigation/getFrame")) << message_;
 }
+#undef MAYBE_GetFrame
 
 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ClientRedirect) {
   ASSERT_TRUE(RunExtensionTest("webnavigation/clientRedirect"))
diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc
index 50f65867..caed3e96 100644
--- a/chrome/browser/extensions/extension_context_menu_browsertest.cc
+++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc
@@ -571,20 +571,28 @@
   // We expect to see the following items in the menu:
   //  radio1
   //  radio2
+  //  --separator-- (automatically added)
   //  normal1
+  //  --separator--
   //  normal2
+  //  --separator--
   //  radio3
   //  radio4
+  //  --separator--
   //  normal3
 
   int index = 0;
-  ASSERT_EQ(7, menu.GetItemCount());
+  ASSERT_EQ(11, menu.GetItemCount());
   ExpectLabelAndType("radio1", MenuModel::TYPE_RADIO, menu, index++);
   ExpectLabelAndType("radio2", MenuModel::TYPE_RADIO, menu, index++);
+  EXPECT_EQ(MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(index++));
   ExpectLabelAndType("normal1", MenuModel::TYPE_COMMAND, menu, index++);
+  EXPECT_EQ(MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(index++));
   ExpectLabelAndType("normal2", MenuModel::TYPE_COMMAND, menu, index++);
+  EXPECT_EQ(MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(index++));
   ExpectLabelAndType("radio3", MenuModel::TYPE_RADIO, menu, index++);
   ExpectLabelAndType("radio4", MenuModel::TYPE_RADIO, menu, index++);
+  EXPECT_EQ(MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(index++));
   ExpectLabelAndType("normal3", MenuModel::TYPE_COMMAND, menu, index++);
 }
 
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 5e45e3e1..2859de5 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -42,6 +42,7 @@
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
 #include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
 #include "components/password_manager/core/browser/login_model.h"
+#include "components/password_manager/core/browser/new_password_form_manager.h"
 #include "components/password_manager/core/browser/test_password_store.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/version_info/version_info.h"
@@ -78,7 +79,13 @@
     : public PasswordManagerBrowserTestBase,
       public ::testing::WithParamInterface<bool> {
  public:
-  PasswordManagerBrowserTestWithViewsFeature() = default;
+  PasswordManagerBrowserTestWithViewsFeature() {
+    // Turn off waiting for server predictions before filing. It makes filling
+    // behaviour more deterministic. Filling with server predictions is tested
+    // in NewPasswordFormManager unit tests.
+    password_manager::NewPasswordFormManager::
+        set_wait_for_server_predictions_for_filling(false);
+  }
   ~PasswordManagerBrowserTestWithViewsFeature() override = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -1760,7 +1767,7 @@
 }
 
 // Test that if the same dynamic form is created multiple times then all of them
-// are autofilled and no unnecessary PasswordStore requests are fired.
+// are autofilled.
 IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature,
                        DuplicateFormsGetFilled) {
   // At first let us save a credential to the password store.
@@ -1783,9 +1790,14 @@
   WaitForJsElementValue("document.body.children[0].children[0]", "temp");
   WaitForJsElementValue("document.body.children[0].children[1]", "random");
 
-  // It's a trick. There should be no second request to the password store since
-  // the existing PasswordFormManager will manage the new form.
-  password_store->Clear();
+  if (!base::FeatureList::IsEnabled(
+          password_manager::features::kNewPasswordFormParsing)) {
+    // It's a trick. There should be no second request to the password store
+    // since the existing PasswordFormManager will manage the new form. On other
+    // hand NewPasswordFormManager uses renderer ids for matching forms so a new
+    // NewPasswordFormManager is created for each DOM form object.
+    password_store->Clear();
+  }
   // Add one more form.
   ASSERT_TRUE(content::ExecuteScript(WebContents(), "addForm();"));
   // Wait until the username is filled, to make sure autofill kicked in.
@@ -3838,6 +3850,44 @@
   CheckThatCredentialsStored("user", "new password");
 }
 
+IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature,
+                       NoFillGaiaReauthenticationForm) {
+  scoped_refptr<password_manager::TestPasswordStore> password_store =
+      static_cast<password_manager::TestPasswordStore*>(
+          PasswordStoreFactory::GetForProfile(
+              browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
+              .get());
+
+  // Visit Gaia reath page.
+  const GURL url = https_test_server().GetURL("accounts.google.com",
+                                              "/password/gaia_reath_form.html");
+
+  NavigationObserver observer(WebContents());
+  ui_test_utils::NavigateToURL(browser(), url);
+  observer.Wait();
+  // Expects no requests to the password store. So no filling.
+  EXPECT_EQ(0, password_store->fill_matching_logins_calls());
+}
+
+IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature,
+                       NoFillGaiaWithSkipSavePasswordForm) {
+  scoped_refptr<password_manager::TestPasswordStore> password_store =
+      static_cast<password_manager::TestPasswordStore*>(
+          PasswordStoreFactory::GetForProfile(
+              browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
+              .get());
+
+  // Visit Gaia form with ssp=1 as query (ssp stands for Skip Save Password).
+  const GURL url = https_test_server().GetURL(
+      "accounts.google.com", "/password/password_form.html?ssp=1");
+
+  NavigationObserver observer(WebContents());
+  ui_test_utils::NavigateToURL(browser(), url);
+  observer.Wait();
+  // Expects no requests to the password store. So no filling.
+  EXPECT_EQ(0, password_store->fill_matching_logins_calls());
+}
+
 INSTANTIATE_TEST_CASE_P(All,
                         PasswordManagerBrowserTestWithViewsFeature,
                         /*popup_views_enabled=*/::testing::Bool());
diff --git a/chrome/browser/password_manager/password_manager_interactive_uitest.cc b/chrome/browser/password_manager/password_manager_interactive_uitest.cc
index 88ba694..ac184880 100644
--- a/chrome/browser/password_manager/password_manager_interactive_uitest.cc
+++ b/chrome/browser/password_manager/password_manager_interactive_uitest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "components/autofill/core/browser/autofill_experiments.h"
+#include "components/password_manager/core/browser/new_password_form_manager.h"
 #include "components/password_manager/core/browser/test_password_store.h"
 #include "content/public/test/browser_test_utils.h"
 
@@ -29,7 +30,13 @@
     : public PasswordManagerInteractiveTestBase,
       public ::testing::WithParamInterface<bool> {
  public:
-  PasswordManagerBrowserTestWithConditionalPopupViews() = default;
+  PasswordManagerBrowserTestWithConditionalPopupViews() {
+    // Turn off waiting for server predictions before filing. It makes filling
+    // behaviour more deterministic. Filling with server predictions is tested
+    // in NewPasswordFormManager unit tests.
+    password_manager::NewPasswordFormManager::
+        set_wait_for_server_predictions_for_filling(false);
+  }
   ~PasswordManagerBrowserTestWithConditionalPopupViews() override = default;
 
   void SetUp() override {
diff --git a/chrome/browser/password_manager/password_store_factory.cc b/chrome/browser/password_manager/password_store_factory.cc
index 3581fe5..7f4a8c2 100644
--- a/chrome/browser/password_manager/password_store_factory.cc
+++ b/chrome/browser/password_manager/password_store_factory.cc
@@ -282,6 +282,11 @@
 
   password_manager_util::CleanBlacklistedCredentials(ps.get(),
                                                      profile->GetPrefs(), 60);
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&password_manager_util::ReportHttpMigrationMetrics, ps,
+                     base::WrapRefCounted(profile->GetRequestContext())),
+      base::TimeDelta::FromSeconds(60));
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
     (defined(OS_LINUX) && !defined(OS_CHROMEOS))
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 82cbcce..3b894075 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -34,6 +34,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/referrer.h"
 #include "content/public/test/web_contents_tester.h"
+#include "device/base/mock_device_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace resource_coordinator {
@@ -415,11 +416,13 @@
   TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
                                                    LoadingState::LOADED);
 
+  // Make sure there is a DeviceClient instance.
+  device::MockDeviceClient device_client;
   UsbTabHelper* usb_tab_helper =
       UsbTabHelper::GetOrCreateForWebContents(web_contents_);
-  usb_tab_helper->CreateChooserService(
+  usb_tab_helper->CreateWebUsbService(
       web_contents_->GetMainFrame(),
-      mojo::InterfaceRequest<device::mojom::UsbChooserService>());
+      mojo::InterfaceRequest<blink::mojom::WebUsbService>());
 
   // Page could be intending to use the WebUSB API, but there's no connection
   // open yet, so it can still be discarded/frozen.
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index a6c206a..37aef035 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -884,7 +884,8 @@
       "TabManager.Discarding.DiscardedTabCouldFastShutdown", false, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(TabManagerTest, ProactiveFastShutdownWithUnloadHandler) {
+IN_PROC_BROWSER_TEST_F(TabManagerTest,
+                       DISABLED_ProactiveFastShutdownWithUnloadHandler) {
   ASSERT_TRUE(embedded_test_server()->Start());
   // Disable the protection of recent tabs.
   OpenTwoTabs(GURL(chrome::kChromeUIAboutURL),
@@ -931,8 +932,9 @@
 #endif  // OS_CHROMEOS
 }
 
+// https://crbug.com/874915, flaky on all platform
 IN_PROC_BROWSER_TEST_F(TabManagerTest,
-                       ProactiveFastShutdownWithBeforeunloadHandler) {
+                       DISABLED_ProactiveFastShutdownWithBeforeunloadHandler) {
   ASSERT_TRUE(embedded_test_server()->Start());
   // Disable the protection of recent tabs.
   OpenTwoTabs(GURL(chrome::kChromeUIAboutURL),
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/nav_braille.js b/chrome/browser/resources/chromeos/chromevox/braille/nav_braille.js
index 5ec2769..77928ca 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/nav_braille.js
+++ b/chrome/browser/resources/chromeos/chromevox/braille/nav_braille.js
@@ -109,3 +109,14 @@
     endIndex: this.endIndex
   };
 };
+
+/**
+ *  Output braille text to console.
+ */
+cvox.NavBraille.prototype.brailleLogging = function() {
+  if (localStorage['enableBrailleLogging'] != 'true')
+    return;
+
+  var logStr = 'Braille "' + this.text.toString() + '"';
+  console.log(logStr);
+};
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.html b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.html
index f614d8c2..ce6a923 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.html
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.html
@@ -142,7 +142,24 @@
       </span>
     </label>
   </div>
-
+  <div class="option" id="developerEarconLogging">
+    <label>
+      <input id="enableEarconLogging" type="checkbox" class="checkbox pref"
+             name="enableEarconLogging">
+      <span class="i18n" msgid="options_developer_earcon_logging">
+        Enable earcon logging
+      </span>
+    </label>
+  </div>
+  <div class="option" id="developerBrailleLogging">
+    <label>
+      <input id="enableBrailleLogging" type="checkbox" class="checkbox pref"
+             name="enableBrailleLogging">
+      <span class="i18n" msgid="options_developer_braille_logging">
+        Enable braille logging
+      </span>
+    </label>
+  </div>
   <div class="option" id="developerEventStream">
     <label>
       <input id="enableEventStreamLogging" type="checkbox" class="checkbox pref"
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
index 17162d0..6b1b3d4f 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
@@ -121,6 +121,8 @@
         if (!enable) {
           $('developerDescription').hidden = true;
           $('developerSpeechLogging').hidden = true;
+          $('developerEarconLogging').hidden = true;
+          $('developerBrailleLogging').hidden = true;
           $('developerEventStream').hidden = true;
           return;
         }
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/prefs.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/prefs.js
index 8b35a9b..5be6dc3 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/prefs.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/prefs.js
@@ -72,6 +72,8 @@
   // should just store in local storage.
   'currentKeyMap': cvox.KeyMap.DEFAULT_KEYMAP,
   'cvoxKey': '',
+  'enableBrailleLogging': false,
+  'enableEarconLogging': true,
   'enableSpeechLogging': true,
   'earcons': true,
   'enableEventStreamLogging': false,
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/next_earcons.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/next_earcons.js
index 7073398..9a01047 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/next_earcons.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/next_earcons.js
@@ -56,7 +56,8 @@
     if (!cvox.AbstractEarcons.enabled) {
       return;
     }
-    console.log('Earcon ' + earcon);
+    if (localStorage['enableEarconLogging'] == 'true')
+      console.log('Earcon ' + earcon);
     if (ChromeVoxState.instance.currentRange &&
         ChromeVoxState.instance.currentRange.isValid()) {
       var node = ChromeVoxState.instance.currentRange.start.node;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index 7173f550..7ecff29b 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -1039,6 +1039,7 @@
 
       var output = new cvox.NavBraille(
           {text: buff, startIndex: startIndex, endIndex: endIndex});
+      output.brailleLogging();
 
       cvox.ChromeVox.braille.write(output);
     }
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index 7e7bb42..7eecf08 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -562,6 +562,9 @@
       <message desc="List of chromevox developer options." name="IDS_CHROMEVOX_OPTIONS_DEVELOPER_OPTIONS">
         Developer Options
       </message>
+      <message desc="Enable chromevox earcon logging." name="IDS_CHROMEVOX_OPTIONS_DEVELOPER_EARCON_LOGGING">
+        Enable earcon logging
+      </message>
       <message desc="Enable chromevox speech logging." name="IDS_CHROMEVOX_OPTIONS_DEVELOPER_SPEECH_LOGGING">
         Enable speech logging
       </message>
@@ -574,6 +577,9 @@
       <message desc="Hide event stream filters options for event stream logging." name="IDS_CHROMEVOX_OPTIONS_HIDE_EVENT_STREAM_FILTERS">
         Hide event stream filters
       </message>
+      <message desc="Enable chromevox braille logging." name="IDS_CHROMEVOX_OPTIONS_DEVELOPER_BRAILLE_LOGGING">
+        Enable braille logging
+      </message>
       <message desc="The title of ChromeVox Learn Mode page.  The keyboard explorer voices the name of each key when the user presses it." name="IDS_CHROMEVOX_KBEXPLORER_TITLE">
         ChromeVox Learn Mode
       </message>
diff --git a/chrome/browser/resources/settings/printing_page/BUILD.gn b/chrome/browser/resources/settings/printing_page/BUILD.gn
index b4717d8..aeb05cf 100644
--- a/chrome/browser/resources/settings/printing_page/BUILD.gn
+++ b/chrome/browser/resources/settings/printing_page/BUILD.gn
@@ -8,7 +8,7 @@
   deps = [
     ":cloud_printers",
     ":cups_add_printer_dialog",
-    ":cups_add_printer_dialog_util",
+    ":cups_add_printer_dialog_elements",
     ":cups_edit_printer_dialog",
     ":cups_printers",
     ":cups_printers_browser_proxy",
@@ -34,7 +34,7 @@
   ]
 }
 
-js_library("cups_add_printer_dialog_util") {
+js_library("cups_add_printer_dialog_elements") {
   deps = [
     ":cups_printers_browser_proxy",
   ]
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
index a8f885f..d8557cc9 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
@@ -7,7 +7,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="../i18n_setup.html">
-<link rel="import" href="cups_add_printer_dialog_util.html">
+<link rel="import" href="cups_add_printer_dialog_elements.html">
 <link rel="import" href="cups_printer_shared_css.html">
 <link rel="import" href="cups_printers_browser_proxy.html">
 <link rel="import" href="cups_set_manufacturer_model_behavior.html">
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html
new file mode 100644
index 0000000..695ce6d
--- /dev/null
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html
@@ -0,0 +1,58 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="cups_printer_shared_css.html">
+<link rel="import" href="cups_printers_browser_proxy.html">
+
+<dom-module id="add-printer-list">
+  <template>
+    <style include="cups-printer-shared">
+      .list-item {
+        padding: 0 20px;
+      }
+    </style>
+    <div>
+      <array-selector id="arraySelector" items="[[printers]]"
+          selected="{{selectedPrinter}}">
+      </array-selector>
+      <template is="dom-repeat" items="[[printers]]">
+        <button class="list-item" on-click="onSelect_">
+          [[item.printerName]]
+        </button>
+      </template>
+    </div>
+  </template>
+</dom-module>
+
+<dom-module id="add-printer-dialog">
+  <template>
+    <style include="settings-shared">
+      #dialog {
+        --cr-dialog-body-container: {
+          /* Force a bottom border regardless of scroll state. */
+          border-bottom: 1px solid var(--paper-grey-300) !important;
+        };
+      }
+      #dialog [slot=body] {
+        height: 350px;
+        padding-inline-end: 0;
+        padding-inline-start: 0;
+      }
+    </style>
+
+    <cr-dialog id="dialog" close-text="$i18n{close}">
+      <div slot="title">
+        <slot name="dialog-title"></slot>
+      </div>
+      <div slot="body">
+        <slot name="dialog-body"></slot>
+      </div>
+      <div slot="button-container">
+        <slot name="dialog-buttons"></slot>
+      </div>
+    </cr-dialog>
+  </template>
+  <script src="cups_add_printer_dialog_elements.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.js
similarity index 100%
rename from chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js
rename to chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.js
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
deleted file mode 100644
index 8b9645c..0000000
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="cups_printer_shared_css.html">
-<link rel="import" href="cups_printers_browser_proxy.html">
-
-<dom-module id="add-printer-list">
-  <template>
-    <style include="cups-printer-shared">
-      .list-item {
-        padding: 0 20px;
-      }
-    </style>
-    <div>
-      <array-selector id="arraySelector" items="[[printers]]"
-          selected="{{selectedPrinter}}">
-      </array-selector>
-      <template is="dom-repeat" items="[[printers]]">
-        <button class="list-item" on-click="onSelect_">
-          [[item.printerName]]
-        </button>
-      </template>
-    </div>
-  </template>
-</dom-module>
-
-<dom-module id="add-printer-dialog">
-  <template>
-    <style include="settings-shared">
-      #dialog {
-        --cr-dialog-body-container: {
-          /* Force a bottom border regardless of scroll state. */
-          border-bottom: 1px solid var(--paper-grey-300) !important;
-        };
-      }
-      #dialog [slot=body] {
-        height: 350px;
-        padding-inline-end: 0;
-        padding-inline-start: 0;
-      }
-    </style>
-
-    <cr-dialog id="dialog" close-text="$i18n{close}">
-      <div slot="title">
-        <slot name="dialog-title"></slot>
-      </div>
-      <div slot="body">
-        <slot name="dialog-body"></slot>
-      </div>
-      <div slot="button-container">
-        <slot name="dialog-buttons"></slot>
-      </div>
-    </cr-dialog>
-  </template>
-  <script src="cups_add_printer_dialog_util.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
index 4c88679..31456eb6 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="cups_add_printer_dialog_util.html">
+<link rel="import" href="cups_add_printer_dialog_elements.html">
 <link rel="import" href="cups_printer_shared_css.html">
 <link rel="import" href="cups_printers_browser_proxy.html">
 <link rel="import" href="cups_set_manufacturer_model_behavior.html">
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 3978f46..aefffa5 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -900,11 +900,11 @@
         <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_JS"
                    file="printing_page/cups_add_printer_dialog.js"
                    type="chrome_html" />
-        <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_UTIL_HTML"
-                   file="printing_page/cups_add_printer_dialog_util.html"
+        <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_ELEMENTS_HTML"
+                   file="printing_page/cups_add_printer_dialog_elements.html"
                    type="chrome_html" />
-        <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_UTIL_JS"
-                   file="printing_page/cups_add_printer_dialog_util.js"
+        <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_ELEMENTS_JS"
+                   file="printing_page/cups_add_printer_dialog_elements.js"
                    type="chrome_html" />
       </if>
       <if expr="not chromeos">
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 429c623..f0af7bc7 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -66,7 +66,7 @@
   base::test::ScopedFeatureList override_features_;
 };
 
-class SingleClientBookmarksSyncTest : public SyncTest {
+class SingleClientBookmarksSyncTest : public UssSwitchToggler, public SyncTest {
  public:
   SingleClientBookmarksSyncTest() : SyncTest(SINGLE_CLIENT) {}
   ~SingleClientBookmarksSyncTest() override {}
@@ -97,20 +97,7 @@
   }
 }
 
-// TODO(crbug.com/516866): Merge the two fixtures into one when all tests are
-// passing for USS.
-class SingleClientBookmarksSyncTestIncludingUssTests
-    : public UssSwitchToggler,
-      public SingleClientBookmarksSyncTest {
- public:
-  SingleClientBookmarksSyncTestIncludingUssTests() {}
-  ~SingleClientBookmarksSyncTestIncludingUssTests() override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SingleClientBookmarksSyncTestIncludingUssTests);
-};
-
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests, Sanity) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, Sanity) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
   // Starting state:
@@ -244,8 +231,7 @@
     VerifyBookmarkModelMatchesFakeServer(kSingleProfileIndex);
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests,
-                       CommitLocalCreations) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, CommitLocalCreations) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
   // Starting state:
@@ -286,8 +272,7 @@
   EXPECT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests,
-                       InjectedBookmark) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, InjectedBookmark) {
   std::string title = "Montreal Canadiens";
   fake_server::EntityBuilderFactory entity_builder_factory;
   fake_server::BookmarkEntityBuilder bookmark_builder =
@@ -305,7 +290,7 @@
 // Test that a client doesn't mutate the favicon data in the process
 // of storing the favicon data from sync to the database or in the process
 // of requesting data from the database for sync.
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        SetFaviconHiDPIDifferentCodec) {
   // Set the supported scale factors to 1x and 2x such that
   // BookmarkModel::GetFavicon() requests both 1x and 2x.
@@ -350,8 +335,7 @@
 
 // Test that a client deletes favicons from sync when they have been removed
 // from the local database.
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests,
-                       DeleteFaviconFromSync) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DeleteFaviconFromSync) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
 
@@ -377,7 +361,7 @@
       GetBookmarkModel(kSingleProfileIndex)->GetFavicon(bookmark).IsEmpty());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        BookmarkAllNodesRemovedEvent) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   // Starting state:
@@ -438,8 +422,7 @@
   ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests,
-                       DownloadDeletedBookmark) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DownloadDeletedBookmark) {
   std::string title = "Patrick Star";
   fake_server::EntityBuilderFactory entity_builder_factory;
   fake_server::BookmarkEntityBuilder bookmark_builder =
@@ -469,7 +452,7 @@
                   .Wait());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        DownloadModifiedBookmark) {
   std::string title = "Syrup";
   GURL original_url = GURL("https://en.wikipedia.org/?title=Maple_syrup");
@@ -508,8 +491,7 @@
   ASSERT_EQ(1, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests,
-                       DownloadBookmarkFolder) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DownloadBookmarkFolder) {
   const std::string title = "Seattle Sounders FC";
   fake_server::EntityBuilderFactory entity_builder_factory;
   fake_server::BookmarkEntityBuilder bookmark_builder =
@@ -525,7 +507,7 @@
   ASSERT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        DownloadBookmarkFoldersWithPositions) {
   const std::string title0 = "Folder left";
   const std::string title1 = "Folder middle";
@@ -565,12 +547,12 @@
   EXPECT_EQ(base::ASCIIToUTF16(title2), bar->GetChild(2)->GetTitle());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, E2E_ONLY(SanitySetup)) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, E2E_ONLY(SanitySetup)) {
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
 }
 
 INSTANTIATE_TEST_CASE_P(USS,
-                        SingleClientBookmarksSyncTestIncludingUssTests,
+                        SingleClientBookmarksSyncTest,
                         ::testing::Values(false, true));
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
index 855a70d..e36c3fdf 100644
--- a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
@@ -155,7 +155,8 @@
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SimultaneousURLChanges) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
+                       SimultaneousURLChanges) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -638,7 +639,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SC_DeleteBMEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -650,7 +651,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SC_DelBMNonEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -741,7 +742,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SC_DelEmptyBMFoldEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -753,7 +754,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SC_DelEmptyBMFoldNonEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -775,7 +776,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SC_DelBMFoldWithBMsNonEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -804,7 +805,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SC_DelBMFoldWithBMsAndBMFoldsNonEmptyACAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1595,7 +1596,8 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, DisableBookmarks) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
+                       DisableBookmarks) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -1800,7 +1802,7 @@
   ASSERT_FALSE(ContainsDuplicateBookmarks(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SingleClientEnabledEncryption) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1812,7 +1814,7 @@
   ASSERT_TRUE(AllModelsMatchVerifier());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SingleClientEnabledEncryptionAndChanged) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1825,7 +1827,7 @@
   ASSERT_TRUE(AllModelsMatchVerifier());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        BothClientsEnabledEncryption) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1838,7 +1840,7 @@
   ASSERT_TRUE(AllModelsMatchVerifier());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SingleClientEnabledEncryptionBothChanged) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1855,7 +1857,7 @@
   ASSERT_TRUE(IsEncryptionComplete(1));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        SingleClientEnabledEncryptionAndChangedMultipleTimes) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1872,7 +1874,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        FirstClientEnablesEncryptionWithPassSecondChanges) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1916,7 +1918,8 @@
 // Deliberately racy rearranging of bookmarks to test that our conflict resolver
 // code results in a consistent view across machines (no matter what the final
 // order is).
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, RacyPositionChanges) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
+                       RacyPositionChanges) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -2120,11 +2123,12 @@
   ASSERT_EQ(0, managed_node1->child_count());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, E2E_ONLY(SanitySetup)) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
+                       E2E_ONLY(SanitySetup)) {
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        E2E_ONLY(OneClientAddsBookmark)) {
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
   // All profiles should sync same bookmarks.
@@ -2149,7 +2153,7 @@
 }
 
 // TODO(shadi): crbug.com/569213: Enable this as E2E test.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        OneClientAddsFolderAndBookmark) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // All profiles should sync same bookmarks.
@@ -2174,7 +2178,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        E2E_ONLY(TwoClientsAddBookmarks)) {
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
   // ALl profiles should sync same bookmarks.
@@ -2204,7 +2208,7 @@
 
 // Verify that a bookmark added on a client with bookmark syncing disabled gets
 // synced to a second client once bookmark syncing is re-enabled.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
                        E2E_ENABLED(AddBookmarkWhileDisabled)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(BookmarksMatchChecker().Wait())
diff --git a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
index 06e78ec..4a3892f 100644
--- a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
@@ -141,8 +141,9 @@
   ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
 }
 
+// https://crbug.com/874929, flaky on all platform.
 IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
-                       SetPassphraseAndThenSetupSync) {
+                       MAYBE_SetPassphraseAndThenSetupSync) {
   ASSERT_TRUE(SetupClients());
 
   ASSERT_TRUE(GetClient(0)->SetupSync());
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index cce40021..4d608b4 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3794,7 +3794,8 @@
   if (enable_extensions) {
     deps += [
       "//apps",
-      "//chrome/browser/apps/platform_apps",
+      "//chrome/browser/apps",
+      "//chrome/browser/apps/platform_apps",  # TODO(loyso): Remove this dep.
       "//chrome/browser/extensions",
       "//chrome/common/extensions/api",
       "//components/drive",
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index 45631b6d..f7d77b8 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -3530,25 +3530,25 @@
       model_->GetShelfItemDelegate(gmail_id);
   ASSERT_TRUE(item_delegate);
   EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
-  // Execute the second item in the menu, after the title,
+  // Execute the second item in the menu, after the title and two separators,
   // this shouldn't do anything since that item is already the active tab.
   {
     ash::ShelfApplicationMenuModel menu(
         base::string16(),
         launcher_controller_->GetAppMenuItemsForTesting(item_gmail),
         item_delegate);
-    menu.ActivatedAt(2);
+    menu.ActivatedAt(4);
   }
   EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
 
-  // Execute the first item in the menu, after the title,
+  // Execute the first item in the menu, after the title and two separators,
   // this should activate the other tab.
   {
     ash::ShelfApplicationMenuModel menu(
         base::string16(),
         launcher_controller_->GetAppMenuItemsForTesting(item_gmail),
         item_delegate);
-    menu.ActivatedAt(1);
+    menu.ActivatedAt(3);
   }
   EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
 }
@@ -3558,6 +3558,7 @@
   InitLauncherControllerWithBrowser();
 
   // Add |extension3_| to the launcher and add two items.
+  GURL gmail = GURL("https://mail.google.com/mail/u");
   const ash::ShelfID gmail_id(extension3_->id());
   extension_service_->AddExtension(extension3_.get());
   launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(kGmailUrl));
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
index 674bb119..bf04cf6 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/chromeos/arc/icon_decode_request.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
@@ -79,10 +78,6 @@
     tablet_mode_client_ = std::make_unique<TabletModeClient>();
     tablet_mode_client_->InitForTesting(
         fake_tablet_mode_controller_.CreateInterfacePtr());
-
-    // Disable safe icon decoding to ensure ArcAppShortcutRequests returns in
-    // the test environment.
-    arc::IconDecodeRequest::DisableSafeDecodingForTesting();
   }
 
   std::unique_ptr<LauncherContextMenu> CreateLauncherContextMenu(
@@ -351,7 +346,7 @@
   }
 }
 
-TEST_F(LauncherContextMenuTest, ArcLauncherSuspendAppMenu) {
+TEST_F(LauncherContextMenuTest, ArcLauncherSuspenedAppMenu) {
   arc::mojom::AppInfo app = arc_test().fake_apps()[0];
   app.suspended = true;
   arc_test().app_instance()->RefreshAppList();
@@ -441,7 +436,7 @@
 }
 
 TEST_F(LauncherContextMenuTest, ArcContextMenuOptions) {
-  // Tests that there are 8 ARC app context menu options. If you're
+  // Tests that there are 4 ARC app context menu options. If you're
   // adding a context menu option ensure that you have added the enum to
   // tools/metrics/enums.xml and that you haven't modified the order of the
   // existing enums.
@@ -462,8 +457,8 @@
   std::unique_ptr<ui::MenuModel> menu =
       GetContextMenu(item_delegate, primary_id);
 
-  // Test that there are 8 items in an ARC app context menu.
-  EXPECT_EQ(8, menu->GetItemCount());
+  // Test that there are 4 items in an ARC app context menu.
+  EXPECT_EQ(4, menu->GetItemCount());
 }
 
 // Tests that the context menu of internal app  is correct.
@@ -515,7 +510,7 @@
     std::unique_ptr<ui::MenuModel> menu =
         GetContextMenu(item_delegate, primary_id);
 
-    const int expected_options_num = internal_app.show_in_launcher ? 2 : 1;
+    const int expected_options_num = internal_app.show_in_launcher ? 4 : 2;
     EXPECT_EQ(expected_options_num, menu->GetItemCount());
   }
 }
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index dc3e5f17..98280ff 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -22,13 +22,12 @@
 #include "base/strings/string_util.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
+#include "chrome/browser/apps/apps_launch.h"
 #include "chrome/browser/apps/platform_apps/install_chrome_app.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
 #include "chrome/browser/defaults.h"
-#include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/obsolete_system/obsolete_system.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
@@ -43,8 +42,6 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/extensions/app_launch_params.h"
-#include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/session_crashed_bubble.h"
 #include "chrome/browser/ui/startup/automation_infobar_delegate.h"
 #include "chrome/browser/ui/startup/bad_flags_prompt.h"
@@ -55,8 +52,6 @@
 #include "chrome/browser/ui/startup/startup_tab_provider.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/extensions/extension_metrics.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/rappor/public/rappor_utils.h"
@@ -65,11 +60,6 @@
 #include "content/public/browser/dom_storage_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_switches.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/common/constants.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_set.h"
 #include "google_apis/google_api_keys.h"
 #include "rlz/buildflags/buildflags.h"
 #include "ui/base/ui_features.h"
@@ -106,10 +96,6 @@
 #include "chrome/browser/ui/startup/supervised_users_deprecated_infobar_delegate.h"
 #endif
 
-using content::ChildProcessSecurityPolicy;
-using content::WebContents;
-using extensions::Extension;
-
 namespace {
 
 // Utility functions ----------------------------------------------------------
@@ -286,55 +272,6 @@
   return urls;
 }
 
-// Return true if the command line option --app-id is used.  Set
-// |out_extension| to the app to open, and |out_launch_container|
-// to the type of window into which the app should be open.
-bool GetAppLaunchContainer(
-    Profile* profile,
-    const std::string& app_id,
-    const Extension** out_extension,
-    extensions::LaunchContainer* out_launch_container) {
-
-  const Extension* extension = extensions::ExtensionRegistry::Get(
-      profile)->enabled_extensions().GetByID(app_id);
-  // The extension with id |app_id| may have been uninstalled.
-  if (!extension)
-    return false;
-
-  // Don't launch platform apps in incognito mode.
-  if (profile->IsOffTheRecord() && extension->is_platform_app())
-    return false;
-
-  // Look at preferences to find the right launch container. If no
-  // preference is set, launch as a window.
-  extensions::LaunchContainer launch_container = extensions::GetLaunchContainer(
-      extensions::ExtensionPrefs::Get(profile), extension);
-
-  if (!extensions::util::IsNewBookmarkAppsEnabled() &&
-      !extensions::HasPreferredLaunchContainer(
-          extensions::ExtensionPrefs::Get(profile), extension)) {
-    launch_container = extensions::LAUNCH_CONTAINER_WINDOW;
-  }
-
-  *out_extension = extension;
-  *out_launch_container = launch_container;
-  return true;
-}
-
-void RecordCmdLineAppHistogram(extensions::Manifest::Type app_type) {
-  extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_CMD_LINE_APP,
-                                  app_type);
-}
-
-// TODO(koz): Consolidate this function and remove the special casing.
-const Extension* GetPlatformApp(Profile* profile,
-                                const std::string& extension_id) {
-  const Extension* extension =
-      extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
-          extension_id, extensions::ExtensionRegistry::EVERYTHING);
-  return extension && extension->is_platform_app() ? extension : NULL;
-}
-
 // Appends the contents of |from| to the end of |to|.
 void AppendTabs(const StartupTabs& from, StartupTabs* to) {
   if (!from.empty())
@@ -393,19 +330,11 @@
 
   if (command_line_.HasSwitch(switches::kAppId)) {
     std::string app_id = command_line_.GetSwitchValueASCII(switches::kAppId);
-    const Extension* extension = GetPlatformApp(profile, app_id);
     // If |app_id| is a disabled or terminated platform app we handle it
     // specially here, otherwise it will be handled below.
-    if (extension) {
-      RecordCmdLineAppHistogram(extensions::Manifest::TYPE_PLATFORM_APP);
-      AppLaunchParams params(
-          profile, extension, extensions::LAUNCH_CONTAINER_NONE,
-          WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_COMMAND_LINE);
-      params.command_line = command_line_;
-      params.current_directory = cur_dir_;
-      ::OpenApplicationWithReenablePrompt(params);
+    if (apps::OpenApplicationWithReenablePrompt(profile, app_id, command_line_,
+                                                cur_dir_))
       return true;
-    }
   }
 
   // Open the required browser windows and tabs. First, see if
@@ -581,29 +510,8 @@
   // TODO(skerner): Do something reasonable here. Pop up a warning panel?
   // Open an URL to the gallery page of the extension id?
   if (!app_id.empty()) {
-    extensions::LaunchContainer launch_container;
-    const Extension* extension;
-    if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container))
-      return false;
-
-    // TODO(skerner): Could pass in |extension| and |launch_container|,
-    // and avoid calling GetAppLaunchContainer() both here and in
-    // OpenApplicationTab().
-
-    if (launch_container == extensions::LAUNCH_CONTAINER_TAB)
-      return false;
-
-    RecordCmdLineAppHistogram(extension->GetType());
-
-    AppLaunchParams params(profile, extension, launch_container,
-                           WindowOpenDisposition::NEW_WINDOW,
-                           extensions::SOURCE_COMMAND_LINE);
-    params.command_line = command_line_;
-    params.current_directory = cur_dir_;
-    WebContents* tab_in_app_window = ::OpenApplication(params);
-
-    // Platform apps fire off a launch event which may or may not open a window.
-    return (tab_in_app_window != NULL || CanLaunchViaEvent(extension));
+    return apps::OpenApplicationWindow(profile, app_id, command_line_,
+                                       cur_dir_);
   }
 
   if (url_string.empty())
@@ -616,23 +524,11 @@
 
   // Restrict allowed URLs for --app switch.
   if (!url.is_empty() && url.is_valid()) {
-    ChildProcessSecurityPolicy* policy =
-        ChildProcessSecurityPolicy::GetInstance();
+    content::ChildProcessSecurityPolicy* policy =
+        content::ChildProcessSecurityPolicy::GetInstance();
     if (policy->IsWebSafeScheme(url.scheme()) ||
         url.SchemeIs(url::kFileScheme)) {
-      const extensions::Extension* extension =
-          extensions::ExtensionRegistry::Get(profile)
-              ->enabled_extensions().GetAppByURL(url);
-      if (extension) {
-        RecordCmdLineAppHistogram(extension->GetType());
-      } else {
-        extensions::RecordAppLaunchType(
-            extension_misc::APP_LAUNCH_CMD_LINE_APP_LEGACY,
-            extensions::Manifest::TYPE_HOSTED_APP);
-      }
-
-      WebContents* app_tab = ::OpenAppShortcutWindow(profile, url);
-      return (app_tab != NULL);
+      return apps::OpenAppShortcutWindow(profile, url);
     }
   }
   return false;
@@ -644,25 +540,10 @@
   // function will open an app that should be in a tab, there is no need
   // to look at the app URL.  OpenApplicationWindow() will open app url
   // shortcuts.
-  if (!IsAppLaunch(NULL, &app_id) || app_id.empty())
+  if (!IsAppLaunch(nullptr, &app_id) || app_id.empty())
     return false;
 
-  extensions::LaunchContainer launch_container;
-  const Extension* extension;
-  if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container))
-    return false;
-
-  // If the user doesn't want to open a tab, fail.
-  if (launch_container != extensions::LAUNCH_CONTAINER_TAB)
-    return false;
-
-  RecordCmdLineAppHistogram(extension->GetType());
-
-  WebContents* app_tab = ::OpenApplication(
-      AppLaunchParams(profile, extension, extensions::LAUNCH_CONTAINER_TAB,
-                      WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                      extensions::SOURCE_COMMAND_LINE));
-  return (app_tab != NULL);
+  return apps::OpenApplicationTab(profile, app_id);
 }
 
 void StartupBrowserCreatorImpl::DetermineURLsAndLaunch(
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
index 70cffcc..f3cd94b 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/grit/generated_resources.h"
+#include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_features.h"
 #include "ui/gfx/color_palette.h"
@@ -318,6 +319,26 @@
       l10n_util::GetStringUTF16(IDS_AUTOFILL_POPUP_ACCESSIBLE_NODE_DATA));
 }
 
+void AutofillPopupBaseView::VisibilityChanged(View* starting_from,
+                                              bool is_visible) {
+  if (is_visible) {
+    // Announce that the suggestions are available before the pop up is open.
+    // The password generation pop up relies on this call.
+    ui::AXPlatformNode::OnInputSuggestionsAvailable();
+    // Fire these the first time a menu is visible. By firing these and the
+    // matching end events, we are telling screen readers that the focus
+    // is only changing temporarily, and the screen reader will restore the
+    // focus back to the appropriate textfield when the menu closes.
+    NotifyAccessibilityEvent(ax::mojom::Event::kMenuStart, true);
+  } else {
+    // TODO(https://crbug.com/848427) Only call if suggestions are actually no
+    // longer available. The suggestions could be hidden but still available, as
+    // is the case when the Escape key is pressed.
+    ui::AXPlatformNode::OnInputSuggestionsUnavailable();
+    NotifyAccessibilityEvent(ax::mojom::Event::kMenuEnd, true);
+  }
+}
+
 void AutofillPopupBaseView::SetSelection(const gfx::Point& point) {
   if (delegate_)
     delegate_->SetSelectionAtPoint(point);
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
index 8019675..8f45126 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
@@ -85,6 +85,7 @@
   void OnMouseReleased(const ui::MouseEvent& event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  void VisibilityChanged(View* starting_from, bool is_visible) override;
 
   // views::WidgetFocusChangeListener implementation.
   void OnNativeFocusChanged(gfx::NativeView focused_now) override;
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index 4d42dfc..db09d7f 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -17,7 +17,6 @@
 #include "components/autofill/core/browser/suggestion.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -700,26 +699,6 @@
   DoHide();
 }
 
-void AutofillPopupViewNativeViews::VisibilityChanged(View* starting_from,
-                                                     bool is_visible) {
-  if (is_visible) {
-    // TODO(https://crbug.com/848427) Call this when suggestions become
-    // available at all, even if it not currently visible.
-    ui::AXPlatformNode::OnInputSuggestionsAvailable();
-    // Fire these the first time a menu is visible. By firing these and the
-    // matching end events, we are telling screen readers that the focus
-    // is only changing temporarily, and the screen reader will restore the
-    // focus back to the appropriate textfield when the menu closes.
-    NotifyAccessibilityEvent(ax::mojom::Event::kMenuStart, true);
-  } else {
-    // TODO(https://crbug.com/848427) Only call if suggestions are actually no
-    // longer available. The suggestions could be hidden but still available, as
-    // is the case when the Escape key is pressed.
-    ui::AXPlatformNode::OnInputSuggestionsUnavailable();
-    NotifyAccessibilityEvent(ax::mojom::Event::kMenuEnd, true);
-  }
-}
-
 void AutofillPopupViewNativeViews::OnSelectedRowChanged(
     base::Optional<int> previous_row_selection,
     base::Optional<int> current_row_selection) {
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
index 3f6479ceb..a78affd 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
@@ -89,9 +89,6 @@
   AutofillPopupController* controller() { return controller_; }
 
  private:
-  // views::View:
-  void VisibilityChanged(View* starting_from, bool is_visible) override;
-
   void OnSelectedRowChanged(base::Optional<int> previous_row_selection,
                             base::Optional<int> current_row_selection) override;
   void OnSuggestionsChanged() override;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 776e268e..79e0d9a 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -317,6 +317,11 @@
   // OmniboxEditModel::OnSetFocus(), which handles restoring visibility when the
   // omnibox regains focus after losing focus.
   model()->SetCaretVisibility(true);
+  // If the user attempts to focus the omnibox, and the ctrl key is pressed, we
+  // want to prevent ctrl-enter behavior until the ctrl key is released and
+  // re-pressed. This occurs even if the omnibox is already focused and we
+  // re-request focus (e.g. pressing ctrl-l twice).
+  model()->ConsumeCtrlKey();
 }
 
 int OmniboxViewViews::GetTextWidth() const {
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
index 711b005e..1885d96 100644
--- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
+++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/passwords/password_generation_popup_controller.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
@@ -199,7 +200,10 @@
 
 void PasswordGenerationPopupViewViews::GetAccessibleNodeData(
     ui::AXNodeData* node_data) {
-  node_data->SetName(controller_->SuggestedText());
+  node_data->SetName(
+      base::JoinString({controller_->SuggestedText(), controller_->password()},
+                       base::ASCIIToUTF16(" ")));
+  node_data->SetDescription(controller_->HelpText());
   node_data->role = ax::mojom::Role::kMenuItem;
 }
 
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc b/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc
index f75a9a5f..ce3a394f 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc
@@ -71,8 +71,8 @@
   ChromeLauncherController::instance()->FlushForTesting();
 
   // Move the cursor up to the "New window" menu option - assumes menu content.
-  generator.MoveMouseBy(
-      0, -3 * views::MenuConfig::instance().touchable_menu_height);
+  generator.MoveMouseBy(0, -5 * views::MenuConfig::instance().item_min_height -
+                               views::MenuConfig::instance().separator_height);
   generator.ReleaseRightButton();
 
   // Ash notifies Chrome's ShelfItemDelegate that the menu item was selected.
diff --git a/chrome/browser/usb/usb_browsertest.cc b/chrome/browser/usb/usb_browsertest.cc
index b214115..3230dfaf 100644
--- a/chrome/browser/usb/usb_browsertest.cc
+++ b/chrome/browser/usb/usb_browsertest.cc
@@ -14,7 +14,9 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
 #include "chrome/browser/usb/usb_chooser_controller.h"
-#include "chrome/browser/usb/web_usb_chooser_service.h"
+#include "chrome/browser/usb/web_usb_chooser.h"
+#include "chrome/browser/usb/web_usb_permission_provider.h"
+#include "chrome/browser/usb/web_usb_service_impl.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -25,10 +27,15 @@
 #include "device/base/mock_device_client.h"
 #include "device/usb/mock_usb_device.h"
 #include "device/usb/mock_usb_service.h"
-#include "device/usb/public/mojom/chooser_service.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 
+namespace blink {
+namespace mojom {
+class WebUsbService;
+}
+}  // namespace blink
+
 using content::RenderFrameHost;
 using device::MockDeviceClient;
 using device::MockUsbDevice;
@@ -64,37 +71,46 @@
   DISALLOW_COPY_AND_ASSIGN(FakeChooserView);
 };
 
-class FakeChooserService : public WebUsbChooserService {
+class FakeUsbChooser : public WebUsbChooser {
  public:
-  explicit FakeChooserService(RenderFrameHost* render_frame_host)
-      : WebUsbChooserService(render_frame_host) {}
+  explicit FakeUsbChooser(RenderFrameHost* render_frame_host)
+      : WebUsbChooser(render_frame_host), weak_factory_(this) {}
 
-  ~FakeChooserService() override {}
+  ~FakeUsbChooser() override {}
 
   void ShowChooser(std::unique_ptr<UsbChooserController> controller) override {
     new FakeChooserView(std::move(controller));
   }
 
+  base::WeakPtr<WebUsbChooser> GetWeakPtr() override {
+    return weak_factory_.GetWeakPtr();
+  }
+
  private:
-  DISALLOW_COPY_AND_ASSIGN(FakeChooserService);
+  base::WeakPtrFactory<FakeUsbChooser> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeUsbChooser);
 };
 
 class TestContentBrowserClient : public ChromeContentBrowserClient {
  public:
   TestContentBrowserClient() {}
+
   ~TestContentBrowserClient() override {}
 
   // ChromeContentBrowserClient:
-  void CreateUsbChooserService(
+  void CreateWebUsbService(
       content::RenderFrameHost* render_frame_host,
-      device::mojom::UsbChooserServiceRequest request) override {
+      mojo::InterfaceRequest<blink::mojom::WebUsbService> request) override {
     if (use_real_chooser_) {
-      ChromeContentBrowserClient::CreateUsbChooserService(render_frame_host,
-                                                          std::move(request));
+      ChromeContentBrowserClient::CreateWebUsbService(render_frame_host,
+                                                      std::move(request));
     } else {
-      mojo::MakeStrongBinding(
-          std::make_unique<FakeChooserService>(render_frame_host),
-          std::move(request));
+      permission_provider_.reset(
+          new WebUSBPermissionProvider(render_frame_host));
+      usb_chooser_.reset(new FakeUsbChooser(render_frame_host));
+      WebUsbServiceImpl::Create(permission_provider_->GetWeakPtr(),
+                                usb_chooser_->GetWeakPtr(), std::move(request));
     }
   }
 
@@ -102,6 +118,8 @@
 
  private:
   bool use_real_chooser_ = false;
+  std::unique_ptr<WebUSBPermissionProvider> permission_provider_;
+  std::unique_ptr<WebUsbChooser> usb_chooser_;
 
   DISALLOW_COPY_AND_ASSIGN(TestContentBrowserClient);
 };
diff --git a/chrome/browser/usb/usb_chooser_controller.cc b/chrome/browser/usb/usb_chooser_controller.cc
index 390eb2c6..0c33491 100644
--- a/chrome/browser/usb/usb_chooser_controller.cc
+++ b/chrome/browser/usb/usb_chooser_controller.cc
@@ -69,7 +69,7 @@
 UsbChooserController::UsbChooserController(
     RenderFrameHost* render_frame_host,
     std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
-    device::mojom::UsbChooserService::GetPermissionCallback callback)
+    blink::mojom::WebUsbService::GetPermissionCallback callback)
     : ChooserController(render_frame_host,
                         IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN,
                         IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME),
diff --git a/chrome/browser/usb/usb_chooser_controller.h b/chrome/browser/usb/usb_chooser_controller.h
index 4971574..febd80f9 100644
--- a/chrome/browser/usb/usb_chooser_controller.h
+++ b/chrome/browser/usb/usb_chooser_controller.h
@@ -14,8 +14,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/chooser_controller/chooser_controller.h"
-#include "device/usb/public/mojom/chooser_service.mojom.h"
 #include "device/usb/usb_service.h"
+#include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -37,7 +37,7 @@
   UsbChooserController(
       content::RenderFrameHost* render_frame_host,
       std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
-      device::mojom::UsbChooserService::GetPermissionCallback callback);
+      blink::mojom::WebUsbService::GetPermissionCallback callback);
   ~UsbChooserController() override;
 
   // ChooserController:
@@ -61,7 +61,7 @@
   bool DisplayDevice(scoped_refptr<device::UsbDevice> device) const;
 
   std::vector<device::mojom::UsbDeviceFilterPtr> filters_;
-  device::mojom::UsbChooserService::GetPermissionCallback callback_;
+  blink::mojom::WebUsbService::GetPermissionCallback callback_;
   GURL requesting_origin_;
   GURL embedding_origin_;
 
diff --git a/chrome/browser/usb/usb_chooser_controller_unittest.cc b/chrome/browser/usb/usb_chooser_controller_unittest.cc
index 2ea9302..6e784d5 100644
--- a/chrome/browser/usb/usb_chooser_controller_unittest.cc
+++ b/chrome/browser/usb/usb_chooser_controller_unittest.cc
@@ -14,7 +14,6 @@
 #include "device/base/mock_device_client.h"
 #include "device/usb/mock_usb_device.h"
 #include "device/usb/mock_usb_service.h"
-#include "device/usb/public/mojom/device_manager.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -49,7 +48,7 @@
     ChromeRenderViewHostTestHarness::SetUp();
 
     std::vector<device::mojom::UsbDeviceFilterPtr> device_filters;
-    device::mojom::UsbChooserService::GetPermissionCallback callback;
+    blink::mojom::WebUsbService::GetPermissionCallback callback;
     content::WebContentsTester* web_contents_tester =
         content::WebContentsTester::For(web_contents());
     web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
diff --git a/chrome/browser/usb/usb_tab_helper.cc b/chrome/browser/usb/usb_tab_helper.cc
index 8414534..5508f38ae 100644
--- a/chrome/browser/usb/usb_tab_helper.cc
+++ b/chrome/browser/usb/usb_tab_helper.cc
@@ -18,9 +18,9 @@
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom.h"
 
 #if defined(OS_ANDROID)
-#include "chrome/browser/android/usb/web_usb_chooser_service_android.h"
+#include "chrome/browser/android/usb/web_usb_chooser_android.h"
 #else
-#include "chrome/browser/usb/web_usb_chooser_service_desktop.h"
+#include "chrome/browser/usb/web_usb_chooser_desktop.h"
 #endif  // defined(OS_ANDROID)
 
 using content::RenderFrameHost;
@@ -37,11 +37,7 @@
 
 struct FrameUsbServices {
   std::unique_ptr<WebUSBPermissionProvider> permission_provider;
-#if defined(OS_ANDROID)
-  std::unique_ptr<WebUsbChooserServiceAndroid> chooser_service;
-#else
-  std::unique_ptr<WebUsbChooserServiceDesktop> chooser_service;
-#endif  // defined(OS_ANDROID)
+  std::unique_ptr<WebUsbChooser> usb_chooser;
   int device_connection_count_ = 0;
 };
 
@@ -66,19 +62,10 @@
     return;
   }
   WebUsbServiceImpl::Create(GetPermissionProvider(render_frame_host),
+                            GetUsbChooser(render_frame_host),
                             std::move(request));
 }
 
-void UsbTabHelper::CreateChooserService(
-    content::RenderFrameHost* render_frame_host,
-    mojo::InterfaceRequest<device::mojom::UsbChooserService> request) {
-  if (!AllowedByFeaturePolicy(render_frame_host)) {
-    mojo::ReportBadMessage(kFeaturePolicyViolation);
-    return;
-  }
-  GetChooserService(render_frame_host, std::move(request));
-}
-
 void UsbTabHelper::IncrementConnectionCount(
     RenderFrameHost* render_frame_host) {
   auto it = frame_usb_services_.find(render_frame_host);
@@ -143,19 +130,18 @@
   return frame_usb_services->permission_provider->GetWeakPtr();
 }
 
-void UsbTabHelper::GetChooserService(
-    content::RenderFrameHost* render_frame_host,
-    mojo::InterfaceRequest<device::mojom::UsbChooserService> request) {
+base::WeakPtr<WebUsbChooser> UsbTabHelper::GetUsbChooser(
+    content::RenderFrameHost* render_frame_host) {
   FrameUsbServices* frame_usb_services = GetFrameUsbService(render_frame_host);
-  if (!frame_usb_services->chooser_service) {
-    frame_usb_services->chooser_service.reset(
+  if (!frame_usb_services->usb_chooser) {
+    frame_usb_services->usb_chooser.reset(
 #if defined(OS_ANDROID)
-        new WebUsbChooserServiceAndroid(render_frame_host));
+        new WebUsbChooserAndroid(render_frame_host));
 #else
-        new WebUsbChooserServiceDesktop(render_frame_host));
+        new WebUsbChooserDesktop(render_frame_host));
 #endif  // defined(OS_ANDROID)
   }
-  frame_usb_services->chooser_service->Bind(std::move(request));
+  return frame_usb_services->usb_chooser->GetWeakPtr();
 }
 
 void UsbTabHelper::NotifyTabStateChanged() const {
diff --git a/chrome/browser/usb/usb_tab_helper.h b/chrome/browser/usb/usb_tab_helper.h
index 76eb51e..8cd7c37 100644
--- a/chrome/browser/usb/usb_tab_helper.h
+++ b/chrome/browser/usb/usb_tab_helper.h
@@ -20,15 +20,13 @@
 }  // namespace blink
 
 namespace device {
-namespace mojom {
-class UsbChooserService;
-}
-
 namespace usb {
 class PermissionProvider;
 }
 }  // namespace device
 
+class WebUsbChooser;
+
 struct FrameUsbServices;
 
 typedef std::map<content::RenderFrameHost*, std::unique_ptr<FrameUsbServices>>
@@ -47,10 +45,6 @@
       content::RenderFrameHost* render_frame_host,
       mojo::InterfaceRequest<blink::mojom::WebUsbService> request);
 
-  void CreateChooserService(
-      content::RenderFrameHost* render_frame_host,
-      mojo::InterfaceRequest<device::mojom::UsbChooserService> request);
-
   void IncrementConnectionCount(content::RenderFrameHost* render_frame_host);
   void DecrementConnectionCount(content::RenderFrameHost* render_frame_host);
   bool IsDeviceConnected() const;
@@ -69,9 +63,8 @@
   base::WeakPtr<device::usb::PermissionProvider> GetPermissionProvider(
       content::RenderFrameHost* render_frame_host);
 
-  void GetChooserService(
-      content::RenderFrameHost* render_frame_host,
-      mojo::InterfaceRequest<device::mojom::UsbChooserService> request);
+  base::WeakPtr<WebUsbChooser> GetUsbChooser(
+      content::RenderFrameHost* render_frame_host);
 
   void NotifyTabStateChanged() const;
 
diff --git a/chrome/browser/usb/web_usb_chooser.cc b/chrome/browser/usb/web_usb_chooser.cc
new file mode 100644
index 0000000..a636f534
--- /dev/null
+++ b/chrome/browser/usb/web_usb_chooser.cc
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/usb/web_usb_chooser.h"
+
+#include <utility>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/usb/usb_chooser_context.h"
+#include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/browser/usb/usb_chooser_controller.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+
+WebUsbChooser::WebUsbChooser(content::RenderFrameHost* render_frame_host)
+    : render_frame_host_(render_frame_host) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(render_frame_host);
+}
+
+WebUsbChooser::~WebUsbChooser() {}
+
+void WebUsbChooser::GetPermission(
+    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
+    blink::mojom::WebUsbService::GetPermissionCallback callback) {
+  auto* web_contents =
+      content::WebContents::FromRenderFrameHost(render_frame_host_);
+  GURL requesting_origin =
+      render_frame_host_->GetLastCommittedURL().GetOrigin();
+  GURL embedding_origin =
+      web_contents->GetMainFrame()->GetLastCommittedURL().GetOrigin();
+  auto* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  auto* context = UsbChooserContextFactory::GetForProfile(profile);
+  if (!context->CanRequestObjectPermission(requesting_origin,
+                                           embedding_origin)) {
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
+  auto controller = std::make_unique<UsbChooserController>(
+      render_frame_host_, std::move(device_filters), std::move(callback));
+  ShowChooser(std::move(controller));
+}
diff --git a/chrome/browser/usb/web_usb_chooser.h b/chrome/browser/usb/web_usb_chooser.h
new file mode 100644
index 0000000..d86bbe4c9
--- /dev/null
+++ b/chrome/browser/usb/web_usb_chooser.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef CHROME_BROWSER_USB_WEB_USB_CHOOSER_H_
+#define CHROME_BROWSER_USB_WEB_USB_CHOOSER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/bubble/bubble_reference.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
+
+namespace content {
+class RenderFrameHost;
+}
+
+class UsbChooserController;
+
+// This interface can be used by a webpage to request permission from user
+// to access a certain device.
+class WebUsbChooser {
+ public:
+  explicit WebUsbChooser(content::RenderFrameHost* render_frame_host);
+
+  virtual ~WebUsbChooser();
+
+  void GetPermission(
+      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
+      blink::mojom::WebUsbService::GetPermissionCallback callback);
+
+  virtual void ShowChooser(
+      std::unique_ptr<UsbChooserController> controller) = 0;
+
+  virtual base::WeakPtr<WebUsbChooser> GetWeakPtr() = 0;
+
+  content::RenderFrameHost* render_frame_host() { return render_frame_host_; }
+
+ private:
+  content::RenderFrameHost* const render_frame_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebUsbChooser);
+};
+
+#endif  // CHROME_BROWSER_USB_WEB_USB_CHOOSER_H_
diff --git a/chrome/browser/usb/web_usb_chooser_desktop.cc b/chrome/browser/usb/web_usb_chooser_desktop.cc
new file mode 100644
index 0000000..d2f0befb0
--- /dev/null
+++ b/chrome/browser/usb/web_usb_chooser_desktop.cc
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/usb/web_usb_chooser_desktop.h"
+
+#include <utility>
+
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_bubble_manager.h"
+#include "chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h"
+#include "chrome/browser/usb/usb_chooser_controller.h"
+#include "components/bubble/bubble_controller.h"
+#include "content/public/browser/web_contents.h"
+
+WebUsbChooserDesktop::WebUsbChooserDesktop(
+    content::RenderFrameHost* render_frame_host)
+    : WebUsbChooser(render_frame_host), weak_factory_(this) {}
+
+WebUsbChooserDesktop::~WebUsbChooserDesktop() {
+  if (bubble_)
+    bubble_->CloseBubble(BUBBLE_CLOSE_FORCED);
+}
+
+void WebUsbChooserDesktop::ShowChooser(
+    std::unique_ptr<UsbChooserController> controller) {
+  // Only one chooser bubble may be shown at a time.
+  if (bubble_)
+    bubble_->CloseBubble(BUBBLE_CLOSE_FORCED);
+
+  auto delegate = std::make_unique<ChooserBubbleDelegate>(
+      render_frame_host(), std::move(controller));
+  auto* web_contents =
+      content::WebContents::FromRenderFrameHost(render_frame_host());
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
+  if (browser)
+    bubble_ = browser->GetBubbleManager()->ShowBubble(std::move(delegate));
+}
+
+base::WeakPtr<WebUsbChooser> WebUsbChooserDesktop::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
diff --git a/chrome/browser/usb/web_usb_chooser_desktop.h b/chrome/browser/usb/web_usb_chooser_desktop.h
new file mode 100644
index 0000000..cbbe2f0
--- /dev/null
+++ b/chrome/browser/usb/web_usb_chooser_desktop.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_USB_WEB_USB_CHOOSER_DESKTOP_H_
+#define CHROME_BROWSER_USB_WEB_USB_CHOOSER_DESKTOP_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/usb/web_usb_chooser.h"
+#include "components/bubble/bubble_reference.h"
+
+// Implementation of WebUsbChooser for desktop browsers that uses a bubble to
+// display the permission prompt.
+class WebUsbChooserDesktop : public WebUsbChooser {
+ public:
+  explicit WebUsbChooserDesktop(content::RenderFrameHost* render_frame_host);
+  ~WebUsbChooserDesktop() override;
+
+  // WebUsbChooser implementation
+  void ShowChooser(std::unique_ptr<UsbChooserController> controller) override;
+
+  base::WeakPtr<WebUsbChooser> GetWeakPtr() override;
+
+ private:
+  BubbleReference bubble_;
+
+  base::WeakPtrFactory<WebUsbChooserDesktop> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(WebUsbChooserDesktop);
+};
+
+#endif  // CHROME_BROWSER_USB_WEB_USB_CHOOSER_DESKTOP_H_
diff --git a/chrome/browser/usb/web_usb_chooser_service.cc b/chrome/browser/usb/web_usb_chooser_service.cc
deleted file mode 100644
index 3771b8f..0000000
--- a/chrome/browser/usb/web_usb_chooser_service.cc
+++ /dev/null
@@ -1,53 +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.
-
-#include "chrome/browser/usb/web_usb_chooser_service.h"
-
-#include <utility>
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/usb/usb_chooser_context.h"
-#include "chrome/browser/usb/usb_chooser_context_factory.h"
-#include "chrome/browser/usb/usb_chooser_controller.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-
-WebUsbChooserService::WebUsbChooserService(
-    content::RenderFrameHost* render_frame_host)
-    : render_frame_host_(render_frame_host) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(render_frame_host);
-}
-
-WebUsbChooserService::~WebUsbChooserService() {}
-
-void WebUsbChooserService::GetPermission(
-    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
-    GetPermissionCallback callback) {
-  auto* web_contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host_);
-  GURL requesting_origin =
-      render_frame_host_->GetLastCommittedURL().GetOrigin();
-  GURL embedding_origin =
-      web_contents->GetMainFrame()->GetLastCommittedURL().GetOrigin();
-  auto* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  auto* context = UsbChooserContextFactory::GetForProfile(profile);
-  if (!context->CanRequestObjectPermission(requesting_origin,
-                                           embedding_origin)) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-
-  auto controller = std::make_unique<UsbChooserController>(
-      render_frame_host_, std::move(device_filters), std::move(callback));
-  ShowChooser(std::move(controller));
-}
-
-void WebUsbChooserService::Bind(
-    device::mojom::UsbChooserServiceRequest request) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  bindings_.AddBinding(this, std::move(request));
-}
diff --git a/chrome/browser/usb/web_usb_chooser_service.h b/chrome/browser/usb/web_usb_chooser_service.h
deleted file mode 100644
index 419a489f..0000000
--- a/chrome/browser/usb/web_usb_chooser_service.h
+++ /dev/null
@@ -1,50 +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.
-
-#ifndef CHROME_BROWSER_USB_WEB_USB_CHOOSER_SERVICE_H_
-#define CHROME_BROWSER_USB_WEB_USB_CHOOSER_SERVICE_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "components/bubble/bubble_reference.h"
-#include "device/usb/public/mojom/chooser_service.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-
-namespace content {
-class RenderFrameHost;
-}
-
-class UsbChooserController;
-
-// Implementation of the public device::usb::ChooserService interface.
-// This interface can be used by a webpage to request permission from user
-// to access a certain device.
-class WebUsbChooserService : public device::mojom::UsbChooserService {
- public:
-  explicit WebUsbChooserService(content::RenderFrameHost* render_frame_host);
-
-  ~WebUsbChooserService() override;
-
-  // device::mojom::UsbChooserService implementation
-  void GetPermission(
-      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
-      GetPermissionCallback callback) override;
-
-  void Bind(device::mojom::UsbChooserServiceRequest request);
-
-  virtual void ShowChooser(
-      std::unique_ptr<UsbChooserController> controller) = 0;
-
-  content::RenderFrameHost* render_frame_host() { return render_frame_host_; }
-
- private:
-  content::RenderFrameHost* const render_frame_host_;
-  mojo::BindingSet<device::mojom::UsbChooserService> bindings_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebUsbChooserService);
-};
-
-#endif  // CHROME_BROWSER_USB_WEB_USB_CHOOSER_SERVICE_H_
diff --git a/chrome/browser/usb/web_usb_chooser_service_desktop.cc b/chrome/browser/usb/web_usb_chooser_service_desktop.cc
deleted file mode 100644
index bc0cc909..0000000
--- a/chrome/browser/usb/web_usb_chooser_service_desktop.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/usb/web_usb_chooser_service_desktop.h"
-
-#include <utility>
-
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/chrome_bubble_manager.h"
-#include "chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h"
-#include "chrome/browser/usb/usb_chooser_controller.h"
-#include "components/bubble/bubble_controller.h"
-#include "content/public/browser/web_contents.h"
-
-WebUsbChooserServiceDesktop::WebUsbChooserServiceDesktop(
-    content::RenderFrameHost* render_frame_host)
-    : WebUsbChooserService(render_frame_host) {}
-
-WebUsbChooserServiceDesktop::~WebUsbChooserServiceDesktop() {
-  if (bubble_)
-    bubble_->CloseBubble(BUBBLE_CLOSE_FORCED);
-}
-
-void WebUsbChooserServiceDesktop::ShowChooser(
-    std::unique_ptr<UsbChooserController> controller) {
-  // Only one chooser bubble may be shown at a time.
-  if (bubble_)
-    bubble_->CloseBubble(BUBBLE_CLOSE_FORCED);
-
-  auto delegate = std::make_unique<ChooserBubbleDelegate>(
-      render_frame_host(), std::move(controller));
-  auto* web_contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host());
-  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
-  if (browser)
-    bubble_ = browser->GetBubbleManager()->ShowBubble(std::move(delegate));
-}
diff --git a/chrome/browser/usb/web_usb_chooser_service_desktop.h b/chrome/browser/usb/web_usb_chooser_service_desktop.h
deleted file mode 100644
index fe33292..0000000
--- a/chrome/browser/usb/web_usb_chooser_service_desktop.h
+++ /dev/null
@@ -1,29 +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 CHROME_BROWSER_USB_WEB_USB_CHOOSER_SERVICE_DESKTOP_H_
-#define CHROME_BROWSER_USB_WEB_USB_CHOOSER_SERVICE_DESKTOP_H_
-
-#include "base/macros.h"
-#include "chrome/browser/usb/web_usb_chooser_service.h"
-#include "components/bubble/bubble_reference.h"
-
-// Implementation of WebUsbChooserService for desktop browsers that uses a
-// bubble to display the permission prompt.
-class WebUsbChooserServiceDesktop : public WebUsbChooserService {
- public:
-  explicit WebUsbChooserServiceDesktop(
-      content::RenderFrameHost* render_frame_host);
-  ~WebUsbChooserServiceDesktop() override;
-
-  // WebUsbChooserServiceDesktop implementation
-  void ShowChooser(std::unique_ptr<UsbChooserController> controller) override;
-
- private:
-  BubbleReference bubble_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebUsbChooserServiceDesktop);
-};
-
-#endif  // CHROME_BROWSER_USB_WEB_USB_CHOOSER_SERVICE_DESKTOP_H_
diff --git a/chrome/browser/usb/web_usb_service_impl.cc b/chrome/browser/usb/web_usb_service_impl.cc
index 84e08f7..3218a27 100644
--- a/chrome/browser/usb/web_usb_service_impl.cc
+++ b/chrome/browser/usb/web_usb_service_impl.cc
@@ -15,16 +15,20 @@
 // static
 void WebUsbServiceImpl::Create(
     base::WeakPtr<device::usb::PermissionProvider> permission_provider,
+    base::WeakPtr<WebUsbChooser> usb_chooser,
     blink::mojom::WebUsbServiceRequest request) {
   // Bind the Blink request with WebUsbServiceImpl.
-  auto* web_usb_service = new WebUsbServiceImpl(std::move(permission_provider));
+  auto* web_usb_service = new WebUsbServiceImpl(std::move(permission_provider),
+                                                std::move(usb_chooser));
   web_usb_service->binding_ = mojo::MakeStrongBinding(
       base::WrapUnique(web_usb_service), std::move(request));
 }
 
 WebUsbServiceImpl::WebUsbServiceImpl(
-    base::WeakPtr<device::usb::PermissionProvider> permission_provider)
+    base::WeakPtr<device::usb::PermissionProvider> permission_provider,
+    base::WeakPtr<WebUsbChooser> usb_chooser)
     : permission_provider_(std::move(permission_provider)),
+      usb_chooser_(std::move(usb_chooser)),
       observer_(this),
       weak_factory_(this) {
   // Bind |device_manager_| to UsbDeviceManager and set error handler.
@@ -62,6 +66,15 @@
                              std::move(device_client));
 }
 
+void WebUsbServiceImpl::GetPermission(
+    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
+    GetPermissionCallback callback) {
+  if (!usb_chooser_)
+    std::move(callback).Run(nullptr);
+
+  usb_chooser_->GetPermission(std::move(device_filters), std::move(callback));
+}
+
 void WebUsbServiceImpl::SetClient(
     device::mojom::UsbDeviceManagerClientPtr client) {
   client_ = std::move(client);
diff --git a/chrome/browser/usb/web_usb_service_impl.h b/chrome/browser/usb/web_usb_service_impl.h
index 5bff68a..9dc24d9 100644
--- a/chrome/browser/usb/web_usb_service_impl.h
+++ b/chrome/browser/usb/web_usb_service_impl.h
@@ -6,10 +6,12 @@
 #define CHROME_BROWSER_USB_WEB_USB_SERVICE_IMPL_H_
 
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
+#include "chrome/browser/usb/web_usb_chooser.h"
 #include "device/usb/mojo/permission_provider.h"
 #include "device/usb/public/mojom/device.mojom.h"
 #include "device/usb/public/mojom/device_manager.mojom.h"
@@ -31,18 +33,23 @@
  public:
   static void Create(
       base::WeakPtr<device::usb::PermissionProvider> permission_provider,
+      base::WeakPtr<WebUsbChooser> usb_chooser,
       blink::mojom::WebUsbServiceRequest request);
 
   ~WebUsbServiceImpl() override;
 
  private:
   WebUsbServiceImpl(
-      base::WeakPtr<device::usb::PermissionProvider> permission_provider);
+      base::WeakPtr<device::usb::PermissionProvider> permission_provider,
+      base::WeakPtr<WebUsbChooser> usb_chooser);
 
   // blink::mojom::WebUsbService implementation:
   void GetDevices(GetDevicesCallback callback) override;
   void GetDevice(const std::string& guid,
                  device::mojom::UsbDeviceRequest device_request) override;
+  void GetPermission(
+      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
+      GetPermissionCallback callback) override;
   void SetClient(device::mojom::UsbDeviceManagerClientPtr client) override;
 
   // device::UsbService::Observer implementation:
@@ -58,6 +65,7 @@
   void OnConnectionError();
 
   base::WeakPtr<device::usb::PermissionProvider> permission_provider_;
+  base::WeakPtr<WebUsbChooser> usb_chooser_;
 
   // Used to bind with Blink.
   mojo::StrongBindingPtr<blink::mojom::WebUsbService> binding_;
diff --git a/chrome/browser/usb/web_usb_service_impl_unittest.cc b/chrome/browser/usb/web_usb_service_impl_unittest.cc
index f4620ee..376abd5 100644
--- a/chrome/browser/usb/web_usb_service_impl_unittest.cc
+++ b/chrome/browser/usb/web_usb_service_impl_unittest.cc
@@ -50,7 +50,7 @@
  protected:
   WebUsbServicePtr ConnectToService() {
     WebUsbServicePtr service;
-    WebUsbServiceImpl::Create(permission_provider_.GetWeakPtr(),
+    WebUsbServiceImpl::Create(permission_provider_.GetWeakPtr(), nullptr,
                               mojo::MakeRequest(&service));
     return service;
   }
diff --git a/chrome/browser/vr/base_compositor_delegate.cc b/chrome/browser/vr/base_compositor_delegate.cc
index bbd57d9..23c7925e1 100644
--- a/chrome/browser/vr/base_compositor_delegate.cc
+++ b/chrome/browser/vr/base_compositor_delegate.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "base/bind_helpers.h"
+#include "base/trace_event/trace_event.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_share_group.h"
 #include "ui/gl/gl_surface.h"
@@ -43,6 +45,12 @@
   return MakeContextCurrent(kMainContext);
 }
 
+void BaseCompositorDelegate::SwapSurfaceBuffers() {
+  TRACE_EVENT0("gpu", __func__);
+  DCHECK(surface_);
+  surface_->SwapBuffers(base::DoNothing());
+}
+
 bool BaseCompositorDelegate::MakeContextCurrent(ContextId context_id) {
   DCHECK(context_id > kNone && context_id < kNumContexts);
   if (curr_context_id_ == context_id)
diff --git a/chrome/browser/vr/base_compositor_delegate.h b/chrome/browser/vr/base_compositor_delegate.h
index 2d83b08b..4025b44 100644
--- a/chrome/browser/vr/base_compositor_delegate.h
+++ b/chrome/browser/vr/base_compositor_delegate.h
@@ -26,6 +26,9 @@
   bool Initialize(const scoped_refptr<gl::GLSurface>& surface) override;
   bool RunInSkiaContext(SkiaContextCallback callback) override;
 
+ protected:
+  void SwapSurfaceBuffers();
+
  private:
   enum ContextId { kNone = -1, kMainContext, kSkiaContext, kNumContexts };
 
diff --git a/chrome/browser/vr/keyboard_delegate.h b/chrome/browser/vr/keyboard_delegate.h
index 38c5c4f..6de0daa 100644
--- a/chrome/browser/vr/keyboard_delegate.h
+++ b/chrome/browser/vr/keyboard_delegate.h
@@ -16,12 +16,15 @@
 
 namespace vr {
 
+class KeyboardUiInterface;
 struct CameraModel;
+struct TextInputInfo;
 
 class VR_EXPORT KeyboardDelegate {
  public:
   virtual ~KeyboardDelegate() {}
 
+  virtual void SetUiInterface(KeyboardUiInterface* ui) {}
   virtual void ShowKeyboard() = 0;
   virtual void HideKeyboard() = 0;
   virtual void SetTransform(const gfx::Transform&) = 0;
@@ -39,6 +42,9 @@
   virtual void OnHoverMove(const gfx::PointF& position) {}
   virtual void OnButtonDown(const gfx::PointF& position) {}
   virtual void OnButtonUp(const gfx::PointF& position) {}
+
+  // Called to update GVR keyboard with the given text input info.
+  virtual void UpdateInput(const TextInputInfo& info) {}
 };
 
 }  // namespace vr
diff --git a/chrome/browser/vr/render_loop.cc b/chrome/browser/vr/render_loop.cc
index 7401ef16..2cc41fb 100644
--- a/chrome/browser/vr/render_loop.cc
+++ b/chrome/browser/vr/render_loop.cc
@@ -21,12 +21,14 @@
 namespace vr {
 
 RenderLoop::RenderLoop(std::unique_ptr<UiInterface> ui,
-                       RenderLoopBrowserInterface* browser,
                        CompositorDelegate* compositor_delegate,
+                       std::unique_ptr<ControllerDelegate> controller_delegate,
+                       RenderLoopBrowserInterface* browser,
                        size_t sliding_time_size)
     : ui_(std::move(ui)),
-      browser_(browser),
       compositor_delegate_(compositor_delegate),
+      controller_delegate_(std::move(controller_delegate)),
+      browser_(browser),
       ui_processing_time_(sliding_time_size),
       ui_controller_update_time_(sliding_time_size) {}
 RenderLoop::~RenderLoop() = default;
diff --git a/chrome/browser/vr/render_loop.h b/chrome/browser/vr/render_loop.h
index b59be28..62f61f2 100644
--- a/chrome/browser/vr/render_loop.h
+++ b/chrome/browser/vr/render_loop.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "chrome/browser/vr/controller_delegate.h"
 #include "chrome/browser/vr/sliding_average.h"
 #include "chrome/browser/vr/vr_export.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
@@ -22,6 +21,7 @@
 
 enum class VrUiTestActivityResult;
 class CompositorDelegate;
+class ControllerDelegate;
 class RenderLoopBrowserInterface;
 class UiInterface;
 struct ControllerTestInput;
@@ -38,10 +38,11 @@
  public:
   enum FrameType { kUiFrame, kWebXrFrame };
 
-  explicit RenderLoop(std::unique_ptr<UiInterface> ui,
-                      RenderLoopBrowserInterface* browser,
-                      CompositorDelegate* compositor_delegate,
-                      size_t sliding_time_size);
+  RenderLoop(std::unique_ptr<UiInterface> ui,
+             CompositorDelegate* compositor_delegate,
+             std::unique_ptr<ControllerDelegate> controller_delegate,
+             RenderLoopBrowserInterface* browser,
+             size_t sliding_time_size);
   virtual ~RenderLoop();
 
   virtual void OnPause();
@@ -52,10 +53,6 @@
       UiTestActivityExpectation ui_expectation);
 
  protected:
-  void set_controller_delegate(std::unique_ptr<ControllerDelegate> delegate) {
-    controller_delegate_ = std::move(delegate);
-  }
-
   // Position, hide and/or show UI elements, process input and update textures.
   // Returns true if the scene changed.
   void UpdateUi(const RenderInfo& render_info,
@@ -83,13 +80,13 @@
                                 bool ui_updated);
   void ReportUiActivityResultForTesting(VrUiTestActivityResult result);
 
-  RenderLoopBrowserInterface* browser_;
-
   CompositorDelegate* compositor_delegate_;
   std::unique_ptr<ControllerDelegate> controller_delegate_;
   std::unique_ptr<ControllerDelegate> controller_delegate_for_testing_;
   bool using_controller_delegate_for_testing_ = false;
 
+  RenderLoopBrowserInterface* browser_;
+
   std::unique_ptr<UiTestState> ui_test_state_;
   SlidingTimeDeltaAverage ui_processing_time_;
   SlidingTimeDeltaAverage ui_controller_update_time_;
diff --git a/chrome/browser/vr/test/ui_pixel_test.cc b/chrome/browser/vr/test/ui_pixel_test.cc
index 5d0063b..c65e78d 100644
--- a/chrome/browser/vr/test/ui_pixel_test.cc
+++ b/chrome/browser/vr/test/ui_pixel_test.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/vr/render_info.h"
 #include "chrome/browser/vr/test/animation_utils.h"
 #include "chrome/browser/vr/test/constants.h"
+#include "chrome/browser/vr/text_input_delegate.h"
 #include "third_party/skia/include/core/SkImageEncoder.h"
 #include "third_party/skia/include/core/SkStream.h"
 #include "ui/gl/gl_bindings.h"
diff --git a/chrome/browser/vr/testapp/gl_renderer.cc b/chrome/browser/vr/testapp/gl_renderer.cc
index 31eb46e..f0cdfe86 100644
--- a/chrome/browser/vr/testapp/gl_renderer.cc
+++ b/chrome/browser/vr/testapp/gl_renderer.cc
@@ -20,10 +20,6 @@
 
 namespace {
 
-void OnPresentedFrame(const gfx::PresentationFeedback& feedback) {
-  // Do nothing for now.
-}
-
 bool ClearGlErrors() {
   bool errors = false;
   while (glGetError() != GL_NO_ERROR)
@@ -33,22 +29,14 @@
 
 }  // namespace
 
-GlRenderer::GlRenderer(const scoped_refptr<gl::GLSurface>& surface,
-                       vr::VrTestContext* vr)
-    : surface_(surface), vr_(vr), weak_ptr_factory_(this) {}
+GlRenderer::GlRenderer() : weak_ptr_factory_(this) {}
 
 GlRenderer::~GlRenderer() {}
 
-bool GlRenderer::Initialize() {
-  std::unique_ptr<CompositorDelegate> compositor_delegate =
-      std::make_unique<BaseCompositorDelegate>();
-  if (!compositor_delegate->Initialize(surface_)) {
+bool GlRenderer::Initialize(const scoped_refptr<gl::GLSurface>& surface) {
+  if (!BaseCompositorDelegate::Initialize(surface))
     return false;
-  }
-
-  vr_->OnGlInitialized(std::move(compositor_delegate));
-
-  PostRenderFrameTask(gfx::SwapResult::SWAP_ACK);
+  PostRenderFrameTask();
   return true;
 }
 
@@ -61,15 +49,15 @@
   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   glClear(GL_COLOR_BUFFER_BIT);
 
-  vr_->DrawFrame();
+  vr_context_->DrawFrame();
 
   DCHECK(!ClearGlErrors());
 
-  PostRenderFrameTask(
-      surface_->SwapBuffers(base::BindRepeating(&OnPresentedFrame)));
+  SwapSurfaceBuffers();
+  PostRenderFrameTask();
 }
 
-void GlRenderer::PostRenderFrameTask(gfx::SwapResult result) {
+void GlRenderer::PostRenderFrameTask() {
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&GlRenderer::RenderFrame, weak_ptr_factory_.GetWeakPtr()),
diff --git a/chrome/browser/vr/testapp/gl_renderer.h b/chrome/browser/vr/testapp/gl_renderer.h
index 2cbd594..fdc82c1 100644
--- a/chrome/browser/vr/testapp/gl_renderer.h
+++ b/chrome/browser/vr/testapp/gl_renderer.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/vr/base_compositor_delegate.h"
 #include "ui/gfx/swap_result.h"
 
 namespace gl {
@@ -19,20 +20,18 @@
 class VrTestContext;
 
 // This class manages an OpenGL context and initiates per-frame rendering.
-class GlRenderer {
+class GlRenderer : public BaseCompositorDelegate {
  public:
-  GlRenderer(const scoped_refptr<gl::GLSurface>& surface,
-             vr::VrTestContext* vr);
+  GlRenderer();
+  ~GlRenderer() override;
 
-  virtual ~GlRenderer();
-
-  bool Initialize();
+  bool Initialize(const scoped_refptr<gl::GLSurface>& surface) override;
   void RenderFrame();
-  void PostRenderFrameTask(gfx::SwapResult result);
+  void PostRenderFrameTask();
+  void set_vr_context(VrTestContext* vr_context) { vr_context_ = vr_context; }
 
  private:
-  scoped_refptr<gl::GLSurface> surface_;
-  vr::VrTestContext* vr_;
+  VrTestContext* vr_context_;
 
   base::WeakPtrFactory<GlRenderer> weak_ptr_factory_;
 
diff --git a/chrome/browser/vr/testapp/test_keyboard_delegate.cc b/chrome/browser/vr/testapp/test_keyboard_delegate.cc
index 869c372..9a92a19 100644
--- a/chrome/browser/vr/testapp/test_keyboard_delegate.cc
+++ b/chrome/browser/vr/testapp/test_keyboard_delegate.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/vr/testapp/test_keyboard_delegate.h"
 
 #include <memory>
+#include <string>
 
 #include "base/strings/utf_string_conversion_utils.h"
 #include "base/strings/utf_string_conversions.h"
@@ -25,6 +26,10 @@
 
 TestKeyboardDelegate::~TestKeyboardDelegate() {}
 
+void TestKeyboardDelegate::SetUiInterface(KeyboardUiInterface* ui) {
+  ui_interface_ = ui;
+}
+
 void TestKeyboardDelegate::ShowKeyboard() {
   editing_ = true;
 }
diff --git a/chrome/browser/vr/testapp/test_keyboard_delegate.h b/chrome/browser/vr/testapp/test_keyboard_delegate.h
index 92dbcf7..716b858 100644
--- a/chrome/browser/vr/testapp/test_keyboard_delegate.h
+++ b/chrome/browser/vr/testapp/test_keyboard_delegate.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_VR_TESTAPP_TEST_KEYBOARD_DELEGATE_H_
 #define CHROME_BROWSER_VR_TESTAPP_TEST_KEYBOARD_DELEGATE_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "chrome/browser/vr/keyboard_delegate.h"
 #include "chrome/browser/vr/keyboard_ui_interface.h"
@@ -30,6 +32,7 @@
   TestKeyboardDelegate();
   ~TestKeyboardDelegate() override;
 
+  void SetUiInterface(KeyboardUiInterface* ui) override;
   void ShowKeyboard() override;
   void HideKeyboard() override;
   void SetTransform(const gfx::Transform& transform) override;
@@ -38,12 +41,9 @@
                gfx::Point3F* hit_position) override;
   void Draw(const CameraModel& model) override;
   bool SupportsSelection() override;
+  void UpdateInput(const vr::TextInputInfo& info) override;
 
   void Initialize(SkiaSurfaceProvider* provider, UiElementRenderer* renderer);
-  void SetUiInterface(KeyboardUiInterface* keyboard) {
-    ui_interface_ = keyboard;
-  }
-  void UpdateInput(const vr::TextInputInfo& info);
   bool HandleInput(ui::Event* e);
 
  private:
diff --git a/chrome/browser/vr/testapp/vr_test_context.cc b/chrome/browser/vr/testapp/vr_test_context.cc
index e60c7312..5205d5a 100644
--- a/chrome/browser/vr/testapp/vr_test_context.cc
+++ b/chrome/browser/vr/testapp/vr_test_context.cc
@@ -101,7 +101,9 @@
 
 }  // namespace
 
-VrTestContext::VrTestContext() : view_scale_factor_(kDefaultViewScaleFactor) {
+VrTestContext::VrTestContext(CompositorDelegate* compositor_delegate)
+    : view_scale_factor_(kDefaultViewScaleFactor),
+      compositor_delegate_(compositor_delegate) {
   base::FilePath pak_path;
   base::PathService::Get(base::DIR_MODULE, &pak_path);
   ui::ResourceBundle::InitSharedInstanceWithPakPath(
@@ -109,27 +111,22 @@
 
   base::i18n::InitializeICU();
 
-  text_input_delegate_ = std::make_unique<TextInputDelegate>();
-  keyboard_delegate_ = std::make_unique<TestKeyboardDelegate>();
+  auto text_input_delegate = std::make_unique<TextInputDelegate>();
+  auto keyboard_delegate = std::make_unique<TestKeyboardDelegate>();
+  keyboard_delegate_ = keyboard_delegate.get();
+  text_input_delegate->SetUpdateInputCallback(
+      base::BindRepeating(&TestKeyboardDelegate::UpdateInput,
+                          base::Unretained(keyboard_delegate.get())));
 
   UiInitialState ui_initial_state;
   ui_initial_state.create_tabs_view = true;
-  ui_instance_ = std::make_unique<Ui>(this, nullptr, keyboard_delegate_.get(),
-                                      text_input_delegate_.get(), nullptr,
-                                      ui_initial_state);
+  ui_instance_ = std::make_unique<Ui>(
+      this, nullptr, std::move(keyboard_delegate),
+      std::move(text_input_delegate), nullptr, ui_initial_state);
   ui_ = ui_instance_.get();
 
   LoadAssets();
 
-  text_input_delegate_->SetRequestFocusCallback(base::BindRepeating(
-      &vr::UiInterface::RequestFocus, base::Unretained(ui_)));
-  text_input_delegate_->SetRequestUnfocusCallback(base::BindRepeating(
-      &vr::UiInterface::RequestUnfocus, base::Unretained(ui_)));
-  text_input_delegate_->SetUpdateInputCallback(
-      base::BindRepeating(&TestKeyboardDelegate::UpdateInput,
-                          base::Unretained(keyboard_delegate_.get())));
-  keyboard_delegate_->SetUiInterface(ui_instance_.get());
-
   touchpad_touch_position_ = kInitialTouchPosition;
 
   model_ = ui_instance_->model_for_test();
@@ -158,10 +155,23 @@
     ui_->AddOrUpdateTab(tab_id_++, true,
                         base::UTF8ToUTF16("VR - Google Search"));
   }
+
+  InitializeGl();
 }
 
 VrTestContext::~VrTestContext() = default;
 
+void VrTestContext::InitializeGl() {
+  unsigned int content_texture_id = CreateTexture(0xFF000080);
+  unsigned int ui_texture_id = CreateTexture(0xFF008000);
+  ui_->OnGlInitialized(content_texture_id, kGlTextureLocationLocal,
+                       content_texture_id, kGlTextureLocationLocal,
+                       ui_texture_id);
+  keyboard_delegate_->Initialize(
+      ui_instance_->scene()->SurfaceProviderForTesting(),
+      ui_instance_->ui_element_renderer());
+}
+
 void VrTestContext::DrawFrame() {
   base::TimeTicks current_time = base::TimeTicks::Now();
 
@@ -455,21 +465,6 @@
   return controller_model;
 }
 
-void VrTestContext::OnGlInitialized(
-    std::unique_ptr<CompositorDelegate> compositor_delegate) {
-  compositor_delegate_ = std::move(compositor_delegate);
-  unsigned int content_texture_id = CreateTexture(0xFF000080);
-  unsigned int ui_texture_id = CreateTexture(0xFF008000);
-
-  ui_->OnGlInitialized(content_texture_id, kGlTextureLocationLocal,
-                       content_texture_id, kGlTextureLocationLocal,
-                       ui_texture_id);
-
-  keyboard_delegate_->Initialize(
-      ui_instance_->scene()->SurfaceProviderForTesting(),
-      ui_instance_->ui_element_renderer());
-}
-
 unsigned int VrTestContext::CreateTexture(SkColor color) {
   sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(1, 1);
   SkCanvas* canvas = surface->getCanvas();
diff --git a/chrome/browser/vr/testapp/vr_test_context.h b/chrome/browser/vr/testapp/vr_test_context.h
index d74ed06..4c4fdd4 100644
--- a/chrome/browser/vr/testapp/vr_test_context.h
+++ b/chrome/browser/vr/testapp/vr_test_context.h
@@ -24,7 +24,6 @@
 namespace vr {
 
 class CompositorDelegate;
-class TextInputDelegate;
 class TestKeyboardDelegate;
 class Ui;
 struct Model;
@@ -33,10 +32,9 @@
 // manipulates the UI according to user input.
 class VrTestContext : public vr::UiBrowserInterface {
  public:
-  VrTestContext();
+  explicit VrTestContext(CompositorDelegate* compositor_delgate);
   ~VrTestContext() override;
 
-  void OnGlInitialized(std::unique_ptr<CompositorDelegate> compositor_delegate);
   // TODO(vollick): we should refactor VrShellGl's rendering logic and use it
   // directly. crbug.com/767282
   void DrawFrame();
@@ -74,6 +72,7 @@
   void set_window_size(const gfx::Size& size) { window_size_ = size; }
 
  private:
+  void InitializeGl();
   unsigned int CreateTexture(SkColor color);
   void CreateFakeVoiceSearchResult();
   void CycleWebVrModes();
@@ -118,9 +117,8 @@
   int tab_id_ = 0;
   bool hosted_ui_enabled_ = false;
 
-  std::unique_ptr<TextInputDelegate> text_input_delegate_;
-  std::unique_ptr<TestKeyboardDelegate> keyboard_delegate_;
-  std::unique_ptr<CompositorDelegate> compositor_delegate_;
+  CompositorDelegate* compositor_delegate_;
+  TestKeyboardDelegate* keyboard_delegate_;
 
   PlatformController::Handedness handedness_ = PlatformController::kRightHanded;
 
diff --git a/chrome/browser/vr/testapp/vr_testapp.cc b/chrome/browser/vr/testapp/vr_testapp.cc
index 5615de0b..8dcb8c0 100644
--- a/chrome/browser/vr/testapp/vr_testapp.cc
+++ b/chrome/browser/vr/testapp/vr_testapp.cc
@@ -12,6 +12,7 @@
 #include "base/task/task_scheduler/task_scheduler.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
+#include "chrome/browser/vr/base_compositor_delegate.h"
 #include "chrome/browser/vr/testapp/gl_renderer.h"
 #include "chrome/browser/vr/testapp/vr_test_context.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -45,8 +46,7 @@
   ~RendererFactory();
 
   bool Initialize();
-  std::unique_ptr<vr::GlRenderer> CreateRenderer(gfx::AcceleratedWidget widget,
-                                                 vr::VrTestContext* vr);
+  std::unique_ptr<vr::GlRenderer> CreateRenderer(gfx::AcceleratedWidget widget);
 
  private:
   // Helper for applications that do GL on main thread.
@@ -100,7 +100,6 @@
             const gfx::Rect& bounds)
       : window_manager_(window_manager),
         renderer_factory_(renderer_factory),
-        vr_(std::make_unique<vr::VrTestContext>()),
         weak_ptr_factory_(this) {
     ui::PlatformWindowInitProperties properties;
     properties.bounds = gfx::Rect(1024, 768);
@@ -131,10 +130,12 @@
 
   // PlatformWindowDelegate:
   void OnBoundsChanged(const gfx::Rect& new_bounds) override {
-    vr_->set_window_size(new_bounds.size());
+    vr_context_->set_window_size(new_bounds.size());
   }
   void OnDamageRect(const gfx::Rect& damaged_region) override {}
-  void DispatchEvent(ui::Event* event) override { vr_->HandleInput(event); }
+  void DispatchEvent(ui::Event* event) override {
+    vr_context_->HandleInput(event);
+  }
   void OnCloseRequest() override { Quit(); }
   void OnClosed() override {}
   void OnWindowStateChanged(ui::PlatformWindowState new_state) override {}
@@ -150,15 +151,15 @@
   // Since we pretend to have a GPU process, we should also pretend to
   // initialize the GPU resources via a posted task.
   void StartOnGpu() {
-    renderer_ =
-        renderer_factory_->CreateRenderer(GetAcceleratedWidget(), vr_.get());
-    renderer_->Initialize();
+    renderer_ = renderer_factory_->CreateRenderer(GetAcceleratedWidget());
+    vr_context_ = std::make_unique<vr::VrTestContext>(renderer_.get());
+    renderer_->set_vr_context(vr_context_.get());
   }
 
   WindowManager* window_manager_;      // Not owned.
   RendererFactory* renderer_factory_;  // Not owned.
 
-  std::unique_ptr<vr::VrTestContext> vr_;
+  std::unique_ptr<vr::VrTestContext> vr_context_;
   std::unique_ptr<vr::GlRenderer> renderer_;
 
   // Window-related state.
@@ -187,14 +188,15 @@
 }
 
 std::unique_ptr<vr::GlRenderer> RendererFactory::CreateRenderer(
-    gfx::AcceleratedWidget widget,
-    vr::VrTestContext* vr) {
+    gfx::AcceleratedWidget widget) {
   scoped_refptr<gl::GLSurface> surface = gl::init::CreateViewGLSurface(widget);
   if (!surface) {
     LOG(FATAL) << "Failed to create GL surface";
     return nullptr;
   }
-  return std::make_unique<vr::GlRenderer>(surface, vr);
+  auto renderer = std::make_unique<vr::GlRenderer>();
+  CHECK(renderer->Initialize(surface));
+  return renderer;
 }
 
 WindowManager::WindowManager(const base::Closure& quit_closure)
diff --git a/chrome/browser/vr/ui.cc b/chrome/browser/vr/ui.cc
index e0fb1c92..1d65628 100644
--- a/chrome/browser/vr/ui.cc
+++ b/chrome/browser/vr/ui.cc
@@ -77,38 +77,48 @@
 
 Ui::Ui(UiBrowserInterface* browser,
        PlatformInputHandler* content_input_forwarder,
-       KeyboardDelegate* keyboard_delegate,
-       TextInputDelegate* text_input_delegate,
-       AudioDelegate* audio_delegate,
+       std::unique_ptr<KeyboardDelegate> keyboard_delegate,
+       std::unique_ptr<TextInputDelegate> text_input_delegate,
+       std::unique_ptr<AudioDelegate> audio_delegate,
        const UiInitialState& ui_initial_state)
     : Ui(browser,
          std::make_unique<ContentInputDelegate>(content_input_forwarder),
-         keyboard_delegate,
-         text_input_delegate,
-         audio_delegate,
+         std::move(keyboard_delegate),
+         std::move(text_input_delegate),
+         std::move(audio_delegate),
          ui_initial_state) {}
 
 Ui::Ui(UiBrowserInterface* browser,
        std::unique_ptr<ContentInputDelegate> content_input_delegate,
-       KeyboardDelegate* keyboard_delegate,
-       TextInputDelegate* text_input_delegate,
-       AudioDelegate* audio_delegate,
+       std::unique_ptr<KeyboardDelegate> keyboard_delegate,
+       std::unique_ptr<TextInputDelegate> text_input_delegate,
+       std::unique_ptr<AudioDelegate> audio_delegate,
        const UiInitialState& ui_initial_state)
     : browser_(browser),
       scene_(std::make_unique<UiScene>()),
       model_(std::make_unique<Model>()),
       content_input_delegate_(std::move(content_input_delegate)),
       input_manager_(std::make_unique<UiInputManager>(scene_.get())),
-      audio_delegate_(audio_delegate),
+      keyboard_delegate_(std::move(keyboard_delegate)),
+      text_input_delegate_(std::move(text_input_delegate)),
+      audio_delegate_(std::move(audio_delegate)),
       weak_ptr_factory_(this) {
   UiInitialState state = ui_initial_state;
-  if (keyboard_delegate != nullptr)
-    state.supports_selection = keyboard_delegate->SupportsSelection();
+  if (text_input_delegate_) {
+    text_input_delegate_->SetRequestFocusCallback(
+        base::BindRepeating(&Ui::RequestFocus, base::Unretained(this)));
+    text_input_delegate_->SetRequestUnfocusCallback(
+        base::BindRepeating(&Ui::RequestUnfocus, base::Unretained(this)));
+  }
+  if (keyboard_delegate_) {
+    keyboard_delegate_->SetUiInterface(this);
+    state.supports_selection = keyboard_delegate_->SupportsSelection();
+  }
   InitializeModel(state);
 
   UiSceneCreator(browser, scene_.get(), this, content_input_delegate_.get(),
-                 keyboard_delegate, text_input_delegate, audio_delegate,
-                 model_.get())
+                 keyboard_delegate_.get(), text_input_delegate_.get(),
+                 audio_delegate_.get(), model_.get())
       .CreateScene();
 }
 
diff --git a/chrome/browser/vr/ui.h b/chrome/browser/vr/ui.h
index 98095bf..de54a5a 100644
--- a/chrome/browser/vr/ui.h
+++ b/chrome/browser/vr/ui.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <queue>
+#include <utility>
 #include <vector>
 
 #include "base/macros.h"
@@ -50,16 +51,16 @@
  public:
   Ui(UiBrowserInterface* browser,
      PlatformInputHandler* content_input_forwarder,
-     KeyboardDelegate* keyboard_delegate,
-     TextInputDelegate* text_input_delegate,
-     AudioDelegate* audio_delegate,
+     std::unique_ptr<KeyboardDelegate> keyboard_delegate,
+     std::unique_ptr<TextInputDelegate> text_input_delegate,
+     std::unique_ptr<AudioDelegate> audio_delegate,
      const UiInitialState& ui_initial_state);
 
   Ui(UiBrowserInterface* browser,
      std::unique_ptr<ContentInputDelegate> content_input_delegate,
-     KeyboardDelegate* keyboard_delegate,
-     TextInputDelegate* text_input_delegate,
-     AudioDelegate* audio_delegate,
+     std::unique_ptr<KeyboardDelegate> keyboard_delegate,
+     std::unique_ptr<TextInputDelegate> text_input_delegate,
+     std::unique_ptr<AudioDelegate> audio_delegate,
      const UiInitialState& ui_initial_state);
 
   ~Ui() override;
@@ -189,15 +190,14 @@
       const FovRectangle& fov_recommended_right,
       float z_near) override;
 
-  void RequestFocus(int element_id) override;
-  void RequestUnfocus(int element_id) override;
-
   // KeyboardUiInterface
   void OnInputEdited(const EditedText& info) override;
   void OnInputCommitted(const EditedText& info) override;
   void OnKeyboardHidden() override;
 
  private:
+  void RequestFocus(int element_id);
+  void RequestUnfocus(int element_id);
   void OnMenuButtonClicked();
   void OnSpeechRecognitionEnded();
   void InitializeModel(const UiInitialState& ui_initial_state);
@@ -222,7 +222,9 @@
   // frame.
   ContentElement* content_element_ = nullptr;
 
-  AudioDelegate* audio_delegate_ = nullptr;
+  std::unique_ptr<KeyboardDelegate> keyboard_delegate_;
+  std::unique_ptr<TextInputDelegate> text_input_delegate_;
+  std::unique_ptr<AudioDelegate> audio_delegate_;
 
   base::WeakPtrFactory<Ui> weak_ptr_factory_;
 
diff --git a/chrome/browser/vr/ui_factory.cc b/chrome/browser/vr/ui_factory.cc
index 059ed31..c288cd0 100644
--- a/chrome/browser/vr/ui_factory.cc
+++ b/chrome/browser/vr/ui_factory.cc
@@ -4,7 +4,12 @@
 
 #include "chrome/browser/vr/ui_factory.h"
 
+#include <utility>
+
+#include "chrome/browser/vr/audio_delegate.h"
 #include "chrome/browser/vr/content_input_delegate.h"
+#include "chrome/browser/vr/keyboard_delegate.h"
+#include "chrome/browser/vr/text_input_delegate.h"
 #include "chrome/browser/vr/ui.h"
 
 namespace vr {
@@ -12,13 +17,14 @@
 std::unique_ptr<UiInterface> UiFactory::Create(
     UiBrowserInterface* browser,
     PlatformInputHandler* content_input_forwarder,
-    KeyboardDelegate* keyboard_delegate,
-    TextInputDelegate* text_input_delegate,
-    AudioDelegate* audio_delegate,
+    std::unique_ptr<KeyboardDelegate> keyboard_delegate,
+    std::unique_ptr<TextInputDelegate> text_input_delegate,
+    std::unique_ptr<AudioDelegate> audio_delegate,
     const UiInitialState& ui_initial_state) {
   return std::make_unique<Ui>(browser, content_input_forwarder,
-                              keyboard_delegate, text_input_delegate,
-                              audio_delegate, ui_initial_state);
+                              std::move(keyboard_delegate),
+                              std::move(text_input_delegate),
+                              std::move(audio_delegate), ui_initial_state);
 }
 
 }  // namespace vr
diff --git a/chrome/browser/vr/ui_factory.h b/chrome/browser/vr/ui_factory.h
index 935e4ed..023935b 100644
--- a/chrome/browser/vr/ui_factory.h
+++ b/chrome/browser/vr/ui_factory.h
@@ -24,9 +24,9 @@
   static std::unique_ptr<UiInterface> Create(
       UiBrowserInterface* browser,
       PlatformInputHandler* content_input_forwarder,
-      KeyboardDelegate* keyboard_delegate,
-      TextInputDelegate* text_input_delegate,
-      AudioDelegate* audio_delegate,
+      std::unique_ptr<KeyboardDelegate> keyboard_delegate,
+      std::unique_ptr<TextInputDelegate> text_input_delegate,
+      std::unique_ptr<AudioDelegate> audio_delegate,
       const UiInitialState& ui_initial_state);
 };
 
diff --git a/chrome/browser/vr/ui_interface.h b/chrome/browser/vr/ui_interface.h
index 3054191..ba2fdef8 100644
--- a/chrome/browser/vr/ui_interface.h
+++ b/chrome/browser/vr/ui_interface.h
@@ -105,8 +105,6 @@
                            ReticleModel* reticle_model,
                            InputEventList* input_event_list) = 0;
   virtual void HandleMenuButtonEvents(InputEventList* input_event_list) = 0;
-  virtual void RequestFocus(int element_id) = 0;
-  virtual void RequestUnfocus(int element_id) = 0;
 
   // This function calculates the minimal FOV (in degrees) which covers all
   // visible overflow elements as if it was viewing from fov_recommended. For
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 11d9855..092c8d5c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3777,6 +3777,7 @@
       "//extensions/strings",
       "//google_apis",
       "//media/cast:test_support",
+      "//services/network/public/mojom",
 
       # This will add all of the unit tests for the schema compiler to this
       # target.
diff --git a/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/manifest.json b/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/manifest.json
new file mode 100644
index 0000000..2d48b9e
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/manifest.json
@@ -0,0 +1,7 @@
+{
+  "name" : "Content Settings API Test Extension",
+  "version" : "0.1",
+  "manifest_version": 2,
+  "description" : "Content Settings API Test Extension",
+  "permissions": [ "contentSettings" ]
+}
diff --git a/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/test.html b/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/test.html
new file mode 100644
index 0000000..8a5b4b6
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/test.html
@@ -0,0 +1,6 @@
+<!--
+ * 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.
+-->
+<script src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/test.js b/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/test.js
new file mode 100644
index 0000000..4728ae7
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_settings/embeddedsettingsmetric/test.js
@@ -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.
+
+var cs = chrome.contentSettings;
+
+chrome.test.runTests([
+  function setEmbeddedSettings() {
+    let callsCompleted = 0;
+    function setComplete() {
+      ++callsCompleted;
+      if (callsCompleted == 4) {
+        chrome.test.succeed()
+      }
+    }
+
+    // Embedded patterns.
+    cs['images'].set({
+      'primaryPattern': 'http://google.com/*',
+      'secondaryPattern': 'http://example.com/*',
+      'setting': 'allow'
+    }, setComplete);
+    cs['location'].set({
+      'primaryPattern': 'http://google.com/*',
+      'secondaryPattern': 'http://example.com/*',
+      'setting': 'allow'
+    }, setComplete);
+
+    // Top level patterns.
+    cs['images'].set({
+      'primaryPattern': 'http://google.com/*',
+      'secondaryPattern': 'http://google.com/*',
+      'setting': 'allow'
+    }, setComplete);
+    cs['cookies'].set({
+      'primaryPattern': 'http://google.com/*',
+      'secondaryPattern': '<all_urls>',
+      'setting': 'allow'
+    }, setComplete);
+  },
+]);
diff --git a/chrome/test/data/password/gaia_reath_form.html b/chrome/test/data/password/gaia_reath_form.html
new file mode 100644
index 0000000..7db9b36
--- /dev/null
+++ b/chrome/test/data/password/gaia_reath_form.html
@@ -0,0 +1,17 @@
+
+<!--
+    A form that emulates a Gaia reath form, namely it contains 2 hidden fields
+    with name "rart" and "continue".
+ -->
+<html>
+<body>
+<form action="done.html" id="gaiaReath">
+  <input type="email" name="identifier" autocomplete="username">
+  <input type="password" name="password" autocomplete="password">
+  <input type="hidden" name="rart">
+  <input type="hidden" name="continue" value="passwords.google.com">
+</form>
+
+</body>
+</html>
+
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/AsyncTaskRunner.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/AsyncTaskRunner.java
index 1c89d7a..1f17b26 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/AsyncTaskRunner.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/AsyncTaskRunner.java
@@ -24,10 +24,6 @@
         mExecutor = executor;
     }
 
-    public AsyncTaskRunner() {
-        mExecutor = null;
-    }
-
     /**
      * Schedules work on this runner's executor, with the result provided to the given callback.
      *
@@ -48,11 +44,7 @@
                 callback.accept(result);
             }
         };
-        if (mExecutor != null) {
-            asyncTask.executeOnExecutor(mExecutor);
-        } else {
-            asyncTask.execute();
-        }
+        asyncTask.executeOnExecutor(mExecutor);
         return () -> asyncTask.cancel(false);
     }
 }
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/ExternalServiceDeviceLogcatProvider.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/ExternalServiceDeviceLogcatProvider.java
index 42d6342..bc98f86 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/ExternalServiceDeviceLogcatProvider.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/ExternalServiceDeviceLogcatProvider.java
@@ -11,6 +11,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 
+import org.chromium.base.AsyncTask;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 
@@ -45,7 +46,7 @@
 
                 ServiceConnection conn = this;
 
-                new AsyncTaskRunner().doAsync(() -> {
+                new AsyncTaskRunner(AsyncTask.THREAD_POOL_EXECUTOR).doAsync(() -> {
                     String logsFileName = "";
                     try {
                         // getLogs() currently gives us the filename of the location of the logs
diff --git a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/AsyncTaskRunnerTest.java b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/AsyncTaskRunnerTest.java
index d0f6d57..6e67481 100644
--- a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/AsyncTaskRunnerTest.java
+++ b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/AsyncTaskRunnerTest.java
@@ -10,7 +10,6 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.asynctask.ShadowAsyncTask;
@@ -44,14 +43,6 @@
     }
 
     @Test
-    public void testHappyPath() {
-        List<Integer> result = new ArrayList<>();
-        new AsyncTaskRunner().doAsync(() -> 45, result::add);
-        Robolectric.flushBackgroundThreadScheduler();
-        assertThat(result, contains(45));
-    }
-
-    @Test
     public void testSchedulesOnExecutor() {
         List<Integer> result = new ArrayList<>();
         TestExecutor executor = new TestExecutor();
diff --git a/chromeos/components/drivefs/drivefs_host.cc b/chromeos/components/drivefs/drivefs_host.cc
index 1b86e48c..22f58eb 100644
--- a/chromeos/components/drivefs/drivefs_host.cc
+++ b/chromeos/components/drivefs/drivefs_host.cc
@@ -24,7 +24,7 @@
 constexpr char kMountScheme[] = "drivefs://";
 constexpr char kDataPath[] = "GCache/v2";
 constexpr char kIdentityConsumerId[] = "drivefs";
-const base::TimeDelta kMountTimeout = base::TimeDelta::FromSeconds(10);
+constexpr base::TimeDelta kMountTimeout = base::TimeDelta::FromMinutes(1);
 
 class MojoConnectionDelegateImpl : public DriveFsHost::MojoConnectionDelegate {
  public:
diff --git a/chromeos/network/onc/onc_validator.cc b/chromeos/network/onc/onc_validator.cc
index 1045a6a..f2a2723 100644
--- a/chromeos/network/onc/onc_validator.cc
+++ b/chromeos/network/onc/onc_validator.cc
@@ -394,8 +394,8 @@
   msg << "Found value '" << actual_value
       << "', but expected a value in the range [" << lower_bound << ", "
       << upper_bound << "] (boundaries inclusive)";
-  path_.pop_back();
   AddValidationIssue(true /* is_error */, msg.str());
+  path_.pop_back();
   return true;
 }
 
@@ -421,8 +421,8 @@
   path_.push_back(field_name);
   std::ostringstream msg;
   msg << "Found an empty string, but expected a non-empty string.";
-  path_.pop_back();
   AddValidationIssue(true /* is_error */, msg.str());
+  path_.pop_back();
   return true;
 }
 
@@ -473,15 +473,16 @@
     path_.push_back(::onc::wifi::kSSID);
     std::ostringstream msg;
     msg << kInvalidLength;
-    path_.pop_back();
     // If the HexSSID field is present, ignore errors in SSID because these
     // might be caused by the usage of a non-UTF-8 encoding when the SSID
     // field was automatically added (see FillInHexSSIDField).
     if (!object->HasKey(::onc::wifi::kHexSSID)) {
       AddValidationIssue(true /* is_error */, msg.str());
+      path_.pop_back();
       return false;
     }
     AddValidationIssue(false /* is_error */, msg.str());
+    path_.pop_back();
   }
 
   // Check HexSSID validity.
@@ -493,8 +494,8 @@
       path_.push_back(::onc::wifi::kHexSSID);
       std::ostringstream msg;
       msg << "Not a valid hex representation: '" << hex_ssid_string << "'";
-      path_.pop_back();
       AddValidationIssue(true /* is_error */, msg.str());
+      path_.pop_back();
       return false;
     }
     if (decoded_ssid.size() <= 0 ||
@@ -502,8 +503,8 @@
       path_.push_back(::onc::wifi::kHexSSID);
       std::ostringstream msg;
       msg << kInvalidLength;
-      path_.pop_back();
       AddValidationIssue(true /* is_error */, msg.str());
+      path_.pop_back();
       return false;
     }
 
@@ -518,8 +519,8 @@
         std::ostringstream msg;
         msg << "Fields '" << ::onc::wifi::kSSID << "' and '"
             << ::onc::wifi::kHexSSID << "' contain inconsistent values.";
-        path_.pop_back();
         AddValidationIssue(false /* is_error */, msg.str());
+        path_.pop_back();
         object->RemoveWithoutPathExpansion(::onc::wifi::kSSID, nullptr);
       }
     }
@@ -547,8 +548,8 @@
       path_.push_back(key_guid);
       std::ostringstream msg;
       msg << "Found a duplicate GUID '" << guid << "'.";
-      path_.pop_back();
       AddValidationIssue(true /* is_error */, msg.str());
+      path_.pop_back();
       return false;
     }
     guids->insert(guid);
@@ -1033,8 +1034,8 @@
     path_.push_back(::onc::kRemove);
     std::ostringstream msg;
     msg << "Removal of certificates is not supported.";
-    path_.pop_back();
     AddValidationIssue(true /* is_error */, msg.str());
+    path_.pop_back();
     return false;
   }
 
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index 7a80e06..849f50e 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -844,6 +844,13 @@
   return true;
 }
 
+bool HasGaiaSchemeAndHost(const WebFormElement& form) {
+  GURL form_url = form.GetDocument().Url();
+  GURL gaia_url = GaiaUrls::GetInstance()->gaia_url();
+  return form_url.scheme() == gaia_url.scheme() &&
+         form_url.host() == gaia_url.host();
+}
+
 }  // namespace
 
 AutocompleteFlag AutocompleteFlagForElement(const WebInputElement& element) {
@@ -864,10 +871,8 @@
 }
 
 bool IsGaiaReauthenticationForm(const blink::WebFormElement& form) {
-  if (GURL(form.GetDocument().Url()).GetOrigin() !=
-      GaiaUrls::GetInstance()->gaia_url().GetOrigin()) {
+  if (!HasGaiaSchemeAndHost(form))
     return false;
-  }
 
   bool has_rart_field = false;
   bool has_continue_field = false;
@@ -895,16 +900,14 @@
       has_continue_field = true;
     }
   }
-
   return has_rart_field && has_continue_field;
 }
 
 bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form) {
-  GURL url(form.GetDocument().Url());
-  if (url.GetOrigin() != GaiaUrls::GetInstance()->gaia_url().GetOrigin()) {
+  if (!HasGaiaSchemeAndHost(form))
     return false;
-  }
 
+  GURL url(form.GetDocument().Url());
   std::string should_skip_password;
   if (!net::GetValueForKeyInQuery(url, "ssp", &should_skip_password))
     return false;
diff --git a/components/cronet/android/api.txt b/components/cronet/android/api.txt
index 7b2acb6..cbc5450 100644
--- a/components/cronet/android/api.txt
+++ b/components/cronet/android/api.txt
@@ -112,7 +112,6 @@
   public abstract java.lang.String getDefaultUserAgent();
   public abstract org.chromium.net.ExperimentalCronetEngine build();
   public org.chromium.net.ICronetEngineBuilder enableNetworkQualityEstimator(boolean);
-  public org.chromium.net.ICronetEngineBuilder setCertVerifierData(java.lang.String);
   public org.chromium.net.ICronetEngineBuilder setThreadPriority(int);
 }
 public final class org.chromium.net.InlineExecutionProhibitedException extends java.util.concurrent.RejectedExecutionException {
@@ -295,4 +294,4 @@
   public abstract java.lang.String getProxyServer();
   public abstract long getReceivedByteCount();
 }
-Stamp: f4909b6572d83edd28472e7087b0e6f2
+Stamp: fadacc3f49758e52a33a1ae7f032ec97
diff --git a/components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java b/components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java
index 7482fae..1f6694d 100644
--- a/components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java
+++ b/components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java
@@ -48,10 +48,6 @@
         return this;
     }
 
-    public ICronetEngineBuilder setCertVerifierData(String certVerifierData) {
-        return this;
-    }
-
     public ICronetEngineBuilder setThreadPriority(int priority) {
         return this;
     }
diff --git a/components/cronet/android/api_version.txt b/components/cronet/android/api_version.txt
index b4de394..48082f72 100644
--- a/components/cronet/android/api_version.txt
+++ b/components/cronet/android/api_version.txt
@@ -1 +1 @@
-11
+12
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 3377a3e..b85e0c8 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -491,7 +491,7 @@
   // typed. If we can successfully generate a URL_WHAT_YOU_TYPED match doing
   // that, then we use this. These matches are marked as generated by the
   // HistoryURLProvider so we only generate them if this provider is present.
-  if (control_key_state_ == DOWN_WITHOUT_CHANGE && !is_keyword_selected() &&
+  if (control_key_state_ == DOWN && !is_keyword_selected() &&
       autocomplete_controller()->history_url_provider()) {
     // Generate a new AutocompleteInput, copying the latest one but using "com"
     // as the desired TLD. Then use this autocomplete input to generate a
@@ -912,7 +912,10 @@
   // focus can be regained without an accompanying call to
   // OmniboxView::SetFocus(), e.g. by tabbing in.
   SetFocusState(OMNIBOX_FOCUS_VISIBLE, OMNIBOX_FOCUS_CHANGE_EXPLICIT);
-  control_key_state_ = control_down ? DOWN_WITHOUT_CHANGE : UP;
+  // On focusing the omnibox, if the ctrl key is pressed, we don't want to
+  // trigger ctrl-enter behavior unless it is released and re-pressed. For
+  // example, if the user presses ctrl-l to focus the omnibox.
+  control_key_state_ = control_down ? DOWN_AND_CONSUMED : UP;
 
   // Try to get ZeroSuggest suggestions if a page is loaded and the user has
   // not been typing in the omnibox.  The |user_input_in_progress_| check is
@@ -945,6 +948,11 @@
   }
 }
 
+void OmniboxEditModel::ConsumeCtrlKey() {
+  if (control_key_state_ == DOWN)
+    control_key_state_ = DOWN_AND_CONSUMED;
+}
+
 void OmniboxEditModel::OnWillKillFocus() {
   if (user_input_in_progress_ || !in_revert_)
     client_->OnInputStateChanged();
@@ -1007,7 +1015,7 @@
 
 void OmniboxEditModel::OnControlKeyChanged(bool pressed) {
   if (pressed == (control_key_state_ == UP))
-    control_key_state_ = pressed ? DOWN_WITHOUT_CHANGE : UP;
+    control_key_state_ = pressed ? DOWN : UP;
 }
 
 void OmniboxEditModel::OnPaste() {
@@ -1077,16 +1085,15 @@
       inline_autocomplete_text_.clear();
       view_->OnInlineAutocompleteTextCleared();
     }
-    if (control_key_state_ == DOWN_WITHOUT_CHANGE) {
-      // Arrowing around the popup cancels control-enter.
-      control_key_state_ = DOWN_WITH_CHANGE;
-      // Now things are a bit screwy: the desired_tld has changed, but if we
-      // update the popup, the new order of entries won't match the old, so the
-      // user's selection gets screwy; and if we don't update the popup, and the
-      // user reverts, then the selected item will be as if control is still
-      // pressed, even though maybe it isn't any more.  There is no obvious
-      // right answer here :(
-    }
+    // Arrowing around the popup cancels control-enter.
+    ConsumeCtrlKey();
+    // Now things are a bit screwy: the desired_tld has changed, but if we
+    // update the popup, the new order of entries won't match the old, so the
+    // user's selection gets screwy; and if we don't update the popup, and the
+    // user reverts, then the selected item will be as if control is still
+    // pressed, even though maybe it isn't any more.  There is no obvious
+    // right answer here :(
+
     const AutocompleteMatch& match = CurrentMatch(nullptr);
     view_->OnTemporaryTextMaybeChanged(MaybeStripKeyword(text), match,
                                        save_original_selection, true);
@@ -1167,11 +1174,10 @@
     SetFocusState(OMNIBOX_FOCUS_VISIBLE, OMNIBOX_FOCUS_CHANGE_TYPING);
   }
 
-  // If something has changed while the control key is down, prevent
-  // "ctrl-enter" until the control key is released.
-  if ((state_changes.text_differs || state_changes.selection_differs) &&
-      (control_key_state_ == DOWN_WITHOUT_CHANGE))
-    control_key_state_ = DOWN_WITH_CHANGE;
+  // When the user performs an action with the ctrl key pressed down, we assume
+  // the ctrl key was intended for that action. If they then press enter without
+  // releasing the ctrl key, we prevent "ctrl-enter" behavior.
+  ConsumeCtrlKey();
 
   // If the user text does not need to be changed, return now, so we don't
   // change any other state, lest arrowing around the omnibox do something like
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index b07f476..47967cac 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -290,6 +290,10 @@
   // switching tabs.
   void SetCaretVisibility(bool visible);
 
+  // If the ctrl key is down, marks it as consumed to prevent it from triggering
+  // ctrl-enter behavior unless it is released and re-pressed.
+  void ConsumeCtrlKey();
+
   // Sent before |OnKillFocus| and before the popup is closed.
   void OnWillKillFocus();
 
@@ -360,6 +364,9 @@
 
  private:
   friend class OmniboxControllerTest;
+  FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelTest, ConsumeCtrlKey);
+  FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelTest, ConsumeCtrlKeyOnRequestFocus);
+  FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelTest, ConsumeCtrlKeyOnCtrlAction);
 
   enum PasteState {
     NONE,           // Most recent edit was not a paste.
@@ -373,17 +380,14 @@
   };
 
   enum ControlKeyState {
-    UP,                   // The control key is not depressed.
-    DOWN_WITHOUT_CHANGE,  // The control key is depressed, and the edit's
-                          // contents/selection have not changed since it was
-                          // depressed.  This is the only state in which we
-                          // do the "ctrl-enter" behavior when the user hits
-                          // enter.
-    DOWN_WITH_CHANGE,     // The control key is depressed, and the edit's
-                          // contents/selection have changed since it was
-                          // depressed.  If the user now hits enter, we assume
-                          // they simply haven't released the key, rather than
-                          // that they intended to hit "ctrl-enter".
+    UP,                // The control key is not depressed.
+    DOWN,              // The control key is depressed and should trigger the
+                       // "ctrl-enter" behavior when the user hits enter.
+    DOWN_AND_CONSUMED  // The control key is depressed, but has been consumed
+                       // and should not trigger the "ctrl-enter" behavior.
+                       // The control key becomes consumed if it has been used
+                       // for another action such as focusing the location bar
+                       // with ctrl-l or copying the selected text with ctrl-c.
   };
 
   // Returns true if a query to an autocomplete provider is currently
diff --git a/components/omnibox/browser/omnibox_edit_model_unittest.cc b/components/omnibox/browser/omnibox_edit_model_unittest.cc
index 831c13f..f540571 100644
--- a/components/omnibox/browser/omnibox_edit_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_edit_model_unittest.cc
@@ -261,3 +261,38 @@
   EXPECT_TRUE(model()->has_focus());
   EXPECT_TRUE(model()->is_caret_visible());
 }
+
+// Tests ConsumeCtrlKey() consumes ctrl key when down, but does not affect ctrl
+// state otherwise.
+TEST_F(OmniboxEditModelTest, ConsumeCtrlKey) {
+  model()->control_key_state_ = TestOmniboxEditModel::UP;
+  model()->ConsumeCtrlKey();
+  EXPECT_EQ(model()->control_key_state_, TestOmniboxEditModel::UP);
+  model()->control_key_state_ = TestOmniboxEditModel::DOWN;
+  model()->ConsumeCtrlKey();
+  EXPECT_EQ(model()->control_key_state_,
+            TestOmniboxEditModel::DOWN_AND_CONSUMED);
+  model()->ConsumeCtrlKey();
+  EXPECT_EQ(model()->control_key_state_,
+            TestOmniboxEditModel::DOWN_AND_CONSUMED);
+}
+
+// Tests ctrl_key_state_ is set consumed if the ctrl key is down on focus.
+TEST_F(OmniboxEditModelTest, ConsumeCtrlKeyOnRequestFocus) {
+  model()->control_key_state_ = TestOmniboxEditModel::DOWN;
+  model()->OnSetFocus(false);
+  EXPECT_EQ(model()->control_key_state_, TestOmniboxEditModel::UP);
+  model()->OnSetFocus(true);
+  EXPECT_EQ(model()->control_key_state_,
+            TestOmniboxEditModel::DOWN_AND_CONSUMED);
+}
+
+// Tests the ctrl key is consumed on a ctrl-action (e.g. ctrl-c to copy)
+TEST_F(OmniboxEditModelTest, ConsumeCtrlKeyOnCtrlAction) {
+  model()->control_key_state_ = TestOmniboxEditModel::DOWN;
+  OmniboxView::StateChanges state_changes{nullptr, nullptr, 0,     0,
+                                          false,   false,   false, false};
+  model()->OnAfterPossibleChange(state_changes, false);
+  EXPECT_EQ(model()->control_key_state_,
+            TestOmniboxEditModel::DOWN_AND_CONSUMED);
+}
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 69ee5a7..d9ae2e2 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -835,6 +835,22 @@
 
 PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) {
   PasswordStoreChangeList list;
+  if (form.blacklisted_by_user) {
+    sql::Statement blacklist_statement(db_.GetUniqueStatement(
+        "SELECT EXISTS(SELECT 1 FROM logins WHERE signon_realm == ? AND "
+        "blacklisted_by_user == 1)"));
+    blacklist_statement.BindString(0, form.signon_realm);
+    const bool is_already_blacklisted =
+        blacklist_statement.Step() && blacklist_statement.ColumnBool(0);
+    UMA_HISTOGRAM_BOOLEAN(
+        "PasswordManager.BlacklistedSites.PreventedAddingDuplicates",
+        is_already_blacklisted);
+    if (is_already_blacklisted) {
+      // The site is already blacklisted, so we need to ignore the request to
+      // avoid duplicates.
+      return list;
+    }
+  }
   if (!DoesMatchConstraints(form))
     return list;
   std::string encrypted_password;
@@ -865,6 +881,25 @@
   return list;
 }
 
+PasswordStoreChangeList LoginDatabase::AddBlacklistedLoginForTesting(
+    const PasswordForm& form) {
+  DCHECK(form.blacklisted_by_user);
+  PasswordStoreChangeList list;
+
+  std::string encrypted_password;
+  if (EncryptedString(form.password_value, &encrypted_password) !=
+      ENCRYPTION_RESULT_SUCCESS)
+    return list;
+
+  DCHECK(!add_statement_.empty());
+  sql::Statement s(
+      db_.GetCachedStatement(SQL_FROM_HERE, add_statement_.c_str()));
+  BindAddStatement(form, encrypted_password, &s);
+  if (s.Run())
+    list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
+  return list;
+}
+
 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) {
   std::string encrypted_password;
   if (EncryptedString(form.password_value, &encrypted_password) !=
diff --git a/components/password_manager/core/browser/login_database.h b/components/password_manager/core/browser/login_database.h
index 51558dd0..ff22615 100644
--- a/components/password_manager/core/browser/login_database.h
+++ b/components/password_manager/core/browser/login_database.h
@@ -56,6 +56,12 @@
   PasswordStoreChangeList AddLogin(const autofill::PasswordForm& form)
       WARN_UNUSED_RESULT;
 
+  // This function does the same thing as AddLogin() with the difference that
+  // doesn't check if a site is already blacklisted before adding it. This is
+  // needed for tests that will require to have duplicates in the database.
+  PasswordStoreChangeList AddBlacklistedLoginForTesting(
+      const autofill::PasswordForm& form) WARN_UNUSED_RESULT;
+
   // Updates existing password form. Returns the list of applied changes
   // ({}, {UPDATE}). The password is looked up by the tuple {origin,
   // username_element, username_value, password_element, signon_realm}.
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index b189ca9..f2036cf 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -1497,12 +1497,14 @@
   password_form.origin = GURL("http://rsolomakhin.github.io/autofill/123");
   password_form.signon_realm = "http://rsolomakhin.github.io/";
   password_form.blacklisted_by_user = true;
-  EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
+  EXPECT_EQ(AddChangeForForm(password_form),
+            db().AddBlacklistedLoginForTesting(password_form));
 
   password_form.origin = GURL("https://rsolomakhin.github.io/autofill/1234");
   password_form.signon_realm = "https://rsolomakhin.github.io/";
   password_form.blacklisted_by_user = true;
-  EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
+  EXPECT_EQ(AddChangeForForm(password_form),
+            db().AddBlacklistedLoginForTesting(password_form));
 
   base::HistogramTester histogram_tester;
   db().ReportMetrics("", false);
@@ -1568,6 +1570,58 @@
                                       2, 1);
 }
 
+// This test will check that adding a blacklist entry is prevented due to an
+// already existing entry.
+TEST_F(LoginDatabaseTest, AddBlacklistedDuplicates) {
+  PasswordForm password_form;
+  password_form.origin = GURL("http://rsolomakhin.github.io/autofill/");
+  password_form.signon_realm = "http://rsolomakhin.github.io/";
+  password_form.blacklisted_by_user = true;
+  EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
+
+  PasswordForm password_form_duplicated;
+  password_form_duplicated.origin =
+      GURL("http://rsolomakhin.github.io/autofill/123");
+  password_form_duplicated.signon_realm = "http://rsolomakhin.github.io/";
+  password_form_duplicated.blacklisted_by_user = true;
+  EXPECT_EQ(PasswordStoreChangeList(), db().AddLogin(password_form_duplicated));
+
+  PasswordForm password_form_example;
+  password_form_example.origin = GURL("http://example.com/");
+  password_form_example.signon_realm = "http://example.com/";
+  password_form_example.blacklisted_by_user = false;
+  EXPECT_EQ(AddChangeForForm(password_form_example),
+            db().AddLogin(password_form_example));
+
+  PasswordForm password_form_example_blacklisted;
+  password_form_example_blacklisted.origin = GURL("http://example.com/1");
+  password_form_example_blacklisted.signon_realm = "http://example.com/";
+  password_form_example_blacklisted.blacklisted_by_user = true;
+  EXPECT_EQ(AddChangeForForm(password_form_example_blacklisted),
+            db().AddLogin(password_form_example_blacklisted));
+
+  PasswordForm password_form_example_blacklisted_duplicated;
+  password_form_example_blacklisted_duplicated.origin =
+      GURL("http://example.com/123");
+  password_form_example_blacklisted_duplicated.signon_realm =
+      "http://example.com/";
+  password_form_example_blacklisted_duplicated.blacklisted_by_user = true;
+  EXPECT_EQ(PasswordStoreChangeList(),
+            db().AddLogin(password_form_example_blacklisted_duplicated));
+
+  std::vector<std::unique_ptr<PasswordForm>> forms;
+  ASSERT_TRUE(db().GetAutofillableLogins(&forms));
+  EXPECT_THAT(forms,
+              UnorderedElementsAre(::testing::Pointee(password_form_example)));
+
+  std::vector<std::unique_ptr<PasswordForm>> blacklisted_forms;
+  ASSERT_TRUE(db().GetBlacklistLogins(&blacklisted_forms));
+  EXPECT_THAT(blacklisted_forms,
+              UnorderedElementsAre(
+                  ::testing::Pointee(password_form),
+                  ::testing::Pointee(password_form_example_blacklisted)));
+}
+
 TEST_F(LoginDatabaseTest, PasswordReuseMetrics) {
   // -- Group of accounts that are reusing password #1.
   //
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index 410250ff7..5fc394b2 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -29,6 +29,8 @@
 
 namespace password_manager {
 
+bool NewPasswordFormManager::wait_for_server_predictions_for_filling_ = true;
+
 namespace {
 
 constexpr TimeDelta kMaxFillingDelayForServerPerdictions =
@@ -209,7 +211,7 @@
 
   autofills_left_ = kMaxTimesAutofill;
 
-  if (predictions_) {
+  if (predictions_ || !wait_for_server_predictions_for_filling_) {
     ReportTimeBetweenStoreAndServerUMA();
     Fill();
   } else {
diff --git a/components/password_manager/core/browser/new_password_form_manager.h b/components/password_manager/core/browser/new_password_form_manager.h
index 7fce87d..06b3bd8 100644
--- a/components/password_manager/core/browser/new_password_form_manager.h
+++ b/components/password_manager/core/browser/new_password_form_manager.h
@@ -105,6 +105,12 @@
   void PermanentlyBlacklist() override;
   void OnPasswordsRevealed() override;
 
+#if defined(UNIT_TEST)
+  static void set_wait_for_server_predictions_for_filling(bool value) {
+    wait_for_server_predictions_for_filling_ = value;
+  }
+#endif
+
  protected:
   // FormFetcher::Consumer:
   void ProcessMatches(
@@ -233,6 +239,9 @@
   // loop.
   int autofills_left_ = kMaxTimesAutofill;
 
+  // Controls whether to wait or not server before filling. It is used in tests.
+  static bool wait_for_server_predictions_for_filling_;
+
   // Used for comparison metrics.
   // TODO(https://crbug.com/831123): Remove it when the old form parsing is
   // removed.
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 43b3c9a..40517bd 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -292,6 +292,8 @@
       prefs::kWasAutoSignInFirstRunExperienceShown, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
   registry->RegisterBooleanPref(prefs::kBlacklistedCredentialsStripped, false);
+  registry->RegisterBooleanPref(prefs::kDuplicatedBlacklistedCredentialsRemoved,
+                                false);
 #if defined(OS_MACOSX)
   registry->RegisterIntegerPref(
       prefs::kKeychainMigrationStatus,
@@ -667,6 +669,10 @@
     if (base::EndsWith(iter->signon_realm, kSpdyProxyRealm,
                        base::CompareCase::SENSITIVE))
       continue;
+
+    if (iter->is_gaia_with_skip_save_password_form)
+      continue;
+
     bool old_manager_found = false;
     for (const auto& old_manager : pending_login_managers_) {
       if (old_manager->DoesManage(*iter, driver) !=
@@ -716,6 +722,8 @@
   // Find new forms.
   std::vector<const PasswordForm*> new_forms;
   for (const PasswordForm& form : forms) {
+    if (form.is_gaia_with_skip_save_password_form)
+      continue;
     auto form_it =
         std::find_if(form_managers_.begin(), form_managers_.end(),
                      [&form, driver](const auto& form_manager) {
diff --git a/components/password_manager/core/browser/password_manager_util.cc b/components/password_manager/core/browser/password_manager_util.cc
index 0413115c..f78f8d7a 100644
--- a/components/password_manager/core/browser/password_manager_util.cc
+++ b/components/password_manager/core/browser/password_manager_util.cc
@@ -5,15 +5,18 @@
 #include "components/password_manager/core/browser/password_manager_util.h"
 
 #include <algorithm>
-#include <set>
 #include <string>
+#include <utility>
 
-#include "base/metrics/histogram_macros.h"
+#include "base/containers/flat_set.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
+#include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/popup_item_ids.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/autofill/core/common/password_generation_util.h"
+#include "components/password_manager/core/browser/hsts_query.h"
 #include "components/password_manager/core/browser/log_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
@@ -40,14 +43,14 @@
   ~BlacklistedCredentialsCleaner() override = default;
 
   void OnGetPasswordStoreResults(
-      std::vector<std::unique_ptr<PasswordForm>> results) override {
-    bool need_to_clean = !prefs_->GetBoolean(
-        password_manager::prefs::kBlacklistedCredentialsStripped);
-    UMA_HISTOGRAM_BOOLEAN("PasswordManager.BlacklistedSites.NeedToBeCleaned",
-                          need_to_clean);
-    if (need_to_clean)
+      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override {
+    if (!prefs_->GetBoolean(
+            password_manager::prefs::kBlacklistedCredentialsStripped))
       RemoveUsernameAndPassword(results);
-    RemoveDuplicates(results);
+
+    if (!prefs_->GetBoolean(
+            password_manager::prefs::kDuplicatedBlacklistedCredentialsRemoved))
+      RemoveDuplicates(results);
     delete this;
   }
 
@@ -86,6 +89,12 @@
         store_->RemoveLogin(*form);
       }
     }
+    const size_t duplicates = results.size() - signon_realms.size();
+    if (duplicates == 0) {
+      prefs_->SetBoolean(
+          password_manager::prefs::kDuplicatedBlacklistedCredentialsRemoved,
+          true);
+    }
   }
 
   password_manager::PasswordStore* store_;
@@ -110,8 +119,137 @@
          std::make_pair(!rhs->is_public_suffix_match, rhs->preferred);
 }
 
+// This class is responsible for reporting metrics about HTTP to HTTPS
+// migration.
+class HttpMetricsMigrationReporter
+    : public password_manager::PasswordStoreConsumer {
+ public:
+  HttpMetricsMigrationReporter(
+      password_manager::PasswordStore* store,
+      scoped_refptr<net::URLRequestContextGetter> request_context)
+      : request_context_(std::move(request_context)) {
+    store->GetAutofillableLogins(this);
+  }
+
+ private:
+  // This type define a subset of PasswordForm where first argument is the host
+  // and the second argument is the username of the form.
+  typedef std::pair<std::string, base::string16> FormKey;
+
+  // This overrides the PasswordStoreConsumer method.
+  void OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+
+  void OnHSTSQueryResult(FormKey key,
+                         base::string16 password_value,
+                         bool is_hsts);
+
+  void ReportMetrics();
+
+  std::map<FormKey, base::flat_set<base::string16>> https_credentials_map_;
+  const scoped_refptr<net::URLRequestContextGetter> request_context_;
+  size_t processed_results_ = 0;
+
+  // The next three counters are in pairs where [0] component means that HSTS is
+  // not enabled and [1] component means that HSTS is enabled for that HTTP type
+  // of credentials.
+
+  // Number of HTTP credentials for which no HTTPS credential for the same
+  // username exists.
+  size_t https_credential_not_found_[2] = {0, 0};
+
+  // Number of HTTP credentials for which an equivalent (i.e. same host,
+  // username and password) HTTPS credential exists.
+  size_t same_password_[2] = {0, 0};
+
+  // Number of HTTP credentials for which a conflicting (i.e. same host and
+  // username, but different password) HTTPS credential exists.
+  size_t different_password_[2] = {0, 0};
+
+  // Number of HTTP credentials from the Password Store.
+  size_t total_http_credentials_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpMetricsMigrationReporter);
+};
+
+void HttpMetricsMigrationReporter::OnGetPasswordStoreResults(
+    std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+  for (auto& form : results) {
+    FormKey form_key({form->origin.host(), form->username_value});
+    if (form->origin.SchemeIs(url::kHttpScheme)) {
+      password_manager::PostHSTSQueryForHostAndRequestContext(
+          form->origin, request_context_,
+          base::Bind(&HttpMetricsMigrationReporter::OnHSTSQueryResult,
+                     base::Unretained(this), form_key, form->password_value));
+      ++total_http_credentials_;
+    } else if (form->origin.SchemeIs(url::kHttpsScheme)) {
+      https_credentials_map_[form_key].insert(form->password_value);
+    }
+  }
+  ReportMetrics();
+}
+
+// |key| and |password_value| was created from the same form.
+void HttpMetricsMigrationReporter::OnHSTSQueryResult(
+    FormKey key,
+    base::string16 password_value,
+    bool is_hsts) {
+  ++processed_results_;
+  base::ScopedClosureRunner report(base::BindOnce(
+      &HttpMetricsMigrationReporter::ReportMetrics, base::Unretained(this)));
+  auto user_it = https_credentials_map_.find(key);
+  if (user_it == https_credentials_map_.end()) {
+    // Credentials are not migrated yet.
+    ++https_credential_not_found_[is_hsts];
+    return;
+  }
+  if (base::ContainsKey(user_it->second, password_value)) {
+    // The password store contains the same credentials (username and
+    // password) on HTTP version of the form.
+    ++same_password_[is_hsts];
+  } else {
+    ++different_password_[is_hsts];
+  }
+}
+
+void HttpMetricsMigrationReporter::ReportMetrics() {
+  // The metrics have to be recorded after all requests are done.
+  if (processed_results_ != total_http_credentials_)
+    return;
+
+  for (bool is_hsts_enabled : {false, true}) {
+    std::string suffix = (is_hsts_enabled ? std::string("WithHSTSEnabled")
+                                          : std::string("HSTSNotEnabled"));
+
+    base::UmaHistogramCounts1000(
+        "PasswordManager.HttpCredentialsWithEquivalentHttpsCredential." +
+            suffix,
+        same_password_[is_hsts_enabled]);
+
+    base::UmaHistogramCounts1000(
+        "PasswordManager.HttpCredentialsWithConflictingHttpsCredential." +
+            suffix,
+        different_password_[is_hsts_enabled]);
+
+    base::UmaHistogramCounts1000(
+        "PasswordManager.HttpCredentialsWithoutMatchingHttpsCredential." +
+            suffix,
+        https_credential_not_found_[is_hsts_enabled]);
+  }
+  delete this;
+}
+
 }  // namespace
 
+#if !defined(OS_IOS)
+void ReportHttpMigrationMetrics(
+    scoped_refptr<password_manager::PasswordStore> store,
+    scoped_refptr<net::URLRequestContextGetter> request_context) {
+  // The object will delete itself once the metrics are recorded.
+  new HttpMetricsMigrationReporter(store.get(), std::move(request_context));
+}
+#endif  // !defined(OS_IOS)
+
 // Update |credential| to reflect usage.
 void UpdateMetadataForUsage(PasswordForm* credential) {
   ++credential->times_used;
@@ -242,11 +380,21 @@
 void CleanBlacklistedCredentials(password_manager::PasswordStore* store,
                                  PrefService* prefs,
                                  int delay_in_seconds) {
-  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&StartCleaningBlacklisted, base::WrapRefCounted(store),
-                     prefs),
-      base::TimeDelta::FromSeconds(delay_in_seconds));
+  const bool need_to_stripe_username = !prefs->GetBoolean(
+      password_manager::prefs::kBlacklistedCredentialsStripped);
+  base::UmaHistogramBoolean("PasswordManager.BlacklistedSites.NeedToBeCleaned",
+                            need_to_stripe_username);
+  const bool need_to_remove_blacklisted_duplicates = !prefs->GetBoolean(
+      password_manager::prefs::kDuplicatedBlacklistedCredentialsRemoved);
+  base::UmaHistogramBoolean(
+      "PasswordManager.BlacklistedSites.NeedRemoveBlacklistDuplicates",
+      need_to_remove_blacklisted_duplicates);
+  if (need_to_stripe_username || need_to_remove_blacklisted_duplicates)
+    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&StartCleaningBlacklisted, base::WrapRefCounted(store),
+                       prefs),
+        base::TimeDelta::FromSeconds(delay_in_seconds));
 }
 
 void FindBestMatches(
diff --git a/components/password_manager/core/browser/password_manager_util.h b/components/password_manager/core/browser/password_manager_util.h
index 9363a20..cf4746e 100644
--- a/components/password_manager/core/browser/password_manager_util.h
+++ b/components/password_manager/core/browser/password_manager_util.h
@@ -19,6 +19,10 @@
 class AutofillClient;
 }
 
+namespace net {
+class URLRequestContextGetter;
+}
+
 namespace password_manager {
 class PasswordManagerClient;
 class PasswordStore;
@@ -91,6 +95,12 @@
                                  PrefService* prefs,
                                  int delay_in_seconds);
 
+// Report metrics about HTTP to HTTPS migration process. This function cannot be
+// used on iOS platform because the HSTS query is not supported.
+void ReportHttpMigrationMetrics(
+    scoped_refptr<password_manager::PasswordStore> store,
+    scoped_refptr<net::URLRequestContextGetter> request_context);
+
 // Given all non-blacklisted |matches|, finds and populates
 // |best_matches_|, |preferred_match_| and |non_best_matches_| accordingly.
 // For comparing credentials the following rule is used: non-psl match is better
diff --git a/components/password_manager/core/browser/password_manager_util_unittest.cc b/components/password_manager/core/browser/password_manager_util_unittest.cc
index 2be08f0..47fcb37 100644
--- a/components/password_manager/core/browser/password_manager_util_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_util_unittest.cc
@@ -9,7 +9,9 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_task_environment.h"
+#include "build/build_config.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/password_manager/core/browser/mock_password_store.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
@@ -17,6 +19,7 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
+#include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -98,6 +101,9 @@
   TestingPrefServiceSimple prefs;
   prefs.registry()->RegisterBooleanPref(
       password_manager::prefs::kBlacklistedCredentialsStripped, false);
+  // Prevent cleaning of duplicated blacklist entries.
+  prefs.registry()->RegisterBooleanPref(
+      password_manager::prefs::kDuplicatedBlacklistedCredentialsRemoved, true);
   auto password_store = base::MakeRefCounted<
       testing::StrictMock<password_manager::MockPasswordStore>>();
   ASSERT_TRUE(
@@ -112,10 +118,6 @@
   EXPECT_CALL(*password_store, RemoveLogin(blacklisted_with_password));
   EXPECT_CALL(*password_store, AddLogin(blacklisted)).Times(2);
 
-  // These deletions are not related to clean-up user credentials.
-  // They are performed by RemoveDuplicated().
-  EXPECT_CALL(*password_store, RemoveLogin(blacklisted)).Times(2);
-
   CleanBlacklistedCredentials(password_store.get(), &prefs, 0);
   scoped_task_environment.RunUntilIdle();
 
@@ -149,10 +151,14 @@
   base::test::ScopedTaskEnvironment scoped_task_environment;
   TestingPrefServiceSimple prefs;
 
-  // The following preference is used inside the blacklist cleaning utility
-  // for an unrelated clean-up
+  // In this test we are explicitly only testing the clean up of duplicated
+  // credentials and setting this true will prevent making other unrelated
+  // clean-up.
   prefs.registry()->RegisterBooleanPref(
-      password_manager::prefs::kBlacklistedCredentialsStripped, false);
+      password_manager::prefs::kBlacklistedCredentialsStripped, true);
+
+  prefs.registry()->RegisterBooleanPref(
+      password_manager::prefs::kDuplicatedBlacklistedCredentialsRemoved, false);
 
   auto password_store = base::MakeRefCounted<
       testing::StrictMock<password_manager::MockPasswordStore>>();
@@ -189,11 +195,15 @@
   base::test::ScopedTaskEnvironment scoped_task_environment;
   TestingPrefServiceSimple prefs;
 
-  // The following preference is used inside the blacklist cleaning utility
-  // for an unrelated clean-up
+  // Here we test the behavior when the Password Store contains blacklisted
+  // entries with password or username values that have to be cleared and
+  // blacklisted duplicates, too.
   prefs.registry()->RegisterBooleanPref(
       password_manager::prefs::kBlacklistedCredentialsStripped, false);
 
+  prefs.registry()->RegisterBooleanPref(
+      password_manager::prefs::kDuplicatedBlacklistedCredentialsRemoved, false);
+
   auto password_store =
       base::MakeRefCounted<password_manager::TestPasswordStore>();
   ASSERT_TRUE(
@@ -213,6 +223,92 @@
   scoped_task_environment.RunUntilIdle();
 }
 
+#if !defined(OS_IOS)
+TEST(PasswordManagerUtil, ReportHttpMigrationMetrics) {
+  for (bool is_hsts_enabled : {false, true}) {
+    base::test::ScopedTaskEnvironment scoped_task_environment;
+    auto request_context =
+        base::MakeRefCounted<net::TestURLRequestContextGetter>(
+            base::ThreadTaskRunnerHandle::Get());
+
+    net::TransportSecurityState* state =
+        request_context->GetURLRequestContext()->transport_security_state();
+    const base::Time expiry = base::Time::Max();
+    const bool include_subdomains = false;
+
+    auto password_store =
+        base::MakeRefCounted<password_manager::TestPasswordStore>();
+    ASSERT_TRUE(password_store->Init(syncer::SyncableService::StartSyncFlare(),
+                                     nullptr));
+
+    autofill::PasswordForm hsts_host_1;
+    hsts_host_1.origin = GURL("http://hsts-host-1/");
+    hsts_host_1.username_value = base::ASCIIToUTF16(kTestUsername);
+    hsts_host_1.password_value = base::ASCIIToUTF16(kTestPassword);
+    password_store->AddLogin(hsts_host_1);
+
+    autofill::PasswordForm hsts_host_1_https;
+    hsts_host_1_https.origin = GURL("https://hsts-host-1/");
+    hsts_host_1_https.username_value = base::ASCIIToUTF16(kTestUsername);
+    hsts_host_1_https.password_value = base::ASCIIToUTF16(kTestPassword);
+    password_store->AddLogin(hsts_host_1_https);
+
+    autofill::PasswordForm hsts_host_2;
+    hsts_host_2.origin = GURL("http://hsts-host-2/");
+    hsts_host_2.username_value = base::ASCIIToUTF16(kTestUsername2);
+    hsts_host_2.password_value = base::ASCIIToUTF16(kTestPassword);
+    password_store->AddLogin(hsts_host_2);
+
+    autofill::PasswordForm hsts_host_2_https;
+    hsts_host_2_https.origin = GURL("https://hsts-host-2/");
+    hsts_host_2_https.username_value = base::ASCIIToUTF16(kTestUsername2);
+    hsts_host_2_https.password_value = base::ASCIIToUTF16("different_password");
+    password_store->AddLogin(hsts_host_2_https);
+
+    // HTTPS version is not in Password Store.
+    autofill::PasswordForm hsts_host_3;
+    hsts_host_3.origin = GURL("http://hsts-host-3/");
+    password_store->AddLogin(hsts_host_3);
+
+    if (is_hsts_enabled) {
+      state->AddHSTS(hsts_host_1.origin.host(), expiry, include_subdomains);
+      state->AddHSTS(hsts_host_2.origin.host(), expiry, include_subdomains);
+      state->AddHSTS(hsts_host_3.origin.host(), expiry, include_subdomains);
+    }
+    scoped_task_environment.RunUntilIdle();
+
+    base::HistogramTester histogram_tester;
+    ReportHttpMigrationMetrics(password_store, std::move(request_context));
+    scoped_task_environment.RunUntilIdle();
+
+    for (bool test_hsts_enabled : {false, true}) {
+      std::string suffix =
+          (test_hsts_enabled ? "WithHSTSEnabled" : "HSTSNotEnabled");
+
+      // hsts_host_1
+      histogram_tester.ExpectUniqueSample(
+          "PasswordManager.HttpCredentialsWithEquivalentHttpsCredential." +
+              suffix,
+          (test_hsts_enabled == is_hsts_enabled), 1);
+
+      // hsts_host_2
+      histogram_tester.ExpectUniqueSample(
+          "PasswordManager.HttpCredentialsWithConflictingHttpsCredential." +
+              suffix,
+          (test_hsts_enabled == is_hsts_enabled), 1);
+
+      // hsts_host_3
+      histogram_tester.ExpectUniqueSample(
+          "PasswordManager.HttpCredentialsWithoutMatchingHttpsCredential." +
+              suffix,
+          (test_hsts_enabled == is_hsts_enabled), 1);
+    }
+    password_store->ShutdownOnUIThread();
+    scoped_task_environment.RunUntilIdle();
+  }
+}
+#endif  // !defined(OS_IOS)
+
 TEST(PasswordManagerUtil, FindBestMatches) {
   const int kNotFound = -1;
   struct TestMatch {
diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc
index d429f17..07747851 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -89,6 +89,7 @@
 
 std::vector<std::unique_ptr<autofill::PasswordForm>>
 TestPasswordStore::FillMatchingLogins(const FormDigest& form) {
+  ++fill_matching_logins_calls_;
   std::vector<std::unique_ptr<autofill::PasswordForm>> matched_forms;
   for (const auto& elements : stored_passwords_) {
     // The code below doesn't support PSL federated credential. It's doable but
diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h
index a74d1dc4..a3e6902 100644
--- a/components/password_manager/core/browser/test_password_store.h
+++ b/components/password_manager/core/browser/test_password_store.h
@@ -35,6 +35,8 @@
   // have entries of size 0.
   bool IsEmpty() const;
 
+  int fill_matching_logins_calls() const { return fill_matching_logins_calls_; }
+
  protected:
   ~TestPasswordStore() override;
 
@@ -86,6 +88,9 @@
  private:
   PasswordMap stored_passwords_;
 
+  // Number of calls of FillMatchingLogins() method.
+  int fill_matching_logins_calls_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(TestPasswordStore);
 };
 
diff --git a/components/password_manager/core/common/password_manager_pref_names.cc b/components/password_manager/core/common/password_manager_pref_names.cc
index bddb4499..d0069b0 100644
--- a/components/password_manager/core/common/password_manager_pref_names.cc
+++ b/components/password_manager/core/common/password_manager_pref_names.cc
@@ -43,6 +43,9 @@
 const char kBlacklistedCredentialsStripped[] =
     "profile.blacklisted_credentials_stripped";
 
+const char kDuplicatedBlacklistedCredentialsRemoved[] =
+    "profile.duplicated_blacklisted_credentials_removed";
+
 const char kPasswordHashDataList[] = "profile.password_hash_data_list";
 
 }  // namespace prefs
diff --git a/components/password_manager/core/common/password_manager_pref_names.h b/components/password_manager/core/common/password_manager_pref_names.h
index ff8bbbf..421c5488 100644
--- a/components/password_manager/core/common/password_manager_pref_names.h
+++ b/components/password_manager/core/common/password_manager_pref_names.h
@@ -68,6 +68,9 @@
 // Whether Chrome cleaned up username/password in the blacklisted credentials.
 extern const char kBlacklistedCredentialsStripped[];
 
+// Whether Chrome deleted blacklisted credentials that were duplicated.
+extern const char kDuplicatedBlacklistedCredentialsRemoved[];
+
 // List that contains captured password hashes.
 extern const char kPasswordHashDataList[];
 
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index c86f611..37f6135 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -210,9 +210,9 @@
       }
     }
     std::string value = base::StringPrintf(
-        "cause=\"%s\", destination=\"document\", target=\"%s\", site=\"%s\"",
+        "cause=\"%s\", destination=\"%s\", site=\"%s\"",
         has_user_gesture ? "user-activated" : "forced",
-        frame_tree_node->IsMainFrame() ? "top-level" : "nested",
+        frame_tree_node->IsMainFrame() ? "document" : "nested-document",
         site_value.c_str());
     headers->SetHeaderIfMissing("Sec-Metadata", value);
   }
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 7328c63..4a752d0 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3495,9 +3495,6 @@
   registry_->AddInterface(base::BindRepeating(
       &RenderFrameHostImpl::CreateWebUsbService, base::Unretained(this)));
 
-  registry_->AddInterface(base::BindRepeating(
-      &RenderFrameHostImpl::CreateUsbChooserService, base::Unretained(this)));
-
   registry_->AddInterface<media::mojom::InterfaceFactory>(
       base::Bind(&RenderFrameHostImpl::BindMediaInterfaceFactoryRequest,
                  base::Unretained(this)));
@@ -4967,12 +4964,6 @@
   GetContentClient()->browser()->CreateWebUsbService(this, std::move(request));
 }
 
-void RenderFrameHostImpl::CreateUsbChooserService(
-    device::mojom::UsbChooserServiceRequest request) {
-  GetContentClient()->browser()->CreateUsbChooserService(this,
-                                                         std::move(request));
-}
-
 void RenderFrameHostImpl::ResetFeaturePolicy() {
   RenderFrameHostImpl* parent_frame_host = GetParent();
   const blink::FeaturePolicy* parent_policy =
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 970421be..a42baef1 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -112,7 +112,7 @@
 namespace mojom {
 class WebUsbService;
 }
-}
+}  // namespace blink
 
 namespace gfx {
 class Range;
@@ -1095,7 +1095,6 @@
   // Creates connections to WebUSB interfaces bound to this frame.
   void CreateWebUsbService(
       mojo::InterfaceRequest<blink::mojom::WebUsbService> request);
-  void CreateUsbChooserService(device::mojom::UsbChooserServiceRequest request);
 
   void CreateAudioInputStreamFactory(
       mojom::RendererAudioInputStreamFactoryRequest request);
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index e6e0973..a7524d5 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -655,6 +655,14 @@
                                               std::move(interface_pipe));
 }
 
+void GpuProcessHost::TerminateGpuProcess(const std::string& message) {
+  // At the moment, this path is only used by Ozone/Wayland. Once others start
+  // to use this, start to distinguish the origin of termination. By default,
+  // it's unknown.
+  termination_origin_ = GpuTerminationOrigin::kOzoneWaylandProxy;
+  process_->TerminateOnBadMessageReceived(message);
+}
+
 // static
 GpuProcessHost* GpuProcessHost::FromID(int host_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -792,6 +800,9 @@
                                      info.exit_code);
         break;
       case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
+        UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessTerminationOrigin",
+                                  termination_origin_,
+                                  GpuTerminationOrigin::kMax);
         message = "You killed the GPU process! Why?";
         break;
 #if defined(OS_CHROMEOS)
@@ -827,24 +838,31 @@
   // possible to ensure the latter always has a valid device. crbug.com/608839
   // When running with mus, the OzonePlatform may not have been created yet. So
   // defer the callback until OzonePlatform instance is created.
-  if (features::IsOzoneDrmMojo()) {
+  bool using_mojo = true;
+#if defined(OS_CHROMEOS)
+  using_mojo = features::IsOzoneDrmMojo();
+#endif
+  if (using_mojo) {
     // TODO(rjkroege): Remove the legacy IPC code paths when no longer
     // necessary. https://crbug.com/806092
     auto interface_binder = base::BindRepeating(&GpuProcessHost::BindInterface,
                                                 weak_ptr_factory_.GetWeakPtr());
+    auto terminate_cb = base::BindOnce(&GpuProcessHost::TerminateGpuProcess,
+                                       weak_ptr_factory_.GetWeakPtr());
 
     auto io_callback = base::BindOnce(
         [](const base::RepeatingCallback<void(const std::string&,
                                               mojo::ScopedMessagePipeHandle)>&
                interface_binder,
+           base::OnceCallback<void(const std::string&)> terminate_cb,
            ui::OzonePlatform* platform) {
           DCHECK_CURRENTLY_ON(BrowserThread::IO);
           platform->GetGpuPlatformSupportHost()->OnGpuServiceLaunched(
               BrowserThread::GetTaskRunnerForThread(BrowserThread::UI),
               BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
-              interface_binder);
+              interface_binder, std::move(terminate_cb));
         },
-        interface_binder);
+        interface_binder, std::move(terminate_cb));
 
     OzoneRegisterStartupCallbackHelper(std::move(io_callback));
   } else {
diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h
index 7187e92b..324152f 100644
--- a/content/browser/gpu/gpu_process_host.h
+++ b/content/browser/gpu/gpu_process_host.h
@@ -123,6 +123,7 @@
 
   void BindInterface(const std::string& interface_name,
                      mojo::ScopedMessagePipeHandle interface_pipe);
+  void TerminateGpuProcess(const std::string& message);
 
   // Get the GPU process host for the GPU process with the given ID. Returns
   // null if the process no longer exists.
@@ -187,6 +188,12 @@
 
   enum GpuInitializationStatus { UNKNOWN, SUCCESS, FAILURE };
 
+  enum class GpuTerminationOrigin {
+    kUnknownOrigin = 0,
+    kOzoneWaylandProxy = 1,
+    kMax = 2,
+  };
+
   static bool ValidateHost(GpuProcessHost* host);
 
   // Increments |crash_count| by one. Before incrementing |crash_count|, for
@@ -294,6 +301,9 @@
 
   GpuInitializationStatus status_;
 
+  GpuTerminationOrigin termination_origin_ =
+      GpuTerminationOrigin::kUnknownOrigin;
+
   // Time Init started.  Used to log total GPU process startup time to UMA.
   base::TimeTicks init_start_time_;
 
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index 4e931c6..8a66801 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -56,6 +56,7 @@
       DCHECK(!suppress_manipulation_events_);
       DCHECK(!touchscreen_scroll_in_progress_);
       touchscreen_scroll_in_progress_ = true;
+      gesture_sequence_.append("B");
       // TODO(https://crbug.com/851644): Make sure the value is properly set.
       if (!scrolling_touch_action_.has_value()) {
         static auto* crash_key = base::debug::AllocateCrashKeyString(
@@ -130,6 +131,7 @@
 
     // If double tap is disabled, there's no reason for the tap delay.
     case WebInputEvent::kGestureTapUnconfirmed: {
+      gesture_sequence_.append("C");
       DCHECK_EQ(1, gesture_event->data.tap.tap_count);
       // TODO(https://crbug.com/851644): Make sure the value is properly set.
       if (!scrolling_touch_action_.has_value()) {
@@ -158,6 +160,7 @@
       break;
 
     case WebInputEvent::kGestureTapDown:
+      gesture_sequence_.append("O");
       // If the gesture is hitting a region that has a non-blocking (such as a
       // passive) event listener.
       if (gesture_event->is_source_touch_event_set_non_blocking)
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 9b5d1c59..baf44ffd 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -359,6 +359,7 @@
       WebContents* outer_web_contents,
       RenderFrameHost* outer_contents_frame) override;
   WebContentsImpl* GetOuterWebContents() override;
+  WebContentsImpl* GetOutermostWebContents() override;
   void DidChangeVisibleSecurityState() override;
   void NotifyPreferencesChanged() override;
 
@@ -1255,9 +1256,6 @@
   // focused WebContents.
   bool ContainsOrIsFocusedWebContents();
 
-  // Returns the root of the WebContents tree.
-  WebContentsImpl* GetOutermostWebContents();
-
   // Walks up the outer WebContents chain and focuses the FrameTreeNode where
   // each inner WebContents is attached.
   void FocusOuterAttachmentFrameChain();
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index beb94e9..b6978d1 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -331,6 +331,7 @@
   DCHECK(timer_);
 
   protocols_.insert(device::FidoTransportProtocol::kUsbHumanInterfaceDevice);
+  protocols_.insert(device::FidoTransportProtocol::kInternal);
   if (base::FeatureList::IsEnabled(features::kWebAuthBle)) {
     protocols_.insert(device::FidoTransportProtocol::kBluetoothLowEnergy);
   }
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index b3814b5..80cbb9c 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -261,6 +261,7 @@
   std::vector<uint8_t> id(32, 0x0A);
   credential->id = id;
   credential->transports.push_back(AuthenticatorTransport::USB);
+  credential->transports.push_back(AuthenticatorTransport::BLE);
   descriptors.push_back(std::move(credential));
   return descriptors;
 }
diff --git a/content/public/app/mojo/content_gpu_manifest.json b/content/public/app/mojo/content_gpu_manifest.json
index 1335cff..f56ca6cc 100644
--- a/content/public/app/mojo/content_gpu_manifest.json
+++ b/content/public/app/mojo/content_gpu_manifest.json
@@ -14,6 +14,7 @@
           "service_manager.mojom.ServiceFactory",
           "ui.ozone.mojom.DeviceCursor",
           "ui.ozone.mojom.DrmDevice",
+          "ui.ozone.mojom.WaylandConnectionClient",
           "viz.mojom.CompositingModeReporter",
           "viz.mojom.VizMain"
         ],
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 341a3d9..ba41da2 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -738,10 +738,6 @@
     RenderFrameHost* render_frame_host,
     mojo::InterfaceRequest<blink::mojom::WebUsbService> request) {}
 
-void ContentBrowserClient::CreateUsbChooserService(
-    RenderFrameHost* render_frame_host,
-    device::mojom::UsbChooserServiceRequest request) {}
-
 bool ContentBrowserClient::ShowPaymentHandlerWindow(
     content::BrowserContext* browser_context,
     const GURL& url,
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 02b8478..e9ce260 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -31,7 +31,6 @@
 #include "content/public/common/resource_type.h"
 #include "content/public/common/socket_permission_request.h"
 #include "content/public/common/window_container_type.mojom.h"
-#include "device/usb/public/mojom/chooser_service.mojom.h"
 #include "media/base/video_codecs.h"
 #include "media/cdm/cdm_proxy.h"
 #include "media/media_buildflags.h"
@@ -1227,10 +1226,6 @@
       RenderFrameHost* render_frame_host,
       mojo::InterfaceRequest<blink::mojom::WebUsbService> request);
 
-  virtual void CreateUsbChooserService(
-      RenderFrameHost* render_frame_host,
-      device::mojom::UsbChooserServiceRequest request);
-
   // Attempt to open the Payment Handler window inside its corresponding
   // PaymentRequest UI surface. Returns true if the ContentBrowserClient
   // implementation supports this operation (desktop Chrome) or false otherwise.
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index ed51132..53e1a38 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -534,6 +534,10 @@
   // Otherwise, return nullptr.
   virtual WebContents* GetOuterWebContents() = 0;
 
+  // Returns the root WebContents of the WebContents tree. Always returns
+  // non-null value.
+  virtual WebContents* GetOutermostWebContents() = 0;
+
   // Invoked when visible security state changes.
   virtual void DidChangeVisibleSecurityState() = 0;
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index c809210..5b73e127 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -700,6 +700,7 @@
     "//media/mojo/clients",
     "//media/mojo/interfaces",
     "//media/mojo/interfaces:remoting",
+    "//media/webrtc",
     "//mojo/public/cpp/bindings",
     "//net",
     "//ppapi/buildflags",
diff --git a/content/renderer/media/audio/audio_device_factory.cc b/content/renderer/media/audio/audio_device_factory.cc
index d20dbe3..df240f5 100644
--- a/content/renderer/media/audio/audio_device_factory.cc
+++ b/content/renderer/media/audio/audio_device_factory.cc
@@ -43,11 +43,10 @@
 
 scoped_refptr<media::AudioOutputDevice> NewOutputDevice(
     int render_frame_id,
-    int session_id,
-    const std::string& device_id) {
+    const media::AudioSinkParameters& params) {
   auto device = base::MakeRefCounted<media::AudioOutputDevice>(
       AudioOutputIPCFactory::get()->CreateAudioOutputIPC(render_frame_id),
-      AudioOutputIPCFactory::get()->io_task_runner(), session_id, device_id,
+      AudioOutputIPCFactory::get()->io_task_runner(), params,
       // Set authorization request timeout at 80% of renderer hung timeout,
       // but no more than kMaxAuthorizationTimeout.
       base::TimeDelta::FromMilliseconds(
@@ -69,14 +68,14 @@
 scoped_refptr<media::SwitchableAudioRendererSink> NewMixableSink(
     AudioDeviceFactory::SourceType source_type,
     int render_frame_id,
-    int session_id,
-    const std::string& device_id) {
+    const media::AudioSinkParameters& params) {
   RenderThreadImpl* render_thread = RenderThreadImpl::current();
   DCHECK(render_thread) << "RenderThreadImpl is not instantiated, or "
                         << "GetOutputDeviceInfo() is called on a wrong thread ";
+  DCHECK(!params.processing_id.has_value());
   return scoped_refptr<media::AudioRendererMixerInput>(
       render_thread->GetAudioRendererMixerManager()->CreateInput(
-          render_frame_id, session_id, device_id,
+          render_frame_id, params.session_id, params.device_id,
           AudioDeviceFactory::GetSourceLatencyType(source_type)));
 }
 
@@ -103,32 +102,35 @@
 }
 
 scoped_refptr<media::AudioRendererSink>
-AudioDeviceFactory::NewAudioRendererMixerSink(int render_frame_id,
-                                              int session_id,
-                                              const std::string& device_id) {
-  return NewFinalAudioRendererSink(render_frame_id, session_id, device_id);
+AudioDeviceFactory::NewAudioRendererMixerSink(
+    int render_frame_id,
+    const media::AudioSinkParameters& params) {
+  return NewFinalAudioRendererSink(render_frame_id, params);
 }
 
 // static
 scoped_refptr<media::AudioRendererSink>
-AudioDeviceFactory::NewAudioRendererSink(SourceType source_type,
-                                         int render_frame_id,
-                                         int session_id,
-                                         const std::string& device_id) {
+AudioDeviceFactory::NewAudioRendererSink(
+    SourceType source_type,
+    int render_frame_id,
+    const media::AudioSinkParameters& params) {
   if (factory_) {
     scoped_refptr<media::AudioRendererSink> device =
-        factory_->CreateAudioRendererSink(source_type, render_frame_id,
-                                          session_id, device_id);
+        factory_->CreateAudioRendererSink(source_type, render_frame_id, params);
     if (device)
       return device;
   }
 
+  // Perhaps streams with a processing ID just shouldn't be mixable, i.e. call
+  // NewFinalAudioRendererSink for them rather than DCHECK?
+  DCHECK(!(params.processing_id.has_value() && IsMixable(source_type)));
+
   if (IsMixable(source_type))
-    return NewMixableSink(source_type, render_frame_id, session_id, device_id);
+    return NewMixableSink(source_type, render_frame_id, params);
 
   UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.UsedForSinkCreation",
                         false);
-  return NewFinalAudioRendererSink(render_frame_id, session_id, device_id);
+  return NewFinalAudioRendererSink(render_frame_id, params);
 }
 
 // static
@@ -136,18 +138,17 @@
 AudioDeviceFactory::NewSwitchableAudioRendererSink(
     SourceType source_type,
     int render_frame_id,
-    int session_id,
-    const std::string& device_id) {
+    const media::AudioSinkParameters& params) {
   if (factory_) {
     scoped_refptr<media::SwitchableAudioRendererSink> sink =
-        factory_->CreateSwitchableAudioRendererSink(
-            source_type, render_frame_id, session_id, device_id);
+        factory_->CreateSwitchableAudioRendererSink(source_type,
+                                                    render_frame_id, params);
     if (sink)
       return sink;
   }
 
   if (IsMixable(source_type))
-    return NewMixableSink(source_type, render_frame_id, session_id, device_id);
+    return NewMixableSink(source_type, render_frame_id, params);
 
   // AudioOutputDevice is not RestartableAudioRendererSink, so we can't return
   // anything for those who wants to create an unmixable sink.
@@ -176,13 +177,12 @@
 // static
 media::OutputDeviceInfo AudioDeviceFactory::GetOutputDeviceInfo(
     int render_frame_id,
-    int session_id,
-    const std::string& device_id) {
+    const media::AudioSinkParameters& params) {
   RenderThreadImpl* render_thread = RenderThreadImpl::current();
   DCHECK(render_thread) << "RenderThreadImpl is not instantiated, or "
                         << "GetOutputDeviceInfo() is called on a wrong thread ";
   return render_thread->GetAudioRendererMixerManager()->GetOutputDeviceInfo(
-      render_frame_id, session_id, device_id);
+      render_frame_id, params.session_id, params.device_id);
 }
 
 AudioDeviceFactory::AudioDeviceFactory() {
@@ -196,18 +196,17 @@
 
 // static
 scoped_refptr<media::AudioRendererSink>
-AudioDeviceFactory::NewFinalAudioRendererSink(int render_frame_id,
-                                              int session_id,
-                                              const std::string& device_id) {
+AudioDeviceFactory::NewFinalAudioRendererSink(
+    int render_frame_id,
+    const media::AudioSinkParameters& params) {
   if (factory_) {
     scoped_refptr<media::AudioRendererSink> sink =
-        factory_->CreateFinalAudioRendererSink(render_frame_id, session_id,
-                                               device_id);
+        factory_->CreateFinalAudioRendererSink(render_frame_id, params);
     if (sink)
       return sink;
   }
 
-  return NewOutputDevice(render_frame_id, session_id, device_id);
+  return NewOutputDevice(render_frame_id, params);
 }
 
 }  // namespace content
diff --git a/content/renderer/media/audio/audio_device_factory.h b/content/renderer/media/audio/audio_device_factory.h
index d4f3704..00f9f19 100644
--- a/content/renderer/media/audio/audio_device_factory.h
+++ b/content/renderer/media/audio/audio_device_factory.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
+#include "media/audio/audio_sink_parameters.h"
 #include "media/base/audio_latency.h"
 #include "media/base/output_device_info.h"
 
@@ -18,7 +19,7 @@
 class AudioRendererSink;
 class SwitchableAudioRendererSink;
 class AudioCapturerSource;
-}
+}  // namespace media
 
 namespace content {
 
@@ -51,16 +52,10 @@
 
   // Creates a sink for AudioRendererMixer.
   // |render_frame_id| refers to the RenderFrame containing the entity
-  // producing the audio. If |session_id| is nonzero, it is used by the browser
-  // to select the correct input device ID and its associated output device, if
-  // it exists. If |session_id| is zero, |device_id| identify the output device
-  // to use.
-  // If |session_id| is zero and |device_id| is empty, the default output
-  // device will be selected.
+  // producing the audio.
   static scoped_refptr<media::AudioRendererSink> NewAudioRendererMixerSink(
       int render_frame_id,
-      int session_id,
-      const std::string& device_id);
+      const media::AudioSinkParameters& params);
 
   // Creates an AudioRendererSink bound to an AudioOutputDevice.
   // Basing on |source_type| and build configuration, audio played out through
@@ -70,8 +65,7 @@
   static scoped_refptr<media::AudioRendererSink> NewAudioRendererSink(
       SourceType source_type,
       int render_frame_id,
-      int session_id,
-      const std::string& device_id);
+      const media::AudioSinkParameters& params);
 
   // Creates a SwitchableAudioRendererSink bound to an AudioOutputDevice
   // Basing on |source_type| and build configuration, audio played out through
@@ -79,15 +73,13 @@
   static scoped_refptr<media::SwitchableAudioRendererSink>
   NewSwitchableAudioRendererSink(SourceType source_type,
                                  int render_frame_id,
-                                 int session_id,
-                                 const std::string& device_id);
+                                 const media::AudioSinkParameters& params);
 
   // A helper to get device info in the absence of AudioOutputDevice.
   // Must be called on renderer thread only.
   static media::OutputDeviceInfo GetOutputDeviceInfo(
       int render_frame_id,
-      int session_id,
-      const std::string& device_id);
+      const media::AudioSinkParameters& params);
 
   // Creates an AudioCapturerSource using the currently registered factory.
   // |render_frame_id| refers to the RenderFrame containing the entity
@@ -109,20 +101,18 @@
   // output device.
   virtual scoped_refptr<media::AudioRendererSink> CreateFinalAudioRendererSink(
       int render_frame_id,
-      int sesssion_id,
-      const std::string& device_id) = 0;
+      const media::AudioSinkParameters& params) = 0;
 
   virtual scoped_refptr<media::AudioRendererSink> CreateAudioRendererSink(
       SourceType source_type,
       int render_frame_id,
-      int sesssion_id,
-      const std::string& device_id) = 0;
+      const media::AudioSinkParameters& params) = 0;
 
   virtual scoped_refptr<media::SwitchableAudioRendererSink>
-  CreateSwitchableAudioRendererSink(SourceType source_type,
-                                    int render_frame_id,
-                                    int sesssion_id,
-                                    const std::string& device_id) = 0;
+  CreateSwitchableAudioRendererSink(
+      SourceType source_type,
+      int render_frame_id,
+      const media::AudioSinkParameters& params) = 0;
 
   virtual scoped_refptr<media::AudioCapturerSource> CreateAudioCapturerSource(
       int render_frame_id) = 0;
@@ -134,8 +124,7 @@
 
   static scoped_refptr<media::AudioRendererSink> NewFinalAudioRendererSink(
       int render_frame_id,
-      int session_id,
-      const std::string& device_id);
+      const media::AudioSinkParameters& params);
 
   DISALLOW_COPY_AND_ASSIGN(AudioDeviceFactory);
 };
diff --git a/content/renderer/media/audio/audio_renderer_sink_cache_impl.cc b/content/renderer/media/audio/audio_renderer_sink_cache_impl.cc
index a28bcf9e..9a8988784 100644
--- a/content/renderer/media/audio/audio_renderer_sink_cache_impl.cc
+++ b/content/renderer/media/audio/audio_renderer_sink_cache_impl.cc
@@ -25,6 +25,7 @@
 
 AudioRendererSinkCacheImpl* AudioRendererSinkCacheImpl::instance_ = nullptr;
 constexpr int kDeleteTimeoutMs = 5000;
+constexpr int kDefaultSessionId = 0;
 
 class AudioRendererSinkCacheImpl::FrameObserver : public RenderFrameObserver {
  public:
@@ -138,7 +139,7 @@
     // We are provided with session id instead of device id. Session id is
     // unique, so we can't find any matching sink. Creating a new one.
     scoped_refptr<media::AudioRendererSink> sink =
-        create_sink_cb_.Run(source_render_frame_id, session_id, device_id);
+        create_sink_cb_.Run(source_render_frame_id, {session_id, device_id});
 
     CacheOrStopUnusedSink(source_render_frame_id,
                           sink->GetOutputDeviceInfo().device_id(), sink);
@@ -169,7 +170,8 @@
 
   // No matching sink found, create a new one.
   scoped_refptr<media::AudioRendererSink> sink = create_sink_cb_.Run(
-      source_render_frame_id, 0 /* session_id */, device_id);
+      source_render_frame_id,
+      media::AudioSinkParameters(kDefaultSessionId, device_id));
 
   CacheOrStopUnusedSink(source_render_frame_id, device_id, sink);
 
@@ -208,10 +210,12 @@
   }
 
   // No unused sink is found, create one, mark it used, cache it and return.
-  CacheEntry cache_entry = {source_render_frame_id, device_id,
-                            create_sink_cb_.Run(source_render_frame_id,
-                                                0 /* session_id */, device_id),
-                            true /* used */};
+  CacheEntry cache_entry = {
+      source_render_frame_id, device_id,
+      create_sink_cb_.Run(
+          source_render_frame_id,
+          media::AudioSinkParameters(kDefaultSessionId, device_id)),
+      true /* used */};
 
   if (SinkIsHealthy(cache_entry.sink.get())) {
     TRACE_EVENT_INSTANT0(
diff --git a/content/renderer/media/audio/audio_renderer_sink_cache_impl.h b/content/renderer/media/audio/audio_renderer_sink_cache_impl.h
index 0b6e872..21e92df 100644
--- a/content/renderer/media/audio/audio_renderer_sink_cache_impl.h
+++ b/content/renderer/media/audio/audio_renderer_sink_cache_impl.h
@@ -16,6 +16,7 @@
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
 #include "content/common/content_export.h"
+#include "media/audio/audio_sink_parameters.h"
 
 namespace content {
 
@@ -29,8 +30,7 @@
   using CreateSinkCallback =
       base::RepeatingCallback<scoped_refptr<media::AudioRendererSink>(
           int render_frame_id,
-          int session_id,
-          const std::string& device_id)>;
+          const media::AudioSinkParameters& params)>;
 
   AudioRendererSinkCacheImpl(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
diff --git a/content/renderer/media/audio/audio_renderer_sink_cache_unittest.cc b/content/renderer/media/audio/audio_renderer_sink_cache_unittest.cc
index 3c3172e..a08ba6a 100644
--- a/content/renderer/media/audio/audio_renderer_sink_cache_unittest.cc
+++ b/content/renderer/media/audio/audio_renderer_sink_cache_unittest.cc
@@ -55,10 +55,9 @@
 
   scoped_refptr<media::AudioRendererSink> CreateSink(
       int render_frame_id,
-      int session_id,
-      const std::string& device_id) {
+      const media::AudioSinkParameters& params) {
     return new testing::NiceMock<media::MockAudioRendererSink>(
-        device_id, (device_id == kUnhealthyDeviceId)
+        params.device_id, (params.device_id == kUnhealthyDeviceId)
                        ? media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL
                        : media::OUTPUT_DEVICE_STATUS_OK);
   }
@@ -250,10 +249,10 @@
       task_env_.GetMainThreadTaskRunner(),
       base::BindRepeating(
           [](scoped_refptr<media::AudioRendererSink> sink, int render_frame_id,
-             int session_id, const std::string& device_id) {
+             const media::AudioSinkParameters& params) {
             EXPECT_EQ(kRenderFrameId, render_frame_id);
-            EXPECT_EQ(0, session_id);
-            EXPECT_EQ(kUnhealthyDeviceId, device_id);
+            EXPECT_EQ(0, params.session_id);
+            EXPECT_EQ(kUnhealthyDeviceId, params.device_id);
             return sink;
           },
           sink),
@@ -276,10 +275,10 @@
       task_env_.GetMainThreadTaskRunner(),
       base::BindRepeating(
           [](scoped_refptr<media::AudioRendererSink> sink, int render_frame_id,
-             int session_id, const std::string& device_id) {
+             const media::AudioSinkParameters& params) {
             EXPECT_EQ(kRenderFrameId, render_frame_id);
-            EXPECT_EQ(kNonZeroSessionId, session_id);
-            EXPECT_TRUE(device_id.empty());
+            EXPECT_EQ(kNonZeroSessionId, params.session_id);
+            EXPECT_TRUE(params.device_id.empty());
             return sink;
           },
           sink),
diff --git a/content/renderer/media/audio/mock_audio_device_factory.h b/content/renderer/media/audio/mock_audio_device_factory.h
index 0958cc23..a4010bdc 100644
--- a/content/renderer/media/audio/mock_audio_device_factory.h
+++ b/content/renderer/media/audio/mock_audio_device_factory.h
@@ -46,23 +46,20 @@
 
   // These methods are just mocked because tests currently don't need them to be
   // implemented.
-  MOCK_METHOD3(
-      CreateFinalAudioRendererSink,
-      scoped_refptr<media::AudioRendererSink>(int render_frame_id,
-                                              int sesssion_id,
-                                              const std::string& device_id));
-  MOCK_METHOD4(
-      CreateAudioRendererSink,
-      scoped_refptr<media::AudioRendererSink>(SourceType source_type,
-                                              int render_frame_id,
-                                              int sesssion_id,
-                                              const std::string& device_id));
-  MOCK_METHOD4(CreateSwitchableAudioRendererSink,
+  MOCK_METHOD2(CreateFinalAudioRendererSink,
+               scoped_refptr<media::AudioRendererSink>(
+                   int render_frame_id,
+                   const media::AudioSinkParameters& params));
+  MOCK_METHOD3(CreateAudioRendererSink,
+               scoped_refptr<media::AudioRendererSink>(
+                   SourceType source_type,
+                   int render_frame_id,
+                   const media::AudioSinkParameters& params));
+  MOCK_METHOD3(CreateSwitchableAudioRendererSink,
                scoped_refptr<media::SwitchableAudioRendererSink>(
                    SourceType source_type,
                    int render_frame_id,
-                   int sesssion_id,
-                   const std::string& device_id));
+                   const media::AudioSinkParameters& params));
 
   // Returns mock_capturer_source_ once. If called a second time, the process
   // will crash.
diff --git a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
index fa1ed7e3..3a5258e4 100644
--- a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
+++ b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
@@ -31,6 +31,7 @@
 #include "media/video/video_encode_accelerator.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
+#include "third_party/skia/include/core/SkPostConfig.h"
 
 namespace content {
 
@@ -348,6 +349,15 @@
       return media::GpuVideoAcceleratorFactories::OutputFormat::I420;
     return media::GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED;
   }
+
+  if (pixel_format == media::PIXEL_FORMAT_I420A) {
+#if SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
+    return media::GpuVideoAcceleratorFactories::OutputFormat::BGRA;
+#elif SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
+    return media::GpuVideoAcceleratorFactories::OutputFormat::RGBA;
+#endif
+  }
+
   if (capabilities.image_ycbcr_420v &&
       !capabilities.image_ycbcr_420v_disabled_for_video_frames) {
     return media::GpuVideoAcceleratorFactories::OutputFormat::NV12_SINGLE_GMB;
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index a594229..3d3c61b 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -243,7 +243,8 @@
   scoped_refptr<media::SwitchableAudioRendererSink> audio_renderer_sink =
       AudioDeviceFactory::NewSwitchableAudioRendererSink(
           AudioDeviceFactory::kSourceMediaElement,
-          render_frame_->GetRoutingID(), 0, sink_id.Utf8());
+          render_frame_->GetRoutingID(),
+          media::AudioSinkParameters(0, sink_id.Utf8()));
 
   const WebPreferences webkit_preferences =
       render_frame_->GetWebkitPreferences();
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.cc b/content/renderer/media/renderer_webaudiodevice_impl.cc
index 1c57ddf3..16dfe2e6 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl.cc
@@ -92,8 +92,8 @@
 media::AudioParameters GetOutputDeviceParameters(int frame_id,
                                                  int session_id,
                                                  const std::string& device_id) {
-  return AudioDeviceFactory::GetOutputDeviceInfo(frame_id, session_id,
-                                                 device_id)
+  return AudioDeviceFactory::GetOutputDeviceInfo(frame_id,
+                                                 {session_id, device_id})
       .output_params();
 }
 
@@ -168,7 +168,7 @@
 
   sink_ = AudioDeviceFactory::NewAudioRendererSink(
       GetLatencyHintSourceType(latency_hint_.Category()), frame_id_,
-      session_id_, std::string());
+      media::AudioSinkParameters(session_id_, std::string()));
 
   // Use the media thread instead of the render thread for fake Render() calls
   // since it has special connotations for Blink and garbage collection. Timeout
diff --git a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
index 337fff50..21034fb 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
@@ -82,27 +82,25 @@
 
   MOCK_METHOD1(CreateAudioCapturerSource,
                scoped_refptr<media::AudioCapturerSource>(int));
-  MOCK_METHOD3(CreateFinalAudioRendererSink,
-               scoped_refptr<media::AudioRendererSink>(int,
-                                                       int,
-                                                       const std::string&));
-  MOCK_METHOD4(
-      CreateSwitchableAudioRendererSink,
-      scoped_refptr<media::SwitchableAudioRendererSink>(SourceType,
-                                                        int,
-                                                        int,
-                                                        const std::string&));
+  MOCK_METHOD2(CreateFinalAudioRendererSink,
+               scoped_refptr<media::AudioRendererSink>(
+                   int,
+                   const media::AudioSinkParameters&));
+  MOCK_METHOD3(CreateSwitchableAudioRendererSink,
+               scoped_refptr<media::SwitchableAudioRendererSink>(
+                   SourceType,
+                   int,
+                   const media::AudioSinkParameters&));
 
   scoped_refptr<media::AudioRendererSink> CreateAudioRendererSink(
       SourceType source_type,
       int render_frame_id,
-      int session_id,
-      const std::string& device_id) override {
+      const media::AudioSinkParameters& params) override {
     scoped_refptr<media::MockAudioRendererSink> mock_sink =
         new media::MockAudioRendererSink(
-            device_id, media::OUTPUT_DEVICE_STATUS_OK,
-            MockGetOutputDeviceParameters(render_frame_id, session_id,
-                                          device_id));
+            params.device_id, media::OUTPUT_DEVICE_STATUS_OK,
+            MockGetOutputDeviceParameters(render_frame_id, params.session_id,
+                                          params.device_id));
 
     EXPECT_CALL(*mock_sink.get(), Start());
     EXPECT_CALL(*mock_sink.get(), Play());
diff --git a/content/renderer/media/stream/media_stream_audio_processor.cc b/content/renderer/media/stream/media_stream_audio_processor.cc
index bb99d12..4b37917 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor.cc
@@ -29,6 +29,7 @@
 #include "media/base/audio_fifo.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
+#include "media/webrtc/echo_information.h"
 #include "third_party/webrtc/api/audio/echo_canceller3_factory.h"
 #include "third_party/webrtc/api/mediaconstraintsinterface.h"
 #include "third_party/webrtc/modules/audio_processing/include/audio_processing_statistics.h"
@@ -628,7 +629,7 @@
     // updated.
     if (properties.echo_cancellation_type !=
         EchoCancellationType::kEchoCancellationAec3) {
-      echo_information_ = std::make_unique<EchoInformation>();
+      echo_information_ = std::make_unique<media::EchoInformation>();
     }
   }
 
diff --git a/content/renderer/media/stream/media_stream_audio_processor.h b/content/renderer/media/stream/media_stream_audio_processor.h
index c523757..f738f82 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.h
+++ b/content/renderer/media/stream/media_stream_audio_processor.h
@@ -30,6 +30,7 @@
 namespace media {
 class AudioBus;
 class AudioParameters;
+class EchoInformation;
 }  // namespace media
 
 namespace webrtc {
@@ -38,7 +39,6 @@
 
 namespace content {
 
-class EchoInformation;
 class MediaStreamAudioBus;
 class MediaStreamAudioFifo;
 
@@ -147,11 +147,6 @@
   // Helper to initialize the capture converter.
   void InitializeCaptureFifo(const media::AudioParameters& input_format);
 
-  // Helper to initialize the render converter.
-  void InitializeRenderFifoIfNeeded(int sample_rate,
-                                    int number_of_channels,
-                                    int frames_per_buffer);
-
   // Called by ProcessAndConsumeData().
   // Returns the new microphone volume in the range of |0, 255].
   // When the volume does not need to be updated, it returns 0.
@@ -221,7 +216,7 @@
 
   // Object for logging UMA stats for echo information when the AEC is enabled.
   // Accessed on the main render thread.
-  std::unique_ptr<EchoInformation> echo_information_;
+  std::unique_ptr<media::EchoInformation> echo_information_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaStreamAudioProcessor);
 };
diff --git a/content/renderer/media/stream/media_stream_audio_processor_options.cc b/content/renderer/media/stream/media_stream_audio_processor_options.cc
index 68d09fe..a2c09a5 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_options.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor_options.cc
@@ -28,40 +28,6 @@
 
 namespace content {
 
-namespace {
-
-// Used to log echo quality based on delay estimates.
-enum DelayBasedEchoQuality {
-  DELAY_BASED_ECHO_QUALITY_GOOD = 0,
-  DELAY_BASED_ECHO_QUALITY_SPURIOUS,
-  DELAY_BASED_ECHO_QUALITY_BAD,
-  DELAY_BASED_ECHO_QUALITY_INVALID,
-  DELAY_BASED_ECHO_QUALITY_MAX
-};
-
-DelayBasedEchoQuality EchoDelayFrequencyToQuality(float delay_frequency) {
-  const float kEchoDelayFrequencyLowerLimit = 0.1f;
-  const float kEchoDelayFrequencyUpperLimit = 0.8f;
-  // DELAY_BASED_ECHO_QUALITY_GOOD
-  //   delay is out of bounds during at most 10 % of the time.
-  // DELAY_BASED_ECHO_QUALITY_SPURIOUS
-  //   delay is out of bounds 10-80 % of the time.
-  // DELAY_BASED_ECHO_QUALITY_BAD
-  //   delay is mostly out of bounds >= 80 % of the time.
-  // DELAY_BASED_ECHO_QUALITY_INVALID
-  //   delay_frequency is negative which happens if we have insufficient data.
-  if (delay_frequency < 0)
-    return DELAY_BASED_ECHO_QUALITY_INVALID;
-  else if (delay_frequency <= kEchoDelayFrequencyLowerLimit)
-    return DELAY_BASED_ECHO_QUALITY_GOOD;
-  else if (delay_frequency < kEchoDelayFrequencyUpperLimit)
-    return DELAY_BASED_ECHO_QUALITY_SPURIOUS;
-  else
-    return DELAY_BASED_ECHO_QUALITY_BAD;
-}
-
-}  // namespace
-
 AudioProcessingProperties::AudioProcessingProperties() = default;
 AudioProcessingProperties::AudioProcessingProperties(
     const AudioProcessingProperties& other) = default;
@@ -90,117 +56,6 @@
          echo_cancellation_type == EchoCancellationType::kEchoCancellationAec3;
 }
 
-EchoInformation::EchoInformation()
-    : delay_stats_time_ms_(0),
-      echo_frames_received_(false),
-      divergent_filter_stats_time_ms_(0),
-      num_divergent_filter_fraction_(0),
-      num_non_zero_divergent_filter_fraction_(0) {}
-
-EchoInformation::~EchoInformation() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  ReportAndResetAecDivergentFilterStats();
-}
-
-void EchoInformation::UpdateAecStats(
-    webrtc::EchoCancellation* echo_cancellation) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (!echo_cancellation->is_enabled())
-    return;
-
-  UpdateAecDelayStats(echo_cancellation);
-  UpdateAecDivergentFilterStats(echo_cancellation);
-}
-
-void EchoInformation::UpdateAecDelayStats(
-    webrtc::EchoCancellation* echo_cancellation) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // Only start collecting stats if we know echo cancellation has measured an
-  // echo. Otherwise we clutter the stats with for example cases where only the
-  // microphone is used.
-  if (!echo_frames_received_ & !echo_cancellation->stream_has_echo())
-    return;
-
-  echo_frames_received_ = true;
-
-  // In WebRTC, three echo delay metrics are calculated and updated every
-  // five seconds. We use one of them, |fraction_poor_delays| to log in a UMA
-  // histogram an Echo Cancellation quality metric. The stat in WebRTC has a
-  // fixed aggregation window of five seconds, so we use the same query
-  // frequency to avoid logging old values.
-  if (!echo_cancellation->is_delay_logging_enabled())
-    return;
-
-  delay_stats_time_ms_ += webrtc::AudioProcessing::kChunkSizeMs;
-  if (delay_stats_time_ms_ <
-      500 * webrtc::AudioProcessing::kChunkSizeMs) {  // 5 seconds
-    return;
-  }
-
-  int dummy_median = 0, dummy_std = 0;
-  float fraction_poor_delays = 0;
-  if (echo_cancellation->GetDelayMetrics(
-          &dummy_median, &dummy_std, &fraction_poor_delays) ==
-      webrtc::AudioProcessing::kNoError) {
-    delay_stats_time_ms_ = 0;
-    // Map |fraction_poor_delays| to an Echo Cancellation quality and log in UMA
-    // histogram. See DelayBasedEchoQuality for information on histogram
-    // buckets.
-    UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality",
-                              EchoDelayFrequencyToQuality(fraction_poor_delays),
-                              DELAY_BASED_ECHO_QUALITY_MAX);
-  }
-}
-
-void EchoInformation::UpdateAecDivergentFilterStats(
-    webrtc::EchoCancellation* echo_cancellation) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (!echo_cancellation->are_metrics_enabled())
-    return;
-
-  divergent_filter_stats_time_ms_ += webrtc::AudioProcessing::kChunkSizeMs;
-  if (divergent_filter_stats_time_ms_ <
-      100 * webrtc::AudioProcessing::kChunkSizeMs) {  // 1 second
-    return;
-  }
-
-  webrtc::EchoCancellation::Metrics metrics;
-  if (echo_cancellation->GetMetrics(&metrics) ==
-      webrtc::AudioProcessing::kNoError) {
-    // If not yet calculated, |metrics.divergent_filter_fraction| is -1.0. After
-    // being calculated the first time, it is updated periodically.
-    if (metrics.divergent_filter_fraction < 0.0f) {
-      DCHECK_EQ(num_divergent_filter_fraction_, 0);
-      return;
-    }
-    if (metrics.divergent_filter_fraction > 0.0f) {
-      ++num_non_zero_divergent_filter_fraction_;
-    }
-  } else {
-    DLOG(WARNING) << "Get echo cancellation metrics failed.";
-  }
-  ++num_divergent_filter_fraction_;
-  divergent_filter_stats_time_ms_ = 0;
-}
-
-void EchoInformation::ReportAndResetAecDivergentFilterStats() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (num_divergent_filter_fraction_ == 0)
-    return;
-
-  int non_zero_percent = 100 * num_non_zero_divergent_filter_fraction_ /
-                         num_divergent_filter_fraction_;
-  UMA_HISTOGRAM_PERCENTAGE("WebRTC.AecFilterHasDivergence", non_zero_percent);
-
-  divergent_filter_stats_time_ms_ = 0;
-  num_non_zero_divergent_filter_fraction_ = 0;
-  num_divergent_filter_fraction_ = 0;
-}
-
 void EnableEchoCancellation(AudioProcessing* audio_processing) {
   // TODO(bugs.webrtc.org/9535): Remove double-booking AEC toggle when the
   // config applies (from 2018-08-16).
diff --git a/content/renderer/media/stream/media_stream_audio_processor_options.h b/content/renderer/media/stream/media_stream_audio_processor_options.h
index 75dd60a..f216dda 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_options.h
+++ b/content/renderer/media/stream/media_stream_audio_processor_options.h
@@ -22,7 +22,6 @@
 
 namespace webrtc {
 
-class EchoCancellation;
 class TypingDetection;
 
 }
@@ -78,46 +77,6 @@
   bool goog_experimental_auto_gain_control = true;
 };
 
-// A helper class to log echo information in general and Echo Cancellation
-// quality in particular.
-class CONTENT_EXPORT EchoInformation {
- public:
-  EchoInformation();
-  virtual ~EchoInformation();
-
-  // Updates stats, and reports delay metrics as UMA stats every 5 seconds.
-  // Must be called every time AudioProcessing::ProcessStream() is called.
-  void UpdateAecStats(webrtc::EchoCancellation* echo_cancellation);
-
-  // Reports AEC divergent filter metrics as UMA and resets the associated data.
-  void ReportAndResetAecDivergentFilterStats();
-
- private:
-  void UpdateAecDelayStats(webrtc::EchoCancellation* echo_cancellation);
-  void UpdateAecDivergentFilterStats(
-      webrtc::EchoCancellation* echo_cancellation);
-
-  // Counter to track 5 seconds of data in order to query a new metric from
-  // webrtc::EchoCancellation::GetEchoDelayMetrics().
-  int delay_stats_time_ms_;
-  bool echo_frames_received_;
-
-  // Counter to track 1 second of data in order to query a new divergent filter
-  // fraction metric from webrtc::EchoCancellation::GetMetrics().
-  int divergent_filter_stats_time_ms_;
-
-  // Total number of times we queried for the divergent filter fraction metric.
-  int num_divergent_filter_fraction_;
-
-  // Number of non-zero divergent filter fraction metrics.
-  int num_non_zero_divergent_filter_fraction_;
-
-  // Ensures that this class is accessed on the same thread.
-  base::ThreadChecker thread_checker_;
-
-  DISALLOW_COPY_AND_ASSIGN(EchoInformation);
-};
-
 // Enables the echo cancellation in |audio_processing|.
 void EnableEchoCancellation(AudioProcessing* audio_processing);
 
diff --git a/content/renderer/media/stream/track_audio_renderer.cc b/content/renderer/media/stream/track_audio_renderer.cc
index bcedaf5..892a310 100644
--- a/content/renderer/media/stream/track_audio_renderer.cc
+++ b/content/renderer/media/stream/track_audio_renderer.cc
@@ -151,7 +151,7 @@
   DCHECK(!sink_);
   sink_ = AudioDeviceFactory::NewAudioRendererSink(
       AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_,
-      session_id_, output_device_id_);
+      {session_id_, output_device_id_});
 
   base::AutoLock auto_lock(thread_lock_);
   prior_elapsed_render_time_ = base::TimeDelta();
@@ -253,7 +253,7 @@
   scoped_refptr<media::AudioRendererSink> new_sink =
       AudioDeviceFactory::NewAudioRendererSink(
           AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_,
-          session_id_, device_id);
+          {session_id_, device_id});
 
   media::OutputDeviceStatus new_sink_status =
       new_sink->GetOutputDeviceInfo().device_status();
@@ -344,7 +344,7 @@
   sink_started_ = false;
   sink_ = AudioDeviceFactory::NewAudioRendererSink(
       AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_,
-      session_id_, output_device_id_);
+      {session_id_, output_device_id_});
   MaybeStartSink();
 }
 
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 4588dbd..e9b8755e 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -151,6 +151,16 @@
   closure.Run();
 }
 
+void RunSynchronousOnceClosure(base::OnceClosure closure,
+                               const char* trace_event_name,
+                               base::WaitableEvent* event) {
+  {
+    TRACE_EVENT0("webrtc", trace_event_name);
+    std::move(closure).Run();
+  }
+  event->Signal();
+}
+
 void RunSynchronousClosure(const base::Closure& closure,
                            const char* trace_event_name,
                            base::WaitableEvent* event) {
@@ -164,11 +174,11 @@
 // Initializes |web_description| if |description_callback| returns non-null,
 // otherwise does nothing.
 void GetWebRTCSessionDescriptionFromSessionDescriptionCallback(
-    const base::Callback<const webrtc::SessionDescriptionInterface*()>&
+    base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
         description_callback,
     blink::WebRTCSessionDescription* web_description) {
   const webrtc::SessionDescriptionInterface* description =
-      description_callback.Run();
+      std::move(description_callback).Run();
   if (description) {
     std::string sdp;
     description->ToString(&sdp);
@@ -1359,45 +1369,72 @@
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::localDescription");
 
-  // Since webrtc::PeerConnectionInterface::local_description() returns a
-  // pointer to a non-reference-counted object that lives on the signaling
-  // thread, we cannot fetch a pointer to it and use it directly here. Instead,
-  // we access the object completely on the signaling thread. Initializing
-  // |local_description| on the signaling thread is safe because we own it and
-  // wait for it to be initialized here.
-  blink::WebRTCSessionDescription local_description;  // IsNull() by default.
-  base::Callback<const webrtc::SessionDescriptionInterface*()> description_cb =
-      base::Bind(&webrtc::PeerConnectionInterface::local_description,
-                 native_peer_connection_);
-  RunSynchronousClosureOnSignalingThread(
-      base::Bind(&GetWebRTCSessionDescriptionFromSessionDescriptionCallback,
-                 std::move(description_cb),
-                 base::Unretained(&local_description)),
-      "localDescription");
-
-  return local_description;
+  base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
+      description_cb =
+          base::BindOnce(&webrtc::PeerConnectionInterface::local_description,
+                         native_peer_connection_);
+  return GetWebRTCSessionDescriptionOnSignalingThread(std::move(description_cb),
+                                                      "localDescription");
 }
 
 blink::WebRTCSessionDescription RTCPeerConnectionHandler::RemoteDescription() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::remoteDescription");
-  // Since webrtc::PeerConnectionInterface::remote_description() returns a
-  // pointer to a non-reference-counted object that lives on the signaling
-  // thread, we cannot fetch a pointer to it and use it directly here. Instead,
-  // we access the object completely on the signaling thread. Initializing
-  // |remote_description| on the signaling thread is safe because we own it and
-  // wait for it to be initialized here.
-  blink::WebRTCSessionDescription remote_description;  // IsNull() by default.
-  base::Callback<const webrtc::SessionDescriptionInterface*()> description_cb =
-      base::Bind(&webrtc::PeerConnectionInterface::remote_description,
-                 native_peer_connection_);
-  RunSynchronousClosureOnSignalingThread(
-      base::Bind(&GetWebRTCSessionDescriptionFromSessionDescriptionCallback,
-                 std::move(description_cb),
-                 base::Unretained(&remote_description)),
-      "remoteDescription");
+  base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
+      description_cb =
+          base::BindOnce(&webrtc::PeerConnectionInterface::remote_description,
+                         native_peer_connection_);
+  return GetWebRTCSessionDescriptionOnSignalingThread(std::move(description_cb),
+                                                      "remoteDescription");
+}
 
-  return remote_description;
+blink::WebRTCSessionDescription
+RTCPeerConnectionHandler::CurrentLocalDescription() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::currentLocalDescription");
+
+  base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
+      description_cb = base::BindOnce(
+          &webrtc::PeerConnectionInterface::current_local_description,
+          native_peer_connection_);
+  return GetWebRTCSessionDescriptionOnSignalingThread(
+      std::move(description_cb), "currentLocalDescription");
+}
+
+blink::WebRTCSessionDescription
+RTCPeerConnectionHandler::CurrentRemoteDescription() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::currentRemoteDescription");
+  base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
+      description_cb = base::BindOnce(
+          &webrtc::PeerConnectionInterface::current_remote_description,
+          native_peer_connection_);
+  return GetWebRTCSessionDescriptionOnSignalingThread(
+      std::move(description_cb), "currentRemoteDescription");
+}
+
+blink::WebRTCSessionDescription
+RTCPeerConnectionHandler::PendingLocalDescription() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::pendingLocalDescription");
+  base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
+      description_cb = base::BindOnce(
+          &webrtc::PeerConnectionInterface::pending_local_description,
+          native_peer_connection_);
+  return GetWebRTCSessionDescriptionOnSignalingThread(
+      std::move(description_cb), "pendingLocalDescription");
+}
+
+blink::WebRTCSessionDescription
+RTCPeerConnectionHandler::PendingRemoteDescription() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::pendingRemoteDescription");
+  base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
+      description_cb = base::BindOnce(
+          &webrtc::PeerConnectionInterface::pending_remote_description,
+          native_peer_connection_);
+  return GetWebRTCSessionDescriptionOnSignalingThread(
+      std::move(description_cb), "pendingRemoteDescription");
 }
 
 webrtc::RTCErrorType RTCPeerConnectionHandler::SetConfiguration(
@@ -2351,8 +2388,29 @@
   return dependency_factory_->GetWebRtcSignalingThread();
 }
 
+void RTCPeerConnectionHandler::RunSynchronousOnceClosureOnSignalingThread(
+    base::OnceClosure closure,
+    const char* trace_event_name) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  scoped_refptr<base::SingleThreadTaskRunner> thread(signaling_thread());
+  if (!thread.get() || thread->BelongsToCurrentThread()) {
+    TRACE_EVENT0("webrtc", trace_event_name);
+    std::move(closure).Run();
+  } else {
+    base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                              base::WaitableEvent::InitialState::NOT_SIGNALED);
+    thread->PostTask(
+        FROM_HERE,
+        base::BindOnce(&RunSynchronousOnceClosure, std::move(closure),
+                       base::Unretained(trace_event_name),
+                       base::Unretained(&event)));
+    event.Wait();
+  }
+}
+
+// Deprecated version - uses a RepeatingCosure (aka old-style Closure)
 void RTCPeerConnectionHandler::RunSynchronousClosureOnSignalingThread(
-    const base::Closure& closure,
+    const base::RepeatingClosure& closure,
     const char* trace_event_name) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   scoped_refptr<base::SingleThreadTaskRunner> thread(signaling_thread());
@@ -2370,6 +2428,27 @@
   }
 }
 
+blink::WebRTCSessionDescription
+RTCPeerConnectionHandler::GetWebRTCSessionDescriptionOnSignalingThread(
+    base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
+        description_cb,
+    const char* log_text) {
+  // Since the webrtc::PeerConnectionInterface::*_description() functions
+  // return a pointer to a non-reference-counted object that lives on the
+  // signaling thread, we cannot fetch a pointer to it and use it directly
+  // here.
+  // Instead, we access the object completely on the signaling thread.
+  // Initializing |description| on the signaling thread is safe because we
+  // own it and wait for it to be initialized here.
+
+  blink::WebRTCSessionDescription description;  // IsNull() by default.
+  RunSynchronousOnceClosureOnSignalingThread(
+      base::BindOnce(&GetWebRTCSessionDescriptionFromSessionDescriptionCallback,
+                     std::move(description_cb), base::Unretained(&description)),
+      log_text);
+  return description;
+}
+
 void RTCPeerConnectionHandler::ReportICEState(
     webrtc::PeerConnectionInterface::IceConnectionState new_state) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.h b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
index 639d170..9fff828 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.h
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
@@ -132,6 +132,10 @@
 
   blink::WebRTCSessionDescription LocalDescription() override;
   blink::WebRTCSessionDescription RemoteDescription() override;
+  blink::WebRTCSessionDescription CurrentLocalDescription() override;
+  blink::WebRTCSessionDescription CurrentRemoteDescription() override;
+  blink::WebRTCSessionDescription PendingLocalDescription() override;
+  blink::WebRTCSessionDescription PendingRemoteDescription() override;
 
   webrtc::RTCErrorType SetConfiguration(
       const blink::WebRTCConfiguration& configuration) override;
@@ -237,6 +241,11 @@
       const std::string& type,
       webrtc::SdpParseError* error);
 
+  blink::WebRTCSessionDescription GetWebRTCSessionDescriptionOnSignalingThread(
+      base::OnceCallback<const webrtc::SessionDescriptionInterface*()>
+          description_cb,
+      const char* log_text);
+
   // Report to UMA whether an IceConnectionState has occurred. It only records
   // the first occurrence of a given state.
   void ReportICEState(
@@ -295,6 +304,8 @@
 
   void RunSynchronousClosureOnSignalingThread(const base::Closure& closure,
                                               const char* trace_event_name);
+  void RunSynchronousOnceClosureOnSignalingThread(base::OnceClosure closure,
+                                                  const char* trace_event_name);
 
   // Corresponds to the experimental RTCPeerConnection.id read-only attribute.
   const std::string id_;
diff --git a/content/renderer/media/webrtc/webrtc_audio_renderer.cc b/content/renderer/media/webrtc/webrtc_audio_renderer.cc
index 874d23a..73f688a 100644
--- a/content/renderer/media/webrtc/webrtc_audio_renderer.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_renderer.cc
@@ -196,9 +196,10 @@
     DCHECK(!source_);
   }
 
+  media::AudioSinkParameters sink_params(session_id_, output_device_id_);
+  /* TODO(ossu): Add processing id */
   sink_ = AudioDeviceFactory::NewAudioRendererSink(
-      AudioDeviceFactory::kSourceWebRtc, source_render_frame_id_, session_id_,
-      output_device_id_);
+      AudioDeviceFactory::kSourceWebRtc, source_render_frame_id_, sink_params);
 
   if (sink_->GetOutputDeviceInfo().device_status() !=
       media::OUTPUT_DEVICE_STATUS_OK) {
@@ -384,10 +385,12 @@
     DCHECK_NE(state_, UNINITIALIZED);
   }
 
+  media::AudioSinkParameters sink_params(session_id_, device_id);
+  /* TODO(ossu): Add processing id */
   scoped_refptr<media::AudioRendererSink> new_sink =
       AudioDeviceFactory::NewAudioRendererSink(
           AudioDeviceFactory::kSourceWebRtc, source_render_frame_id_,
-          session_id_, device_id);
+          sink_params);
   media::OutputDeviceStatus status =
       new_sink->GetOutputDeviceInfo().device_status();
   if (status != media::OUTPUT_DEVICE_STATUS_OK) {
diff --git a/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc b/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
index e3e2cce..11826aaa 100644
--- a/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
@@ -75,8 +75,9 @@
   void SetupRenderer(const std::string& device_id) {
     renderer_ = new WebRtcAudioRenderer(message_loop_->task_runner(), stream_,
                                         1, 1, device_id);
-    EXPECT_CALL(*this, MockCreateAudioRendererSink(
-                           AudioDeviceFactory::kSourceWebRtc, _, _, device_id));
+    EXPECT_CALL(
+        *this, MockCreateAudioRendererSink(AudioDeviceFactory::kSourceWebRtc, _,
+                                           _, device_id, _));
     EXPECT_CALL(*source_.get(), SetOutputDeviceForAec(device_id));
     EXPECT_TRUE(renderer_->Initialize(source_.get()));
 
@@ -84,42 +85,44 @@
   }
   MOCK_METHOD1(CreateAudioCapturerSource,
                scoped_refptr<media::AudioCapturerSource>(int));
-  MOCK_METHOD3(CreateFinalAudioRendererSink,
-               scoped_refptr<media::AudioRendererSink>(int,
-                                                       int,
-                                                       const std::string&));
-  MOCK_METHOD4(
-      CreateSwitchableAudioRendererSink,
-      scoped_refptr<media::SwitchableAudioRendererSink>(SourceType,
-                                                        int,
-                                                        int,
-                                                        const std::string&));
-  MOCK_METHOD4(MockCreateAudioRendererSink,
-               void(SourceType, int, int, const std::string&));
+  MOCK_METHOD2(CreateFinalAudioRendererSink,
+               scoped_refptr<media::AudioRendererSink>(
+                   int,
+                   const media::AudioSinkParameters&));
+  MOCK_METHOD3(CreateSwitchableAudioRendererSink,
+               scoped_refptr<media::SwitchableAudioRendererSink>(
+                   SourceType,
+                   int,
+                   const media::AudioSinkParameters&));
+  MOCK_METHOD5(MockCreateAudioRendererSink,
+               void(SourceType,
+                    int,
+                    int,
+                    const std::string&,
+                    const base::Optional<base::UnguessableToken>&));
 
   scoped_refptr<media::AudioRendererSink> CreateAudioRendererSink(
       SourceType source_type,
       int render_frame_id,
-      int session_id,
-      const std::string& device_id) override {
+      const media::AudioSinkParameters& params) override {
     mock_sink_ = new media::MockAudioRendererSink(
-        device_id,
-        device_id == kInvalidOutputDeviceId
+        params.device_id,
+        params.device_id == kInvalidOutputDeviceId
             ? media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL
             : media::OUTPUT_DEVICE_STATUS_OK,
         media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                media::CHANNEL_LAYOUT_STEREO,
                                kHardwareSampleRate, kHardwareBufferSize));
 
-    if (device_id != kInvalidOutputDeviceId) {
+    if (params.device_id != kInvalidOutputDeviceId) {
       EXPECT_CALL(*mock_sink_.get(), Start());
       EXPECT_CALL(*mock_sink_.get(), Play());
     } else {
       EXPECT_CALL(*mock_sink_.get(), Stop());
     }
 
-    MockCreateAudioRendererSink(source_type, render_frame_id, session_id,
-                                device_id);
+    MockCreateAudioRendererSink(source_type, render_frame_id, params.session_id,
+                                params.device_id, params.processing_id);
     return mock_sink_;
   }
 
@@ -237,7 +240,7 @@
   EXPECT_CALL(*mock_sink_.get(), Stop());
   EXPECT_CALL(*this,
               MockCreateAudioRendererSink(AudioDeviceFactory::kSourceWebRtc, _,
-                                          _, kOtherOutputDeviceId));
+                                          _, kOtherOutputDeviceId, _));
   EXPECT_CALL(*source_.get(), AudioRendererThreadStopped());
   EXPECT_CALL(*source_.get(), SetOutputDeviceForAec(kOtherOutputDeviceId));
   EXPECT_CALL(*this, MockSwitchDeviceCallback(media::OUTPUT_DEVICE_STATUS_OK));
@@ -264,7 +267,7 @@
 
   EXPECT_CALL(*this,
               MockCreateAudioRendererSink(AudioDeviceFactory::kSourceWebRtc, _,
-                                          _, kInvalidOutputDeviceId));
+                                          _, kInvalidOutputDeviceId, _));
   EXPECT_CALL(*this, MockSwitchDeviceCallback(
                          media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL));
   base::RunLoop loop;
@@ -287,7 +290,7 @@
 
   EXPECT_CALL(*this,
               MockCreateAudioRendererSink(AudioDeviceFactory::kSourceWebRtc, _,
-                                          _, kInvalidOutputDeviceId));
+                                          _, kInvalidOutputDeviceId, _));
 
   EXPECT_FALSE(renderer_->Initialize(source_.get()));
 
diff --git a/content/renderer/media/webrtc_local_audio_source_provider.cc b/content/renderer/media/webrtc_local_audio_source_provider.cc
index 8087bd2..1a3c58f 100644
--- a/content/renderer/media/webrtc_local_audio_source_provider.cc
+++ b/content/renderer/media/webrtc_local_audio_source_provider.cc
@@ -39,10 +39,11 @@
       blink::WebLocalFrame::FrameForCurrentContext();
   RenderFrame* const render_frame = RenderFrame::FromWebFrame(web_frame);
   if (render_frame) {
-    int sample_rate = AudioDeviceFactory::GetOutputDeviceInfo(
-                          render_frame->GetRoutingID(), 0, std::string())
-                          .output_params()
-                          .sample_rate();
+    int sample_rate =
+        AudioDeviceFactory::GetOutputDeviceInfo(render_frame->GetRoutingID(),
+                                                media::AudioSinkParameters())
+            .output_params()
+            .sample_rate();
     sink_params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                        media::CHANNEL_LAYOUT_STEREO, sample_rate,
                        kWebAudioRenderBufferSize);
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 2330eed..e2491c2 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -2611,7 +2611,7 @@
     PP_Instance instance) {
   return render_frame() ? AudioDeviceFactory::GetOutputDeviceInfo(
                               render_frame()->GetRoutingID(),
-                              0 /* session_id */, std::string() /* device_id */)
+                              media::AudioSinkParameters())
                               .output_params()
                               .sample_rate()
                         : 0;
@@ -2621,8 +2621,7 @@
     PP_Instance instance) {
   return render_frame() ? AudioDeviceFactory::GetOutputDeviceInfo(
                               render_frame()->GetRoutingID(),
-                              0 /* session_id */, std::string() /* device_id */
-                              )
+                              media::AudioSinkParameters())
                               .output_params()
                               .frames_per_buffer()
                         : 0;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index e406f99..3d099d94 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -7062,7 +7062,8 @@
   media::OutputDeviceStatusCB callback =
       media::ConvertToOutputDeviceStatusCB(web_callbacks);
   std::move(callback).Run(
-      AudioDeviceFactory::GetOutputDeviceInfo(GetRoutingID(), 0, sink_id.Utf8())
+      AudioDeviceFactory::GetOutputDeviceInfo(
+          GetRoutingID(), media::AudioSinkParameters(0, sink_id.Utf8()))
           .device_status());
 }
 
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index f9e8afc..b49f1f9 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -175,7 +175,7 @@
     return media::AudioParameters::UnavailableDeviceParams();
 
   return AudioDeviceFactory::GetOutputDeviceInfo(render_frame->GetRoutingID(),
-                                                 0, std::string())
+                                                 media::AudioSinkParameters())
       .output_params();
 }
 
diff --git a/content/test/data/media/canvas_capture_color.html b/content/test/data/media/canvas_capture_color.html
index 34900c9..be5c580 100644
--- a/content/test/data/media/canvas_capture_color.html
+++ b/content/test/data/media/canvas_capture_color.html
@@ -87,6 +87,11 @@
   var checkLength = alphaContext ? color.length : color.length - 1;
   expectedColor[expectedColor.length - 1] *= MAX_ALPHA;
   for (var i = 0; i < checkLength; i++) {
+    // When |alphaContext| is true we have an alpha channel; premultiply the RGB
+    // channel values that are defined in this file unpremultiplied.
+    if (alphaContext)
+      expectedColor[i] *= expectedColor[-1];
+
     if (Math.abs(pixel[i] - expectedColor[i]) > TOLERANCE) {
       console.log('Expected ' + expectedColor + ', got ' + pixel);
       return false;
diff --git a/content/test/renderer_audio_output_stream_factory_context_impl_unittest.cc b/content/test/renderer_audio_output_stream_factory_context_impl_unittest.cc
index 170b966..a0cd2af 100644
--- a/content/test/renderer_audio_output_stream_factory_context_impl_unittest.cc
+++ b/content/test/renderer_audio_output_stream_factory_context_impl_unittest.cc
@@ -45,7 +45,6 @@
 
 const int kRenderProcessId = 42;
 const int kRenderFrameId = 24;
-const int kNoSessionId = 0;
 const float kWaveFrequency = 440.f;
 const int kChannels = 1;
 const int kBuffers = 1000;
@@ -259,8 +258,8 @@
       renderer_ipc_task_runner);
 
   auto device = base::MakeRefCounted<media::AudioOutputDevice>(
-      std::move(renderer_side_ipc), renderer_ipc_task_runner, kNoSessionId, "",
-      base::TimeDelta());
+      std::move(renderer_side_ipc), renderer_ipc_task_runner,
+      media::AudioSinkParameters(), base::TimeDelta());
 
   StrictMock<TestRenderCallback> source;
 
diff --git a/device/fido/fido_request_handler.h b/device/fido/fido_request_handler.h
index b97092c..f117704d 100644
--- a/device/fido/fido_request_handler.h
+++ b/device/fido/fido_request_handler.h
@@ -30,20 +30,23 @@
                               base::Optional<Response> response_data,
                               FidoTransportProtocol transport_used)>;
 
-  FidoRequestHandler(service_manager::Connector* connector,
-                     const base::flat_set<FidoTransportProtocol>& transports,
-                     CompletionCallback completion_callback)
+  // The |available_transports| should be the intersection of transports
+  // supported by the client and allowed by the relying party.
+  FidoRequestHandler(
+      service_manager::Connector* connector,
+      const base::flat_set<FidoTransportProtocol>& available_transports,
+      CompletionCallback completion_callback)
       : FidoRequestHandler(connector,
-                           transports,
+                           available_transports,
                            std::move(completion_callback),
                            AddPlatformAuthenticatorCallback()) {}
   FidoRequestHandler(
       service_manager::Connector* connector,
-      const base::flat_set<FidoTransportProtocol>& transports,
+      const base::flat_set<FidoTransportProtocol>& available_transports,
       CompletionCallback completion_callback,
       AddPlatformAuthenticatorCallback add_platform_authenticator)
       : FidoRequestHandlerBase(connector,
-                               transports,
+                               available_transports,
                                std::move(add_platform_authenticator)),
         completion_callback_(std::move(completion_callback)) {}
   ~FidoRequestHandler() override {
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index a3fbc539..b1a7a6d 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -21,10 +21,6 @@
 
 namespace {
 
-// Number of async calls we need to wait before notifying the embedder layer
-// of FidoUiAprioriData.
-constexpr size_t kNumAsyncTransportInfoCallbacks = 2;
-
 bool ShouldDeferRequestDispatchToUi(const FidoAuthenticator& authenticator) {
   // TODO(hongjunchoi): Change this to be dependent on authenticator transport
   // type once UI component is in place.
@@ -57,27 +53,44 @@
 
 FidoRequestHandlerBase::FidoRequestHandlerBase(
     service_manager::Connector* connector,
-    const base::flat_set<FidoTransportProtocol>& transports)
+    const base::flat_set<FidoTransportProtocol>& available_transports)
     : FidoRequestHandlerBase(connector,
-                             transports,
+                             available_transports,
                              AddPlatformAuthenticatorCallback()) {}
 
 FidoRequestHandlerBase::FidoRequestHandlerBase(
     service_manager::Connector* connector,
-    const base::flat_set<FidoTransportProtocol>& transports,
+    const base::flat_set<FidoTransportProtocol>& available_transports,
     AddPlatformAuthenticatorCallback add_platform_authenticator)
     : add_platform_authenticator_(std::move(add_platform_authenticator)),
       weak_factory_(this) {
-  for (const auto transport : transports) {
+  // The number of times |notify_observer_callback_| needs to be invoked before
+  // Observer::OnTransportAvailabilityEnumerated is dispatched. Essentially this
+  // is used to wait until all the parts of |transport_availability_info_| are
+  // filled out; the |notify_observer_callback_| is invoked once for each part
+  // once that part is ready, namely:
+  //
+  //   1) Once the platform authenticator related fields are filled out.
+  //   2) [Optionally, if BLE enabled] Once BLE related fields are filled out.
+  //   3) [Optionally, if caBLE enabled] Once caBLE related fields are filled
+  //   out.
+  //
+  // On top of that, we wait for (4) an invocation that happens when the
+  // |observer_| is set, so that OnTransportAvailabilityEnumerated is never
+  // called before the observer is set.
+  size_t transport_info_callback_count = 1u + 0u + 0u + 1u;
+
+  for (const auto transport : available_transports) {
     // Construction of CaBleDiscovery is handled by the implementing class as it
     // requires an extension passed on from the relying party.
-    if (transport == FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy)
+    if (transport == FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy) {
+      ++transport_info_callback_count;
       continue;
+    }
 
     if (transport == FidoTransportProtocol::kInternal) {
       // Internal authenticators are injected through
       // AddPlatformAuthenticatorCallback.
-      NOTREACHED();
       continue;
     }
 
@@ -87,12 +100,17 @@
       // HID transports are not configured.
       continue;
     }
+
+    if (transport == FidoTransportProtocol::kBluetoothLowEnergy)
+      ++transport_info_callback_count;
+
     discovery->set_observer(this);
     discoveries_.push_back(std::move(discovery));
   }
 
+  transport_availability_info_.available_transports = available_transports;
   notify_observer_callback_ = base::BarrierClosure(
-      kNumAsyncTransportInfoCallbacks,
+      transport_info_callback_count,
       base::BindOnce(&FidoRequestHandlerBase::NotifyObserverUiData,
                      weak_factory_.GetWeakPtr()));
 }
@@ -123,14 +141,14 @@
 
 void FidoRequestHandlerBase::DiscoveryStarted(FidoDiscovery* discovery,
                                               bool success) {
-  if (discovery->transport() == FidoTransportProtocol::kBluetoothLowEnergy) {
-    // For FidoBleDiscovery, discovery is started with |success| set to true
-    // if device::BluetoothAdapter is present in the system.
+  if (discovery->transport() == FidoTransportProtocol::kBluetoothLowEnergy ||
+      discovery->transport() ==
+          FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy) {
+    // For FidoBleDiscovery and FidoCableDiscovery, discovery is started with
+    // |success| set to false if no BluetoothAdapter is present in the system.
     if (!success) {
       transport_availability_info_.available_transports.erase(
-          FidoTransportProtocol::kBluetoothLowEnergy);
-      transport_availability_info_.available_transports.erase(
-          FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy);
+          discovery->transport());
     }
 
     DCHECK(notify_observer_callback_);
@@ -191,8 +209,11 @@
 
 void FidoRequestHandlerBase::MaybeAddPlatformAuthenticator() {
   std::unique_ptr<FidoAuthenticator> authenticator;
-  if (add_platform_authenticator_)
+  if (add_platform_authenticator_ &&
+      base::ContainsKey(transport_availability_info_.available_transports,
+                        FidoTransportProtocol::kInternal)) {
     authenticator = std::move(add_platform_authenticator_).Run();
+  }
 
   if (authenticator) {
     AddAuthenticator(std::move(authenticator));
@@ -206,8 +227,7 @@
 }
 
 void FidoRequestHandlerBase::NotifyObserverUiData() {
-  if (!observer_)
-    return;
+  DCHECK(observer_);
 
   observer_->OnTransportAvailabilityEnumerated(transport_availability_info_);
 }
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h
index 8253124..c923479c 100644
--- a/device/fido/fido_request_handler_base.h
+++ b/device/fido/fido_request_handler_base.h
@@ -15,6 +15,7 @@
 #include "base/callback.h"
 #include "base/component_export.h"
 #include "base/containers/flat_set.h"
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_piece_forward.h"
@@ -60,7 +61,11 @@
     ~TransportAvailabilityInfo();
 
     RequestType request_type = RequestType::kMakeCredential;
-    std::set<FidoTransportProtocol> available_transports;
+
+    // The intersection of transports supported by the client and allowed by the
+    // relying party.
+    base::flat_set<FidoTransportProtocol> available_transports;
+
     bool has_recognized_mac_touch_id_credential = false;
     bool is_ble_powered = false;
     bool can_power_on_ble_adapter = false;
@@ -70,6 +75,7 @@
    public:
     virtual ~TransportAvailabilityObserver();
 
+    // This method will not be invoked until the observer is set.
     virtual void OnTransportAvailabilityEnumerated(
         TransportAvailabilityInfo data) = 0;
     virtual void BluetoothAdapterPowerChanged(bool is_powered_on) = 0;
@@ -79,13 +85,15 @@
   };
 
   // TODO(https://crbug.com/769631): Remove the dependency on Connector once
-  // device/fido is servicified.
+  // device/fido is servicified. The |available_transports| should be the
+  // intersection of transports supported by the client and allowed by the
+  // relying party.
   FidoRequestHandlerBase(
       service_manager::Connector* connector,
-      const base::flat_set<FidoTransportProtocol>& transports);
+      const base::flat_set<FidoTransportProtocol>& available_transports);
   FidoRequestHandlerBase(
       service_manager::Connector* connector,
-      const base::flat_set<FidoTransportProtocol>& transports,
+      const base::flat_set<FidoTransportProtocol>& available_transports,
       AddPlatformAuthenticatorCallback add_platform_authenticator);
   ~FidoRequestHandlerBase() override;
 
@@ -104,6 +112,13 @@
   void set_observer(TransportAvailabilityObserver* observer) {
     DCHECK(!observer_) << "Only one observer is supported.";
     observer_ = observer;
+
+    DCHECK(notify_observer_callback_);
+    notify_observer_callback_.Run();
+  }
+
+  TransportAvailabilityInfo& transport_availability_info() {
+    return transport_availability_info_;
   }
 
  protected:
@@ -124,9 +139,6 @@
   std::vector<std::unique_ptr<FidoDiscovery>>& discoveries() {
     return discoveries_;
   }
-  TransportAvailabilityInfo& transport_availability_info() {
-    return transport_availability_info_;
-  }
   TransportAvailabilityObserver* observer() const { return observer_; }
 
  private:
diff --git a/device/fido/fido_request_handler_unittest.cc b/device/fido/fido_request_handler_unittest.cc
index 1660af61..f071f2c2 100644
--- a/device/fido/fido_request_handler_unittest.cc
+++ b/device/fido/fido_request_handler_unittest.cc
@@ -45,6 +45,44 @@
   kProcessingError = 0x02,
 };
 
+class TestTransportAvailabilityObserver
+    : public FidoRequestHandlerBase::TransportAvailabilityObserver {
+ public:
+  using TransportAvailabilityNotificationReceiver = test::TestCallbackReceiver<
+      FidoRequestHandlerBase::TransportAvailabilityInfo>;
+
+  TestTransportAvailabilityObserver() {}
+  ~TestTransportAvailabilityObserver() override {}
+
+  void WaitForAndExpectAvailableTransportsAre(
+      base::flat_set<FidoTransportProtocol> expected_transports) {
+    transport_availability_notification_receiver_.WaitForCallback();
+    auto result =
+        std::get<0>(*transport_availability_notification_receiver_.result());
+    EXPECT_THAT(result.available_transports,
+                ::testing::UnorderedElementsAreArray(expected_transports));
+  }
+
+ protected:
+  // FidoRequestHandlerBase::TransportAvailabilityObserver:
+  void OnTransportAvailabilityEnumerated(
+      FidoRequestHandlerBase::TransportAvailabilityInfo data) override {
+    transport_availability_notification_receiver_.callback().Run(
+        std::move(data));
+  }
+
+  void BluetoothAdapterPowerChanged(bool is_powered_on) override {}
+  void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator) override {
+  }
+  void FidoAuthenticatorRemoved(base::StringPiece device_id) override {}
+
+ private:
+  TransportAvailabilityNotificationReceiver
+      transport_availability_notification_receiver_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestTransportAvailabilityObserver);
+};
+
 // Fake FidoTask implementation that sends an empty byte array to the device
 // when StartTask() is invoked.
 class FakeFidoTask : public FidoTask {
@@ -151,13 +189,15 @@
  public:
   void ForgeNextHidDiscovery() {
     discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
+    ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
   }
 
   std::unique_ptr<FakeFidoRequestHandler> CreateFakeHandler() {
     ForgeNextHidDiscovery();
     return std::make_unique<FakeFidoRequestHandler>(
         base::flat_set<FidoTransportProtocol>(
-            {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
+            {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+             FidoTransportProtocol::kBluetoothLowEnergy}),
         cb_.callback());
   }
 
@@ -166,11 +206,13 @@
       FidoRequestHandlerBase::AddPlatformAuthenticatorCallback
           add_platform_authenticator) {
     return std::make_unique<FakeFidoRequestHandler>(
-        base::flat_set<FidoTransportProtocol>(), cb_.callback(),
-        std::move(add_platform_authenticator));
+        base::flat_set<FidoTransportProtocol>(
+            {FidoTransportProtocol::kInternal}),
+        cb_.callback(), std::move(add_platform_authenticator));
   }
 
   test::FakeFidoDiscovery* discovery() const { return discovery_; }
+  test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
   FakeHandlerCallbackReceiver& callback() { return cb_; }
 
  protected:
@@ -178,6 +220,7 @@
       base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
   test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
   test::FakeFidoDiscovery* discovery_;
+  test::FakeFidoDiscovery* ble_discovery_;
   FakeHandlerCallbackReceiver cb_;
 };
 
@@ -373,13 +416,70 @@
             return std::make_unique<FakeFidoAuthenticator>(device);
           },
           device.get());
+
+  TestTransportAvailabilityObserver observer;
   auto request_handler = CreateFakeHandlerWithPlatformAuthenticatorCallback(
       std::move(make_platform_authenticator));
+  request_handler->set_observer(&observer);
 
-  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  observer.WaitForAndExpectAvailableTransportsAre(
+      {FidoTransportProtocol::kInternal});
+
   callback().WaitForCallback();
   EXPECT_TRUE(request_handler->is_complete());
   EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
 }
 
+TEST_F(FidoRequestHandlerTest,
+       InternalTransportDisallowedIfFactoryYieldsNoAuthenticator) {
+  TestTransportAvailabilityObserver observer;
+  auto request_handler =
+      CreateFakeHandlerWithPlatformAuthenticatorCallback(base::BindOnce(
+          []() -> std::unique_ptr<FidoAuthenticator> { return nullptr; }));
+  request_handler->set_observer(&observer);
+  observer.WaitForAndExpectAvailableTransportsAre({});
+}
+
+TEST_F(FidoRequestHandlerTest,
+       BleTransportAllowedIfDiscoveryStartsSuccessfully) {
+  TestTransportAvailabilityObserver observer;
+  auto request_handler = CreateFakeHandler();
+  request_handler->set_observer(&observer);
+
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+  ble_discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  observer.WaitForAndExpectAvailableTransportsAre(
+      {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+       FidoTransportProtocol::kBluetoothLowEnergy});
+}
+
+TEST_F(FidoRequestHandlerTest, BleTransportDisallowedIfDiscoveryFails) {
+  TestTransportAvailabilityObserver observer;
+  auto request_handler = CreateFakeHandler();
+  request_handler->set_observer(&observer);
+
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+  ble_discovery()->WaitForCallToStart();
+  ble_discovery()->SimulateStarted(false /* success */);
+
+  observer.WaitForAndExpectAvailableTransportsAre(
+      {FidoTransportProtocol::kUsbHumanInterfaceDevice});
+}
+
+TEST_F(FidoRequestHandlerTest,
+       TransportAvailabilityNotificationOnObserverSetLate) {
+  TestTransportAvailabilityObserver observer;
+  auto request_handler = CreateFakeHandler();
+
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+  ble_discovery()->WaitForCallToStartAndSimulateSuccess();
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+
+  request_handler->set_observer(&observer);
+  observer.WaitForAndExpectAvailableTransportsAre(
+      {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+       FidoTransportProtocol::kBluetoothLowEnergy});
+}
+
 }  // namespace device
diff --git a/device/fido/fido_transport_protocol.cc b/device/fido/fido_transport_protocol.cc
index 5035da66..2c98c52 100644
--- a/device/fido/fido_transport_protocol.cc
+++ b/device/fido/fido_transport_protocol.cc
@@ -12,6 +12,14 @@
 const char kCloudAssistedBluetoothLowEnergy[] = "cable";
 const char kInternal[] = "internal";
 
+base::flat_set<FidoTransportProtocol> GetAllTransportProtocols() {
+  return {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+          FidoTransportProtocol::kBluetoothLowEnergy,
+          FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
+          FidoTransportProtocol::kNearFieldCommunication,
+          FidoTransportProtocol::kInternal};
+}
+
 base::Optional<FidoTransportProtocol> ConvertToFidoTransportProtocol(
     base::StringPiece protocol) {
   if (protocol == kUsbHumanInterfaceDevice)
diff --git a/device/fido/fido_transport_protocol.h b/device/fido/fido_transport_protocol.h
index 046564f5..cf4847c 100644
--- a/device/fido/fido_transport_protocol.h
+++ b/device/fido/fido_transport_protocol.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/component_export.h"
+#include "base/containers/flat_set.h"
 #include "base/optional.h"
 #include "base/strings/string_piece.h"
 
@@ -31,6 +32,9 @@
 extern const char kInternal[];
 
 COMPONENT_EXPORT(DEVICE_FIDO)
+base::flat_set<FidoTransportProtocol> GetAllTransportProtocols();
+
+COMPONENT_EXPORT(DEVICE_FIDO)
 base::Optional<FidoTransportProtocol> ConvertToFidoTransportProtocol(
     base::StringPiece protocol);
 
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index 14430c2..e28a158 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -5,11 +5,14 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "device/base/features.h"
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/ctap_get_assertion_request.h"
+#include "device/fido/device_response_converter.h"
 #include "device/fido/fake_fido_discovery.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
@@ -25,17 +28,30 @@
 
 namespace {
 
+constexpr uint8_t kBogusCredentialId[] = {0x01, 0x02, 0x03, 0x04};
+
 using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver<
     FidoReturnCode,
     base::Optional<AuthenticatorGetAssertionResponse>,
     FidoTransportProtocol>;
 
+// Returns the set of transport protocols that are safe to test with. Because
+// FidoCableDiscovery cannot be faked out, and attempting to start the real
+// thing would flakily work/fail depending on the environment, avoid testing.
+base::flat_set<FidoTransportProtocol> GetTestableTransportProtocols() {
+  auto transports = GetAllTransportProtocols();
+  transports.erase(FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy);
+  return transports;
+}
+
 }  // namespace
 
 class FidoGetAssertionHandlerTest : public ::testing::Test {
  public:
-  void ForgeNextHidDiscovery() {
+  void ForgeDiscoveries() {
     discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
+    ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
+    nfc_discovery_ = scoped_fake_discovery_factory_.ForgeNextNfcDiscovery();
   }
 
   std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() {
@@ -58,13 +74,55 @@
 
   std::unique_ptr<GetAssertionRequestHandler>
   CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
-    ForgeNextHidDiscovery();
+    ForgeDiscoveries();
 
     return std::make_unique<GetAssertionRequestHandler>(
-        nullptr /* connector */,
-        base::flat_set<FidoTransportProtocol>(
-            {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
-        std::move(request), get_assertion_cb_.callback());
+        nullptr /* connector */, supported_transports_, std::move(request),
+        get_assertion_cb_.callback(),
+        base::BindOnce(
+            &FidoGetAssertionHandlerTest::CreatePlatformAuthenticator,
+            base::Unretained(this)));
+  }
+
+  void ExpectAllowedTransportsForRequestAre(
+      GetAssertionRequestHandler* request_handler,
+      base::flat_set<FidoTransportProtocol> transports) {
+    using Transport = FidoTransportProtocol;
+    if (base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
+      discovery()->WaitForCallToStartAndSimulateSuccess();
+    if (base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
+      ble_discovery()->WaitForCallToStartAndSimulateSuccess();
+    if (base::ContainsKey(transports, Transport::kNearFieldCommunication))
+      nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
+    if (base::ContainsKey(transports, Transport::kInternal))
+      platform_authenticator_factory().WaitForCallback();
+
+    scoped_task_environment_.FastForwardUntilNoTasksRemain();
+    EXPECT_FALSE(get_assertion_callback().was_called());
+
+    if (!base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
+      EXPECT_FALSE(discovery()->is_start_requested());
+    if (!base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
+      EXPECT_FALSE(ble_discovery()->is_start_requested());
+    if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
+      EXPECT_FALSE(nfc_discovery()->is_start_requested());
+    if (!base::ContainsKey(transports, Transport::kInternal))
+      EXPECT_FALSE(platform_authenticator_factory().was_called());
+
+    // Even with FidoTransportProtocol::kInternal allowed, unless the platform
+    // authenticator factory returns a FidoAuthenticator instance (which it will
+    // not be default), the transport will be marked `unavailable`.
+    transports.erase(Transport::kInternal);
+
+    EXPECT_THAT(
+        request_handler->transport_availability_info().available_transports,
+        ::testing::UnorderedElementsAreArray(transports));
+  }
+
+  void ExpectAllTransportsAreAllowedForRequest(
+      GetAssertionRequestHandler* request_handler) {
+    ExpectAllowedTransportsForRequestAre(request_handler,
+                                         GetTestableTransportProtocols());
   }
 
   void InitFeatureListAndDisableCtapFlag() {
@@ -72,17 +130,46 @@
   }
 
   test::FakeFidoDiscovery* discovery() const { return discovery_; }
+  test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
+  test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
   TestGetAssertionRequestCallback& get_assertion_callback() {
     return get_assertion_cb_;
   }
 
+  void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
+    mock_platform_device_ = std::move(device);
+  }
+
+  test::TestCallbackReceiver<>& platform_authenticator_factory() {
+    return create_platform_authenticator_receiver_;
+  }
+
+  void set_supported_transports(
+      base::flat_set<FidoTransportProtocol> transports) {
+    supported_transports_ = std::move(transports);
+  }
+
  protected:
+  std::unique_ptr<FidoAuthenticator> CreatePlatformAuthenticator() {
+    create_platform_authenticator_receiver_.callback().Run();
+    if (!mock_platform_device_)
+      return nullptr;
+    return std::make_unique<FidoDeviceAuthenticator>(
+        mock_platform_device_.get());
+  }
+
   base::test::ScopedTaskEnvironment scoped_task_environment_{
       base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
   base::test::ScopedFeatureList scoped_feature_list_;
   test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
   test::FakeFidoDiscovery* discovery_;
+  test::FakeFidoDiscovery* ble_discovery_;
+  test::FakeFidoDiscovery* nfc_discovery_;
+  std::unique_ptr<MockFidoDevice> mock_platform_device_;
+  test::TestCallbackReceiver<> create_platform_authenticator_receiver_;
   TestGetAssertionRequestCallback get_assertion_cb_;
+  base::flat_set<FidoTransportProtocol> supported_transports_ =
+      GetTestableTransportProtocols();
 };
 
 TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
@@ -239,18 +326,247 @@
   EXPECT_FALSE(get_assertion_callback().was_called());
 }
 
+TEST_F(FidoGetAssertionHandlerTest,
+       AllTransportsAllowedIfAllowCredentialsListUndefined) {
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
+          test_data::kRelyingPartyId, test_data::kClientDataHash));
+  ExpectAllTransportsAreAllowedForRequest(request_handler.get());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+       AllTransportsAllowedIfAllowCredentialsListIsEmpty) {
+  CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                  test_data::kClientDataHash);
+  request.SetAllowList({});
+
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+  ExpectAllTransportsAreAllowedForRequest(request_handler.get());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+       AllTransportsAllowedIfHasAllowedCredentialWithEmptyTransportsList) {
+  CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                  test_data::kClientDataHash);
+  request.SetAllowList({
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(
+           test_data::kTestGetAssertionCredentialId),
+       {FidoTransportProtocol::kBluetoothLowEnergy}},
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(kBogusCredentialId)},
+  });
+
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+  ExpectAllTransportsAreAllowedForRequest(request_handler.get());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+       AllowedTransportsAreUnionOfTransportsLists) {
+  CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                  test_data::kClientDataHash);
+  request.SetAllowList({
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(
+           test_data::kTestGetAssertionCredentialId),
+       {FidoTransportProtocol::kBluetoothLowEnergy}},
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(kBogusCredentialId),
+       {FidoTransportProtocol::kInternal,
+        FidoTransportProtocol::kNearFieldCommunication}},
+  });
+
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+  ExpectAllowedTransportsForRequestAre(
+      request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
+                              FidoTransportProtocol::kInternal,
+                              FidoTransportProtocol::kNearFieldCommunication});
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyBleAndNfc) {
+  const base::flat_set<FidoTransportProtocol> kBleAndNfc = {
+      FidoTransportProtocol::kBluetoothLowEnergy,
+      FidoTransportProtocol::kNearFieldCommunication,
+  };
+
+  set_supported_transports(kBleAndNfc);
+
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
+          test_data::kRelyingPartyId, test_data::kClientDataHash));
+  ExpectAllowedTransportsForRequestAre(request_handler.get(), kBleAndNfc);
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyUsbAndInternal) {
+  const base::flat_set<FidoTransportProtocol> kUsbAndInternal = {
+      FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
+      FidoTransportProtocol::kInternal,
+  };
+
+  set_supported_transports(kUsbAndInternal);
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
+          test_data::kRelyingPartyId, test_data::kClientDataHash));
+  ExpectAllowedTransportsForRequestAre(request_handler.get(), kUsbAndInternal);
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyUsbTransportAllowed) {
+  CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                  test_data::kClientDataHash);
+  request.SetAllowList({
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(
+           test_data::kTestGetAssertionCredentialId),
+       {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
+  });
+
+  set_supported_transports({FidoTransportProtocol::kUsbHumanInterfaceDevice});
+
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+
+  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetAssertion,
+      test_data::kTestGetAssertionResponse);
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+  discovery()->AddDevice(std::move(device));
+
+  get_assertion_callback().WaitForCallback();
+
+  EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
+  EXPECT_TRUE(get_assertion_callback().value<0>());
+  EXPECT_TRUE(request_handler->is_complete());
+  EXPECT_THAT(
+      request_handler->transport_availability_info().available_transports,
+      ::testing::UnorderedElementsAre(
+          FidoTransportProtocol::kUsbHumanInterfaceDevice));
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyBleTransportAllowed) {
+  CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                  test_data::kClientDataHash);
+  request.SetAllowList({
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(
+           test_data::kTestGetAssertionCredentialId),
+       {FidoTransportProtocol::kBluetoothLowEnergy}},
+  });
+
+  set_supported_transports({FidoTransportProtocol::kBluetoothLowEnergy});
+
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+
+  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device->SetDeviceTransport(FidoTransportProtocol::kBluetoothLowEnergy);
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetAssertion,
+      test_data::kTestGetAssertionResponse);
+  ble_discovery()->WaitForCallToStartAndSimulateSuccess();
+  ble_discovery()->AddDevice(std::move(device));
+
+  get_assertion_callback().WaitForCallback();
+
+  EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
+  EXPECT_TRUE(get_assertion_callback().value<0>());
+  EXPECT_TRUE(request_handler->is_complete());
+  EXPECT_THAT(
+      request_handler->transport_availability_info().available_transports,
+      ::testing::UnorderedElementsAre(
+          FidoTransportProtocol::kBluetoothLowEnergy));
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyNfcTransportAllowed) {
+  CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                  test_data::kClientDataHash);
+  request.SetAllowList({
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(
+           test_data::kTestGetAssertionCredentialId),
+       {FidoTransportProtocol::kNearFieldCommunication}},
+  });
+
+  set_supported_transports({FidoTransportProtocol::kNearFieldCommunication});
+
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+
+  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device->SetDeviceTransport(FidoTransportProtocol::kNearFieldCommunication);
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetAssertion,
+      test_data::kTestGetAssertionResponse);
+  nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
+  nfc_discovery()->AddDevice(std::move(device));
+
+  get_assertion_callback().WaitForCallback();
+
+  EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
+  EXPECT_TRUE(get_assertion_callback().value<0>());
+  EXPECT_TRUE(request_handler->is_complete());
+  EXPECT_THAT(
+      request_handler->transport_availability_info().available_transports,
+      ::testing::UnorderedElementsAre(
+          FidoTransportProtocol::kNearFieldCommunication));
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyInternalTransportAllowed) {
+  CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                  test_data::kClientDataHash);
+  request.SetAllowList({
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(
+           test_data::kTestGetAssertionCredentialId),
+       {FidoTransportProtocol::kInternal}},
+  });
+
+  set_supported_transports({FidoTransportProtocol::kInternal});
+
+  auto device = MockFidoDevice::MakeCtap(
+      ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetAssertion,
+      test_data::kTestGetAssertionResponse);
+  set_mock_platform_device(std::move(device));
+
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+  get_assertion_callback().WaitForCallback();
+
+  EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
+  EXPECT_TRUE(get_assertion_callback().value<0>());
+  EXPECT_TRUE(request_handler->is_complete());
+  EXPECT_THAT(
+      request_handler->transport_availability_info().available_transports,
+      ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
+}
+
 // Tests a scenario where authenticator of incorrect transport type was used to
 // conduct CTAP GetAssertion call.
+//
+// TODO(engedy): This should not happen, instead |allowCredentials| should be
+// filtered to only contain items compatible with the transport actually used to
+// talk to the authenticator.
 TEST_F(FidoGetAssertionHandlerTest, IncorrectTransportType) {
   // GetAssertion request that expects GetAssertion call for credential
   // |CredentialType::kPublicKey| to be signed with Cable authenticator.
   CtapGetAssertionRequest request(test_data::kRelyingPartyId,
                                   test_data::kClientDataHash);
-  request.SetAllowList(
-      {{CredentialType::kPublicKey,
-        fido_parsing_utils::Materialize(
-            test_data::kTestGetAssertionCredentialId),
-        {FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy}}});
+  request.SetAllowList({
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(
+           test_data::kTestGetAssertionCredentialId),
+       {FidoTransportProtocol::kBluetoothLowEnergy}},
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(kBogusCredentialId),
+       {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
+  });
   auto request_handler =
       CreateGetAssertionHandlerWithRequest(std::move(request));
   discovery()->WaitForCallToStartAndSimulateSuccess();
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index 71ead12..b1f6a49 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/stl_util.h"
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/cable/fido_cable_discovery.h"
 #include "device/fido/fido_authenticator.h"
@@ -116,53 +117,67 @@
   return false;
 }
 
-std::set<FidoTransportProtocol> GetAllowedTransports(
+base::flat_set<FidoTransportProtocol> GetTransportsAllowedByRP(
     const CtapGetAssertionRequest& request) {
+  const base::flat_set<FidoTransportProtocol> kAllTransports = {
+      FidoTransportProtocol::kInternal,
+      FidoTransportProtocol::kNearFieldCommunication,
+      FidoTransportProtocol::kUsbHumanInterfaceDevice,
+      FidoTransportProtocol::kBluetoothLowEnergy,
+      FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy};
+
+  // TODO(https://crbug.com/874479): |allowed_list| will |has_value| even if the
+  // WebAuthn request has `allowCredential` undefined.
   const auto& allowed_list = request.allow_list();
-  if (!allowed_list) {
-    return {FidoTransportProtocol::kInternal,
-            FidoTransportProtocol::kNearFieldCommunication,
-            FidoTransportProtocol::kUsbHumanInterfaceDevice,
-            FidoTransportProtocol::kBluetoothLowEnergy,
-            FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy};
+  if (!allowed_list || allowed_list->empty()) {
+    return kAllTransports;
   }
 
-  std::set<FidoTransportProtocol> protocols;
+  base::flat_set<FidoTransportProtocol> transports;
   for (const auto credential : *allowed_list) {
-    protocols.insert(credential.transports().begin(),
-                     credential.transports().end());
+    if (credential.transports().empty())
+      return kAllTransports;
+    transports.insert(credential.transports().begin(),
+                      credential.transports().end());
   }
 
-  return protocols;
+  return transports;
 }
 
 }  // namespace
 
 GetAssertionRequestHandler::GetAssertionRequestHandler(
     service_manager::Connector* connector,
-    const base::flat_set<FidoTransportProtocol>& protocols,
+    const base::flat_set<FidoTransportProtocol>& supported_transports,
     CtapGetAssertionRequest request,
     SignResponseCallback completion_callback)
     : GetAssertionRequestHandler(connector,
-                                 protocols,
+                                 supported_transports,
                                  std::move(request),
                                  std::move(completion_callback),
                                  AddPlatformAuthenticatorCallback()) {}
 
 GetAssertionRequestHandler::GetAssertionRequestHandler(
     service_manager::Connector* connector,
-    const base::flat_set<FidoTransportProtocol>& protocols,
+    const base::flat_set<FidoTransportProtocol>& supported_transports,
     CtapGetAssertionRequest request,
     SignResponseCallback completion_callback,
     AddPlatformAuthenticatorCallback add_platform_authenticator)
-    : FidoRequestHandler(connector,
-                         protocols,
-                         std::move(completion_callback),
-                         std::move(add_platform_authenticator)),
+    : FidoRequestHandler(
+          connector,
+          base::STLSetIntersection<base::flat_set<FidoTransportProtocol>>(
+              supported_transports,
+              GetTransportsAllowedByRP(request)),
+          std::move(completion_callback),
+          std::move(add_platform_authenticator)),
       request_(std::move(request)),
       weak_factory_(this) {
+  transport_availability_info().request_type =
+      FidoRequestHandlerBase::RequestType::kGetAssertion;
+
   if (base::ContainsKey(
-          protocols, FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy) &&
+          transport_availability_info().available_transports,
+          FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy) &&
       request_.cable_extension()) {
     auto discovery =
         std::make_unique<FidoCableDiscovery>(*request_.cable_extension());
@@ -170,10 +185,6 @@
     discoveries().push_back(std::move(discovery));
   }
 
-  transport_availability_info().request_type =
-      FidoRequestHandlerBase::RequestType::kGetAssertion;
-  transport_availability_info().available_transports =
-      GetAllowedTransports(request_);
   Start();
 }
 
diff --git a/device/fido/get_assertion_request_handler.h b/device/fido/get_assertion_request_handler.h
index 735b3800..1cf7255 100644
--- a/device/fido/get_assertion_request_handler.h
+++ b/device/fido/get_assertion_request_handler.h
@@ -35,12 +35,12 @@
  public:
   GetAssertionRequestHandler(
       service_manager::Connector* connector,
-      const base::flat_set<FidoTransportProtocol>& protocols,
+      const base::flat_set<FidoTransportProtocol>& supported_transports,
       CtapGetAssertionRequest request_parameter,
       SignResponseCallback completion_callback);
   GetAssertionRequestHandler(
       service_manager::Connector* connector,
-      const base::flat_set<FidoTransportProtocol>& protocols,
+      const base::flat_set<FidoTransportProtocol>& supported_transports,
       CtapGetAssertionRequest request_parameter,
       SignResponseCallback completion_callback,
       AddPlatformAuthenticatorCallback add_platform_authenticator);
diff --git a/device/fido/make_credential_handler_unittest.cc b/device/fido/make_credential_handler_unittest.cc
index 243975d..fb8ab96a 100644
--- a/device/fido/make_credential_handler_unittest.cc
+++ b/device/fido/make_credential_handler_unittest.cc
@@ -5,15 +5,19 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "device/base/features.h"
 #include "device/fido/authenticator_make_credential_response.h"
 #include "device/fido/authenticator_selection_criteria.h"
 #include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/device_response_converter.h"
 #include "device/fido/fake_fido_discovery.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_device.h"
+#include "device/fido/fido_device_authenticator.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_test_data.h"
 #include "device/fido/fido_transport_protocol.h"
@@ -35,12 +39,23 @@
     base::Optional<AuthenticatorMakeCredentialResponse>,
     FidoTransportProtocol>;
 
+// Returns the set of transport protocols that are safe to test with. Because
+// FidoCableDiscovery cannot be faked out, and attempting to start the real
+// thing would flakily work/fail depending on the environment, avoid testing.
+base::flat_set<FidoTransportProtocol> GetTestableTransportProtocols() {
+  auto transports = GetAllTransportProtocols();
+  transports.erase(FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy);
+  return transports;
+}
+
 }  // namespace
 
 class FidoMakeCredentialHandlerTest : public ::testing::Test {
  public:
-  void ForgeNextHidDiscovery() {
+  void ForgeDiscoveries() {
     discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
+    ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
+    nfc_discovery_ = scoped_fake_discovery_factory_.ForgeNextNfcDiscovery();
   }
 
   std::unique_ptr<MakeCredentialRequestHandler> CreateMakeCredentialHandler() {
@@ -51,7 +66,7 @@
   std::unique_ptr<MakeCredentialRequestHandler>
   CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
       AuthenticatorSelectionCriteria authenticator_selection_criteria) {
-    ForgeNextHidDiscovery();
+    ForgeDiscoveries();
     PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
     PublicKeyCredentialUserEntity user(
         fido_parsing_utils::Materialize(test_data::kUserId));
@@ -63,11 +78,52 @@
         std::move(credential_params));
 
     return std::make_unique<MakeCredentialRequestHandler>(
-        nullptr,
-        base::flat_set<FidoTransportProtocol>(
-            {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
-        std::move(request_parameter),
-        std::move(authenticator_selection_criteria), cb_.callback());
+        nullptr, supported_transports_, std::move(request_parameter),
+        std::move(authenticator_selection_criteria), cb_.callback(),
+        base::BindOnce(
+            &FidoMakeCredentialHandlerTest::CreatePlatformAuthenticator,
+            base::Unretained(this)));
+  }
+
+  void ExpectAllowedTransportsForRequestAre(
+      MakeCredentialRequestHandler* request_handler,
+      base::flat_set<FidoTransportProtocol> transports) {
+    using Transport = FidoTransportProtocol;
+    if (base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
+      discovery()->WaitForCallToStartAndSimulateSuccess();
+    if (base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
+      ble_discovery()->WaitForCallToStartAndSimulateSuccess();
+    if (base::ContainsKey(transports, Transport::kNearFieldCommunication))
+      nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
+    if (base::ContainsKey(transports, Transport::kInternal))
+      platform_authenticator_factory().WaitForCallback();
+
+    scoped_task_environment_.FastForwardUntilNoTasksRemain();
+    EXPECT_FALSE(callback().was_called());
+
+    if (!base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
+      EXPECT_FALSE(discovery()->is_start_requested());
+    if (!base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
+      EXPECT_FALSE(ble_discovery()->is_start_requested());
+    if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
+      EXPECT_FALSE(nfc_discovery()->is_start_requested());
+    if (!base::ContainsKey(transports, Transport::kInternal))
+      EXPECT_FALSE(platform_authenticator_factory().was_called());
+
+    // Even with FidoTransportProtocol::kInternal allowed, unless the platform
+    // authenticator factory returns a FidoAuthenticator instance (which it will
+    // not be default), the transport will be marked `unavailable`.
+    transports.erase(Transport::kInternal);
+
+    EXPECT_THAT(
+        request_handler->transport_availability_info().available_transports,
+        ::testing::UnorderedElementsAreArray(transports));
+  }
+
+  void ExpectAllTransportsAreAllowedForRequest(
+      MakeCredentialRequestHandler* request_handler) {
+    ExpectAllowedTransportsForRequestAre(request_handler,
+                                         GetTestableTransportProtocols());
   }
 
   void InitFeatureListAndDisableCtapFlag() {
@@ -75,15 +131,44 @@
   }
 
   test::FakeFidoDiscovery* discovery() const { return discovery_; }
+  test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
+  test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
   TestMakeCredentialRequestCallback& callback() { return cb_; }
 
+  void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
+    mock_platform_device_ = std::move(device);
+  }
+
+  test::TestCallbackReceiver<>& platform_authenticator_factory() {
+    return create_platform_authenticator_receiver_;
+  }
+
+  void set_supported_transports(
+      base::flat_set<FidoTransportProtocol> transports) {
+    supported_transports_ = std::move(transports);
+  }
+
  protected:
+  std::unique_ptr<FidoAuthenticator> CreatePlatformAuthenticator() {
+    create_platform_authenticator_receiver_.callback().Run();
+    if (!mock_platform_device_)
+      return nullptr;
+    return std::make_unique<FidoDeviceAuthenticator>(
+        mock_platform_device_.get());
+  }
+
   base::test::ScopedFeatureList scoped_feature_list_;
   base::test::ScopedTaskEnvironment scoped_task_environment_{
       base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
   test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
   test::FakeFidoDiscovery* discovery_;
+  test::FakeFidoDiscovery* ble_discovery_;
+  test::FakeFidoDiscovery* nfc_discovery_;
+  std::unique_ptr<MockFidoDevice> mock_platform_device_;
+  test::TestCallbackReceiver<> create_platform_authenticator_receiver_;
   TestMakeCredentialRequestCallback cb_;
+  base::flat_set<FidoTransportProtocol> supported_transports_ =
+      GetTestableTransportProtocols();
 };
 
 TEST_F(FidoMakeCredentialHandlerTest, TestCtap2MakeCredentialWithFlagEnabled) {
@@ -152,24 +237,6 @@
   EXPECT_FALSE(callback().was_called());
 }
 
-TEST_F(FidoMakeCredentialHandlerTest,
-       U2fRegisterWithPlatformDeviceRequirement) {
-  auto request_handler =
-      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria(
-              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
-                  kPlatform,
-              false /* require_resident_key */,
-              UserVerificationRequirement::kPreferred));
-  discovery()->WaitForCallToStartAndSimulateSuccess();
-
-  auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
-  discovery()->AddDevice(std::move(device));
-
-  scoped_task_environment_.FastForwardUntilNoTasksRemain();
-  EXPECT_FALSE(callback().was_called());
-}
-
 TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithResidentKeyRequirement) {
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
@@ -186,8 +253,7 @@
   EXPECT_FALSE(callback().was_called());
 }
 
-TEST_F(FidoMakeCredentialHandlerTest,
-       UserVerificationAuthenticatorSelectionCriteria) {
+TEST_F(FidoMakeCredentialHandlerTest, UserVerificationRequirementNotMet) {
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
           AuthenticatorSelectionCriteria(
@@ -206,49 +272,36 @@
 
 // TODO(crbug.com/873710): Platform authenticators are temporarily disabled if
 // AuthenticatorAttachment is unset (kAny).
-TEST_F(FidoMakeCredentialHandlerTest,
-       PlatformDeviceAuthenticatorSelectionCriteriaAnyAttachmentPlatform) {
+TEST_F(FidoMakeCredentialHandlerTest, AnyAttachment) {
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
           AuthenticatorSelectionCriteria(
               AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
               false /* require_resident_key */,
               UserVerificationRequirement::kPreferred));
-  discovery()->WaitForCallToStartAndSimulateSuccess();
 
-  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
-      test_data::kTestGetInfoResponsePlatformDevice);
-  discovery()->AddDevice(std::move(device));
-
-  scoped_task_environment_.FastForwardUntilNoTasksRemain();
-  EXPECT_FALSE(callback().was_called());
+  ExpectAllowedTransportsForRequestAre(
+      request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
+                              FidoTransportProtocol::kNearFieldCommunication,
+                              FidoTransportProtocol::kUsbHumanInterfaceDevice});
 }
 
-// A cross-platform authenticator can respond to a request with
-// AuthenticatorAttachment::kAny.
-TEST_F(FidoMakeCredentialHandlerTest,
-       PlatformDeviceAuthenticatorSelectionCriteriaAnyAttachmentCrossPlatform) {
+TEST_F(FidoMakeCredentialHandlerTest, CrossPlatformAttachment) {
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
           AuthenticatorSelectionCriteria(
-              AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kCrossPlatform,
               false /* require_resident_key */,
               UserVerificationRequirement::kPreferred));
-  discovery()->WaitForCallToStartAndSimulateSuccess();
 
-  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
-      test_data::kTestAuthenticatorGetInfoResponse);
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorMakeCredential,
-      test_data::kTestMakeCredentialResponse);
-  discovery()->AddDevice(std::move(device));
-
-  scoped_task_environment_.FastForwardUntilNoTasksRemain();
-  EXPECT_TRUE(callback().was_called());
+  ExpectAllowedTransportsForRequestAre(
+      request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
+                              FidoTransportProtocol::kNearFieldCommunication,
+                              FidoTransportProtocol::kUsbHumanInterfaceDevice});
 }
 
-TEST_F(FidoMakeCredentialHandlerTest,
-       PlatformDeviceAuthenticatorSelectionCriteria) {
+TEST_F(FidoMakeCredentialHandlerTest, PlatformAttachment) {
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
           AuthenticatorSelectionCriteria(
@@ -256,18 +309,12 @@
                   kPlatform,
               false /* require_resident_key */,
               UserVerificationRequirement::kRequired));
-  discovery()->WaitForCallToStartAndSimulateSuccess();
 
-  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
-      test_data::kTestAuthenticatorGetInfoResponse);
-  discovery()->AddDevice(std::move(device));
-
-  scoped_task_environment_.FastForwardUntilNoTasksRemain();
-  EXPECT_FALSE(callback().was_called());
+  ExpectAllowedTransportsForRequestAre(request_handler.get(),
+                                       {FidoTransportProtocol::kInternal});
 }
 
-TEST_F(FidoMakeCredentialHandlerTest,
-       ResidentKeyAuthenticatorSelectionCriteria) {
+TEST_F(FidoMakeCredentialHandlerTest, ResidentKeyRequirementNotMet) {
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
           AuthenticatorSelectionCriteria(
@@ -285,7 +332,44 @@
 }
 
 TEST_F(FidoMakeCredentialHandlerTest,
-       SatisfyAllAuthenticatorSelectionCriteria) {
+       AuthenticatorSelectionCriteriaSatisfiedByCrossPlatformDevice) {
+  set_supported_transports({FidoTransportProtocol::kUsbHumanInterfaceDevice});
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kCrossPlatform,
+              true /* require_resident_key */,
+              UserVerificationRequirement::kRequired));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorMakeCredential,
+      test_data::kTestMakeCredentialResponse);
+  discovery()->AddDevice(std::move(device));
+
+  callback().WaitForCallback();
+  EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+
+  EXPECT_THAT(
+      request_handler->transport_availability_info().available_transports,
+      ::testing::UnorderedElementsAre(
+          FidoTransportProtocol::kUsbHumanInterfaceDevice));
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+       AuthenticatorSelectionCriteriaSatisfiedByPlatformDevice) {
+  set_supported_transports({FidoTransportProtocol::kInternal});
+  auto platform_device = MockFidoDevice::MakeCtap(
+      ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
+  EXPECT_CALL(*platform_device, GetId())
+      .WillRepeatedly(testing::Return("device0"));
+  platform_device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorMakeCredential,
+      test_data::kTestMakeCredentialResponse);
+  set_mock_platform_device(std::move(platform_device));
+
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
           AuthenticatorSelectionCriteria(
@@ -293,37 +377,77 @@
                   kPlatform,
               true /* require_resident_key */,
               UserVerificationRequirement::kRequired));
+
+  callback().WaitForCallback();
+  EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+
+  EXPECT_THAT(
+      request_handler->transport_availability_info().available_transports,
+      ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
+}
+
+// A cross-platform authenticator claiming to be a platform authenticator as per
+// its GetInfo response is rejected.
+TEST_F(FidoMakeCredentialHandlerTest,
+       CrossPlatformAuthenticatorPretendingToBePlatform) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kCrossPlatform,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kPreferred));
   discovery()->WaitForCallToStartAndSimulateSuccess();
 
   auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
       test_data::kTestGetInfoResponsePlatformDevice);
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorMakeCredential,
-      test_data::kTestMakeCredentialResponse);
-  discovery()->AddDevice(std::move(device));
-
-  scoped_task_environment_.FastForwardUntilNoTasksRemain();
-  callback().WaitForCallback();
-  EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
-}
-
-TEST_F(FidoMakeCredentialHandlerTest, IncompatibleUserVerificationSetting) {
-  auto request_handler =
-      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria(
-              AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
-              false /* require_resident_key */,
-              UserVerificationRequirement::kRequired));
-  discovery()->WaitForCallToStartAndSimulateSuccess();
-
-  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
-      test_data::kTestGetInfoResponseWithoutUvSupport);
   discovery()->AddDevice(std::move(device));
 
   scoped_task_environment_.FastForwardUntilNoTasksRemain();
   EXPECT_FALSE(callback().was_called());
 }
 
+// A platform authenticator claiming to be a cross-platform authenticator as per
+// its GetInfo response is rejected.
+TEST_F(FidoMakeCredentialHandlerTest,
+       PlatformAuthenticatorPretendingToBeCrossPlatform) {
+  auto platform_device = MockFidoDevice::MakeCtap(
+      ReadCTAPGetInfoResponse(test_data::kTestAuthenticatorGetInfoResponse));
+  EXPECT_CALL(*platform_device, GetId())
+      .WillRepeatedly(testing::Return("device0"));
+  set_mock_platform_device(std::move(platform_device));
+
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kPlatform,
+              true /* require_resident_key */,
+              UserVerificationRequirement::kRequired));
+
+  platform_authenticator_factory().WaitForCallback();
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, SupportedTransportsAreOnlyBleAndNfc) {
+  const base::flat_set<FidoTransportProtocol> kBleAndNfc = {
+      FidoTransportProtocol::kBluetoothLowEnergy,
+      FidoTransportProtocol::kNearFieldCommunication,
+  };
+
+  set_supported_transports(kBleAndNfc);
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kCrossPlatform,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kPreferred));
+
+  ExpectAllowedTransportsForRequestAre(request_handler.get(), kBleAndNfc);
+}
+
 TEST_F(FidoMakeCredentialHandlerTest, IncorrectRpIdHash) {
   auto request_handler =
       CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index d971a68..0212e60 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/stl_util.h"
 #include "device/fido/authenticator_make_credential_response.h"
 #include "device/fido/fido_authenticator.h"
 #include "device/fido/fido_parsing_utils.h"
@@ -62,7 +63,7 @@
              UvAvailability::kSupportedAndConfigured;
 }
 
-std::set<FidoTransportProtocol> GetValidTransportProtocols(
+base::flat_set<FidoTransportProtocol> GetTransportsAllowedByRP(
     const AuthenticatorSelectionCriteria& authenticator_selection_criteria) {
   using AttachmentType =
       AuthenticatorSelectionCriteria::AuthenticatorAttachment;
@@ -77,27 +78,28 @@
               FidoTransportProtocol::kNearFieldCommunication,
               FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy};
     case AttachmentType::kAny:
-      return {FidoTransportProtocol::kInternal,
-              FidoTransportProtocol::kNearFieldCommunication,
+      // TODO(crbug.com/873710): Re-enable platform authenticators for kAny,
+      // once Touch ID is integrated into the UI.
+      return {FidoTransportProtocol::kNearFieldCommunication,
               FidoTransportProtocol::kUsbHumanInterfaceDevice,
               FidoTransportProtocol::kBluetoothLowEnergy,
               FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy};
   }
 
   NOTREACHED();
-  return std::set<FidoTransportProtocol>();
+  return base::flat_set<FidoTransportProtocol>();
 }
 
 }  // namespace
 
 MakeCredentialRequestHandler::MakeCredentialRequestHandler(
     service_manager::Connector* connector,
-    const base::flat_set<FidoTransportProtocol>& protocols,
+    const base::flat_set<FidoTransportProtocol>& supported_transports,
     CtapMakeCredentialRequest request,
     AuthenticatorSelectionCriteria authenticator_selection_criteria,
     RegisterResponseCallback completion_callback)
     : MakeCredentialRequestHandler(connector,
-                                   protocols,
+                                   supported_transports,
                                    std::move(request),
                                    authenticator_selection_criteria,
                                    std::move(completion_callback),
@@ -105,23 +107,24 @@
 
 MakeCredentialRequestHandler::MakeCredentialRequestHandler(
     service_manager::Connector* connector,
-    const base::flat_set<FidoTransportProtocol>& protocols,
+    const base::flat_set<FidoTransportProtocol>& supported_transports,
     CtapMakeCredentialRequest request,
     AuthenticatorSelectionCriteria authenticator_selection_criteria,
     RegisterResponseCallback completion_callback,
     AddPlatformAuthenticatorCallback add_platform_authenticator)
-    : FidoRequestHandler(connector,
-                         protocols,
-                         std::move(completion_callback),
-                         std::move(add_platform_authenticator)),
+    : FidoRequestHandler(
+          connector,
+          base::STLSetIntersection<base::flat_set<FidoTransportProtocol>>(
+              supported_transports,
+              GetTransportsAllowedByRP(authenticator_selection_criteria)),
+          std::move(completion_callback),
+          std::move(add_platform_authenticator)),
       request_parameter_(std::move(request)),
       authenticator_selection_criteria_(
           std::move(authenticator_selection_criteria)),
       weak_factory_(this) {
   transport_availability_info().request_type =
       FidoRequestHandlerBase::RequestType::kMakeCredential;
-  transport_availability_info().available_transports =
-      GetValidTransportProtocols(authenticator_selection_criteria);
   Start();
 }
 
diff --git a/device/fido/make_credential_request_handler.h b/device/fido/make_credential_request_handler.h
index 5ddba77..1151dfa 100644
--- a/device/fido/make_credential_request_handler.h
+++ b/device/fido/make_credential_request_handler.h
@@ -37,13 +37,13 @@
  public:
   MakeCredentialRequestHandler(
       service_manager::Connector* connector,
-      const base::flat_set<FidoTransportProtocol>& protocols,
+      const base::flat_set<FidoTransportProtocol>& supported_transports,
       CtapMakeCredentialRequest request_parameter,
       AuthenticatorSelectionCriteria authenticator_criteria,
       RegisterResponseCallback completion_callback);
   MakeCredentialRequestHandler(
       service_manager::Connector* connector,
-      const base::flat_set<FidoTransportProtocol>& protocols,
+      const base::flat_set<FidoTransportProtocol>& supported_transports,
       CtapMakeCredentialRequest request_parameter,
       AuthenticatorSelectionCriteria authenticator_criteria,
       RegisterResponseCallback completion_callback,
diff --git a/device/fido/mock_fido_device.h b/device/fido/mock_fido_device.h
index 840e331..5aae859 100644
--- a/device/fido/mock_fido_device.h
+++ b/device/fido/mock_fido_device.h
@@ -23,7 +23,7 @@
 
 namespace device {
 
-class MockFidoDevice : public FidoDevice {
+class MockFidoDevice : public ::testing::StrictMock<FidoDevice> {
  public:
   // MakeU2f returns a fully initialized U2F device. This represents the state
   // after |DiscoverSupportedProtocolAndDeviceInfo| has been called by the
diff --git a/device/usb/public/mojom/BUILD.gn b/device/usb/public/mojom/BUILD.gn
index c643b53..703ff1e 100644
--- a/device/usb/public/mojom/BUILD.gn
+++ b/device/usb/public/mojom/BUILD.gn
@@ -6,7 +6,6 @@
 
 mojom("mojom") {
   sources = [
-    "chooser_service.mojom",
     "device.mojom",
     "device_manager.mojom",
   ]
diff --git a/device/usb/public/mojom/chooser_service.mojom b/device/usb/public/mojom/chooser_service.mojom
deleted file mode 100644
index 0b08dae..0000000
--- a/device/usb/public/mojom/chooser_service.mojom
+++ /dev/null
@@ -1,19 +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.
-
-module device.mojom;
-
-import "device/usb/public/mojom/device.mojom";
-import "device/usb/public/mojom/device_manager.mojom";
-
-interface UsbChooserService {
-  // Get permission from user to use the device.
-  //
-  // |device_filters| filters the available devices and the filtered
-  // devices will be listed for user to grant permission.
-  //
-  // |result| is the device that user grants permission to use.
-  GetPermission(array<device.mojom.UsbDeviceFilter> device_filters)
-      => (device.mojom.UsbDeviceInfo? result);
-};
diff --git a/docs/callback.md b/docs/callback.md
index 8bc03606..475954c8 100644
--- a/docs/callback.md
+++ b/docs/callback.md
@@ -514,7 +514,7 @@
  - Determining the number of parameters that are bound
  - Creating the BindState storing the bound parameters
  - Performing compile-time asserts to avoid error-prone behavior
- - Returning an `Callback<>` with an arity matching the number of unbound
+ - Returning a `Callback<>` with an arity matching the number of unbound
    parameters and that knows the correct refcounting semantics for the
    target object if we are binding a method.
 
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index fa223e2..8b389db 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -351,13 +351,9 @@
     content::RenderFrameHost* render_frame_host,
     const GURL& security_origin,
     content::MediaStreamType type) {
-  DCHECK(AppWindow::web_contents() ==
-             content::WebContents::FromRenderFrameHost(render_frame_host) ||
-         (content::WebContents::FromRenderFrameHost(render_frame_host)
-              ->GetOuterWebContents() &&
-          AppWindow::web_contents() ==
-              content::WebContents::FromRenderFrameHost(render_frame_host)
-                  ->GetOuterWebContents()));
+  DCHECK_EQ(web_contents(),
+            content::WebContents::FromRenderFrameHost(render_frame_host)
+                ->GetOutermostWebContents());
   return helper_->CheckMediaAccessPermission(render_frame_host, security_origin,
                                              type);
 }
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index f2f28d1..df7281a9 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -3301,6 +3301,11 @@
     category: "android"
   }
   builders {
+    name: "buildbot/chromium.perf.fyi/android-go_webview-perf"
+    name: "buildbucket/luci.chromium.ci/android-go_webview-perf"
+    category: "android"
+  }
+  builders {
     name: "buildbot/chromium.perf.fyi/Mojo Linux Perf"
     name: "buildbucket/luci.chromium.ci/Mojo Linux Perf"
     category: "linux"
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 64dd26d..2a11252b 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -112,6 +112,7 @@
 #import "ios/chrome/browser/tabs/tab_model_observer.h"
 #import "ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
+#include "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/commands/show_signin_command.h"
 #import "ios/chrome/browser/ui/external_file_remover_factory.h"
diff --git a/ios/chrome/browser/ui/activity_services/BUILD.gn b/ios/chrome/browser/ui/activity_services/BUILD.gn
index 830da1d..62e5997 100644
--- a/ios/chrome/browser/ui/activity_services/BUILD.gn
+++ b/ios/chrome/browser/ui/activity_services/BUILD.gn
@@ -31,6 +31,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/bookmarks",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/find_in_page",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/snapshots",
     "//ios/chrome/browser/tabs",
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_controller.mm b/ios/chrome/browser/ui/activity_services/activity_service_controller.mm
index 2545a4c4..151aa29d 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_controller.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_controller.mm
@@ -268,9 +268,12 @@
                                        dispatcher:dispatcher];
         [applicationActivities addObject:bookmarkActivity];
       }
-      FindInPageActivity* findInPageActivity =
-          [[FindInPageActivity alloc] initWithDispatcher:dispatcher];
-      [applicationActivities addObject:findInPageActivity];
+
+      if (data.isPageSearchable) {
+        FindInPageActivity* findInPageActivity =
+            [[FindInPageActivity alloc] initWithDispatcher:dispatcher];
+        [applicationActivities addObject:findInPageActivity];
+      }
 
       if (data.userAgent != web::UserAgentType::NONE) {
         RequestDesktopOrMobileSiteActivity* requestActivity =
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_controller_unittest.mm b/ios/chrome/browser/ui/activity_services/activity_service_controller_unittest.mm
index 09a023a..a92eb59d 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_controller_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_controller_unittest.mm
@@ -16,6 +16,7 @@
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/passwords/password_form_filler.h"
 #import "ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h"
+#import "ios/chrome/browser/ui/activity_services/activities/find_in_page_activity.h"
 #import "ios/chrome/browser/ui/activity_services/activities/print_activity.h"
 #import "ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity.h"
 #import "ios/chrome/browser/ui/activity_services/activity_type_util.h"
@@ -210,6 +211,7 @@
                                         title:@""
                               isOriginalTitle:YES
                               isPagePrintable:YES
+                             isPageSearchable:YES
                                     userAgent:web::UserAgentType::MOBILE
                            thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   }
@@ -366,6 +368,7 @@
                                       title:@"foo"
                             isOriginalTitle:YES
                             isPagePrintable:YES
+                           isPageSearchable:YES
                                   userAgent:web::UserAgentType::DESKTOP
                          thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   NSArray* items = [activityController activityItemsForData:data];
@@ -389,6 +392,7 @@
                    title:@"kung fu fighting"
          isOriginalTitle:YES
          isPagePrintable:YES
+        isPageSearchable:YES
                userAgent:web::UserAgentType::DESKTOP
       thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   NSArray* items = [activityController activityItemsForData:data];
@@ -452,6 +456,7 @@
                    title:@"kung fu fighting"
          isOriginalTitle:YES
          isPagePrintable:YES
+        isPageSearchable:YES
                userAgent:web::UserAgentType::DESKTOP
       thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   NSArray* items = [activityController activityItemsForData:data];
@@ -555,6 +560,7 @@
                    title:@"bar"
          isOriginalTitle:YES
          isPagePrintable:YES
+        isPageSearchable:YES
                userAgent:web::UserAgentType::NONE
       thumbnailGenerator:DummyThumbnailGeneratorBlock()];
 
@@ -579,6 +585,7 @@
                    title:@"baz"
          isOriginalTitle:YES
          isPagePrintable:NO
+        isPageSearchable:YES
                userAgent:web::UserAgentType::NONE
       thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   items = [activityController applicationActivitiesForData:data
@@ -611,6 +618,7 @@
                                       title:@"bar"
                             isOriginalTitle:YES
                             isPagePrintable:YES
+                           isPageSearchable:YES
                                   userAgent:web::UserAgentType::MOBILE
                          thumbnailGenerator:DummyThumbnailGeneratorBlock()];
 
@@ -626,6 +634,7 @@
                                          title:@"baz"
                                isOriginalTitle:YES
                                isPagePrintable:YES
+                              isPageSearchable:YES
                                      userAgent:web::UserAgentType::MOBILE
                             thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   items = [activityController applicationActivitiesForData:data
@@ -649,6 +658,7 @@
                                       title:@"bar"
                             isOriginalTitle:YES
                             isPagePrintable:YES
+                           isPageSearchable:YES
                                   userAgent:web::UserAgentType::NONE
                          thumbnailGenerator:DummyThumbnailGeneratorBlock()];
 
@@ -675,6 +685,7 @@
                    title:@"baz"
          isOriginalTitle:YES
          isPagePrintable:YES
+        isPageSearchable:YES
                userAgent:web::UserAgentType::NONE
       thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   items = [activityController applicationActivitiesForData:data
@@ -706,6 +717,7 @@
                                       title:@"bar"
                             isOriginalTitle:YES
                             isPagePrintable:YES
+                           isPageSearchable:YES
                                   userAgent:web::UserAgentType::MOBILE
                          thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   id mockDispatcher = OCMProtocolMock(@protocol(BrowserCommands));
@@ -730,6 +742,7 @@
                                          title:@"bar"
                                isOriginalTitle:YES
                                isPagePrintable:YES
+                              isPageSearchable:YES
                                      userAgent:web::UserAgentType::DESKTOP
                             thumbnailGenerator:DummyThumbnailGeneratorBlock()];
   mockDispatcher = OCMProtocolMock(@protocol(BrowserCommands));
@@ -833,4 +846,62 @@
   EXPECT_FALSE(provider.latestSnackbarMessage);
 }
 
+// Verifies that the FindInPageActivity is sent to the UIActivityViewController
+// if and only if the activity is "searchable".
+TEST_F(ActivityServiceControllerTest, FindInPageActivity) {
+  if (!IsUIRefreshPhase1Enabled())
+    return;
+
+  ActivityServiceController* activityController =
+      [[ActivityServiceController alloc] init];
+
+  // Verify searchable data.
+  ShareToData* data = [[ShareToData alloc]
+        initWithShareURL:GURL("https://chromium.org/printable")
+              visibleURL:GURL("https://chromium.org/printable")
+                   title:@"bar"
+         isOriginalTitle:YES
+         isPagePrintable:YES
+        isPageSearchable:YES
+               userAgent:web::UserAgentType::NONE
+      thumbnailGenerator:DummyThumbnailGeneratorBlock()];
+
+  NSArray* items =
+      [activityController applicationActivitiesForData:data
+                                            dispatcher:nil
+                                         bookmarkModel:bookmark_model_];
+  ASSERT_EQ(4U, [items count]);
+  BOOL foundFindInPageActivity = NO;
+  for (id item in items) {
+    if ([item class] == [FindInPageActivity class]) {
+      foundFindInPageActivity = YES;
+      break;
+    }
+  }
+  EXPECT_TRUE(foundFindInPageActivity);
+
+  // Verify non-searchable data.
+  data = [[ShareToData alloc]
+        initWithShareURL:GURL("https://chromium.org/unprintable")
+              visibleURL:GURL("https://chromium.org/unprintable")
+                   title:@"baz"
+         isOriginalTitle:YES
+         isPagePrintable:YES
+        isPageSearchable:NO
+               userAgent:web::UserAgentType::NONE
+      thumbnailGenerator:DummyThumbnailGeneratorBlock()];
+  items = [activityController applicationActivitiesForData:data
+                                                dispatcher:nil
+                                             bookmarkModel:bookmark_model_];
+  EXPECT_EQ(3U, [items count]);
+  foundFindInPageActivity = NO;
+  for (id item in items) {
+    if ([item class] == [FindInPageActivity class]) {
+      foundFindInPageActivity = YES;
+      break;
+    }
+  }
+  EXPECT_FALSE(foundFindInPageActivity);
+}
+
 }  // namespace
diff --git a/ios/chrome/browser/ui/activity_services/share_to_data.h b/ios/chrome/browser/ui/activity_services/share_to_data.h
index 56082b6..2ecf065c 100644
--- a/ios/chrome/browser/ui/activity_services/share_to_data.h
+++ b/ios/chrome/browser/ui/activity_services/share_to_data.h
@@ -19,6 +19,7 @@
                  title:(NSString*)title
        isOriginalTitle:(BOOL)isOriginalTitle
        isPagePrintable:(BOOL)isPagePrintable
+      isPageSearchable:(BOOL)isPageSearchable
              userAgent:(web::UserAgentType)userAgent
     thumbnailGenerator:(ThumbnailGeneratorBlock)thumbnailGenerator;
 
@@ -36,6 +37,8 @@
 @property(nonatomic, readonly, copy) NSString* title;
 @property(nonatomic, readonly, assign) BOOL isOriginalTitle;
 @property(nonatomic, readonly, assign) BOOL isPagePrintable;
+// Whether FindInPage can be enabled for this page.
+@property(nonatomic, readonly, assign) BOOL isPageSearchable;
 @property(nonatomic, readonly, assign) web::UserAgentType userAgent;
 @property(nonatomic, strong) UIImage* image;
 @property(nonatomic, copy) ThumbnailGeneratorBlock thumbnailGenerator;
diff --git a/ios/chrome/browser/ui/activity_services/share_to_data.mm b/ios/chrome/browser/ui/activity_services/share_to_data.mm
index 2aecb2c..e1839b1 100644
--- a/ios/chrome/browser/ui/activity_services/share_to_data.mm
+++ b/ios/chrome/browser/ui/activity_services/share_to_data.mm
@@ -44,6 +44,7 @@
 @synthesize thumbnailGenerator = thumbnailGenerator_;
 @synthesize isOriginalTitle = isOriginalTitle_;
 @synthesize isPagePrintable = isPagePrintable_;
+@synthesize isPageSearchable = isPageSearchable_;
 @synthesize userAgent = userAgent_;
 
 - (id)initWithShareURL:(const GURL&)shareURL
@@ -51,6 +52,7 @@
                  title:(NSString*)title
        isOriginalTitle:(BOOL)isOriginalTitle
        isPagePrintable:(BOOL)isPagePrintable
+      isPageSearchable:(BOOL)isPageSearchable
              userAgent:(web::UserAgentType)userAgent
     thumbnailGenerator:(ThumbnailGeneratorBlock)thumbnailGenerator {
   DCHECK(shareURL.is_valid());
@@ -63,6 +65,7 @@
     title_ = [title copy];
     isOriginalTitle_ = isOriginalTitle;
     isPagePrintable_ = isPagePrintable;
+    isPageSearchable_ = isPageSearchable;
     userAgent_ = userAgent;
     thumbnailGenerator_ = thumbnailGenerator;
   }
diff --git a/ios/chrome/browser/ui/activity_services/share_to_data_builder.mm b/ios/chrome/browser/ui/activity_services/share_to_data_builder.mm
index 6b2d6b3..8e7452a4 100644
--- a/ios/chrome/browser/ui/activity_services/share_to_data_builder.mm
+++ b/ios/chrome/browser/ui/activity_services/share_to_data_builder.mm
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #import "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
 #include "ios/chrome/browser/tabs/tab.h"
 #include "ios/chrome/browser/ui/activity_services/chrome_activity_item_thumbnail_generator.h"
 #include "ios/chrome/browser/ui/activity_services/share_to_data.h"
@@ -54,19 +55,23 @@
   const GURL& finalURLToShare =
       !shareURL.is_empty() ? shareURL : tab.webState->GetVisibleURL();
 
+  web::NavigationItem* visibleItem =
+      tab.webState->GetNavigationManager()->GetVisibleItem();
   web::UserAgentType userAgent = web::UserAgentType::NONE;
-  if (tab.webState) {
-    web::NavigationItem* visibleItem =
-        tab.webState->GetNavigationManager()->GetVisibleItem();
-    if (visibleItem)
-      userAgent = visibleItem->GetUserAgentType();
-  }
+  if (visibleItem)
+    userAgent = visibleItem->GetUserAgentType();
+
+  auto* helper = FindTabHelper::FromWebState(tab.webState);
+  BOOL is_page_searchable =
+      (helper && helper->CurrentPageSupportsFindInPage() &&
+       !helper->IsFindUIActive());
 
   return [[ShareToData alloc] initWithShareURL:finalURLToShare
                                     visibleURL:tab.webState->GetVisibleURL()
                                          title:tab.title
                                isOriginalTitle:is_original_title
                                isPagePrintable:is_page_printable
+                              isPageSearchable:is_page_searchable
                                      userAgent:userAgent
                             thumbnailGenerator:thumbnail_generator];
 }
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/BUILD.gn b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/BUILD.gn
index c5f60913..5a03fd1 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/BUILD.gn
@@ -32,6 +32,7 @@
     "identity_chooser_animator.mm",
     "identity_chooser_cell.h",
     "identity_chooser_cell.mm",
+    "identity_chooser_consumer.h",
     "identity_chooser_header_item.h",
     "identity_chooser_header_item.mm",
     "identity_chooser_item.h",
@@ -56,6 +57,7 @@
     "//ios/chrome/browser/ui:ui_util",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/image_util",
+    "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/resources:menu_shadow",
     "//ios/chrome/browser/ui/table_view/cells",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_consumer.h b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_consumer.h
new file mode 100644
index 0000000..e7a03ad3
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_consumer.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 IOS_CHROME_BROWSER_UI_AUTHENTICATION_UNIFIED_CONSENT_IDENTITY_CHOOSER_IDENTITY_CHOOSER_CONSUMER_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_UNIFIED_CONSENT_IDENTITY_CHOOSER_IDENTITY_CHOOSER_CONSUMER_H_
+
+@class IdentityChooserItem;
+
+// Consumer for the IdentityChooser.
+@protocol IdentityChooserConsumer
+
+// Sets the |items| displayed by this consumer.
+- (void)setIdentityItems:(NSArray<IdentityChooserItem*>*)items;
+
+// Notifies the consumer that the |changedItem| has changed.
+- (void)itemHasChanged:(IdentityChooserItem*)changedItem;
+
+// Returns an IdentityChooserItem based on a gaia ID.
+- (IdentityChooserItem*)identityChooserItemWithGaiaID:(NSString*)gaiaID;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_UNIFIED_CONSENT_IDENTITY_CHOOSER_IDENTITY_CHOOSER_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_coordinator.mm b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_coordinator.mm
index 0aa318d..a74b4ba4 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_coordinator.mm
@@ -77,8 +77,7 @@
 
   // Creates the mediator.
   self.identityChooserMediator = [[IdentityChooserMediator alloc] init];
-  self.identityChooserMediator.identityChooserViewController =
-      self.identityChooserViewController;
+  self.identityChooserMediator.consumer = self.identityChooserViewController;
   // Setups.
   self.identityChooserViewController.presentationDelegate = self;
   // Starts.
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_header_item.mm b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_header_item.mm
index cd32dc2..b224d024 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_header_item.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_header_item.mm
@@ -32,6 +32,7 @@
   if (self) {
     UILabel* label = [[UILabel alloc] init];
     label.translatesAutoresizingMaskIntoConstraints = NO;
+    label.numberOfLines = 2;
     label.text =
         l10n_util::GetNSString(IDS_IOS_ACCOUNT_IDENTITY_CHOOSER_CHOOSE_ACCOUNT);
     label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.h b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.h
index b65cc41a7..5b53a52 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.h
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.h
@@ -8,7 +8,7 @@
 #import <UIKit/UIKit.h>
 
 @class ChromeIdentity;
-@class IdentityChooserViewController;
+@protocol IdentityChooserConsumer;
 
 // A mediator object that monitors updates of chrome identities, and updates the
 // IdentityChooserViewController.
@@ -17,8 +17,7 @@
 // Selected Chrome identity.
 @property(nonatomic, strong) ChromeIdentity* selectedIdentity;
 // View controller.
-@property(nonatomic, weak)
-    IdentityChooserViewController* identityChooserViewController;
+@property(nonatomic, weak) id<IdentityChooserConsumer> consumer;
 
 // Starts this mediator.
 - (void)start;
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.mm b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.mm
index b9b4b16..feb09a1 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_mediator.mm
@@ -7,12 +7,8 @@
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/chrome_browser_provider_observer_bridge.h"
 #import "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
-#import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_add_account_item.h"
-#import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_header_item.h"
+#import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_consumer.h"
 #import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_item.h"
-#import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_header_footer_item.h"
-#import "ios/chrome/browser/ui/table_view/table_view_model.h"
 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
 #include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
 
@@ -20,20 +16,6 @@
 #error "This file requires ARC support."
 #endif
 
-namespace {
-
-typedef NS_ENUM(NSInteger, SectionIdentifier) {
-  IdentitiesSectionIdentifier = kSectionIdentifierEnumZero,
-  AddAccountSectionIdentifier,
-};
-
-typedef NS_ENUM(NSInteger, ItemType) {
-  IdentityItemType = kItemTypeEnumZero,
-  AddAccountItemType,
-};
-
-}  // namespace
-
 @interface IdentityChooserMediator ()<ChromeIdentityServiceObserver,
                                       ChromeBrowserProviderObserver> {
   std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
@@ -48,43 +30,32 @@
 
 @implementation IdentityChooserMediator
 
-@synthesize identityChooserViewController = _identityChooserViewController;
+@synthesize consumer = _consumer;
 @synthesize selectedIdentity = _selectedIdentity;
 
 - (void)start {
-  [self.identityChooserViewController loadModel];
   _identityServiceObserver =
       std::make_unique<ChromeIdentityServiceObserverBridge>(self);
   _browserProviderObserver =
       std::make_unique<ChromeBrowserProviderObserverBridge>(self);
-  TableViewModel* tableViewModel =
-      self.identityChooserViewController.tableViewModel;
   [self loadIdentitySection];
-  [tableViewModel addSectionWithIdentifier:AddAccountSectionIdentifier];
-  IdentityChooserAddAccountItem* addAccountItem =
-      [[IdentityChooserAddAccountItem alloc] initWithType:AddAccountItemType];
-  [tableViewModel addItem:addAccountItem
-      toSectionWithIdentifier:AddAccountSectionIdentifier];
-  [self.identityChooserViewController.tableView reloadData];
 }
 
 - (void)setSelectedIdentity:(ChromeIdentity*)selectedIdentity {
   if (_selectedIdentity == selectedIdentity)
     return;
-  IdentityChooserItem* previousSelectedItem =
-      [self identityChooserItemWithGaiaID:self.selectedIdentity.gaiaID];
+  IdentityChooserItem* previousSelectedItem = [self.consumer
+      identityChooserItemWithGaiaID:self.selectedIdentity.gaiaID];
   if (previousSelectedItem) {
     previousSelectedItem.selected = NO;
-    [self.identityChooserViewController
-        reconfigureCellsForItems:@[ previousSelectedItem ]];
+    [self.consumer itemHasChanged:previousSelectedItem];
   }
   _selectedIdentity = selectedIdentity;
-  IdentityChooserItem* selectedItem =
-      [self identityChooserItemWithGaiaID:self.selectedIdentity.gaiaID];
+  IdentityChooserItem* selectedItem = [self.consumer
+      identityChooserItemWithGaiaID:self.selectedIdentity.gaiaID];
   DCHECK(selectedItem);
   selectedItem.selected = YES;
-  [self.identityChooserViewController
-      reconfigureCellsForItems:@[ selectedItem ]];
+  [self.consumer itemHasChanged:selectedItem];
 }
 
 - (void)selectIdentityWithGaiaID:(NSString*)gaiaID {
@@ -96,41 +67,20 @@
 
 #pragma mark - Private
 
-// Returns an IdentityChooserItem based on a gaia ID.
-- (IdentityChooserItem*)identityChooserItemWithGaiaID:(NSString*)gaiaID {
-  for (IdentityChooserItem* item in
-       [self.identityChooserViewController.tableViewModel
-           itemsInSectionWithIdentifier:IdentitiesSectionIdentifier]) {
-    if ([item.gaiaID isEqualToString:gaiaID])
-      return item;
-  }
-  return nil;
-}
-
 // Creates the identity section with its header item, and all the identity items
 // based on the ChromeIdentity.
 - (void)loadIdentitySection {
-  TableViewModel* tableViewModel =
-      self.identityChooserViewController.tableViewModel;
-  DCHECK(![tableViewModel
-      hasSectionForSectionIdentifier:IdentitiesSectionIdentifier]);
-  // Create the section.
-  [tableViewModel insertSectionWithIdentifier:IdentitiesSectionIdentifier
-                                      atIndex:0];
-  // Create the header item.
-  [tableViewModel setHeader:[[IdentityChooserHeaderItem alloc] init]
-      forSectionWithIdentifier:IdentitiesSectionIdentifier];
   // Create all the identity items.
   NSArray<ChromeIdentity*>* identities =
       self.chromeIdentityService->GetAllIdentitiesSortedForDisplay();
+  NSMutableArray<IdentityChooserItem*>* items = [NSMutableArray array];
   for (ChromeIdentity* identity in identities) {
-    IdentityChooserItem* item =
-        [[IdentityChooserItem alloc] initWithType:IdentityItemType];
+    IdentityChooserItem* item = [[IdentityChooserItem alloc] initWithType:0];
     [self updateIdentityChooserItem:item withChromeIdentity:identity];
-    [self.identityChooserViewController.tableViewModel
-                        addItem:item
-        toSectionWithIdentifier:IdentitiesSectionIdentifier];
+    [items addObject:item];
   }
+
+  [self.consumer setIdentityItems:items];
 }
 
 // Updates an IdentityChooserItem based on a ChromeIdentity.
@@ -143,12 +93,8 @@
       [self.selectedIdentity.gaiaID isEqualToString:identity.gaiaID];
   __weak __typeof(self) weakSelf = self;
   ios::GetAvatarCallback callback = ^(UIImage* identityAvatar) {
-    if (![weakSelf.identityChooserViewController.tableViewModel hasItem:item]) {
-      // Make sure the item is still displayed.
-      return;
-    }
     item.avatar = identityAvatar;
-    [weakSelf.identityChooserViewController reconfigureCellsForItems:@[ item ]];
+    [weakSelf.consumer itemHasChanged:item];
   };
   self.chromeIdentityService->GetAvatarForIdentity(identity, callback);
 }
@@ -161,11 +107,7 @@
 #pragma mark - ChromeIdentityServiceObserver
 
 - (void)identityListChanged {
-  TableViewModel* tableViewModel =
-      self.identityChooserViewController.tableViewModel;
-  [tableViewModel removeSectionWithIdentifier:IdentitiesSectionIdentifier];
   [self loadIdentitySection];
-  [self.identityChooserViewController.tableView reloadData];
   // Updates the selection.
   NSArray* allIdentities =
       self.chromeIdentityService->GetAllIdentitiesSortedForDisplay();
@@ -176,7 +118,7 @@
 
 - (void)profileUpdate:(ChromeIdentity*)identity {
   IdentityChooserItem* item =
-      [self identityChooserItemWithGaiaID:identity.gaiaID];
+      [self.consumer identityChooserItemWithGaiaID:identity.gaiaID];
   [self updateIdentityChooserItem:item withChromeIdentity:identity];
 }
 
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.h b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.h
index f8f7bb5..d46ef99 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.h
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.h
@@ -7,6 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_consumer.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
 
 @protocol IdentityChooserViewControllerPresentationDelegate;
@@ -14,7 +15,8 @@
 // View controller to display the list of identities, to let the user choose an
 // identity. IdentityChooserViewController also displays "Add Account…" cell
 // at the end.
-@interface IdentityChooserViewController : ChromeTableViewController
+@interface IdentityChooserViewController
+    : ChromeTableViewController<IdentityChooserConsumer>
 
 // Presentation delegate.
 @property(nonatomic, weak) id<IdentityChooserViewControllerPresentationDelegate>
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.mm b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.mm
index 7b4800e..418d60e4 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller.mm
@@ -6,8 +6,11 @@
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_add_account_item.h"
+#import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_header_item.h"
 #import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_item.h"
 #import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_view_controller_presentation_delegate.h"
+#import "ios/chrome/browser/ui/list_model/list_item+Controller.h"
 #import "ios/third_party/material_components_ios/src/components/Dialogs/src/MaterialDialogs.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -15,6 +18,7 @@
 #endif
 
 namespace {
+
 const CGFloat kViewControllerWidth = 312.;
 const CGFloat kViewControllerHeight = 230.;
 // Header height for identity section.
@@ -23,6 +27,16 @@
 const CGFloat kRowHeight = 54.;
 // Footer height for "Add Account…" section.
 const CGFloat kFooterHeight = 17.;
+
+typedef NS_ENUM(NSInteger, SectionIdentifier) {
+  IdentitiesSectionIdentifier = kSectionIdentifierEnumZero,
+};
+
+typedef NS_ENUM(NSInteger, ItemType) {
+  IdentityItemType = kItemTypeEnumZero,
+  AddAccountItemType,
+};
+
 }  // namespace
 
 @implementation IdentityChooserViewController
@@ -39,17 +53,13 @@
   // Setting -UITableView.rowHeight is required for iOS 10. On iOS 11, the row
   // height is automatically set.
   self.tableView.estimatedRowHeight = kRowHeight;
+  self.tableView.estimatedSectionHeaderHeight = kHeaderHeight;
 }
 
 - (void)viewDidDisappear:(BOOL)animated {
   [self.presentationDelegate identityChooserViewControllerDidDisappear:self];
 }
 
-- (CGFloat)tableView:(UITableView*)tableView
-    heightForHeaderInSection:(NSInteger)section {
-  return (section == 0) ? kHeaderHeight : 0;
-}
-
 - (void)tableView:(UITableView*)tableView
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
   [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
@@ -75,7 +85,56 @@
   }
 }
 
-#pragma mark UIAccessibilityAction
+#pragma mark - IdentityChooserConsumer
+
+- (void)setIdentityItems:(NSArray<TableViewItem*>*)items {
+  [self loadModel];
+
+  TableViewModel* tableViewModel = self.tableViewModel;
+  if ([tableViewModel
+          hasSectionForSectionIdentifier:IdentitiesSectionIdentifier]) {
+    [tableViewModel removeSectionWithIdentifier:IdentitiesSectionIdentifier];
+  }
+  [tableViewModel addSectionWithIdentifier:IdentitiesSectionIdentifier];
+  // Create the header item.
+  [tableViewModel setHeader:[[IdentityChooserHeaderItem alloc] init]
+      forSectionWithIdentifier:IdentitiesSectionIdentifier];
+  // Insert the items.
+  for (TableViewItem* item in items) {
+    item.type = IdentityItemType;
+    [tableViewModel addItem:item
+        toSectionWithIdentifier:IdentitiesSectionIdentifier];
+  }
+  // Insert "Add Account" item.
+  IdentityChooserAddAccountItem* addAccountItem =
+      [[IdentityChooserAddAccountItem alloc] initWithType:AddAccountItemType];
+  [tableViewModel addItem:addAccountItem
+      toSectionWithIdentifier:IdentitiesSectionIdentifier];
+
+  [self.tableView reloadData];
+}
+
+- (void)itemHasChanged:(TableViewItem*)changedItem {
+  if (![self.tableViewModel hasItem:changedItem])
+    return;
+
+  [self reconfigureCellsForItems:@[ changedItem ]];
+}
+
+- (IdentityChooserItem*)identityChooserItemWithGaiaID:(NSString*)gaiaID {
+  for (IdentityChooserItem* item in [self.tableViewModel
+           itemsInSectionWithIdentifier:IdentitiesSectionIdentifier]) {
+    if (item.type != IdentityItemType)
+      continue;
+    IdentityChooserItem* identityItem =
+        base::mac::ObjCCastStrict<IdentityChooserItem>(item);
+    if ([identityItem.gaiaID isEqualToString:gaiaID])
+      return identityItem;
+  }
+  return nil;
+}
+
+#pragma mark - UIAccessibilityAction
 
 - (BOOL)accessibilityPerformEscape {
   [self dismissViewControllerAnimated:YES completion:nil];
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_legacy_view.mm b/ios/chrome/browser/ui/location_bar/location_bar_legacy_view.mm
index c801ce8b..a43de79 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_legacy_view.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_legacy_view.mm
@@ -7,7 +7,7 @@
 #import "ios/chrome/browser/ui/animation_util.h"
 #import "ios/chrome/browser/ui/omnibox/clipping_textfield_container.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
index a42a19f..0e9a72a3 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
@@ -11,7 +11,7 @@
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_consumer.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
@@ -216,12 +216,8 @@
     return [self imageForOfflinePage];
   }
 
-  return [self imageForSecurityLevel:self.toolbarModel->GetSecurityLevel(true)];
-}
-
-- (UIImage*)imageForSecurityLevel:(security_state::SecurityLevel)level {
-  base::string16 iconName = GetUIRefreshIconNameForSecurityState(level);
-  return [UIImage imageNamed:base::SysUTF16ToNSString(iconName)];
+  return GetLocationBarSecurityIconForSecurityState(
+      self.toolbarModel->GetSecurityLevel(true));
 }
 
 // Returns a location icon for offline pages.
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
index 20dc6ab..487b6cfe 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -217,9 +217,7 @@
 
 - (void)updateLocationIcon:(UIImage*)icon
         securityStatusText:(NSString*)statusText {
-  [self.locationBarSteadyView
-      setLocationImage:
-          [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
+  [self.locationBarSteadyView setLocationImage:icon];
   self.locationBarSteadyView.securityLevelAccessibilityString = statusText;
 }
 
diff --git a/ios/chrome/browser/ui/omnibox/BUILD.gn b/ios/chrome/browser/ui/omnibox/BUILD.gn
index e566691..5c29bb1 100644
--- a/ios/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/BUILD.gn
@@ -36,9 +36,11 @@
 }
 
 source_set("omnibox_util") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+
   sources = [
-    "omnibox_util.cc",
     "omnibox_util.h",
+    "omnibox_util.mm",
   ]
   deps = [
     "//base",
diff --git a/ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.mm b/ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.mm
index 5b231301..93239ab 100644
--- a/ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.mm
+++ b/ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.mm
@@ -10,7 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/suggestion_answer.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #import "ios/chrome/browser/ui/ui_util.h"
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 
@@ -231,11 +231,8 @@
       !(self.isIncognito && _match.type == AutocompleteMatchType::CALCULATOR))
       << "Calculator answers are never shown in incognito mode because input "
          "is never sent to the search provider.";
-  NSString* imageName = base::SysUTF8ToNSString(
-      GetResourceNameForAutocompleteMatchType(_match.type, self.isStarred));
-  UIImage* icon = [[UIImage imageNamed:imageName]
-      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-  return icon;
+  return GetOmniboxSuggestionIconForAutocompleteMatchType(_match.type,
+                                                          self.isStarred);
 }
 
 #pragma mark helpers
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
index 50b96b3e..dc4f72d 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
@@ -13,7 +13,7 @@
 #import "ios/chrome/browser/ui/location_bar/location_bar_constants.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_mediator.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #include "ios/chrome/browser/ui/omnibox/omnibox_view_controller.h"
 #include "ios/chrome/browser/ui/omnibox/omnibox_view_ios.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h"
@@ -58,19 +58,9 @@
 
   self.viewController =
       [[OmniboxViewController alloc] initWithIncognito:isIncognito];
-  std::string defaultLeadingImageName = GetResourceNameForAutocompleteMatchType(
-      AutocompleteMatchType::URL_WHAT_YOU_TYPED, /* is_starred */ false);
-  UIImage* defaultLeadingImage =
-      [[UIImage imageNamed:base::SysUTF8ToNSString(defaultLeadingImageName)]
-          imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-  self.viewController.defaultLeadingImage = defaultLeadingImage;
-  std::string defaultEmptyOmniboxLeadingImageName =
-      GetResourceNameForAutocompleteMatchType(
-          AutocompleteMatchType::SEARCH_SUGGEST, /* is_starred */ false);
-  UIImage* defaultEmptyOmniboxLeadingImage = [[UIImage
-      imageNamed:base::SysUTF8ToNSString(defaultEmptyOmniboxLeadingImageName)]
-      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-  self.viewController.emptyTextLeadingImage = defaultEmptyOmniboxLeadingImage;
+  self.viewController.defaultLeadingImage =
+      GetOmniboxSuggestionIcon(DEFAULT_FAVICON);
+  self.viewController.emptyTextLeadingImage = GetOmniboxSuggestionIcon(SEARCH);
 
   self.viewController.dispatcher =
       static_cast<id<LoadQueryCommands, OmniboxFocuser>>(self.dispatcher);
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
index 2f11b42..68a3332 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
@@ -6,7 +6,7 @@
 
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_consumer.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -19,10 +19,8 @@
 #pragma mark - OmniboxLeftImageConsumer
 
 - (void)setLeftImageForAutocompleteType:(AutocompleteMatchType::Type)type {
-  std::string imageName =
-      GetResourceNameForAutocompleteMatchType(type, /* is_starred */ false);
-  UIImage* image = [[UIImage imageNamed:base::SysUTF8ToNSString(imageName)]
-      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+  UIImage* image = GetOmniboxSuggestionIconForAutocompleteMatchType(
+      type, /* is_starred */ false);
   [self.consumer updateAutocompleteIcon:image];
 }
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
index 4f3ea8a7..40cf15e 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
@@ -18,7 +18,7 @@
 #include "ios/chrome/browser/autocomplete/autocomplete_scheme_classifier_impl.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/ui/animation_util.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #import "ios/chrome/browser/ui/reversed_animation.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.h"
@@ -927,6 +927,7 @@
   [_selection setTextColor:_displayedTextColor];
   [_selection setOpaque:NO];
   [_selection setBackgroundColor:[UIColor clearColor]];
+  _selection.lineBreakMode = NSLineBreakByClipping;
   [self addSubview:_selection];
   [self hideTextAndCursor];
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.cc b/ios/chrome/browser/ui/omnibox/omnibox_util.cc
deleted file mode 100644
index c9d9a1b8..0000000
--- a/ios/chrome/browser/ui/omnibox/omnibox_util.cc
+++ /dev/null
@@ -1,138 +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 "ios/chrome/browser/ui/omnibox/omnibox_util.h"
-
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
-#include "ios/chrome/grit/ios_theme_resources.h"
-
-int GetIconForAutocompleteMatchType(AutocompleteMatchType::Type type,
-                                    bool is_starred,
-                                    bool is_incognito) {
-  if (is_starred)
-    return is_incognito ? IDR_IOS_OMNIBOX_STAR_INCOGNITO : IDR_IOS_OMNIBOX_STAR;
-
-  switch (type) {
-    case AutocompleteMatchType::BOOKMARK_TITLE:
-    case AutocompleteMatchType::CLIPBOARD:
-    case AutocompleteMatchType::NAVSUGGEST:
-    case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED:
-    case AutocompleteMatchType::PHYSICAL_WEB_DEPRECATED:
-    case AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW_DEPRECATED:
-    case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
-      return is_incognito ? IDR_IOS_OMNIBOX_HTTP_INCOGNITO
-                          : IDR_IOS_OMNIBOX_HTTP;
-    case AutocompleteMatchType::HISTORY_BODY:
-    case AutocompleteMatchType::HISTORY_KEYWORD:
-    case AutocompleteMatchType::HISTORY_TITLE:
-    case AutocompleteMatchType::HISTORY_URL:
-    case AutocompleteMatchType::SEARCH_HISTORY:
-    case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
-      return is_incognito ? IDR_IOS_OMNIBOX_HISTORY_INCOGNITO
-                          : IDR_IOS_OMNIBOX_HISTORY;
-    case AutocompleteMatchType::CONTACT_DEPRECATED:
-    case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
-    case AutocompleteMatchType::SEARCH_SUGGEST:
-    case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
-    case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
-    case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE:
-    case AutocompleteMatchType::SEARCH_SUGGEST_TAIL:
-    case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
-    case AutocompleteMatchType::VOICE_SUGGEST:
-      return is_incognito ? IDR_IOS_OMNIBOX_SEARCH_INCOGNITO
-                          : IDR_IOS_OMNIBOX_SEARCH;
-    case AutocompleteMatchType::CALCULATOR:
-      // Calculator answers are never shown in incognito mode because input is
-      // never sent to the search provider.
-      DCHECK(!is_incognito);
-      return IDR_IOS_OMNIBOX_CALCULATOR;
-    case AutocompleteMatchType::DOCUMENT_SUGGESTION:
-      // Document suggeestions aren't yet supported on mobile.
-      NOTREACHED();
-      return IDR_IOS_OMNIBOX_HTTP;
-    case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
-    case AutocompleteMatchType::NUM_TYPES:
-      NOTREACHED();
-      return IDR_IOS_OMNIBOX_HTTP;
-  }
-}
-
-std::string GetResourceNameForAutocompleteMatchType(
-    AutocompleteMatchType::Type type,
-    bool is_starred) {
-  if (is_starred)
-    return "omnibox_completion_bookmark";
-
-  switch (type) {
-    case AutocompleteMatchType::BOOKMARK_TITLE:
-    case AutocompleteMatchType::CLIPBOARD:
-    case AutocompleteMatchType::NAVSUGGEST:
-    case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED:
-    case AutocompleteMatchType::PHYSICAL_WEB_DEPRECATED:
-    case AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW_DEPRECATED:
-    case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
-    case AutocompleteMatchType::DOCUMENT_SUGGESTION:
-      return "omnibox_completion_default_favicon";
-    case AutocompleteMatchType::HISTORY_BODY:
-    case AutocompleteMatchType::HISTORY_KEYWORD:
-    case AutocompleteMatchType::HISTORY_TITLE:
-    case AutocompleteMatchType::HISTORY_URL:
-    case AutocompleteMatchType::SEARCH_HISTORY:
-    case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
-      return "omnibox_completion_history";
-    case AutocompleteMatchType::CONTACT_DEPRECATED:
-    case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
-    case AutocompleteMatchType::SEARCH_SUGGEST:
-    case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
-    case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
-    case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE:
-    case AutocompleteMatchType::SEARCH_SUGGEST_TAIL:
-    case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
-    case AutocompleteMatchType::VOICE_SUGGEST:
-      return "omnibox_completion_search";
-    case AutocompleteMatchType::CALCULATOR:
-      return "omnibox_completion_calculator";
-    case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
-    case AutocompleteMatchType::NUM_TYPES:
-      NOTREACHED();
-      return "omnibox_completion_default_favicon";
-  }
-}
-
-int GetIconForSecurityState(security_state::SecurityLevel security_level) {
-  switch (security_level) {
-    case security_state::NONE:
-    case security_state::HTTP_SHOW_WARNING:
-      return IDR_IOS_OMNIBOX_HTTP;
-    case security_state::EV_SECURE:
-    case security_state::SECURE:
-      return IDR_IOS_OMNIBOX_HTTPS_VALID;
-    case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
-      return IDR_IOS_OMNIBOX_HTTPS_POLICY_WARNING;
-    case security_state::DANGEROUS:
-      return IDR_IOS_OMNIBOX_HTTPS_INVALID;
-    case security_state::SECURITY_LEVEL_COUNT:
-      NOTREACHED();
-      return IDR_IOS_OMNIBOX_HTTP;
-  }
-}
-
-base::string16 GetUIRefreshIconNameForSecurityState(
-    security_state::SecurityLevel security_level) {
-  switch (security_level) {
-    case security_state::NONE:
-    case security_state::HTTP_SHOW_WARNING:
-      return base::ASCIIToUTF16("location_bar_insecure");
-    case security_state::EV_SECURE:
-    case security_state::SECURE:
-    case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
-      return base::ASCIIToUTF16("location_bar_secure");
-    case security_state::DANGEROUS:
-      return base::ASCIIToUTF16("location_bar_dangerous");
-    case security_state::SECURITY_LEVEL_COUNT:
-      NOTREACHED();
-      return base::ASCIIToUTF16("location_bar_insecure");
-  }
-}
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.h b/ios/chrome/browser/ui/omnibox/omnibox_util.h
index 2d817ac..d9fbc80 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_util.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_util.h
@@ -5,28 +5,78 @@
 #ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_UTIL_H_
 #define IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_UTIL_H_
 
+#import <UIKit/UIKit.h>
+
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/security_state/core/security_state.h"
 
+#pragma mark - Suggestion icons.
+
+// All available icons for autocomplete suggestions in the popup.
+enum OmniboxSuggestionIconType {
+  BOOKMARK = 0,
+  CALCULATOR,
+  DEFAULT_FAVICON,
+  HISTORY,
+  SEARCH,
+  OMNIBOX_SUGGESTION_ICON_TYPE_COUNT,
+};
+
+// Returns the asset name (to be used in -[UIImage imageNamed:]).
+NSString* GetOmniboxSuggestionIconTypeAssetName(OmniboxSuggestionIconType icon);
+
+// Returns the asset with "always template" rendering mode.
+UIImage* GetOmniboxSuggestionIcon(OmniboxSuggestionIconType icon);
+
+// Converts |type| to the appropriate icon type for this match type to show in
+// the omnibox.
+OmniboxSuggestionIconType GetOmniboxSuggestionIconTypeForAutocompleteMatchType(
+    AutocompleteMatchType::Type type,
+    bool is_starred);
+
+// Converts |type| to the appropriate icon for this type to show in the omnibox.
+// Returns UI Refresh icons.
+UIImage* GetOmniboxSuggestionIconForAutocompleteMatchType(
+    AutocompleteMatchType::Type type,
+    bool is_starred);
+
+#pragma mark - Security icons.
+
+// All available icons for security states.
+enum LocationBarSecurityIconType {
+  INSECURE = 0,
+  SECURE,
+  DANGEROUS,
+  LOCATION_BAR_SECURITY_ICON_TYPE_COUNT,
+};
+
+// Returns the asset name (to be used in -[UIImage imageNamed:]).
+NSString* GetLocationBarSecurityIconTypeAssetName(
+    LocationBarSecurityIconType icon);
+
+// Returns the asset with "always template" rendering mode.
+UIImage* GetLocationBarSecurityIcon(LocationBarSecurityIconType icon);
+
+// Converts the |security_level| to an appropriate security icon type.
+LocationBarSecurityIconType GetLocationBarSecurityIconTypeForSecurityState(
+    security_state::SecurityLevel security_level);
+
+// Converts the |security_level| to an appropriate icon in "always template"
+// rendering mode.
+UIImage* GetLocationBarSecurityIconForSecurityState(
+    security_state::SecurityLevel security_level);
+
+#pragma mark - Legacy utils.
+
 // Converts |type| to a resource identifier for the appropriate icon for this
 // type to show in the omnibox.
 int GetIconForAutocompleteMatchType(AutocompleteMatchType::Type type,
                                     bool is_starred,
                                     bool is_incognito);
 
-// Converts |type| to a resource identifier for the appropriate icon for this
-// type to show in the omnibox. Returns UI Refresh icons.
-std::string GetResourceNameForAutocompleteMatchType(
-    AutocompleteMatchType::Type type,
-    bool is_starred);
 
 // Converts |security_level| to a resource identifier for the appropriate icon
 // for this security level in the omnibox.
 int GetIconForSecurityState(security_state::SecurityLevel security_level);
 
-// Converts |security_level| to the icon name used as a resource identifier for
-// this security level in the location bar.
-base::string16 GetUIRefreshIconNameForSecurityState(
-    security_state::SecurityLevel security_level);
-
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_UTIL_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.mm b/ios/chrome/browser/ui/omnibox/omnibox_util.mm
new file mode 100644
index 0000000..f6774545
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/omnibox_util.mm
@@ -0,0 +1,213 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ios/chrome/grit/ios_theme_resources.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#pragma mark - Suggestion icons.
+
+NSString* GetOmniboxSuggestionIconTypeAssetName(
+    OmniboxSuggestionIconType iconType) {
+  switch (iconType) {
+    case BOOKMARK:
+      return @"omnibox_completion_bookmark";
+    case CALCULATOR:
+      return @"omnibox_completion_calculator";
+    case DEFAULT_FAVICON:
+      return @"omnibox_completion_default_favicon";
+    case HISTORY:
+      return @"omnibox_completion_history";
+    case SEARCH:
+      return @"omnibox_completion_search";
+    case OMNIBOX_SUGGESTION_ICON_TYPE_COUNT:
+      NOTREACHED();
+      return @"omnibox_completion_default_favicon";
+  }
+}
+
+UIImage* GetOmniboxSuggestionIcon(OmniboxSuggestionIconType iconType) {
+  NSString* imageName = GetOmniboxSuggestionIconTypeAssetName(iconType);
+  return [[UIImage imageNamed:imageName]
+      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+}
+
+OmniboxSuggestionIconType GetOmniboxSuggestionIconTypeForAutocompleteMatchType(
+    AutocompleteMatchType::Type type,
+    bool is_starred) {
+  if (is_starred)
+    return BOOKMARK;
+
+  switch (type) {
+    case AutocompleteMatchType::BOOKMARK_TITLE:
+    case AutocompleteMatchType::CLIPBOARD:
+    case AutocompleteMatchType::NAVSUGGEST:
+    case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED:
+    case AutocompleteMatchType::PHYSICAL_WEB_DEPRECATED:
+    case AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW_DEPRECATED:
+    case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
+    case AutocompleteMatchType::DOCUMENT_SUGGESTION:
+      return DEFAULT_FAVICON;
+    case AutocompleteMatchType::HISTORY_BODY:
+    case AutocompleteMatchType::HISTORY_KEYWORD:
+    case AutocompleteMatchType::HISTORY_TITLE:
+    case AutocompleteMatchType::HISTORY_URL:
+    case AutocompleteMatchType::SEARCH_HISTORY:
+    case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
+      return HISTORY;
+    case AutocompleteMatchType::CONTACT_DEPRECATED:
+    case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
+    case AutocompleteMatchType::SEARCH_SUGGEST:
+    case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
+    case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
+    case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE:
+    case AutocompleteMatchType::SEARCH_SUGGEST_TAIL:
+    case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
+    case AutocompleteMatchType::VOICE_SUGGEST:
+      return SEARCH;
+    case AutocompleteMatchType::CALCULATOR:
+      return CALCULATOR;
+    case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
+    case AutocompleteMatchType::NUM_TYPES:
+      NOTREACHED();
+      return DEFAULT_FAVICON;
+  }
+}
+
+UIImage* GetOmniboxSuggestionIconForAutocompleteMatchType(
+    AutocompleteMatchType::Type type,
+    bool is_starred) {
+  OmniboxSuggestionIconType iconType =
+      GetOmniboxSuggestionIconTypeForAutocompleteMatchType(type, is_starred);
+  return GetOmniboxSuggestionIcon(iconType);
+}
+
+#pragma mark - Security icons.
+
+NSString* GetLocationBarSecurityIconTypeAssetName(
+    LocationBarSecurityIconType iconType) {
+  switch (iconType) {
+    case INSECURE:
+      return @"location_bar_insecure";
+    case SECURE:
+      return @"location_bar_secure";
+    case DANGEROUS:
+      return @"location_bar_dangerous";
+    case LOCATION_BAR_SECURITY_ICON_TYPE_COUNT:
+      NOTREACHED();
+      return @"location_bar_insecure";
+  }
+}
+
+// Returns the asset with "always template" rendering mode.
+UIImage* GetLocationBarSecurityIcon(LocationBarSecurityIconType iconType) {
+  NSString* imageName = GetLocationBarSecurityIconTypeAssetName(iconType);
+  return [[UIImage imageNamed:imageName]
+      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+}
+
+// Converts the |security_level| to an appropriate security icon type.
+LocationBarSecurityIconType GetLocationBarSecurityIconTypeForSecurityState(
+    security_state::SecurityLevel security_level) {
+  switch (security_level) {
+    case security_state::NONE:
+    case security_state::HTTP_SHOW_WARNING:
+      return INSECURE;
+    case security_state::EV_SECURE:
+    case security_state::SECURE:
+    case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
+      return SECURE;
+    case security_state::DANGEROUS:
+      return DANGEROUS;
+    case security_state::SECURITY_LEVEL_COUNT:
+      NOTREACHED();
+      return LOCATION_BAR_SECURITY_ICON_TYPE_COUNT;
+  }
+}
+
+// Converts the |security_level| to an appropriate icon in "always template"
+// rendering mode.
+UIImage* GetLocationBarSecurityIconForSecurityState(
+    security_state::SecurityLevel security_level) {
+  LocationBarSecurityIconType iconType =
+      GetLocationBarSecurityIconTypeForSecurityState(security_level);
+  return GetLocationBarSecurityIcon(iconType);
+}
+
+#pragma mark - Legacy utils.
+
+int GetIconForAutocompleteMatchType(AutocompleteMatchType::Type type,
+                                    bool is_starred,
+                                    bool is_incognito) {
+  if (is_starred)
+    return is_incognito ? IDR_IOS_OMNIBOX_STAR_INCOGNITO : IDR_IOS_OMNIBOX_STAR;
+
+  switch (type) {
+    case AutocompleteMatchType::BOOKMARK_TITLE:
+    case AutocompleteMatchType::CLIPBOARD:
+    case AutocompleteMatchType::NAVSUGGEST:
+    case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED:
+    case AutocompleteMatchType::PHYSICAL_WEB_DEPRECATED:
+    case AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW_DEPRECATED:
+    case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
+      return is_incognito ? IDR_IOS_OMNIBOX_HTTP_INCOGNITO
+                          : IDR_IOS_OMNIBOX_HTTP;
+    case AutocompleteMatchType::HISTORY_BODY:
+    case AutocompleteMatchType::HISTORY_KEYWORD:
+    case AutocompleteMatchType::HISTORY_TITLE:
+    case AutocompleteMatchType::HISTORY_URL:
+    case AutocompleteMatchType::SEARCH_HISTORY:
+    case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
+      return is_incognito ? IDR_IOS_OMNIBOX_HISTORY_INCOGNITO
+                          : IDR_IOS_OMNIBOX_HISTORY;
+    case AutocompleteMatchType::CONTACT_DEPRECATED:
+    case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
+    case AutocompleteMatchType::SEARCH_SUGGEST:
+    case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
+    case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
+    case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE:
+    case AutocompleteMatchType::SEARCH_SUGGEST_TAIL:
+    case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
+    case AutocompleteMatchType::VOICE_SUGGEST:
+      return is_incognito ? IDR_IOS_OMNIBOX_SEARCH_INCOGNITO
+                          : IDR_IOS_OMNIBOX_SEARCH;
+    case AutocompleteMatchType::CALCULATOR:
+      // Calculator answers are never shown in incognito mode because input is
+      // never sent to the search provider.
+      DCHECK(!is_incognito);
+      return IDR_IOS_OMNIBOX_CALCULATOR;
+    case AutocompleteMatchType::DOCUMENT_SUGGESTION:
+      // Document suggeestions aren't yet supported on mobile.
+      NOTREACHED();
+      return IDR_IOS_OMNIBOX_HTTP;
+    case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
+    case AutocompleteMatchType::NUM_TYPES:
+      NOTREACHED();
+      return IDR_IOS_OMNIBOX_HTTP;
+  }
+}
+
+int GetIconForSecurityState(security_state::SecurityLevel security_level) {
+  switch (security_level) {
+    case security_state::NONE:
+    case security_state::HTTP_SHOW_WARNING:
+      return IDR_IOS_OMNIBOX_HTTP;
+    case security_state::EV_SECURE:
+    case security_state::SECURE:
+      return IDR_IOS_OMNIBOX_HTTPS_VALID;
+    case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
+      return IDR_IOS_OMNIBOX_HTTPS_POLICY_WARNING;
+    case security_state::DANGEROUS:
+      return IDR_IOS_OMNIBOX_HTTPS_INVALID;
+    case security_state::SECURITY_LEVEL_COUNT:
+      NOTREACHED();
+      return IDR_IOS_OMNIBOX_HTTP;
+  }
+}
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
index 7bf2e3b..13f5777c 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
@@ -24,7 +24,7 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h"
 #include "ios/chrome/browser/ui/omnibox/omnibox_text_field_paste_delegate.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #include "ios/chrome/browser/ui/omnibox/web_omnibox_edit_controller.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
index 923e0a7..752381de 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -9,7 +9,7 @@
 #include "base/ios/ios_util.h"
 #include "ios/chrome/browser/ui/animation_util.h"
 #import "ios/chrome/browser/ui/omnibox/image_retriever.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h"
 #import "ios/chrome/browser/ui/omnibox/popup/self_sizing_table_view.h"
 #import "ios/chrome/browser/ui/omnibox/truncating_attributed_label.h"
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.mm
index ae2ace4..3456eb63 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.mm
@@ -18,7 +18,7 @@
 #include "components/open_from_clipboard/clipboard_recent_content.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/experimental_flags.h"
-#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h"
 #include "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_suggestions_delegate.h"
 #include "ios/chrome/browser/ui/ui_util.h"
diff --git a/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
index 5014734f..8316784 100644
--- a/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
@@ -11,6 +11,7 @@
 #import "ios/chrome/test/app/histogram_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/web/public/web_client.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -777,8 +778,19 @@
       buckets[0].min & JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS, @"");
   GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW, @"");
   GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_COMPLETED, @"");
-  GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
-  GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+
+  // TODO(crbug.com/676129): LegacyNavigationManager has a bug that doesn't
+  // create a pending item when reloading the page. This is incorrectly causing
+  // the second navigation above to be considered a renderer-initiated
+  // navigation, and the abort reason is incorrectly logged as
+  // EVENT_OTHER_ABORTED.
+  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+    GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
+    GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+  } else {
+    GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
+    GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+  }
   GREYAssertTrue(
       buckets[0].min & JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT, @"");
   GREYAssertTrue(
@@ -828,8 +840,18 @@
       buckets[0].min & JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS, @"");
   GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW, @"");
   GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_COMPLETED, @"");
-  GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
-  GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+  // TODO(crbug.com/676129): LegacyNavigationManager has a bug that doesn't
+  // create a pending item when reloading the page. This is incorrectly causing
+  // the second navigation above to be considered a renderer-initiated
+  // navigation, and the abort reason is incorrectly logged as
+  // EVENT_OTHER_ABORTED.
+  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+    GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
+    GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+  } else {
+    GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
+    GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+  }
   GREYAssertFalse(
       buckets[0].min & JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT, @"");
   GREYAssertFalse(
@@ -880,8 +902,18 @@
       buckets[0].min & JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS, @"");
   GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW, @"");
   GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_COMPLETED, @"");
-  GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
-  GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+  // TODO(crbug.com/676129): LegacyNavigationManager has a bug that doesn't
+  // create a pending item when reloading the page. This is incorrectly causing
+  // the second navigation above to be considered a renderer-initiated
+  // navigation, and the abort reason is incorrectly logged as
+  // EVENT_OTHER_ABORTED.
+  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+    GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
+    GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+  } else {
+    GREYAssertTrue(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED, @"");
+    GREYAssertFalse(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED, @"");
+  }
   GREYAssertFalse(
       buckets[0].min & JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT, @"");
   GREYAssertFalse(
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index aaa600d1..08c80676a0 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -344,7 +344,7 @@
     ReadingListAddCommand* command = [[ReadingListAddCommand alloc]
         initWithURL:web_state->GetVisibleURL()
               title:base::SysUTF16ToNSString(web_state->GetTitle())];
-    [chrome_test_util::DispatcherForActiveViewController()
+    [chrome_test_util::DispatcherForActiveBrowserViewController()
         addToReadingList:command];
   } else {
     [ChromeEarlGreyUI openShareMenu];
diff --git a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
index 261d593e9..fbfd969 100644
--- a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
@@ -786,80 +786,6 @@
       performAction:grey_tap()];
 }
 
-// Checks that deleting a duplicated blacklisted form from password details view
-// goes back to the list-of-passwords view which doesn't display that form
-// anymore.
-// TODO(crbug.com/871223): Disabled because the PasswordStore no longer allows
-// adding duplicate blacklisted forms.
-- (void)DISABLED_testDuplicatedBlacklistedFormDeletionInDetailView {
-  // Save blacklisted form to be deleted later.
-  PasswordForm blacklisted;
-  blacklisted.origin = GURL("https://blacklisted.com");
-  blacklisted.signon_realm = blacklisted.origin.spec();
-  blacklisted.blacklisted_by_user = true;
-  SaveToPasswordStore(blacklisted);
-  // Save duplicate of the previously saved form to be deleted at the same time.
-  // This entry is considered duplicated because it maps to the same sort key
-  // as the previous one.
-  PasswordForm blacklistedDuplicate;
-  blacklistedDuplicate.origin = GURL("https://blacklisted.com/blacklisted");
-  blacklistedDuplicate.signon_realm = blacklisted.origin.spec();
-  blacklistedDuplicate.blacklisted_by_user = true;
-  SaveToPasswordStore(blacklistedDuplicate);
-
-  OpenPasswordSettings();
-
-  [GetInteractionForPasswordEntry(@"blacklisted.com") performAction:grey_tap()];
-
-  [GetInteractionForPasswordDetailItem(DeleteButton())
-      performAction:grey_tap()];
-
-  // Tap the alert's Delete button to confirm. Check accessibilityTrait to
-  // differentiate against the above DeleteButton()-matching element, which is
-  // has UIAccessibilityTraitSelected.
-  // TODO(crbug.com/751311): Revisit and check if there is a better solution to
-  // match the Delete button.
-  id<GREYMatcher> deleteConfirmationButton = grey_allOf(
-      ButtonWithAccessibilityLabel(
-          l10n_util::GetNSString(IDS_IOS_CONFIRM_PASSWORD_DELETION)),
-      grey_not(grey_accessibilityTrait(UIAccessibilityTraitSelected)), nil);
-  [[EarlGrey selectElementWithMatcher:deleteConfirmationButton]
-      performAction:grey_tap()];
-
-  // Wait until the alert and the detail view are dismissed.
-  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
-
-  // Check that the current view is now the list view, by locating the header
-  // of the list of passwords.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_allOf(grey_accessibilityLabel(l10n_util::GetNSString(
-                                IDS_IOS_SETTINGS_PASSWORDS_EXCEPTIONS_HEADING)),
-                            grey_accessibilityTrait(UIAccessibilityTraitHeader),
-                            nullptr)] assertWithMatcher:grey_notNil()];
-
-  // Verify that the deletion was propagated to the PasswordStore.
-  TestStoreConsumer consumer;
-  GREYAssert(consumer.GetStoreResults().empty(),
-             @"Stored password was not removed from PasswordStore.");
-
-  // Also verify that the removed password is no longer in the list.
-  [GetInteractionForPasswordEntry(@"secret.com")
-      assertWithMatcher:grey_not(grey_sufficientlyVisible())];
-
-  // Finally, verify that the Edit button is visible and disabled, because there
-  // are no other password entries left for deletion via the "Edit" mode.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON)]
-      assertWithMatcher:grey_allOf(grey_sufficientlyVisible(),
-                                   grey_not(grey_enabled()), nil)];
-
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
-      performAction:grey_tap()];
-}
-
 // Checks that deleting a password from password details can be cancelled.
 - (void)testCancelDeletionInDetailView {
   // Save form to be deleted later.
diff --git a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm
index ba0b402b..f9278c4 100644
--- a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm
+++ b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm
@@ -325,7 +325,7 @@
   // Open new tab to cancel sign-in.
   OpenNewTabCommand* command =
       [OpenNewTabCommand commandWithURLFromChrome:GURL("about:blank")];
-  [chrome_test_util::DispatcherForActiveViewController()
+  [chrome_test_util::DispatcherForActiveBrowserViewController()
       openURLInNewTab:command];
 
   // Re-open the sign-in screen. If it wasn't correctly dismissed previously,
@@ -367,7 +367,7 @@
   // Open new tab to cancel sign-in.
   OpenNewTabCommand* command =
       [OpenNewTabCommand commandWithURLFromChrome:GURL("about:blank")];
-  [chrome_test_util::DispatcherForActiveViewController()
+  [chrome_test_util::DispatcherForActiveBrowserViewController()
       openURLInNewTab:command];
 
   // Re-open the sign-in screen. If it wasn't correctly dismissed previously,
@@ -427,7 +427,7 @@
   // Open new tab to cancel sign-in.
   OpenNewTabCommand* command =
       [OpenNewTabCommand commandWithURLFromChrome:GURL("about:blank")];
-  [chrome_test_util::DispatcherForActiveViewController()
+  [chrome_test_util::DispatcherForActiveBrowserViewController()
       openURLInNewTab:command];
 
   // Re-open the sign-in screen. If it wasn't correctly dismissed previously,
@@ -470,7 +470,7 @@
   // Open new tab to cancel sign-in.
   OpenNewTabCommand* command =
       [OpenNewTabCommand commandWithURLFromChrome:GURL("about:blank")];
-  [chrome_test_util::DispatcherForActiveViewController()
+  [chrome_test_util::DispatcherForActiveBrowserViewController()
       openURLInNewTab:command];
 
   // Re-open the sign-in screen. If it wasn't correctly dismissed previously,
diff --git a/ios/chrome/browser/ui/stack_view/stack_view_controller.h b/ios/chrome/browser/ui/stack_view/stack_view_controller.h
index c2a00a7..934bb2f 100644
--- a/ios/chrome/browser/ui/stack_view/stack_view_controller.h
+++ b/ios/chrome/browser/ui/stack_view/stack_view_controller.h
@@ -11,6 +11,7 @@
 #include "ui/base/page_transition_types.h"
 
 @protocol ApplicationCommands;
+@protocol BrowserCommands;
 @class Tab;
 @class TabModel;
 
@@ -29,6 +30,13 @@
 
 @property(nonatomic, weak) id<StackViewControllerTestDelegate> testDelegate;
 
+// Dispatcher for anything that acts in a "browser" role, with the
+// |BrowserCommands| added. It is an extension of the |dispatcher| property
+// defined by the TabSwitcher protocol.
+@property(nonatomic, readonly)
+    id<ApplicationCommands, BrowserCommands, OmniboxFocuser, ToolbarCommands>
+        dispatcher;
+
 // Initializes with the given tab models, which must not be nil.
 // |activeTabModel| is the model which starts active, and must be one of the
 // other two models.
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.h b/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.h
index d660e420..d73966b 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.h
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.h
@@ -23,7 +23,7 @@
 // Dispatcher object this adaptor will expose as the dispacther for the
 // TabSwitcher protocol.
 @property(nonatomic, weak)
-    id<ApplicationCommands, BrowserCommands, OmniboxFocuser, ToolbarCommands>
+    id<ApplicationCommands, OmniboxFocuser, ToolbarCommands>
         adaptedDispatcher;
 // Object that can set the current page of the tab grid.
 @property(nonatomic, weak) id<TabGridPaging> tabGridPager;
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.mm
index 1579300..f6df447b 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.mm
@@ -30,10 +30,9 @@
 
 #pragma mark - TabSwitcher
 
-- (id<ApplicationCommands, BrowserCommands, OmniboxFocuser, ToolbarCommands>)
-    dispatcher {
-  return static_cast<id<ApplicationCommands, BrowserCommands, OmniboxFocuser,
-                        ToolbarCommands>>(self.adaptedDispatcher);
+- (id<ApplicationCommands, OmniboxFocuser, ToolbarCommands>)dispatcher {
+  return static_cast<id<ApplicationCommands, OmniboxFocuser, ToolbarCommands>>(
+      self.adaptedDispatcher);
 }
 
 - (void)setAnimationDelegate:
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
index 5c7becd..8f99b7d 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
@@ -9,6 +9,7 @@
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
@@ -85,8 +86,6 @@
         (id<ApplicationCommands>)applicationCommandEndpoint {
   if ((self = [super initWithWindow:window])) {
     _dispatcher = [[CommandDispatcher alloc] init];
-    [_dispatcher startDispatchingToTarget:self
-                              forProtocol:@protocol(BrowserCommands)];
     [_dispatcher startDispatchingToTarget:applicationCommandEndpoint
                               forProtocol:@protocol(ApplicationCommands)];
     // -startDispatchingToTarget:forProtocol: doesn't pick up protocols the
@@ -171,13 +170,18 @@
   self.adaptor = [[TabGridAdaptor alloc] init];
   self.adaptor.tabGridViewController = self.mainViewController;
   self.adaptor.adaptedDispatcher =
-      static_cast<id<ApplicationCommands, BrowserCommands, OmniboxFocuser,
-                     ToolbarCommands>>(self.dispatcher);
+      static_cast<id<ApplicationCommands, OmniboxFocuser, ToolbarCommands>>(
+          self.dispatcher);
   self.adaptor.tabGridPager = mainViewController;
 
   self.regularTabsMediator = [[TabGridMediator alloc]
       initWithConsumer:mainViewController.regularTabsConsumer];
   self.regularTabsMediator.tabModel = _regularTabModel;
+  if (_regularTabModel.browserState) {
+    self.regularTabsMediator.tabRestoreService =
+        IOSChromeTabRestoreServiceFactory::GetForBrowserState(
+            _regularTabModel.browserState);
+  }
   self.incognitoTabsMediator = [[TabGridMediator alloc]
       initWithConsumer:mainViewController.incognitoTabsConsumer];
   self.incognitoTabsMediator.tabModel = _incognitoTabModel;
@@ -241,7 +245,6 @@
 }
 
 - (void)stop {
-  [self.dispatcher stopDispatchingForProtocol:@protocol(BrowserCommands)];
   [self.dispatcher stopDispatchingForProtocol:@protocol(ApplicationCommands)];
   [self.dispatcher
       stopDispatchingForProtocol:@protocol(ApplicationSettingsCommands)];
@@ -365,22 +368,6 @@
                             focusOmnibox:focusOmnibox];
 }
 
-#pragma mark - BrowserCommands
-
-- (void)openNewTab:(OpenNewTabCommand*)command {
-  DCHECK(self.regularTabModel && self.incognitoTabModel);
-  TabModel* activeTabModel =
-      command.inIncognito ? self.incognitoTabModel : self.regularTabModel;
-  // TODO(crbug.com/804587) : It is better to use the mediator to insert a
-  // webState and show the active tab.
-  DCHECK(self.tabSwitcher);
-  [self.tabSwitcher
-      dismissWithNewTabAnimationToModel:activeTabModel
-                                withURL:GURL(kChromeUINewTabURL)
-                                atIndex:NSNotFound
-                             transition:ui::PAGE_TRANSITION_TYPED];
-}
-
 #pragma mark - RecentTabsHandsetViewControllerCommand
 
 - (void)dismissRecentTabs {
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h
index 6e2618d..b35f5e30 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h
@@ -13,11 +13,17 @@
 @protocol GridConsumer;
 @class TabModel;
 
+namespace sessions {
+class TabRestoreService;
+}  // namespace sessions
+
 // Mediates between model layer and tab grid UI layer.
 @interface TabGridMediator : NSObject<GridCommands, GridImageDataSource>
 
 // The source tab model.
 @property(nonatomic, weak) TabModel* tabModel;
+// TabRestoreService holds the recently closed tabs.
+@property(nonatomic, assign) sessions::TabRestoreService* tabRestoreService;
 
 // Initializer with |consumer| as the receiver of model layer updates.
 - (instancetype)initWithConsumer:(id<GridConsumer>)consumer
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
index 4ef5541..1ac28ec 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
@@ -14,7 +14,6 @@
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/chrome_url_util.h"
 #include "ios/chrome/browser/experimental_flags.h"
-#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_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"
@@ -118,6 +117,7 @@
 
 // Public properties.
 @synthesize tabModel = _tabModel;
+@synthesize tabRestoreService = _tabRestoreService;
 // Private properties.
 @synthesize webStateList = _webStateList;
 @synthesize consumer = _consumer;
@@ -305,6 +305,7 @@
                           createParams));
   self.closedSessionWindow = nil;
   [self removeEntriesFromTabRestoreService];
+  self.closedTabsCount = 0;
   // Unmark all images for deletion since they are now active tabs again.
   ios::ChromeBrowserState* browserState = self.tabModel.browserState;
   [SnapshotCacheFactory::GetForBrowserState(browserState) unmarkAllImages];
@@ -378,20 +379,19 @@
 // Removes |self.closedTabsCount| most recent entries from the
 // TabRestoreService.
 - (void)removeEntriesFromTabRestoreService {
-  sessions::TabRestoreService* tabRestoreService =
-      IOSChromeTabRestoreServiceFactory::GetForBrowserState(
-          self.tabModel.browserState);
+  if (!self.tabRestoreService) {
+    return;
+  }
   std::vector<SessionID> identifiers;
-  auto iter = tabRestoreService->entries().begin();
-  auto end = tabRestoreService->entries().end();
+  auto iter = self.tabRestoreService->entries().begin();
+  auto end = self.tabRestoreService->entries().end();
   for (int i = 0; i < self.closedTabsCount && iter != end; i++) {
     identifiers.push_back(iter->get()->id);
     iter++;
   }
   for (const SessionID sessionID : identifiers) {
-    tabRestoreService->RemoveTabEntryById(sessionID);
+    self.tabRestoreService->RemoveTabEntryById(sessionID);
   }
-  self.closedTabsCount = 0;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
index 22c1e21..533fc18 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
@@ -272,9 +272,7 @@
 
 // Tests that the |web_state_list_| is restored to 3 items when
 // |-undoCloseAllItems| is called.
-// TODO(crbug.com/873632): This test is flaky.
-// TODO(crbug.com/873633): What introduced flakyness was cherry picked in M-69.
-TEST_F(TabGridMediatorTest, DISABLED_UndoCloseAllItemsCommand) {
+TEST_F(TabGridMediatorTest, UndoCloseAllItemsCommand) {
   // Previously there were 3 items.
   [mediator_ saveAndCloseAllItems];
   [mediator_ undoCloseAllItems];
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher.h b/ios/chrome/browser/ui/tab_switcher/tab_switcher.h
index 8b1da3d..1deaf82 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher.h
@@ -8,7 +8,6 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/commands/application_commands.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #include "ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_context.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
@@ -65,7 +64,7 @@
 
 // Dispatcher for anything that acts in a "browser" role.
 @property(nonatomic, readonly)
-    id<ApplicationCommands, BrowserCommands, OmniboxFocuser, ToolbarCommands>
+    id<ApplicationCommands, OmniboxFocuser, ToolbarCommands>
         dispatcher;
 
 // Restores the internal state of the tab switcher with the given tab models,
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h
index a2dd83cd..307c207 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
 
 @protocol ApplicationCommands;
+@protocol BrowserCommands;
 
 namespace ios {
 class ChromeBrowserState;
@@ -23,6 +24,13 @@
                       activeTabModel:(TabModel*)activeTabModel
           applicationCommandEndpoint:(id<ApplicationCommands>)endpoint;
 
+// Dispatcher for anything that acts in a "browser" role, with the
+// |BrowserCommands| added. It is an extension of the |dispatcher| property
+// defined by the TabSwitcher protocol.
+@property(nonatomic, readonly)
+    id<ApplicationCommands, BrowserCommands, OmniboxFocuser, ToolbarCommands>
+        dispatcher;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_SWITCHER_CONTROLLER_H_
diff --git a/ios/chrome/test/app/chrome_test_util.h b/ios/chrome/test/app/chrome_test_util.h
index 5dd473d..8875c17 100644
--- a/ios/chrome/test/app/chrome_test_util.h
+++ b/ios/chrome/test/app/chrome_test_util.h
@@ -53,8 +53,15 @@
 UIViewController* GetActiveViewController();
 
 // Returns the dispatcher for the active view controller.
+// DEPRECATED. Please use DispatcherForActiveBrowserViewController.
+// TODO(crbug.com/800266): Remove this.
 id<ApplicationCommands, BrowserCommands> DispatcherForActiveViewController();
 
+// Returns the dispatcher for the active BrowserViewController. If the
+// BrowserViewController isn't presented, returns nil.
+id<ApplicationCommands, BrowserCommands>
+DispatcherForActiveBrowserViewController();
+
 // Removes all presented infobars.
 void RemoveAllInfoBars();
 
diff --git a/ios/chrome/test/app/chrome_test_util.mm b/ios/chrome/test/app/chrome_test_util.mm
index 2c39f702..7fecf28 100644
--- a/ios/chrome/test/app/chrome_test_util.mm
+++ b/ios/chrome/test/app/chrome_test_util.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/test/app/chrome_test_util.h"
 
+#include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
@@ -25,6 +26,7 @@
 #import "ios/chrome/browser/ui/main/view_controller_swapping.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/test/app/navigation_test_util.h"
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/web/public/navigation_manager.h"
@@ -144,6 +146,7 @@
 }
 
 id<ApplicationCommands, BrowserCommands> DispatcherForActiveViewController() {
+  DCHECK(!IsUIRefreshPhase1Enabled());
   UIViewController* vc = GetActiveViewController();
   BrowserViewController* bvc = base::mac::ObjCCast<BrowserViewController>(vc);
   if (bvc)
@@ -152,7 +155,8 @@
     // In stack_view and the iPad tab switcher, the view controller has a
     // dispatcher.
     id<TabSwitcher> tabSwitcher = static_cast<id<TabSwitcher>>(vc);
-    return tabSwitcher.dispatcher;
+    return static_cast<id<ApplicationCommands, BrowserCommands>>(
+        tabSwitcher.dispatcher);
   }
   // In tab grid, the TabSwitcher object is not in the view hierarchy so it must
   // be gotten through the MainController.
@@ -160,6 +164,13 @@
       GetMainController().tabSwitcher.dispatcher);
 }
 
+id<ApplicationCommands, BrowserCommands>
+DispatcherForActiveBrowserViewController() {
+  UIViewController* vc = GetActiveViewController();
+  BrowserViewController* bvc = base::mac::ObjCCast<BrowserViewController>(vc);
+  return bvc.dispatcher;
+}
+
 void RemoveAllInfoBars() {
   web::WebState* webState = [GetCurrentTab() webState];
   if (webState) {
diff --git a/ios/chrome/test/app/tab_test_util.mm b/ios/chrome/test/app/tab_test_util.mm
index d55f7cb..698e135a 100644
--- a/ios/chrome/test/app/tab_test_util.mm
+++ b/ios/chrome/test/app/tab_test_util.mm
@@ -9,6 +9,7 @@
 #import "base/mac/foundation_util.h"
 #import "base/test/ios/wait_util.h"
 #import "ios/chrome/app/main_controller_private.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/metrics/tab_usage_recorder.h"
 #import "ios/chrome/browser/tabs/tab.h"
@@ -16,6 +17,7 @@
 #import "ios/chrome/browser/ui/browser_view_controller.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
@@ -51,14 +53,50 @@
 void OpenNewTab() {
   @autoreleasepool {  // Make sure that all internals are deallocated.
     OpenNewTabCommand* command = [OpenNewTabCommand command];
-    [chrome_test_util::DispatcherForActiveViewController() openNewTab:command];
+    if (IsUIRefreshPhase1Enabled()) {
+      id<ApplicationCommands, BrowserCommands> BVCDispatcher =
+          chrome_test_util::DispatcherForActiveBrowserViewController();
+      if (BVCDispatcher) {
+        [BVCDispatcher openNewTab:command];
+        return;
+      }
+      // The TabGrid is currently presented.
+      [GetMainController().tabSwitcher
+          dismissWithNewTabAnimationToModel:[[GetMainController()
+                                                browserViewInformation]
+                                                mainTabModel]
+                                    withURL:GURL(kChromeUINewTabURL)
+                                    atIndex:NSNotFound
+                                 transition:ui::PAGE_TRANSITION_TYPED];
+    } else {
+      [chrome_test_util::DispatcherForActiveViewController()
+          openNewTab:command];
+    }
   }
 }
 
 void OpenNewIncognitoTab() {
   @autoreleasepool {  // Make sure that all internals are deallocated.
     OpenNewTabCommand* command = [OpenNewTabCommand incognitoTabCommand];
-    [chrome_test_util::DispatcherForActiveViewController() openNewTab:command];
+    if (IsUIRefreshPhase1Enabled()) {
+      id<ApplicationCommands, BrowserCommands> BVCDispatcher =
+          chrome_test_util::DispatcherForActiveBrowserViewController();
+      if (BVCDispatcher) {
+        [BVCDispatcher openNewTab:command];
+        return;
+      }
+      // The TabGrid is currently presented.
+      [GetMainController().tabSwitcher
+          dismissWithNewTabAnimationToModel:[[GetMainController()
+                                                browserViewInformation]
+                                                otrTabModel]
+                                    withURL:GURL(kChromeUINewTabURL)
+                                    atIndex:NSNotFound
+                                 transition:ui::PAGE_TRANSITION_TYPED];
+    } else {
+      [chrome_test_util::DispatcherForActiveViewController()
+          openNewTab:command];
+    }
   }
 }
 
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index 3b5e57d..90913182 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -124,6 +124,8 @@
     "audio_power_monitor.cc",
     "audio_power_monitor.h",
     "audio_processing.h",
+    "audio_sink_parameters.cc",
+    "audio_sink_parameters.h",
     "audio_source_diverter.h",
     "audio_sync_reader.cc",
     "audio_sync_reader.h",
diff --git a/media/audio/audio_output_device.cc b/media/audio/audio_output_device.cc
index 09c7063..8598166 100644
--- a/media/audio/audio_output_device.cc
+++ b/media/audio/audio_output_device.cc
@@ -29,15 +29,15 @@
 AudioOutputDevice::AudioOutputDevice(
     std::unique_ptr<AudioOutputIPC> ipc,
     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
-    int session_id,
-    const std::string& device_id,
+    const AudioSinkParameters& sink_params,
     base::TimeDelta authorization_timeout)
     : io_task_runner_(io_task_runner),
       callback_(NULL),
       ipc_(std::move(ipc)),
       state_(IDLE),
-      session_id_(session_id),
-      device_id_(device_id),
+      session_id_(sink_params.session_id),
+      device_id_(sink_params.device_id),
+      processing_id_(sink_params.processing_id),
       stopping_hack_(false),
       did_receive_auth_(base::WaitableEvent::ResetPolicy::MANUAL,
                         base::WaitableEvent::InitialState::NOT_SIGNALED),
@@ -182,7 +182,7 @@
   if (state_ == IDLE && !(did_receive_auth_.IsSignaled() && device_id_.empty()))
     RequestDeviceAuthorizationOnIOThread();
 
-  ipc_->CreateStream(this, audio_parameters_);
+  ipc_->CreateStream(this, audio_parameters_ /* TODO(ossu):, processing_id_ */);
   // By default, start playing right away.
   ipc_->PlayStream();
   state_ = STREAM_CREATION_REQUESTED;
diff --git a/media/audio/audio_output_device.h b/media/audio/audio_output_device.h
index fd42339..be9a5ff 100644
--- a/media/audio/audio_output_device.h
+++ b/media/audio/audio_output_device.h
@@ -73,6 +73,7 @@
 #include "base/time/time.h"
 #include "media/audio/audio_device_thread.h"
 #include "media/audio/audio_output_ipc.h"
+#include "media/audio/audio_sink_parameters.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/audio_renderer_sink.h"
 #include "media/base/media_export.h"
@@ -93,8 +94,7 @@
   AudioOutputDevice(
       std::unique_ptr<AudioOutputIPC> ipc,
       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
-      int session_id,
-      const std::string& device_id,
+      const AudioSinkParameters& sink_params,
       base::TimeDelta authorization_timeout);
 
   // Request authorization to use the device specified in the constructor.
@@ -201,6 +201,8 @@
   // received in OnDeviceAuthorized().
   std::string matched_device_id_;
 
+  base::Optional<base::UnguessableToken> processing_id_;
+
   // In order to avoid a race between OnStreamCreated and Stop(), we use this
   // guard to control stopping and starting the audio thread.
   base::Lock audio_thread_lock_;
diff --git a/media/audio/audio_output_device_unittest.cc b/media/audio/audio_output_device_unittest.cc
index 9579550..f172c30 100644
--- a/media/audio/audio_output_device_unittest.cc
+++ b/media/audio/audio_output_device_unittest.cc
@@ -138,9 +138,9 @@
     StopAudioDevice();
 
   audio_output_ipc_ = new NiceMock<MockAudioOutputIPC>();
-  audio_device_ = new AudioOutputDevice(base::WrapUnique(audio_output_ipc_),
-                                        task_env_.GetMainThreadTaskRunner(), 0,
-                                        device_id, kAuthTimeout);
+  audio_device_ = new AudioOutputDevice(
+      base::WrapUnique(audio_output_ipc_), task_env_.GetMainThreadTaskRunner(),
+      AudioSinkParameters(0, device_id), kAuthTimeout);
 }
 
 void AudioOutputDeviceTest::SetDevice(const std::string& device_id) {
@@ -294,9 +294,9 @@
   // Clear audio device set by fixture.
   StopAudioDevice();
   audio_output_ipc_ = new NiceMock<MockAudioOutputIPC>();
-  audio_device_ = new AudioOutputDevice(base::WrapUnique(audio_output_ipc_),
-                                        task_env_.GetMainThreadTaskRunner(), 0,
-                                        kDefaultDeviceId, kAuthTimeout);
+  audio_device_ = new AudioOutputDevice(
+      base::WrapUnique(audio_output_ipc_), task_env_.GetMainThreadTaskRunner(),
+      AudioSinkParameters(0, kDefaultDeviceId), kAuthTimeout);
   EXPECT_CALL(
       *audio_output_ipc_,
       RequestDeviceAuthorization(audio_device_.get(), 0, kDefaultDeviceId));
@@ -380,8 +380,8 @@
   TestEnvironment env(params);
   auto* ipc = new MockAudioOutputIPC();  // owned by |audio_device|.
   auto audio_device = base::MakeRefCounted<AudioOutputDevice>(
-      base::WrapUnique(ipc), task_env_.GetMainThreadTaskRunner(), 0,
-      kDefaultDeviceId, kAuthTimeout);
+      base::WrapUnique(ipc), task_env_.GetMainThreadTaskRunner(),
+      AudioSinkParameters(0, kDefaultDeviceId), kAuthTimeout);
 
   // Start a stream.
   audio_device->RequestDeviceAuthorization();
@@ -442,8 +442,8 @@
   TestEnvironment env(params);
   auto* ipc = new MockAudioOutputIPC();  // owned by |audio_device|.
   auto audio_device = base::MakeRefCounted<AudioOutputDevice>(
-      base::WrapUnique(ipc), task_env_.GetMainThreadTaskRunner(), 0,
-      kNonDefaultDeviceId, kAuthTimeout);
+      base::WrapUnique(ipc), task_env_.GetMainThreadTaskRunner(),
+      AudioSinkParameters(0, kNonDefaultDeviceId), kAuthTimeout);
 
   audio_device->RequestDeviceAuthorization();
   audio_device->Initialize(params, &env.callback);
@@ -477,8 +477,8 @@
   TestEnvironment env(params);
   auto* ipc = new MockAudioOutputIPC();  // owned by |audio_device|.
   auto audio_device = base::MakeRefCounted<AudioOutputDevice>(
-      base::WrapUnique(ipc), task_env_.GetMainThreadTaskRunner(), 0,
-      kNonDefaultDeviceId, kAuthTimeout);
+      base::WrapUnique(ipc), task_env_.GetMainThreadTaskRunner(),
+      AudioSinkParameters(0, kNonDefaultDeviceId), kAuthTimeout);
 
   // Start a stream.
   audio_device->RequestDeviceAuthorization();
diff --git a/media/audio/audio_sink_parameters.cc b/media/audio/audio_sink_parameters.cc
new file mode 100644
index 0000000..2d0b4e8
--- /dev/null
+++ b/media/audio/audio_sink_parameters.cc
@@ -0,0 +1,17 @@
+// 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 "media/audio/audio_sink_parameters.h"
+
+namespace media {
+
+AudioSinkParameters::AudioSinkParameters() = default;
+AudioSinkParameters::AudioSinkParameters(int session_id,
+                                         const std::string& device_id)
+    : session_id(session_id), device_id(device_id) {}
+AudioSinkParameters::AudioSinkParameters(const AudioSinkParameters& params) =
+    default;
+AudioSinkParameters::~AudioSinkParameters() = default;
+
+}  // namespace media
diff --git a/media/audio/audio_sink_parameters.h b/media/audio/audio_sink_parameters.h
new file mode 100644
index 0000000..88e3fab2
--- /dev/null
+++ b/media/audio/audio_sink_parameters.h
@@ -0,0 +1,38 @@
+// 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 MEDIA_AUDIO_AUDIO_SINK_PARAMETERS_H_
+#define MEDIA_AUDIO_AUDIO_SINK_PARAMETERS_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "base/unguessable_token.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// The set of parameters used to create an AudioOutputDevice.
+// |session_id| and |device_id| are used to select which device to
+// use. |device_id| is preferred over |session_id| if both are set
+// (i.e. session_id is nonzero).  If neither is set, the default output device
+// will be selected. This is the state when default constructed.
+// If the optional |processing_id| is provided, it is used to indicate that this
+// stream is to be used as the reference signal during audio processing. An
+// audio source must be constructed with the same processing id to complete the
+// association.
+struct MEDIA_EXPORT AudioSinkParameters final {
+  AudioSinkParameters();
+  AudioSinkParameters(int session_id, const std::string& device_id);
+  AudioSinkParameters(const AudioSinkParameters& params);
+  ~AudioSinkParameters();
+
+  int session_id = 0;
+  std::string device_id;
+  base::Optional<base::UnguessableToken> processing_id;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SINK_PARAMETERS_H_
diff --git a/media/video/gpu_memory_buffer_video_frame_pool.cc b/media/video/gpu_memory_buffer_video_frame_pool.cc
index ff6c203..8b04a0c 100644
--- a/media/video/gpu_memory_buffer_video_frame_pool.cc
+++ b/media/video/gpu_memory_buffer_video_frame_pool.cc
@@ -251,6 +251,12 @@
     case GpuVideoAcceleratorFactories::OutputFormat::XB30:
       DCHECK_EQ(0u, plane);
       return gfx::BufferFormat::RGBX_1010102;
+    case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
+      DCHECK_EQ(0u, plane);
+      return gfx::BufferFormat::RGBA_8888;
+    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
+      DCHECK_EQ(0u, plane);
+      return gfx::BufferFormat::BGRA_8888;
     case GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED:
       NOTREACHED();
       break;
@@ -279,6 +285,12 @@
       // Technically speaking we should say GL_RGB10_EXT, but that format is not
       // supported in OpenGLES.
       return GL_RGB10_A2_EXT;
+    case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
+      DCHECK_EQ(0u, plane);
+      return GL_RGBA;
+    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
+      DCHECK_EQ(0u, plane);
+      return GL_BGRA_EXT;
     case GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED:
       NOTREACHED();
       break;
@@ -291,6 +303,8 @@
   switch (format) {
     case GpuVideoAcceleratorFactories::OutputFormat::I420:
     case GpuVideoAcceleratorFactories::OutputFormat::UYVY:
+    case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
+    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
       return 1;
     case GpuVideoAcceleratorFactories::OutputFormat::NV12_DUAL_GMB:
     case GpuVideoAcceleratorFactories::OutputFormat::NV12_SINGLE_GMB:
@@ -318,6 +332,8 @@
     case GpuVideoAcceleratorFactories::OutputFormat::XR30:
       return PIXEL_FORMAT_ARGB;
     case GpuVideoAcceleratorFactories::OutputFormat::XB30:
+    case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
+    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
       return PIXEL_FORMAT_RGB32;
     case GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED:
       NOTREACHED();
@@ -340,6 +356,9 @@
     case GpuVideoAcceleratorFactories::OutputFormat::XR30:
     case GpuVideoAcceleratorFactories::OutputFormat::XB30:
       return 1;
+    case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
+    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
+      return 1;
     case GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED:
       NOTREACHED();
       break;
@@ -414,7 +433,8 @@
   DCHECK_LE(bytes_per_row, std::abs(dest_stride_y));
   DCHECK_LE(bytes_per_row, std::abs(dest_stride_uv));
   DCHECK_EQ(0, first_row % 2);
-
+  DCHECK(source_frame->format() == PIXEL_FORMAT_I420 ||
+         source_frame->format() == PIXEL_FORMAT_YV12);
   libyuv::I420ToNV12(
       source_frame->visible_data(VideoFrame::kYPlane) +
           first_row * source_frame->stride(VideoFrame::kYPlane),
@@ -447,6 +467,8 @@
   DCHECK_NE(dest_stride, 0);
   DCHECK_LE(width, std::abs(dest_stride / 2));
   DCHECK_EQ(0, first_row % 2);
+  DCHECK(source_frame->format() == PIXEL_FORMAT_I420 ||
+         source_frame->format() == PIXEL_FORMAT_YV12);
   libyuv::I420ToUYVY(
       source_frame->visible_data(VideoFrame::kYPlane) +
           first_row * source_frame->stride(VideoFrame::kYPlane),
@@ -477,6 +499,7 @@
   DCHECK_NE(dest_stride, 0);
   DCHECK_LE(width, std::abs(dest_stride / 2));
   DCHECK_EQ(0, first_row % 2);
+  DCHECK_EQ(source_frame->format(), PIXEL_FORMAT_YUV420P10);
 
   const uint16_t* y_plane = reinterpret_cast<const uint16_t*>(
       source_frame->visible_data(VideoFrame::kYPlane) +
@@ -518,6 +541,46 @@
   }
 }
 
+void CopyRowsToRGBABuffer(bool is_rgba,
+                          int first_row,
+                          int rows,
+                          int width,
+                          const scoped_refptr<VideoFrame>& source_frame,
+                          uint8_t* output,
+                          int dest_stride,
+                          base::OnceClosure done) {
+  base::ScopedClosureRunner done_runner(std::move(done));
+  TRACE_EVENT2("media", "CopyRowsToRGBABuffer", "bytes_per_row", width * 2,
+               "rows", rows);
+
+  if (!output)
+    return;
+
+  DCHECK_NE(dest_stride, 0);
+  DCHECK_LE(width, std::abs(dest_stride / 2));
+  DCHECK_EQ(0, first_row % 2);
+  DCHECK_EQ(source_frame->format(), PIXEL_FORMAT_I420A);
+
+  // libyuv uses little-endian for RGBx formats, whereas here we use big endian.
+  auto* func_ptr = is_rgba ? libyuv::I420AlphaToABGR : libyuv::I420AlphaToARGB;
+
+  func_ptr(source_frame->visible_data(VideoFrame::kYPlane) +
+               first_row * source_frame->stride(VideoFrame::kYPlane),
+           source_frame->stride(VideoFrame::kYPlane),
+           source_frame->visible_data(VideoFrame::kUPlane) +
+               first_row / 2 * source_frame->stride(VideoFrame::kUPlane),
+           source_frame->stride(VideoFrame::kUPlane),
+           source_frame->visible_data(VideoFrame::kVPlane) +
+               first_row / 2 * source_frame->stride(VideoFrame::kVPlane),
+           source_frame->stride(VideoFrame::kVPlane),
+           source_frame->visible_data(VideoFrame::kAPlane) +
+               first_row * source_frame->stride(VideoFrame::kAPlane),
+           source_frame->stride(VideoFrame::kAPlane),
+           output + first_row * dest_stride, dest_stride, width, rows,
+           // Textures are expected to be premultiplied by GL and compositors.
+           1 /* attenuate, meaning premultiply */);
+}
+
 gfx::Size CodedSize(const scoped_refptr<VideoFrame>& video_frame,
                     GpuVideoAcceleratorFactories::OutputFormat output_format) {
   DCHECK(gfx::Rect(video_frame->coded_size())
@@ -535,6 +598,8 @@
     case GpuVideoAcceleratorFactories::OutputFormat::UYVY:
     case GpuVideoAcceleratorFactories::OutputFormat::XR30:
     case GpuVideoAcceleratorFactories::OutputFormat::XB30:
+    case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
+    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
       output = gfx::Size((video_frame->visible_rect().width() + 1) & ~1,
                          video_frame->visible_rect().height());
       break;
@@ -577,9 +642,9 @@
     case PIXEL_FORMAT_YUV420P9:
     case PIXEL_FORMAT_YUV420P10:
     case PIXEL_FORMAT_YUV420P12:
+    case PIXEL_FORMAT_I420A:
       break;
     // Unsupported cases.
-    case PIXEL_FORMAT_I420A:
     case PIXEL_FORMAT_I422:
     case PIXEL_FORMAT_I444:
     case PIXEL_FORMAT_NV12:
@@ -820,6 +885,20 @@
                              buffer->stride(0), barrier));
           break;
         }
+
+        case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
+        case GpuVideoAcceleratorFactories::OutputFormat::BGRA: {
+          const bool is_rgba = output_format_ ==
+                               GpuVideoAcceleratorFactories::OutputFormat::RGBA;
+          worker_task_runner_->PostTask(
+              FROM_HERE,
+              base::BindOnce(&CopyRowsToRGBABuffer, is_rgba, row, rows_to_copy,
+                             coded_size.width(), video_frame,
+                             static_cast<uint8_t*>(buffer->memory(0)),
+                             buffer->stride(0), barrier));
+          break;
+        }
+
         case GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED:
           NOTREACHED();
       }
@@ -918,7 +997,11 @@
       // libyuv or find a way for later passes to make up the difference.
       frame->set_color_space(video_frame->ColorSpace().GetAsRGB());
       break;
-    default:
+    case GpuVideoAcceleratorFactories::OutputFormat::RGBA:
+    case GpuVideoAcceleratorFactories::OutputFormat::BGRA:
+      allow_overlay = true;
+      break;
+    case GpuVideoAcceleratorFactories::OutputFormat::UNDEFINED:
       break;
   }
 
diff --git a/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc b/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc
index 452fbca..05b823a 100644
--- a/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc
+++ b/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc
@@ -107,8 +107,8 @@
     const int kDimension = 10;
     // Data buffers are overdimensioned to acommodate up to 16bpc samples.
     static uint8_t y_data[2 * kDimension * kDimension] = {0};
-    static uint8_t u_data[2 * kDimension * kDimension / 2] = {0};
-    static uint8_t v_data[2 * kDimension * kDimension / 2] = {0};
+    static uint8_t u_data[2 * kDimension * kDimension / 4] = {0};
+    static uint8_t v_data[2 * kDimension * kDimension / 4] = {0};
 
     const VideoPixelFormat format =
         (bit_depth > 8) ? PIXEL_FORMAT_YUV420P10 : PIXEL_FORMAT_I420;
@@ -133,6 +133,35 @@
     return video_frame;
   }
 
+  static scoped_refptr<VideoFrame> CreateTestYUVAVideoFrame(int dimension) {
+    const int kDimension = 10;
+    static uint8_t y_data[kDimension * kDimension] = {0};
+    static uint8_t u_data[kDimension * kDimension / 4] = {0};
+    static uint8_t v_data[kDimension * kDimension / 4] = {0};
+    static uint8_t a_data[kDimension * kDimension] = {0};
+
+    constexpr VideoPixelFormat format = PIXEL_FORMAT_I420A;
+    DCHECK_LE(dimension, kDimension);
+    const gfx::Size size(dimension, dimension);
+
+    scoped_refptr<VideoFrame> video_frame =
+        VideoFrame::WrapExternalYuvaData(format,              // format
+                                         size,                // coded_size
+                                         gfx::Rect(size),     // visible_rect
+                                         size,                // natural_size
+                                         size.width(),        // y_stride
+                                         size.width() / 2,    // u_stride
+                                         size.width() / 2,    // v_stride
+                                         size.width(),        // a_stride
+                                         y_data,              // y_data
+                                         u_data,              // u_data
+                                         v_data,              // v_data
+                                         a_data,              // a_data
+                                         base::TimeDelta());  // timestamp
+    EXPECT_TRUE(video_frame);
+    return video_frame;
+  }
+
   // Note, the X portion is set to 1 since it may use ARGB instead of
   // XRGB on some platforms.
   uint32_t as_xr30(uint32_t r, uint32_t g, uint32_t b) {
@@ -421,6 +450,24 @@
       media::VideoFrameMetadata::READ_LOCK_FENCES_ENABLED));
 }
 
+TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareRGBAFrame) {
+  scoped_refptr<VideoFrame> software_frame = CreateTestYUVAVideoFrame(10);
+  scoped_refptr<VideoFrame> frame;
+  mock_gpu_factories_->SetVideoFrameOutputFormat(
+      media::GpuVideoAcceleratorFactories::OutputFormat::RGBA);
+  gpu_memory_buffer_pool_->MaybeCreateHardwareFrame(
+      software_frame, base::BindOnce(MaybeCreateHardwareFrameCallback, &frame));
+
+  RunUntilIdle();
+
+  EXPECT_NE(software_frame.get(), frame.get());
+  EXPECT_EQ(PIXEL_FORMAT_RGB32, frame->format());
+  EXPECT_EQ(1u, frame->NumTextures());
+  EXPECT_EQ(1u, gles2_->gen_textures_count());
+  EXPECT_TRUE(frame->metadata()->IsTrue(
+      media::VideoFrameMetadata::READ_LOCK_FENCES_ENABLED));
+}
+
 TEST_F(GpuMemoryBufferVideoFramePoolTest, PreservesMetadata) {
   scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10);
   software_frame->metadata()->SetBoolean(
diff --git a/media/video/gpu_video_accelerator_factories.h b/media/video/gpu_video_accelerator_factories.h
index f6794ef..c6ef64c 100644
--- a/media/video/gpu_video_accelerator_factories.h
+++ b/media/video/gpu_video_accelerator_factories.h
@@ -59,8 +59,10 @@
     UYVY,             // One 422 GMB
     NV12_SINGLE_GMB,  // One NV12 GMB
     NV12_DUAL_GMB,    // One R8, one RG88 GMB
-    XR30,             // 10:10:10:2 BGRX in one GMB
+    XR30,             // 10:10:10:2 BGRX in one GMB (Usually Mac)
     XB30,             // 10:10:10:2 RGBX in one GMB
+    RGBA,             // One 8:8:8:8 RGBA
+    BGRA,             // One 8:8:8:8 BGRA (Usually Mac)
   };
 
   // Return whether GPU encoding/decoding is enabled.
diff --git a/media/video/mock_gpu_video_accelerator_factories.cc b/media/video/mock_gpu_video_accelerator_factories.cc
index b058262..82e90ee 100644
--- a/media/video/mock_gpu_video_accelerator_factories.cc
+++ b/media/video/mock_gpu_video_accelerator_factories.cc
@@ -29,7 +29,9 @@
            gfx::BufferFormat::YUV_420_BIPLANAR == format_ ||
            gfx::BufferFormat::UYVY_422 == format_ ||
            gfx::BufferFormat::BGRX_1010102 == format_ ||
-           gfx::BufferFormat::RGBX_1010102 == format_);
+           gfx::BufferFormat::RGBX_1010102 == format_ ||
+           gfx::BufferFormat::RGBA_8888 == format_ ||
+           gfx::BufferFormat::BGRA_8888 == format_);
     DCHECK(num_planes_ <= kMaxPlanes);
     for (int i = 0; i < static_cast<int>(num_planes_); ++i) {
       bytes_[i].resize(gfx::RowSizeForBufferFormat(size_.width(), format_, i) *
diff --git a/media/webrtc/BUILD.gn b/media/webrtc/BUILD.gn
index 27b02e0..5d1de6b 100644
--- a/media/webrtc/BUILD.gn
+++ b/media/webrtc/BUILD.gn
@@ -5,13 +5,19 @@
 import("//media/media_options.gni")
 import("//third_party/webrtc/webrtc.gni")
 
-source_set("webrtc") {
+component("webrtc") {
+  output_name = "media_webrtc"
+
   sources = [
     "audio_processor_controls.h",
+    "echo_information.cc",
+    "echo_information.h",
   ]
+
+  defines = [ "IS_MEDIA_WEBRTC_IMPL" ]
+
   deps = [
     "//base",
-    "//media",
     "//third_party/webrtc/api:libjingle_peerconnection_api",
     "//third_party/webrtc/modules/audio_processing",
     "//third_party/webrtc/modules/audio_processing:audio_processing_statistics",
diff --git a/media/webrtc/audio_processor_controls.h b/media/webrtc/audio_processor_controls.h
index 173bf49..4b3a28b 100644
--- a/media/webrtc/audio_processor_controls.h
+++ b/media/webrtc/audio_processor_controls.h
@@ -10,7 +10,7 @@
 
 namespace media {
 
-class MEDIA_EXPORT AudioProcessorControls {
+class AudioProcessorControls {
  public:
   using GetStatsCB = base::OnceCallback<void(
       const webrtc::AudioProcessorInterface::AudioProcessorStatistics& stats)>;
diff --git a/media/webrtc/echo_information.cc b/media/webrtc/echo_information.cc
new file mode 100644
index 0000000..3cf9ed0f
--- /dev/null
+++ b/media/webrtc/echo_information.cc
@@ -0,0 +1,157 @@
+// 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 "media/webrtc/echo_information.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
+
+namespace media {
+
+namespace {
+
+// Used to log echo quality based on delay estimates.
+enum DelayBasedEchoQuality {
+  DELAY_BASED_ECHO_QUALITY_GOOD = 0,
+  DELAY_BASED_ECHO_QUALITY_SPURIOUS,
+  DELAY_BASED_ECHO_QUALITY_BAD,
+  DELAY_BASED_ECHO_QUALITY_INVALID,
+  DELAY_BASED_ECHO_QUALITY_MAX = DELAY_BASED_ECHO_QUALITY_INVALID
+};
+
+DelayBasedEchoQuality EchoDelayFrequencyToQuality(float delay_frequency) {
+  const float kEchoDelayFrequencyLowerLimit = 0.1f;
+  const float kEchoDelayFrequencyUpperLimit = 0.8f;
+  // DELAY_BASED_ECHO_QUALITY_GOOD
+  //   delay is out of bounds during at most 10 % of the time.
+  // DELAY_BASED_ECHO_QUALITY_SPURIOUS
+  //   delay is out of bounds 10-80 % of the time.
+  // DELAY_BASED_ECHO_QUALITY_BAD
+  //   delay is mostly out of bounds >= 80 % of the time.
+  // DELAY_BASED_ECHO_QUALITY_INVALID
+  //   delay_frequency is negative which happens if we have insufficient data.
+  if (delay_frequency < 0)
+    return DELAY_BASED_ECHO_QUALITY_INVALID;
+  else if (delay_frequency <= kEchoDelayFrequencyLowerLimit)
+    return DELAY_BASED_ECHO_QUALITY_GOOD;
+  else if (delay_frequency < kEchoDelayFrequencyUpperLimit)
+    return DELAY_BASED_ECHO_QUALITY_SPURIOUS;
+  else
+    return DELAY_BASED_ECHO_QUALITY_BAD;
+}
+
+}  // namespace
+
+EchoInformation::EchoInformation()
+    : delay_stats_time_ms_(0),
+      echo_frames_received_(false),
+      divergent_filter_stats_time_ms_(0),
+      num_divergent_filter_fraction_(0),
+      num_non_zero_divergent_filter_fraction_(0) {}
+
+EchoInformation::~EchoInformation() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  ReportAndResetAecDivergentFilterStats();
+}
+
+void EchoInformation::UpdateAecStats(
+    webrtc::EchoCancellation* echo_cancellation) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (!echo_cancellation->is_enabled())
+    return;
+
+  UpdateAecDelayStats(echo_cancellation);
+  UpdateAecDivergentFilterStats(echo_cancellation);
+}
+
+void EchoInformation::UpdateAecDelayStats(
+    webrtc::EchoCancellation* echo_cancellation) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // Only start collecting stats if we know echo cancellation has measured an
+  // echo. Otherwise we clutter the stats with for example cases where only the
+  // microphone is used.
+  if (!echo_frames_received_ & !echo_cancellation->stream_has_echo())
+    return;
+
+  echo_frames_received_ = true;
+
+  // In WebRTC, three echo delay metrics are calculated and updated every
+  // five seconds. We use one of them, |fraction_poor_delays| to log in a UMA
+  // histogram an Echo Cancellation quality metric. The stat in WebRTC has a
+  // fixed aggregation window of five seconds, so we use the same query
+  // frequency to avoid logging old values.
+  if (!echo_cancellation->is_delay_logging_enabled())
+    return;
+
+  delay_stats_time_ms_ += webrtc::AudioProcessing::kChunkSizeMs;
+  if (delay_stats_time_ms_ <
+      500 * webrtc::AudioProcessing::kChunkSizeMs) {  // 5 seconds
+    return;
+  }
+
+  int dummy_median = 0, dummy_std = 0;
+  float fraction_poor_delays = 0;
+  if (echo_cancellation->GetDelayMetrics(&dummy_median, &dummy_std,
+                                         &fraction_poor_delays) ==
+      webrtc::AudioProcessing::kNoError) {
+    delay_stats_time_ms_ = 0;
+    // Map |fraction_poor_delays| to an Echo Cancellation quality and log in UMA
+    // histogram. See DelayBasedEchoQuality for information on histogram
+    // buckets.
+    UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality",
+                              EchoDelayFrequencyToQuality(fraction_poor_delays),
+                              DELAY_BASED_ECHO_QUALITY_MAX + 1);
+  }
+}
+
+void EchoInformation::UpdateAecDivergentFilterStats(
+    webrtc::EchoCancellation* echo_cancellation) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (!echo_cancellation->are_metrics_enabled())
+    return;
+
+  divergent_filter_stats_time_ms_ += webrtc::AudioProcessing::kChunkSizeMs;
+  if (divergent_filter_stats_time_ms_ <
+      100 * webrtc::AudioProcessing::kChunkSizeMs) {  // 1 second
+    return;
+  }
+
+  webrtc::EchoCancellation::Metrics metrics;
+  if (echo_cancellation->GetMetrics(&metrics) ==
+      webrtc::AudioProcessing::kNoError) {
+    // If not yet calculated, |metrics.divergent_filter_fraction| is -1.0. After
+    // being calculated the first time, it is updated periodically.
+    if (metrics.divergent_filter_fraction < 0.0f) {
+      DCHECK_EQ(num_divergent_filter_fraction_, 0);
+      return;
+    }
+    if (metrics.divergent_filter_fraction > 0.0f) {
+      ++num_non_zero_divergent_filter_fraction_;
+    }
+  } else {
+    DLOG(WARNING) << "Get echo cancellation metrics failed.";
+  }
+  ++num_divergent_filter_fraction_;
+  divergent_filter_stats_time_ms_ = 0;
+}
+
+void EchoInformation::ReportAndResetAecDivergentFilterStats() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (num_divergent_filter_fraction_ == 0)
+    return;
+
+  int non_zero_percent = 100 * num_non_zero_divergent_filter_fraction_ /
+                         num_divergent_filter_fraction_;
+  UMA_HISTOGRAM_PERCENTAGE("WebRTC.AecFilterHasDivergence", non_zero_percent);
+
+  divergent_filter_stats_time_ms_ = 0;
+  num_non_zero_divergent_filter_fraction_ = 0;
+  num_divergent_filter_fraction_ = 0;
+}
+
+}  // namespace media
diff --git a/media/webrtc/echo_information.h b/media/webrtc/echo_information.h
new file mode 100644
index 0000000..c69f2cf
--- /dev/null
+++ b/media/webrtc/echo_information.h
@@ -0,0 +1,56 @@
+// 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 MEDIA_WEBRTC_ECHO_INFORMATION_H_
+#define MEDIA_WEBRTC_ECHO_INFORMATION_H_
+
+#include "base/component_export.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
+
+namespace media {
+
+// A helper class to log echo information in general and Echo Cancellation
+// quality in particular.
+class COMPONENT_EXPORT(MEDIA_WEBRTC) EchoInformation {
+ public:
+  EchoInformation();
+  virtual ~EchoInformation();
+
+  // Updates stats, and reports delay metrics as UMA stats every 5 seconds.
+  // Must be called every time AudioProcessing::ProcessStream() is called.
+  void UpdateAecStats(webrtc::EchoCancellation* echo_cancellation);
+
+  // Reports AEC divergent filter metrics as UMA and resets the associated data.
+  void ReportAndResetAecDivergentFilterStats();
+
+ private:
+  void UpdateAecDelayStats(webrtc::EchoCancellation* echo_cancellation);
+  void UpdateAecDivergentFilterStats(
+      webrtc::EchoCancellation* echo_cancellation);
+
+  // Counter to track 5 seconds of data in order to query a new metric from
+  // webrtc::EchoCancellation::GetEchoDelayMetrics().
+  int delay_stats_time_ms_;
+  bool echo_frames_received_;
+
+  // Counter to track 1 second of data in order to query a new divergent filter
+  // fraction metric from webrtc::EchoCancellation::GetMetrics().
+  int divergent_filter_stats_time_ms_;
+
+  // Total number of times we queried for the divergent filter fraction metric.
+  int num_divergent_filter_fraction_;
+
+  // Number of non-zero divergent filter fraction metrics.
+  int num_non_zero_divergent_filter_fraction_;
+
+  // Ensures that this class is accessed on the same thread.
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(EchoInformation);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_WEBRTC_ECHO_INFORMATION_H_
diff --git a/mojo/core/shared_buffer_unittest.cc b/mojo/core/shared_buffer_unittest.cc
index 58a0b88..b1e873f 100644
--- a/mojo/core/shared_buffer_unittest.cc
+++ b/mojo/core/shared_buffer_unittest.cc
@@ -289,8 +289,9 @@
   WriteMessage(h, "ok");
 }
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
 // Android multi-process tests are not executing the new process. This is flaky.
+// Flaky on Fuchsia; see http://crbug.com/874719
 #define MAYBE_CreateAndPassFromChildReadOnlyBuffer \
   DISABLED_CreateAndPassFromChildReadOnlyBuffer
 #else
diff --git a/net/android/javatests/src/org/chromium/net/HttpUtilTest.java b/net/android/javatests/src/org/chromium/net/HttpUtilTest.java
index 63c6f057..4bc208bd 100644
--- a/net/android/javatests/src/org/chromium/net/HttpUtilTest.java
+++ b/net/android/javatests/src/org/chromium/net/HttpUtilTest.java
@@ -28,7 +28,9 @@
     private static final List<String> UNALLOWED_HEADER_NAMES = Arrays.asList(
             "accept-encoding",  // Unsafe header.
             "ACCEPT-ENCODING",  // Unsafe header.
+            "referer ",  // Unsafe header.
             "referer",  // Unsafe header.
+            " referer",  // Unsafe header.
             "",  // Badly formed header.
             "ref(erer",  // Badly formed header.
             "ref\nerer"  // Badly formed header.
diff --git a/remoting/codec/webrtc_video_encoder_vpx.cc b/remoting/codec/webrtc_video_encoder_vpx.cc
index 20a5930d..69b3637d 100644
--- a/remoting/codec/webrtc_video_encoder_vpx.cc
+++ b/remoting/codec/webrtc_video_encoder_vpx.cc
@@ -91,7 +91,7 @@
   // to be met, we relax the max quantizer. The quality will get topped-off
   // in subsequent frames.
   config->rc_min_quantizer = 20;
-  config->rc_max_quantizer = 63;
+  config->rc_max_quantizer = 50;
 }
 
 void SetVp9CodecParameters(vpx_codec_enc_cfg_t* config,
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.cc b/remoting/protocol/webrtc_frame_scheduler_simple.cc
index 799a90b..068b50d 100644
--- a/remoting/protocol/webrtc_frame_scheduler_simple.cc
+++ b/remoting/protocol/webrtc_frame_scheduler_simple.cc
@@ -27,6 +27,14 @@
 // Target quantizer at which stop the encoding top-off.
 const int kTargetQuantizerForVp8TopOff = 30;
 
+// Maximum quantizer at which to encode frames. Lowering this value will
+// improve image quality (in cases of low-bandwidth or large frames) at the
+// cost of latency. Increasing the value will improve latency (in these cases)
+// at the cost of image quality, resulting in longer top-off times.
+// TODO(lambroslambrou): Move this, and any other encoder-specific parameters
+// into the WebrtcVideoEncoderXXX implementations.
+const int kMaxQuantizer = 50;
+
 const int64_t kPixelsPerMegapixel = 1000000;
 
 // Threshold in number of updated pixels used to detect "big" frames. These
@@ -178,7 +186,8 @@
   // If bandwidth is being underutilized then libvpx is likely to choose the
   // minimum allowed quantizer value, which means that encoded frame size may
   // be significantly bigger than the bandwidth allows. Detect this case and set
-  // vpx_min_quantizer to 60. The quality will be topped off later.
+  // vpx_min_quantizer to the maximum value. The quality will be topped off
+  // later.
   if (updated_area - updated_region_area_.Max() > kBigFrameThresholdPixels) {
     int expected_frame_size =
         updated_area * kEstimatedBytesPerMegapixel / kPixelsPerMegapixel;
@@ -186,13 +195,13 @@
         base::Time::kMicrosecondsPerSecond * expected_frame_size /
         pacing_bucket_.rate());
     if (expected_send_delay > kTargetFrameInterval) {
-      params_out->vpx_min_quantizer = 60;
+      params_out->vpx_min_quantizer = kMaxQuantizer;
     }
   }
 
   updated_region_area_.Record(updated_area);
 
-  params_out->vpx_max_quantizer = 63;
+  params_out->vpx_max_quantizer = kMaxQuantizer;
 
   params_out->clear_active_map = !top_off_is_active_;
 
diff --git a/sandbox/linux/syscall_broker/broker_process.cc b/sandbox/linux/syscall_broker/broker_process.cc
index 5caced5..c510b8a 100644
--- a/sandbox/linux/syscall_broker/broker_process.cc
+++ b/sandbox/linux/syscall_broker/broker_process.cc
@@ -103,99 +103,6 @@
   return false;
 }
 
-bool BrokerProcess::IsSyscallAllowed(int sysno) const {
-  switch (sysno) {
-    // In the event that there are no case statements defined, provide a
-    // "backstop" that returns false, so that one of the below return
-    // statements does not execute instead.
-    case 0:
-      return false;
-
-#if defined(__NR_access)
-    case __NR_access:
-#endif
-#if defined(__NR_faccessat)
-    case __NR_faccessat:
-#endif
-      return !fast_check_in_client_ ||
-             allowed_command_set_.test(COMMAND_ACCESS);
-
-#if defined(__NR_mkdir)
-    case __NR_mkdir:
-#endif
-#if defined(__NR_mkdirat)
-    case __NR_mkdirat:
-#endif
-      return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_MKDIR);
-
-#if defined(__NR_open)
-    case __NR_open:
-#endif
-#if defined(__NR_openat)
-    case __NR_openat:
-#endif
-      return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_OPEN);
-
-#if defined(__NR_readlink)
-    case __NR_readlink:
-#endif
-#if defined(__NR_readlinkat)
-    case __NR_readlinkat:
-#endif
-      return !fast_check_in_client_ ||
-             allowed_command_set_.test(COMMAND_READLINK);
-
-#if defined(__NR_rename)
-    case __NR_rename:
-#endif
-#if defined(__NR_renameat)
-    case __NR_renameat:
-#endif
-      return !fast_check_in_client_ ||
-             allowed_command_set_.test(COMMAND_RENAME);
-
-#if defined(__NR_rmdir)
-    case __NR_rmdir:
-#endif
-      return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_RMDIR);
-
-#if defined(__NR_stat)
-    case __NR_stat:
-#endif
-#if defined(__NR_lstat)
-    case __NR_lstat:
-#endif
-#if defined(__NR_fstatat)
-    case __NR_fstatat:
-#endif
-#if defined(__NR_newfstatat)
-    case __NR_newfstatat:
-#endif
-      return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_STAT);
-
-#if defined(__NR_stat64)
-    case __NR_stat64:
-#endif
-#if defined(__NR_lstat64)
-    case __NR_lstat64:
-#endif
-      return !fast_check_in_client_ ||
-             allowed_command_set_.test(COMMAND_STAT64);
-
-#if defined(__NR_unlink)
-    case __NR_unlink:
-#endif
-#if defined(__NR_unlinkat)
-    case __NR_unlinkat:
-#endif
-      return !fast_check_in_client_ ||
-             allowed_command_set_.test(COMMAND_UNLINK);
-
-    default:
-      return false;
-  }
-}
-
 void BrokerProcess::CloseChannel() {
   broker_client_.reset();
 }
diff --git a/sandbox/linux/syscall_broker/broker_process.h b/sandbox/linux/syscall_broker/broker_process.h
index e994a01..1359930 100644
--- a/sandbox/linux/syscall_broker/broker_process.h
+++ b/sandbox/linux/syscall_broker/broker_process.h
@@ -74,14 +74,6 @@
   // Return the PID of the child created by Init().
   int broker_pid() const { return broker_pid_; }
 
-  // Can be used in bpf_dsl::Policy::EvaluateSyscall() implementations to
-  // determine if the system call |sysno| should be trapped and forwarded
-  // to the broker process for handling. This examines the
-  // |allowed_command_set_| iff |fast_check_in_client_| is true. If
-  // the fast checks are disabled, then all possible brokerable system
-  // calls are forwarded to the broker process for handling.
-  bool IsSyscallAllowed(int sysno) const;
-
   // The following methods are used in place of the equivalently-named
   // syscalls by the trap handler. They, in turn, forward the call onto
   // |broker_client_| for further processing. They will all be async signal
diff --git a/sandbox/linux/syscall_broker/broker_process_unittest.cc b/sandbox/linux/syscall_broker/broker_process_unittest.cc
index 7d04b8b..8df69d0 100644
--- a/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -27,8 +27,6 @@
 #include "base/macros.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/posix/unix_domain_socket.h"
-#include "base/stl_util.h"
-#include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "sandbox/linux/syscall_broker/broker_client.h"
 #include "sandbox/linux/tests/scoped_temporary_file.h"
@@ -1454,99 +1452,5 @@
   TestUnlinkHelper(false);
 }
 
-TEST(BrokerProcess, IsSyscallAllowed) {
-  const struct {
-    int sysno;
-    BrokerCommand command;
-  } kSyscallToCommandMap[] = {
-#if defined(__NR_access)
-    {__NR_access, COMMAND_ACCESS},
-#endif
-#if defined(__NR_faccessat)
-    {__NR_faccessat, COMMAND_ACCESS},
-#endif
-#if defined(__NR_mkdir)
-    {__NR_mkdir, COMMAND_MKDIR},
-#endif
-#if defined(__NR_mkdirat)
-    {__NR_mkdirat, COMMAND_MKDIR},
-#endif
-#if defined(__NR_open)
-    {__NR_open, COMMAND_OPEN},
-#endif
-#if defined(__NR_openat)
-    {__NR_openat, COMMAND_OPEN},
-#endif
-#if defined(__NR_readlink)
-    {__NR_readlink, COMMAND_READLINK},
-#endif
-#if defined(__NR_readlinkat)
-    {__NR_readlinkat, COMMAND_READLINK},
-#endif
-#if defined(__NR_rename)
-    {__NR_rename, COMMAND_RENAME},
-#endif
-#if defined(__NR_renameat)
-    {__NR_renameat, COMMAND_RENAME},
-#endif
-#if defined(__NR_rmdir)
-    {__NR_rmdir, COMMAND_RMDIR},
-#endif
-#if defined(__NR_stat)
-    {__NR_stat, COMMAND_STAT},
-#endif
-#if defined(__NR_lstat)
-    {__NR_lstat, COMMAND_STAT},
-#endif
-#if defined(__NR_fstatat)
-    {__NR_fstatat, COMMAND_STAT},
-#endif
-#if defined(__NR_newfstatat)
-    {__NR_newfstatat, COMMAND_STAT},
-#endif
-#if defined(__NR_stat64)
-    {__NR_stat64, COMMAND_STAT64},
-#endif
-#if defined(__NR_lstat64)
-    {__NR_lstat64, COMMAND_STAT64},
-#endif
-#if defined(__NR_unlink)
-    {__NR_unlink, COMMAND_UNLINK},
-#endif
-#if defined(__NR_unlinkat)
-    {__NR_unlinkat, COMMAND_UNLINK},
-#endif
-  };
-
-  EXPECT_GT(base::size(kSyscallToCommandMap), 0u);
-
-  for (const auto& test : kSyscallToCommandMap) {
-    // Test with fast_check_in_client.
-    {
-      SCOPED_TRACE(base::StringPrintf("fast check, sysno=%d", test.sysno));
-      BrokerProcess process(ENOSYS, MakeBrokerCommandSet({test.command}), {},
-                            true, true);
-      EXPECT_TRUE(process.IsSyscallAllowed(test.sysno));
-      for (const auto& other : kSyscallToCommandMap) {
-        SCOPED_TRACE(base::StringPrintf("others test, sysno=%d", other.sysno));
-        EXPECT_EQ(other.command == test.command,
-                  process.IsSyscallAllowed(other.sysno));
-      }
-    }
-
-    // Test without fast_check_in_client.
-    {
-      SCOPED_TRACE(base::StringPrintf("no fast check, sysno=%d", test.sysno));
-      BrokerProcess process(ENOSYS, MakeBrokerCommandSet({test.command}), {},
-                            false, true);
-      EXPECT_TRUE(process.IsSyscallAllowed(test.sysno));
-      for (const auto& other : kSyscallToCommandMap) {
-        SCOPED_TRACE(base::StringPrintf("others test, sysno=%d", other.sysno));
-        EXPECT_TRUE(process.IsSyscallAllowed(other.sysno));
-      }
-    }
-  }
-}
-
 }  // namespace syscall_broker
 }  // namespace sandbox
diff --git a/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc
index c2b81743..0a07fdf 100644
--- a/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc
@@ -55,6 +55,29 @@
     case __NR_prctl:
     case __NR_sysinfo:
       return Allow();
+#if !defined(__aarch64__)
+    case __NR_access:
+    case __NR_open:
+#endif  // !defined(__aarch64__)
+    case __NR_faccessat:
+    case __NR_openat:
+#if defined(__NR_stat)
+    case __NR_stat:
+#endif
+#if defined(__NR_stat64)
+    case __NR_stat64:
+#endif
+#if defined(__NR_fstatat)
+    case __NR_fstatat:
+#endif
+#if defined(__NR_newfstatat)
+    case __NR_newfstatat:
+#endif
+    {
+      auto* broker_process = SandboxLinux::GetInstance()->broker_process();
+      DCHECK(broker_process);
+      return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
+    }
     case __NR_sched_getaffinity:
     case __NR_sched_setaffinity:
       return sandbox::RestrictSchedTarget(GetPolicyPid(), sysno);
@@ -62,11 +85,6 @@
       if (SyscallSets::IsEventFd(sysno))
         return Allow();
 
-      auto* broker_process = SandboxLinux::GetInstance()->broker_process();
-      if (broker_process->IsSyscallAllowed(sysno)) {
-        return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
-      }
-
       // Default on the baseline policy.
       return BPFBasePolicy::EvaluateSyscall(sysno);
   }
diff --git a/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
index b7474d5..49c378f 100644
--- a/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
@@ -33,13 +33,70 @@
 NetworkProcessPolicy::~NetworkProcessPolicy() {}
 
 ResultExpr NetworkProcessPolicy::EvaluateSyscall(int sysno) const {
-  auto* broker_process = SandboxLinux::GetInstance()->broker_process();
-  if (broker_process->IsSyscallAllowed(sysno)) {
-    return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
+  switch (sysno) {
+#if defined(__NR_access)
+    case __NR_access:
+#endif
+#if defined(__NR_faccessat)
+    case __NR_faccessat:
+#endif
+#if defined(__NR_mkdir)
+    case __NR_mkdir:
+#endif
+#if defined(__NR_mkdirat)
+    case __NR_mkdirat:
+#endif
+#if defined(__NR_open)
+    case __NR_open:
+#endif
+#if defined(__NR_openat)
+    case __NR_openat:
+#endif
+#if defined(__NR_readlink)
+    case __NR_readlink:
+#endif
+#if defined(__NR_readlinkat)
+    case __NR_readlinkat:
+#endif
+#if defined(__NR_rmdir)
+    case __NR_rmdir:
+#endif
+#if defined(__NR_rename)
+    case __NR_rename:
+#endif
+#if defined(__NR_renameat)
+    case __NR_renameat:
+#endif
+#if defined(__NR_stat)
+    case __NR_stat:
+#endif
+#if defined(__NR_stat64)
+    case __NR_stat64:
+#endif
+#if defined(__NR_lstat)
+    case __NR_lstat:
+#endif
+#if defined(__NR_lstat64)
+    case __NR_lstat64:
+#endif
+#if defined(__NR_fstatat)
+    case __NR_fstatat:
+#endif
+#if defined(__NR_newfstatat)
+    case __NR_newfstatat:
+#endif
+#if defined(__NR_unlink)
+    case __NR_unlink:
+#endif
+#if defined(__NR_unlinkat)
+    case __NR_unlinkat:
+#endif
+      return Trap(BrokerProcess::SIGSYS_Handler,
+                  SandboxLinux::GetInstance()->broker_process());
+    default:
+      // TODO(tsepez): FIX this.
+      return Allow();
   }
-
-  // TODO(tsepez): FIX this.
-  return Allow();
 }
 
 }  // namespace service_manager
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index f4f9e529..ede34a0 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -363,6 +363,56 @@
       }
     ]
   },
+  "android-go_webview-perf": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "-v",
+          "--browser=android-webview",
+          "--upload-results",
+          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk",
+          "--test-shard-map-filename=android_go_webview_shard_map.json"
+        ],
+        "isolate_name": "performance_webview_test_suite",
+        "merge": {
+          "args": [
+            "--service-account-file",
+            "/creds/service_accounts/service-account-chromium-perf-histograms.json"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "performance_webview_test_suite",
+        "override_compile_targets": [
+          "performance_webview_test_suite"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "O",
+              "device_os_flavor": "google",
+              "device_type": "gobo",
+              "os": "Android",
+              "pool": "chrome.tests.perf-webview"
+            }
+          ],
+          "expiration": 7200,
+          "hard_timeout": 25200,
+          "ignore_task_failure": false,
+          "io_timeout": 1800,
+          "shards": 25,
+          "upload_test_results": true
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/perf_device_trigger.py"
+        }
+      }
+    ]
+  },
   "android-pixel2-perf": {
     "isolated_scripts": [
       {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 05440aa..608451f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2596,6 +2596,26 @@
             ]
         }
     ],
+    "NewPasswordFormParsing": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows",
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "new-password-form-parsing"
+                    ]
+                }
+            ]
+        }
+    ],
     "NewPrintPreview": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 43ef9fd..6255b1b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2734,8 +2734,6 @@
 crbug.com/626703 [ Win10 ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-column-row-gap-004.html [ Failure ]
 crbug.com/626703 [ Linux Mac Win7 ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-column-row-gap-004.html [ Failure ]
 crbug.com/626703 [ Mac10.12 Mac10.13 ] external/wpt/service-workers/service-worker/clients-matchall-order.https.html [ Timeout ]
-crbug.com/626703 external/wpt/svg/render/reftests/blending-002.svg [ Failure ]
-crbug.com/626703 external/wpt/svg/render/reftests/blending-001.svg [ Failure ]
 crbug.com/626703 [ Win7 ] external/wpt/css/css-lists/content-property/marker-text-matches-armenian.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-lists/content-property/marker-text-matches-upper-latin.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-lists/content-property/marker-text-matches-disc.html [ Failure ]
@@ -3925,6 +3923,8 @@
 crbug.com/874444 [ Linux ] external/wpt/fullscreen/api/element-request-fullscreen-two-elements-manual.html [ Pass Failure ]
 crbug.com/874500 [ Mac ] media/media-ended.html [ Pass Timeout ]
 crbug.com/874567 [ Mac ] svg/custom/getscreenctm-in-scrollable-div-area-nested.xhtml [ Pass Failure ]
+crbug.com/874837 [ Win ] ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling.htm [ Pass Failure ]
+crbug.com/874866 [ Mac ] virtual/new-remote-playback-pipeline/media/controls/modern/doubletap-to-jump-backwards-at-start.html [ Pass Timeout ]
 
 crbug.com/715718 external/wpt/media-source/mediasource-activesourcebuffers.html [ Failure Pass ]
 crbug.com/715718 external/wpt/media-source/mediasource-remove.html [ Failure Pass ]
@@ -4501,7 +4501,7 @@
 # Sheriff 2018-03-29
 crbug.com/827088 bluetooth/requestDevice/chooser/new-scan-device-added.html [ Pass Crash ]
 crbug.com/827209 fast/events/middleClickAutoscroll-latching.html [ Pass Failure Timeout ]
-crbug.com/827209 virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-latching.html [ Pass Failure ]
+crbug.com/827209 virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-latching.html [ Pass Failure Timeout ]
 # Sheriff 2018-08-14
 crbug.com/827209 virtual/user-activation-v2/fast/events/middleClickAutoscroll-latching.html [ Pass Timeout Failure ]
 
@@ -4689,7 +4689,7 @@
 
 # Sheriff 2018-06-29
 crbug.com/859064 http/tests/devtools/console-resource-errors.js [ Pass Failure ]
-crbug.com/859169 [ Win7 ] http/tests/devtools/layers/layer-scroll-rects-get.js [ Failure Pass ]
+crbug.com/859169 http/tests/devtools/layers/layer-scroll-rects-get.js [ Failure Pass ]
 
 # User Activation
 crbug.com/736415 external/wpt/html/user-activation/activation-api-iframe.tenative.html [ Failure ]
@@ -4938,7 +4938,14 @@
 # Tests that work only when LazyImageLoading feature is enabled.
 crbug.com/846170 http/tests/lazyload/lazy.html [ Skip ]
 
-# Sheriff 2018-08-15
+# Sheriff 2018-08-16
 crbug.com/874733 [ Android ] accessibility/aria-labelledby-on-input.html [ Crash ]
 crbug.com/874733 [ Android ] accessibility/aria-link-supports-press.html [ Crash ]
 crbug.com/874733 [ Android ] accessibility/insert-adjacent-html-causes-crash.xhtml [ Crash ]
+
+crbug.com/874737 [ Android ] animations/animation-shorthand-overriding.html [ Crash ]
+crbug.com/874737 [ Android ] animations/interpolation/border-image-slice-interpolation-stability.html [ Crash ]
+crbug.com/874737 [ Android ] animations/responsive/interpolation/offset-rotate-responsive.html [ Crash ]
+crbug.com/874737 [ Android ] animations/stability/empty-keyframes.html [ Crash ]
+crbug.com/874737 [ Android ] animations/svg-attribute-composition/svg-bias-composition.html [ Crash ]
+crbug.com/874737 [ Android ] animations/svg-attribute-interpolation/svg-numOctaves-interpolation.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index aa6cf94..ea9a15c 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -160475,6 +160475,11 @@
      {}
     ]
    ],
+   "interfaces/mst-content-hint.idl": [
+    [
+     {}
+    ]
+   ],
    "interfaces/navigation-timing.idl": [
     [
      {}
@@ -162315,6 +162320,11 @@
      {}
     ]
    ],
+   "mst-content-hint/META.yml": [
+    [
+     {}
+    ]
+   ],
    "navigation-timing/META.yml": [
     [
      {}
@@ -230295,6 +230305,12 @@
      {}
     ]
    ],
+   "mst-content-hint/idlharness.window.js": [
+    [
+     "/mst-content-hint/idlharness.window.html",
+     {}
+    ]
+   ],
    "navigation-timing/idlharness.window.js": [
     [
      "/navigation-timing/idlharness.window.html",
@@ -382012,6 +382028,10 @@
    "42dfb4d307e139541069e98af17e757c9916b23f",
    "support"
   ],
+  "interfaces/mst-content-hint.idl": [
+   "b99163702ef0fc8fbe730de152cc326ba81bd784",
+   "support"
+  ],
   "interfaces/navigation-timing.idl": [
    "89bb9238943a1e375c514ae6ff08ccdede13311e",
    "support"
@@ -385168,6 +385188,14 @@
    "531546fa4c30f76ed51c7771b9dca10de58d2df0",
    "testharness"
   ],
+  "mst-content-hint/META.yml": [
+   "a8545775dc52f66495a43b6d488701c882967a23",
+   "support"
+  ],
+  "mst-content-hint/idlharness.window.js": [
+   "2141dff1aaa94d329a1c874f2bced6097c2903b1",
+   "testharness"
+  ],
   "navigation-timing/META.yml": [
    "f6d2a52da759aeb26b3b6de62e5b3592a1299aa5",
    "support"
@@ -404469,7 +404497,7 @@
    "support"
   ],
   "service-workers/service-worker/navigation-preload/resources/resource-timing-worker.js": [
-   "8eb75e9bb7e3aaec818427b5f101a5d2353e65bf",
+   "c0d42dd6eee07f44124dd84bb9cc00686017739a",
    "support"
   ],
   "service-workers/service-worker/navigation-preload/resources/wait-for-activate-worker.js": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/attribute-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/attribute-tests.html
index 6e7d5fea..1d2141d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/attribute-tests.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/attribute-tests.html
@@ -9,8 +9,14 @@
     <script src="/resources/testharnessreport.js"></script>
     <script src="resources/cookie-http-state-template.js"></script>
   </head>
-  <body>
-    <div id="log"></div>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
     <script>
       setup({ explicit_timeout: true });
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/charset-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/charset-tests.html
new file mode 100644
index 0000000..9d9ef1b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/charset-tests.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests cookie charset functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "charset0001", name: "charset0001"},
+        {file: "charset0002", name: "charset0002"},
+        {file: "charset0003", name: "charset0003"},
+        {file: "charset0004", name: "charset0004"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/chromium-tests-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/chromium-tests-expected.txt
new file mode 100644
index 0000000..2616af43
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/chromium-tests-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+PASS chromium0001 - chromium0001
+PASS chromium0002 - chromium0002
+PASS chromium0003 - chromium0003
+PASS chromium0004 - chromium0004
+PASS chromium0005 - chromium0005
+FAIL chromium0006 - chromium0006 assert_equals: expected "aBc=\"zzz \" \"ppp\"" but got "aBc=\"zzz \"   \"ppp\""
+FAIL chromium0007 - chromium0007 assert_equals: expected "aBc=\"zzz \" \"ppp\"" but got "aBc=\"zzz \"   \"ppp\""
+PASS chromium0008 - chromium0008
+FAIL chromium0009 - chromium0009 assert_equals: expected "" but got "BLAHHH"
+FAIL chromium0010 - chromium0010 assert_equals: expected "" but got "\"BLA\\\"HHH\""
+PASS chromium0011 - chromium0011
+FAIL chromium0012 - chromium0012 assert_equals: expected "" but got "ABC"
+PASS chromium0013 - chromium0013
+PASS chromium0014 - chromium0014
+PASS chromium0015 - chromium0015
+PASS chromium0016 - chromium0016
+PASS chromium0017 - chromium0017
+PASS chromium0018 - chromium0018
+PASS chromium0019 - chromium0019
+PASS disabled-chromium0020 - disabled-chromium0020
+PASS chromium0021 - chromium0021
+FAIL disabled-chromium0022 - disabled-chromium0022 assert_equals: expected "AAA=BB" but got "AAA=BBZYX"
+PASS disabled-chromium0023 - disabled-chromium0023
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/chromium-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/chromium-tests.html
new file mode 100644
index 0000000..e5d745e03
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/chromium-tests.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests chromium cookie functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "chromium0001", name: "chromium0001"},
+        {file: "chromium0002", name: "chromium0002"},
+        {file: "chromium0003", name: "chromium0003"},
+        {file: "chromium0004", name: "chromium0004"},
+        {file: "chromium0005", name: "chromium0005"},
+        {file: "chromium0006", name: "chromium0006"},
+        {file: "chromium0007", name: "chromium0007"},
+        {file: "chromium0008", name: "chromium0008"},
+        {file: "chromium0009", name: "chromium0009"},
+        {file: "chromium0010", name: "chromium0010"},
+        {file: "chromium0011", name: "chromium0011"},
+        {file: "chromium0012", name: "chromium0012"},
+        {file: "chromium0013", name: "chromium0013"},
+        {file: "chromium0014", name: "chromium0014"},
+        {file: "chromium0015", name: "chromium0015"},
+        {file: "chromium0016", name: "chromium0016"},
+        {file: "chromium0017", name: "chromium0017"},
+        {file: "chromium0018", name: "chromium0018"},
+        {file: "chromium0019", name: "chromium0019"},
+        {file: "disabled-chromium0020", name: "disabled-chromium0020"},
+        {file: "chromium0021", name: "chromium0021"},
+        {file: "disabled-chromium0022", name: "disabled-chromium0022"},
+        {file: "disabled-chromium0023", name: "disabled-chromium0023"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/comma-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/comma-tests.html
new file mode 100644
index 0000000..f71bb8d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/comma-tests.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests cookie setting comma functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "comma0001", name: "comma0001"},
+        {file: "comma0002", name: "comma0002"},
+        {file: "comma0003", name: "comma0003"},
+        {file: "comma0004", name: "comma0004"},
+        {file: "comma0005", name: "comma0005"},
+        {file: "comma0006", name: "comma0006"},
+        {file: "comma0007", name: "comma0007"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/domain-tests-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/domain-tests-expected.txt
new file mode 100644
index 0000000..cc37bc7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/domain-tests-expected.txt
@@ -0,0 +1,47 @@
+This is a testharness.js-based test.
+FAIL domain0001 - domain0001 assert_equals: expected "foo=bar" but got ""
+PASS domain0002 - domain0002
+FAIL domain0003 - domain0003 assert_equals: expected "foo=bar" but got ""
+FAIL domain0004 - domain0004 assert_equals: expected "foo=bar" but got ""
+FAIL domain0005 - domain0005 assert_equals: expected "foo=bar" but got ""
+PASS domain0006 - domain0006
+PASS domain0007 - domain0007
+FAIL domain0008 - domain0008 assert_equals: expected "foo=bar" but got ""
+FAIL domain0009 - domain0009 assert_equals: expected "foo=bar" but got ""
+PASS domain0010 - domain0010
+PASS domain0011 - domain0011
+FAIL domain0012 - domain0012 assert_equals: expected "foo=bar" but got ""
+PASS domain0013 - domain0013
+PASS domain0014 - domain0014
+PASS domain0015 - domain0015
+PASS domain0016 - domain0016
+PASS domain0017 - domain0017
+PASS domain0018 - domain0018
+FAIL domain0019 - domain0019 assert_equals: expected "foo=bar; foo2=bar2" but got ""
+FAIL domain0020 - domain0020 assert_equals: expected "foo2=bar2; foo=bar" but got ""
+PASS domain0021 - domain0021
+FAIL domain0022 - domain0022 assert_equals: expected "foo=bar; foo2=bar2" but got ""
+FAIL domain0023 - domain0023 assert_equals: expected "foo2=bar2; foo=bar" but got ""
+PASS domain0024 - domain0024
+FAIL domain0025 - domain0025 assert_equals: expected "foo=bar" but got ""
+FAIL domain0026 - domain0026 assert_equals: expected "foo=bar" but got ""
+PASS domain0027 - domain0027
+PASS domain0028 - domain0028
+FAIL domain0029 - domain0029 assert_equals: expected "" but got "foo=bar"
+PASS optional-domain0030 - optional-domain0030
+FAIL domain0031 - domain0031 assert_equals: expected "foo=bar" but got ""
+FAIL domain0033 - domain0033 assert_equals: expected "foo=bar" but got ""
+PASS domain0034 - domain0034
+FAIL domain0035 - domain0035 assert_equals: expected "foo=bar" but got ""
+FAIL domain0036 - domain0036 assert_equals: expected "foo=bar" but got ""
+PASS domain0037 - domain0037
+FAIL domain0038 - domain0038 assert_equals: expected "foo=bar" but got ""
+FAIL domain0039 - domain0039 assert_equals: expected "foo=bar" but got ""
+FAIL domain0040 - domain0040 assert_equals: expected "foo=bar" but got ""
+PASS domain0041 - domain0041
+PASS domain0042 - domain0042
+FAIL optional-domain0041 - optional-domain0041 assert_equals: expected "foo=bar" but got ""
+PASS optional-domain0042 - optional-domain0042
+PASS optional-domain0043 - optional-domain0043
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/domain-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/domain-tests.html
new file mode 100644
index 0000000..85c8cd0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/domain-tests.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests cookie setting domain functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "domain0001", name: "domain0001"},
+        {file: "domain0002", name: "domain0002"},
+        {file: "domain0003", name: "domain0003"},
+        {file: "domain0004", name: "domain0004"},
+        {file: "domain0005", name: "domain0005"},
+        {file: "domain0006", name: "domain0006"},
+        {file: "domain0007", name: "domain0007"},
+        {file: "domain0008", name: "domain0008"},
+        {file: "domain0009", name: "domain0009"},
+        {file: "domain0010", name: "domain0010"},
+        {file: "domain0011", name: "domain0011"},
+        {file: "domain0012", name: "domain0012"},
+        {file: "domain0013", name: "domain0013"},
+        {file: "domain0014", name: "domain0014"},
+        {file: "domain0015", name: "domain0015"},
+        {file: "domain0016", name: "domain0016"},
+        {file: "domain0017", name: "domain0017"},
+        {file: "domain0018", name: "domain0018"},
+        {file: "domain0019", name: "domain0019"},
+        {file: "domain0020", name: "domain0020"},
+        {file: "domain0021", name: "domain0021"},
+        {file: "domain0022", name: "domain0022"},
+        {file: "domain0023", name: "domain0023"},
+        {file: "domain0024", name: "domain0024"},
+        {file: "domain0025", name: "domain0025"},
+        {file: "domain0026", name: "domain0026"},
+        {file: "domain0027", name: "domain0027"},
+        {file: "domain0028", name: "domain0028"},
+        {file: "domain0029", name: "domain0029"},
+        {file: "optional-domain0030", name: "optional-domain0030"},
+        {file: "domain0031", name: "domain0031"},
+        {file: "domain0033", name: "domain0033"},
+        {file: "domain0034", name: "domain0034"},
+        {file: "domain0035", name: "domain0035"},
+        {file: "domain0036", name: "domain0036"},
+        {file: "domain0037", name: "domain0037"},
+        {file: "domain0038", name: "domain0038"},
+        {file: "domain0039", name: "domain0039"},
+        {file: "domain0040", name: "domain0040"},
+        {file: "domain0041", name: "domain0041"},
+        {file: "domain0042", name: "domain0042"},
+        {file: "optional-domain0041", name: "optional-domain0041"},
+        {file: "optional-domain0042", name: "optional-domain0042"},
+        {file: "optional-domain0043", name: "optional-domain0043"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/general-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/general-tests.html
index 169984d..1e63043a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/general-tests.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/general-tests.html
@@ -9,8 +9,14 @@
     <script src="/resources/testharnessreport.js"></script>
     <script src="resources/cookie-http-state-template.js"></script>
   </head>
-  <body>
-    <div id="log"></div>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
     <script>
       setup({ explicit_timeout: true });
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/mozilla-tests-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/mozilla-tests-expected.txt
new file mode 100644
index 0000000..b7db7733
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/mozilla-tests-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS mozilla0001 - mozilla0001
+PASS mozilla0002 - mozilla0002
+PASS mozilla0003 - mozilla0003
+PASS mozilla0004 - mozilla0004
+PASS mozilla0005 - mozilla0005
+PASS mozilla0006 - mozilla0006
+PASS mozilla0007 - mozilla0007
+PASS mozilla0008 - mozilla0008
+PASS mozilla0009 - mozilla0009
+PASS mozilla0010 - mozilla0010
+PASS mozilla0011 - mozilla0011
+FAIL mozilla0012 - mozilla0012 assert_equals: expected "test=\"fubar! = foo" but got "test=\"fubar! = foo; five"
+PASS mozilla0013 - mozilla0013
+FAIL mozilla0014 - mozilla0014 assert_equals: expected "" but got "six"
+FAIL mozilla0015 - mozilla0015 assert_equals: expected "" but got "seven"
+FAIL mozilla0016 - mozilla0016 assert_equals: expected "" but got "eight"
+FAIL mozilla0017 - mozilla0017 assert_equals: expected "test=six" but got "eight; test=six"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/mozilla-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/mozilla-tests.html
new file mode 100644
index 0000000..01a5645b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/mozilla-tests.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests basic cookie setting functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "mozilla0001", name: "mozilla0001"},
+        {file: "mozilla0002", name: "mozilla0002"},
+        {file: "mozilla0003", name: "mozilla0003"},
+        {file: "mozilla0004", name: "mozilla0004"},
+        {file: "mozilla0005", name: "mozilla0005"},
+        {file: "mozilla0006", name: "mozilla0006"},
+        {file: "mozilla0007", name: "mozilla0007"},
+        {file: "mozilla0008", name: "mozilla0008"},
+        {file: "mozilla0009", name: "mozilla0009"},
+        {file: "mozilla0010", name: "mozilla0010"},
+        {file: "mozilla0011", name: "mozilla0011"},
+        {file: "mozilla0012", name: "mozilla0012"},
+        {file: "mozilla0013", name: "mozilla0013"},
+        {file: "mozilla0014", name: "mozilla0014"},
+        {file: "mozilla0015", name: "mozilla0015"},
+        {file: "mozilla0016", name: "mozilla0016"},
+        {file: "mozilla0017", name: "mozilla0017"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/name-tests-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/name-tests-expected.txt
new file mode 100644
index 0000000..72ee647
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/name-tests-expected.txt
@@ -0,0 +1,36 @@
+This is a testharness.js-based test.
+PASS name0001 - name0001
+PASS name0002 - name0002
+PASS name0003 - name0003
+PASS name0004 - name0004
+PASS name0005 - name0005
+PASS name0006 - name0006
+PASS name0007 - name0007
+PASS name0008 - name0008
+PASS name0009 - name0009
+PASS name0010 - name0010
+PASS name0011 - name0011
+PASS name0012 - name0012
+PASS name0013 - name0013
+PASS name0014 - name0014
+PASS name0015 - name0015
+PASS name0016 - name0016
+FAIL name0017 - name0017 assert_equals: expected "" but got "a=bar"
+PASS name0018 - name0018
+PASS name0019 - name0019
+PASS name0020 - name0020
+PASS name0021 - name0021
+PASS name0022 - name0022
+FAIL name0023 - name0023 assert_equals: expected "" but got "foo"
+PASS name0024 - name0024
+FAIL name0025 - name0025 assert_equals: expected "" but got "==a=bar"
+PASS name0026 - name0026
+PASS name0027 - name0027
+FAIL name0028 - name0028 assert_equals: expected "" but got "a"
+PASS name0029 - name0029
+PASS name0030 - name0030
+FAIL name0031 - name0031 assert_equals: expected "" but got "\"foo"
+FAIL name0032 - name0032 assert_equals: expected "" but got "\"foo\\\"bar"
+FAIL name0033 - name0033 assert_equals: expected "" but got "aaa"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/name-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/name-tests.html
new file mode 100644
index 0000000..9dd2781f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/name-tests.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests basic cookie setting functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "name0001", name: "name0001"},
+        {file: "name0002", name: "name0002"},
+        {file: "name0003", name: "name0003"},
+        {file: "name0004", name: "name0004"},
+        {file: "name0005", name: "name0005"},
+        {file: "name0006", name: "name0006"},
+        {file: "name0007", name: "name0007"},
+        {file: "name0008", name: "name0008"},
+        {file: "name0009", name: "name0009"},
+        {file: "name0010", name: "name0010"},
+        {file: "name0011", name: "name0011"},
+        {file: "name0012", name: "name0012"},
+        {file: "name0013", name: "name0013"},
+        {file: "name0014", name: "name0014"},
+        {file: "name0015", name: "name0015"},
+        {file: "name0016", name: "name0016"},
+        {file: "name0017", name: "name0017"},
+        {file: "name0018", name: "name0018"},
+        {file: "name0019", name: "name0019"},
+        {file: "name0020", name: "name0020"},
+        {file: "name0021", name: "name0021"},
+        {file: "name0022", name: "name0022"},
+        {file: "name0023", name: "name0023"},
+        {file: "name0024", name: "name0024"},
+        {file: "name0025", name: "name0025"},
+        {file: "name0026", name: "name0026"},
+        {file: "name0027", name: "name0027"},
+        {file: "name0028", name: "name0028"},
+        {file: "name0029", name: "name0029"},
+        {file: "name0030", name: "name0030"},
+        {file: "name0031", name: "name0031"},
+        {file: "name0032", name: "name0032"},
+        {file: "name0033", name: "name0033"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/ordering-tests-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/ordering-tests-expected.txt
new file mode 100644
index 0000000..d9ef091
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/ordering-tests-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL ordering0001 - ordering0001 assert_equals: expected "key=val5; key=val1; key=val2; key=val4" but got "key=val0; key=val2"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/ordering-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/ordering-tests.html
new file mode 100644
index 0000000..53cfae45
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/ordering-tests.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests basic cookie setting functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "ordering0001", name: "ordering0001"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/path-tests-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/path-tests-expected.txt
new file mode 100644
index 0000000..d901fbec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/path-tests-expected.txt
@@ -0,0 +1,36 @@
+This is a testharness.js-based test.
+FAIL path0001 - path0001 assert_equals: expected "x=y; a=b" but got "a=b"
+FAIL path0002 - path0002 assert_equals: expected "a=b; x=y" but got "x=y"
+FAIL path0003 - path0003 assert_equals: expected "a=b; x=y" but got "x=y"
+FAIL path0004 - path0004 assert_equals: expected "x=y; a=b" but got "a=b"
+PASS path0005 - path0005
+PASS path0006 - path0006
+FAIL path0007 - path0007 assert_equals: expected "foo=bar" but got ""
+PASS path0008 - path0008
+PASS path0009 - path0009
+FAIL path0010 - path0010 assert_equals: expected "foo=bar" but got ""
+PASS path0011 - path0011
+PASS path0012 - path0012
+PASS path0013 - path0013
+PASS path0014 - path0014
+FAIL path0015 - path0015 assert_equals: expected "foo=bar" but got ""
+FAIL path0016 - path0016 assert_equals: expected "foo=bar" but got ""
+FAIL path0017 - path0017 assert_equals: expected "foo=bar" but got ""
+PASS path0018 - path0018
+PASS path0019 - path0019
+PASS path0020 - path0020
+PASS path0021 - path0021
+PASS path0022 - path0022
+PASS path0023 - path0023
+PASS path0024 - path0024
+PASS path0025 - path0025
+FAIL path0026 - path0026 assert_equals: expected "foo=bar" but got ""
+PASS path0027 - path0027
+PASS path0028 - path0028
+FAIL disabled-path0029 - disabled-path0029 assert_equals: expected "foo=bar" but got ""
+FAIL path0029 - path0029 assert_equals: expected "a=b" but got ""
+PASS path0030 - path0030
+PASS path0031 - path0031
+FAIL path0032 - path0032 assert_equals: expected "foo=qux; foo=bar" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/path-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/path-tests.html
new file mode 100644
index 0000000..464f39be
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/path-tests.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests basic cookie setting functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "path0001", name: "path0001"},
+        {file: "path0002", name: "path0002"},
+        {file: "path0003", name: "path0003"},
+        {file: "path0004", name: "path0004"},
+        {file: "path0005", name: "path0005"},
+        {file: "path0006", name: "path0006"},
+        {file: "path0007", name: "path0007"},
+        {file: "path0008", name: "path0008"},
+        {file: "path0009", name: "path0009"},
+        {file: "path0010", name: "path0010"},
+        {file: "path0011", name: "path0011"},
+        {file: "path0012", name: "path0012"},
+        {file: "path0013", name: "path0013"},
+        {file: "path0014", name: "path0014"},
+        {file: "path0015", name: "path0015"},
+        {file: "path0016", name: "path0016"},
+        {file: "path0017", name: "path0017"},
+        {file: "path0018", name: "path0018"},
+        {file: "path0019", name: "path0019"},
+        {file: "path0020", name: "path0020"},
+        {file: "path0021", name: "path0021"},
+        {file: "path0022", name: "path0022"},
+        {file: "path0023", name: "path0023"},
+        {file: "path0024", name: "path0024"},
+        {file: "path0025", name: "path0025"},
+        {file: "path0026", name: "path0026"},
+        {file: "path0027", name: "path0027"},
+        {file: "path0028", name: "path0028"},
+        {file: "disabled-path0029", name: "disabled-path0029"},
+        {file: "path0029", name: "path0029"},
+        {file: "path0030", name: "path0030"},
+        {file: "path0031", name: "path0031"},
+        {file: "path0032", name: "path0032"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/all-tests.html.py-str b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/all-tests.html.py-str
new file mode 100644
index 0000000..37d630f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/all-tests.html.py-str
@@ -0,0 +1,79 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests basic cookie setting functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h1>Cookie Tests</h1>
+    This page is a debug page for cookie tests. To run a test in isolation,
+    append "?debug=" and the test id to this URL. E.g. to debug "value0001", use
+    this url:<br>
+    <a href="cookie-setter.py?debug=value0001">
+        cookie-setter.py?debug=value0001
+    </a><br>
+    <br>
+    To request, append "?file=" and the file name (stripped of
+    "-tests.html"). For value0001, this would be:<br>
+    <a href="cookie-setter.py?file=value0001">
+        cookie-setter.py?file=value0001
+    </a><br>
+    <br>
+    Please note, the general tests are one level higher:<br>
+    <a href="../attribute-tests.html"> ../attribute-tests.html </a><br>
+    <a href="../charset-tests.html"> ../charset-tests.html </a><br>
+    <a href="../chromium-tests.html"> ../chromium-tests.html </a><br>
+    <a href="../comma-tests.html"> ../comma-tests.html </a><br>
+    <a href="../domain-tests.html"> ../domain-tests.html </a><br>
+    <a href="../general-tests.html"> ../general-tests.html </a><br>
+    <a href="../mozilla-tests.html"> ../mozilla-tests.html </a><br>
+    <a href="../name-tests.html"> ../name-tests.html </a><br>
+    <a href="../ordering-tests.html"> ../ordering-tests.html </a><br>
+    <a href="../path-tests.html"> ../path-tests.html </a><br>
+    <a href="../value-tests.html"> ../value-tests.html </a><br>
+
+    <h2>Current Cookies</h2>
+    These are all cookies, currently set by the browser for this domain: <br>
+    <span id="cookies">(None)</span>
+    <script type="text/javascript">
+        setInterval(() => {
+            document.getElementById("cookies").textContent =
+                document.cookie || "(None)";
+        }, 500);
+    </script>
+
+    <h2>All Tests</h2>
+    Below, all tests are running. The Log contains messages (e.g. when cookies
+    are left over) and the IFrames list preserves all requested files and shows
+    which cookies were expected to show. <br>
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <!-- No element should come after this - the test harness appends the result
+    here when it finished running all of them. -->
+
+    <script type="text/javascript">
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [%s];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/cookie-http-state-template.js b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/cookie-http-state-template.js
index 470d46e..d737b38 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/cookie-http-state-template.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/cookie-http-state-template.js
@@ -1,16 +1,32 @@
 const SERVER_LOCATION = "resources";
 const SERVER_SCRIPT = SERVER_LOCATION + "/cookie-setter.py";
 
+/* Adds a div with "<time> [<tag>] - <message>" to the "#log" container.*/
+function log(message, tag) {
+  let log_str = document.createElement('div');
+  log_str.textContent = new Date().toTimeString().replace(/\s.+$/, '');
+  if (tag) {
+    log_str.textContent += " [" + tag + "] ";
+  }
+  log_str.textContent += " - " + message;
+  let log_container = document.getElementById("log");
+  log_container.appendChild(log_str);
+  log_container.scrollTo(0, log_container.scrollHeight);
+}
+
+/* Removes the "Cookie: " prefix and strip any leading or trailing whitespace.*/
 function stripPrefixAndWhitespace(cookie_text) {
   return cookie_text.replace(/^Cookie: /, '').replace(/^\s+|\s+$/g, '');
 }
 
+/* Returns the absolute path of the resource folder, ignoring any navigation. */
 function getLocalResourcesPath() {
   let replace = "(" + SERVER_LOCATION + "\/)*";  // Redundant location.
   replace += "[^\/]*$";  // Everything after the last "/".
   return location.pathname.replace(new RegExp(replace), "") + SERVER_LOCATION;
 }
 
+/* Returns the absolute server location ignoring any navgation.*/
 function getAbsoluteServerLocation() {
   // Replace the server location and everything coming after it ...
   let replace = SERVER_LOCATION + ".*$";
@@ -18,6 +34,7 @@
   return getLocalResourcesPath().replace(new RegExp(replace),'')+ SERVER_SCRIPT;
 }
 
+/* Expires a cookie by name by setting it's expiry date into the past.*/
 function expireCookie(name, expiry_date, path) {
   name = name || "";
   expiry_date = expiry_date || "Thu, 01 Jan 1970 00:00:00 UTC";
@@ -25,10 +42,16 @@
   document.cookie = name + "=; expires=" + expiry_date + "; path=" + path + ";";
 }
 
+/* Captures a snapshot of cookies with |parse| and allows to diff it with a
+second snapshot with |diffWith|. This allows to run tests even if cookies were
+previously set that would mess with the expected final set of Cookies.
+With |resetCookies|, all cookies set between first and second snapshot are
+expired. */
 function CookieManager() {
   this.initial_cookies = [];
 }
 
+/* Creates a snapshot of the current given document cookies.*/
 CookieManager.prototype.parse = document_cookies => {
   this.initial_cookies = [];
   document_cookies = document_cookies.replace(/^Cookie: /, '');
@@ -37,22 +60,25 @@
   }
 }
 
+/* Creates a diff of newly added cookies between the initial snapshot and the
+newly given cookies. The diff is stored for cleaning purposes. A second call
+will replace the the stored diff entirely.*/
 CookieManager.prototype.diffWith = document_cookies => {
   this.actual_cookies = document_cookies;
   for (let i in initial_cookies) {
     let no_spaces_cookie_regex =
-        new RegExp(/\s*[\;]*\s/.source + initial_cookies[i]);
-    this.actual_cookies = actual_cookies.replace(no_spaces_cookie_regex, '');
+        new RegExp(/\s*[\;]*\s/.source + initial_cookies[i].replace(/\\/, "\\\\"));
+    this.actual_cookies = this.actual_cookies.replace(no_spaces_cookie_regex, '');
   }
   return this.actual_cookies;
 }
 
+/* Cleans cookies between the first and the second snapshot.
+Some tests might set cookies to the root path or cookies without key. Both cases
+are dropped here.*/
 CookieManager.prototype.resetCookies = () => {
-  expireCookie(/*name=*/"");  // If a cookie without keys was accepted, drop it.
-  if (this.actual_cookies == "") {
-    return;  // There is nothing to reset here.
-  }
-  let cookies_to_delete = this.actual_cookies.split(/\s*;\s*/)
+  // If a cookie without keys was accepted, drop it additionally.
+  let cookies_to_delete = [""].concat(this.actual_cookies.split(/\s*;\s*/))
   for (let i in cookies_to_delete){
     expireCookie(cookies_to_delete[i].replace(/=.*$/, ""));
     // Drop cookies with same name that were set to the root path which happens
@@ -63,14 +89,27 @@
   }
 }
 
+/* Returns a new cookie test.
+The test creates an iframe where a |file| from the cookie-setter.py is loaded.
+This sets cookies which are diffed with an initial cookie snapshot and compared
+to the expectation that the server returned.
+Finally, it cleans up newly set cookies and all cookies in the root path or
+without key. */
 function createCookieTest(file) {
   return t => {
+    let iframe_container = document.getElementById("iframes");
     const iframe = document.createElement('iframe');
-    document.body.appendChild(iframe);
+    iframe_container.appendChild(iframe);
+    iframe_container.scrollTo(0, iframe_container.scrollHeight);
     let diff_tool = new CookieManager();
     t.add_cleanup(diff_tool.resetCookies);
     return new Promise((resolve, reject) => {
       diff_tool.parse(document.cookie);
+      if (diff_tool.initial_cookies.length > 0) {
+        // The cookies should ideally be empty. If that isn't the case, log it.
+        //Cookies with equal keys (e.g. foo=) may have unwanted side-effects.
+        log("Run with existing cookies: " + diff_tool.initial_cookies, file);
+      }
       window.addEventListener("message", t.step_func(e => {
         assert_true(!!e.data, "Message contains data")
         resolve(e.data);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/cookie-setter.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/cookie-setter.py
index 0418f4b7..758b142 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/cookie-setter.py
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/cookie-setter.py
@@ -1,5 +1,17 @@
-from os import path;
+from os import path
+from os import listdir
 
+"""
+The main purpose of this script is to set cookies based on files in this folder:
+    cookies/http-state/resources
+
+If a wpt server is running, navigate to
+    http://<server>/cookies/http-state/resources/cookie-setter.py
+which will run all cookie tests and explain the usage of this script in detail.
+
+If you want to run a test in isolation, append "?debug=" and the test id to the
+URL above.
+"""
 
 SETUP_FILE_TEMPLATE = "{}-test"
 EXPECTATION_FILE_TEMPLATE = "{}-expected"
@@ -7,13 +19,18 @@
 DEBUGGING_HTML_SCAFFOLD = "debugging-single-test.html.py-str"
 DEFAULT_RESOURCE_DIR = path.join("cookies", "http-state", "resources")
 DEFAULT_TEST_DIR = "test-files"
+ALL_TESTS = "all-tests.html.py-str"
 
 
 def dump_file(directory, filename):
+  """Reads a file into a string."""
   return open(path.join(directory, filename), "r").read()
 
 
 class CookieTestResponse(object):
+  """Loads the Set-Cookie header from a given file name. Relies on the naming
+  convention that ever test file is called '<test_id>-test' and every
+  expectation is called '<test_id>-expected'."""
   def __init__(self, file, root):
     super(CookieTestResponse, self).__init__()
     self.__test_file = SETUP_FILE_TEMPLATE.format(file)
@@ -22,16 +39,36 @@
     self.__test_files_dir = path.join(self.__resources_dir, DEFAULT_TEST_DIR)
 
   def cookie_setting_header(self):
+    """Returns the loaded header."""
     return dump_file(self.__test_files_dir, self.__test_file)
 
   def body_with_expectation(self):
+    """Returns a full HTML document which contains the expectation."""
     html_frame = dump_file(self.__resources_dir, EXPECTATION_HTML_SCAFFOLD)
-    expected_data = dump_file(self.__test_files_dir, self.__expectation_file);
+    expected_data = dump_file(self.__test_files_dir, self.__expectation_file)
     return html_frame.format(**{'data' : expected_data})
 
+def find_all_test_files(root):
+  """Retrieves all files from the test-files/ folder and returns them as
+  string pair as used in the JavaScript object. Sorted by filename."""
+  all_files = []
+  line_template = '{{file: "{filename}", name: "{filename}"}},'
+  for file in listdir(path.join(root, DEFAULT_RESOURCE_DIR, DEFAULT_TEST_DIR)):
+    if file.endswith('-test'):
+      name = file.replace('-test', '')
+      all_files.append(line_template.format(**{'filename' : name}))
+  all_files.sort()
+  return all_files
+
+def generate_for_all_tests(root):
+  """Returns an HTML document which loads and executes all tests in the
+  test-files/ directory."""
+  html_frame = dump_file(path.join(root, DEFAULT_RESOURCE_DIR), ALL_TESTS)
+  return html_frame % '\n'.join(find_all_test_files(root))
 
 def main(request, response):
   if "debug" in request.GET:
+    """If '?debug=' is set, return the document for a single test."""
     response.writer.write_status(200)
     response.writer.end_headers()
     html_frame = dump_file(path.join(request.doc_root, DEFAULT_RESOURCE_DIR),
@@ -40,15 +77,18 @@
     response.writer.write_content(test_file)
     return;
 
-  if not "file" in request.GET:
-    response.writer.write_status(404)
+  if "file" in request.GET:
+    """If '?file=' is set, send a cookie and a document which contains the
+    expectation of which cookies should be set by the browser in response."""
+    cookie_response = CookieTestResponse(request.GET['file'], request.doc_root)
+
+    response.writer.write_status(200)
+    response.writer.write(cookie_response.cookie_setting_header())
     response.writer.end_headers()
-    response.writer.write_content("The 'file' parameter is missing!")
+    response.writer.write_content(cookie_response.body_with_expectation())
     return;
 
-  cookie_response = CookieTestResponse(request.GET['file'], request.doc_root)
-
+  """Without any arguments, return documentation and run all available tests."""
   response.writer.write_status(200)
-  response.writer.write(cookie_response.cookie_setting_header())
   response.writer.end_headers()
-  response.writer.write_content(cookie_response.body_with_expectation())
+  response.writer.write_content(generate_for_all_tests(request.doc_root))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/debugging-single-test.html.py-str b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/debugging-single-test.html.py-str
index d34ff87..40d9809 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/debugging-single-test.html.py-str
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/debugging-single-test.html.py-str
@@ -8,9 +8,19 @@
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="cookie-http-state-template.js"></script>
+    <style type="text/css">
+        .scrollable {
+            height:150px;
+            overflow-y:scroll;
+        }
+    </style>
   </head>
   <body>
-    <div id="log"></div>
+    <h3>Log</h3>
+    <div id="log" class="scrollable"></div>
+    <h3>IFrame</h3>
+    <div id="iframes" class="toggleable scrollable"></div>
+    <h3>Test Results</h3>
     <script>
       setup({ explicit_timeout: true });
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0001-expected
new file mode 100644
index 0000000..8646afc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0001-expected
@@ -0,0 +1 @@
+Cookie: foo=春节回家路·春运完全手册
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0001-test
new file mode 100644
index 0000000..e89a36dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0001-test
@@ -0,0 +1 @@
+Set-Cookie: foo=春节回家路·春运完全手册
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0002-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0002-expected
new file mode 100644
index 0000000..fc7d256
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0002-expected
@@ -0,0 +1 @@
+Cookie: 春节回=家路·春运完全手册
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0002-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0002-test
new file mode 100644
index 0000000..70ce472
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0002-test
@@ -0,0 +1 @@
+Set-Cookie: 春节回=家路·春运完全手册
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0003-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0003-expected
new file mode 100644
index 0000000..1969bc8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0003-expected
@@ -0,0 +1 @@
+Cookie: 春节回=家路·春运
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0003-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0003-test
new file mode 100644
index 0000000..446e2fd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0003-test
@@ -0,0 +1 @@
+Set-Cookie: 春节回=家路·春运; 完全手册
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0004-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0004-expected
new file mode 100644
index 0000000..1d6d952
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0004-expected
@@ -0,0 +1 @@
+Cookie: foo="春节回家路·春运完全手册"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0004-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0004-test
new file mode 100644
index 0000000..1564ba4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/charset0004-test
@@ -0,0 +1 @@
+Set-Cookie: foo="春节回家路·春运完全手册"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0001-expected
new file mode 100644
index 0000000..1022f64a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0001-expected
@@ -0,0 +1 @@
+Cookie: a=b
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0001-test
new file mode 100644
index 0000000..4635f0e7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0001-test
@@ -0,0 +1 @@
+Set-Cookie: a=b
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0002-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0002-expected
new file mode 100644
index 0000000..64932dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0002-expected
@@ -0,0 +1 @@
+Cookie: aBc="zzz "
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0002-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0002-test
new file mode 100644
index 0000000..24ab0d2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0002-test
@@ -0,0 +1 @@
+Set-Cookie: aBc="zzz "   ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0003-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0003-expected
new file mode 100644
index 0000000..64932dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0003-expected
@@ -0,0 +1 @@
+Cookie: aBc="zzz "
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0003-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0003-test
new file mode 100644
index 0000000..bac41f99
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0003-test
@@ -0,0 +1 @@
+Set-Cookie: aBc="zzz " ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0004-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0004-expected
new file mode 100644
index 0000000..9be3e8f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0004-expected
@@ -0,0 +1 @@
+Cookie: aBc="zz
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0004-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0004-test
new file mode 100644
index 0000000..45cab2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0004-test
@@ -0,0 +1 @@
+Set-Cookie: aBc="zz;pp" ; ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0005-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0005-expected
new file mode 100644
index 0000000..9be3e8f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0005-expected
@@ -0,0 +1 @@
+Cookie: aBc="zz
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0005-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0005-test
new file mode 100644
index 0000000..c2dfebd3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0005-test
@@ -0,0 +1 @@
+Set-Cookie: aBc="zz ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0006-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0006-expected
new file mode 100644
index 0000000..8121145
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0006-expected
@@ -0,0 +1 @@
+Cookie: aBc="zzz "   "ppp"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0006-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0006-test
new file mode 100644
index 0000000..a9da268
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0006-test
@@ -0,0 +1 @@
+Set-Cookie: aBc="zzz "   "ppp"  ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0007-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0007-expected
new file mode 100644
index 0000000..8121145
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0007-expected
@@ -0,0 +1 @@
+Cookie: aBc="zzz "   "ppp"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0007-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0007-test
new file mode 100644
index 0000000..dca20b8e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0007-test
@@ -0,0 +1 @@
+Set-Cookie: aBc="zzz "   "ppp" ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0008-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0008-expected
new file mode 100644
index 0000000..602c0e0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0008-expected
@@ -0,0 +1 @@
+Cookie: aBc=A"B
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0008-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0008-test
new file mode 100644
index 0000000..ece6c66
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0008-test
@@ -0,0 +1 @@
+Set-Cookie: aBc=A"B ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0009-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0009-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0009-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0009-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0009-test
new file mode 100644
index 0000000..eecdae6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0009-test
@@ -0,0 +1 @@
+Set-Cookie: BLAHHH; path=/;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0010-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0010-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0010-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0010-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0010-test
new file mode 100644
index 0000000..17a18ea
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0010-test
@@ -0,0 +1 @@
+Set-Cookie: "BLA\"HHH"; path=/;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0011-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0011-expected
new file mode 100644
index 0000000..2062e23
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0011-expected
@@ -0,0 +1 @@
+Cookie: a="B
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0011-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0011-test
new file mode 100644
index 0000000..4000f26
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0011-test
@@ -0,0 +1 @@
+Set-Cookie: a="B
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0012-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0012-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0012-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0012-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0012-test
new file mode 100644
index 0000000..b33a4e7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0012-test
@@ -0,0 +1 @@
+Set-Cookie: =ABC
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0013-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0013-expected
new file mode 100644
index 0000000..5d2d0609
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0013-expected
@@ -0,0 +1 @@
+Cookie: ABC=
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0013-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0013-test
new file mode 100644
index 0000000..f61a087
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0013-test
@@ -0,0 +1 @@
+Set-Cookie: ABC=;  path = /
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0014-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0014-expected
new file mode 100644
index 0000000..21bfd13
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0014-expected
@@ -0,0 +1 @@
+Cookie: A=BC
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0014-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0014-test
new file mode 100644
index 0000000..edbb793
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0014-test
@@ -0,0 +1 @@
+Set-Cookie:   A  = BC  ;foo;;;   bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0015-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0015-expected
new file mode 100644
index 0000000..b968e74
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0015-expected
@@ -0,0 +1 @@
+Cookie: A=== BC
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0015-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0015-test
new file mode 100644
index 0000000..80d3236
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0015-test
@@ -0,0 +1 @@
+Set-Cookie:   A=== BC  ;foo;;;   bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0016-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0016-expected
new file mode 100644
index 0000000..11d8efa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0016-expected
@@ -0,0 +1 @@
+Cookie: foo="zohNumRKgI0oxyhSsV3Z7D"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0016-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0016-test
new file mode 100644
index 0000000..8abf1202
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0016-test
@@ -0,0 +1 @@
+Set-Cookie: foo="zohNumRKgI0oxyhSsV3Z7D"  ; expires=Sun, 18-Apr-2027 21:06:29 GMT ; path=/  ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0017-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0017-expected
new file mode 100644
index 0000000..6bb8448
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0017-expected
@@ -0,0 +1 @@
+Cookie: foo=zohNumRKgI0oxyhSsV3Z7D
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0017-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0017-test
new file mode 100644
index 0000000..94cce02
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0017-test
@@ -0,0 +1 @@
+Set-Cookie: foo=zohNumRKgI0oxyhSsV3Z7D  ; expires=Sun, 18-Apr-2027 21:06:29 GMT ; path=/  ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0018-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0018-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0018-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0018-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0018-test
new file mode 100644
index 0000000..145ad2c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0018-test
@@ -0,0 +1 @@
+Set-Cookie:
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0019-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0019-expected
new file mode 100644
index 0000000..b6df50e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0019-expected
@@ -0,0 +1 @@
+Cookie: a=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0019-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0019-test
new file mode 100644
index 0000000..e3bcc4a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0019-test
@@ -0,0 +1 @@
+Set-Cookie: a=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0021-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0021-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0021-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0021-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0021-test
new file mode 100644
index 0000000..145ad2c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/chromium0021-test
@@ -0,0 +1 @@
+Set-Cookie:
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0001-expected
new file mode 100644
index 0000000..ab55cea01
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0001-expected
@@ -0,0 +1 @@
+Cookie: foo=bar, baz=qux
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0001-test
new file mode 100644
index 0000000..5b3678d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0001-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar, baz=qux
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0002-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0002-expected
new file mode 100644
index 0000000..33590a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0002-expected
@@ -0,0 +1 @@
+Cookie: foo="bar, baz=qux"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0002-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0002-test
new file mode 100644
index 0000000..164c0a93
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0002-test
@@ -0,0 +1 @@
+Set-Cookie: foo="bar, baz=qux"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0003-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0003-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0003-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0003-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0003-test
new file mode 100644
index 0000000..213d3a5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0003-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; b,az=qux
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0004-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0004-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0004-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0004-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0004-test
new file mode 100644
index 0000000..e93f6f406
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0004-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; baz=q,ux
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0005-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0005-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0005-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0005-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0005-test
new file mode 100644
index 0000000..eeb040d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0005-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; Max-Age=50,399
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0006-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0006-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0006-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0006-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0006-test
new file mode 100644
index 0000000..a3eaff0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0006-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; Expires=Fri, 07 Aug 2019 08:04:19 GMT
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0007-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0007-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0007-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0007-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0007-test
new file mode 100644
index 0000000..29fb0a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/comma0007-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; Expires=Fri 07 Aug 2019 08:04:19 GMT, baz=qux
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0020-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0020-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0020-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0020-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0020-test
new file mode 100644
index 0000000..67cefa3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0020-test
@@ -0,0 +1 @@
+Set-Cookie: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0022-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0022-expected
new file mode 100644
index 0000000..8ac1321
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0022-expected
@@ -0,0 +1 @@
+Cookie: AAA=BB
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0022-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0022-test
new file mode 100644
index 0000000..1a8f35ff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0022-test
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0023-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0023-expected
new file mode 100644
index 0000000..8ac1321
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0023-expected
@@ -0,0 +1 @@
+Cookie: AAA=BB
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0023-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0023-test
new file mode 100644
index 0000000..76a8ed4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-chromium0023-test
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-path0029-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-path0029-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-path0029-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-path0029-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-path0029-test
new file mode 100644
index 0000000..d7219d59
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/disabled-path0029-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/bar
+Location: /cookie-parser-result/f%6Fo/bar?disabled-path0029
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0001-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0001-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0001-test
new file mode 100644
index 0000000..fd8926d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0001-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0001
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0002-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0002-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0002-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0002-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0002-test
new file mode 100644
index 0000000..a6ad2a8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0002-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org
+Location: http://sibling.example.org:8888/cookie-parser-result?domain0002
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0003-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0003-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0003-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0003-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0003-test
new file mode 100644
index 0000000..ff051f48
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0003-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0003
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0004-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0004-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0004-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0004-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0004-test
new file mode 100644
index 0000000..f05029b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0004-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org
+Location: http://subdomain.home.example.org:8888/cookie-parser-result?domain0004
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0005-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0005-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0005-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0005-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0005-test
new file mode 100644
index 0000000..697ab18
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0005-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.home.example.org
+Location: http://subdomain.home.example.org:8888/cookie-parser-result?domain0005
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0006-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0006-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0006-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0006-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0006-test
new file mode 100644
index 0000000..fe5f90df
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0006-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.home.example.org
+Location: http://sibling.example.org:8888/cookie-parser-result?domain0006
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0007-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0007-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0007-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0007-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0007-test
new file mode 100644
index 0000000..1d2c6fce
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0007-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=sibling.example.org
+Location: http://sibling.example.org:8888/cookie-parser-result?domain0007
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0008-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0008-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0008-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0008-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0008-test
new file mode 100644
index 0000000..491c52d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0008-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0008
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0009-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0009-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0009-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0009-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0009-test
new file mode 100644
index 0000000..736e69f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0009-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0009
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0010-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0010-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0010-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0010-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0010-test
new file mode 100644
index 0000000..5bf212f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0010-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=..home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0010
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0011-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0011-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0011-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0011-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0011-test
new file mode 100644
index 0000000..e1c54db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0011-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home..example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0011
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0012-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0012-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0012-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0012-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0012-test
new file mode 100644
index 0000000..a189f380
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0012-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=  .home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0012
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0013-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0013-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0013-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0013-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0013-test
new file mode 100644
index 0000000..8493146
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0013-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=  .  home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0013
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0014-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0014-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0014-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0014-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0014-test
new file mode 100644
index 0000000..adccd0d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0014-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org.
+Location: http://home.example.org:8888/cookie-parser-result?domain0014
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0015-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0015-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0015-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0015-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0015-test
new file mode 100644
index 0000000..c123ba9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0015-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org..
+Location: http://home.example.org:8888/cookie-parser-result?domain0015
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0016-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0016-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0016-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0016-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0016-test
new file mode 100644
index 0000000..9136ac4d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0016-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org .
+Location: http://home.example.org:8888/cookie-parser-result?domain0016
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0017-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0017-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0017-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0017-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0017-test
new file mode 100644
index 0000000..8e876b8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0017-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0017
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0018-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0018-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0018-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0018-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0018-test
new file mode 100644
index 0000000..3763f962
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0018-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.org.
+Location: http://home.example.org:8888/cookie-parser-result?domain0018
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0019-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0019-expected
new file mode 100644
index 0000000..1f359c4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0019-expected
@@ -0,0 +1 @@
+Cookie: foo=bar; foo2=bar2
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0019-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0019-test
new file mode 100644
index 0000000..f5aebfd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0019-test
@@ -0,0 +1,3 @@
+Set-Cookie: foo=bar; domain=home.example.org
+Set-Cookie: foo2=bar2; domain=.home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0019
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0020-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0020-expected
new file mode 100644
index 0000000..5eb3ac42
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0020-expected
@@ -0,0 +1 @@
+Cookie: foo2=bar2; foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0020-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0020-test
new file mode 100644
index 0000000..1c7a61c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0020-test
@@ -0,0 +1,3 @@
+Set-Cookie: foo2=bar2; domain=.home.example.org
+Set-Cookie: foo=bar; domain=home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0020
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0021-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0021-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0021-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0021-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0021-test
new file mode 100644
index 0000000..895744e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0021-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain="home.example.org"
+Location: http://home.example.org:8888/cookie-parser-result?domain0021
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0022-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0022-expected
new file mode 100644
index 0000000..1f359c4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0022-expected
@@ -0,0 +1 @@
+Cookie: foo=bar; foo2=bar2
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0022-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0022-test
new file mode 100644
index 0000000..6eeae00
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0022-test
@@ -0,0 +1,3 @@
+Set-Cookie: foo=bar; domain=home.example.org
+Set-Cookie: foo2=bar2; domain=.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0022
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0023-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0023-expected
new file mode 100644
index 0000000..5eb3ac42
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0023-expected
@@ -0,0 +1 @@
+Cookie: foo2=bar2; foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0023-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0023-test
new file mode 100644
index 0000000..91a81ed
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0023-test
@@ -0,0 +1,3 @@
+Set-Cookie: foo2=bar2; domain=.example.org
+Set-Cookie: foo=bar; domain=home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0023
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0024-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0024-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0024-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0024-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0024-test
new file mode 100644
index 0000000..63157b9b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0024-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.example.org; domain=home.example.org
+Location: http://sibling.example.org:8888/cookie-parser-result?domain0024
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0025-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0025-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0025-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0025-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0025-test
new file mode 100644
index 0000000..353c3bb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0025-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org; domain=.example.org
+Location: http://sibling.example.org:8888/cookie-parser-result?domain0025
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0026-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0026-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0026-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0026-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0026-test
new file mode 100644
index 0000000..3b14e30
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0026-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.eXaMpLe.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0026
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0027-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0027-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0027-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0027-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0027-test
new file mode 100644
index 0000000..8b7adbd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0027-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org:8888
+Location: http://home.example.org:8888/cookie-parser-result?domain0027
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0028-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0028-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0028-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0028-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0028-test
new file mode 100644
index 0000000..ab8f2c4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0028-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=subdomain.home.example.org
+Location: http://subdomain.home.example.org:8888/cookie-parser-result?domain0028
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0029-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0029-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0029-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0029-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0029-test
new file mode 100644
index 0000000..cfab57a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0029-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar
+Location: http://subdomain.home.example.org:8888/cookie-parser-result?domain0029
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0031-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0031-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0031-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0031-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0031-test
new file mode 100644
index 0000000..6eccb78b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0031-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org; domain=.example.org
+Location: http://sibling.example.org:8888/cookie-parser-result?domain0031
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0033-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0033-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0033-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0033-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0033-test
new file mode 100644
index 0000000..7fdcc83
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0033-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org
+Location: http://hoMe.eXaMplE.org:8888/cookie-parser-result?domain0033
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0034-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0034-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0034-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0034-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0034-test
new file mode 100644
index 0000000..d3f4723
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0034-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org; domain=home.example.com
+Location: http://home.example.org:8888/cookie-parser-result?domain0034
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0035-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0035-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0035-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0035-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0035-test
new file mode 100644
index 0000000..967d248c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0035-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.com; domain=home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0035
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0036-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0036-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0036-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0036-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0036-test
new file mode 100644
index 0000000..a618ec4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0036-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org; domain=home.example.com; domain=home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0036
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0037-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0037-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0037-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0037-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0037-test
new file mode 100644
index 0000000..c61fe8f2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0037-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.com; domain=home.example.org; domain=home.example.com
+Location: http://home.example.org:8888/cookie-parser-result?domain0037
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0038-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0038-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0038-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0038-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0038-test
new file mode 100644
index 0000000..2d8fcca
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0038-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org; domain=home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0038
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0039-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0039-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0039-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0039-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0039-test
new file mode 100644
index 0000000..3225b28
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0039-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=home.example.org; domain=example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0039
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0040-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0040-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0040-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0040-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0040-test
new file mode 100644
index 0000000..3e32fa8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0040-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=example.org; domain=home.example.org
+Location: http://home.example.org:8888/cookie-parser-result?domain0040
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0041-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0041-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0041-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0041-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0041-test
new file mode 100644
index 0000000..6fbc4ad
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0041-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.sibling.example.org
+Location: http://sibling.example.org:8888/cookie-parser-result?domain0041
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0042-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0042-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0042-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0042-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0042-test
new file mode 100644
index 0000000..9258624
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/domain0042-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=.sibling.home.example.org
+Location: http://sibling.home.example.org:8888/cookie-parser-result?domain0042
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0001-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0001-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0001-test
new file mode 100644
index 0000000..53f98e23
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0001-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; max-age=-1
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0002-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0002-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0002-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0002-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0002-test
new file mode 100644
index 0000000..a79c5be41
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0002-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; max-age=0
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0003-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0003-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0003-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0003-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0003-test
new file mode 100644
index 0000000..785c326
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0003-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; expires=Thu, 10 Apr 1980 16:33:12 GMT
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0004-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0004-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0004-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0004-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0004-test
new file mode 100644
index 0000000..567134f5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0004-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; max-age=60
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0005-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0005-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0005-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0005-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0005-test
new file mode 100644
index 0000000..0fa350a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0005-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; max-age=-20
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0006-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0006-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0006-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0006-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0006-test
new file mode 100644
index 0000000..567134f5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0006-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; max-age=60
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0007-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0007-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0007-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0007-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0007-test
new file mode 100644
index 0000000..785c326
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0007-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; expires=Thu, 10 Apr 1980 16:33:12 GMT
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0008-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0008-expected
new file mode 100644
index 0000000..0f62bab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0008-expected
@@ -0,0 +1 @@
+Cookie: foo=bar; foo1=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0008-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0008-test
new file mode 100644
index 0000000..d6350d6c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0008-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; max-age=60
+Set-Cookie: foo1=bar; max-age=60
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0009-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0009-expected
new file mode 100644
index 0000000..555fbaf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0009-expected
@@ -0,0 +1 @@
+Cookie: foo1=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0009-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0009-test
new file mode 100644
index 0000000..84944d4d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0009-test
@@ -0,0 +1,3 @@
+Set-Cookie: foo=bar; max-age=60
+Set-Cookie: foo1=bar; max-age=60
+Set-Cookie: foo=differentvalue; max-age=0
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0010-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0010-expected
new file mode 100644
index 0000000..555fbaf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0010-expected
@@ -0,0 +1 @@
+Cookie: foo1=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0010-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0010-test
new file mode 100644
index 0000000..13477a4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0010-test
@@ -0,0 +1,4 @@
+Set-Cookie: foo=bar; max-age=60
+Set-Cookie: foo1=bar; max-age=60
+Set-Cookie: foo=differentvalue; max-age=0
+Set-Cookie: foo2=evendifferentvalue; max-age=0
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0011-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0011-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0011-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0011-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0011-test
new file mode 100644
index 0000000..bba2053
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0011-test
@@ -0,0 +1 @@
+Set-Cookie: test=parser; domain=.parser.test; ;; ;=; ,,, ===,abc,=; abracadabra! max-age=20;=;;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0012-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0012-expected
new file mode 100644
index 0000000..10e669a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0012-expected
@@ -0,0 +1 @@
+Cookie: test="fubar! = foo
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0012-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0012-test
new file mode 100644
index 0000000..9ba773f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0012-test
@@ -0,0 +1,2 @@
+Set-Cookie: test="fubar! = foo;bar\";" parser; max-age=6
+Set-Cookie: five; max-age=2.63,
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0013-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0013-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0013-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0013-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0013-test
new file mode 100644
index 0000000..d491267e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0013-test
@@ -0,0 +1,2 @@
+Set-Cookie: test=kill; max-age=0
+Set-Cookie: five; max-age=0
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0014-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0014-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0014-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0014-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0014-test
new file mode 100644
index 0000000..4cc1f450
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0014-test
@@ -0,0 +1 @@
+Set-Cookie: six
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0015-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0015-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0015-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0015-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0015-test
new file mode 100644
index 0000000..13254db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0015-test
@@ -0,0 +1,2 @@
+Set-Cookie: six
+Set-Cookie: seven
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0016-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0016-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0016-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0016-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0016-test
new file mode 100644
index 0000000..6aab3e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0016-test
@@ -0,0 +1,3 @@
+Set-Cookie: six
+Set-Cookie: seven
+Set-Cookie:  =eight
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0017-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0017-expected
new file mode 100644
index 0000000..fc9a2496
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0017-expected
@@ -0,0 +1 @@
+Cookie: test=six
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0017-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0017-test
new file mode 100644
index 0000000..b6c297c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/mozilla0017-test
@@ -0,0 +1,4 @@
+Set-Cookie: six
+Set-Cookie: seven
+Set-Cookie:  =eight
+Set-Cookie: test=six
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0001-expected
new file mode 100644
index 0000000..9652792
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0001-expected
@@ -0,0 +1 @@
+Cookie: a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0001-test
new file mode 100644
index 0000000..3ce5f5f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0001-test
@@ -0,0 +1 @@
+Set-Cookie: a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0002-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0002-expected
new file mode 100644
index 0000000..d4d3cda8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0002-expected
@@ -0,0 +1 @@
+Cookie: 1=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0002-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0002-test
new file mode 100644
index 0000000..d6eac8a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0002-test
@@ -0,0 +1 @@
+Set-Cookie: 1=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0003-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0003-expected
new file mode 100644
index 0000000..0c00f45
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0003-expected
@@ -0,0 +1 @@
+Cookie: $=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0003-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0003-test
new file mode 100644
index 0000000..7ea9615
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0003-test
@@ -0,0 +1 @@
+Set-Cookie: $=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0004-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0004-expected
new file mode 100644
index 0000000..b079529
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0004-expected
@@ -0,0 +1 @@
+Cookie: !a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0004-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0004-test
new file mode 100644
index 0000000..99f0e61
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0004-test
@@ -0,0 +1 @@
+Set-Cookie: !a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0005-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0005-expected
new file mode 100644
index 0000000..a0f031b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0005-expected
@@ -0,0 +1 @@
+Cookie: @a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0005-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0005-test
new file mode 100644
index 0000000..9e33e0c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0005-test
@@ -0,0 +1 @@
+Set-Cookie: @a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0006-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0006-expected
new file mode 100644
index 0000000..ee0e7d7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0006-expected
@@ -0,0 +1 @@
+Cookie: #a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0006-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0006-test
new file mode 100644
index 0000000..fbd03632
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0006-test
@@ -0,0 +1 @@
+Set-Cookie: #a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0007-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0007-expected
new file mode 100644
index 0000000..6d6e56e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0007-expected
@@ -0,0 +1 @@
+Cookie: $a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0007-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0007-test
new file mode 100644
index 0000000..d41e64b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0007-test
@@ -0,0 +1 @@
+Set-Cookie: $a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0008-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0008-expected
new file mode 100644
index 0000000..a4b8c242
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0008-expected
@@ -0,0 +1 @@
+Cookie: %a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0008-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0008-test
new file mode 100644
index 0000000..7afcf70
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0008-test
@@ -0,0 +1 @@
+Set-Cookie: %a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0009-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0009-expected
new file mode 100644
index 0000000..49506ac
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0009-expected
@@ -0,0 +1 @@
+Cookie: ^a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0009-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0009-test
new file mode 100644
index 0000000..f40d2c4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0009-test
@@ -0,0 +1 @@
+Set-Cookie: ^a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0010-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0010-expected
new file mode 100644
index 0000000..1e725578
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0010-expected
@@ -0,0 +1 @@
+Cookie: &a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0010-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0010-test
new file mode 100644
index 0000000..fb4fd92
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0010-test
@@ -0,0 +1 @@
+Set-Cookie: &a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0011-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0011-expected
new file mode 100644
index 0000000..260d702
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0011-expected
@@ -0,0 +1 @@
+Cookie: *a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0011-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0011-test
new file mode 100644
index 0000000..b36b723
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0011-test
@@ -0,0 +1 @@
+Set-Cookie: *a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0012-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0012-expected
new file mode 100644
index 0000000..0a2686a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0012-expected
@@ -0,0 +1 @@
+Cookie: (a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0012-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0012-test
new file mode 100644
index 0000000..6927aac6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0012-test
@@ -0,0 +1 @@
+Set-Cookie: (a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0013-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0013-expected
new file mode 100644
index 0000000..87dec78b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0013-expected
@@ -0,0 +1 @@
+Cookie: )a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0013-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0013-test
new file mode 100644
index 0000000..59ada98
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0013-test
@@ -0,0 +1 @@
+Set-Cookie: )a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0014-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0014-expected
new file mode 100644
index 0000000..82bfe0e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0014-expected
@@ -0,0 +1 @@
+Cookie: -a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0014-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0014-test
new file mode 100644
index 0000000..a113e99
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0014-test
@@ -0,0 +1 @@
+Set-Cookie: -a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0015-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0015-expected
new file mode 100644
index 0000000..390b77b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0015-expected
@@ -0,0 +1 @@
+Cookie: _a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0015-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0015-test
new file mode 100644
index 0000000..60fc074
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0015-test
@@ -0,0 +1 @@
+Set-Cookie: _a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0016-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0016-expected
new file mode 100644
index 0000000..7d4d9e33
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0016-expected
@@ -0,0 +1 @@
+Cookie: +=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0016-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0016-test
new file mode 100644
index 0000000..371dbcd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0016-test
@@ -0,0 +1 @@
+Set-Cookie: +=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0017-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0017-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0017-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0017-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0017-test
new file mode 100644
index 0000000..0561431
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0017-test
@@ -0,0 +1 @@
+Set-Cookie: =a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0018-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0018-expected
new file mode 100644
index 0000000..9652792
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0018-expected
@@ -0,0 +1 @@
+Cookie: a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0018-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0018-test
new file mode 100644
index 0000000..e86a4836
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0018-test
@@ -0,0 +1 @@
+Set-Cookie: a =bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0019-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0019-expected
new file mode 100644
index 0000000..8d0bc2d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0019-expected
@@ -0,0 +1 @@
+Cookie: "a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0019-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0019-test
new file mode 100644
index 0000000..d48e3f6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0019-test
@@ -0,0 +1 @@
+Set-Cookie: "a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0020-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0020-expected
new file mode 100644
index 0000000..aa9cd6d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0020-expected
@@ -0,0 +1 @@
+Cookie: "a=b"=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0020-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0020-test
new file mode 100644
index 0000000..b84f64d17
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0020-test
@@ -0,0 +1 @@
+Set-Cookie: "a=b"=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0021-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0021-expected
new file mode 100644
index 0000000..206ff76
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0021-expected
@@ -0,0 +1 @@
+Cookie: "a=qux
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0021-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0021-test
new file mode 100644
index 0000000..56b319e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0021-test
@@ -0,0 +1,2 @@
+Set-Cookie: "a=b"=bar
+Set-Cookie: "a=qux
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0022-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0022-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0022-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0022-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0022-test
new file mode 100644
index 0000000..cc59ff1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0022-test
@@ -0,0 +1 @@
+Set-Cookie:    foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0023-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0023-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0023-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0023-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0023-test
new file mode 100644
index 0000000..b7f9cc2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0023-test
@@ -0,0 +1 @@
+Set-Cookie: foo;bar=baz
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0024-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0024-expected
new file mode 100644
index 0000000..5ac4f25
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0024-expected
@@ -0,0 +1 @@
+Cookie: $Version=1
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0024-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0024-test
new file mode 100644
index 0000000..da7b696
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0024-test
@@ -0,0 +1 @@
+Set-Cookie: $Version=1; foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0025-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0025-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0025-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0025-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0025-test
new file mode 100644
index 0000000..708f006
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0025-test
@@ -0,0 +1 @@
+Set-Cookie: ===a=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0026-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0026-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0026-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0026-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0026-test
new file mode 100644
index 0000000..bbeb77a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0026-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0027-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0027-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0027-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0027-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0027-test
new file mode 100644
index 0000000..d222227
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0027-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar    ;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0028-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0028-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0028-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0028-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0028-test
new file mode 100644
index 0000000..1c197e3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0028-test
@@ -0,0 +1 @@
+Set-Cookie: =a
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0029-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0029-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0029-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0029-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0029-test
new file mode 100644
index 0000000..4421246
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0029-test
@@ -0,0 +1 @@
+Set-Cookie: =
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0030-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0030-expected
new file mode 100644
index 0000000..a391380
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0030-expected
@@ -0,0 +1 @@
+Cookie: foo bar=baz
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0030-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0030-test
new file mode 100644
index 0000000..cf3ff16
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0030-test
@@ -0,0 +1 @@
+Set-Cookie: foo bar=baz
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0031-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0031-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0031-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0031-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0031-test
new file mode 100644
index 0000000..9394184
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0031-test
@@ -0,0 +1 @@
+Set-Cookie: "foo;bar"=baz
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0032-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0032-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0032-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0032-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0032-test
new file mode 100644
index 0000000..93fc9752
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0032-test
@@ -0,0 +1 @@
+Set-Cookie: "foo\"bar;baz"=qux
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0033-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0033-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0033-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0033-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0033-test
new file mode 100644
index 0000000..7bbdd89
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/name0033-test
@@ -0,0 +1,2 @@
+Set-Cookie: =foo=bar
+Set-Cookie: aaa
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0030-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0030-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0030-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0030-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0030-test
new file mode 100644
index 0000000..3ab75aa3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0030-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=
+Location: http://home.example.org:8888/cookie-parser-result?optional-domain0030
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0041-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0041-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0041-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0041-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0041-test
new file mode 100644
index 0000000..b85a6c9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0041-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=example.org; domain=
+Location: http://home.example.org:8888/cookie-parser-result?optional-domain0041
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0042-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0042-expected
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0042-expected
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0042-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0042-test
new file mode 100644
index 0000000..524d192
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0042-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=foo.example.org; domain=
+Location: http://home.example.org:8888/cookie-parser-result?optional-domain0042
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0043-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0043-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0043-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0043-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0043-test
new file mode 100644
index 0000000..506cdc4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/optional-domain0043-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; domain=foo.example.org; domain=
+Location: http://subdomain.home.example.org:8888/cookie-parser-result?optional-domain0043
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/ordering0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/ordering0001-expected
new file mode 100644
index 0000000..3d81959
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/ordering0001-expected
@@ -0,0 +1 @@
+Cookie: key=val5; key=val1; key=val2; key=val4
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/ordering0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/ordering0001-test
new file mode 100644
index 0000000..ba6e85c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/ordering0001-test
@@ -0,0 +1,7 @@
+Set-Cookie: key=val0;
+Set-Cookie: key=val1; path=/cookie-parser-result
+Set-Cookie: key=val2; path=/
+Set-Cookie: key=val3; path=/bar
+Set-Cookie: key=val4; domain=.example.org
+Set-Cookie: key=val5; domain=.example.org; path=/cookie-parser-result/foo
+Location: /cookie-parser-result/foo/baz?ordering0001
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0001-expected
new file mode 100644
index 0000000..785d0a2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0001-expected
@@ -0,0 +1 @@
+Cookie: x=y; a=b
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0001-test
new file mode 100644
index 0000000..ed81cb9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0001-test
@@ -0,0 +1,2 @@
+Set-Cookie: a=b; path=/
+Set-Cookie: x=y; path=/cookie-parser-result
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0002-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0002-expected
new file mode 100644
index 0000000..2314ba0d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0002-expected
@@ -0,0 +1 @@
+Cookie: a=b; x=y
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0002-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0002-test
new file mode 100644
index 0000000..2249328
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0002-test
@@ -0,0 +1,2 @@
+Set-Cookie: a=b; path=/cookie-parser-result
+Set-Cookie: x=y; path=/
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0003-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0003-expected
new file mode 100644
index 0000000..2314ba0d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0003-expected
@@ -0,0 +1 @@
+Cookie: a=b; x=y
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0003-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0003-test
new file mode 100644
index 0000000..e4b5a4b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0003-test
@@ -0,0 +1,2 @@
+Set-Cookie: x=y; path=/
+Set-Cookie: a=b; path=/cookie-parser-result
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0004-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0004-expected
new file mode 100644
index 0000000..785d0a2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0004-expected
@@ -0,0 +1 @@
+Cookie: x=y; a=b
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0004-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0004-test
new file mode 100644
index 0000000..a80ea6e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0004-test
@@ -0,0 +1,2 @@
+Set-Cookie: x=y; path=/cookie-parser-result
+Set-Cookie: a=b; path=/
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0005-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0005-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0005-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0005-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0005-test
new file mode 100644
index 0000000..6447c91
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0005-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0006-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0006-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0006-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0006-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0006-test
new file mode 100644
index 0000000..07ecf27b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0006-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar
+Set-Cookie: foo=qux; path=/cookie-parser-result/foo
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0007-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0007-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0007-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0007-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0007-test
new file mode 100644
index 0000000..66a54de7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0007-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo
+Location: /cookie-parser-result/foo?path0007
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0008-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0008-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0008-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0008-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0008-test
new file mode 100644
index 0000000..b10012d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0008-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo
+Location: /cookie-parser-result/bar?path0008
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0009-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0009-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0009-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0009-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0009-test
new file mode 100644
index 0000000..874f21ba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0009-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux
+Location: /cookie-parser-result/foo?path0009
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0010-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0010-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0010-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0010-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0010-test
new file mode 100644
index 0000000..0ea62fd5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0010-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux
+Location: /cookie-parser-result/foo/qux?path0010
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0011-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0011-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0011-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0011-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0011-test
new file mode 100644
index 0000000..35ea062
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0011-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux
+Location: /cookie-parser-result/bar/qux?path0011
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0012-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0012-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0012-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0012-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0012-test
new file mode 100644
index 0000000..4c0577897
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0012-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux
+Location: /cookie-parser-result/foo/baz?path0012
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0013-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0013-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0013-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0013-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0013-test
new file mode 100644
index 0000000..d17b99b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0013-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux/
+Location: /cookie-parser-result/foo/baz?path0013
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0014-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0014-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0014-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0014-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0014-test
new file mode 100644
index 0000000..a953e707b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0014-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux/
+Location: /cookie-parser-result/foo/qux?path0014
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0015-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0015-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0015-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0015-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0015-test
new file mode 100644
index 0000000..253a9bb2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0015-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux/
+Location: /cookie-parser-result/foo/qux/?path0015
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0016-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0016-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0016-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0016-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0016-test
new file mode 100644
index 0000000..433faeb9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0016-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/
+Location: /cookie-parser-result/foo/qux?path0016
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0017-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0017-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0017-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0017-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0017-test
new file mode 100644
index 0000000..a602d7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0017-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/
+Location: /cookie-parser-result/foo//qux?path0017
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0018-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0018-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0018-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0018-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0018-test
new file mode 100644
index 0000000..37040da
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0018-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/
+Location: /cookie-parser-result/fooqux?path0018
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0019-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0019-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0019-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0019-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0019-test
new file mode 100644
index 0000000..6d49860
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0019-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; path
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0020-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0020-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0020-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0020-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0020-test
new file mode 100644
index 0000000..603eded
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0020-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; path=
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0021-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0021-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0021-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0021-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0021-test
new file mode 100644
index 0000000..4577c51
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0021-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; path=/
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0022-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0022-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0022-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0022-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0022-test
new file mode 100644
index 0000000..4aade9e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0022-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; path= /
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0023-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0023-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0023-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0023-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0023-test
new file mode 100644
index 0000000..f14d683
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0023-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; Path=/cookie-PARSER-result
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0024-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0024-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0024-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0024-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0024-test
new file mode 100644
index 0000000..18a71c87
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0024-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux?
+Location: /cookie-parser-result/foo/qux?path0024
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0025-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0025-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0025-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0025-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0025-test
new file mode 100644
index 0000000..b6c497b9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0025-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux#
+Location: /cookie-parser-result/foo/qux?path0025
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0026-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0026-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0026-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0026-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0026-test
new file mode 100644
index 0000000..ffc3b7a5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0026-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/foo/qux;
+Location: /cookie-parser-result/foo/qux?path0026
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0027-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0027-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0027-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0027-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0027-test
new file mode 100644
index 0000000..bd021f3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0027-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path="/cookie-parser-result/foo/qux;"
+Location: /cookie-parser-result/foo/qux?path0027
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0028-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0028-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0028-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0028-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0028-test
new file mode 100644
index 0000000..3bcd6a5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0028-test
@@ -0,0 +1,2 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result/f%6Fo/bar
+Location: /cookie-parser-result/foo/bar?path0028
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0029-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0029-expected
new file mode 100644
index 0000000..1022f64a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0029-expected
@@ -0,0 +1 @@
+Cookie: a=b
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0029-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0029-test
new file mode 100644
index 0000000..17bd42f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0029-test
@@ -0,0 +1,2 @@
+Set-Cookie: a=b; 	path	=	/cookie-parser-result
+Set-Cookie: x=y; 	path	=	/book
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0030-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0030-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0030-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0030-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0030-test
new file mode 100644
index 0000000..e17a2d4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0030-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; path=/dog; path=
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0031-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0031-expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0031-expected
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0031-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0031-test
new file mode 100644
index 0000000..3b24090
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0031-test
@@ -0,0 +1 @@
+Set-Cookie: foo=bar; path=; path=/dog
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0032-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0032-expected
new file mode 100644
index 0000000..e7403c4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0032-expected
@@ -0,0 +1 @@
+Cookie: foo=qux; foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0032-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0032-test
new file mode 100644
index 0000000..db6ceb2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/path0032-test
@@ -0,0 +1,3 @@
+Set-Cookie: foo=bar; path=/cookie-parser-result
+Set-Cookie: foo=qux; path=/cookie-parser-result/
+Location: /cookie-parser-result/dog?path0032
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0001-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0001-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0001-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0001-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0001-test
new file mode 100644
index 0000000..38b7dd2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0001-test
@@ -0,0 +1 @@
+Set-Cookie: foo=  bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0002-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0002-expected
new file mode 100644
index 0000000..9e96a81
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0002-expected
@@ -0,0 +1 @@
+Cookie: foo="bar"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0002-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0002-test
new file mode 100644
index 0000000..bed691f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0002-test
@@ -0,0 +1 @@
+Set-Cookie: foo="bar"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0003-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0003-expected
new file mode 100644
index 0000000..5cc2d46
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0003-expected
@@ -0,0 +1 @@
+Cookie: foo="  bar "
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0003-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0003-test
new file mode 100644
index 0000000..ce1d455
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0003-test
@@ -0,0 +1 @@
+Set-Cookie: foo="  bar "
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0004-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0004-expected
new file mode 100644
index 0000000..400030f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0004-expected
@@ -0,0 +1 @@
+Cookie: foo="bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0004-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0004-test
new file mode 100644
index 0000000..c569216
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0004-test
@@ -0,0 +1 @@
+Set-Cookie: foo="bar;baz"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0005-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0005-expected
new file mode 100644
index 0000000..cad285f7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0005-expected
@@ -0,0 +1 @@
+Cookie: foo="bar=baz"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0005-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0005-test
new file mode 100644
index 0000000..514c0f1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0005-test
@@ -0,0 +1 @@
+Set-Cookie: foo="bar=baz"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0006-expected b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0006-expected
new file mode 100644
index 0000000..b14d4f69
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0006-expected
@@ -0,0 +1 @@
+Cookie: foo=bar
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0006-test b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0006-test
new file mode 100644
index 0000000..a939a82
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/resources/test-files/value0006-test
@@ -0,0 +1 @@
+Set-Cookie: 	foo	=	bar	 	;	ttt
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/value-tests-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/value-tests-expected.txt
new file mode 100644
index 0000000..0662176
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/value-tests-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS value0001 - value0001
+PASS value0002 - value0002
+FAIL value0003 - value0003 assert_equals: expected "foo=\" bar \"" but got "foo=\"  bar \""
+PASS value0004 - value0004
+PASS value0005 - value0005
+PASS value0006 - value0006
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/value-tests.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/value-tests.html
new file mode 100644
index 0000000..cb09034
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/http-state/value-tests.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Tests basic cookie setting functionality</title>
+    <meta name=help href="https://tools.ietf.org/html/rfc6265#page-8">
+
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/cookie-http-state-template.js"></script>
+  </head>
+  <body style="background:#EEE">
+    <h3>Log</h3>
+    <div id="log" style="height:50px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>IFrames</h3>
+    <div id="iframes" style="height:170px; overflow-y:scroll; background: #FFF">
+    </div>
+    <h3>Test Results</h3>
+    <script>
+      setup({ explicit_timeout: true });
+
+      const TEST_CASES = [
+        {file: "value0001", name: "value0001"},
+        {file: "value0002", name: "value0002"},
+        {file: "value0003", name: "value0003"},
+        {file: "value0004", name: "value0004"},
+        {file: "value0005", name: "value0005"},
+        {file: "value0006", name: "value0006"},
+      ];
+
+      for (const i in TEST_CASES) {
+        const t = TEST_CASES[i];
+        promise_test(createCookieTest(t.file),
+                     t.file + " - " + t.name,
+                     { timeout: 3000 });
+      }
+
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window-expected.txt
index c1824c6..0c086b2 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window-expected.txt
@@ -3,7 +3,7 @@
 FAIL window.event for constructors from another global: XMLHttpRequest assert_equals: expected (undefined) undefined but got (object) object "[object Event]"
 PASS window.event and element from another document
 FAIL window.event and moving an element post-dispatch assert_equals: expected (object) object "[object Event]" but got (undefined) undefined
-FAIL window.event should not be affected by nodes moving post-dispatch assert_equals: expected (object) object "[object Event]" but got (undefined) undefined
+FAIL window.event should not be affected by nodes moving post-dispatch assert_equals: expected (undefined) undefined but got (object) object "[object Event]"
 FAIL Listener from a different global assert_equals: expected (object) object "[object Event]" but got (undefined) undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window.js b/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window.js
index f2687696..0f14961 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/events/event-global-extra.window.js
@@ -56,16 +56,16 @@
     assert_equals(counter++, 3);
   }));
   child.addEventListener("hi", t.step_func(e => {
-    assert_equals(window.event, e);
+    assert_equals(window.event, undefined);
     assert_equals(counter++, 2);
   }));
   furtherChild.addEventListener("hi", t.step_func(e => {
     host.appendChild(child);
-    assert_equals(window.event, e);
+    assert_equals(window.event, undefined);
     assert_equals(counter++, 0);
   }));
   furtherChild.addEventListener("hi", t.step_func(e => {
-    assert_equals(window.event, e);
+    assert_equals(window.event, undefined);
     assert_equals(counter++, 1);
   }));
   furtherChild.dispatchEvent(new Event("hi", { composed: true, bubbles: true }));
@@ -75,7 +75,7 @@
 async_test(t => {
   const frame = document.body.appendChild(document.createElement("iframe"));
   frame.src = "resources/event-global-extra-frame.html";
-  frame.onload = t.step_func_done(() => {
+  frame.onload = t.step_func_done((load_event) => {
     const event = new Event("hi");
     document.addEventListener("hi", frame.contentWindow.listener); // listener intentionally not wrapped in t.step_func
     document.addEventListener("hi", t.step_func(e => {
@@ -85,6 +85,6 @@
     document.dispatchEvent(event);
     assert_equals(frameState.event, event);
     assert_equals(frameState.windowEvent, event);
-    assert_equals(frameState.parentEvent, undefined);
+    assert_equals(frameState.parentEvent, load_event);
   });
 }, "Listener from a different global");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/embed.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/embed.tentative.https.sub.html
index 70accac..745ef42 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/embed.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/embed.tentative.https.sub.html
@@ -13,7 +13,7 @@
       let e = document.createElement('embed');
       e.src = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"destination":"embed", "target":"subresource", "site":"same-origin"};
+        let expected = {"destination":"embed", "site":"same-origin"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -32,7 +32,7 @@
       let e = document.createElement('embed');
       e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"destination":"embed", "target":"subresource", "site":"same-site"};
+        let expected = {"destination":"embed", "site":"same-site"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -51,7 +51,7 @@
       let e = document.createElement('embed');
       e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"destination":"embed", "target":"subresource", "site":"cross-site"};
+        let expected = {"destination":"embed", "site":"cross-site"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/fetch.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/fetch.tentative.https.sub.html
index 7a2c223..12072476b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/fetch.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/fetch.tentative.https.sub.html
@@ -10,7 +10,6 @@
           assert_header_equals(j.header, {
             "cause": undefined,
             "destination": "",
-            "target": "subresource",
             "site": "same-origin"
           });
         });
@@ -23,7 +22,6 @@
           assert_header_equals(j.header, {
             "cause": undefined,
             "destination": "",
-            "target": "subresource",
             "site": "same-site"
           });
         });
@@ -36,7 +34,6 @@
           assert_header_equals(j.header, {
             "cause": undefined,
             "destination": "",
-            "target": "subresource",
             "site": "cross-site"
           });
         });
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/font.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/font.tentative.https.sub.html
index 679ff81..65432b5b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/font.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/font.tentative.https.sub.html
@@ -52,7 +52,7 @@
     var same_origin_test = async_test("Same-Origin font");
     same_origin_test.step(function () {
         key = "font-same-origin";
-        expected_same_origin = {"destination":"font", "target":"subresource", "site":"same-origin"};
+        expected_same_origin = {"destination":"font", "site":"same-origin"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_origin_xhr = new XMLHttpRequest();
@@ -70,7 +70,7 @@
     var same_site_test = async_test("Same-Site font");
     same_site_test.step(function () {
         key = "font-same-site";
-        expected_same_site = {"destination":"font", "target":"subresource", "site":"same-site"};
+        expected_same_site = {"destination":"font", "site":"same-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_site_xhr = new XMLHttpRequest();
@@ -88,7 +88,7 @@
     var cross_site_test = async_test("Cross-Site font");
     cross_site_test.step(function () {
         key = "font-cross-site";
-        expected_cross_site = {"destination":"font", "target":"subresource", "site":"cross-site"};
+        expected_cross_site = {"destination":"font", "site":"cross-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         cross_site_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/iframe.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/iframe.tentative.https.sub.html
index 5b56c62..8d89cda 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/iframe.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/iframe.tentative.https.sub.html
@@ -13,8 +13,7 @@
 
       assert_header_equals(e.data, {
         "cause": "forced",
-        "destination": "document",
-        "target": "nested",
+        "destination": "nested-document",
         "site": "same-origin"
       });
       t.done();
@@ -32,8 +31,7 @@
 
       assert_header_equals(e.data, {
         "cause": "forced",
-        "destination": "document",
-        "target": "nested",
+        "destination": "nested-document",
         "site": "same-site"
       });
       t.done();
@@ -51,8 +49,7 @@
 
       assert_header_equals(e.data, {
         "cause": "forced",
-        "destination": "document",
-        "target": "nested",
+        "destination": "nested-document",
         "site": "cross-site"
       });
       t.done();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/img.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/img.tentative.https.sub.html
index 7c5cbc3..20701a651 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/img.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/img.tentative.https.sub.html
@@ -14,7 +14,6 @@
         assert_header_equals(decodeImageData(extractImageData(img)).headers["sec-metadata"], {
           "cause": undefined,
           "destination": "image",
-          "target": "subresource",
           "site": "same-origin"
         });
       }),
@@ -29,7 +28,6 @@
         assert_header_equals(decodeImageData(extractImageData(img)).headers["sec-metadata"], {
           "cause": undefined,
           "destination": "image",
-          "target": "subresource",
           "site": "same-site"
         });
       }),
@@ -44,7 +42,6 @@
         assert_header_equals(decodeImageData(extractImageData(img)).headers["sec-metadata"], {
           "cause": undefined,
           "destination": "image",
-          "target": "subresource",
           "site": "cross-site"
         });
       }),
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/object.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/object.tentative.https.sub.html
index 7bd2504..e1ac531 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/object.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/object.tentative.https.sub.html
@@ -13,7 +13,7 @@
       let e = document.createElement('object');
       e.data = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"destination":"object", "target":"subresource", "site":"same-origin"};
+        let expected = {"destination":"object", "site":"same-origin"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -32,7 +32,7 @@
       let e = document.createElement('object');
       e.data = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"destination":"object", "target":"subresource", "site":"same-site"};
+        let expected = {"destination":"object", "site":"same-site"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -51,7 +51,7 @@
       let e = document.createElement('object');
       e.data = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"destination":"object", "target":"subresource", "site":"cross-site"};
+        let expected = {"destination":"object", "site":"cross-site"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/cross-site.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/cross-site.tentative.https.sub.html
index 37b75f2..e25fd3f6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/cross-site.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/cross-site.tentative.https.sub.html
@@ -14,7 +14,7 @@
     var cross_site_test = async_test("Cross-Site -> Cross-Site redirect");
     cross_site_test.step(function () {
         filename = "redirect-cross-site-cross-site";
-        expected_cross_site = {"destination":"image", "target":"subresource", "site":"cross-site"};
+        expected_cross_site = {"destination":"image", "site":"cross-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         cross_site_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/same-origin.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/same-origin.tentative.https.sub.html
index 2af262e7..ac5982d8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/same-origin.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/same-origin.tentative.https.sub.html
@@ -15,7 +15,7 @@
     var same_origin_test = async_test("Cross-Site -> Same-Origin redirect");
     same_origin_test.step(function () {
         filename = "redirect-cross-site-same-origin";
-        expected_same_origin = {"destination":"image", "target":"subresource", "site":"cross-site"};
+        expected_same_origin = {"destination":"image", "site":"cross-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_origin_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/same-site.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/same-site.tentative.https.sub.html
index 77054496..5b3b965 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/same-site.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/cross-site/same-site.tentative.https.sub.html
@@ -14,7 +14,7 @@
     var same_site_test = async_test("Cross-Site -> Same-Site redirect");
     same_site_test.step(function () {
         filename = "redirect-cross-site-same-site";
-        expected_same_site = {"destination":"image", "target":"subresource", "site":"cross-site"};
+        expected_same_site = {"destination":"image", "site":"cross-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_site_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/cross-site.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/cross-site.tentative.https.sub.html
index 5a76bc9..ea6b167 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/cross-site.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/cross-site.tentative.https.sub.html
@@ -14,7 +14,7 @@
     var cross_site_test = async_test("Same-Origin -> Cross-Site redirect");
     cross_site_test.step(function () {
         filename = "redirect-same-origin-cross-site";
-        expected_cross_site = {"destination":"image", "target":"subresource", "site":"same-origin"};
+        expected_cross_site = {"destination":"image", "site":"same-origin"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         cross_site_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/same-origin.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/same-origin.tentative.https.sub.html
index 3735c5fc..430990a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/same-origin.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/same-origin.tentative.https.sub.html
@@ -15,7 +15,7 @@
     var same_origin_test = async_test("Same-Origin -> Same-Origin redirect");
     same_origin_test.step(function () {
         filename = "redirect-same-origin-same-origin";
-        expected_same_origin = {"destination":"image", "target":"subresource", "site":"same-origin"};
+        expected_same_origin = {"destination":"image", "site":"same-origin"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_origin_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/same-site.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/same-site.tentative.https.sub.html
index 1e24752..591cf67 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/same-site.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-origin/same-site.tentative.https.sub.html
@@ -15,7 +15,7 @@
     var same_site_test = async_test("Same-Origin -> Same-Site redirect");
     same_site_test.step(function () {
         filename = "redirect-same-origin-same-site";
-        expected_same_site = {"destination":"image", "target":"subresource", "site":"same-origin"};
+        expected_same_site = {"destination":"image", "site":"same-origin"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_site_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/cross-site.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/cross-site.tentative.https.sub.html
index 986e3f3..8592d02c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/cross-site.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/cross-site.tentative.https.sub.html
@@ -15,7 +15,7 @@
     var cross_site_test = async_test("Same-Site -> Cross-Site redirect");
     cross_site_test.step(function () {
         key = "redirect-same-site-cross-site";
-        expected_cross_site = {"destination":"image", "target":"subresource", "site":"same-site"};
+        expected_cross_site = {"destination":"image", "site":"same-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         cross_site_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/same-origin.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/same-origin.tentative.https.sub.html
index dbe3fea..191dbaa7 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/same-origin.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/same-origin.tentative.https.sub.html
@@ -15,7 +15,7 @@
     var same_origin_test = async_test("Same-Site -> Same-Origin redirect");
     same_origin_test.step(function () {
         key = "redirect-same-site-same-origin";
-        expected_same_origin = {"destination":"image", "target":"subresource", "site":"same-site"};
+        expected_same_origin = {"destination":"image", "site":"same-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_origin_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/same-site.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/same-site.tentative.https.sub.html
index c6437f0..11d6047 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/same-site.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/redirect/same-site/same-site.tentative.https.sub.html
@@ -15,7 +15,7 @@
     var same_site_test = async_test("Same-Site -> Same-Site redirect");
     same_site_test.step(function () {
         key = "redirect-same-site-same-site";
-        expected_same_site = {"destination":"image", "target":"subresource", "site":"same-site"};
+        expected_same_site = {"destination":"image", "site":"same-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_site_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/report.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/report.tentative.https.sub.html
index d82dab9..2798364 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/report.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/report.tentative.https.sub.html
@@ -11,21 +11,21 @@
     counter++;
     if (counter == 3) {
       promise_test(t => {
-        expected = {"destination":"report", "target":"subresource", "site":"same-origin"};
+        expected = {"destination":"report", "site":"same-origin"};
         return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=report-same-origin")
             .then(response => response.text())
             .then(text => assert_header_equals(text, expected));
       }, "Same-Origin report");
 
       promise_test(t => {
-        expected = {"destination":"report", "target":"subresource", "site":"same-site"};
+        expected = {"destination":"report", "site":"same-site"};
         return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=report-same-site")
             .then(response => response.text())
             .then(text => assert_header_equals(text, expected));
       }, "Same-site report");
 
       promise_test(t => {
-        expected = {"destination":"report", "target":"subresource", "site":"cross-site"};
+        expected = {"destination":"report", "site":"cross-site"};
         return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=report-cross-site")
             .then(response => response.text())
             .then(text => assert_header_equals(text, expected));
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/resources/echo-as-json.py b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/resources/echo-as-json.py
index a45bb68..16cc677 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/resources/echo-as-json.py
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/resources/echo-as-json.py
@@ -8,5 +8,5 @@
         headers.append(("Access-Control-Allow-Origin", request.headers["origin"]))
 
 
-    body = json.dumps({ "header": request.headers["sec-metadata"] })
+    body = json.dumps({ "header": request.headers.get("sec-metadata", "") })
     return headers, body
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/resources/echo-as-script.py b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/resources/echo-as-script.py
index c503822..c1c6a467 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/resources/echo-as-script.py
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/resources/echo-as-script.py
@@ -3,6 +3,6 @@
 def main(request, response):
     headers = [("Content-Type", "text/javascript")]
 
-    body = "var header = %s;" % json.dumps(request.headers["sec-metadata"]);
+    body = "var header = %s;" % json.dumps(request.headers.get("sec-metadata", ""));
 
     return headers, body
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/script.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/script.tentative.https.sub.html
index 6f86e87..643e118 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/script.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/script.tentative.https.sub.html
@@ -12,7 +12,6 @@
     assert_header_equals(header, {
       "cause": undefined,
       "destination": "script",
-      "target": "subresource",
       "site": "same-origin"
     });
   }, "Same-origin script");
@@ -27,7 +26,6 @@
     assert_header_equals(header, {
       "cause": undefined,
       "destination": "script",
-      "target": "subresource",
       "site": "same-site"
     });
   }, "Same-site script");
@@ -42,7 +40,6 @@
     assert_header_equals(header, {
       "cause": undefined,
       "destination": "script",
-      "target": "subresource",
       "site": "cross-site"
     });
   }, "Cross-site script");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/serviceworker.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/serviceworker.tentative.https.sub.html
index 25e8f67..9d1fe2a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/serviceworker.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/serviceworker.tentative.https.sub.html
@@ -35,7 +35,7 @@
     var same_origin_test = async_test("Same-Origin serviceworker");
     same_origin_test.step(function () {
         key = "serviceworker-same-origin";
-        expected_same_origin = {"destination":"serviceworker", "target":"subresource", "site":"same-origin"};
+        expected_same_origin = {"destination":"serviceworker", "site":"same-origin"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_origin_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/sharedworker.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/sharedworker.tentative.https.sub.html
index 98cb601..aa118e04 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/sharedworker.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/sharedworker.tentative.https.sub.html
@@ -26,7 +26,7 @@
     var same_origin_test = async_test("Same-Origin sharedworker");
     same_origin_test.step(function () {
         key = "sharedworker-same-origin";
-        expected_same_origin = {"destination":"sharedworker", "target":"subresource", "site":"same-origin"};
+        expected_same_origin = {"destination":"sharedworker", "site":"same-origin"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_origin_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/style.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/style.tentative.https.sub.html
index e832b91..78fac56 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/style.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/style.tentative.https.sub.html
@@ -9,7 +9,7 @@
     var same_origin_test = async_test("Same-Origin style");
     same_origin_test.step(function () {
         key = "style-same-origin";
-        expected_same_origin = {"destination":"style", "target":"subresource", "site":"same-origin"};
+        expected_same_origin = {"destination":"style", "site":"same-origin"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_origin_xhr = new XMLHttpRequest();
@@ -27,7 +27,7 @@
     var same_site_test = async_test("Same-Site style");
     same_site_test.step(function () {
         key = "style-same-site";
-        expected_same_site = {"destination":"style", "target":"subresource", "site":"same-site"};
+        expected_same_site = {"destination":"style", "site":"same-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         same_site_xhr = new XMLHttpRequest();
@@ -45,7 +45,7 @@
     var cross_site_test = async_test("Cross-Site style");
     cross_site_test.step(function () {
         key = "style-cross-site";
-        expected_cross_site = {"destination":"style", "target":"subresource", "site":"cross-site"};
+        expected_cross_site = {"destination":"style", "site":"cross-site"};
 
         //  Requests from the server the saved value of the Sec-Metadata header
         cross_site_xhr = new XMLHttpRequest();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/track.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/track.tentative.https.sub.html
index 299b53b77..e89d474 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/track.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/track.tentative.https.sub.html
@@ -5,77 +5,75 @@
 <script src=/resources/testharnessreport.js></script>
 <script src=/fetch/sec-metadata/resources/helper.js></script>
 <body>
-  <!-- Same-Origin request -->
-  <video src="/media/movie_5.mp4" controls>
-    <track default kind="captions" src="https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-origin"
-    srclang="en" onload="test_same_origin()">
-  </video>
-
-  <!-- Same-Site request -->
-  <video src="/media/movie_5.mp4" controls crossorigin>
-    <track default kind="captions" src="https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-site"
-    srclang="pl" onload="test_same_site()">
-  </video>
-
-  <!-- Cross-Site request -->
-  <video src="/media/movie_5.mp4" controls crossorigin>
-    <track default kind="captions" src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-cross-site"
-    srclang="de" onload="test_cross_site()">
-  </video>
 </body>
-
 <script>
-  function test_same_origin(){
-    var same_origin_test = async_test("Same-Origin track");
-    same_origin_test.step(function () {
-        key = "track-same-origin";
-        expected_same_origin = {"destination":"track", "target":"subresource", "site":"same-origin"};
-
-        //  Requests from the server the saved value of the Sec-Metadata header
-        same_origin_xhr = new XMLHttpRequest();
-        same_origin_xhr.open("GET", "/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key);
-
-        // Async test step triggered when the response is loaded
-        same_origin_xhr.onreadystatechange = same_origin_test.step_func(function () {
-          verify_response(same_origin_xhr, same_origin_test, expected_same_origin)
-        });
-        same_origin_xhr.send();
-    });
+  function createVideoElement() {
+    let el = document.createElement('video');
+    el.src = "/media/movie_5.mp4";
+    el.setAttribute("controls", "");
+    el.setAttribute("crossorigin", "");
+    return el;
   }
 
-  function test_same_site(){
-    var same_site_test = async_test("Same-Site track");
-    same_site_test.step(function () {
-        key = "track-same-site";
-        expected_same_site = {"destination":"track", "target":"subresource", "site":"same-site"};
-
-        //  Requests from the server the saved value of the Sec-Metadata header
-        same_site_xhr = new XMLHttpRequest();
-        same_site_xhr.open("GET", "/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key);
-
-        // Async test step triggered when the response is loaded
-        same_site_xhr.onreadystatechange = same_site_test.step_func(function () {
-          verify_response(same_site_xhr, same_site_test, expected_same_site)
-        });
-        same_site_xhr.send();
-    });
+  function createTrack() {
+    let el = document.createElement("track");
+    el.setAttribute("default", "");
+    el.setAttribute("kind", "captions");
+    el.setAttribute("srclang", "en");
+    return el;
   }
 
-  function test_cross_site(){
-    var cross_site_test = async_test("Cross-Site track");
-    cross_site_test.step(function () {
-        key = "track-cross-site";
-        expected_cross_site = {"destination":"track", "target":"subresource", "site":"cross-site"};
-
-        //  Requests from the server the saved value of the Sec-Metadata header
-        cross_site_xhr = new XMLHttpRequest();
-        cross_site_xhr.open("GET", "/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key);
-
-        // Async test step triggered when the response is loaded
-        cross_site_xhr.onreadystatechange = cross_site_test.step_func(function () {
-          verify_response(cross_site_xhr, cross_site_test, expected_cross_site)
-        });
-        cross_site_xhr.send();
+  promise_test(t => {
+    return new Promise((resolve, reject) => {
+      let video = createVideoElement();
+      let el = createTrack();
+      el.src = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-origin";
+      el.onload = t.step_func(_ => {
+        expected = {"destination":"track", "site":"same-origin"};
+        fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-same-origin")
+            .then(response => response.text())
+            .then(text => assert_header_equals(text, expected))
+            .then(_ => resolve());
+      });
+      video.appendChild(el);
+      document.body.appendChild(video);
     });
-  }
+  }, "Same-Origin track");
+
+  promise_test(t => {
+    return new Promise((resolve, reject) => {
+      let video = createVideoElement();
+      let el = createTrack();
+      el.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-site";
+      el.onload = t.step_func(_ => {
+        expected = {"destination":"track", "site":"same-site"};
+        fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-same-site")
+            .then(response => response.text())
+            .then(text => assert_header_equals(text, expected))
+            .then(resolve)
+            .catch(reject);
+
+      });
+      video.appendChild(el);
+      document.body.appendChild(video);
+    });
+  }, "Same-Site track");
+
+  promise_test(t => {
+    return new Promise((resolve, reject) => {
+      let video = createVideoElement();
+      let el = createTrack();
+      el.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-cross-site";
+      el.onload = t.step_func(_ => {
+        expected = {"destination":"track", "site":"cross-site"};
+        fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-cross-site")
+            .then(response => response.text())
+            .then(text => assert_header_equals(text, expected))
+            .then(resolve)
+            .catch(reject);
+      });
+      video.appendChild(el);
+      document.body.appendChild(video);
+    });
+  }, "Cross-Site track");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/window-open.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/window-open.tentative.https.sub.html
index 1f9df663..3cd6190 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/window-open.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/window-open.tentative.https.sub.html
@@ -17,7 +17,6 @@
       assert_header_equals(e.data, {
         "cause": "forced",
         "destination": "document",
-        "target": "top-level",
         "site": "same-origin"
       });
       t.done();
@@ -34,7 +33,6 @@
       assert_header_equals(e.data, {
         "cause": "forced",
         "destination": "document",
-        "target": "top-level",
         "site": "same-site"
       });
       t.done();
@@ -51,7 +49,6 @@
       assert_header_equals(e.data, {
         "cause": "forced",
         "destination": "document",
-        "target": "top-level",
         "site": "cross-site"
       });
       t.done();
@@ -71,7 +68,6 @@
         assert_header_equals(e.data, {
           "cause": "user-activated",
           "destination": "document",
-          "target": "top-level",
           "site": "same-origin"
         });
         t.done();
@@ -93,7 +89,6 @@
         assert_header_equals(e.data, {
           "cause": "user-activated",
           "destination": "document",
-          "target": "top-level",
           "site": "same-site"
         });
         t.done();
@@ -115,7 +110,6 @@
         assert_header_equals(e.data, {
           "cause": "user-activated",
           "destination": "document",
-          "target": "top-level",
           "site": "cross-site"
         });
         t.done();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/worker.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/worker.tentative.https.sub.html
index ac809c6e..eff66fc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/worker.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/worker.tentative.https.sub.html
@@ -10,7 +10,7 @@
       let key = "worker-same-origin";
       let w = new Worker("/fetch/sec-metadata/resources/record-header.py?file=" + key);
       w.onmessage = e => {
-        let expected = {"destination":"worker", "target":"subresource", "site":"same-origin"};
+        let expected = {"destination":"worker", "site":"same-origin"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/xslt.tentative.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/xslt.tentative.https.sub.html
index 2fe8c91..dff9966 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/xslt.tentative.https.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/sec-metadata/xslt.tentative.https.sub.html
@@ -12,21 +12,21 @@
       return;
 
     promise_test(t => {
-      let expected = {"destination":"xslt", "target":"subresource", "site":"same-origin"};
+      let expected = {"destination":"xslt", "site":"same-origin"};
       return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-same-origin")
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected));
     }, "Same-Origin xslt");
 
     promise_test(t => {
-      let expected = {"destination":"xslt", "target":"subresource", "site":"same-site"};
+      let expected = {"destination":"xslt", "site":"same-site"};
       return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-same-site")
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected));
     }, "Same-site xslt");
 
     promise_test(t => {
-      let expected = {"destination":"xslt", "target":"subresource", "site":"cross-site"};
+      let expected = {"destination":"xslt", "site":"cross-site"};
       return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-cross-site")
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected));
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/mst-content-hint.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/mst-content-hint.idl
new file mode 100644
index 0000000..15e17cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/mst-content-hint.idl
@@ -0,0 +1,8 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: MediaStreamTrack Content Hints (https://w3c.github.io/mst-content-hint/)
+
+partial interface MediaStreamTrack {
+  attribute DOMString contentHint;
+};
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/META.yml b/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/META.yml
new file mode 100644
index 0000000..7f79ecc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/META.yml
@@ -0,0 +1,3 @@
+spec: https://w3c.github.io/mst-content-hint/
+suggested_reviewers:
+  - alvestrand
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/MediaStreamTrack-contentHint.html b/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/MediaStreamTrack-contentHint.html
new file mode 100644
index 0000000..98c88e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/MediaStreamTrack-contentHint.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<canvas id="canvas">
+</canvas>
+<script>
+
+function createAudioTrack() {
+  ac = new AudioContext();
+  var osc = ac.createOscillator();
+  var dest = ac.createMediaStreamDestination();
+  osc.connect(dest);
+  audio_track = dest.stream.getAudioTracks()[0];
+
+  assert_equals(audio_track.kind, "audio");
+  return audio_track;
+}
+
+function createVideoTrack() {
+  canvas = document.getElementById("canvas");
+  video_track = canvas.captureStream().getVideoTracks()[0];
+
+  assert_equals(video_track.kind, "video");
+  return video_track;
+}
+
+test(t => {
+  audio_track = createAudioTrack();
+  assert_equals("", audio_track.contentHint);
+
+  video_track = createVideoTrack();
+  assert_equals("", video_track.contentHint);
+}, "Tracks have empty default content hint");
+
+test(t => {
+  audio_track = createAudioTrack();
+  audio_track.contentHint = "speech";
+  assert_equals(audio_track.contentHint, "speech");
+  audio_track.contentHint = "music";
+  assert_equals(audio_track.contentHint, "music");
+  audio_track.contentHint = "";
+  assert_equals(audio_track.contentHint, "");
+}, "Accepts valid audio contentHints");
+
+test(t => {
+  audio_track = createAudioTrack();
+  audio_track.contentHint = "speech";
+  assert_equals(audio_track.contentHint, "speech");
+  audio_track.contentHint = "motion";
+  assert_equals(audio_track.contentHint, "speech",
+                "Audio tracks should ignore video-only contentHints.");
+  audio_track.contentHint = "bogus";
+  assert_equals(audio_track.contentHint, "speech",
+                "Audio tracks should ignore garbage contentHints");
+}, "Audio tracks ignore invalid/video contentHints");
+
+test(t => {
+  video_track = createVideoTrack();
+  video_track.contentHint = "motion";
+  assert_equals(video_track.contentHint, "motion");
+  video_track.contentHint = "detail";
+  assert_equals(video_track.contentHint, "detail");
+  video_track.contentHint = "text";
+  assert_equals(video_track.contentHint, "text");
+  video_track.contentHint = "";
+  assert_equals(video_track.contentHint, "");
+}, "Accepts valid video contentHints");
+
+test(t => {
+  video_track = createVideoTrack();
+  video_track.contentHint = "motion";
+  assert_equals(video_track.contentHint, "motion");
+  video_track.contentHint = "speech";
+  assert_equals(video_track.contentHint, "motion",
+                "Video tracks should ignore audio-only contentHints.");
+  video_track.contentHint = "bogus";
+  assert_equals(video_track.contentHint, "motion",
+                "Video tracks should ignore garbage contentHints");
+}, "Video tracks ignore invalid/audio contentHints");
+
+test(t => {
+  video_track = createVideoTrack();
+  video_track.contentHint = "motion";
+  assert_equals(video_track.contentHint, "motion");
+
+  // Cloning a track should preserve contentHint.
+  video_track_clone = video_track.clone();
+  assert_equals(video_track_clone.contentHint, "motion");
+
+  // Changing a cloned track's contentHint should not change the original.
+  video_track_clone.contentHint = "detail";
+  assert_equals(video_track_clone.contentHint, "detail");
+  assert_equals(video_track.contentHint, "motion");
+}, "Cloned video tracks have separate contentHints");
+
+test(t => {
+  audio_track = createAudioTrack();
+  audio_track.contentHint = "speech";
+  assert_equals(audio_track.contentHint, "speech");
+
+  // Cloning a track should preserve contentHint.
+  audio_track_clone = audio_track.clone();
+  assert_equals(audio_track_clone.contentHint, "speech");
+
+  // Changing a cloned track's contentHint should not change the original.
+  audio_track_clone.contentHint = "music";
+  assert_equals(audio_track_clone.contentHint, "music");
+  assert_equals(audio_track.contentHint, "speech");
+}, "Cloned audio tracks have separate contentHints");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/idlharness.window.js b/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/idlharness.window.js
new file mode 100644
index 0000000..0d9137d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mst-content-hint/idlharness.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+// META: script=/webrtc/RTCPeerConnection-helper.js
+
+'use strict';
+
+idl_test(
+  ['mst-content-hint'],
+  ['mediacapture-streams', 'dom'],
+  async idl_array => {
+    idl_array.add_objects({
+      MediaStreamTrack: ['audioTrack', 'videoTrack'],
+    });
+
+    const stream = await getNoiseStream({ audio: true, video: true });
+    self.audioTrack = stream.getAudioTracks()[0];
+    self.videoTrack = stream.getVideoTracks()[0];
+  }
+);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js
deleted file mode 100644
index fd344e7..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js
+++ /dev/null
@@ -1,273 +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.
-
-'use strict';
-
-(function() {
-  var mojomId = 'device/usb/public/mojom/chooser_service.mojom';
-  if (mojo.internal.isMojomLoaded(mojomId)) {
-    console.warn('The following mojom is loaded multiple times: ' + mojomId);
-    return;
-  }
-  mojo.internal.markMojomLoaded(mojomId);
-  var bindings = mojo;
-  var associatedBindings = mojo;
-  var codec = mojo.internal;
-  var validator = mojo.internal;
-
-  var exports = mojo.internal.exposeNamespace('device.mojom');
-  var device$ =
-      mojo.internal.exposeNamespace('device.mojom');
-  if (mojo.config.autoLoadMojomDeps) {
-    mojo.internal.loadMojomIfNecessary(
-        'device/usb/public/mojom/device.mojom', 'device.mojom.js');
-  }
-  var device_manager$ =
-      mojo.internal.exposeNamespace('device.mojom');
-  if (mojo.config.autoLoadMojomDeps) {
-    mojo.internal.loadMojomIfNecessary(
-        'device/usb/public/mojom/device_manager.mojom', 'device_manager.mojom.js');
-  }
-
-
-
-  function UsbChooserService_GetPermission_Params(values) {
-    this.initDefaults_();
-    this.initFields_(values);
-  }
-
-
-  UsbChooserService_GetPermission_Params.prototype.initDefaults_ = function() {
-    this.deviceFilters = null;
-  };
-  UsbChooserService_GetPermission_Params.prototype.initFields_ = function(fields) {
-    for(var field in fields) {
-        if (this.hasOwnProperty(field))
-          this[field] = fields[field];
-    }
-  };
-
-  UsbChooserService_GetPermission_Params.validate = function(messageValidator, offset) {
-    var err;
-    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
-    if (err !== validator.validationError.NONE)
-        return err;
-
-    var kVersionSizes = [
-      {version: 0, numBytes: 16}
-    ];
-    err = messageValidator.validateStructVersion(offset, kVersionSizes);
-    if (err !== validator.validationError.NONE)
-        return err;
-
-
-    // validate UsbChooserService_GetPermission_Params.deviceFilters
-    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(device_manager$.UsbDeviceFilter), false, [0], 0);
-    if (err !== validator.validationError.NONE)
-        return err;
-
-    return validator.validationError.NONE;
-  };
-
-  UsbChooserService_GetPermission_Params.encodedSize = codec.kStructHeaderSize + 8;
-
-  UsbChooserService_GetPermission_Params.decode = function(decoder) {
-    var packed;
-    var val = new UsbChooserService_GetPermission_Params();
-    var numberOfBytes = decoder.readUint32();
-    var version = decoder.readUint32();
-    val.deviceFilters = decoder.decodeArrayPointer(new codec.PointerTo(device_manager$.UsbDeviceFilter));
-    return val;
-  };
-
-  UsbChooserService_GetPermission_Params.encode = function(encoder, val) {
-    var packed;
-    encoder.writeUint32(UsbChooserService_GetPermission_Params.encodedSize);
-    encoder.writeUint32(0);
-    encoder.encodeArrayPointer(new codec.PointerTo(device_manager$.UsbDeviceFilter), val.deviceFilters);
-  };
-  function UsbChooserService_GetPermission_ResponseParams(values) {
-    this.initDefaults_();
-    this.initFields_(values);
-  }
-
-
-  UsbChooserService_GetPermission_ResponseParams.prototype.initDefaults_ = function() {
-    this.result = null;
-  };
-  UsbChooserService_GetPermission_ResponseParams.prototype.initFields_ = function(fields) {
-    for(var field in fields) {
-        if (this.hasOwnProperty(field))
-          this[field] = fields[field];
-    }
-  };
-
-  UsbChooserService_GetPermission_ResponseParams.validate = function(messageValidator, offset) {
-    var err;
-    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
-    if (err !== validator.validationError.NONE)
-        return err;
-
-    var kVersionSizes = [
-      {version: 0, numBytes: 16}
-    ];
-    err = messageValidator.validateStructVersion(offset, kVersionSizes);
-    if (err !== validator.validationError.NONE)
-        return err;
-
-
-    // validate UsbChooserService_GetPermission_ResponseParams.result
-    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, true);
-    if (err !== validator.validationError.NONE)
-        return err;
-
-    return validator.validationError.NONE;
-  };
-
-  UsbChooserService_GetPermission_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
-
-  UsbChooserService_GetPermission_ResponseParams.decode = function(decoder) {
-    var packed;
-    var val = new UsbChooserService_GetPermission_ResponseParams();
-    var numberOfBytes = decoder.readUint32();
-    var version = decoder.readUint32();
-    val.result = decoder.decodeStructPointer(device$.UsbDeviceInfo);
-    return val;
-  };
-
-  UsbChooserService_GetPermission_ResponseParams.encode = function(encoder, val) {
-    var packed;
-    encoder.writeUint32(UsbChooserService_GetPermission_ResponseParams.encodedSize);
-    encoder.writeUint32(0);
-    encoder.encodeStructPointer(device$.UsbDeviceInfo, val.result);
-  };
-  var kUsbChooserService_GetPermission_Name = 0;
-
-  function UsbChooserServicePtr(handleOrPtrInfo) {
-    this.ptr = new bindings.InterfacePtrController(UsbChooserService,
-                                                   handleOrPtrInfo);
-  }
-
-  function UsbChooserServiceAssociatedPtr(associatedInterfacePtrInfo) {
-    this.ptr = new associatedBindings.AssociatedInterfacePtrController(
-        UsbChooserService, associatedInterfacePtrInfo);
-  }
-
-  UsbChooserServiceAssociatedPtr.prototype =
-      Object.create(UsbChooserServicePtr.prototype);
-  UsbChooserServiceAssociatedPtr.prototype.constructor =
-      UsbChooserServiceAssociatedPtr;
-
-  function UsbChooserServiceProxy(receiver) {
-    this.receiver_ = receiver;
-  }
-  UsbChooserServicePtr.prototype.getPermission = function() {
-    return UsbChooserServiceProxy.prototype.getPermission
-        .apply(this.ptr.getProxy(), arguments);
-  };
-
-  UsbChooserServiceProxy.prototype.getPermission = function(deviceFilters) {
-    var params = new UsbChooserService_GetPermission_Params();
-    params.deviceFilters = deviceFilters;
-    return new Promise(function(resolve, reject) {
-      var builder = new codec.MessageV1Builder(
-          kUsbChooserService_GetPermission_Name,
-          codec.align(UsbChooserService_GetPermission_Params.encodedSize),
-          codec.kMessageExpectsResponse, 0);
-      builder.encodeStruct(UsbChooserService_GetPermission_Params, params);
-      var message = builder.finish();
-      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
-        var reader = new codec.MessageReader(message);
-        var responseParams =
-            reader.decodeStruct(UsbChooserService_GetPermission_ResponseParams);
-        resolve(responseParams);
-      }).catch(function(result) {
-        reject(Error("Connection error: " + result));
-      });
-    }.bind(this));
-  };
-
-  function UsbChooserServiceStub(delegate) {
-    this.delegate_ = delegate;
-  }
-  UsbChooserServiceStub.prototype.getPermission = function(deviceFilters) {
-    return this.delegate_ && this.delegate_.getPermission && this.delegate_.getPermission(deviceFilters);
-  }
-
-  UsbChooserServiceStub.prototype.accept = function(message) {
-    var reader = new codec.MessageReader(message);
-    switch (reader.messageName) {
-    default:
-      return false;
-    }
-  };
-
-  UsbChooserServiceStub.prototype.acceptWithResponder =
-      function(message, responder) {
-    var reader = new codec.MessageReader(message);
-    switch (reader.messageName) {
-    case kUsbChooserService_GetPermission_Name:
-      var params = reader.decodeStruct(UsbChooserService_GetPermission_Params);
-      this.getPermission(params.deviceFilters).then(function(response) {
-        var responseParams =
-            new UsbChooserService_GetPermission_ResponseParams();
-        responseParams.result = response.result;
-        var builder = new codec.MessageV1Builder(
-            kUsbChooserService_GetPermission_Name,
-            codec.align(UsbChooserService_GetPermission_ResponseParams.encodedSize),
-            codec.kMessageIsResponse, reader.requestID);
-        builder.encodeStruct(UsbChooserService_GetPermission_ResponseParams,
-                             responseParams);
-        var message = builder.finish();
-        responder.accept(message);
-      });
-      return true;
-    default:
-      return false;
-    }
-  };
-
-  function validateUsbChooserServiceRequest(messageValidator) {
-    var message = messageValidator.message;
-    var paramsClass = null;
-    switch (message.getName()) {
-      case kUsbChooserService_GetPermission_Name:
-        if (message.expectsResponse())
-          paramsClass = UsbChooserService_GetPermission_Params;
-      break;
-    }
-    if (paramsClass === null)
-      return validator.validationError.NONE;
-    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
-  }
-
-  function validateUsbChooserServiceResponse(messageValidator) {
-   var message = messageValidator.message;
-   var paramsClass = null;
-   switch (message.getName()) {
-      case kUsbChooserService_GetPermission_Name:
-        if (message.isResponse())
-          paramsClass = UsbChooserService_GetPermission_ResponseParams;
-        break;
-    }
-    if (paramsClass === null)
-      return validator.validationError.NONE;
-    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
-  }
-
-  var UsbChooserService = {
-    name: 'device.mojom.UsbChooserService',
-    kVersion: 0,
-    ptrClass: UsbChooserServicePtr,
-    proxyClass: UsbChooserServiceProxy,
-    stubClass: UsbChooserServiceStub,
-    validateRequest: validateUsbChooserServiceRequest,
-    validateResponse: validateUsbChooserServiceResponse,
-  };
-  UsbChooserServiceStub.prototype.validator = validateUsbChooserServiceRequest;
-  UsbChooserServiceProxy.prototype.validator = validateUsbChooserServiceResponse;
-  exports.UsbChooserService = UsbChooserService;
-  exports.UsbChooserServicePtr = UsbChooserServicePtr;
-  exports.UsbChooserServiceAssociatedPtr = UsbChooserServiceAssociatedPtr;
-})();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js.headers b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js.headers
deleted file mode 100644
index 6805c323..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js.headers
+++ /dev/null
@@ -1 +0,0 @@
-Content-Type: text/javascript; charset=utf-8
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/web_usb_service.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/web_usb_service.mojom.js
index c283f0e..d0b93a1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/web_usb_service.mojom.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/web_usb_service.mojom.js
@@ -282,6 +282,166 @@
     encoder.skip(1);
     encoder.skip(1);
   };
+  function WebUsbService_GetPermission_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  WebUsbService_GetPermission_Params.prototype.initDefaults_ = function() {
+    this.deviceFilters = null;
+  };
+  WebUsbService_GetPermission_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+  WebUsbService_GetPermission_Params.generate = function(generator_) {
+    var generated = new WebUsbService_GetPermission_Params;
+    generated.deviceFilters = generator_.generateArray(function() {
+      return generator_.generateStruct(device.mojom.UsbDeviceFilter, false);
+    });
+    return generated;
+  };
+
+  WebUsbService_GetPermission_Params.prototype.mutate = function(mutator_) {
+    if (mutator_.chooseMutateField()) {
+      this.deviceFilters = mutator_.mutateArray(this.deviceFilters, function(val) {
+        return mutator_.mutateStruct(val, device.mojom.UsbDeviceFilter, false);
+      });
+    }
+    return this;
+  };
+  WebUsbService_GetPermission_Params.prototype.getHandleDeps = function() {
+    var handles = [];
+    return handles;
+  };
+
+  WebUsbService_GetPermission_Params.prototype.setHandles = function() {
+    this.setHandlesInternal_(arguments, 0);
+  };
+  WebUsbService_GetPermission_Params.prototype.setHandlesInternal_ = function(handles, idx) {
+    return idx;
+  };
+
+  WebUsbService_GetPermission_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate WebUsbService_GetPermission_Params.deviceFilters
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(device_manager$.UsbDeviceFilter), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  WebUsbService_GetPermission_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  WebUsbService_GetPermission_Params.decode = function(decoder) {
+    var packed;
+    var val = new WebUsbService_GetPermission_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.deviceFilters = decoder.decodeArrayPointer(new codec.PointerTo(device_manager$.UsbDeviceFilter));
+    return val;
+  };
+
+  WebUsbService_GetPermission_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(WebUsbService_GetPermission_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeArrayPointer(new codec.PointerTo(device_manager$.UsbDeviceFilter), val.deviceFilters);
+  };
+  function WebUsbService_GetPermission_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  WebUsbService_GetPermission_ResponseParams.prototype.initDefaults_ = function() {
+    this.result = null;
+  };
+  WebUsbService_GetPermission_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+  WebUsbService_GetPermission_ResponseParams.generate = function(generator_) {
+    var generated = new WebUsbService_GetPermission_ResponseParams;
+    generated.result = generator_.generateStruct(device.mojom.UsbDeviceInfo, true);
+    return generated;
+  };
+
+  WebUsbService_GetPermission_ResponseParams.prototype.mutate = function(mutator_) {
+    if (mutator_.chooseMutateField()) {
+      this.result = mutator_.mutateStruct(this.result, device.mojom.UsbDeviceInfo, true);
+    }
+    return this;
+  };
+  WebUsbService_GetPermission_ResponseParams.prototype.getHandleDeps = function() {
+    var handles = [];
+    return handles;
+  };
+
+  WebUsbService_GetPermission_ResponseParams.prototype.setHandles = function() {
+    this.setHandlesInternal_(arguments, 0);
+  };
+  WebUsbService_GetPermission_ResponseParams.prototype.setHandlesInternal_ = function(handles, idx) {
+    return idx;
+  };
+
+  WebUsbService_GetPermission_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate WebUsbService_GetPermission_ResponseParams.result
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, true);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  WebUsbService_GetPermission_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  WebUsbService_GetPermission_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new WebUsbService_GetPermission_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.result = decoder.decodeStructPointer(device$.UsbDeviceInfo);
+    return val;
+  };
+
+  WebUsbService_GetPermission_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(WebUsbService_GetPermission_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(device$.UsbDeviceInfo, val.result);
+  };
   function WebUsbService_SetClient_Params(values) {
     this.initDefaults_();
     this.initFields_(values);
@@ -366,7 +526,8 @@
   };
   var kWebUsbService_GetDevices_Name = 0;
   var kWebUsbService_GetDevice_Name = 1;
-  var kWebUsbService_SetClient_Name = 2;
+  var kWebUsbService_GetPermission_Name = 2;
+  var kWebUsbService_SetClient_Name = 3;
 
   function WebUsbServicePtr(handleOrPtrInfo) {
     this.ptr = new bindings.InterfacePtrController(WebUsbService,
@@ -426,6 +587,31 @@
     var message = builder.finish();
     this.receiver_.accept(message);
   };
+  WebUsbServicePtr.prototype.getPermission = function() {
+    return WebUsbServiceProxy.prototype.getPermission
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  WebUsbServiceProxy.prototype.getPermission = function(deviceFilters) {
+    var params_ = new WebUsbService_GetPermission_Params();
+    params_.deviceFilters = deviceFilters;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kWebUsbService_GetPermission_Name,
+          codec.align(WebUsbService_GetPermission_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(WebUsbService_GetPermission_Params, params_);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(WebUsbService_GetPermission_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
   WebUsbServicePtr.prototype.setClient = function() {
     return WebUsbServiceProxy.prototype.setClient
         .apply(this.ptr.getProxy(), arguments);
@@ -451,6 +637,9 @@
   WebUsbServiceStub.prototype.getDevice = function(guid, deviceRequestd) {
     return this.delegate_ && this.delegate_.getDevice && this.delegate_.getDevice(guid, deviceRequestd);
   }
+  WebUsbServiceStub.prototype.getPermission = function(deviceFilters) {
+    return this.delegate_ && this.delegate_.getPermission && this.delegate_.getPermission(deviceFilters);
+  }
   WebUsbServiceStub.prototype.setClient = function(client) {
     return this.delegate_ && this.delegate_.setClient && this.delegate_.setClient(client);
   }
@@ -491,6 +680,22 @@
         responder.accept(message);
       });
       return true;
+    case kWebUsbService_GetPermission_Name:
+      var params = reader.decodeStruct(WebUsbService_GetPermission_Params);
+      this.getPermission(params.deviceFilters).then(function(response) {
+        var responseParams =
+            new WebUsbService_GetPermission_ResponseParams();
+        responseParams.result = response.result;
+        var builder = new codec.MessageV1Builder(
+            kWebUsbService_GetPermission_Name,
+            codec.align(WebUsbService_GetPermission_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(WebUsbService_GetPermission_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
     default:
       return false;
     }
@@ -508,6 +713,10 @@
         if (!message.expectsResponse() && !message.isResponse())
           paramsClass = WebUsbService_GetDevice_Params;
       break;
+      case kWebUsbService_GetPermission_Name:
+        if (message.expectsResponse())
+          paramsClass = WebUsbService_GetPermission_Params;
+      break;
       case kWebUsbService_SetClient_Name:
         if (!message.expectsResponse() && !message.isResponse())
           paramsClass = WebUsbService_SetClient_Params;
@@ -526,6 +735,10 @@
         if (message.isResponse())
           paramsClass = WebUsbService_GetDevices_ResponseParams;
         break;
+      case kWebUsbService_GetPermission_Name:
+        if (message.isResponse())
+          paramsClass = WebUsbService_GetPermission_ResponseParams;
+        break;
     }
     if (paramsClass === null)
       return validator.validationError.NONE;
@@ -548,6 +761,9 @@
       getDevice: {
         params: WebUsbService_GetDevice_Params,
       },
+      getPermission: {
+        params: WebUsbService_GetPermission_Params,
+      },
       setClient: {
         params: WebUsbService_SetClient_Params,
       },
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js
index 94db93d3..9037a10 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js
@@ -13,10 +13,6 @@
   webUsbService: null,
   webUsbServiceInterceptor: null,
   webUsbServiceCrossFrameProxy: null,
-
-  chooser: null,
-  chooserInterceptor: null,
-  chooserCrossFrameProxy: null,
 };
 
 // Converts an ECMAScript String object to an instance of
@@ -376,6 +372,17 @@
     }
   }
 
+  getPermission(deviceFilters) {
+    return new Promise(resolve => {
+      if (navigator.usb.test.onrequestdevice) {
+        navigator.usb.test.onrequestdevice(
+            new USBDeviceRequestEvent(deviceFilters, resolve));
+      } else {
+        resolve({ result: null });
+      }
+    });
+  }
+
   setClient(client) {
     this.client_ = client;
   }
@@ -403,27 +410,6 @@
   }
 }
 
-class FakeChooserService {
-  constructor() {
-    this.bindingSet_ = new mojo.BindingSet(device.mojom.UsbChooserService);
-  }
-
-  addBinding(handle) {
-    this.bindingSet_.addBinding(this, handle);
-  }
-
-  getPermission(deviceFilters) {
-    return new Promise(resolve => {
-      if (navigator.usb.test.onrequestdevice) {
-        navigator.usb.test.onrequestdevice(
-            new USBDeviceRequestEvent(deviceFilters, resolve));
-      } else {
-        resolve({ result: null });
-      }
-    });
-  }
-}
-
 // Unlike FakeDevice this class is exported to callers of USBTest.addFakeDevice.
 class FakeUSBDevice {
   constructor() {
@@ -472,15 +458,6 @@
     internal.webUsbServiceCrossFrameProxy = new CrossFrameHandleProxy(
         handle => internal.webUsbService.addBinding(handle));
 
-    internal.chooser = new FakeChooserService();
-    internal.chooserInterceptor =
-        new MojoInterfaceInterceptor(device.mojom.UsbChooserService.name);
-    internal.chooserInterceptor.oninterfacerequest =
-        e => internal.chooser.addBinding(e.handle);
-    internal.chooserInterceptor.start();
-    internal.chooserCrossFrameProxy = new CrossFrameHandleProxy(
-        handle => internal.chooser.addBinding(handle));
-
     // Wait for a call to GetDevices() to pass between the renderer and the
     // mock in order to establish that everything is set up.
     await navigator.usb.getDevices();
@@ -498,13 +475,6 @@
         e => internal.webUsbServiceCrossFrameProxy.forwardHandle(e.handle);
     otherWindow.webUsbServiceInterceptor.start();
 
-    otherWindow.chooserInterceptor =
-        new otherWindow.MojoInterfaceInterceptor(
-            device.mojom.UsbChooserService.name);
-    otherWindow.chooserInterceptor.oninterfacerequest =
-        e => internal.chooserCrossFrameProxy.forwardHandle(e.handle);
-    otherWindow.chooserInterceptor.start();
-
     // Wait for a call to GetDevices() to pass between the renderer and the
     // mock in order to establish that everything is set up.
     await otherWindow.navigator.usb.getDevices();
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 a20bbe5..05ef39e 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
@@ -14,11 +14,11 @@
 PASS new RTCPeerConnection({ certificates: [undefined] })
 PASS new RTCPeerConnection({ iceCandidatePoolSize: toNumberThrows })
 PASS localDescription initial value
-FAIL currentLocalDescription initial value assert_equals: expected (object) null but got (undefined) undefined
-FAIL pendingLocalDescription initial value assert_equals: expected (object) null but got (undefined) undefined
+PASS currentLocalDescription initial value
+PASS pendingLocalDescription initial value
 PASS remoteDescription initial value
-FAIL currentRemoteDescription initial value assert_equals: expected (object) null but got (undefined) undefined
-FAIL pendingRemoteDescription initial value assert_equals: expected (object) null but got (undefined) undefined
+PASS currentRemoteDescription initial value
+PASS pendingRemoteDescription initial value
 PASS signalingState initial value
 PASS iceGatheringState initial value
 PASS iceConnectionState initial value
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 7f55db4..e051cf4 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
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 FAIL createOffer() with no argument from newly created RTCPeerConnection should succeed assert_false: Expect offer to not be instance of RTCSessionDescription expected false got true
-FAIL createOffer() and then setLocalDescription() should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
+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
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt
index 44e2c10..a505556e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
-FAIL setLocalDescription() with valid answer should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-FAIL setLocalDescription() with type answer and null sdp should use lastAnswer generated from createAnswer assert_not_equals: Expect session description to be defined got disallowed value undefined
+PASS setLocalDescription() with valid answer should succeed
+PASS setLocalDescription() with type answer and null sdp should use lastAnswer generated from createAnswer
 PASS setLocalDescription() with answer not created by own createAnswer() should reject with InvalidModificationError
 FAIL Calling setLocalDescription(answer) from stable state should reject with InvalidStateError assert_throws: function "function() { throw e }" threw object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to set local answer sdp: Called in wrong state: kStable" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
 FAIL Calling setLocalDescription(answer) from have-local-offer state should reject with InvalidStateError assert_throws: function "function() { throw e }" threw object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to set local answer sdp: Called in wrong state: kHaveLocalOffer" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-expected.txt
deleted file mode 100644
index b344dced..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL Calling createOffer() and setLocalDescription() again after one round of local-offer/remote-answer should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-FAIL Switching role from answerer to offerer after going back to stable state should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-PASS onsignalingstatechange fires before setLocalDescription resolves
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt
index 277771e..32fe2b6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
-FAIL setLocalDescription with valid offer should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-FAIL setLocalDescription with type offer and null sdp should use lastOffer generated from createOffer assert_not_equals: Expect session description to be defined got disallowed value undefined
+PASS setLocalDescription with valid offer should succeed
+PASS setLocalDescription with type offer and null sdp should use lastOffer generated from createOffer
 PASS setLocalDescription() with offer not created by own createOffer() should reject with InvalidModificationError
 FAIL Set created offer other than last offer should reject with InvalidModificationError assert_unreached: Should have rejected: undefined Reached unreachable code
 FAIL Creating and setting offer multiple times should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer-expected.txt
index 4e203a5..10e1954 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
 FAIL setLocalDescription(pranswer) from stable state should reject with InvalidStateError assert_throws: function "function() { throw e }" threw object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to set local pranswer sdp: Called in wrong state: kStable" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
-FAIL setLocalDescription(pranswer) should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
+FAIL setLocalDescription(pranswer) should succeed assert_equals: expected null but got object "[object RTCSessionDescription]"
 PASS setLocalDescription(pranswer) can be applied multiple times while still in have-local-pranswer
-FAIL setLocalDescription(answer) from have-local-pranswer state should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
+PASS setLocalDescription(answer) from have-local-pranswer state should succeed
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt
index 6ea8f05e..f38feca 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL setLocalDescription(rollback) from have-local-offer state should reset back to stable state assert_equals: expected (object) null but got (undefined) undefined
+FAIL setLocalDescription(rollback) from have-local-offer state should reset back to stable state promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
 FAIL setLocalDescription(rollback) from stable state should reject with InvalidStateError assert_throws: function "function() { throw e }" threw object "TypeError: Failed to execute 'setLocalDescription' 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 setLocalDescription(rollback) after setting answer description should reject with InvalidStateError assert_throws: function "function() { throw e }" threw object "TypeError: Failed to execute 'setLocalDescription' 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 setLocalDescription(rollback) should ignore invalid sdp content and succeed promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-answer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-answer-expected.txt
deleted file mode 100644
index 83e25392..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-answer-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL setRemoteDescription() with valid state and answer should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-PASS Calling setRemoteDescription(answer) from stable state should reject with InvalidStateError
-PASS Calling setRemoteDescription(answer) from have-remote-offer state should reject with InvalidStateError
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-offer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-offer-expected.txt
index a2b12a4..667000f2 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-offer-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-offer-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
-FAIL setRemoteDescription with valid offer should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-FAIL setRemoteDescription multiple times should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-FAIL setRemoteDescription multiple times with different offer should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
+PASS setRemoteDescription with valid offer should succeed
+PASS setRemoteDescription multiple times should succeed
+PASS setRemoteDescription multiple times with different offer should succeed
 FAIL setRemoteDescription(offer) with invalid SDP should reject with RTCError assert_equals: Expect error detail field to set to sdp-syntax-error expected (string) "sdp-syntax-error" but got (undefined) undefined
 PASS setRemoteDescription(offer) from have-local-offer state should reject with InvalidStateError
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-pranswer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-pranswer-expected.txt
deleted file mode 100644
index aafcd3ab..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-pranswer-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS setRemoteDescription(pranswer) from stable state should reject with InvalidStateError
-FAIL setRemoteDescription(pranswer) from have-local-offer state should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-PASS setRemoteDescription(pranswer) multiple times should succeed
-FAIL setRemoteDescription(answer) from have-remote-pranswer state should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
index 335c5e17..5dd73b0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -20,12 +20,12 @@
 PASS RTCPeerConnection interface: operation createAnswer(RTCAnswerOptions)
 PASS RTCPeerConnection interface: operation setLocalDescription(RTCSessionDescriptionInit)
 PASS RTCPeerConnection interface: attribute localDescription
-FAIL RTCPeerConnection interface: attribute currentLocalDescription assert_true: The prototype object must have a property "currentLocalDescription" expected true got false
-FAIL RTCPeerConnection interface: attribute pendingLocalDescription assert_true: The prototype object must have a property "pendingLocalDescription" expected true got false
+PASS RTCPeerConnection interface: attribute currentLocalDescription
+PASS RTCPeerConnection interface: attribute pendingLocalDescription
 PASS RTCPeerConnection interface: operation setRemoteDescription(RTCSessionDescriptionInit)
 PASS RTCPeerConnection interface: attribute remoteDescription
-FAIL RTCPeerConnection interface: attribute currentRemoteDescription assert_true: The prototype object must have a property "currentRemoteDescription" expected true got false
-FAIL RTCPeerConnection interface: attribute pendingRemoteDescription assert_true: The prototype object must have a property "pendingRemoteDescription" expected true got false
+PASS RTCPeerConnection interface: attribute currentRemoteDescription
+PASS RTCPeerConnection interface: attribute pendingRemoteDescription
 PASS RTCPeerConnection interface: operation addIceCandidate(RTCIceCandidateInit)
 PASS RTCPeerConnection interface: attribute signalingState
 PASS RTCPeerConnection interface: attribute iceGatheringState
@@ -70,13 +70,13 @@
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setLocalDescription(RTCSessionDescriptionInit)" with the proper type
 PASS RTCPeerConnection interface: calling setLocalDescription(RTCSessionDescriptionInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "localDescription" with the proper type
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentLocalDescription" with the proper type assert_inherits: property "currentLocalDescription" not found in prototype chain
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingLocalDescription" with the proper type assert_inherits: property "pendingLocalDescription" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentLocalDescription" with the proper type
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingLocalDescription" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setRemoteDescription(RTCSessionDescriptionInit)" with the proper type
 PASS RTCPeerConnection interface: calling setRemoteDescription(RTCSessionDescriptionInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "remoteDescription" with the proper type
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentRemoteDescription" with the proper type assert_inherits: property "currentRemoteDescription" not found in prototype chain
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingRemoteDescription" with the proper type assert_inherits: property "pendingRemoteDescription" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentRemoteDescription" with the proper type
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingRemoteDescription" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addIceCandidate(RTCIceCandidateInit)" with the proper type
 PASS RTCPeerConnection interface: calling addIceCandidate(RTCIceCandidateInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "signalingState" with the proper type
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js
index 881ea8b..9374092 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js
@@ -21,7 +21,6 @@
     '/resources/chromium/string16.mojom.js',
     '/resources/chromium/device.mojom.js',
     '/resources/chromium/device_manager.mojom.js',
-    '/resources/chromium/chooser_service.mojom.js',
     '/resources/chromium/web_usb_service.mojom.js',
     '/resources/chromium/webusb-test.js',
   ].forEach(path => {
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index 660f2700..97de5e9 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -68,6 +68,7 @@
 PASS oldChildWindow.navigator.vendorSub is newChildWindow.navigator.vendorSub
 PASS oldChildWindow.navigator.xr.ondevicechange is newChildWindow.navigator.xr.ondevicechange
 PASS oldChildWindow.onabort is newChildWindow.onabort
+PASS oldChildWindow.onactivateinvisible is newChildWindow.onactivateinvisible
 PASS oldChildWindow.onafterprint is newChildWindow.onafterprint
 PASS oldChildWindow.onanimationend is newChildWindow.onanimationend
 PASS oldChildWindow.onanimationiteration is newChildWindow.onanimationiteration
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index fc2e1f4..f600bf2e 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -36,6 +36,7 @@
 PASS childWindow.navigator.vendor is window.navigator.vendor
 PASS childWindow.navigator.vendorSub is ''
 PASS childWindow.onabort is null
+PASS childWindow.onactivateinvisible is null
 PASS childWindow.onafterprint is null
 PASS childWindow.onanimationend is null
 PASS childWindow.onanimationiteration is null
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index a1173909..975f97d6 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -36,6 +36,7 @@
 PASS childWindow.navigator.vendor is window.navigator.vendor
 PASS childWindow.navigator.vendorSub is ''
 PASS childWindow.onabort is null
+PASS childWindow.onactivateinvisible is null
 PASS childWindow.onafterprint is null
 PASS childWindow.onanimationend is null
 PASS childWindow.onanimationiteration is null
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-contentHint.html b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-contentHint.html
deleted file mode 100644
index b62b9c44..0000000
--- a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-contentHint.html
+++ /dev/null
@@ -1,111 +0,0 @@
-<!DOCTYPE HTML>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<canvas id="canvas">
-</canvas>
-<script>
-
-function createAudioTrack() {
-  ac = new AudioContext();
-  var osc = ac.createOscillator();
-  var dest = ac.createMediaStreamDestination();
-  osc.connect(dest);
-  audio_track = dest.stream.getAudioTracks()[0];
-
-  assert_equals(audio_track.kind, "audio");
-  return audio_track;
-}
-
-function createVideoTrack() {
-  canvas = document.getElementById("canvas");
-  video_track = canvas.captureStream().getVideoTracks()[0];
-
-  assert_equals(video_track.kind, "video");
-  return video_track;
-}
-
-test(t => {
-  audio_track = createAudioTrack();
-  assert_equals("", audio_track.contentHint);
-
-  video_track = createVideoTrack();
-  assert_equals("", video_track.contentHint);
-}, "Tracks have empty default content hint");
-
-test(t => {
-  audio_track = createAudioTrack();
-  audio_track.contentHint = "speech";
-  assert_equals(audio_track.contentHint, "speech");
-  audio_track.contentHint = "music";
-  assert_equals(audio_track.contentHint, "music");
-  audio_track.contentHint = "";
-  assert_equals(audio_track.contentHint, "");
-}, "Accepts valid audio contentHints");
-
-test(t => {
-  audio_track = createAudioTrack();
-  audio_track.contentHint = "speech";
-  assert_equals(audio_track.contentHint, "speech");
-  audio_track.contentHint = "motion";
-  assert_equals(audio_track.contentHint, "speech",
-                "Audio tracks should ignore video-only contentHints.");
-  audio_track.contentHint = "bogus";
-  assert_equals(audio_track.contentHint, "speech",
-                "Audio tracks should ignore garbage contentHints");
-}, "Audio tracks ignore invalid/video contentHints");
-
-test(t => {
-  video_track = createVideoTrack();
-  video_track.contentHint = "motion";
-  assert_equals(video_track.contentHint, "motion");
-  video_track.contentHint = "detail";
-  assert_equals(video_track.contentHint, "detail");
-  video_track.contentHint = "text";
-  assert_equals(video_track.contentHint, "text");
-  video_track.contentHint = "";
-  assert_equals(video_track.contentHint, "");
-}, "Accepts valid video contentHints");
-
-test(t => {
-  video_track = createVideoTrack();
-  video_track.contentHint = "motion";
-  assert_equals(video_track.contentHint, "motion");
-  video_track.contentHint = "speech";
-  assert_equals(video_track.contentHint, "motion",
-                "Video tracks should ignore audio-only contentHints.");
-  video_track.contentHint = "bogus";
-  assert_equals(video_track.contentHint, "motion",
-                "Video tracks should ignore garbage contentHints");
-}, "Video tracks ignore invalid/audio contentHints");
-
-test(t => {
-  video_track = createVideoTrack();
-  video_track.contentHint = "motion";
-  assert_equals(video_track.contentHint, "motion");
-
-  // Cloning a track should preserve contentHint.
-  video_track_clone = video_track.clone();
-  assert_equals(video_track_clone.contentHint, "motion");
-
-  // Changing a cloned track's contentHint should not change the original.
-  video_track_clone.contentHint = "detail";
-  assert_equals(video_track_clone.contentHint, "detail");
-  assert_equals(video_track.contentHint, "motion");
-}, "Cloned video tracks have separate contentHints");
-
-test(t => {
-  audio_track = createAudioTrack();
-  audio_track.contentHint = "speech";
-  assert_equals(audio_track.contentHint, "speech");
-
-  // Cloning a track should preserve contentHint.
-  audio_track_clone = audio_track.clone();
-  assert_equals(audio_track_clone.contentHint, "speech");
-
-  // Changing a cloned track's contentHint should not change the original.
-  audio_track_clone.contentHint = "music";
-  assert_equals(audio_track_clone.contentHint, "music");
-  assert_equals(audio_track.contentHint, "speech");
-}, "Cloned audio tracks have separate contentHints");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-get-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-get-expected.txt
index afc7089..f9d5e35 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-get-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-get-expected.txt
@@ -14,5 +14,5 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = localhost:8080
 HTTP_REFERER = http://127.0.0.1:8000/navigation/form-targets-cross-site-frame-get.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="nested", site="cross-site"
+HTTP_SEC_METADATA = cause="forced", destination="nested-document", site="cross-site"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-no-referrer-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-no-referrer-expected.txt
index dcfe1cc4..0a86364 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-no-referrer-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-no-referrer-expected.txt
@@ -15,5 +15,5 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = localhost:8080
 HTTP_ORIGIN = null
-HTTP_SEC_METADATA = cause="forced", destination="document", target="nested", site="cross-site"
+HTTP_SEC_METADATA = cause="forced", destination="nested-document", site="cross-site"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-post-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-post-expected.txt
index e8c68ff4..ad60416 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-post-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/form-targets-cross-site-frame-post-expected.txt
@@ -16,5 +16,5 @@
 HTTP_HOST = localhost:8080
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/form-targets-cross-site-frame-post.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="nested", site="cross-site"
+HTTP_SEC_METADATA = cause="forced", destination="nested-document", site="cross-site"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/form-with-enctype-targets-cross-site-frame-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/form-with-enctype-targets-cross-site-frame-expected.txt
index cc9c484..09424e7 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/form-with-enctype-targets-cross-site-frame-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/form-with-enctype-targets-cross-site-frame-expected.txt
@@ -16,5 +16,5 @@
 HTTP_HOST = localhost:8080
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/form-with-enctype-targets-cross-site-frame.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="nested", site="cross-site"
+HTTP_SEC_METADATA = cause="forced", destination="nested-document", site="cross-site"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/post-basic-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/post-basic-expected.txt
index 0af6987..7db0e92 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/post-basic-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/post-basic-expected.txt
@@ -11,7 +11,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/resources/page-that-posts.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/post-frames-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/post-frames-expected.txt
index 85141c7..cbe4b6d1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/post-frames-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/post-frames-expected.txt
@@ -18,7 +18,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/resources/page-that-posts.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="nested", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="nested-document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/post-frames-goback1-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/post-frames-goback1-expected.txt
index 2d6eac7..f7e952d 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/post-frames-goback1-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/post-frames-goback1-expected.txt
@@ -18,7 +18,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/post-frames-goback1.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="nested", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="nested-document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/post-goback1-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/post-goback1-expected.txt
index b347ee0..40a1b6d 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/post-goback1-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/post-goback1-expected.txt
@@ -11,7 +11,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/resources/page-that-posts.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/rename-subframe-goback-expected.txt b/third_party/WebKit/LayoutTests/http/tests/navigation/rename-subframe-goback-expected.txt
index daf9c93..1f501b85 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/rename-subframe-goback-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/rename-subframe-goback-expected.txt
@@ -19,5 +19,5 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/resources/page-that-posts.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="nested", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="nested-document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect-expected.txt
index cd2a06b..2eeaa6d 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect-expected.txt
@@ -8,7 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt
index d12c6fc..eeb17c3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt
@@ -10,7 +10,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-allowed.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect-expected.txt
index 371e67d..ed05f6b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect-expected.txt
@@ -8,7 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt
index 29839e6..bb54c81 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt
@@ -10,7 +10,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect-expected.txt
index fdaf488..3ba24adc 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect-expected.txt
@@ -8,7 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt
index 67c894a4..a82b47f5 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt
@@ -8,7 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect-expected.txt
index 3e5bc89a..8303dde 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect-expected.txt
@@ -8,7 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect.html
-HTTP_SEC_METADATA = cause="forced", destination="document", target="top-level", site="same-origin"
+HTTP_SEC_METADATA = cause="forced", destination="document", site="same-origin"
 HTTP_UPGRADE_INSECURE_REQUESTS = 1
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/invisible_dom/OWNERS b/third_party/WebKit/LayoutTests/invisible_dom/OWNERS
new file mode 100644
index 0000000..63218e0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/invisible_dom/OWNERS
@@ -0,0 +1 @@
+rakina@chromium.org
diff --git a/third_party/WebKit/LayoutTests/invisible_dom/invisible-activate.html b/third_party/WebKit/LayoutTests/invisible_dom/invisible-activate.html
new file mode 100644
index 0000000..d2868782
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/invisible_dom/invisible-activate.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<title>activateinvisible Event</title>
+<body>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<div id="outer">
+  Outer
+  <div id="inner">
+    Inner
+    <div id="innermost">
+      Innermost
+    </div>
+  </div>
+  <div id="host">
+    <div id="slotted">
+      Slotted
+    </div>
+  </div>
+</div>
+<a id="outerLink" href="#outer">Click</a>
+<a id="innerLink" href="#inner">Click</a>
+<a id="innermostLink" href ="#innermost">Click</a>
+<a id="hostLink" href="#host">Click</a>
+<a id="slottedLink" href="#slotted">Click</a>
+<a id="slotLink" href="#slot">Click</a>
+<script>
+'use strict';
+const shadowRoot = host.attachShadow({mode:"open"});
+const slot = document.createElement("slot");
+slot.id = "slot";
+shadowRoot.appendChild(slot);
+const elements = [outer, inner, innermost, host, slot, slotted];
+
+function setUp() {
+  elements.forEach((val) => {
+    val.removeAttribute("invisible");
+    val.onactivateinvisible = null;
+  });
+}
+
+promise_test(() => {
+  setUp();
+  inner.invisible = "invisible";
+  return new Promise((resolve, reject) => {
+    const eventPromise = new Promise((resolve, reject) => {
+      inner.onactivateinvisible = (e) => {
+        assert_equals(e.target, inner);
+        assert_equals(e.activatedElement, inner);
+        resolve();
+      };
+      innerLink.click();
+    });
+    eventPromise.then(() => {
+      assert_false(inner.hasAttribute("invisible"));
+      resolve();
+    });
+  });
+}, "Activating invisible element fires activateinvisible and removes invisible");
+
+promise_test(() => {
+  setUp();
+  return new Promise((resolve, reject) => {
+    // #inner and #innermost should not receive activateinvisible.
+    inner.onactivateinvisible = innermost.onactivateinvisible = reject;
+    const eventReceivedOnOuter = new Promise((resolve, reject) => {
+      outer.onactivateinvisible = (e) => {
+        assert_equals(e.target, outer);
+        assert_equals(e.activatedElement, innermost);
+        resolve();
+      };
+      outer.invisible = "invisible";
+      innermostLink.click();
+    });
+    eventReceivedOnOuter.then(() => {
+      assert_false(outer.hasAttribute("invisible"));
+      assert_false(inner.hasAttribute("invisible"));
+      assert_false(innermost.hasAttribute("invisible"));
+      resolve();
+    });
+  });
+}, "Activating descendant invisible element fires activateinvisible and removes invisible on ancestors");
+
+promise_test(() => {
+  setUp();
+  return new Promise((resolve, reject) => {
+    const eventReceivedOnKumaAncestors = new Promise((resolve, reject) => {
+      outer.onactivateinvisible = (e) => {
+        // Event sent to #host bubbles to #outer.
+        assert_equals(e.target, host);
+        assert_equals(e.activatedElement, slotted);
+      };
+      const eventReceivedOnHost = new Promise((resolve, reject) => {
+        host.onactivateinvisible = (e) => {
+          assert_equals(e.target, host);
+          assert_equals(e.activatedElement, slotted);
+          resolve();
+        };
+      });
+      const eventReceivedOnSlot = new Promise((resolve, reject) => {
+        slot.onactivateinvisible = (e) => {
+          assert_equals(e.target, slot);
+          assert_equals(e.activatedElement, slotted);
+          resolve();
+        };
+      });
+      // Event is not sent to #slotted because it has no invisible attribute.
+      slotted.onactivateinvisible = reject;
+
+      host.invisible = slot.invisible = "invisible";
+      slottedLink.click();
+      Promise.all([eventReceivedOnHost, eventReceivedOnSlot]).then(resolve);
+    });
+    eventReceivedOnKumaAncestors.then(() => {
+      assert_false(outer.hasAttribute("invisible"));
+      assert_false(host.hasAttribute("invisible"));
+      assert_false(slot.hasAttribute("invisible"));
+      assert_false(slotted.hasAttribute("invisible"));
+      resolve();
+    });
+  });
+}, "Activating flat-tree descendant invisible element fires activateinvisible and removes invisible on invisible ancestors");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/invisible_dom/invisible-attribute.html b/third_party/WebKit/LayoutTests/invisible_dom/invisible-attribute.html
new file mode 100644
index 0000000..ab7000d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/invisible_dom/invisible-attribute.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<title>Kuma Elements</title>
+<body>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<div id="base"></div>
+
+<template id="temp">
+  <div id="outerDiv">
+    Can you
+    <div id="innerDiv" invisible>
+      see me?
+    </div>
+  </div>
+</template>
+<script>
+let outerDiv = innerDiv = null;
+'use strict';
+
+function setUp() {
+  base.innerHTML = "";
+  const clone = document.importNode(temp.content, true);
+  base.appendChild(clone);
+  outerDiv = document.querySelector("#outerDiv");
+  innerDiv = document.querySelector("#innerDiv");
+}
+
+function assertIsDisplayed(el) {
+  assert_not_equals(el.offsetWidth, 0);
+  assert_not_equals(el.offsetHeight, 0);
+}
+
+function assertIsNotDisplayed(el) {
+  assert_equals(el.offsetWidth, 0);
+  assert_equals(el.offsetHeight, 0);
+}
+
+test(() => {
+  setUp();
+  assert_false(outerDiv.hasAttribute("invisible"));
+  assert_true(innerDiv.hasAttribute("invisible"));
+
+  outerDiv.setAttribute("invisible", "invisible");
+  assert_true(outerDiv.hasAttribute("invisible"));
+
+  outerDiv.removeAttribute("invisible");
+  assert_false(outerDiv.hasAttribute("invisible"));
+}, "Setting/removing invisible attributes works");
+
+test(() => {
+  setUp();
+  // TODO(rakina): modify this test when we have levels ("invisible"/"static")
+  outerDiv.setAttribute("invisible", "x");
+  assert_equals(outerDiv.getAttribute("invisible"), "x");
+  assert_true(outerDiv.hasAttribute("invisible"));
+
+  outerDiv.setAttribute("invisible", "");
+  assert_equals(outerDiv.getAttribute("invisible"), "");
+  assert_true(outerDiv.hasAttribute("invisible"));
+}, "Setting/removing invisible attribute preserves original value.");
+
+test(() => {
+  setUp();
+  assert_equals(getComputedStyle(innerDiv).display, "none");
+  innerDiv.removeAttribute("invisible");
+  assert_equals(getComputedStyle(innerDiv).display, "block");
+}, "Kuma attributes makes element's display=none");
+
+test(() => {
+  setUp();
+  innerDiv.removeAttribute("invisible");
+  assertIsDisplayed(innerDiv);
+
+  outerDiv.setAttribute("invisible", "invisible");
+  assert_equals(getComputedStyle(outerDiv).display, "none", "outerDiv display=none");
+  assertIsNotDisplayed(outerDiv);
+  assert_equals(getComputedStyle(innerDiv).display, "block", "innerDiv display=block");
+  assertIsNotDisplayed(innerDiv);
+
+  outerDiv.removeAttribute("invisible");
+  assertIsDisplayed(outerDiv);
+  assertIsDisplayed(innerDiv);
+}, "Descendants of a invisible element are not displayed");
+
+test(() => {
+  setUp();
+  const host = document.createElement("span");
+  outerDiv.appendChild(host);
+  const shadowRoot = host.attachShadow({mode: "open"});
+  const slot = document.createElement("slot");
+  shadowRoot.appendChild(slot);
+  const slottedChild = document.createElement("p");
+  slottedChild.innerHTML = "I am <b>invisible</b>";
+  host.appendChild(slottedChild);
+  assertIsDisplayed(slottedChild);
+
+  slot.invisible = "invisible";
+  assertIsNotDisplayed(slottedChild);
+}, "Flat tree descendants of a invisible element are not displayed");
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/invisible_dom/invisible-reftest-expected.html b/third_party/WebKit/LayoutTests/invisible_dom/invisible-reftest-expected.html
new file mode 100644
index 0000000..7e280ca
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/invisible_dom/invisible-reftest-expected.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Kuma Reftest</title>
+<div>
+  Not Kuma
+  <div>
+    Not Kuma, again
+    <span id="secondHost">
+      Second Host
+    </span>
+  </div>
+  <ul>
+    <li>Second</li>
+  </ul>
+</div>
+
+<script>
+const secondShadowRoot = secondHost.attachShadow({mode: "open"});
+const visibleDiv = document.createElement("div");
+div.innerHTML = "<i>visible</i>";
+secondShadowRoot.appendChild(visibleDiv);
+</script>
diff --git a/third_party/WebKit/LayoutTests/invisible_dom/invisible-reftest.html b/third_party/WebKit/LayoutTests/invisible_dom/invisible-reftest.html
new file mode 100644
index 0000000..b7a8ec51
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/invisible_dom/invisible-reftest.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>Kuma Reftest</title>
+<div>
+  Not Kuma
+  <div id="first" invisible="invisible">
+    First Kuma
+    <h1>
+      Inside Kuma
+    </h1>
+    <span id="firstHost">
+      Kuma Host
+    </span>
+  </div>
+  <div>
+    Not Kuma, again
+    <span id="secondHost">
+      Second Host
+      <div>
+        I'm gonna be invisible.
+      </div>
+    </span>
+  </div>
+  <p invisible>
+    Another Kuma
+  </p>
+  <ul>
+    <li invisible="">First</li>
+    <li>Second</li>
+  </ul>
+</div>
+
+<script>
+const firstShadowRoot = firstHost.attachShadow({mode: 'open'});
+firstShadowRoot.innerHTML = "not <b>visible</b>";
+
+const secondShadowRoot = secondHost.attachShadow({mode: 'open'});
+const visibleDiv = document.createElement("div");
+div.innerHTML = "<i>visible</i>";
+secondShadowRoot.appendChild(visibleDiv);
+const slot = document.createElement("slot");
+slot.invisible = "invisible";
+secondShadowRoot.appendChild(slot);
+</script>
diff --git a/third_party/WebKit/LayoutTests/usb/protected-interface-classes.html b/third_party/WebKit/LayoutTests/usb/protected-interface-classes.html
index 8780f20..022c2b9 100644
--- a/third_party/WebKit/LayoutTests/usb/protected-interface-classes.html
+++ b/third_party/WebKit/LayoutTests/usb/protected-interface-classes.html
@@ -5,7 +5,6 @@
 <script src="../external/wpt/resources/chromium/string16.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/device.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/device_manager.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/chooser_service.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/web_usb_service.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/webusb-test.js"></script>
 <body>
diff --git a/third_party/WebKit/LayoutTests/usb/usbDevice-iframe.html b/third_party/WebKit/LayoutTests/usb/usbDevice-iframe.html
index bf54e8c..90b701d 100644
--- a/third_party/WebKit/LayoutTests/usb/usbDevice-iframe.html
+++ b/third_party/WebKit/LayoutTests/usb/usbDevice-iframe.html
@@ -5,7 +5,6 @@
 <script src="../external/wpt/resources/chromium/mojo_bindings.js"></script>
 <script src="../external/wpt/resources/chromium/device.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/device_manager.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/chooser_service.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/web_usb_service.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/webusb-test.js"></script>
 <body>
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
index f266c137..d6bd9d9c 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3899,6 +3899,7 @@
     method constructor
 interface MediaStreamTrack : EventTarget
     attribute @@toStringTag
+    getter contentHint
     getter enabled
     getter id
     getter kind
@@ -3915,6 +3916,7 @@
     method getConstraints
     method getSettings
     method stop
+    setter contentHint
     setter enabled
     setter onended
     setter onmute
@@ -4809,6 +4811,8 @@
 interface RTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
+    getter currentLocalDescription
+    getter currentRemoteDescription
     getter iceConnectionState
     getter iceGatheringState
     getter localDescription
@@ -4821,6 +4825,8 @@
     getter onremovestream
     getter onsignalingstatechange
     getter ontrack
+    getter pendingLocalDescription
+    getter pendingRemoteDescription
     getter remoteDescription
     getter signalingState
     method addIceCandidate
@@ -8341,6 +8347,8 @@
 interface webkitRTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
+    getter currentLocalDescription
+    getter currentRemoteDescription
     getter iceConnectionState
     getter iceGatheringState
     getter localDescription
@@ -8353,6 +8361,8 @@
     getter onremovestream
     getter onsignalingstatechange
     getter ontrack
+    getter pendingLocalDescription
+    getter pendingRemoteDescription
     getter remoteDescription
     getter signalingState
     method addIceCandidate
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 72690d4..39aed2a 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
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 FAIL createOffer() with no argument from newly created RTCPeerConnection should succeed assert_false: Expect offer to not be instance of RTCSessionDescription expected false got true
-FAIL createOffer() and then setLocalDescription() should succeed assert_not_equals: Expect session description to be defined got disallowed value undefined
+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
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
index 40d5666..05117e6e 100644
--- a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -20,12 +20,12 @@
 PASS RTCPeerConnection interface: operation createAnswer(RTCAnswerOptions)
 PASS RTCPeerConnection interface: operation setLocalDescription(RTCSessionDescriptionInit)
 PASS RTCPeerConnection interface: attribute localDescription
-FAIL RTCPeerConnection interface: attribute currentLocalDescription assert_true: The prototype object must have a property "currentLocalDescription" expected true got false
-FAIL RTCPeerConnection interface: attribute pendingLocalDescription assert_true: The prototype object must have a property "pendingLocalDescription" expected true got false
+PASS RTCPeerConnection interface: attribute currentLocalDescription
+PASS RTCPeerConnection interface: attribute pendingLocalDescription
 PASS RTCPeerConnection interface: operation setRemoteDescription(RTCSessionDescriptionInit)
 PASS RTCPeerConnection interface: attribute remoteDescription
-FAIL RTCPeerConnection interface: attribute currentRemoteDescription assert_true: The prototype object must have a property "currentRemoteDescription" expected true got false
-FAIL RTCPeerConnection interface: attribute pendingRemoteDescription assert_true: The prototype object must have a property "pendingRemoteDescription" expected true got false
+PASS RTCPeerConnection interface: attribute currentRemoteDescription
+PASS RTCPeerConnection interface: attribute pendingRemoteDescription
 PASS RTCPeerConnection interface: operation addIceCandidate(RTCIceCandidateInit)
 PASS RTCPeerConnection interface: attribute signalingState
 PASS RTCPeerConnection interface: attribute iceGatheringState
@@ -70,13 +70,13 @@
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setLocalDescription(RTCSessionDescriptionInit)" with the proper type
 PASS RTCPeerConnection interface: calling setLocalDescription(RTCSessionDescriptionInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "localDescription" with the proper type
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentLocalDescription" with the proper type assert_inherits: property "currentLocalDescription" not found in prototype chain
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingLocalDescription" with the proper type assert_inherits: property "pendingLocalDescription" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentLocalDescription" with the proper type
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingLocalDescription" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setRemoteDescription(RTCSessionDescriptionInit)" with the proper type
 PASS RTCPeerConnection interface: calling setRemoteDescription(RTCSessionDescriptionInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "remoteDescription" with the proper type
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentRemoteDescription" with the proper type assert_inherits: property "currentRemoteDescription" not found in prototype chain
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingRemoteDescription" with the proper type assert_inherits: property "pendingRemoteDescription" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentRemoteDescription" with the proper type
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingRemoteDescription" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addIceCandidate(RTCIceCandidateInit)" with the proper type
 PASS RTCPeerConnection interface: calling addIceCandidate(RTCIceCandidateInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "signalingState" with the proper type
diff --git a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
index dd6f236..e01e9a0c 100644
--- a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
@@ -134,6 +134,7 @@
     property insertAdjacentHTML
     property insertAdjacentText
     property insertBefore
+    property invisible
     property isConnected
     property isContentEditable
     property isDefaultNamespace
@@ -160,6 +161,7 @@
     property offsetTop
     property offsetWidth
     property onabort
+    property onactivateinvisible
     property onauxclick
     property onbeforecopy
     property onbeforecut
@@ -1286,6 +1288,7 @@
     property insertAdjacentHTML
     property insertAdjacentText
     property insertBefore
+    property invisible
     property isConnected
     property isDefaultNamespace
     property isEqualNode
@@ -1305,6 +1308,7 @@
     property nonce
     property normalize
     property onabort
+    property onactivateinvisible
     property onauxclick
     property onbeforecopy
     property onbeforecut
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 1d819ec..9f79e066 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -146,6 +146,10 @@
     method item
     method remove
     setter length
+interface ActivateInvisibleEvent : Event
+    attribute @@toStringTag
+    getter activatedElement
+    method constructor
 interface AmbientLightSensor : Sensor
     attribute @@toStringTag
     getter illuminance
@@ -1500,6 +1504,7 @@
     getter linkColor
     getter links
     getter onabort
+    getter onactivateinvisible
     getter onauxclick
     getter onbeforecopy
     getter onbeforecut
@@ -1689,6 +1694,7 @@
     setter fullscreenEnabled
     setter linkColor
     setter onabort
+    setter onactivateinvisible
     setter onauxclick
     setter onbeforecopy
     setter onbeforecut
@@ -1889,6 +1895,7 @@
     getter firstElementChild
     getter id
     getter innerHTML
+    getter invisible
     getter lastElementChild
     getter localName
     getter namespaceURI
@@ -2015,6 +2022,7 @@
     setter className
     setter id
     setter innerHTML
+    setter invisible
     setter onbeforecopy
     setter onbeforecut
     setter onbeforepaste
@@ -2537,6 +2545,7 @@
     getter offsetTop
     getter offsetWidth
     getter onabort
+    getter onactivateinvisible
     getter onauxclick
     getter onblur
     getter oncancel
@@ -2633,6 +2642,7 @@
     setter lang
     setter nonce
     setter onabort
+    setter onactivateinvisible
     setter onauxclick
     setter onblur
     setter oncancel
@@ -5442,6 +5452,8 @@
 interface RTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
+    getter currentLocalDescription
+    getter currentRemoteDescription
     getter iceConnectionState
     getter iceGatheringState
     getter id
@@ -5455,6 +5467,8 @@
     getter onremovestream
     getter onsignalingstatechange
     getter ontrack
+    getter pendingLocalDescription
+    getter pendingRemoteDescription
     getter remoteDescription
     getter signalingState
     method addIceCandidate
@@ -5833,6 +5847,7 @@
     getter dataset
     getter nonce
     getter onabort
+    getter onactivateinvisible
     getter onauxclick
     getter onblur
     getter oncancel
@@ -5916,6 +5931,7 @@
     method focus
     setter nonce
     setter onabort
+    setter onactivateinvisible
     setter onauxclick
     setter onblur
     setter oncancel
@@ -10167,6 +10183,8 @@
 interface webkitRTCPeerConnection : EventTarget
     static method generateCertificate
     attribute @@toStringTag
+    getter currentLocalDescription
+    getter currentRemoteDescription
     getter iceConnectionState
     getter iceGatheringState
     getter id
@@ -10180,6 +10198,8 @@
     getter onremovestream
     getter onsignalingstatechange
     getter ontrack
+    getter pendingLocalDescription
+    getter pendingRemoteDescription
     getter remoteDescription
     getter signalingState
     method addIceCandidate
@@ -10350,6 +10370,7 @@
     getter navigator
     getter offscreenBuffering
     getter onabort
+    getter onactivateinvisible
     getter onafterprint
     getter onanimationend
     getter onanimationiteration
@@ -10536,6 +10557,7 @@
     setter name
     setter offscreenBuffering
     setter onabort
+    setter onactivateinvisible
     setter onafterprint
     setter onanimationend
     setter onanimationiteration
diff --git a/third_party/blink/public/mojom/usb/web_usb_service.mojom b/third_party/blink/public/mojom/usb/web_usb_service.mojom
index 27bb474f..0c9fdd3 100644
--- a/third_party/blink/public/mojom/usb/web_usb_service.mojom
+++ b/third_party/blink/public/mojom/usb/web_usb_service.mojom
@@ -18,6 +18,15 @@
   // Requests a device by guid.
   GetDevice(string guid, device.mojom.UsbDevice& device_requestd);
 
+  // Get permission from user to use a device.
+  //
+  // |device_filters| filters the available devices and the filtered
+  // devices will be listed for user to grant permission.
+  //
+  // |result| is the device that user grants permission to use.
+  GetPermission(array<device.mojom.UsbDeviceFilter> device_filters)
+      => (device.mojom.UsbDeviceInfo? result);
+
   // Sets the client for this WebUsbService.
   SetClient(device.mojom.UsbDeviceManagerClient client);
 };
diff --git a/third_party/blink/public/platform/web_rtc_peer_connection_handler.h b/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
index 9c0e27aa..682f4883 100644
--- a/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
+++ b/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
@@ -82,6 +82,10 @@
                                     const WebRTCSessionDescription&) = 0;
   virtual WebRTCSessionDescription LocalDescription() = 0;
   virtual WebRTCSessionDescription RemoteDescription() = 0;
+  virtual WebRTCSessionDescription CurrentLocalDescription() = 0;
+  virtual WebRTCSessionDescription CurrentRemoteDescription() = 0;
+  virtual WebRTCSessionDescription PendingLocalDescription() = 0;
+  virtual WebRTCSessionDescription PendingRemoteDescription() = 0;
   virtual webrtc::RTCErrorType SetConfiguration(const WebRTCConfiguration&) = 0;
 
   // DEPRECATED
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 af9364b..c066507 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -308,7 +308,9 @@
   suppressed_reason_ = reason;
 }
 
-static void RunScriptStreamingTask(
+namespace {
+
+void RunScriptStreamingTask(
     std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task,
     ScriptStreamer* streamer) {
   TRACE_EVENT1(
@@ -321,6 +323,23 @@
   streamer->StreamingCompleteOnBackgroundThread();
 }
 
+void RunBlockingScriptStreamingTask(
+    std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task,
+    ScriptStreamer* streamer,
+    std::atomic_flag* blocking_task_started_or_cancelled) {
+  if (!blocking_task_started_or_cancelled->test_and_set())
+    return;
+  RunScriptStreamingTask(std::move(task), streamer);
+}
+
+void RunNonBlockingScriptStreamingTask(
+    std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task,
+    ScriptStreamer* streamer) {
+  RunScriptStreamingTask(std::move(task), streamer);
+}
+
+}  // namespace
+
 bool ScriptStreamer::HasEnoughDataForStreaming(size_t resource_buffer_size) {
   if (RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled()) {
     // Enable streaming for small scripts, but we must still check the BOM
@@ -408,17 +427,24 @@
     }
 
     if (RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled()) {
-      // Script streaming tasks are high priority, as they can block the
-      // parser, and they can (and probably will) block during their own
-      // execution as they wait for more input.
+      // Script streaming tasks are high priority, as they can block the parser,
+      // and they can (and probably will) block during their own execution as
+      // they wait for more input.
+      //
+      // Pass through the atomic cancellation token which is set to true by the
+      // task when it is started, or set to true by the streamer if it wants to
+      // cancel the task.
       //
       // TODO(leszeks): Decrease the priority of these tasks where possible.
       BackgroundScheduler::PostOnBackgroundThreadWithTraits(
           FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
-          CrossThreadBind(RunScriptStreamingTask,
+          CrossThreadBind(RunBlockingScriptStreamingTask,
                           WTF::Passed(std::move(script_streaming_task)),
-                          WrapCrossThreadPersistent(this)));
+                          WrapCrossThreadPersistent(this),
+                          WTF::CrossThreadUnretained(
+                              &blocking_task_started_or_cancelled_)));
     } else {
+      blocking_task_started_or_cancelled_.test_and_set();
       ScriptStreamerThread::Shared()->PostTask(
           CrossThreadBind(&ScriptStreamerThread::RunScriptStreamingTask,
                           WTF::Passed(std::move(script_streaming_task)),
@@ -440,8 +466,34 @@
     SuppressStreaming(kScriptTooSmall);
   }
 
-  if (stream_)
+  if (stream_) {
+    // If the corresponding blocking task hasn't started yet, cancel it and post
+    // a non-blocking task, since we know now that all the data is received and
+    // we will no longer block.
+    //
+    // TODO(874080): Once we have mutable task traits, simply unmark the
+    // existing task as no longer MayBlock.
+    if (RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled() &&
+        !blocking_task_started_or_cancelled_.test_and_set()) {
+      ScriptState::Scope scope(script_state_);
+      std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask>
+          script_streaming_task(
+              base::WrapUnique(v8::ScriptCompiler::StartStreamingScript(
+                  script_state_->GetIsolate(), source_.get(),
+                  compile_options_)));
+
+      // The task creation shouldn't fail, since it didn't fail before during
+      // NotifyAppendData.
+      CHECK(script_streaming_task);
+      BackgroundScheduler::PostOnBackgroundThreadWithTraits(
+          FROM_HERE, {base::TaskPriority::USER_BLOCKING},
+          CrossThreadBind(RunNonBlockingScriptStreamingTask,
+                          WTF::Passed(std::move(script_streaming_task)),
+                          WrapCrossThreadPersistent(this)));
+    }
+
     stream_->DidFinishLoading();
+  }
   loading_finished_ = true;
 
   NotifyFinishedToClient();
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 7a4f0ee..a51424e 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_H_
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_H_
 
+#include <atomic>
 #include <memory>
 
 #include "base/single_thread_task_runner.h"
@@ -146,6 +147,11 @@
   // Whether we have received enough data to start the streaming.
   bool have_enough_data_for_streaming_;
 
+  // Flag used to allow atomic cancelling and reposting of the streaming task
+  // when the load completes without the task yet starting.
+  // TODO(874080): Remove once we can mutate task traits.
+  std::atomic_flag blocking_task_started_or_cancelled_;
+
   // Whether the script source code should be retrieved from the Resource
   // instead of the ScriptStreamer.
   bool streaming_suppressed_;
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
index 800f037..eb804767 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h"
 
+#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_crypto.h"
 #include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
@@ -13,7 +14,6 @@
 #include "third_party/blink/renderer/modules/crypto/crypto_key.h"
 #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 
 namespace blink {
 
@@ -32,12 +32,14 @@
       uint32_t raw_type;
       String name;
       String root_url;
-      if (!ReadUint32(&raw_type) || raw_type > kFileSystemTypeLast ||
+      if (!ReadUint32(&raw_type) ||
+          raw_type >
+              static_cast<int32_t>(mojom::blink::FileSystemType::kMaxValue) ||
           !ReadUTF8String(&name) || !ReadUTF8String(&root_url))
         return nullptr;
-      return DOMFileSystem::Create(ExecutionContext::From(GetScriptState()),
-                                   name, static_cast<FileSystemType>(raw_type),
-                                   KURL(root_url));
+      return DOMFileSystem::Create(
+          ExecutionContext::From(GetScriptState()), name,
+          static_cast<mojom::blink::FileSystemType>(raw_type), KURL(root_url));
     }
     case kRTCCertificateTag: {
       String pem_private_key;
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
index bb02b02..346d8ccf 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
@@ -13,7 +13,6 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_dom_file_system.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_certificate.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index 065c2d8..4d56531 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -891,7 +891,7 @@
 
   DOMFileSystem* fs = DOMFileSystem::Create(
       scope.GetExecutionContext(), "http_example.com_0:Persistent",
-      kFileSystemTypePersistent,
+      mojom::blink::FileSystemType::kPersistent,
       KURL("filesystem:http://example.com/persistent/"));
   // At time of writing, this can only happen for filesystems from PPAPI.
   fs->MakeClonable();
@@ -901,7 +901,7 @@
   ASSERT_TRUE(V8DOMFileSystem::hasInstance(result, scope.GetIsolate()));
   DOMFileSystem* new_fs = V8DOMFileSystem::ToImpl(result.As<v8::Object>());
   EXPECT_EQ("http_example.com_0:Persistent", new_fs->name());
-  EXPECT_EQ(kFileSystemTypePersistent, new_fs->GetType());
+  EXPECT_EQ(mojom::blink::FileSystemType::kPersistent, new_fs->GetType());
   EXPECT_EQ("filesystem:http://example.com/persistent/",
             new_fs->RootURL().GetString());
 }
@@ -914,7 +914,7 @@
 
   DOMFileSystem* fs = DOMFileSystem::Create(
       scope.GetExecutionContext(), "http_example.com_0:Persistent",
-      kFileSystemTypePersistent,
+      mojom::blink::FileSystemType::kPersistent,
       KURL("filesystem:http://example.com/persistent/0/"));
   ASSERT_FALSE(fs->Clonable());
   v8::Local<v8::Value> wrapper = ToV8(fs, scope.GetScriptState());
@@ -944,7 +944,7 @@
   ASSERT_TRUE(V8DOMFileSystem::hasInstance(result, scope.GetIsolate()));
   DOMFileSystem* new_fs = V8DOMFileSystem::ToImpl(result.As<v8::Object>());
   EXPECT_EQ("http_example.com_0:Persistent", new_fs->name());
-  EXPECT_EQ(kFileSystemTypePersistent, new_fs->GetType());
+  EXPECT_EQ(mojom::blink::FileSystemType::kPersistent, new_fs->GetType());
   EXPECT_EQ("filesystem:http://example.com/persistent/",
             new_fs->RootURL().GetString());
 }
diff --git a/third_party/blink/renderer/bindings/scripts/v8_callback_function.py b/third_party/blink/renderer/bindings/scripts/v8_callback_function.py
index 4a46b5c..3e9827f 100644
--- a/third_party/blink/renderer/bindings/scripts/v8_callback_function.py
+++ b/third_party/blink/renderer/bindings/scripts/v8_callback_function.py
@@ -8,6 +8,7 @@
 """
 
 from v8_globals import includes
+import v8_types
 
 CALLBACK_FUNCTION_H_INCLUDES = frozenset([
     'platform/bindings/callback_function_base.h',
@@ -45,17 +46,10 @@
         'forward_declarations': sorted(forward_declarations(callback_function)),
         'header_includes': sorted(CALLBACK_FUNCTION_H_INCLUDES),
         'idl_type': idl_type_str,
+        'native_value_traits_tag': v8_types.idl_type_to_native_value_traits_tag(idl_type),
         'return_cpp_type': idl_type.cpp_type,
     }
 
-    if idl_type_str != 'void':
-        context.update({
-            'return_value_conversion': idl_type.v8_value_to_local_cpp_value(
-                callback_function.extended_attributes,
-                'call_result', 'native_result', isolate='GetIsolate()',
-                bailout_return_value='v8::Nothing<%s>()' % context['return_cpp_type']),
-        })
-
     context.update(arguments_context(callback_function.arguments))
     return context
 
diff --git a/third_party/blink/renderer/bindings/scripts/v8_callback_interface.py b/third_party/blink/renderer/bindings/scripts/v8_callback_interface.py
index d3d8728..025650c 100644
--- a/third_party/blink/renderer/bindings/scripts/v8_callback_interface.py
+++ b/third_party/blink/renderer/bindings/scripts/v8_callback_interface.py
@@ -145,24 +145,12 @@
     idl_type = operation.idl_type
     idl_type_str = str(idl_type)
 
-    # TODO(yukishiino,peria,rakuco): We should have this mapping as part of the
-    # bindings generator's infrastructure.
-    native_value_traits_tag_map = {
-        'boolean': 'IDLBoolean',
-        'unsigned short': 'IDLUnsignedShort',
-        'void': None,
-    }
-    if idl_type_str in native_value_traits_tag_map:
-        native_value_traits_tag_name = native_value_traits_tag_map[idl_type_str]
-    else:
-        raise Exception("Callback that returns type `%s' is not supported." % idl_type_str)
-
     add_includes_for_operation(operation)
     context = {
         'cpp_type': idl_type.callback_cpp_type,
         'idl_type': idl_type_str,
         'name': operation.name,
-        'native_value_traits_tag': native_value_traits_tag_name,
+        'native_value_traits_tag': v8_types.idl_type_to_native_value_traits_tag(idl_type),
     }
     context.update(arguments_context(operation.arguments))
     return context
@@ -176,6 +164,7 @@
                 creation_context='argument_creation_context'),
             'handle': '%sHandle' % argument.name,
             'name': argument.name,
+            'v8_name': 'v8_' + argument.name,
         }
 
     argument_declarations = ['ScriptWrappable* callback_this_value']
diff --git a/third_party/blink/renderer/bindings/scripts/v8_types.py b/third_party/blink/renderer/bindings/scripts/v8_types.py
index 9783a7dd..93f03a9e 100644
--- a/third_party/blink/renderer/bindings/scripts/v8_types.py
+++ b/third_party/blink/renderer/bindings/scripts/v8_types.py
@@ -1116,6 +1116,25 @@
 IdlArrayOrSequenceType.literal_cpp_value = array_or_sequence_literal_cpp_value
 
 
+_IDL_TYPE_TO_NATIVE_VALUE_TRAITS_TAG_MAP = {
+    'DOMString': 'IDLString',
+    'any': 'ScriptValue',
+    'boolean': 'IDLBoolean',
+    'long': 'IDLLong',
+    'sequence<DOMString>': 'IDLSequence<IDLString>',
+    'unsigned short': 'IDLUnsignedShort',
+    'void': None,
+}
+
+
+def idl_type_to_native_value_traits_tag(idl_type):
+    idl_type_str = str(idl_type)
+    if idl_type_str in _IDL_TYPE_TO_NATIVE_VALUE_TRAITS_TAG_MAP:
+        return _IDL_TYPE_TO_NATIVE_VALUE_TRAITS_TAG_MAP[idl_type_str]
+    else:
+        raise Exception("Type `%s' is not supported." % idl_type_str)
+
+
 ################################################################################
 # Utility properties for nullable types
 ################################################################################
diff --git a/third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl b/third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
index 09622ad..a32046bf 100644
--- a/third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
@@ -1,4 +1,4 @@
-{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable, v8_value_to_local_cpp_value %}
+{% from 'callback_invoke.cc.tmpl' import callback_invoke %}
 {% filter format_blink_cpp_source_code %}
 
 {% include 'copyright_block.txt' %}
@@ -16,237 +16,18 @@
 }
 
 v8::Maybe<{{return_cpp_type}}> {{cpp_class}}::Invoke({{argument_declarations | join(', ')}}) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
-  {# TODO(yukishiino): This implementation does not support a return type of
-     promise type, in which case this function needs to convert any exception
-     into a rejected promise. See also step 14.4. to 14.6. #}
-
-  if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
-                                  IncumbentScriptState())) {
-    // Wrapper-tracing for the callback function makes the function object and
-    // its creation context alive. Thus it's safe to use the creation context
-    // of the callback function here.
-    v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
-    V8ThrowException::ThrowError(
-        GetIsolate(),
-        ExceptionMessages::FailedToExecute(
-            "invoke",
-            "{{callback_function_name}}",
-            "The provided callback is no longer runnable."));
-    return v8::Nothing<{{return_cpp_type}}>();
-  }
-
-  // step 4. If ! IsCallable(F) is false:
-  //
-  // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
-  // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
-
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
-
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  {% for argument in arguments if argument.enum_values %}
-  // Enum values provided by Blink must be valid, otherwise typo.
-#if DCHECK_IS_ON()
-  {
-    {% set valid_enum_variables = 'valid_' + argument.name + '_values' %}
-    {{declare_enum_validation_variable(argument.enum_values, valid_enum_variables) | trim | indent(4)}}
-    ExceptionState exception_state(GetIsolate(),
-                                   ExceptionState::kExecutionContext,
-                                   "{{callback_function_name}}",
-                                   "invoke");
-    if (!IsValidEnum({{argument.name}}, {{valid_enum_variables}}, base::size({{valid_enum_variables}}), "{{argument.enum_type}}", exception_state)) {
-      NOTREACHED();
-      return v8::Nothing<{{return_cpp_type}}>();
-    }
-  }
-#endif
-
-  {% endfor %}
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
-  //   arguments list. If this throws an exception, set completion to the
-  //   completion value representing the thrown exception and jump to the step
-  //   labeled return.
-  {% if arguments %}
-  v8::Local<v8::Object> argument_creation_context =
-      CallbackRelevantScriptState()->GetContext()->Global();
-  ALLOW_UNUSED_LOCAL(argument_creation_context);
-  {% set has_variadic_argument = arguments[-1].is_variadic %}
-  {% set non_variadic_arguments = arguments | rejectattr('is_variadic') | list %}
-  {% set variadic_argument = arguments[-1] if has_variadic_argument else None %}
-  {% set arguments_length = '%d + %s.size()' % (non_variadic_arguments|length, variadic_argument.name) if has_variadic_argument else non_variadic_arguments|length %}
-  {% for argument in non_variadic_arguments %}
-  v8::Local<v8::Value> {{argument.v8_name}} = {{argument.cpp_value_to_v8_value}};
-  {% endfor %}
-  {% if has_variadic_argument %}
-  const int argc = {{arguments_length}};
-  v8::Local<v8::Value> argv[argc];
-  {% for argument in non_variadic_arguments %}
-  argv[{{loop.index0}}] = {{argument.v8_name}};
-  {% endfor %}
-  for (wtf_size_t i = 0; i < {{variadic_argument.name}}.size(); ++i) {
-    argv[{{non_variadic_arguments|length}} + i] = ToV8({{variadic_argument.name}}[i], argument_creation_context, GetIsolate());
-  }
-  {% else %}{# if has_variadic_argument #}
-  constexpr int argc = {{arguments_length}};
-  v8::Local<v8::Value> argv[] = { {{non_variadic_arguments | join(', ', 'v8_name')}} };
-  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
-  {% endif %}
-  {% else %}{# if arguments #}
-  const int argc = 0;
-  {# Zero-length arrays are ill-formed in C++. #}
-  v8::Local<v8::Value> *argv = nullptr;
-  {% endif %}
-
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
-  v8::Local<v8::Value> call_result;
-  if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
-          ExecutionContext::From(CallbackRelevantScriptState()),
-          this_arg,
-          argc,
-          argv,
-          GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
-    return v8::Nothing<{{return_cpp_type}}>();
-  }
-
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
-  //   an IDL value of the same type as the operation's return type.
-  {% if idl_type == 'void' %}
-  return v8::JustVoid();
-  {% else %}
-  {
-    ExceptionState exceptionState(GetIsolate(),
-                                  ExceptionState::kExecutionContext,
-                                  "{{callback_function_name}}",
-                                  "invoke");
-    {{v8_value_to_local_cpp_value(return_value_conversion) | trim | indent(4)}}
-    return v8::Just<{{return_cpp_type}}>(native_result);
-  }
-  {% endif %}
+{{callback_invoke(
+    'callback function', 'invoke',
+    return_cpp_type, native_value_traits_tag, arguments,
+    callback_function_name, 'invoke')}}
 }
 
 {% if idl_type == 'any' %}
 v8::Maybe<{{return_cpp_type}}> {{cpp_class}}::Construct({{argument_declarations[1:] | join(', ')}}) {
-  // This function implements "construct" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#construct-a-callback-function
-
-  if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
-                                  IncumbentScriptState())) {
-    // Wrapper-tracing for the callback function makes the function object and
-    // its creation context alive. Thus it's safe to use the creation context
-    // of the callback function here.
-    v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
-    V8ThrowException::ThrowError(
-        GetIsolate(),
-        ExceptionMessages::FailedToExecute(
-            "construct",
-            "{{callback_function_name}}",
-            "The provided callback is no longer runnable."));
-    return v8::Nothing<{{return_cpp_type}}>();
-  }
-
-  // step 7. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
-
-  // step 3. If ! IsConstructor(F) is false, throw a TypeError exception.
-  //
-  // Note that step 7. and 8. are side effect free (except for a very rare
-  // exception due to no incumbent realm), so it's okay to put step 3. after
-  // step 7. and 8.
-  if (!CallbackFunction()->IsConstructor()) {
-    V8ThrowException::ThrowTypeError(
-        GetIsolate(),
-        ExceptionMessages::FailedToExecute(
-            "construct",
-            "{{callback_function_name}}",
-            "The provided callback is not a constructor."));
-    return v8::Nothing<{{return_cpp_type}}>();
-  }
-
-  // step 9. Let esArgs be the result of converting args to an ECMAScript
-  //   arguments list. If this throws an exception, set completion to the
-  //   completion value representing the thrown exception and jump to the step
-  //   labeled return.
-  {% if arguments %}
-  v8::Local<v8::Object> argument_creation_context =
-      CallbackRelevantScriptState()->GetContext()->Global();
-  ALLOW_UNUSED_LOCAL(argument_creation_context);
-  {% set has_variadic_argument = arguments[-1].is_variadic %}
-  {% set non_variadic_arguments = arguments | rejectattr('is_variadic') | list %}
-  {% set variadic_argument = arguments[-1] if has_variadic_argument else None %}
-  {% set arguments_length = '%d + %s.size()' % (non_variadic_arguments|length, variadic_argument.name) if has_variadic_argument else non_variadic_arguments|length %}
-  {% for argument in non_variadic_arguments %}
-  v8::Local<v8::Value> {{argument.v8_name}} = {{argument.cpp_value_to_v8_value}};
-  {% endfor %}
-  {% if has_variadic_argument %}
-  const int argc = {{arguments_length}};
-  v8::Local<v8::Value> argv[argc];
-  {% for argument in non_variadic_arguments %}
-  argv[{{loop.index0}}] = {{argument.v8_name}};
-  {% endfor %}
-  for (wtf_size_t i = 0; i < {{variadic_argument.name}}.size(); ++i) {
-    argv[{{non_variadic_arguments|length}} + i] = ToV8({{variadic_argument.name}}[i], argument_creation_context, GetIsolate());
-  }
-  {% else %}{# if has_variadic_argument #}
-  constexpr int argc = {{arguments_length}};
-  v8::Local<v8::Value> argv[] = { {{non_variadic_arguments | join(', ', 'v8_name')}} };
-  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
-  {% endif %}
-  {% else %}{# if arguments #}
-  const int argc = 0;
-  {# Zero-length arrays are ill-formed in C++. #}
-  v8::Local<v8::Value> *argv = nullptr;
-  {% endif %}
-
-  // step 10. Let callResult be Construct(F, esArgs).
-  v8::Local<v8::Value> call_result;
-  if (!V8ScriptRunner::CallAsConstructor(
-          GetIsolate(),
-          CallbackFunction(),
-          ExecutionContext::From(CallbackRelevantScriptState()),
-          argc,
-          argv).ToLocal(&call_result)) {
-    // step 11. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
-    return v8::Nothing<{{return_cpp_type}}>();
-  }
-
-  // step 12. Set completion to the result of converting callResult.[[Value]] to
-  //   an IDL value of the same type as the operation's return type.
-  ExceptionState exceptionState(GetIsolate(),
-                                ExceptionState::kExecutionContext,
-                                "{{callback_function_name}}",
-                                "construct");
-  {{v8_value_to_local_cpp_value(return_value_conversion) | trim | indent(2)}}
-  return v8::Just<{{return_cpp_type}}>(native_result);
+{{callback_invoke(
+    'callback function', 'construct',
+    return_cpp_type, native_value_traits_tag, arguments,
+    callback_function_name, 'construct')}}
 }
 {% endif %}
 
diff --git a/third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl b/third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl
index bd50715..012652e7 100644
--- a/third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl
@@ -1,3 +1,4 @@
+{% from 'callback_invoke.cc.tmpl' import callback_invoke %}
 {% filter format_blink_cpp_source_code %}
 
 {% include 'copyright_block.txt' %}
@@ -70,145 +71,10 @@
 {% for method in methods %}
 
 v8::Maybe<{{method.cpp_type}}> {{v8_class}}::{{method.name}}({{method.argument_declarations | join(', ')}}) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
-  if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
-                                  IncumbentScriptState())) {
-    // Wrapper-tracing for the callback function makes the function object and
-    // its creation context alive. Thus it's safe to use the creation context
-    // of the callback function here.
-    v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
-    V8ThrowException::ThrowError(
-        GetIsolate(),
-        ExceptionMessages::FailedToExecute(
-            "{{method.name}}",
-            "{{cpp_class}}",
-            "The provided callback is no longer runnable."));
-    return v8::Nothing<{{method.cpp_type}}>();
-  }
-
-  // step 7. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
-
-  v8::Local<v8::Function> function;
-  if (IsCallbackObjectCallable()) {
-    // step 9.1. If value's interface is a single operation callback interface
-    //   and !IsCallable(O) is true, then set X to O.
-    function = CallbackObject().As<v8::Function>();
-  } else {
-    // step 9.2.1. Let getResult be Get(O, opName).
-    // step 9.2.2. If getResult is an abrupt completion, set completion to
-    //   getResult and jump to the step labeled return.
-    v8::Local<v8::Value> value;
-    if (!CallbackObject()->Get(CallbackRelevantScriptState()->GetContext(),
-                               V8String(GetIsolate(), "{{method.name}}"))
-        .ToLocal(&value)) {
-      return v8::Nothing<{{method.cpp_type}}>();
-    }
-    // step 10. If !IsCallable(X) is false, then set completion to a new
-    //   Completion{[[Type]]: throw, [[Value]]: a newly created TypeError
-    //   object, [[Target]]: empty}, and jump to the step labeled return.
-    if (!value->IsFunction()) {
-      V8ThrowException::ThrowTypeError(
-          GetIsolate(),
-          ExceptionMessages::FailedToExecute(
-              "{{method.name}}",
-              "{{cpp_class}}",
-              "The provided callback is not callable."));
-      return v8::Nothing<{{method.cpp_type}}>();
-    }
-    function = value.As<v8::Function>();
-  }
-
-  v8::Local<v8::Value> this_arg;
-  if (!IsCallbackObjectCallable()) {
-    // step 11. If value's interface is not a single operation callback
-    //   interface, or if !IsCallable(O) is false, set thisArg to O (overriding
-    //   the provided value).
-    this_arg = CallbackObject();
-  } else if (!callback_this_value) {
-    // step 2. If thisArg was not given, let thisArg be undefined.
-    this_arg = v8::Undefined(GetIsolate());
-  } else {
-    this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
-  }
-
-  {% for argument in arguments if argument.enum_values %}
-  // Enum values provided by Blink must be valid, otherwise typo.
-#if DCHECK_IS_ON()
-  {
-    {% set valid_enum_variables = 'valid_' + argument.name + '_values' %}
-    {{declare_enum_validation_variable(argument.enum_values, valid_enum_variables) | trim | indent(4)}}
-    ExceptionState exception_state(GetIsolate(),
-                                   ExceptionState::kExecutionContext,
-                                   "{{cpp_class}}",
-                                   "{{method.name}}");
-    if (!IsValidEnum({{argument.name}}, {{valid_enum_variables}}, base::size({{valid_enum_variables}}), "{{argument.enum_type}}", exception_state)) { //
-      NOTREACHED();
-      return v8::Nothing<{{method.cpp_type}}>();
-    }
-  }
-#endif
-
-  {% endfor %}
-
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
-  //   arguments list. If this throws an exception, set completion to the
-  //   completion value representing the thrown exception and jump to the step
-  //   labeled return.
-  {% if method.arguments %}
-  v8::Local<v8::Object> argument_creation_context =
-      CallbackRelevantScriptState()->GetContext()->Global();
-  ALLOW_UNUSED_LOCAL(argument_creation_context);
-  {% for argument in method.arguments %}
-  v8::Local<v8::Value> {{argument.handle}} = {{argument.cpp_value_to_v8_value}};
-  {% endfor %}
-  v8::Local<v8::Value> argv[] = { {{method.arguments | join(', ', 'handle')}} };
-  {% else %}
-  {# Zero-length arrays are ill-formed in C++. #}
-  v8::Local<v8::Value> *argv = nullptr;
-  {% endif %}
-
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
-  v8::Local<v8::Value> call_result;
-  if (!V8ScriptRunner::CallFunction(
-          function,
-          ExecutionContext::From(CallbackRelevantScriptState()),
-          this_arg,
-          {{method.arguments | length}},
-          argv,
-          GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
-    return v8::Nothing<{{method.cpp_type}}>();
-  }
-
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
-  //   an IDL value of the same type as the operation's return type.
-  {% if method.idl_type == 'void' %}
-  return v8::JustVoid();
-  {% else %}
-  {
-    ExceptionState exception_state(GetIsolate(),
-                                   ExceptionState::kExecutionContext,
-                                   "{{cpp_class}}",
-                                   "{{method.name}}");
-    auto native_result =
-        NativeValueTraits<{{method.native_value_traits_tag}}>::NativeValue(
-            GetIsolate(), call_result, exception_state);
-    if (exception_state.HadException())
-      return v8::Nothing<{{method.cpp_type}}>();
-    else
-      return v8::Just<{{method.cpp_type}}>(native_result);
-  }
-  {% endif %}
+{{callback_invoke(
+    'callback interface', None,
+    method.cpp_type, method.native_value_traits_tag, method.arguments,
+    interface_name, method.name)}}
 }
 
 {% endfor %}
diff --git a/third_party/blink/renderer/bindings/templates/callback_invoke.cc.tmpl b/third_party/blink/renderer/bindings/templates/callback_invoke.cc.tmpl
new file mode 100644
index 0000000..6127fe0
--- /dev/null
+++ b/third_party/blink/renderer/bindings/templates/callback_invoke.cc.tmpl
@@ -0,0 +1,229 @@
+{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable %}
+
+{# Implements callback interface\'s "call a user object's operation", or
+   callback function\'s "invoke" and/or "construct".
+   https://heycam.github.io/webidl/#call-a-user-objects-operation
+   https://heycam.github.io/webidl/#es-invoking-callback-functions
+
+   Args:
+      interface_or_function = 'callback interface' or 'callback function'
+      invoke_or_construct = 'invoke', 'construct', or None
+      return_cpp_type = Blink (C++) return type
+      return_native_value_traits_tag = tag of NativeValueTraits for return type
+      arguments = dict of arguments\' info
+      interface_name = interface name used for exception
+      operation_name = interface name used for exception and property lookup
+#}
+{% macro callback_invoke(
+    interface_or_function, invoke_or_construct,
+    return_cpp_type, return_native_value_traits_tag, arguments,
+    interface_name, operation_name) %}
+  if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
+                                  IncumbentScriptState())) {
+    // Wrapper-tracing for the callback function makes the function object and
+    // its creation context alive. Thus it's safe to use the creation context
+    // of the callback function here.
+    v8::HandleScope handle_scope(GetIsolate());
+    {% if interface_or_function == 'callback function' %}
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    {% else %}
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    {% endif %}
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
+    V8ThrowException::ThrowError(
+        GetIsolate(),
+        ExceptionMessages::FailedToExecute(
+            "{{operation_name}}",
+            "{{interface_name}}",
+            "The provided callback is no longer runnable."));
+    return v8::Nothing<{{return_cpp_type}}>();
+  }
+
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  {% if invoke_or_construct == 'construct' %}
+  // step 3. If ! IsConstructor(F) is false, throw a TypeError exception.
+  //
+  // Note that step 7. and 8. are side effect free (except for a very rare
+  // exception due to no incumbent realm), so it's okay to put step 3. after
+  // step 7. and 8.
+  if (!CallbackFunction()->IsConstructor()) {
+    V8ThrowException::ThrowTypeError(
+        GetIsolate(),
+        ExceptionMessages::FailedToExecute(
+            "{{operation_name}}",
+            "{{interface_name}}",
+            "The provided callback is not a constructor."));
+    return v8::Nothing<{{return_cpp_type}}>();
+  }
+  {% endif %}
+
+  v8::Local<v8::Function> function;
+  {# Fill |function|. #}
+  {% if interface_or_function == 'callback function' %}
+  // callback function\'s invoke:
+  // step 4. If ! IsCallable(F) is false:
+  //
+  // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
+  // case.
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
+  {% else %}
+  if (IsCallbackObjectCallable()) {
+    // step 9.1. If value's interface is a single operation callback interface
+    //   and !IsCallable(O) is true, then set X to O.
+    function = CallbackObject().As<v8::Function>();
+  } else {
+    // step 9.2.1. Let getResult be Get(O, opName).
+    // step 9.2.2. If getResult is an abrupt completion, set completion to
+    //   getResult and jump to the step labeled return.
+    v8::Local<v8::Value> value;
+    if (!CallbackObject()->Get(CallbackRelevantScriptState()->GetContext(),
+                               V8String(GetIsolate(), "{{operation_name}}"))
+        .ToLocal(&value)) {
+      return v8::Nothing<{{return_cpp_type}}>();
+    }
+    // step 10. If !IsCallable(X) is false, then set completion to a new
+    //   Completion{[[Type]]: throw, [[Value]]: a newly created TypeError
+    //   object, [[Target]]: empty}, and jump to the step labeled return.
+    if (!value->IsFunction()) {
+      V8ThrowException::ThrowTypeError(
+          GetIsolate(),
+          ExceptionMessages::FailedToExecute(
+              "{{operation_name}}",
+              "{{interface_name}}",
+              "The provided callback is not callable."));
+      return v8::Nothing<{{return_cpp_type}}>();
+    }
+    function = value.As<v8::Function>();
+  }
+  {% endif %}
+
+  {% if invoke_or_construct != 'construct' %}
+  v8::Local<v8::Value> this_arg;
+  {% endif %}
+  {# Fill |this_arg|. #}
+  {% if invoke_or_construct == 'invoke' %}
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
+  {% elif interface_or_function == 'callback interface' %}
+  if (!IsCallbackObjectCallable()) {
+    // step 11. If value's interface is not a single operation callback
+    //   interface, or if !IsCallable(O) is false, set thisArg to O (overriding
+    //   the provided value).
+    this_arg = CallbackObject();
+  } else if (!callback_this_value) {
+    // step 2. If thisArg was not given, let thisArg be undefined.
+    this_arg = v8::Undefined(GetIsolate());
+  } else {
+    this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
+  }
+  {% endif %}
+
+  {% for argument in arguments if argument.enum_values %}
+  // Enum values provided by Blink must be valid, otherwise typo.
+#if DCHECK_IS_ON()
+  {
+    {% set valid_enum_variables = 'valid_' + argument.name + '_values' %}
+    {{declare_enum_validation_variable(argument.enum_values, valid_enum_variables) | trim | indent(4)}}
+    ExceptionState exception_state(GetIsolate(),
+                                   ExceptionState::kExecutionContext,
+                                   "{{interface_name}}",
+                                   "{{operation_name}}");
+    if (!IsValidEnum({{argument.name}}, {{valid_enum_variables}}, base::size({{valid_enum_variables}}), "{{argument.enum_type}}", exception_state)) { //
+      NOTREACHED();
+      return v8::Nothing<{{return_cpp_type}}>();
+    }
+  }
+#endif
+  {% endfor %}
+
+  // step: Let esArgs be the result of converting args to an ECMAScript
+  //   arguments list. If this throws an exception, set completion to the
+  //   completion value representing the thrown exception and jump to the step
+  //   labeled return.
+  {% if arguments %}
+  v8::Local<v8::Object> argument_creation_context =
+      CallbackRelevantScriptState()->GetContext()->Global();
+  ALLOW_UNUSED_LOCAL(argument_creation_context);
+  {% set has_variadic_argument = arguments[-1].is_variadic %}
+  {% set non_variadic_arguments = arguments | rejectattr('is_variadic') | list %}
+  {% set variadic_argument = arguments[-1] if has_variadic_argument else None %}
+  {% set arguments_length = '%d + %s.size()' % (non_variadic_arguments|length, variadic_argument.name) if has_variadic_argument else non_variadic_arguments|length %}
+  {% for argument in non_variadic_arguments %}
+  v8::Local<v8::Value> {{argument.v8_name}} = {{argument.cpp_value_to_v8_value}};
+  {% endfor %}
+  {% if has_variadic_argument %}
+  const int argc = {{arguments_length}};
+  v8::Local<v8::Value> argv[argc];
+  {% for argument in non_variadic_arguments %}
+  argv[{{loop.index0}}] = {{argument.v8_name}};
+  {% endfor %}
+  for (wtf_size_t i = 0; i < {{variadic_argument.name}}.size(); ++i) {
+    argv[{{non_variadic_arguments|length}} + i] = ToV8({{variadic_argument.name}}[i], argument_creation_context, GetIsolate());
+  }
+  {% else %}{# if has_variadic_argument #}
+  constexpr int argc = {{arguments_length}};
+  v8::Local<v8::Value> argv[] = { {{non_variadic_arguments | join(', ', 'v8_name')}} };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
+  {% endif %}
+  {% else %}{# if arguments #}
+  const int argc = 0;
+  {# Zero-length arrays are ill-formed in C++. #}
+  v8::Local<v8::Value> *argv = nullptr;
+  {% endif %}
+
+  v8::Local<v8::Value> call_result;
+  {# Fill |call_result|. #}
+  {% if invoke_or_construct == 'construct' %}
+  if (!V8ScriptRunner::CallAsConstructor(
+          GetIsolate(),
+          function,
+          ExecutionContext::From(CallbackRelevantScriptState()),
+          argc,
+          argv).ToLocal(&call_result)) {
+    // step 11. If callResult is an abrupt completion, set completion to
+    //   callResult and jump to the step labeled return.
+    return v8::Nothing<{{return_cpp_type}}>();
+  }
+  {% else %}
+  // step: Let callResult be Call(X, thisArg, esArgs).
+  if (!V8ScriptRunner::CallFunction(
+          function,
+          ExecutionContext::From(CallbackRelevantScriptState()),
+          this_arg,
+          argc,
+          argv,
+          GetIsolate()).ToLocal(&call_result)) {
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
+    return v8::Nothing<{{return_cpp_type}}>();
+  }
+  {% endif %}
+
+
+  // step: Set completion to the result of converting callResult.[[Value]] to
+  //   an IDL value of the same type as the operation's return type.
+  {% if return_cpp_type == 'void' %}
+  return v8::JustVoid();
+  {% else %}
+  {
+    ExceptionState exception_state(GetIsolate(),
+                                   ExceptionState::kExecutionContext,
+                                   "{{interface_name}}",
+                                   "{{operation_name}}");
+    auto native_result =
+        NativeValueTraits<{{return_native_value_traits_tag}}>::NativeValue(
+            GetIsolate(), call_result, exception_state);
+    if (exception_state.HadException())
+      return v8::Nothing<{{return_cpp_type}}>();
+    else
+      return v8::Just<{{return_cpp_type}}>(native_result);
+  }
+  {% endif %}
+{% endmacro %}
diff --git a/third_party/blink/renderer/bindings/templates/templates.gni b/third_party/blink/renderer/bindings/templates/templates.gni
index 8cd38ce9..b6c270e 100644
--- a/third_party/blink/renderer/bindings/templates/templates.gni
+++ b/third_party/blink/renderer/bindings/templates/templates.gni
@@ -10,11 +10,12 @@
                     "callback_function.h.tmpl",
                     "callback_interface.cpp.tmpl",
                     "callback_interface.h.tmpl",
+                    "callback_invoke.cc.tmpl",
                     "constants.cpp.tmpl",
                     "copyright_block.txt",
                     "dictionary_impl.cpp.tmpl",
-                    "dictionary_impl_common.cpp.tmpl",
                     "dictionary_impl.h.tmpl",
+                    "dictionary_impl_common.cpp.tmpl",
                     "dictionary_v8.cpp.tmpl",
                     "dictionary_v8.h.tmpl",
                     "external_reference_table.cpp.tmpl",
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_optional_any_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_optional_any_arg.cc
index f917ea3..ae589a2 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_optional_any_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_optional_any_arg.cc
@@ -28,18 +28,15 @@
 }
 
 v8::Maybe<ScriptValue> V8AnyCallbackFunctionOptionalAnyArg::Invoke(ScriptWrappable* callback_this_value, ScriptValue optionalAnyArg) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -49,28 +46,26 @@
     return v8::Nothing<ScriptValue>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -82,45 +77,47 @@
   v8::Local<v8::Value> argv[] = { v8_optionalAnyArg };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<ScriptValue>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   {
-    ExceptionState exceptionState(GetIsolate(),
-                                  ExceptionState::kExecutionContext,
-                                  "AnyCallbackFunctionOptionalAnyArg",
-                                  "invoke");
-    ScriptValue native_result = ScriptValue(ScriptState::Current(GetIsolate()), call_result);
-    return v8::Just<ScriptValue>(native_result);
+    ExceptionState exception_state(GetIsolate(),
+                                   ExceptionState::kExecutionContext,
+                                   "AnyCallbackFunctionOptionalAnyArg",
+                                   "invoke");
+    auto native_result =
+        NativeValueTraits<ScriptValue>::NativeValue(
+            GetIsolate(), call_result, exception_state);
+    if (exception_state.HadException())
+      return v8::Nothing<ScriptValue>();
+    else
+      return v8::Just<ScriptValue>(native_result);
   }
 }
 
 v8::Maybe<ScriptValue> V8AnyCallbackFunctionOptionalAnyArg::Construct(ScriptValue optionalAnyArg) {
-  // This function implements "construct" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#construct-a-callback-function
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -130,10 +127,10 @@
     return v8::Nothing<ScriptValue>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -152,7 +149,16 @@
     return v8::Nothing<ScriptValue>();
   }
 
-  // step 9. Let esArgs be the result of converting args to an ECMAScript
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
+  // step 4. If ! IsCallable(F) is false:
+  //
+  // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
+  // case.
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
+
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -164,11 +170,10 @@
   v8::Local<v8::Value> argv[] = { v8_optionalAnyArg };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 10. Let callResult be Construct(F, esArgs).
   v8::Local<v8::Value> call_result;
   if (!V8ScriptRunner::CallAsConstructor(
           GetIsolate(),
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           argc,
           argv).ToLocal(&call_result)) {
@@ -177,14 +182,21 @@
     return v8::Nothing<ScriptValue>();
   }
 
-  // step 12. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
-  ExceptionState exceptionState(GetIsolate(),
-                                ExceptionState::kExecutionContext,
-                                "AnyCallbackFunctionOptionalAnyArg",
-                                "construct");
-  ScriptValue native_result = ScriptValue(ScriptState::Current(GetIsolate()), call_result);
-  return v8::Just<ScriptValue>(native_result);
+  {
+    ExceptionState exception_state(GetIsolate(),
+                                   ExceptionState::kExecutionContext,
+                                   "AnyCallbackFunctionOptionalAnyArg",
+                                   "construct");
+    auto native_result =
+        NativeValueTraits<ScriptValue>::NativeValue(
+            GetIsolate(), call_result, exception_state);
+    if (exception_state.HadException())
+      return v8::Nothing<ScriptValue>();
+    else
+      return v8::Just<ScriptValue>(native_result);
+  }
 }
 
 v8::Maybe<ScriptValue> V8PersistentCallbackFunction<V8AnyCallbackFunctionOptionalAnyArg>::Invoke(ScriptWrappable* callback_this_value, ScriptValue optionalAnyArg) {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_variadic_any_args.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_variadic_any_args.cc
index e17091c..9586117 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_variadic_any_args.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_variadic_any_args.cc
@@ -28,18 +28,15 @@
 }
 
 v8::Maybe<ScriptValue> V8AnyCallbackFunctionVariadicAnyArgs::Invoke(ScriptWrappable* callback_this_value, const Vector<ScriptValue>& arguments) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -49,28 +46,26 @@
     return v8::Nothing<ScriptValue>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -83,45 +78,47 @@
     argv[0 + i] = ToV8(arguments[i], argument_creation_context, GetIsolate());
   }
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<ScriptValue>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   {
-    ExceptionState exceptionState(GetIsolate(),
-                                  ExceptionState::kExecutionContext,
-                                  "AnyCallbackFunctionVariadicAnyArgs",
-                                  "invoke");
-    ScriptValue native_result = ScriptValue(ScriptState::Current(GetIsolate()), call_result);
-    return v8::Just<ScriptValue>(native_result);
+    ExceptionState exception_state(GetIsolate(),
+                                   ExceptionState::kExecutionContext,
+                                   "AnyCallbackFunctionVariadicAnyArgs",
+                                   "invoke");
+    auto native_result =
+        NativeValueTraits<ScriptValue>::NativeValue(
+            GetIsolate(), call_result, exception_state);
+    if (exception_state.HadException())
+      return v8::Nothing<ScriptValue>();
+    else
+      return v8::Just<ScriptValue>(native_result);
   }
 }
 
 v8::Maybe<ScriptValue> V8AnyCallbackFunctionVariadicAnyArgs::Construct(const Vector<ScriptValue>& arguments) {
-  // This function implements "construct" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#construct-a-callback-function
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -131,10 +128,10 @@
     return v8::Nothing<ScriptValue>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -153,7 +150,16 @@
     return v8::Nothing<ScriptValue>();
   }
 
-  // step 9. Let esArgs be the result of converting args to an ECMAScript
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
+  // step 4. If ! IsCallable(F) is false:
+  //
+  // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
+  // case.
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
+
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -166,11 +172,10 @@
     argv[0 + i] = ToV8(arguments[i], argument_creation_context, GetIsolate());
   }
 
-  // step 10. Let callResult be Construct(F, esArgs).
   v8::Local<v8::Value> call_result;
   if (!V8ScriptRunner::CallAsConstructor(
           GetIsolate(),
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           argc,
           argv).ToLocal(&call_result)) {
@@ -179,14 +184,21 @@
     return v8::Nothing<ScriptValue>();
   }
 
-  // step 12. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
-  ExceptionState exceptionState(GetIsolate(),
-                                ExceptionState::kExecutionContext,
-                                "AnyCallbackFunctionVariadicAnyArgs",
-                                "construct");
-  ScriptValue native_result = ScriptValue(ScriptState::Current(GetIsolate()), call_result);
-  return v8::Just<ScriptValue>(native_result);
+  {
+    ExceptionState exception_state(GetIsolate(),
+                                   ExceptionState::kExecutionContext,
+                                   "AnyCallbackFunctionVariadicAnyArgs",
+                                   "construct");
+    auto native_result =
+        NativeValueTraits<ScriptValue>::NativeValue(
+            GetIsolate(), call_result, exception_state);
+    if (exception_state.HadException())
+      return v8::Nothing<ScriptValue>();
+    else
+      return v8::Just<ScriptValue>(native_result);
+  }
 }
 
 v8::Maybe<ScriptValue> V8PersistentCallbackFunction<V8AnyCallbackFunctionVariadicAnyArgs>::Invoke(ScriptWrappable* callback_this_value, const Vector<ScriptValue>& arguments) {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_long_callback_function.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_long_callback_function.cc
index 5b74926..7a6e2e1 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_long_callback_function.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_long_callback_function.cc
@@ -28,18 +28,15 @@
 }
 
 v8::Maybe<int32_t> V8LongCallbackFunction::Invoke(ScriptWrappable* callback_this_value, int32_t num1, int32_t num2) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -49,28 +46,26 @@
     return v8::Nothing<int32_t>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -83,31 +78,34 @@
   v8::Local<v8::Value> argv[] = { v8_num1, v8_num2 };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<int32_t>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   {
-    ExceptionState exceptionState(GetIsolate(),
-                                  ExceptionState::kExecutionContext,
-                                  "LongCallbackFunction",
-                                  "invoke");
-    int32_t native_result = NativeValueTraits<IDLLong>::NativeValue(GetIsolate(), call_result, exceptionState, kNormalConversion);
-    if (exceptionState.HadException())
+    ExceptionState exception_state(GetIsolate(),
+                                   ExceptionState::kExecutionContext,
+                                   "LongCallbackFunction",
+                                   "invoke");
+    auto native_result =
+        NativeValueTraits<IDLLong>::NativeValue(
+            GetIsolate(), call_result, exception_state);
+    if (exception_state.HadException())
       return v8::Nothing<int32_t>();
-    return v8::Just<int32_t>(native_result);
+    else
+      return v8::Just<int32_t>(native_result);
   }
 }
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_string_sequence_callback_function_long_sequence_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_string_sequence_callback_function_long_sequence_arg.cc
index 9bc2bf2..ca17584 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_string_sequence_callback_function_long_sequence_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_string_sequence_callback_function_long_sequence_arg.cc
@@ -28,18 +28,15 @@
 }
 
 v8::Maybe<Vector<String>> V8StringSequenceCallbackFunctionLongSequenceArg::Invoke(ScriptWrappable* callback_this_value, const Vector<int32_t>& arg) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -49,28 +46,26 @@
     return v8::Nothing<Vector<String>>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -82,31 +77,34 @@
   v8::Local<v8::Value> argv[] = { v8_arg };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<Vector<String>>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   {
-    ExceptionState exceptionState(GetIsolate(),
-                                  ExceptionState::kExecutionContext,
-                                  "StringSequenceCallbackFunctionLongSequenceArg",
-                                  "invoke");
-    Vector<String> native_result = NativeValueTraits<IDLSequence<IDLString>>::NativeValue(GetIsolate(), call_result, exceptionState);
-    if (exceptionState.HadException())
+    ExceptionState exception_state(GetIsolate(),
+                                   ExceptionState::kExecutionContext,
+                                   "StringSequenceCallbackFunctionLongSequenceArg",
+                                   "invoke");
+    auto native_result =
+        NativeValueTraits<IDLSequence<IDLString>>::NativeValue(
+            GetIsolate(), call_result, exception_state);
+    if (exception_state.HadException())
       return v8::Nothing<Vector<String>>();
-    return v8::Just<Vector<String>>(native_result);
+    else
+      return v8::Just<Vector<String>>(native_result);
   }
 }
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
index 9d839caa..16854a6 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
@@ -26,17 +26,15 @@
 }
 
 v8::Maybe<void> V8TestCallbackInterface::voidMethod(ScriptWrappable* callback_this_value) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -46,10 +44,10 @@
     return v8::Nothing<void>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -96,43 +94,42 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
+  const int argc = 0;
   v8::Local<v8::Value> *argv = nullptr;
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          0,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
 
 v8::Maybe<bool> V8TestCallbackInterface::booleanMethod(ScriptWrappable* callback_this_value) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -142,10 +139,10 @@
     return v8::Nothing<bool>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -192,27 +189,28 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
+  const int argc = 0;
   v8::Local<v8::Value> *argv = nullptr;
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          0,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<bool>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   {
     ExceptionState exception_state(GetIsolate(),
@@ -230,17 +228,15 @@
 }
 
 v8::Maybe<void> V8TestCallbackInterface::voidMethodBooleanArg(ScriptWrappable* callback_this_value, bool boolArg) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -250,10 +246,10 @@
     return v8::Nothing<void>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -300,47 +296,47 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   v8::Local<v8::Object> argument_creation_context =
       CallbackRelevantScriptState()->GetContext()->Global();
   ALLOW_UNUSED_LOCAL(argument_creation_context);
-  v8::Local<v8::Value> boolArgHandle = v8::Boolean::New(GetIsolate(), boolArg);
-  v8::Local<v8::Value> argv[] = { boolArgHandle };
+  v8::Local<v8::Value> v8_boolArg = v8::Boolean::New(GetIsolate(), boolArg);
+  constexpr int argc = 1;
+  v8::Local<v8::Value> argv[] = { v8_boolArg };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          1,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
 
 v8::Maybe<void> V8TestCallbackInterface::voidMethodSequenceArg(ScriptWrappable* callback_this_value, const HeapVector<Member<TestInterfaceEmpty>>& sequenceArg) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -350,10 +346,10 @@
     return v8::Nothing<void>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -400,47 +396,47 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   v8::Local<v8::Object> argument_creation_context =
       CallbackRelevantScriptState()->GetContext()->Global();
   ALLOW_UNUSED_LOCAL(argument_creation_context);
-  v8::Local<v8::Value> sequenceArgHandle = ToV8(sequenceArg, argument_creation_context, GetIsolate());
-  v8::Local<v8::Value> argv[] = { sequenceArgHandle };
+  v8::Local<v8::Value> v8_sequenceArg = ToV8(sequenceArg, argument_creation_context, GetIsolate());
+  constexpr int argc = 1;
+  v8::Local<v8::Value> argv[] = { v8_sequenceArg };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          1,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
 
 v8::Maybe<void> V8TestCallbackInterface::voidMethodFloatArg(ScriptWrappable* callback_this_value, float floatArg) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -450,10 +446,10 @@
     return v8::Nothing<void>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -500,47 +496,47 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   v8::Local<v8::Object> argument_creation_context =
       CallbackRelevantScriptState()->GetContext()->Global();
   ALLOW_UNUSED_LOCAL(argument_creation_context);
-  v8::Local<v8::Value> floatArgHandle = v8::Number::New(GetIsolate(), floatArg);
-  v8::Local<v8::Value> argv[] = { floatArgHandle };
+  v8::Local<v8::Value> v8_floatArg = v8::Number::New(GetIsolate(), floatArg);
+  constexpr int argc = 1;
+  v8::Local<v8::Value> argv[] = { v8_floatArg };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          1,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
 
 v8::Maybe<void> V8TestCallbackInterface::voidMethodTestInterfaceEmptyArg(ScriptWrappable* callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -550,10 +546,10 @@
     return v8::Nothing<void>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -600,47 +596,47 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   v8::Local<v8::Object> argument_creation_context =
       CallbackRelevantScriptState()->GetContext()->Global();
   ALLOW_UNUSED_LOCAL(argument_creation_context);
-  v8::Local<v8::Value> testInterfaceEmptyArgHandle = ToV8(testInterfaceEmptyArg, argument_creation_context, GetIsolate());
-  v8::Local<v8::Value> argv[] = { testInterfaceEmptyArgHandle };
+  v8::Local<v8::Value> v8_testInterfaceEmptyArg = ToV8(testInterfaceEmptyArg, argument_creation_context, GetIsolate());
+  constexpr int argc = 1;
+  v8::Local<v8::Value> argv[] = { v8_testInterfaceEmptyArg };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          1,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
 
 v8::Maybe<void> V8TestCallbackInterface::voidMethodTestInterfaceEmptyStringArg(ScriptWrappable* callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg, const String& stringArg) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -650,10 +646,10 @@
     return v8::Nothing<void>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -700,48 +696,48 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   v8::Local<v8::Object> argument_creation_context =
       CallbackRelevantScriptState()->GetContext()->Global();
   ALLOW_UNUSED_LOCAL(argument_creation_context);
-  v8::Local<v8::Value> testInterfaceEmptyArgHandle = ToV8(testInterfaceEmptyArg, argument_creation_context, GetIsolate());
-  v8::Local<v8::Value> stringArgHandle = V8String(GetIsolate(), stringArg);
-  v8::Local<v8::Value> argv[] = { testInterfaceEmptyArgHandle, stringArgHandle };
+  v8::Local<v8::Value> v8_testInterfaceEmptyArg = ToV8(testInterfaceEmptyArg, argument_creation_context, GetIsolate());
+  v8::Local<v8::Value> v8_stringArg = V8String(GetIsolate(), stringArg);
+  constexpr int argc = 2;
+  v8::Local<v8::Value> argv[] = { v8_testInterfaceEmptyArg, v8_stringArg };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          2,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
 
 v8::Maybe<void> V8TestCallbackInterface::callbackWithThisValueVoidMethodStringArg(ScriptWrappable* callback_this_value, const String& stringArg) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -751,10 +747,10 @@
     return v8::Nothing<void>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -801,47 +797,47 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   v8::Local<v8::Object> argument_creation_context =
       CallbackRelevantScriptState()->GetContext()->Global();
   ALLOW_UNUSED_LOCAL(argument_creation_context);
-  v8::Local<v8::Value> stringArgHandle = V8String(GetIsolate(), stringArg);
-  v8::Local<v8::Value> argv[] = { stringArgHandle };
+  v8::Local<v8::Value> v8_stringArg = V8String(GetIsolate(), stringArg);
+  constexpr int argc = 1;
+  v8::Local<v8::Value> argv[] = { v8_stringArg };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          1,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
 
 v8::Maybe<void> V8TestCallbackInterface::customVoidMethodTestInterfaceEmptyArg(ScriptWrappable* callback_this_value, TestInterfaceEmpty* testInterfaceEmptyArg) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -851,10 +847,10 @@
     return v8::Nothing<void>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -901,31 +897,33 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   v8::Local<v8::Object> argument_creation_context =
       CallbackRelevantScriptState()->GetContext()->Global();
   ALLOW_UNUSED_LOCAL(argument_creation_context);
-  v8::Local<v8::Value> testInterfaceEmptyArgHandle = ToV8(testInterfaceEmptyArg, argument_creation_context, GetIsolate());
-  v8::Local<v8::Value> argv[] = { testInterfaceEmptyArgHandle };
+  v8::Local<v8::Value> v8_testInterfaceEmptyArg = ToV8(testInterfaceEmptyArg, argument_creation_context, GetIsolate());
+  constexpr int argc = 1;
+  v8::Local<v8::Value> argv[] = { v8_testInterfaceEmptyArg };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          1,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
index b8b8b48..599bcd8 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
@@ -82,17 +82,15 @@
 }
 
 v8::Maybe<uint16_t> V8TestLegacyCallbackInterface::acceptNode(ScriptWrappable* callback_this_value, Node* node) {
-  // This function implements "call a user object's operation".
-  // https://heycam.github.io/webidl/#call-a-user-objects-operation
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackObject().IsEmpty());
-    v8::Context::Scope context_scope(CallbackObject()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackObject();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -102,10 +100,10 @@
     return v8::Nothing<uint16_t>();
   }
 
-  // step 7. Prepare to run script with relevant settings.
+  // step: Prepare to run script with relevant settings.
   ScriptState::Scope callback_relevant_context_scope(
       CallbackRelevantScriptState());
-  // step 8. Prepare to run a callback with stored settings.
+  // step: Prepare to run a callback with stored settings.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       IncumbentScriptState()->GetContext());
 
@@ -152,31 +150,33 @@
     this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
   }
 
-  // step 12. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   v8::Local<v8::Object> argument_creation_context =
       CallbackRelevantScriptState()->GetContext()->Global();
   ALLOW_UNUSED_LOCAL(argument_creation_context);
-  v8::Local<v8::Value> nodeHandle = ToV8(node, argument_creation_context, GetIsolate());
-  v8::Local<v8::Value> argv[] = { nodeHandle };
+  v8::Local<v8::Value> v8_node = ToV8(node, argument_creation_context, GetIsolate());
+  constexpr int argc = 1;
+  v8::Local<v8::Value> argv[] = { v8_node };
+  static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 13. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
           function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
-          1,
+          argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 14. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<uint16_t>();
   }
 
-  // step 15. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   {
     ExceptionState exception_state(GetIsolate(),
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function.cc
index b0647919..f23c198d 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function.cc
@@ -27,18 +27,15 @@
 }
 
 v8::Maybe<void> V8VoidCallbackFunction::Invoke(ScriptWrappable* callback_this_value) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -48,49 +45,47 @@
     return v8::Nothing<void>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   const int argc = 0;
   v8::Local<v8::Value> *argv = nullptr;
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_dictionary_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_dictionary_arg.cc
index b3fc80e5..f2d1457 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_dictionary_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_dictionary_arg.cc
@@ -28,18 +28,15 @@
 }
 
 v8::Maybe<void> V8VoidCallbackFunctionDictionaryArg::Invoke(ScriptWrappable* callback_this_value, const TestDictionary& arg) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -49,28 +46,26 @@
     return v8::Nothing<void>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -82,21 +77,21 @@
   v8::Local<v8::Value> argv[] = { v8_arg };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_enum_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_enum_arg.cc
index 91f7884d..f741b1e 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_enum_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_enum_arg.cc
@@ -28,18 +28,15 @@
 }
 
 v8::Maybe<void> V8VoidCallbackFunctionEnumArg::Invoke(ScriptWrappable* callback_this_value, const String& arg) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -49,26 +46,24 @@
     return v8::Nothing<void>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
-
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
   // Enum values provided by Blink must be valid, otherwise typo.
 #if DCHECK_IS_ON()
@@ -83,14 +78,14 @@
                                    ExceptionState::kExecutionContext,
                                    "VoidCallbackFunctionEnumArg",
                                    "invoke");
-    if (!IsValidEnum(arg, valid_arg_values, base::size(valid_arg_values), "TestEnum", exception_state)) {
+    if (!IsValidEnum(arg, valid_arg_values, base::size(valid_arg_values), "TestEnum", exception_state)) { //
       NOTREACHED();
       return v8::Nothing<void>();
     }
   }
 #endif
 
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -102,21 +97,21 @@
   v8::Local<v8::Value> argv[] = { v8_arg };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_interface_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_interface_arg.cc
index c8ad2c2..071bb6f 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_interface_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_interface_arg.cc
@@ -28,18 +28,15 @@
 }
 
 v8::Maybe<void> V8VoidCallbackFunctionInterfaceArg::Invoke(ScriptWrappable* callback_this_value, HTMLDivElement* divElement) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -49,28 +46,26 @@
     return v8::Nothing<void>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -82,21 +77,21 @@
   v8::Local<v8::Value> argv[] = { v8_divElement };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_test_interface_sequence_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_test_interface_sequence_arg.cc
index 6f4a8fc1..4ca8e4bac 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_test_interface_sequence_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_test_interface_sequence_arg.cc
@@ -29,18 +29,15 @@
 }
 
 v8::Maybe<void> V8VoidCallbackFunctionTestInterfaceSequenceArg::Invoke(ScriptWrappable* callback_this_value, const HeapVector<Member<TestInterfaceImplementation>>& arg) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -50,28 +47,26 @@
     return v8::Nothing<void>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -83,21 +78,21 @@
   v8::Local<v8::Value> argv[] = { v8_arg };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_typedef.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_typedef.cc
index 00e11e9c..78893d0 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_typedef.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_typedef.cc
@@ -28,18 +28,15 @@
 }
 
 v8::Maybe<void> V8VoidCallbackFunctionTypedef::Invoke(ScriptWrappable* callback_this_value, const String& arg) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -49,28 +46,26 @@
     return v8::Nothing<void>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
@@ -82,21 +77,21 @@
   v8::Local<v8::Value> argv[] = { v8_arg };
   static_assert(static_cast<size_t>(argc) == base::size(argv), "size mismatch");
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_void_callback_function_modules.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_void_callback_function_modules.cc
index 8a9b0e1..c67f095 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_void_callback_function_modules.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_void_callback_function_modules.cc
@@ -27,18 +27,15 @@
 }
 
 v8::Maybe<void> V8VoidCallbackFunctionModules::Invoke(ScriptWrappable* callback_this_value) {
-  // This function implements "invoke" algorithm defined in
-  // "3.10. Invoking callback functions".
-  // https://heycam.github.io/webidl/#es-invoking-callback-functions
-
   if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState(),
                                   IncumbentScriptState())) {
     // Wrapper-tracing for the callback function makes the function object and
     // its creation context alive. Thus it's safe to use the creation context
     // of the callback function here.
     v8::HandleScope handle_scope(GetIsolate());
-    CHECK(!CallbackFunction().IsEmpty());
-    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
+    v8::Local<v8::Object> callback_object = CallbackFunction();
+    CHECK(!callback_object.IsEmpty());
+    v8::Context::Scope context_scope(callback_object->CreationContext());
     V8ThrowException::ThrowError(
         GetIsolate(),
         ExceptionMessages::FailedToExecute(
@@ -48,49 +45,47 @@
     return v8::Nothing<void>();
   }
 
+  // step: Prepare to run script with relevant settings.
+  ScriptState::Scope callback_relevant_context_scope(
+      CallbackRelevantScriptState());
+  // step: Prepare to run a callback with stored settings.
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      IncumbentScriptState()->GetContext());
+
+  v8::Local<v8::Function> function;
+  // callback function\'s invoke:
   // step 4. If ! IsCallable(F) is false:
   //
   // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
   // case.
-#if DCHECK_IS_ON()
-  {
-    v8::HandleScope handle_scope(GetIsolate());
-    DCHECK(CallbackFunction()->IsFunction());
-  }
-#endif
+  DCHECK(CallbackFunction()->IsFunction());
+  function = CallbackFunction();
 
-  // step 8. Prepare to run script with relevant settings.
-  ScriptState::Scope callback_relevant_context_scope(
-      CallbackRelevantScriptState());
-  // step 9. Prepare to run a callback with stored settings.
-  v8::Context::BackupIncumbentScope backup_incumbent_scope(
-      IncumbentScriptState()->GetContext());
+  v8::Local<v8::Value> this_arg;
+  this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
 
-  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
-                                       CallbackRelevantScriptState());
-
-  // step 10. Let esArgs be the result of converting args to an ECMAScript
+  // step: Let esArgs be the result of converting args to an ECMAScript
   //   arguments list. If this throws an exception, set completion to the
   //   completion value representing the thrown exception and jump to the step
   //   labeled return.
   const int argc = 0;
   v8::Local<v8::Value> *argv = nullptr;
 
-  // step 11. Let callResult be Call(X, thisArg, esArgs).
   v8::Local<v8::Value> call_result;
+  // step: Let callResult be Call(X, thisArg, esArgs).
   if (!V8ScriptRunner::CallFunction(
-          CallbackFunction(),
+          function,
           ExecutionContext::From(CallbackRelevantScriptState()),
           this_arg,
           argc,
           argv,
           GetIsolate()).ToLocal(&call_result)) {
-    // step 12. If callResult is an abrupt completion, set completion to
-    //   callResult and jump to the step labeled return.
+    // step: If callResult is an abrupt completion, set completion to callResult
+    //   and jump to the step labeled return.
     return v8::Nothing<void>();
   }
 
-  // step 13. Set completion to the result of converting callResult.[[Value]] to
+  // step: Set completion to the result of converting callResult.[[Value]] to
   //   an IDL value of the same type as the operation's return type.
   return v8::JustVoid();
 }
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index a7c17fec..d42dd45 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -193,6 +193,7 @@
     "//third_party/blink/renderer/core/input",
     "//third_party/blink/renderer/core/inspector",
     "//third_party/blink/renderer/core/intersection_observer",
+    "//third_party/blink/renderer/core/invisible_dom",
     "//third_party/blink/renderer/core/layout",
     "//third_party/blink/renderer/core/layout/svg:svg_layout",
     "//third_party/blink/renderer/core/loader",
@@ -359,6 +360,7 @@
     "events/wheel_event.idl",
     "html/forms/form_data_event.idl",
     "html/track/track_event.idl",
+    "invisible_dom/activate_invisible_event.idl",
     "mojo/test/mojo_interface_request_event.idl",
   ]
   output_file = "core/event_names.json5"
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index 9941479..3dc993e3 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -295,6 +295,7 @@
                     "inspector/inspector_overlay_host.idl",
                     "intersection_observer/intersection_observer.idl",
                     "intersection_observer/intersection_observer_entry.idl",
+                    "invisible_dom/activate_invisible_event.idl",
                     "layout/custom/layout_constraints.idl",
                     "layout/custom/layout_fragment.idl",
                     "layout/custom/layout_fragment_request.idl",
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index 8dafb53..3b542f7b 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -104,11 +104,16 @@
   if (layout_parent_style.ForceLegacyLayout())
     return true;
 
+  const Document& document = element.GetDocument();
+
   // TODO(layout-dev): Once LayoutNG handles inline content editable, we
   // should get rid of following code fragment.
-  const Document& document = element.GetDocument();
-  if (style.UserModify() != EUserModify::kReadOnly || document.InDesignMode() ||
-      style.Display() == EDisplay::kWebkitBox ||
+  if (!RuntimeEnabledFeatures::EditingNGEnabled()) {
+    if (style.UserModify() != EUserModify::kReadOnly || document.InDesignMode())
+      return true;
+  }
+
+  if (style.Display() == EDisplay::kWebkitBox ||
       style.Display() == EDisplay::kWebkitInlineBox)
     return true;
 
@@ -558,11 +563,14 @@
   const ComputedStyle& parent_style = *state.ParentStyle();
   const ComputedStyle& layout_parent_style = *state.LayoutParentStyle();
 
-  if (element &&
-      (style.Display() != EDisplay::kNone ||
-       element->LayoutObjectIsNeeded(style)) &&
-      element->IsHTMLElement()) {
-    AdjustStyleForHTMLElement(style, ToHTMLElement(*element));
+  if (element && (style.Display() != EDisplay::kNone ||
+                  element->LayoutObjectIsNeeded(style))) {
+    // TODO(rakina): Move this attribute check somewhere else.
+    if (RuntimeEnabledFeatures::InvisibleDOMEnabled() &&
+        !element->invisible().IsNull())
+      style.SetDisplay(EDisplay::kNone);
+    else if (element->IsHTMLElement())
+      AdjustStyleForHTMLElement(style, ToHTMLElement(*element));
   }
   if (style.Display() != EDisplay::kNone) {
     // Per the spec, position 'static' and 'relative' in the top layer compute
diff --git a/third_party/blink/renderer/core/css/style_change_reason.cc b/third_party/blink/renderer/core/css/style_change_reason.cc
index a457e9fb..a8ff0fa 100644
--- a/third_party/blink/renderer/core/css/style_change_reason.cc
+++ b/third_party/blink/renderer/core/css/style_change_reason.cc
@@ -30,6 +30,7 @@
     "Inline CSS style declaration was mutated";
 const char kInspector[] = "Inspector";
 const char kLanguage[] = "Language";
+const char kInvisibleChange[] = "InvisibleChange";
 const char kLinkColorChange[] = "LinkColorChange";
 const char kPlatformColorChange[] = "PlatformColorChange";
 const char kPolicyViolation[] = "Feature Policy Violation";
diff --git a/third_party/blink/renderer/core/css/style_change_reason.h b/third_party/blink/renderer/core/css/style_change_reason.h
index 3288796..06d03249 100644
--- a/third_party/blink/renderer/core/css/style_change_reason.h
+++ b/third_party/blink/renderer/core/css/style_change_reason.h
@@ -30,6 +30,7 @@
 extern const char kInline[];
 extern const char kInlineCSSStyleMutated[];
 extern const char kInspector[];
+extern const char kInvisibleChange[];
 extern const char kLanguage[];
 extern const char kLinkColorChange[];
 extern const char kPlatformColorChange[];
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 42c6d158..e3f0970c 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -45,6 +45,7 @@
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
+#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
 #include "third_party/blink/renderer/core/dom/processing_instruction.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
@@ -1406,8 +1407,10 @@
 
 void StyleEngine::MarkForWhitespaceReattachment() {
   for (auto element : whitespace_reattach_set_) {
-    if (!element->GetLayoutObject())
+    if (element->NeedsReattachLayoutTree() || !element->GetLayoutObject() ||
+        !LayoutTreeBuilderTraversal::FirstChild(*element)) {
       continue;
+    }
     element->SetChildNeedsReattachLayoutTree();
     element->MarkAncestorsWithChildNeedsReattachLayoutTree();
   }
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index c61f4dc..1a70dea4 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -1500,4 +1500,50 @@
   EXPECT_EQ(2u, stats->rules_fast_rejected);
 }
 
+TEST_F(StyleEngineTest, MarkForWhitespaceReattachment) {
+  GetDocument().body()->SetInnerHTMLFromString(R"HTML(
+    <div id=d1><span></span></div>
+    <div id=d2><span></span><span></span></div>
+    <div id=d3><span></span><span></span></div>
+  )HTML");
+
+  Element* d1 = GetDocument().getElementById("d1");
+  Element* d2 = GetDocument().getElementById("d2");
+  Element* d3 = GetDocument().getElementById("d3");
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  d1->firstChild()->remove();
+  EXPECT_TRUE(GetDocument().GetStyleEngine().NeedsWhitespaceReattachment(d1));
+  EXPECT_FALSE(GetDocument().ChildNeedsStyleInvalidation());
+  EXPECT_FALSE(GetDocument().ChildNeedsStyleRecalc());
+  EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree());
+
+  GetDocument().GetStyleEngine().MarkForWhitespaceReattachment();
+  EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree());
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  d2->firstChild()->remove();
+  d2->firstChild()->remove();
+  EXPECT_TRUE(GetDocument().GetStyleEngine().NeedsWhitespaceReattachment(d2));
+  EXPECT_FALSE(GetDocument().ChildNeedsStyleInvalidation());
+  EXPECT_FALSE(GetDocument().ChildNeedsStyleRecalc());
+  EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree());
+
+  GetDocument().GetStyleEngine().MarkForWhitespaceReattachment();
+  EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree());
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  d3->firstChild()->remove();
+  EXPECT_TRUE(GetDocument().GetStyleEngine().NeedsWhitespaceReattachment(d3));
+  EXPECT_FALSE(GetDocument().ChildNeedsStyleInvalidation());
+  EXPECT_FALSE(GetDocument().ChildNeedsStyleRecalc());
+  EXPECT_FALSE(GetDocument().ChildNeedsReattachLayoutTree());
+
+  GetDocument().GetStyleEngine().MarkForWhitespaceReattachment();
+  EXPECT_TRUE(GetDocument().ChildNeedsReattachLayoutTree());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 7f3f11b..fd342e4 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -119,6 +119,7 @@
 #include "third_party/blink/renderer/core/html/parser/nesting_level_incrementer.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
 #include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h"
+#include "third_party/blink/renderer/core/invisible_dom/activate_invisible_event.h"
 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
 #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -1434,6 +1435,57 @@
   return rare_data.EnsureAccessibleNode(this);
 }
 
+const AtomicString& Element::invisible() const {
+  return FastGetAttribute(invisibleAttr);
+}
+
+void Element::setInvisible(const AtomicString& value) {
+  setAttribute(invisibleAttr, value);
+}
+
+void Element::DispatchActivateInvisibleEventIfNeeded() {
+  if (!RuntimeEnabledFeatures::InvisibleDOMEnabled())
+    return;
+  // Traverse all inclusive flat-tree ancestor and send activateinvisible
+  // on the ones that have the invisible attribute. Default event handler
+  // will remove invisible attribute of all invisible element if the event is
+  // not canceled, making this element and all ancestors visible again.
+  // We're saving them and the retargeted activated element as DOM structure
+  // may change due to event handlers.
+  HeapVector<Member<Element>> invisible_ancestors;
+  HeapVector<Member<Element>> activated_elements;
+  for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(*this)) {
+    if (ancestor.IsElementNode() && ToElement(ancestor).invisible()) {
+      invisible_ancestors.push_back(ToElement(ancestor));
+      activated_elements.push_back(ancestor.GetTreeScope().Retarget(*this));
+    }
+  }
+  auto* activated_element_iterator = activated_elements.begin();
+  for (Element* ancestor : invisible_ancestors) {
+    DCHECK(activated_element_iterator != activated_elements.end());
+    ancestor->DispatchEvent(
+        *ActivateInvisibleEvent::Create(*activated_element_iterator));
+    ++activated_element_iterator;
+  }
+}
+
+void Element::InvisibleAttributeChanged() {
+  SetNeedsStyleRecalc(
+      kLocalStyleChange,
+      StyleChangeReasonForTracing::Create(StyleChangeReason::kInvisibleChange));
+}
+
+void Element::DefaultEventHandler(Event* event) {
+  if (RuntimeEnabledFeatures::InvisibleDOMEnabled() &&
+      event->type() == EventTypeNames::activateinvisible &&
+      event->target() == this) {
+    removeAttribute(invisibleAttr);
+    event->SetDefaultHandled();
+    return;
+  }
+  ContainerNode::DefaultEventHandler(event);
+}
+
 bool Element::toggleAttribute(const AtomicString& qualified_name,
                               ExceptionState& exception_state) {
   // https://dom.spec.whatwg.org/#dom-element-toggleattribute
@@ -1706,6 +1758,10 @@
       GetElementData()->presentation_attribute_style_is_dirty_ = true;
       SetNeedsStyleRecalc(kLocalStyleChange,
                           StyleChangeReasonForTracing::FromAttribute(name));
+    } else if (RuntimeEnabledFeatures::InvisibleDOMEnabled() &&
+               name == HTMLNames::invisibleAttr &&
+               params.old_value.IsNull() != params.new_value.IsNull()) {
+      InvisibleAttributeChanged();
     }
   }
 
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 010134b..04a6b0b 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -316,6 +316,12 @@
   AccessibleNode* ExistingAccessibleNode() const;
   AccessibleNode* accessibleNode();
 
+  const AtomicString& invisible() const;
+  void setInvisible(const AtomicString&);
+  void DispatchActivateInvisibleEventIfNeeded();
+
+  void DefaultEventHandler(Event*) override;
+
   void DidMoveToNewDocument(Document&) override;
 
   void removeAttribute(const AtomicString& name);
@@ -970,6 +976,8 @@
   void InlineStyleChanged();
   void SetInlineStyleFromString(const AtomicString&);
 
+  void InvisibleAttributeChanged();
+
   // If the only inherited changes in the parent element are independent,
   // these changes can be directly propagated to this element (the child).
   // If these conditions are met, propagates the changes to the current style
diff --git a/third_party/blink/renderer/core/dom/element.idl b/third_party/blink/renderer/core/dom/element.idl
index 534f5d1..0afa850 100644
--- a/third_party/blink/renderer/core/dom/element.idl
+++ b/third_party/blink/renderer/core/dom/element.idl
@@ -135,6 +135,8 @@
     // Accessibility Object Model
     [RuntimeEnabled=AccessibilityObjectModel] readonly attribute AccessibleNode? accessibleNode;
 
+    [RuntimeEnabled=InvisibleDOM, CEReactions, CustomElementCallbacks] attribute DOMString invisible;
+
     // Event handler attributes
     attribute EventHandler onbeforecopy;
     attribute EventHandler onbeforecut;
diff --git a/third_party/blink/renderer/core/dom/events/event.cc b/third_party/blink/renderer/core/dom/events/event.cc
index bd42170..c6806fe 100644
--- a/third_party/blink/renderer/core/dom/events/event.cc
+++ b/third_party/blink/renderer/core/dom/events/event.cc
@@ -216,6 +216,10 @@
   return false;
 }
 
+bool Event::IsActivateInvisibleEvent() const {
+  return false;
+}
+
 bool Event::IsClipboardEvent() const {
   return false;
 }
diff --git a/third_party/blink/renderer/core/dom/events/event.h b/third_party/blink/renderer/core/dom/events/event.h
index 6c95dcf..8558bc7 100644
--- a/third_party/blink/renderer/core/dom/events/event.h
+++ b/third_party/blink/renderer/core/dom/events/event.h
@@ -190,6 +190,8 @@
 
   virtual bool IsBeforeUnloadEvent() const;
 
+  virtual bool IsActivateInvisibleEvent() const;
+
   bool PropagationStopped() const {
     return propagation_stopped_ || immediate_propagation_stopped_;
   }
diff --git a/third_party/blink/renderer/core/dom/global_event_handlers.h b/third_party/blink/renderer/core/dom/global_event_handlers.h
index eebf55d1b..2378837 100644
--- a/third_party/blink/renderer/core/dom/global_event_handlers.h
+++ b/third_party/blink/renderer/core/dom/global_event_handlers.h
@@ -40,6 +40,7 @@
 
  public:
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(abort);
+  DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(activateinvisible);
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(auxclick);
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(blur);
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(cancel);
diff --git a/third_party/blink/renderer/core/dom/global_event_handlers.idl b/third_party/blink/renderer/core/dom/global_event_handlers.idl
index dfed8b8..0363476 100644
--- a/third_party/blink/renderer/core/dom/global_event_handlers.idl
+++ b/third_party/blink/renderer/core/dom/global_event_handlers.idl
@@ -34,6 +34,7 @@
     NoInterfaceObject // Always used on target of 'implements'
 ] interface GlobalEventHandlers {
     attribute EventHandler onabort;
+    [RuntimeEnabled=InvisibleDOM] attribute EventHandler onactivateinvisible;
     attribute EventHandler onblur;
     attribute EventHandler oncancel;
     attribute EventHandler oncanplay;
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
index f0b42f60..8e00d68 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
@@ -123,39 +123,18 @@
   return nullptr;
 }
 
-static Node* LastChild(const Node& node) {
-  return FlatTreeTraversal::LastChild(node);
-}
+Node* LayoutTreeBuilderTraversal::LastChild(const Node& node) {
+  if (!node.IsElementNode())
+    return FlatTreeTraversal::LastChild(node);
 
-static Node* PseudoAwarePreviousSibling(const Node& node) {
-  Node* previous_node = LayoutTreeBuilderTraversal::PreviousSibling(node);
-  Node* parent_node = LayoutTreeBuilderTraversal::Parent(node);
-
-  if (parent_node && parent_node->IsElementNode() && !previous_node) {
-    if (node.IsAfterPseudoElement()) {
-      if (Node* child = LastChild(*parent_node))
-        return child;
-    }
-    if (!node.IsBeforePseudoElement())
-      return ToElement(parent_node)->GetPseudoElement(kPseudoIdBefore);
-  }
-  return previous_node;
-}
-
-static Node* PseudoAwareLastChild(const Node& node) {
-  if (node.IsElementNode()) {
-    const Element& current_element = ToElement(node);
-    Node* last = current_element.GetPseudoElement(kPseudoIdAfter);
-    if (last)
-      return last;
-
-    last = LastChild(current_element);
-    if (!last)
-      last = current_element.GetPseudoElement(kPseudoIdBefore);
+  const Element& current_element = ToElement(node);
+  Node* last = current_element.GetPseudoElement(kPseudoIdAfter);
+  if (last)
     return last;
-  }
-
-  return LastChild(node);
+  last = FlatTreeTraversal::LastChild(current_element);
+  if (!last)
+    last = current_element.GetPseudoElement(kPseudoIdBefore);
+  return last;
 }
 
 Node* LayoutTreeBuilderTraversal::Previous(const Node& node,
@@ -163,8 +142,8 @@
   if (node == stay_within)
     return nullptr;
 
-  if (Node* previous_node = PseudoAwarePreviousSibling(node)) {
-    while (Node* previous_last_child = PseudoAwareLastChild(*previous_node))
+  if (Node* previous_node = PreviousSibling(node)) {
+    while (Node* previous_last_child = LastChild(*previous_node))
       previous_node = previous_last_child;
     return previous_node;
   }
@@ -172,48 +151,28 @@
 }
 
 Node* LayoutTreeBuilderTraversal::FirstChild(const Node& node) {
-  return FlatTreeTraversal::FirstChild(node);
-}
+  if (!node.IsElementNode())
+    return FlatTreeTraversal::FirstChild(node);
 
-static Node* PseudoAwareNextSibling(const Node& node) {
-  Node* parent_node = LayoutTreeBuilderTraversal::Parent(node);
-  Node* next_node = LayoutTreeBuilderTraversal::NextSibling(node);
-
-  if (parent_node && parent_node->IsElementNode() && !next_node) {
-    if (node.IsBeforePseudoElement()) {
-      if (Node* child = LayoutTreeBuilderTraversal::FirstChild(*parent_node))
-        return child;
-    }
-    if (!node.IsAfterPseudoElement())
-      return ToElement(parent_node)->GetPseudoElement(kPseudoIdAfter);
-  }
-  return next_node;
-}
-
-static Node* PseudoAwareFirstChild(const Node& node) {
-  if (node.IsElementNode()) {
-    const Element& current_element = ToElement(node);
-    Node* first = current_element.GetPseudoElement(kPseudoIdBefore);
-    if (first)
-      return first;
-    first = LayoutTreeBuilderTraversal::FirstChild(current_element);
-    if (!first)
-      first = current_element.GetPseudoElement(kPseudoIdAfter);
+  const Element& current_element = ToElement(node);
+  Node* first = current_element.GetPseudoElement(kPseudoIdBefore);
+  if (first)
     return first;
-  }
-
-  return LayoutTreeBuilderTraversal::FirstChild(node);
+  first = FlatTreeTraversal::FirstChild(node);
+  if (!first)
+    first = current_element.GetPseudoElement(kPseudoIdAfter);
+  return first;
 }
 
 static Node* NextAncestorSibling(const Node& node, const Node* stay_within) {
-  DCHECK(!PseudoAwareNextSibling(node));
+  DCHECK(!LayoutTreeBuilderTraversal::NextSibling(node));
   DCHECK_NE(node, stay_within);
   for (Node* parent_node = LayoutTreeBuilderTraversal::Parent(node);
        parent_node;
        parent_node = LayoutTreeBuilderTraversal::Parent(*parent_node)) {
     if (parent_node == stay_within)
       return nullptr;
-    if (Node* next_node = PseudoAwareNextSibling(*parent_node))
+    if (Node* next_node = LayoutTreeBuilderTraversal::NextSibling(*parent_node))
       return next_node;
   }
   return nullptr;
@@ -224,14 +183,14 @@
     const Node* stay_within) {
   if (node == stay_within)
     return nullptr;
-  if (Node* next_node = PseudoAwareNextSibling(node))
+  if (Node* next_node = NextSibling(node))
     return next_node;
   return NextAncestorSibling(node, stay_within);
 }
 
 Node* LayoutTreeBuilderTraversal::Next(const Node& node,
                                        const Node* stay_within) {
-  if (Node* child = PseudoAwareFirstChild(node))
+  if (Node* child = FirstChild(node))
     return child;
   return NextSkippingChildren(node, stay_within);
 }
@@ -242,8 +201,8 @@
     if (!HasDisplayContentsStyle(*sibling))
       return sibling;
 
-    if (Node* inner =
-            NextLayoutSiblingInternal(PseudoAwareFirstChild(*sibling), limit))
+    if (Node* inner = NextLayoutSiblingInternal(
+            LayoutTreeBuilderTraversal::FirstChild(*sibling), limit))
       return inner;
 
     if (limit == -1)
@@ -276,7 +235,7 @@
       return sibling;
 
     if (Node* inner = PreviousLayoutSiblingInternal(
-            PseudoAwareLastChild(*sibling), limit))
+            LayoutTreeBuilderTraversal::LastChild(*sibling), limit))
       return inner;
 
     if (limit == -1)
@@ -306,7 +265,7 @@
 
 Node* LayoutTreeBuilderTraversal::FirstLayoutChild(const Node& node) {
   int32_t limit = kTraverseAllSiblings;
-  return NextLayoutSiblingInternal(PseudoAwareFirstChild(node), limit);
+  return NextLayoutSiblingInternal(FirstChild(node), limit);
 }
 
 LayoutObject* LayoutTreeBuilderTraversal::NextSiblingLayoutObject(
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h
index b73c21c..be37bb8 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h
@@ -62,6 +62,7 @@
   static ContainerNode* Parent(const Node&, ParentDetails* = nullptr);
   static ContainerNode* LayoutParent(const Node&, ParentDetails* = nullptr);
   static Node* FirstChild(const Node&);
+  static Node* LastChild(const Node&);
   static Node* NextSibling(const Node&);
   static Node* NextLayoutSibling(const Node& node) {
     int32_t limit = kTraverseAllSiblings;
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal_test.cc b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal_test.cc
index 19d5817..ad34ed77 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal_test.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal_test.cc
@@ -40,7 +40,7 @@
   const char* const kHtml =
       "<style>"
       "#top::before { content: \"foo\"; }"
-      "#top::before { content: \"bar\"; }"
+      "#top::after { content: \"bar\"; }"
       "</style>"
       "<div id='top'></div>";
   SetupSampleHTML(kHtml);
@@ -51,6 +51,10 @@
   EXPECT_EQ(before, LayoutTreeBuilderTraversal::Next(*top, nullptr));
   EXPECT_EQ(after, LayoutTreeBuilderTraversal::NextSibling(*before));
   EXPECT_EQ(nullptr, LayoutTreeBuilderTraversal::PreviousSibling(*before));
+  EXPECT_EQ(nullptr, LayoutTreeBuilderTraversal::NextSibling(*after));
+  EXPECT_EQ(before, LayoutTreeBuilderTraversal::PreviousSibling(*after));
+  EXPECT_EQ(before, LayoutTreeBuilderTraversal::FirstChild(*top));
+  EXPECT_EQ(after, LayoutTreeBuilderTraversal::LastChild(*top));
 }
 
 TEST_F(LayoutTreeBuilderTraversalTest, emptyDisplayContents) {
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index 28388eda..70c27b7 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -24,6 +24,7 @@
     "accessibleincrement",
     "accessiblescrollintoview",
     "activate",
+    "activateinvisible",
     "active",
     "addsourcebuffer",
     "addstream",
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index df40cae..d09a43b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1521,6 +1521,9 @@
       !(name.IsEmpty() || DeprecatedEqualIgnoringCase(name, "top")))
     return false;
 
+  if (anchor_node)
+    anchor_node->DispatchActivateInvisibleEventIfNeeded();
+
   if (behavior == kUrlFragmentDontScroll)
     return true;
 
diff --git a/third_party/blink/renderer/core/html/forms/file_chooser.cc b/third_party/blink/renderer/core/html/forms/file_chooser.cc
index 5301bf7a2..223eb74 100644
--- a/third_party/blink/renderer/core/html/forms/file_chooser.cc
+++ b/third_party/blink/renderer/core/html/forms/file_chooser.cc
@@ -76,6 +76,7 @@
 
   // Should be released on file choosing.
   AddRef();
+  chrome_client_impl.RegisterPopupOpeningObserver(client_);
   return true;
 }
 
@@ -127,8 +128,11 @@
 }
 
 void FileChooser::DidCloseChooser() {
-  if (chrome_client_impl_)
+  if (chrome_client_impl_) {
     chrome_client_impl_->DidCompleteFileChooser(*this);
+    if (client_)
+      chrome_client_impl_->UnregisterPopupOpeningObserver(client_);
+  }
   Release();
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/file_chooser.h b/third_party/blink/renderer/core/html/forms/file_chooser.h
index 322d008..d2cb33b 100644
--- a/third_party/blink/renderer/core/html/forms/file_chooser.h
+++ b/third_party/blink/renderer/core/html/forms/file_chooser.h
@@ -32,6 +32,7 @@
 
 #include "third_party/blink/public/web/web_file_chooser_completion.h"
 #include "third_party/blink/public/web/web_file_chooser_params.h"
+#include "third_party/blink/renderer/core/page/popup_opening_observer.h"
 #include "third_party/blink/renderer/platform/file_metadata.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -63,11 +64,11 @@
   const FileMetadata metadata;
 };
 
-class FileChooserClient : public GarbageCollectedMixin {
+class FileChooserClient : public PopupOpeningObserver {
  public:
   virtual void FilesChosen(const Vector<FileChooserFileInfo>&) = 0;
   virtual LocalFrame* FrameOrNull() const = 0;
-  virtual ~FileChooserClient();
+  ~FileChooserClient() override;
 
  protected:
   FileChooser* NewFileChooser(const WebFileChooserParams&);
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.cc b/third_party/blink/renderer/core/html/forms/file_input_type.cc
index 1277582..3b38392 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -171,7 +171,6 @@
                       : WebFeature::kInputTypeFileInsecureOriginOpenChooser);
 
     chrome_client->OpenFileChooser(document.GetFrame(), NewFileChooser(params));
-    chrome_client->RegisterPopupOpeningObserver(this);
   }
   event->SetDefaultHandled();
 }
@@ -349,11 +348,8 @@
 void FileInputType::FilesChosen(const Vector<FileChooserFileInfo>& files) {
   SetFiles(CreateFileList(files,
                           GetElement().FastHasAttribute(webkitdirectoryAttr)));
-  if (HasConnectedFileChooser()) {
+  if (HasConnectedFileChooser())
     DisconnectFileChooser();
-    if (auto* chrome_client = GetChromeClient())
-      chrome_client->UnregisterPopupOpeningObserver(this);
-  }
 }
 
 LocalFrame* FileInputType::FrameOrNull() const {
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.h b/third_party/blink/renderer/core/html/forms/file_input_type.h
index fe7448f..f1ea84d4 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.h
@@ -47,8 +47,7 @@
 
 class CORE_EXPORT FileInputType final : public InputType,
                                         public KeyboardClickableInputTypeView,
-                                        private FileChooserClient,
-                                        private PopupOpeningObserver {
+                                        private FileChooserClient {
   USING_GARBAGE_COLLECTED_MIXIN(FileInputType);
 
  public:
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type_test.cc b/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
index f962ee4..08bfc1d 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
@@ -10,11 +10,33 @@
 #include "third_party/blink/renderer/core/fileapi/file_list.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
+#include "third_party/blink/renderer/core/loader/empty_clients.h"
 #include "third_party/blink/renderer/core/page/drag_data.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 
 namespace blink {
 
+namespace {
+
+class WebKitDirectoryChromeClient : public EmptyChromeClient {
+ public:
+  void EnumerateChosenDirectory(FileChooser* chooser) override {
+    chooser->AddRef();  // Do same as ChromeClientImpl
+    static_cast<WebFileChooserCompletion*>(chooser)->DidChooseFile(
+        WebVector<WebString>());
+  }
+
+  void RegisterPopupOpeningObserver(PopupOpeningObserver*) override {
+    NOTREACHED() << "RegisterPopupOpeningObserver should not be called.";
+  }
+  void UnregisterPopupOpeningObserver(PopupOpeningObserver*) override {
+    NOTREACHED() << "UnregisterPopupOpeningObserver should not be called.";
+  }
+};
+
+}  // namespace
+
 TEST(FileInputTypeTest, createFileList) {
   Vector<FileChooserFileInfo> files;
 
@@ -108,4 +130,24 @@
             file_input->Files()->item(1)->GetPath());
 }
 
+TEST(FileInputTypeTest, DropTouchesNoPopupOpeningObserver) {
+  Page::PageClients page_clients;
+  FillWithEmptyClients(page_clients);
+  auto* chrome_client = new WebKitDirectoryChromeClient;
+  page_clients.chrome_client = chrome_client;
+  auto page_holder = DummyPageHolder::Create(IntSize(), &page_clients);
+  Document& doc = page_holder->GetDocument();
+
+  doc.body()->SetInnerHTMLFromString("<input type=file webkitdirectory>");
+  auto& input = *ToHTMLInputElement(doc.body()->firstChild());
+
+  DragData drag_data(DataObject::Create(), FloatPoint(), FloatPoint(),
+                     kDragOperationCopy);
+  drag_data.PlatformData()->Add(File::Create("/foo/bar"));
+  input.ReceiveDroppedFiles(&drag_data);
+
+  // The test passes if WebKitDirectoryChromeClient::
+  // UnregisterPopupOpeningObserver() was not called.
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index 53d26a5..e2e5fba 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -316,8 +316,7 @@
       ping_value.Contains('<')) {
     Deprecation::CountDeprecation(
         GetDocument(), WebFeature::kCanRequestURLHTTPContainingNewline);
-    if (RuntimeEnabledFeatures::RestrictCanRequestURLCharacterSetEnabled())
-      return;
+    return;
   }
 
   UseCounter::Count(GetDocument(), WebFeature::kHTMLAnchorElementPingAttribute);
diff --git a/third_party/blink/renderer/core/html/html_attribute_names.json5 b/third_party/blink/renderer/core/html/html_attribute_names.json5
index 7e379723..4d10558 100644
--- a/third_party/blink/renderer/core/html/html_attribute_names.json5
+++ b/third_party/blink/renderer/core/html/html_attribute_names.json5
@@ -105,6 +105,7 @@
     "ismap",
     "keytype",
     "kind",
+    "invisible",
     "label",
     "lang",
     "language",
@@ -137,6 +138,7 @@
     "nowrap",
     "object",
     "onabort",
+    "onactivateinvisible",
     "onafterprint",
     "onanimationstart",
     "onanimationiteration",
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 2b4aea4..9d22490e 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -334,6 +334,8 @@
        &HTMLElement::OnXMLLangAttrChanged},
 
       {onabortAttr, kNoWebFeature, EventTypeNames::abort, nullptr},
+      {onactivateinvisibleAttr, kNoWebFeature,
+       EventTypeNames::activateinvisible, nullptr},
       {onanimationendAttr, kNoWebFeature, EventTypeNames::animationend,
        nullptr},
       {onanimationiterationAttr, kNoWebFeature,
diff --git a/third_party/blink/renderer/core/invisible_dom/BUILD.gn b/third_party/blink/renderer/core/invisible_dom/BUILD.gn
new file mode 100644
index 0000000..683b133
--- /dev/null
+++ b/third_party/blink/renderer/core/invisible_dom/BUILD.gn
@@ -0,0 +1,13 @@
+# 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.
+
+import("//third_party/blink/renderer/core/core.gni")
+
+blink_core_sources("invisible_dom") {
+  sources = [
+    "activate_invisible_event.cc",
+    "activate_invisible_event.h",
+    "activate_invisible_event.idl",
+  ]
+}
diff --git a/third_party/blink/renderer/core/invisible_dom/OWNERS b/third_party/blink/renderer/core/invisible_dom/OWNERS
new file mode 100644
index 0000000..3d44e4e
--- /dev/null
+++ b/third_party/blink/renderer/core/invisible_dom/OWNERS
@@ -0,0 +1,4 @@
+rakina@chromium.org
+
+# TEAM: dom-dev@chromium.org
+# COMPONENT: Blink>DOM
diff --git a/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.cc b/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.cc
new file mode 100644
index 0000000..3257145
--- /dev/null
+++ b/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.cc
@@ -0,0 +1,33 @@
+// 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/core/invisible_dom/activate_invisible_event.h"
+
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/events/event_dispatcher.h"
+#include "third_party/blink/renderer/core/event_names.h"
+
+namespace blink {
+
+const AtomicString& ActivateInvisibleEvent::InterfaceName() const {
+  return EventNames::ActivateInvisibleEvent;
+}
+
+bool ActivateInvisibleEvent::IsActivateInvisibleEvent() const {
+  return true;
+}
+
+ActivateInvisibleEvent::ActivateInvisibleEvent(Element* activated_element)
+    : Event(EventTypeNames::activateinvisible,
+            Bubbles::kYes,
+            Cancelable::kYes,
+            ComposedMode::kScoped),
+      activated_element_(activated_element) {}
+
+void ActivateInvisibleEvent::Trace(Visitor* visitor) {
+  visitor->Trace(activated_element_);
+  Event::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.h b/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.h
new file mode 100644
index 0000000..050e2663
--- /dev/null
+++ b/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.h
@@ -0,0 +1,43 @@
+// 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_CORE_INVISIBLE_DOM_ACTIVATE_INVISIBLE_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_INVISIBLE_DOM_ACTIVATE_INVISIBLE_EVENT_H_
+
+#include "third_party/blink/renderer/core/dom/events/event.h"
+
+namespace blink {
+
+class Element;
+
+class ActivateInvisibleEvent : public Event {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static ActivateInvisibleEvent* Create(Element* activated_element) {
+    return new ActivateInvisibleEvent(activated_element);
+  }
+
+  Element* activatedElement() const { return activated_element_.Get(); }
+
+  void SetActivatedElement(Element* activated_element) {
+    activated_element_ = activated_element;
+  }
+
+  const AtomicString& InterfaceName() const override;
+  bool IsActivateInvisibleEvent() const override;
+
+  void Trace(Visitor*) override;
+
+ private:
+  explicit ActivateInvisibleEvent(Element* activated_element);
+
+  Member<Element> activated_element_;
+};
+
+DEFINE_EVENT_TYPE_CASTS(ActivateInvisibleEvent);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_INVISIBLE_DOM_ACTIVATE_INVISIBLE_EVENT_H_
diff --git a/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.idl b/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.idl
new file mode 100644
index 0000000..4ee6aaba
--- /dev/null
+++ b/third_party/blink/renderer/core/invisible_dom/activate_invisible_event.idl
@@ -0,0 +1,11 @@
+// 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.
+
+// Explainer at https://goo.gl/mJUNB5
+
+[
+    RuntimeEnabled=InvisibleDOM
+] interface ActivateInvisibleEvent : Event {
+    readonly attribute Element? activatedElement;
+};
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.cc b/third_party/blink/renderer/core/loader/base_fetch_context.cc
index 34146950..940d69d 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context.cc
@@ -162,9 +162,8 @@
         }
       }
 
-      String value =
-          String::Format("destination=%s, target=subresource, site=%s",
-                         destination_value, site_value);
+      String value = String::Format("destination=%s, site=%s",
+                                    destination_value, site_value);
       request.AddHTTPHeaderField("Sec-Metadata", AtomicString(value));
     }
   }
@@ -380,8 +379,7 @@
 
   if (url.PotentiallyDanglingMarkup() && url.ProtocolIsInHTTPFamily()) {
     CountDeprecation(WebFeature::kCanRequestURLHTTPContainingNewline);
-    if (RuntimeEnabledFeatures::RestrictCanRequestURLCharacterSetEnabled())
-      return ResourceRequestBlockedReason::kOther;
+    return ResourceRequestBlockedReason::kOther;
   }
 
   // Loading of a subresource may be blocked by previews resource loading hints.
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 3832d6d..a0495b86 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -887,8 +887,7 @@
   if (url.PotentiallyDanglingMarkup() && url.ProtocolIsInHTTPFamily()) {
     Deprecation::CountDeprecation(
         frame_, WebFeature::kCanRequestURLHTTPContainingNewline);
-    if (RuntimeEnabledFeatures::RestrictCanRequestURLCharacterSetEnabled())
-      return;
+    return;
   }
 
   policy = Client()->DecidePolicyForNavigation(
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
index 5e4a515..181d499c 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
@@ -31,7 +31,7 @@
 
 namespace {
 
-bool FillsViewport(const Element& element, bool check_location) {
+bool FillsViewport(const Element& element) {
   if (!element.GetLayoutObject())
     return false;
 
@@ -48,23 +48,19 @@
   if (!quad.IsRectilinear())
     return false;
 
-  LayoutRect bounding_box(quad.BoundingBox());
+  IntRect bounding_box = EnclosingIntRect(quad.BoundingBox());
 
-  LayoutSize icb_size =
-      LayoutSize(top_document.GetLayoutView()->GetLayoutSize());
+  IntSize icb_size = top_document.GetLayoutView()->GetLayoutSize();
 
   float zoom = top_document.GetFrame()->PageZoomFactor();
-  LayoutSize controls_hidden_size = LayoutSize(
+  IntSize controls_hidden_size = ExpandedIntSize(
       top_document.View()->ViewportSizeForViewportUnits().ScaledBy(zoom));
 
   if (bounding_box.Size() != icb_size &&
       bounding_box.Size() != controls_hidden_size)
     return false;
 
-  if (!check_location)
-    return true;
-
-  return bounding_box.Location() == LayoutPoint::Zero();
+  return bounding_box.Location() == IntPoint::Zero();
 }
 
 // If the element is an iframe this grabs the ScrollableArea for the owned
@@ -277,7 +273,7 @@
       return false;
   }
 
-  if (!FillsViewport(element, true))
+  if (!FillsViewport(element))
     return false;
 
   return true;
@@ -389,6 +385,9 @@
   if (!document_->GetLayoutView())
     return;
 
+  if (!document_->GetFrame()->IsMainFrame())
+    return;
+
   // If the main document has vertical scrolling, that's a good sign we
   // shouldn't implicitly promote anything.
   if (ScrollsVerticalOverflow(*document_->GetLayoutView()))
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index 4ace9aa9..d39a89c3 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -2395,6 +2395,74 @@
       << "Once vertical overflow is removed, the iframe should be promoted.";
 }
 
+TEST_F(ImplicitRootScrollerSimTest, AppliedAtFractionalZoom) {
+  // Matches Pixel 2XL screen size of 412x671 at 3.5 DevicePixelRatio.
+  WebView().SetZoomFactorForDeviceScaleFactor(3.5f);
+  WebView().ResizeWithBrowserControls(IntSize(1442, 2349), 196, 0, true);
+
+  SimRequest main_request("https://example.com/test.html", "text/html");
+  SimRequest child_request("https://example.com/child.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  main_request.Complete(R"HTML(
+          <!DOCTYPE html>
+          <style>
+            ::-webkit-scrollbar {
+              width: 0px;
+              height: 0px;
+            }
+            body, html {
+              width: 100%;
+              height: 100%;
+              margin: 0px;
+            }
+            iframe {
+              border: 0;
+              display: block;
+            }
+          </style>
+          <iframe id="container" src="child.html">
+          </iframe>
+          <script>
+            // innerHeight is non-fractional so pages don't have a great way to
+            // set the size to "exctly" 100%. Ensure we still promote in this
+            // common pattern.
+            function resize_handler() {
+              document.getElementById("container").style.height =
+                  window.innerHeight + "px";
+              document.getElementById("container").style.width =
+                  window.innerWidth + "px";
+            }
+
+            resize_handler();
+            window.addEventHandler('resize', resize_handler);
+          </script>
+      )HTML");
+
+  child_request.Complete(R"HTML(
+        <!DOCTYPE html>
+        <style>
+          body {
+            height: 1000px;
+          }
+        </style>
+  )HTML");
+
+  Compositor().BeginFrame();
+  PaintLayerScrollableArea* area = GetDocument().View()->LayoutViewport();
+  ASSERT_FALSE(area->HasVerticalOverflow());
+
+  EXPECT_EQ(GetDocument().getElementById("container"),
+            GetDocument().GetRootScrollerController().EffectiveRootScroller())
+      << "<iframe> should be promoted when URL bar is hidden";
+
+  WebView().ResizeWithBrowserControls(IntSize(1442, 2545), 196, 0, false);
+  Compositor().BeginFrame();
+
+  EXPECT_EQ(GetDocument().getElementById("container"),
+            GetDocument().GetRootScrollerController().EffectiveRootScroller())
+      << "<iframe> should remain promoted when URL bar is hidden";
+}
+
 class RootScrollerHitTest : public RootScrollerTest {
  public:
   void CheckHitTestAtBottomOfScreen() {
diff --git a/third_party/blink/renderer/core/paint/fragment_data.cc b/third_party/blink/renderer/core/paint/fragment_data.cc
index 6040846..9cf6f03e 100644
--- a/third_party/blink/renderer/core/paint/fragment_data.cc
+++ b/third_party/blink/renderer/core/paint/fragment_data.cc
@@ -15,6 +15,18 @@
 
 FragmentData::RareData::~RareData() = default;
 
+void FragmentData::DestroyTail() {
+  while (next_fragment_) {
+    // Take the following (next-next) fragment, clearing
+    // |next_fragment_->next_fragment_|.
+    std::unique_ptr<FragmentData> next =
+        std::move(next_fragment_->next_fragment_);
+    // Point |next_fragment_| to the following fragment and destroy
+    // the current |next_fragment_|.
+    next_fragment_ = std::move(next);
+  }
+}
+
 FragmentData& FragmentData::EnsureNextFragment() {
   if (!next_fragment_)
     next_fragment_ = std::make_unique<FragmentData>();
diff --git a/third_party/blink/renderer/core/paint/fragment_data.h b/third_party/blink/renderer/core/paint/fragment_data.h
index 4bd3b319..46e4649 100644
--- a/third_party/blink/renderer/core/paint/fragment_data.h
+++ b/third_party/blink/renderer/core/paint/fragment_data.h
@@ -21,7 +21,7 @@
  public:
   FragmentData* NextFragment() const { return next_fragment_.get(); }
   FragmentData& EnsureNextFragment();
-  void ClearNextFragment() { next_fragment_.reset(); }
+  void ClearNextFragment() { DestroyTail(); }
 
   // Visual offset of this fragment's top-left position from the
   // "paint offset root" which is the containing root PaintLayer of the root
@@ -209,9 +209,16 @@
   const EffectPaintPropertyNode* PreEffect() const;
   const EffectPaintPropertyNode* PreFilter() const;
 
+  ~FragmentData() {
+    if (next_fragment_)
+      DestroyTail();
+  }
+
  private:
   friend class FragmentDataTest;
 
+  void DestroyTail();
+
   // Contains rare data that that is not needed on all fragments.
   struct RareData {
     USING_FAST_MALLOC(RareData);
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.cc b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.cc
index 2355f9d..22f92a5 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.cc
@@ -111,12 +111,6 @@
                       WTF::Passed(location->Clone()), exception_id));
 }
 
-void DedicatedWorkerObjectProxy::DidCreateWorkerGlobalScope(
-    WorkerOrWorkletGlobalScope* global_scope) {
-  DCHECK(!worker_global_scope_);
-  worker_global_scope_ = ToWorkerGlobalScope(global_scope);
-}
-
 void DedicatedWorkerObjectProxy::DidEvaluateClassicScript(bool success) {
   PostCrossThreadTask(
       *GetParentExecutionContextTaskRunners()->Get(TaskType::kInternalDefault),
@@ -133,10 +127,6 @@
                       messaging_proxy_weak_ptr_, success));
 }
 
-void DedicatedWorkerObjectProxy::WillDestroyWorkerGlobalScope() {
-  worker_global_scope_ = nullptr;
-}
-
 DedicatedWorkerObjectProxy::DedicatedWorkerObjectProxy(
     DedicatedWorkerMessagingProxy* messaging_proxy_weak_ptr,
     ParentExecutionContextTaskRunners* parent_execution_context_task_runners)
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
index 5c7d55ba..319def7 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
@@ -49,8 +49,6 @@
 class DedicatedWorkerMessagingProxy;
 class ParentExecutionContextTaskRunners;
 class ThreadedMessagingProxyBase;
-class WorkerGlobalScope;
-class WorkerOrWorkletGlobalScope;
 class WorkerThread;
 
 // A proxy class to talk to a DedicatedWorker object on the main thread via the
@@ -78,10 +76,8 @@
   void ReportException(const String& error_message,
                        std::unique_ptr<SourceLocation>,
                        int exception_id) override;
-  void DidCreateWorkerGlobalScope(WorkerOrWorkletGlobalScope*) override;
   void DidEvaluateClassicScript(bool success) override;
   void DidEvaluateModuleScript(bool success) override;
-  void WillDestroyWorkerGlobalScope() override;
 
  protected:
   DedicatedWorkerObjectProxy(DedicatedWorkerMessagingProxy*,
@@ -99,7 +95,6 @@
   CrossThreadWeakPersistent<DedicatedWorkerMessagingProxy>
       messaging_proxy_weak_ptr_;
 
-  CrossThreadPersistent<WorkerGlobalScope> worker_global_scope_;
   DISALLOW_COPY_AND_ASSIGN(DedicatedWorkerObjectProxy);
 };
 
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index 68ef015e..e34a78a 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -226,7 +226,7 @@
   int last_pending_error_event_id_ = 0;
 
   Member<OffscreenFontSelector> font_selector_;
-  Member<WorkerAnimationFrameProvider> animation_frame_provider_;
+  TraceWrapperMember<WorkerAnimationFrameProvider> animation_frame_provider_;
 
   service_manager::InterfaceProvider interface_provider_;
 
diff --git a/third_party/blink/renderer/modules/exported/web_dom_file_system.cc b/third_party/blink/renderer/modules/exported/web_dom_file_system.cc
index 79e77e2..7c24cfa 100644
--- a/third_party/blink/renderer/modules/exported/web_dom_file_system.cc
+++ b/third_party/blink/renderer/modules/exported/web_dom_file_system.cc
@@ -70,7 +70,7 @@
   DCHECK(ToWebLocalFrameImpl(frame)->GetFrame());
   DOMFileSystem* dom_file_system = DOMFileSystem::Create(
       ToWebLocalFrameImpl(frame)->GetFrame()->GetDocument(), name,
-      static_cast<FileSystemType>(type), root_url);
+      static_cast<mojom::blink::FileSystemType>(type), root_url);
   if (serializable_type == kSerializableTypeSerializable)
     dom_file_system->MakeClonable();
   return WebDOMFileSystem(dom_file_system);
@@ -92,13 +92,13 @@
 WebFileSystem::Type WebDOMFileSystem::GetType() const {
   DCHECK(private_.Get());
   switch (private_->GetType()) {
-    case kFileSystemTypeTemporary:
+    case blink::mojom::FileSystemType::kTemporary:
       return WebFileSystem::kTypeTemporary;
-    case kFileSystemTypePersistent:
+    case blink::mojom::FileSystemType::kPersistent:
       return WebFileSystem::kTypePersistent;
-    case kFileSystemTypeIsolated:
+    case blink::mojom::FileSystemType::kIsolated:
       return WebFileSystem::kTypeIsolated;
-    case kFileSystemTypeExternal:
+    case blink::mojom::FileSystemType::kExternal:
       return WebFileSystem::kTypeExternal;
     default:
       NOTREACHED();
diff --git a/third_party/blink/renderer/modules/filesystem/dev_tools_host_file_system.cc b/third_party/blink/renderer/modules/filesystem/dev_tools_host_file_system.cc
index 1e8b83c7..9616f29 100644
--- a/third_party/blink/renderer/modules/filesystem/dev_tools_host_file_system.cc
+++ b/third_party/blink/renderer/modules/filesystem/dev_tools_host_file_system.cc
@@ -19,7 +19,8 @@
     const String& root_url) {
   ExecutionContext* context = host.FrontendFrame()->GetDocument();
   return DOMFileSystem::Create(context, file_system_name,
-                               kFileSystemTypeIsolated, KURL(root_url));
+                               mojom::blink::FileSystemType::kIsolated,
+                               KURL(root_url));
 }
 
 void DevToolsHostFileSystem::upgradeDraggedFileSystemPermissions(
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_system.cc b/third_party/blink/renderer/modules/filesystem/dom_file_system.cc
index 6a1f851..5e5f3e8e 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_file_system.cc
+++ b/third_party/blink/renderer/modules/filesystem/dom_file_system.cc
@@ -65,7 +65,7 @@
 // static
 DOMFileSystem* DOMFileSystem::Create(ExecutionContext* context,
                                      const String& name,
-                                     FileSystemType type,
+                                     mojom::blink::FileSystemType type,
                                      const KURL& root_url) {
   return new DOMFileSystem(context, name, type, root_url);
 }
@@ -94,13 +94,13 @@
   root_url.Append('/');
 
   return DOMFileSystem::Create(context, filesystem_name.ToString(),
-                               kFileSystemTypeIsolated,
+                               mojom::blink::FileSystemType::kIsolated,
                                KURL(root_url.ToString()));
 }
 
 DOMFileSystem::DOMFileSystem(ExecutionContext* context,
                              const String& name,
-                             FileSystemType type,
+                             mojom::blink::FileSystemType type,
                              const KURL& root_url)
     : DOMFileSystemBase(context, name, type, root_url),
       ContextClient(context),
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_system.h b/third_party/blink/renderer/modules/filesystem/dom_file_system.h
index d2f3e6b..a1e90d31 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_file_system.h
+++ b/third_party/blink/renderer/modules/filesystem/dom_file_system.h
@@ -55,7 +55,7 @@
  public:
   static DOMFileSystem* Create(ExecutionContext*,
                                const String& name,
-                               FileSystemType,
+                               mojom::blink::FileSystemType,
                                const KURL& root_url);
 
   // Creates a new isolated file system for the given filesystemId.
@@ -93,7 +93,7 @@
  private:
   DOMFileSystem(ExecutionContext*,
                 const String& name,
-                FileSystemType,
+                mojom::blink::FileSystemType,
                 const KURL& root_url);
 
   static String TaskNameForInstrumentation() { return "FileSystem"; }
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_system_base.cc b/third_party/blink/renderer/modules/filesystem/dom_file_system_base.cc
index e1ed5f7..98a8bb6 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_file_system_base.cc
+++ b/third_party/blink/renderer/modules/filesystem/dom_file_system_base.cc
@@ -58,7 +58,7 @@
 
 DOMFileSystemBase::DOMFileSystemBase(ExecutionContext* context,
                                      const String& name,
-                                     FileSystemType type,
+                                     mojom::blink::FileSystemType type,
                                      const KURL& root_url)
     : context_(context),
       name_(name),
@@ -84,20 +84,22 @@
   return context_->GetSecurityOrigin();
 }
 
-bool DOMFileSystemBase::IsValidType(FileSystemType type) {
-  return type == kFileSystemTypeTemporary ||
-         type == kFileSystemTypePersistent || type == kFileSystemTypeIsolated ||
-         type == kFileSystemTypeExternal;
+bool DOMFileSystemBase::IsValidType(mojom::blink::FileSystemType type) {
+  return type == mojom::blink::FileSystemType::kTemporary ||
+         type == mojom::blink::FileSystemType::kPersistent ||
+         type == mojom::blink::FileSystemType::kIsolated ||
+         type == mojom::blink::FileSystemType::kExternal;
 }
 
-KURL DOMFileSystemBase::CreateFileSystemRootURL(const String& origin,
-                                                FileSystemType type) {
+KURL DOMFileSystemBase::CreateFileSystemRootURL(
+    const String& origin,
+    mojom::blink::FileSystemType type) {
   String type_string;
-  if (type == kFileSystemTypeTemporary)
+  if (type == mojom::blink::FileSystemType::kTemporary)
     type_string = kTemporaryPathPrefix;
-  else if (type == kFileSystemTypePersistent)
+  else if (type == mojom::blink::FileSystemType::kPersistent)
     type_string = kPersistentPathPrefix;
-  else if (type == kFileSystemTypeExternal)
+  else if (type == mojom::blink::FileSystemType::kExternal)
     type_string = kExternalPathPrefix;
   else
     return KURL();
@@ -108,7 +110,7 @@
 
 bool DOMFileSystemBase::SupportsToURL() const {
   DCHECK(IsValidType(type_));
-  return type_ != kFileSystemTypeIsolated;
+  return type_ != mojom::blink::FileSystemType::kIsolated;
 }
 
 KURL DOMFileSystemBase::CreateFileSystemURL(const EntryBase* entry) const {
@@ -118,7 +120,7 @@
 KURL DOMFileSystemBase::CreateFileSystemURL(const String& full_path) const {
   DCHECK(DOMFilePath::IsAbsolute(full_path));
 
-  if (GetType() == kFileSystemTypeExternal) {
+  if (GetType() == mojom::blink::FileSystemType::kExternal) {
     // For external filesystem originString could be different from what we have
     // in m_filesystemRootURL.
     StringBuilder result;
@@ -143,7 +145,7 @@
   return url;
 }
 
-bool DOMFileSystemBase::PathToAbsolutePath(FileSystemType type,
+bool DOMFileSystemBase::PathToAbsolutePath(mojom::blink::FileSystemType type,
                                            const EntryBase* base,
                                            String path,
                                            String& absolute_path) {
@@ -153,25 +155,26 @@
     path = DOMFilePath::Append(base->fullPath(), path);
   absolute_path = DOMFilePath::RemoveExtraParentReferences(path);
 
-  return (type != kFileSystemTypeTemporary &&
-          type != kFileSystemTypePersistent) ||
+  return (type != mojom::blink::FileSystemType::kTemporary &&
+          type != mojom::blink::FileSystemType::kPersistent) ||
          DOMFilePath::IsValidPath(absolute_path);
 }
 
-bool DOMFileSystemBase::PathPrefixToFileSystemType(const String& path_prefix,
-                                                   FileSystemType& type) {
+bool DOMFileSystemBase::PathPrefixToFileSystemType(
+    const String& path_prefix,
+    mojom::blink::FileSystemType& type) {
   if (path_prefix == kTemporaryPathPrefix) {
-    type = kFileSystemTypeTemporary;
+    type = mojom::blink::FileSystemType::kTemporary;
     return true;
   }
 
   if (path_prefix == kPersistentPathPrefix) {
-    type = kFileSystemTypePersistent;
+    type = mojom::blink::FileSystemType::kPersistent;
     return true;
   }
 
   if (path_prefix == kExternalPathPrefix) {
-    type = kFileSystemTypeExternal;
+    type = mojom::blink::FileSystemType::kExternal;
     return true;
   }
 
@@ -180,7 +183,7 @@
 
 File* DOMFileSystemBase::CreateFile(const FileMetadata& metadata,
                                     const KURL& file_system_url,
-                                    FileSystemType type,
+                                    mojom::blink::FileSystemType type,
                                     const String name) {
   // For regular filesystem types (temporary or persistent), we should not cache
   // file metadata as it could change File semantics.  For other filesystem
@@ -189,12 +192,14 @@
   // pass it to File constructor (so we may cache the metadata).
   // FIXME: We should use the snapshot metadata for all files.
   // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17746
-  if (type == kFileSystemTypeTemporary || type == kFileSystemTypePersistent)
+  if (type == mojom::blink::FileSystemType::kTemporary ||
+      type == mojom::blink::FileSystemType::kPersistent)
     return File::CreateForFileSystemFile(metadata.platform_path, name);
 
-  const File::UserVisibility user_visibility = (type == kFileSystemTypeExternal)
-                                                   ? File::kIsUserVisible
-                                                   : File::kIsNotUserVisible;
+  const File::UserVisibility user_visibility =
+      (type == mojom::blink::FileSystemType::kExternal)
+          ? File::kIsUserVisible
+          : File::kIsNotUserVisible;
 
   if (!metadata.platform_path.IsEmpty()) {
     // If the platformPath in the returned metadata is given, we create a File
@@ -475,9 +480,13 @@
   return FileSystem()->WaitForAdditionalResult(callbacks_id);
 }
 
-STATIC_ASSERT_ENUM(WebFileSystem::kTypeTemporary, kFileSystemTypeTemporary);
-STATIC_ASSERT_ENUM(WebFileSystem::kTypePersistent, kFileSystemTypePersistent);
-STATIC_ASSERT_ENUM(WebFileSystem::kTypeExternal, kFileSystemTypeExternal);
-STATIC_ASSERT_ENUM(WebFileSystem::kTypeIsolated, kFileSystemTypeIsolated);
+STATIC_ASSERT_ENUM(WebFileSystem::kTypeTemporary,
+                   mojom::blink::FileSystemType::kTemporary);
+STATIC_ASSERT_ENUM(WebFileSystem::kTypePersistent,
+                   mojom::blink::FileSystemType::kPersistent);
+STATIC_ASSERT_ENUM(WebFileSystem::kTypeExternal,
+                   mojom::blink::FileSystemType::kExternal);
+STATIC_ASSERT_ENUM(WebFileSystem::kTypeIsolated,
+                   mojom::blink::FileSystemType::kIsolated);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_system_base.h b/third_party/blink/renderer/modules/filesystem/dom_file_system_base.h
index e5c710d..33b79861 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_file_system_base.h
+++ b/third_party/blink/renderer/modules/filesystem/dom_file_system_base.h
@@ -31,12 +31,12 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILESYSTEM_DOM_FILE_SYSTEM_BASE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILESYSTEM_DOM_FILE_SYSTEM_BASE_H_
 
+#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
 #include "third_party/blink/renderer/modules/filesystem/file_system_callbacks.h"
 #include "third_party/blink/renderer/modules/filesystem/file_system_flags.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -81,7 +81,7 @@
   virtual void ReportError(ErrorCallbackBase*, FileError::ErrorCode) = 0;
 
   const String& name() const { return name_; }
-  FileSystemType GetType() const { return type_; }
+  mojom::blink::FileSystemType GetType() const { return type_; }
   KURL RootURL() const { return filesystem_root_url_; }
   WebFileSystem* FileSystem() const;
   const SecurityOrigin* GetSecurityOrigin() const;
@@ -92,20 +92,21 @@
   void MakeClonable() { clonable_ = true; }
   bool Clonable() const { return clonable_; }
 
-  static bool IsValidType(FileSystemType);
-  static KURL CreateFileSystemRootURL(const String& origin, FileSystemType);
+  static bool IsValidType(mojom::blink::FileSystemType);
+  static KURL CreateFileSystemRootURL(const String& origin,
+                                      mojom::blink::FileSystemType);
   bool SupportsToURL() const;
   KURL CreateFileSystemURL(const EntryBase*) const;
   KURL CreateFileSystemURL(const String& full_path) const;
-  static bool PathToAbsolutePath(FileSystemType,
+  static bool PathToAbsolutePath(mojom::blink::FileSystemType,
                                  const EntryBase*,
                                  String path,
                                  String& absolute_path);
   static bool PathPrefixToFileSystemType(const String& path_prefix,
-                                         FileSystemType&);
+                                         mojom::blink::FileSystemType&);
   static File* CreateFile(const FileMetadata&,
                           const KURL& file_system_url,
-                          FileSystemType,
+                          mojom::blink::FileSystemType,
                           const String name);
 
   // Actual FileSystem API implementations. All the validity checks on virtual
@@ -161,7 +162,7 @@
  protected:
   DOMFileSystemBase(ExecutionContext*,
                     const String& name,
-                    FileSystemType,
+                    mojom::blink::FileSystemType,
                     const KURL& root_url);
 
   friend class DOMFileSystemBaseTest;
@@ -169,7 +170,7 @@
 
   Member<ExecutionContext> context_;
   String name_;
-  FileSystemType type_;
+  mojom::blink::FileSystemType type_;
   KURL filesystem_root_url_;
   bool clonable_;
 };
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_system_base_test.cc b/third_party/blink/renderer/modules/filesystem/dom_file_system_base_test.cc
index 861da30..586b10b 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_file_system_base_test.cc
+++ b/third_party/blink/renderer/modules/filesystem/dom_file_system_base_test.cc
@@ -27,11 +27,11 @@
 
 TEST_F(DOMFileSystemBaseTest, externalFilesystemFilesAreUserVisible) {
   KURL root_url = DOMFileSystemBase::CreateFileSystemRootURL(
-      "http://chromium.org/", kFileSystemTypeExternal);
+      "http://chromium.org/", mojom::blink::FileSystemType::kExternal);
 
-  File* file = DOMFileSystemBase::CreateFile(file_metadata_, root_url,
-                                             kFileSystemTypeExternal,
-                                             "dom_file_system_base_test.cc");
+  File* file = DOMFileSystemBase::CreateFile(
+      file_metadata_, root_url, mojom::blink::FileSystemType::kExternal,
+      "dom_file_system_base_test.cc");
   EXPECT_TRUE(file);
   EXPECT_TRUE(file->HasBackingFile());
   EXPECT_EQ(File::kIsUserVisible, file->GetUserVisibility());
@@ -41,11 +41,11 @@
 
 TEST_F(DOMFileSystemBaseTest, temporaryFilesystemFilesAreNotUserVisible) {
   KURL root_url = DOMFileSystemBase::CreateFileSystemRootURL(
-      "http://chromium.org/", kFileSystemTypeTemporary);
+      "http://chromium.org/", mojom::blink::FileSystemType::kTemporary);
 
-  File* file = DOMFileSystemBase::CreateFile(file_metadata_, root_url,
-                                             kFileSystemTypeTemporary,
-                                             "UserVisibleName.txt");
+  File* file = DOMFileSystemBase::CreateFile(
+      file_metadata_, root_url, mojom::blink::FileSystemType::kTemporary,
+      "UserVisibleName.txt");
   EXPECT_TRUE(file);
   EXPECT_TRUE(file->HasBackingFile());
   EXPECT_EQ(File::kIsNotUserVisible, file->GetUserVisibility());
@@ -55,11 +55,11 @@
 
 TEST_F(DOMFileSystemBaseTest, persistentFilesystemFilesAreNotUserVisible) {
   KURL root_url = DOMFileSystemBase::CreateFileSystemRootURL(
-      "http://chromium.org/", kFileSystemTypePersistent);
+      "http://chromium.org/", mojom::blink::FileSystemType::kPersistent);
 
-  File* file = DOMFileSystemBase::CreateFile(file_metadata_, root_url,
-                                             kFileSystemTypePersistent,
-                                             "UserVisibleName.txt");
+  File* file = DOMFileSystemBase::CreateFile(
+      file_metadata_, root_url, mojom::blink::FileSystemType::kPersistent,
+      "UserVisibleName.txt");
   EXPECT_TRUE(file);
   EXPECT_TRUE(file->HasBackingFile());
   EXPECT_EQ(File::kIsNotUserVisible, file->GetUserVisibility());
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_system_sync.cc b/third_party/blink/renderer/modules/filesystem/dom_file_system_sync.cc
index 4bcbb4d..e560d7ae 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_file_system_sync.cc
+++ b/third_party/blink/renderer/modules/filesystem/dom_file_system_sync.cc
@@ -57,7 +57,7 @@
 
 DOMFileSystemSync::DOMFileSystemSync(ExecutionContext* context,
                                      const String& name,
-                                     FileSystemType type,
+                                     mojom::blink::FileSystemType type,
                                      const KURL& root_url)
     : DOMFileSystemBase(context, name, type, root_url),
       root_entry_(DirectoryEntrySync::Create(this, DOMFilePath::kRoot)) {}
@@ -95,7 +95,7 @@
       CreateFileResult* result,
       const String& name,
       const KURL& url,
-      FileSystemType type) {
+      mojom::blink::FileSystemType type) {
     return base::WrapUnique(static_cast<AsyncFileSystemCallbacks*>(
         new CreateFileHelper(result, name, url, type)));
   }
@@ -126,13 +126,13 @@
   CreateFileHelper(CreateFileResult* result,
                    const String& name,
                    const KURL& url,
-                   FileSystemType type)
+                   mojom::blink::FileSystemType type)
       : result_(result), name_(name), url_(url), type_(type) {}
 
   Persistent<CreateFileResult> result_;
   String name_;
   KURL url_;
-  FileSystemType type_;
+  mojom::blink::FileSystemType type_;
 };
 
 }  // namespace
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_system_sync.h b/third_party/blink/renderer/modules/filesystem/dom_file_system_sync.h
index 474cbda..b4d4f86 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_file_system_sync.h
+++ b/third_party/blink/renderer/modules/filesystem/dom_file_system_sync.h
@@ -48,7 +48,7 @@
  public:
   static DOMFileSystemSync* Create(ExecutionContext* context,
                                    const String& name,
-                                   FileSystemType type,
+                                   mojom::blink::FileSystemType type,
                                    const KURL& root_url) {
     return new DOMFileSystemSync(context, name, type, root_url);
   }
@@ -69,7 +69,7 @@
  private:
   DOMFileSystemSync(ExecutionContext*,
                     const String& name,
-                    FileSystemType,
+                    mojom::blink::FileSystemType,
                     const KURL& root_url);
   Member<DirectoryEntrySync> root_entry_;
 };
diff --git a/third_party/blink/renderer/modules/filesystem/dom_window_file_system.cc b/third_party/blink/renderer/modules/filesystem/dom_window_file_system.cc
index 1f14b42..3558c8f7 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_window_file_system.cc
+++ b/third_party/blink/renderer/modules/filesystem/dom_window_file_system.cc
@@ -25,6 +25,7 @@
 
 #include "third_party/blink/renderer/modules/filesystem/dom_window_file_system.h"
 
+#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -32,7 +33,6 @@
 #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
 #include "third_party/blink/renderer/modules/filesystem/file_system_callbacks.h"
 #include "third_party/blink/renderer/modules/filesystem/local_file_system.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
@@ -64,7 +64,8 @@
     UseCounter::Count(document, WebFeature::kFileAccessedFileSystem);
   }
 
-  FileSystemType file_system_type = static_cast<FileSystemType>(type);
+  mojom::blink::FileSystemType file_system_type =
+      static_cast<mojom::blink::FileSystemType>(type);
   if (!DOMFileSystemBase::IsValidType(file_system_type)) {
     DOMFileSystem::ReportError(document,
                                ScriptErrorCallback::Wrap(error_callback),
@@ -121,11 +122,11 @@
 
 static_assert(
     static_cast<int>(DOMWindowFileSystem::kTemporary) ==
-        static_cast<int>(kFileSystemTypeTemporary),
+        static_cast<int>(mojom::blink::FileSystemType::kTemporary),
     "DOMWindowFileSystem::kTemporary should match FileSystemTypeTemporary");
 static_assert(
     static_cast<int>(DOMWindowFileSystem::kPersistent) ==
-        static_cast<int>(kFileSystemTypePersistent),
+        static_cast<int>(mojom::blink::FileSystemType::kPersistent),
     "DOMWindowFileSystem::kPersistent should match FileSystemTypePersistent");
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/filesystem/entry.cc b/third_party/blink/renderer/modules/filesystem/entry.cc
index 4013042..154ff61 100644
--- a/third_party/blink/renderer/modules/filesystem/entry.cc
+++ b/third_party/blink/renderer/modules/filesystem/entry.cc
@@ -44,7 +44,7 @@
     : EntryBase(file_system, full_path) {}
 
 DOMFileSystem* Entry::filesystem(ScriptState* script_state) const {
-  if (file_system_->GetType() == kFileSystemTypeIsolated) {
+  if (file_system_->GetType() == mojom::blink::FileSystemType::kIsolated) {
     UseCounter::Count(
         ExecutionContext::From(script_state),
         WebFeature::kEntry_Filesystem_AttributeGetter_IsolatedFileSystem);
@@ -55,7 +55,7 @@
 void Entry::getMetadata(ScriptState* script_state,
                         V8MetadataCallback* success_callback,
                         V8ErrorCallback* error_callback) {
-  if (file_system_->GetType() == kFileSystemTypeIsolated) {
+  if (file_system_->GetType() == mojom::blink::FileSystemType::kIsolated) {
     UseCounter::Count(ExecutionContext::From(script_state),
                       WebFeature::kEntry_GetMetadata_Method_IsolatedFileSystem);
   }
@@ -70,7 +70,7 @@
                    const String& name,
                    V8EntryCallback* success_callback,
                    V8ErrorCallback* error_callback) const {
-  if (file_system_->GetType() == kFileSystemTypeIsolated) {
+  if (file_system_->GetType() == mojom::blink::FileSystemType::kIsolated) {
     UseCounter::Count(ExecutionContext::From(script_state),
                       WebFeature::kEntry_MoveTo_Method_IsolatedFileSystem);
   }
@@ -85,7 +85,7 @@
                    const String& name,
                    V8EntryCallback* success_callback,
                    V8ErrorCallback* error_callback) const {
-  if (file_system_->GetType() == kFileSystemTypeIsolated) {
+  if (file_system_->GetType() == mojom::blink::FileSystemType::kIsolated) {
     UseCounter::Count(ExecutionContext::From(script_state),
                       WebFeature::kEntry_CopyTo_Method_IsolatedFileSystem);
   }
@@ -98,7 +98,7 @@
 void Entry::remove(ScriptState* script_state,
                    V8VoidCallback* success_callback,
                    V8ErrorCallback* error_callback) const {
-  if (file_system_->GetType() == kFileSystemTypeIsolated) {
+  if (file_system_->GetType() == mojom::blink::FileSystemType::kIsolated) {
     UseCounter::Count(ExecutionContext::From(script_state),
                       WebFeature::kEntry_Remove_Method_IsolatedFileSystem);
   }
@@ -110,7 +110,7 @@
 void Entry::getParent(ScriptState* script_state,
                       V8EntryCallback* success_callback,
                       V8ErrorCallback* error_callback) const {
-  if (file_system_->GetType() == kFileSystemTypeIsolated) {
+  if (file_system_->GetType() == mojom::blink::FileSystemType::kIsolated) {
     UseCounter::Count(ExecutionContext::From(script_state),
                       WebFeature::kEntry_GetParent_Method_IsolatedFileSystem);
   }
@@ -120,7 +120,7 @@
 }
 
 String Entry::toURL(ScriptState* script_state) const {
-  if (file_system_->GetType() == kFileSystemTypeIsolated) {
+  if (file_system_->GetType() == mojom::blink::FileSystemType::kIsolated) {
     UseCounter::Count(ExecutionContext::From(script_state),
                       WebFeature::kEntry_ToURL_Method_IsolatedFileSystem);
   }
diff --git a/third_party/blink/renderer/modules/filesystem/file_system_callbacks.cc b/third_party/blink/renderer/modules/filesystem/file_system_callbacks.cc
index 49f8b95..d7c00ad 100644
--- a/third_party/blink/renderer/modules/filesystem/file_system_callbacks.cc
+++ b/third_party/blink/renderer/modules/filesystem/file_system_callbacks.cc
@@ -239,7 +239,7 @@
     OnDidOpenFileSystemCallback* success_callback,
     ErrorCallbackBase* error_callback,
     ExecutionContext* context,
-    FileSystemType type) {
+    mojom::blink::FileSystemType type) {
   return base::WrapUnique(
       new FileSystemCallbacks(success_callback, error_callback, context, type));
 }
@@ -248,7 +248,7 @@
     OnDidOpenFileSystemCallback* success_callback,
     ErrorCallbackBase* error_callback,
     ExecutionContext* context,
-    FileSystemType type)
+    mojom::blink::FileSystemType type)
     : FileSystemCallbacksBase(error_callback, nullptr, context),
       success_callback_(success_callback),
       type_(type) {}
@@ -284,7 +284,7 @@
 
 void ResolveURICallbacks::DidResolveURL(const String& name,
                                         const KURL& root_url,
-                                        FileSystemType type,
+                                        mojom::blink::FileSystemType type,
                                         const String& file_path,
                                         bool is_directory) {
   DOMFileSystem* filesystem =
diff --git a/third_party/blink/renderer/modules/filesystem/file_system_callbacks.h b/third_party/blink/renderer/modules/filesystem/file_system_callbacks.h
index 024a3db..33c680e 100644
--- a/third_party/blink/renderer/modules/filesystem/file_system_callbacks.h
+++ b/third_party/blink/renderer/modules/filesystem/file_system_callbacks.h
@@ -33,6 +33,7 @@
 
 #include <memory>
 
+#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_void_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_entry_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_error_callback.h"
@@ -43,7 +44,6 @@
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
 #include "third_party/blink/renderer/modules/filesystem/entry_heap_vector.h"
 #include "third_party/blink/renderer/platform/async_file_system_callbacks.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -232,16 +232,16 @@
       OnDidOpenFileSystemCallback*,
       ErrorCallbackBase*,
       ExecutionContext*,
-      FileSystemType);
+      mojom::blink::FileSystemType);
   void DidOpenFileSystem(const String& name, const KURL& root_url) override;
 
  private:
   FileSystemCallbacks(OnDidOpenFileSystemCallback*,
                       ErrorCallbackBase*,
                       ExecutionContext*,
-                      FileSystemType);
+                      mojom::blink::FileSystemType);
   Persistent<OnDidOpenFileSystemCallback> success_callback_;
-  FileSystemType type_;
+  mojom::blink::FileSystemType type_;
 };
 
 class ResolveURICallbacks final : public FileSystemCallbacksBase {
@@ -253,7 +253,7 @@
   Create(OnDidGetEntryCallback*, ErrorCallbackBase*, ExecutionContext*);
   void DidResolveURL(const String& name,
                      const KURL& root_url,
-                     FileSystemType,
+                     mojom::blink::FileSystemType,
                      const String& file_path,
                      bool is_directry) override;
 
diff --git a/third_party/blink/renderer/modules/filesystem/file_system_client.h b/third_party/blink/renderer/modules/filesystem/file_system_client.h
index 3a46194c..2986f22 100644
--- a/third_party/blink/renderer/modules/filesystem/file_system_client.h
+++ b/third_party/blink/renderer/modules/filesystem/file_system_client.h
@@ -32,8 +32,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILESYSTEM_FILE_SYSTEM_CLIENT_H_
 
 #include <memory>
+#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/noncopyable.h"
diff --git a/third_party/blink/renderer/modules/filesystem/local_file_system.cc b/third_party/blink/renderer/modules/filesystem/local_file_system.cc
index 18a481a..4187cbe 100644
--- a/third_party/blink/renderer/modules/filesystem/local_file_system.cc
+++ b/third_party/blink/renderer/modules/filesystem/local_file_system.cc
@@ -90,7 +90,7 @@
 
 void LocalFileSystem::RequestFileSystem(
     ExecutionContext* context,
-    FileSystemType type,
+    mojom::blink::FileSystemType type,
     long long size,
     std::unique_ptr<AsyncFileSystemCallbacks> callbacks) {
   CallbackWrapper* wrapper = new CallbackWrapper(std::move(callbacks));
@@ -145,9 +145,10 @@
                            FileError::kAbortErr));
 }
 
-void LocalFileSystem::FileSystemAllowedInternal(ExecutionContext* context,
-                                                FileSystemType type,
-                                                CallbackWrapper* callbacks) {
+void LocalFileSystem::FileSystemAllowedInternal(
+    ExecutionContext* context,
+    mojom::blink::FileSystemType type,
+    CallbackWrapper* callbacks) {
   WebFileSystem* file_system = GetFileSystem();
   if (!file_system) {
     FileSystemNotAvailable(context, callbacks);
diff --git a/third_party/blink/renderer/modules/filesystem/local_file_system.h b/third_party/blink/renderer/modules/filesystem/local_file_system.h
index 1028975..06b3748 100644
--- a/third_party/blink/renderer/modules/filesystem/local_file_system.h
+++ b/third_party/blink/renderer/modules/filesystem/local_file_system.h
@@ -32,10 +32,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILESYSTEM_LOCAL_FILE_SYSTEM_H_
 
 #include <memory>
+#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/workers/worker_clients.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -68,7 +68,7 @@
                   const KURL&,
                   std::unique_ptr<AsyncFileSystemCallbacks>);
   void RequestFileSystem(ExecutionContext*,
-                         FileSystemType,
+                         mojom::blink::FileSystemType,
                          long long size,
                          std::unique_ptr<AsyncFileSystemCallbacks>);
 
@@ -88,7 +88,7 @@
                                        base::OnceClosure denied);
   void FileSystemNotAllowedInternal(ExecutionContext*, CallbackWrapper*);
   void FileSystemAllowedInternal(ExecutionContext*,
-                                 FileSystemType,
+                                 mojom::blink::FileSystemType,
                                  CallbackWrapper*);
   void ResolveURLInternal(ExecutionContext*, const KURL&, CallbackWrapper*);
 
diff --git a/third_party/blink/renderer/modules/filesystem/worker_global_scope_file_system.cc b/third_party/blink/renderer/modules/filesystem/worker_global_scope_file_system.cc
index f7c40b2f..26b2ce2 100644
--- a/third_party/blink/renderer/modules/filesystem/worker_global_scope_file_system.cc
+++ b/third_party/blink/renderer/modules/filesystem/worker_global_scope_file_system.cc
@@ -29,6 +29,7 @@
 
 #include <memory>
 
+#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
@@ -40,7 +41,6 @@
 #include "third_party/blink/renderer/modules/filesystem/local_file_system.h"
 #include "third_party/blink/renderer/modules/filesystem/sync_callback_helper.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
@@ -61,7 +61,8 @@
     UseCounter::Count(secure_context, WebFeature::kFileAccessedFileSystem);
   }
 
-  FileSystemType file_system_type = static_cast<FileSystemType>(type);
+  mojom::blink::FileSystemType file_system_type =
+      static_cast<mojom::blink::FileSystemType>(type);
   if (!DOMFileSystemBase::IsValidType(file_system_type)) {
     DOMFileSystem::ReportError(&worker,
                                ScriptErrorCallback::Wrap(error_callback),
@@ -91,7 +92,8 @@
     UseCounter::Count(secure_context, WebFeature::kFileAccessedFileSystem);
   }
 
-  FileSystemType file_system_type = static_cast<FileSystemType>(type);
+  mojom::blink::FileSystemType file_system_type =
+      static_cast<mojom::blink::FileSystemType>(type);
   if (!DOMFileSystemBase::IsValidType(file_system_type)) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidModificationError,
@@ -178,11 +180,11 @@
 }
 
 static_assert(static_cast<int>(WorkerGlobalScopeFileSystem::kTemporary) ==
-                  static_cast<int>(kFileSystemTypeTemporary),
+                  static_cast<int>(mojom::blink::FileSystemType::kTemporary),
               "WorkerGlobalScopeFileSystem::kTemporary should match "
               "FileSystemTypeTemporary");
 static_assert(static_cast<int>(WorkerGlobalScopeFileSystem::kPersistent) ==
-                  static_cast<int>(kFileSystemTypePersistent),
+                  static_cast<int>(mojom::blink::FileSystemType::kPersistent),
               "WorkerGlobalScopeFileSystem::kPersistent should match "
               "FileSystemTypePersistent");
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 35f5af9..c50af6c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -886,6 +886,24 @@
   return RTCSessionDescription::Create(web_session_description);
 }
 
+RTCSessionDescription* RTCPeerConnection::currentLocalDescription() {
+  WebRTCSessionDescription web_session_description =
+      peer_handler_->CurrentLocalDescription();
+  if (web_session_description.IsNull())
+    return nullptr;
+
+  return RTCSessionDescription::Create(web_session_description);
+}
+
+RTCSessionDescription* RTCPeerConnection::pendingLocalDescription() {
+  WebRTCSessionDescription web_session_description =
+      peer_handler_->PendingLocalDescription();
+  if (web_session_description.IsNull())
+    return nullptr;
+
+  return RTCSessionDescription::Create(web_session_description);
+}
+
 ScriptPromise RTCPeerConnection::setRemoteDescription(
     ScriptState* script_state,
     const RTCSessionDescriptionInit& session_description_init) {
@@ -949,6 +967,24 @@
   return RTCSessionDescription::Create(web_session_description);
 }
 
+RTCSessionDescription* RTCPeerConnection::currentRemoteDescription() {
+  WebRTCSessionDescription web_session_description =
+      peer_handler_->CurrentRemoteDescription();
+  if (web_session_description.IsNull())
+    return nullptr;
+
+  return RTCSessionDescription::Create(web_session_description);
+}
+
+RTCSessionDescription* RTCPeerConnection::pendingRemoteDescription() {
+  WebRTCSessionDescription web_session_description =
+      peer_handler_->PendingRemoteDescription();
+  if (web_session_description.IsNull())
+    return nullptr;
+
+  return RTCSessionDescription::Create(web_session_description);
+}
+
 void RTCPeerConnection::setConfiguration(
     ScriptState* script_state,
     const RTCConfiguration& rtc_configuration,
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index e055c8f..6ace20e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -112,6 +112,8 @@
       V8VoidFunction*,
       V8RTCPeerConnectionErrorCallback* = nullptr);
   RTCSessionDescription* localDescription();
+  RTCSessionDescription* currentLocalDescription();
+  RTCSessionDescription* pendingLocalDescription();
 
   ScriptPromise setRemoteDescription(ScriptState*,
                                      const RTCSessionDescriptionInit&);
@@ -121,6 +123,8 @@
       V8VoidFunction*,
       V8RTCPeerConnectionErrorCallback* = nullptr);
   RTCSessionDescription* remoteDescription();
+  RTCSessionDescription* currentRemoteDescription();
+  RTCSessionDescription* pendingRemoteDescription();
 
   String signalingState() const;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
index dc3060f..0ff9646a 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
@@ -65,12 +65,12 @@
     [CallWith=ScriptState] Promise<RTCSessionDescription> createAnswer(optional RTCAnswerOptions options);
     [CallWith=ScriptState] Promise<void> setLocalDescription(RTCSessionDescriptionInit description);
     readonly attribute RTCSessionDescription? localDescription;
-    // readonly attribute RTCSessionDescription? currentLocalDescription;
-    // readonly attribute RTCSessionDescription? pendingLocalDescription;
+    readonly attribute RTCSessionDescription? currentLocalDescription;
+    readonly attribute RTCSessionDescription? pendingLocalDescription;
     [CallWith=ScriptState] Promise<void> setRemoteDescription(RTCSessionDescriptionInit description);
     readonly attribute RTCSessionDescription? remoteDescription;
-    // readonly attribute RTCSessionDescription? currentRemoteDescription;
-    // readonly attribute RTCSessionDescription? pendingRemoteDescription;
+    readonly attribute RTCSessionDescription? currentRemoteDescription;
+    readonly attribute RTCSessionDescription? pendingRemoteDescription;
     [CallWith=ScriptState, RaisesException, MeasureAs=RTCPeerConnectionAddIceCandidatePromise] Promise<void> addIceCandidate((RTCIceCandidateInit or RTCIceCandidate) candidate);
     readonly attribute RTCSignalingState signalingState;
     readonly attribute RTCIceGatheringState iceGatheringState;
diff --git a/third_party/blink/renderer/modules/webusb/usb.cc b/third_party/blink/renderer/modules/webusb/usb.cc
index 51068379..471f0e64 100644
--- a/third_party/blink/renderer/modules/webusb/usb.cc
+++ b/third_party/blink/renderer/modules/webusb/usb.cc
@@ -61,11 +61,10 @@
     : ContextLifecycleObserver(&context), client_binding_(this) {}
 
 USB::~USB() {
-  // |service_| and |chooser_service_| may still be valid but there
-  // should be no more outstanding requests to them because each holds a
-  // persistent handle to this object.
-  DCHECK(service_requests_.IsEmpty());
-  DCHECK(chooser_service_requests_.IsEmpty());
+  // |service_| may still be valid but there should be no more outstanding
+  // requests to them because each holds a persistent handle to this object.
+  DCHECK(get_devices_requests_.IsEmpty());
+  DCHECK(get_permission_requests_.IsEmpty());
 }
 
 void USB::Dispose() {
@@ -88,7 +87,7 @@
 
   EnsureServiceConnection();
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
-  service_requests_.insert(resolver);
+  get_devices_requests_.insert(resolver);
   service_->GetDevices(WTF::Bind(&USB::OnGetDevices, WrapPersistent(this),
                                  WrapPersistent(resolver)));
   return resolver->Promise();
@@ -109,12 +108,7 @@
                                            kFeaturePolicyBlocked));
   }
 
-  if (!chooser_service_) {
-    GetFrame()->GetInterfaceProvider().GetInterface(
-        mojo::MakeRequest(&chooser_service_));
-    chooser_service_.set_connection_error_handler(WTF::Bind(
-        &USB::OnChooserServiceConnectionError, WrapWeakPersistent(this)));
-  }
+  EnsureServiceConnection();
 
   if (!Frame::HasTransientUserActivation(frame)) {
     return ScriptPromise::RejectWithDOMException(
@@ -132,9 +126,9 @@
   }
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
-  chooser_service_requests_.insert(resolver);
-  chooser_service_->GetPermission(
-      std::move(filters), WTF::Bind(&USB::OnGetPermission, WrapPersistent(this),
+  get_permission_requests_.insert(resolver);
+  service_->GetPermission(std::move(filters),
+                          WTF::Bind(&USB::OnGetPermission, WrapPersistent(this),
                                     WrapPersistent(resolver)));
   return resolver->Promise();
 }
@@ -149,9 +143,8 @@
 
 void USB::ContextDestroyed(ExecutionContext*) {
   service_.reset();
-  service_requests_.clear();
-  chooser_service_.reset();
-  chooser_service_requests_.clear();
+  get_devices_requests_.clear();
+  get_permission_requests_.clear();
 }
 
 USBDevice* USB::GetOrCreateDevice(UsbDeviceInfoPtr device_info) {
@@ -169,24 +162,18 @@
 
 void USB::OnGetDevices(ScriptPromiseResolver* resolver,
                        Vector<UsbDeviceInfoPtr> device_infos) {
-  auto request_entry = service_requests_.find(resolver);
-  if (request_entry == service_requests_.end())
-    return;
-  service_requests_.erase(request_entry);
+  DCHECK(get_devices_requests_.Contains(resolver));
 
   HeapVector<Member<USBDevice>> devices;
   for (auto& device_info : device_infos)
     devices.push_back(GetOrCreateDevice(std::move(device_info)));
   resolver->Resolve(devices);
-  service_requests_.erase(resolver);
+  get_devices_requests_.erase(resolver);
 }
 
 void USB::OnGetPermission(ScriptPromiseResolver* resolver,
                           UsbDeviceInfoPtr device_info) {
-  auto request_entry = chooser_service_requests_.find(resolver);
-  if (request_entry == chooser_service_requests_.end())
-    return;
-  chooser_service_requests_.erase(request_entry);
+  DCHECK(get_permission_requests_.Contains(resolver));
 
   EnsureServiceConnection();
 
@@ -196,6 +183,7 @@
     resolver->Reject(DOMException::Create(DOMExceptionCode::kNotFoundError,
                                           kNoDeviceSelected));
   }
+  get_permission_requests_.erase(resolver);
 }
 
 void USB::OnDeviceAdded(UsbDeviceInfoPtr device_info) {
@@ -221,18 +209,15 @@
 void USB::OnServiceConnectionError() {
   service_.reset();
   client_binding_.Close();
-  for (ScriptPromiseResolver* resolver : service_requests_)
+  for (ScriptPromiseResolver* resolver : get_devices_requests_)
     resolver->Resolve(HeapVector<Member<USBDevice>>(0));
-  service_requests_.clear();
-}
+  get_devices_requests_.clear();
 
-void USB::OnChooserServiceConnectionError() {
-  chooser_service_.reset();
-  for (ScriptPromiseResolver* resolver : chooser_service_requests_) {
+  for (ScriptPromiseResolver* resolver : get_permission_requests_) {
     resolver->Reject(DOMException::Create(DOMExceptionCode::kNotFoundError,
                                           kNoDeviceSelected));
   }
-  chooser_service_requests_.clear();
+  get_permission_requests_.clear();
 }
 
 void USB::AddedEventListener(const AtomicString& event_type,
@@ -290,8 +275,8 @@
 }
 
 void USB::Trace(blink::Visitor* visitor) {
-  visitor->Trace(service_requests_);
-  visitor->Trace(chooser_service_requests_);
+  visitor->Trace(get_devices_requests_);
+  visitor->Trace(get_permission_requests_);
   visitor->Trace(device_cache_);
   EventTargetWithInlineData::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/webusb/usb.h b/third_party/blink/renderer/modules/webusb/usb.h
index 7b494980..9c871d9 100644
--- a/third_party/blink/renderer/modules/webusb/usb.h
+++ b/third_party/blink/renderer/modules/webusb/usb.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBUSB_USB_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBUSB_USB_H_
 
-#include "device/usb/public/mojom/chooser_service.mojom-blink.h"
 #include "device/usb/public/mojom/device_manager.mojom-blink.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom-blink.h"
@@ -66,7 +65,6 @@
   void OnDeviceRemoved(device::mojom::blink::UsbDeviceInfoPtr) override;
 
   void OnServiceConnectionError();
-  void OnChooserServiceConnectionError();
 
   void Trace(blink::Visitor*) override;
 
@@ -84,9 +82,8 @@
   bool IsFeatureEnabled() const;
 
   mojom::blink::WebUsbServicePtr service_;
-  HeapHashSet<Member<ScriptPromiseResolver>> service_requests_;
-  device::mojom::blink::UsbChooserServicePtr chooser_service_;
-  HeapHashSet<Member<ScriptPromiseResolver>> chooser_service_requests_;
+  HeapHashSet<Member<ScriptPromiseResolver>> get_devices_requests_;
+  HeapHashSet<Member<ScriptPromiseResolver>> get_permission_requests_;
   mojo::Binding<device::mojom::blink::UsbDeviceManagerClient> client_binding_;
   HeapHashMap<String, WeakMember<USBDevice>> device_cache_;
 };
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index b0bdd99..5b18e7b 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -598,7 +598,6 @@
     "feature_policy/feature_policy.h",
     "file_metadata.cc",
     "file_metadata.h",
-    "file_system_type.h",
     "fonts/alternate_font_family.h",
     "fonts/android/font_cache_android.cc",
     "fonts/bitmap_glyphs_blacklist.cc",
diff --git a/third_party/blink/renderer/platform/async_file_system_callbacks.h b/third_party/blink/renderer/platform/async_file_system_callbacks.h
index 94b1cbf..5a6c646 100644
--- a/third_party/blink/renderer/platform/async_file_system_callbacks.h
+++ b/third_party/blink/renderer/platform/async_file_system_callbacks.h
@@ -32,10 +32,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ASYNC_FILE_SYSTEM_CALLBACKS_H_
 
 #include <memory>
+#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/public/platform/web_file_writer.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
 #include "third_party/blink/renderer/platform/file_metadata.h"
-#include "third_party/blink/renderer/platform/file_system_type.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/noncopyable.h"
@@ -61,7 +61,7 @@
   // Called when a filesystem URL is resolved.
   virtual void DidResolveURL(const String& name,
                              const KURL& root_url,
-                             FileSystemType,
+                             mojom::blink::FileSystemType,
                              const String& file_path,
                              bool is_directory) {
     NOTREACHED();
diff --git a/third_party/blink/renderer/platform/exported/web_file_system_callbacks.cc b/third_party/blink/renderer/platform/exported/web_file_system_callbacks.cc
index e4858aa4..c02b1ae1 100644
--- a/third_party/blink/renderer/platform/exported/web_file_system_callbacks.cc
+++ b/third_party/blink/renderer/platform/exported/web_file_system_callbacks.cc
@@ -139,9 +139,9 @@
                                            const WebString& file_path,
                                            bool is_directory) {
   DCHECK(!private_.IsNull());
-  private_->Callbacks()->DidResolveURL(name, root_url,
-                                       static_cast<FileSystemType>(type),
-                                       file_path, is_directory);
+  private_->Callbacks()->DidResolveURL(
+      name, root_url, static_cast<mojom::blink::FileSystemType>(type),
+      file_path, is_directory);
   private_.Reset();
 }
 
diff --git a/third_party/blink/renderer/platform/file_system_type.h b/third_party/blink/renderer/platform/file_system_type.h
deleted file mode 100644
index 2e545dea..0000000
--- a/third_party/blink/renderer/platform/file_system_type.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_SYSTEM_TYPE_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_SYSTEM_TYPE_H_
-
-namespace blink {
-
-// For file system types used in FileSystem API.
-//
-// WARNING: These enumerators can be serialized to disk (with IndexedDB).
-// If you have to update this list, also modify deserialization logic to handle
-// the previous version of this enum.
-enum FileSystemType {
-  kFileSystemTypeTemporary,
-  kFileSystemTypePersistent,
-
-  // Transient isolated non-sandboxed filesystem.
-  kFileSystemTypeIsolated,
-
-  // Non-sandbox filesystem.
-  kFileSystemTypeExternal,
-
-  kFileSystemTypeLast = kFileSystemTypeExternal,
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_SYSTEM_TYPE_H_
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index aeb798fa..a96e2bb8 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -586,6 +586,10 @@
       status: "stable",
     },
     {
+      name: "InvisibleDOM",
+      status: "experimental",
+    },
+    {
       name: "IsolatedCodeCache",
       status: "experimental",
     },
@@ -715,7 +719,7 @@
     },
     {
       name: "MediaStreamTrackContentHint",
-      status: "experimental",
+      status: "stable",
     },
     // This is enabled by default on Windows only. The only part that's
     // "experimental" is the support on other platforms.
@@ -1019,10 +1023,6 @@
       name: "RestrictAppCacheToSecureContexts",
       status: "stable",
     },
-    {
-      name: "RestrictCanRequestURLCharacterSet",
-      status: "stable",
-    },
     // Enables the use of the RTCIceTransport with extensions.
     {
       name: "RTCIceTransportExtension",
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
index db69771..562bb31f 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
@@ -133,6 +133,26 @@
   return WebRTCSessionDescription();
 }
 
+WebRTCSessionDescription
+MockWebRTCPeerConnectionHandler::CurrentLocalDescription() {
+  return WebRTCSessionDescription();
+}
+
+WebRTCSessionDescription
+MockWebRTCPeerConnectionHandler::CurrentRemoteDescription() {
+  return WebRTCSessionDescription();
+}
+
+WebRTCSessionDescription
+MockWebRTCPeerConnectionHandler::PendingLocalDescription() {
+  return WebRTCSessionDescription();
+}
+
+WebRTCSessionDescription
+MockWebRTCPeerConnectionHandler::PendingRemoteDescription() {
+  return WebRTCSessionDescription();
+}
+
 webrtc::RTCErrorType MockWebRTCPeerConnectionHandler::SetConfiguration(
     const WebRTCConfiguration&) {
   return webrtc::RTCErrorType::NONE;
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
index 091f79d..85571042 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
@@ -34,6 +34,10 @@
                             const WebRTCSessionDescription&) override;
   WebRTCSessionDescription LocalDescription() override;
   WebRTCSessionDescription RemoteDescription() override;
+  WebRTCSessionDescription CurrentLocalDescription() override;
+  WebRTCSessionDescription CurrentRemoteDescription() override;
+  WebRTCSessionDescription PendingLocalDescription() override;
+  WebRTCSessionDescription PendingRemoteDescription() override;
   webrtc::RTCErrorType SetConfiguration(const WebRTCConfiguration&) override;
   void GetStats(const WebRTCStatsRequest&) override;
   void GetStats(std::unique_ptr<WebRTCStatsReportCallback>) override;
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.cc b/third_party/blink/renderer/platform/weborigin/security_policy.cc
index f2a20bc..a61a1f9 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.cc
@@ -89,36 +89,6 @@
                                    : OriginAccessEntry::kDisallowSubdomains));
 }
 
-static void RemoveOriginAccessEntry(const SecurityOrigin& source_origin,
-                                    const String& destination_protocol,
-                                    const String& destination_domain,
-                                    bool allow_destination_subdomains,
-                                    OriginAccessMap& access_map) {
-  DCHECK(IsMainThread());
-  DCHECK(!source_origin.IsOpaque());
-  if (source_origin.IsOpaque())
-    return;
-
-  String source_string = source_origin.ToString();
-  OriginAccessMap::iterator it = access_map.find(source_string);
-  if (it == access_map.end())
-    return;
-
-  OriginAccessList* list = it->value.get();
-  size_t index = list->Find(OriginAccessEntry(
-      destination_protocol, destination_domain,
-      allow_destination_subdomains ? OriginAccessEntry::kAllowSubdomains
-                                   : OriginAccessEntry::kDisallowSubdomains));
-
-  if (index == kNotFound)
-    return;
-
-  list->EraseAt(index);
-
-  if (list->IsEmpty())
-    access_map.erase(it);
-}
-
 static void RemoveAllOriginAccessEntriesForOrigin(
     const SecurityOrigin& source_origin,
     OriginAccessMap& access_map) {
@@ -322,16 +292,6 @@
                        GetOriginAccessWhitelistMap());
 }
 
-void SecurityPolicy::RemoveOriginAccessWhitelistEntry(
-    const SecurityOrigin& source_origin,
-    const String& destination_protocol,
-    const String& destination_domain,
-    bool allow_destination_subdomains) {
-  RemoveOriginAccessEntry(source_origin, destination_protocol,
-                          destination_domain, allow_destination_subdomains,
-                          GetOriginAccessWhitelistMap());
-}
-
 void SecurityPolicy::RemoveAllOriginAccessWhitelistEntriesForOrigin(
     const SecurityOrigin& source_origin) {
   RemoveAllOriginAccessEntriesForOrigin(source_origin,
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.h b/third_party/blink/renderer/platform/weborigin/security_policy.h
index d2a61b5..96b85c9 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.h
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.h
@@ -69,11 +69,6 @@
                                             const String& destination_protocol,
                                             const String& destination_domain,
                                             bool allow_destination_subdomains);
-  static void RemoveOriginAccessWhitelistEntry(
-      const SecurityOrigin& source_origin,
-      const String& destination_protocol,
-      const String& destination_domain,
-      bool allow_destination_subdomains);
   static void RemoveAllOriginAccessWhitelistEntriesForOrigin(
       const SecurityOrigin& source_origin);
   static void ResetOriginAccessWhitelists();
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
index a31ecf3..3139432 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
@@ -356,9 +356,8 @@
   EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
                                                    http_example_origin()));
 
-  // Removing the entry should revoke access.
-  SecurityPolicy::RemoveOriginAccessWhitelistEntry(
-      *https_chromium_origin(), "https", "example.com", false);
+  // Clearing the map should revoke all special access.
+  SecurityPolicy::ResetOriginAccessWhitelists();
   EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
                                                    https_example_origin()));
   EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
@@ -376,15 +375,6 @@
                                                   https_sub_example_origin()));
   EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
                                                    http_example_origin()));
-
-  // Clearing the map should revoke all special access.
-  SecurityPolicy::ResetOriginAccessWhitelists();
-  EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
-                                                   https_example_origin()));
-  EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
-                                                   https_sub_example_origin()));
-  EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
-                                                   http_example_origin()));
 }
 
 TEST_F(SecurityPolicyAccessTest, IsAccessWhiteListedWildCard) {
diff --git a/third_party/proguard/README.chromium b/third_party/proguard/README.chromium
index 8ec4a82..14b6982 100644
--- a/third_party/proguard/README.chromium
+++ b/third_party/proguard/README.chromium
@@ -10,6 +10,3 @@
 This directory includes proguard.jar to allow Chromium to shrink, optimize java
 classes for Android, as well as retrace.jar, to allow for deobfuscation of stack
 traces.
-
-Note: 6.0.3 is used for monochrome_public_apk to avoid hitting the dex method
-limit (crbug.com/857572).
diff --git a/third_party/proguard/lib/proguard.6.0.3.jar b/third_party/proguard/lib/proguard.6.0.3.jar
deleted file mode 100644
index a09a6071..0000000
--- a/third_party/proguard/lib/proguard.6.0.3.jar
+++ /dev/null
Binary files differ
diff --git a/tools/binary_size/libsupersize/static/tree-ui.js b/tools/binary_size/libsupersize/static/tree-ui.js
index 7acdd415..c22f789 100644
--- a/tools/binary_size/libsupersize/static/tree-ui.js
+++ b/tools/binary_size/libsupersize/static/tree-ui.js
@@ -149,6 +149,10 @@
    * @param {KeyboardEvent} event Event passed from keydown event listener.
    */
   function _handleKeyNavigation(event) {
+    if (event.altKey || event.ctrlKey || event.metaKey) {
+      return;
+    }
+
     /**
      * @type {HTMLAnchorElement | HTMLSpanElement} Tree node element, either
      * a tree or leaf. Trees use `<a>` tags, leaves use `<span>` tags.
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 258ccb87..df52ce48 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -277,8 +277,8 @@
     'chromium.infra.codesearch': {
       'codesearch-gen-chromium-android': 'codesearch_gen_chromium_android_bot',
       'codesearch-gen-chromium-chromiumos': 'codesearch_gen_chromium_chromiumos_bot',
-      'codesearch-gen-chromium-linux': 'codesearch_gen_chromium_linux_bot',
-      'codesearch-gen-chromium-win': 'codesearch_gen_chromium_win_bot',
+      'codesearch-gen-chromium-linux': 'codesearch_gen_chromium_bot',
+      'codesearch-gen-chromium-win': 'codesearch_gen_chromium_bot',
     },
 
     'chromium.linux': {
@@ -1119,19 +1119,15 @@
     ],
 
     'codesearch_gen_chromium_android_bot': [
-      'goma', 'clang', 'shared', 'debug', 'minimal_symbols', 'arm', 'android_without_codecs',
+      'codesearch', 'android_without_codecs',
     ],
 
     'codesearch_gen_chromium_chromiumos_bot': [
-      'goma', 'clang', 'shared', 'debug', 'minimal_symbols', 'x64', 'chromeos',
+      'codesearch', 'chromeos',
     ],
 
-    'codesearch_gen_chromium_linux_bot': [
-      'goma', 'clang', 'shared', 'debug', 'minimal_symbols', 'x64',
-    ],
-
-    'codesearch_gen_chromium_win_bot': [
-      'goma', 'clang', 'shared', 'debug', 'minimal_symbols', 'x64',
+    'codesearch_gen_chromium_bot': [
+      'codesearch',
     ],
 
     # The 'cros_chrome_sdk_* configs are placeholders that indicate
@@ -1731,6 +1727,12 @@
       'gn_args': 'use_cxx11=true',
     },
 
+    # Settings used by the codesearch builders to generate cross-references.
+    'codesearch': {
+      'gn_args': 'clang_use_chrome_plugins=false',
+      'mixins': ['goma', 'clang', 'shared', 'debug', 'minimal_symbols'],
+    },
+
     'compile_only': {
       'mixins': ['no_symbols'],
     },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 93b735c..1080cd3 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3871,6 +3871,11 @@
   <int value="1" label="Active"/>
 </enum>
 
+<enum name="BooleanAddingBlacklistedDuplicatesPrevented">
+  <int value="0" label="Not prevented"/>
+  <int value="1" label="Prevented"/>
+</enum>
+
 <enum name="BooleanAllowed">
   <int value="0" label="Not Allowed"/>
   <int value="1" label="Allowed"/>
@@ -4451,6 +4456,11 @@
   <int value="1" label="Needs to be clearred"/>
 </enum>
 
+<enum name="BooleanNeedsDeDuplication">
+  <int value="0" label="Doesn't need duplications clearing"/>
+  <int value="1" label="Needs to be duplications cleared"/>
+</enum>
+
 <enum name="BooleanNetworkQuietBeforeSwap">
   <int value="0" label="FMP OK, network not yet quiet"/>
   <int value="1" label="FMP not OK, network was already quiet"/>
@@ -23376,6 +23386,15 @@
   <int value="3" label="active, was active"/>
 </enum>
 
+<enum name="GpuTerminationOrigin">
+  <summary>
+    Return status re-encoded values from GpuTerminationOrigin as defined in
+    content/browser/gpu/gpu_process_host.h enum GpuTerminationOrigin.
+  </summary>
+  <int value="0" label="Unknown Origin">UNKNOWN_ORIGIN</int>
+  <int value="1" label="Ozone Wayland Proxy">OZONE_WAYLAND_PROXY</int>
+</enum>
+
 <enum name="GpuTerminationStatus">
   <summary>
     Return status re-encoded values from GetTerminationStatus as defined in
@@ -41185,11 +41204,11 @@
   <int value="593735191" label="blink.mojom.PresentationService"/>
   <int value="620853817" label="media.mojom.Renderer"/>
   <int value="670026296" label="blink.mojom.CacheStorage"/>
-  <int value="803397464" label="device.mojom.UsbDeviceManager"/>
   <int value="833468074" label="device.mojom.VRService"/>
   <int value="846414148" label="blink.mojom.MediaDevicesDispatcherHost"/>
   <int value="917382314" label="blink.mojom.TextSuggestionHost"/>
   <int value="917568449" label="media.mojom.ImageCapture"/>
+  <int value="948847120" label="blink.mojom.WebUsbService"/>
   <int value="968027799" label="blink.mojom.MediaSessionService"/>
   <int value="996732224" label="autofill.mojom.PasswordManagerDriver"/>
   <int value="1071268620" label="blink.mojom.QuotaDispatcherHost"/>
@@ -41217,7 +41236,6 @@
       label="content.mojom.RendererAudioInputStreamFactory"/>
   <int value="1729889736"
       label="discardable_memory.mojom.DiscardableSharedMemoryManager"/>
-  <int value="1737293395" label="device.mojom.UsbChooserService"/>
   <int value="1750772802" label="blink.mojom.NotificationService"/>
   <int value="1769429998" label="blink.mojom.PrefetchURLLoaderService"/>
   <int value="1785235899" label="shape_detection.mojom.BarcodeDetection"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 3251a34..709a767 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -13830,6 +13830,29 @@
   </summary>
 </histogram>
 
+<histogram name="ContentSettings.ExtensionEmbeddedSettingSet"
+    enum="ContentType">
+  <owner>raymes@chromium.org</owner>
+  <summary>
+    Count of how often a specific content setting is set for an embedded URL by
+    an extension. Only counts settings when the secondary pattern is different
+    to the primary pattern. Exactly one of this or
+    ContentSettings.ExtensionNonEmbeddedSettingSet will be emitted per call to
+    contentSettings.set(), if the arguments to the call are valid.
+  </summary>
+</histogram>
+
+<histogram name="ContentSettings.ExtensionNonEmbeddedSettingSet"
+    enum="ContentType">
+  <owner>raymes@chromium.org</owner>
+  <summary>
+    Count of how often a specific content setting is set when only a single
+    pattern is specified. Exactly one of this or
+    ContentSettings.ExtensionEmbeddedSettingSet will be emitted per call to
+    contentSettings.set(), if the arguments to the call are valid.
+  </summary>
+</histogram>
+
 <histogram name="ContentSettings.ImagePressed" enum="ContentSettingImageType">
   <owner>calamity@chromium.org</owner>
   <summary>
@@ -33456,6 +33479,19 @@
   </summary>
 </histogram>
 
+<histogram name="GPU.GPUProcessTerminationOrigin" enum="GpuTerminationOrigin"
+    expires_after="2019-08-14">
+  <owner>rjkroege@chromium.org</owner>
+  <owner>msisov@igalia.com</owner>
+  <summary>
+    The reason a GPU process is terminated. It works only when
+    TERMINATION_STATUS_PROCESS_WAS_KILLED TerminationStatus is set. The goal of
+    this histogram is to get spikes of the above mentioned case when
+    Ozone/Wayland terminates the GPU process due to invalid data it received if
+    any.
+  </summary>
+</histogram>
+
 <histogram name="GPU.GPUProcessTerminationStatus" enum="TerminationStatus">
   <obsolete>
     Deprecated April 2018. Replaced by GPU.GPUProcessTerminationStatus2.
@@ -70204,6 +70240,17 @@
   </summary>
 </histogram>
 
+<histogram
+    name="PasswordManager.BlacklistedSites.NeedRemoveBlacklistDuplicates"
+    enum="BooleanNeedsDeDuplication" expires_after="M72">
+  <owner>gemene@google.com</owner>
+  <owner>jdoerrie@chromium.org</owner>
+  <summary>
+    Records once on startup whether the blacklisted sites in the password store
+    need to be cleared of duplications.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.BlacklistedSites.NeedToBeCleaned"
     enum="BooleanNeedsClearing">
   <owner>vasilii@chromium.org</owner>
@@ -70214,6 +70261,16 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.BlacklistedSites.PreventedAddingDuplicates"
+    enum="BooleanAddingBlacklistedDuplicatesPrevented" expires_after="M72">
+  <owner>gemene@google.com</owner>
+  <owner>jdoerrie@chromium.org</owner>
+  <summary>
+    Boolean indicating whether adding a blacklist entry was prevented due to an
+    already existing entry. Recorded after every blacklist site submission.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.CertificateErrorsWhileSeeingForms"
     enum="PasswordCertificateError">
   <owner>battre@chromium.org</owner>
@@ -70417,6 +70474,39 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.HttpCredentialsWithConflictingHttpsCredential"
+    units="saved credentials" expires_after="M72">
+  <owner>gemene@google.com</owner>
+  <owner>jdoerrie@chromium.org</owner>
+  <summary>
+    Number of HTTP credentials, with HSTS enabled as given in the histogram
+    suffix, for which a conflicting (i.e.same host and username, but different
+    password) HTTPS credential exists. Recorded once for the profile on startup.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.HttpCredentialsWithEquivalentHttpsCredential"
+    units="saved credentials" expires_after="M72">
+  <owner>gemene@google.com</owner>
+  <owner>jdoerrie@chromium.org</owner>
+  <summary>
+    Number of HTTP credentials, with HSTS enabled as given in the histogram
+    suffix, for which an equivalent (i.e. same host, username and password)
+    HTTPS credential exists. Recorded once for the profile on startup.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.HttpCredentialsWithoutMatchingHttpsCredential"
+    units="saved credentials" expires_after="M72">
+  <owner>gemene@google.com</owner>
+  <owner>jdoerrie@chromium.org</owner>
+  <summary>
+    Number of HTTP credential, with HSTS enabled as given in the histogram
+    suffix, for which no HTTPS credential for the same username exists. Recorded
+    once for the profile on startup.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.HttpPasswordMigrationCount"
     units="saved credentials">
   <owner>vasilii@chromium.org</owner>
@@ -120669,6 +120759,17 @@
   <affected-histogram name="Histogram.InconsistentSnapshotBrowser"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="HstsState" separator=".">
+  <suffix name="HSTSNotEnabled" label="The HSTS is not enabled."/>
+  <suffix name="WithHSTSEnabled" label="The HSTS is enabled."/>
+  <affected-histogram
+      name="PasswordManager.HttpCredentialsWithConflictingHttpsCredential"/>
+  <affected-histogram
+      name="PasswordManager.HttpCredentialsWithEquivalentHttpsCredential"/>
+  <affected-histogram
+      name="PasswordManager.HttpCredentialsWithoutMatchingHttpsCredential"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="HttpCacheAccessToDoneCases" separator=".">
   <suffix name="SentRequest" label="The request was sent over the network."/>
   <suffix name="Used"
diff --git a/tools/perf/chrome_telemetry_build/BUILD.gn b/tools/perf/chrome_telemetry_build/BUILD.gn
index 64598f0..3860bb3 100644
--- a/tools/perf/chrome_telemetry_build/BUILD.gn
+++ b/tools/perf/chrome_telemetry_build/BUILD.gn
@@ -204,6 +204,7 @@
     "//third_party/catapult/telemetry/telemetry/benchmark_runner.py",
     "//third_party/catapult/telemetry/telemetry/benchmark_runner_unittest.py",
     "//third_party/catapult/telemetry/telemetry/benchmark_unittest.py",
+    "//third_party/catapult/telemetry/telemetry/compact_mode_options.py",
     "//third_party/catapult/telemetry/telemetry/decorators.py",
     "//third_party/catapult/telemetry/telemetry/decorators_unittest.py",
     "//third_party/catapult/telemetry/telemetry/project_config.py",
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 943ef7a..a37737b4 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -160,6 +160,25 @@
         'device_os': 'O',
         'device_os_flavor': 'google',
       },
+    },
+    'android-go_webview-perf': {
+      'tests': [
+        {
+          'isolate': 'performance_webview_test_suite',
+          'extra_args': [
+              '--test-shard-map-filename=android_go_webview_shard_map.json',
+          ],
+          'num_shards': 25
+        }
+      ],
+      'platform': 'android-webview',
+      'dimension': {
+        'pool': 'chrome.tests.perf-webview',
+        'os': 'Android',
+        'device_type': 'gobo',
+        'device_os': 'O',
+        'device_os_flavor': 'google',
+      },
     }
   }
 }
diff --git a/tools/perf/core/shard_maps/android_go_webview_shard_map.json b/tools/perf/core/shard_maps/android_go_webview_shard_map.json
new file mode 100644
index 0000000..8e128883
--- /dev/null
+++ b/tools/perf/core/shard_maps/android_go_webview_shard_map.json
@@ -0,0 +1,208 @@
+{
+    "0": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "end": 13
+            }
+        }
+    },
+    "1": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "begin": 13,
+                "end": 19
+            }
+        }
+    },
+    "2": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "begin": 19,
+                "end": 37
+            }
+        }
+    },
+    "3": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "begin": 37,
+                "end": 54
+            }
+        }
+    },
+    "4": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "begin": 54
+            }
+        }
+    },
+    "5": {
+        "benchmarks": {
+            "memory.top_10_mobile": {
+                "end": 7
+            }
+        }
+    },
+    "6": {
+        "benchmarks": {
+            "memory.top_10_mobile": {
+                "begin": 7,
+                "end": 13
+            }
+        }
+    },
+    "7": {
+        "benchmarks": {
+            "memory.top_10_mobile": {
+                "begin": 13
+            }
+        }
+    },
+    "8": {
+        "benchmarks": {
+            "start_with_url.warm.startup_pages": {}
+        }
+    },
+    "9": {
+        "benchmarks": {
+            "start_with_url.cold.startup_pages": {},
+            "system_health.memory_mobile": {
+                "end": 6
+            }
+        }
+    },
+    "10": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 6,
+                "end": 12
+            }
+        }
+    },
+    "11": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 12,
+                "end": 14
+            }
+        }
+    },
+    "12": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 14,
+                "end": 17
+            }
+        }
+    },
+    "13": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 17,
+                "end": 21
+            }
+        }
+    },
+    "14": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 21,
+                "end": 27
+            }
+        }
+    },
+    "15": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 27,
+                "end": 37
+            }
+        }
+    },
+    "16": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 37,
+                "end": 45
+            }
+        }
+    },
+    "17": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 45,
+                "end": 52
+            }
+        }
+    },
+    "18": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 52,
+                "end": 54
+            }
+        }
+    },
+    "19": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 54,
+                "end": 57
+            }
+        }
+    },
+    "20": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 57,
+                "end": 61
+            }
+        }
+    },
+    "21": {
+        "benchmarks": {
+            "system_health.memory_mobile": {
+                "begin": 61
+            },
+            "system_health.webview_startup": {},
+            "power.typical_10_mobile": {
+                "end": 7
+            }
+        }
+    },
+    "22": {
+        "benchmarks": {
+            "power.typical_10_mobile": {
+                "begin": 7
+            },
+            "speedometer2": {},
+            "speedometer": {},
+            "v8.browsing_mobile": {
+                "end": 2
+            }
+        }
+    },
+    "23": {
+        "benchmarks": {
+            "v8.browsing_mobile": {
+                "begin": 2,
+                "end": 13
+            }
+        }
+    },
+    "24": {
+        "benchmarks": {
+            "v8.browsing_mobile": {
+                "begin": 13
+            }
+        }
+    },
+    "extra_infos": {
+        "num_stories": 187,
+        "predicted_min_shard_time": 1242.0,
+        "predicted_min_shard_index": 17,
+        "predicted_max_shard_time": 2820.0,
+        "predicted_max_shard_index": 18
+    }
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/android_go_webview_story_timing.json b/tools/perf/core/shard_maps/timing_data/android_go_webview_story_timing.json
new file mode 100644
index 0000000..e19e27f
--- /dev/null
+++ b/tools/perf/core/shard_maps/timing_data/android_go_webview_story_timing.json
@@ -0,0 +1,678 @@
+[
+    {
+        "duration": "24.0",
+        "name": "memory.top_10_mobile/after_http_en_m_wikipedia_org_wiki_Science"
+    },
+    {
+        "duration": "23.0",
+        "name": "memory.top_10_mobile/after_http_m_intl_taobao_com_group_purchase_html"
+    },
+    {
+        "duration": "23.0",
+        "name": "memory.top_10_mobile/after_http_m_youtube_com_results_q_science"
+    },
+    {
+        "duration": "23.0",
+        "name": "memory.top_10_mobile/after_http_search_yahoo_com_search__ylt_p_google"
+    },
+    {
+        "duration": "23.0",
+        "name": "memory.top_10_mobile/after_http_www_amazon_com_gp_aw_s_k_nexus"
+    },
+    {
+        "duration": "23.0",
+        "name": "memory.top_10_mobile/after_http_www_baidu_com_s_word_google"
+    },
+    {
+        "duration": "23.0",
+        "name": "memory.top_10_mobile/after_http_yandex_ru_touchsearch_text_science"
+    },
+    {
+        "duration": "23.0",
+        "name": "memory.top_10_mobile/after_https_m_facebook_com_rihanna"
+    },
+    {
+        "duration": "35.0",
+        "name": "memory.top_10_mobile/after_https_mobile_twitter_com_justinbieber_skip_interstitial_true"
+    },
+    {
+        "duration": "24.0",
+        "name": "memory.top_10_mobile/after_https_www_google_co_uk_hl_en_q_science"
+    },
+    {
+        "duration": "30.0",
+        "name": "memory.top_10_mobile/http_en_m_wikipedia_org_wiki_Science"
+    },
+    {
+        "duration": "27.0",
+        "name": "memory.top_10_mobile/http_m_intl_taobao_com_group_purchase_html"
+    },
+    {
+        "duration": "28.0",
+        "name": "memory.top_10_mobile/http_m_youtube_com_results_q_science"
+    },
+    {
+        "duration": "27.0",
+        "name": "memory.top_10_mobile/http_search_yahoo_com_search__ylt_p_google"
+    },
+    {
+        "duration": "28.0",
+        "name": "memory.top_10_mobile/http_www_amazon_com_gp_aw_s_k_nexus"
+    },
+    {
+        "duration": "29.0",
+        "name": "memory.top_10_mobile/http_www_baidu_com_s_word_google"
+    },
+    {
+        "duration": "28.0",
+        "name": "memory.top_10_mobile/http_yandex_ru_touchsearch_text_science"
+    },
+    {
+        "duration": "28.0",
+        "name": "memory.top_10_mobile/https_m_facebook_com_rihanna"
+    },
+    {
+        "duration": "28.0",
+        "name": "memory.top_10_mobile/https_mobile_twitter_com_justinbieber_skip_interstitial_true"
+    },
+    {
+        "duration": "33.0",
+        "name": "memory.top_10_mobile/https_www_google_co_uk_hl_en_q_science"
+    },
+    {
+        "duration": "75.0",
+        "name": "power.typical_10_mobile/http://de.m.wikipedia.org/wiki/K%C3%B6lner_Dom"
+    },
+    {
+        "duration": "81.0",
+        "name": "power.typical_10_mobile/http://m.chiebukuro.yahoo.co.jp/detail/q10136829180"
+    },
+    {
+        "duration": "70.0",
+        "name": "power.typical_10_mobile/http://m.ebay.com/itm/351157205404"
+    },
+    {
+        "duration": "93.0",
+        "name": "power.typical_10_mobile/http://m.facebook.com/barackobama"
+    },
+    {
+        "duration": "75.0",
+        "name": "power.typical_10_mobile/http://m.huffpost.com/us/entry/6004486"
+    },
+    {
+        "duration": "77.0",
+        "name": "power.typical_10_mobile/http://m.ynet.co.il"
+    },
+    {
+        "duration": "99.0",
+        "name": "power.typical_10_mobile/http://siriuslymeg.tumblr.com/"
+    },
+    {
+        "duration": "72.0",
+        "name": "power.typical_10_mobile/http://wapbaike.baidu.com/"
+    },
+    {
+        "duration": "75.0",
+        "name": "power.typical_10_mobile/http://www.cnn.com/2014/03/31/showbiz/tv/himym-finale/index.html"
+    },
+    {
+        "duration": "84.0",
+        "name": "power.typical_10_mobile/http://www.rg.ru/2014/10/21/cska-site.html"
+    },
+    {
+        "duration": "75.0",
+        "name": "power.typical_10_mobile/https://en.wikipedia.org/wiki/File:Rotating_earth_(large).gif"
+    },
+    {
+        "duration": "39.0",
+        "name": "start_with_url.cold.startup_pages/about:blank"
+    },
+    {
+        "duration": "42.0",
+        "name": "start_with_url.cold.startup_pages/http://bbc.co.uk"
+    },
+    {
+        "duration": "35.0",
+        "name": "start_with_url.warm.startup_pages/about:blank"
+    },
+    {
+        "duration": "36.0",
+        "name": "start_with_url.warm.startup_pages/http://bbc.co.uk"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.common_mobile/background:media:imgur"
+    },
+    {
+        "duration": "66.0",
+        "name": "system_health.common_mobile/background:news:nytimes"
+    },
+    {
+        "duration": "32.0",
+        "name": "system_health.common_mobile/background:search:google"
+    },
+    {
+        "duration": "55.0",
+        "name": "system_health.common_mobile/background:social:facebook"
+    },
+    {
+        "duration": "48.0",
+        "name": "system_health.common_mobile/background:tools:gmail"
+    },
+    {
+        "duration": "209.0",
+        "name": "system_health.common_mobile/browse:chrome:newtab"
+    },
+    {
+        "duration": "54.0",
+        "name": "system_health.common_mobile/browse:chrome:omnibox"
+    },
+    {
+        "duration": "149.0",
+        "name": "system_health.common_mobile/browse:media:facebook_photos"
+    },
+    {
+        "duration": "69.0",
+        "name": "system_health.common_mobile/browse:media:flickr_infinite_scroll"
+    },
+    {
+        "duration": "105.0",
+        "name": "system_health.common_mobile/browse:media:imgur"
+    },
+    {
+        "duration": "156.0",
+        "name": "system_health.common_mobile/browse:media:youtube"
+    },
+    {
+        "duration": "247.0",
+        "name": "system_health.common_mobile/browse:news:cnn"
+    },
+    {
+        "duration": "91.0",
+        "name": "system_health.common_mobile/browse:news:cricbuzz"
+    },
+    {
+        "duration": "77.0",
+        "name": "system_health.common_mobile/browse:news:qq"
+    },
+    {
+        "duration": "84.0",
+        "name": "system_health.common_mobile/browse:news:reddit"
+    },
+    {
+        "duration": "307.0",
+        "name": "system_health.common_mobile/browse:news:toi"
+    },
+    {
+        "duration": "75.0",
+        "name": "system_health.common_mobile/browse:news:washingtonpost"
+    },
+    {
+        "duration": "115.0",
+        "name": "system_health.common_mobile/browse:shopping:amazon"
+    },
+    {
+        "duration": "115.0",
+        "name": "system_health.common_mobile/browse:shopping:avito"
+    },
+    {
+        "duration": "57.0",
+        "name": "system_health.common_mobile/browse:shopping:lazada"
+    },
+    {
+        "duration": "97.0",
+        "name": "system_health.common_mobile/browse:social:facebook"
+    },
+    {
+        "duration": "156.0",
+        "name": "system_health.common_mobile/browse:social:facebook_infinite_scroll"
+    },
+    {
+        "duration": "120.0",
+        "name": "system_health.common_mobile/browse:social:instagram"
+    },
+    {
+        "duration": "126.0",
+        "name": "system_health.common_mobile/browse:social:pinterest_infinite_scroll"
+    },
+    {
+        "duration": "154.0",
+        "name": "system_health.common_mobile/browse:social:tumblr_infinite_scroll"
+    },
+    {
+        "duration": "75.0",
+        "name": "system_health.common_mobile/browse:social:twitter"
+    },
+    {
+        "duration": "99.0",
+        "name": "system_health.common_mobile/browse:tech:discourse_infinite_scroll"
+    },
+    {
+        "duration": "68.0",
+        "name": "system_health.common_mobile/browse:tools:maps"
+    },
+    {
+        "duration": "26.0",
+        "name": "system_health.common_mobile/load:chrome:blank"
+    },
+    {
+        "duration": "37.0",
+        "name": "system_health.common_mobile/load:games:bubbles"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.common_mobile/load:games:lazors"
+    },
+    {
+        "duration": "50.0",
+        "name": "system_health.common_mobile/load:games:spychase"
+    },
+    {
+        "duration": "37.0",
+        "name": "system_health.common_mobile/load:media:dailymotion"
+    },
+    {
+        "duration": "32.0",
+        "name": "system_health.common_mobile/load:media:facebook_photos"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.common_mobile/load:media:google_images"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.common_mobile/load:media:imgur"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.common_mobile/load:media:soundcloud"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.common_mobile/load:media:youtube"
+    },
+    {
+        "duration": "69.0",
+        "name": "system_health.common_mobile/load:news:cnn"
+    },
+    {
+        "duration": "47.0",
+        "name": "system_health.common_mobile/load:news:irctc"
+    },
+    {
+        "duration": "41.0",
+        "name": "system_health.common_mobile/load:news:nytimes"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.common_mobile/load:news:qq"
+    },
+    {
+        "duration": "33.0",
+        "name": "system_health.common_mobile/load:news:reddit"
+    },
+    {
+        "duration": "36.0",
+        "name": "system_health.common_mobile/load:news:washingtonpost"
+    },
+    {
+        "duration": "33.0",
+        "name": "system_health.common_mobile/load:news:wikipedia"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.common_mobile/load:search:baidu"
+    },
+    {
+        "duration": "55.0",
+        "name": "system_health.common_mobile/load:search:ebay"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.common_mobile/load:search:google"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.common_mobile/load:search:taobao"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.common_mobile/load:search:yahoo"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.common_mobile/load:search:yandex"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.common_mobile/load:social:twitter"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.common_mobile/load:tools:docs"
+    },
+    {
+        "duration": "36.0",
+        "name": "system_health.common_mobile/load:tools:drive"
+    },
+    {
+        "duration": "28.0",
+        "name": "system_health.common_mobile/load:tools:dropbox"
+    },
+    {
+        "duration": "48.0",
+        "name": "system_health.common_mobile/load:tools:gmail"
+    },
+    {
+        "duration": "32.0",
+        "name": "system_health.common_mobile/load:tools:stackoverflow"
+    },
+    {
+        "duration": "45.0",
+        "name": "system_health.common_mobile/load:tools:weather"
+    },
+    {
+        "duration": "146.0",
+        "name": "system_health.common_mobile/long_running:tools:gmail-background"
+    },
+    {
+        "duration": "145.0",
+        "name": "system_health.common_mobile/long_running:tools:gmail-foreground"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.memory_mobile/background:media:imgur"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.memory_mobile/background:search:google"
+    },
+    {
+        "duration": "37.0",
+        "name": "system_health.memory_mobile/background:social:facebook"
+    },
+    {
+        "duration": "42.0",
+        "name": "system_health.memory_mobile/background:tools:gmail"
+    },
+    {
+        "duration": "180.0",
+        "name": "system_health.memory_mobile/browse:chrome:newtab"
+    },
+    {
+        "duration": "52.0",
+        "name": "system_health.memory_mobile/browse:chrome:omnibox"
+    },
+    {
+        "duration": "100.0",
+        "name": "system_health.memory_mobile/browse:media:facebook_photos"
+    },
+    {
+        "duration": "55.0",
+        "name": "system_health.memory_mobile/browse:media:flickr_infinite_scroll"
+    },
+    {
+        "duration": "81.0",
+        "name": "system_health.memory_mobile/browse:media:imgur"
+    },
+    {
+        "duration": "106.0",
+        "name": "system_health.memory_mobile/browse:media:youtube"
+    },
+    {
+        "duration": "178.0",
+        "name": "system_health.memory_mobile/browse:news:cnn"
+    },
+    {
+        "duration": "68.0",
+        "name": "system_health.memory_mobile/browse:news:cricbuzz"
+    },
+    {
+        "duration": "61.0",
+        "name": "system_health.memory_mobile/browse:news:qq"
+    },
+    {
+        "duration": "79.0",
+        "name": "system_health.memory_mobile/browse:news:reddit"
+    },
+    {
+        "duration": "184.0",
+        "name": "system_health.memory_mobile/browse:news:toi"
+    },
+    {
+        "duration": "59.0",
+        "name": "system_health.memory_mobile/browse:news:washingtonpost"
+    },
+    {
+        "duration": "78.0",
+        "name": "system_health.memory_mobile/browse:shopping:amazon"
+    },
+    {
+        "duration": "94.0",
+        "name": "system_health.memory_mobile/browse:shopping:avito"
+    },
+    {
+        "duration": "47.0",
+        "name": "system_health.memory_mobile/browse:shopping:lazada"
+    },
+    {
+        "duration": "75.0",
+        "name": "system_health.memory_mobile/browse:social:facebook"
+    },
+    {
+        "duration": "98.0",
+        "name": "system_health.memory_mobile/browse:social:facebook_infinite_scroll"
+    },
+    {
+        "duration": "89.0",
+        "name": "system_health.memory_mobile/browse:social:instagram"
+    },
+    {
+        "duration": "94.0",
+        "name": "system_health.memory_mobile/browse:social:pinterest_infinite_scroll"
+    },
+    {
+        "duration": "104.0",
+        "name": "system_health.memory_mobile/browse:social:tumblr_infinite_scroll"
+    },
+    {
+        "duration": "61.0",
+        "name": "system_health.memory_mobile/browse:social:twitter"
+    },
+    {
+        "duration": "82.0",
+        "name": "system_health.memory_mobile/browse:tech:discourse_infinite_scroll"
+    },
+    {
+        "duration": "58.0",
+        "name": "system_health.memory_mobile/browse:tools:maps"
+    },
+    {
+        "duration": "27.0",
+        "name": "system_health.memory_mobile/load:chrome:blank"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.memory_mobile/load:games:bubbles"
+    },
+    {
+        "duration": "28.0",
+        "name": "system_health.memory_mobile/load:games:lazors"
+    },
+    {
+        "duration": "47.0",
+        "name": "system_health.memory_mobile/load:games:spychase"
+    },
+    {
+        "duration": "36.0",
+        "name": "system_health.memory_mobile/load:media:dailymotion"
+    },
+    {
+        "duration": "40.0",
+        "name": "system_health.memory_mobile/load:media:facebook_photos"
+    },
+    {
+        "duration": "32.0",
+        "name": "system_health.memory_mobile/load:media:google_images"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.memory_mobile/load:media:imgur"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.memory_mobile/load:media:soundcloud"
+    },
+    {
+        "duration": "40.0",
+        "name": "system_health.memory_mobile/load:media:youtube"
+    },
+    {
+        "duration": "59.0",
+        "name": "system_health.memory_mobile/load:news:cnn"
+    },
+    {
+        "duration": "37.0",
+        "name": "system_health.memory_mobile/load:news:irctc"
+    },
+    {
+        "duration": "39.0",
+        "name": "system_health.memory_mobile/load:news:nytimes"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.memory_mobile/load:news:qq"
+    },
+    {
+        "duration": "34.0",
+        "name": "system_health.memory_mobile/load:news:reddit"
+    },
+    {
+        "duration": "34.0",
+        "name": "system_health.memory_mobile/load:news:washingtonpost"
+    },
+    {
+        "duration": "33.0",
+        "name": "system_health.memory_mobile/load:news:wikipedia"
+    },
+    {
+        "duration": "40.0",
+        "name": "system_health.memory_mobile/load:search:baidu"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.memory_mobile/load:search:ebay"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.memory_mobile/load:search:google"
+    },
+    {
+        "duration": "31.0",
+        "name": "system_health.memory_mobile/load:search:taobao"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.memory_mobile/load:search:yahoo"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.memory_mobile/load:search:yandex"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.memory_mobile/load:social:twitter"
+    },
+    {
+        "duration": "30.0",
+        "name": "system_health.memory_mobile/load:tools:docs"
+    },
+    {
+        "duration": "33.0",
+        "name": "system_health.memory_mobile/load:tools:drive"
+    },
+    {
+        "duration": "29.0",
+        "name": "system_health.memory_mobile/load:tools:dropbox"
+    },
+    {
+        "duration": "32.0",
+        "name": "system_health.memory_mobile/load:tools:stackoverflow"
+    },
+    {
+        "duration": "41.0",
+        "name": "system_health.memory_mobile/load:tools:weather"
+    },
+    {
+        "duration": "290.0",
+        "name": "system_health.memory_mobile/long_running:tools:gmail-foreground"
+    },
+    {
+        "duration": "141.0",
+        "name": "v8.browsing_mobile/browse:chrome:newtab"
+    },
+    {
+        "duration": "50.0",
+        "name": "v8.browsing_mobile/browse:chrome:omnibox"
+    },
+    {
+        "duration": "146.0",
+        "name": "v8.browsing_mobile/browse:media:facebook_photos"
+    },
+    {
+        "duration": "81.0",
+        "name": "v8.browsing_mobile/browse:media:flickr_infinite_scroll"
+    },
+    {
+        "duration": "125.0",
+        "name": "v8.browsing_mobile/browse:media:imgur"
+    },
+    {
+        "duration": "103.0",
+        "name": "v8.browsing_mobile/browse:news:cricbuzz"
+    },
+    {
+        "duration": "84.0",
+        "name": "v8.browsing_mobile/browse:news:qq"
+    },
+    {
+        "duration": "93.0",
+        "name": "v8.browsing_mobile/browse:news:reddit"
+    },
+    {
+        "duration": "423.0",
+        "name": "v8.browsing_mobile/browse:news:toi"
+    },
+    {
+        "duration": "88.0",
+        "name": "v8.browsing_mobile/browse:news:washingtonpost"
+    },
+    {
+        "duration": "167.0",
+        "name": "v8.browsing_mobile/browse:shopping:amazon"
+    },
+    {
+        "duration": "121.0",
+        "name": "v8.browsing_mobile/browse:social:facebook"
+    },
+    {
+        "duration": "146.0",
+        "name": "v8.browsing_mobile/browse:social:instagram"
+    },
+    {
+        "duration": "82.0",
+        "name": "v8.browsing_mobile/browse:social:twitter"
+    },
+    {
+        "duration": "83.0",
+        "name": "v8.browsing_mobile/browse:tools:maps"
+    },
+    {
+        "duration": "133.0",
+        "name": "speedometer/http://browserbench.org/Speedometer/"
+    },
+    {
+        "duration": "250.0",
+        "name": "speedometer2/Speedometer2"
+    }
+]
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 88a64d1..d38ec837 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -18,6 +18,10 @@
 crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-sub-texture.html [ Skip ]
 crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-texture.html [ Skip ]
 
+# Benchmark: blink_perf.image_decoder
+crbug.com/874810 [ Win ] blink_perf.image_decoder/decode-lossy-webp.html [ Skip ]
+crbug.com/874810 [ Win ] blink_perf.image_decoder/decode-png-palette-opaque.html [ Skip ]
+
 # Benchmark: blink_perf.layout
 crbug.com/551950 [ Android_Svelte ] blink_perf.layout/* [ Skip ]
 crbug.com/832686 [ Nexus_5 ] blink_perf.layout/subtree-detaching.html [ Skip ]
@@ -169,6 +173,7 @@
 
 # Benchmark: system_health.common_desktop
 crbug.com/828917 [ Mac ] system_health.common_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/874803 [ Win_10 ] system_health.common_desktop/multitab:misc:typical24 [ Skip ]
 crbug.com/773084 [ Mac ] system_health.common_desktop/browse:tools:maps [ Skip ]
 crbug.com/839411 [ Win ] system_health.common_desktop/browse:social:twitter_infinite_scroll [ Skip ]
 crbug.com/846022 [ Linux ] system_health.common_desktop/browse:social:twitter_infinite_scroll [ Skip ]
@@ -187,6 +192,7 @@
 crbug.com/649392 [ All ] system_health.memory_desktop/play:media:google_play_music [ Skip ]
 crbug.com/742475 [ Mac ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
 crbug.com/838504 [ Linux ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/874803 [ Win_10 ] system_health.memory_desktop/multitab:misc:typical24 [ Skip ]
 crbug.com/773084 [ Mac ] system_health.memory_desktop/browse:tools:maps [ Skip ]
 crbug.com/664661 [ Mac ] system_health.memory_desktop/load:games:miniclip [ Skip ]
 crbug.com/728464 [ All ] system_health.memory_desktop/browse:social:twitter_infinite_scroll [ Skip ]
@@ -221,7 +227,8 @@
 crbug.com/843547 [ Android_Go ] system_health.memory_mobile/background:news:nytimes [ Skip ]
 crbug.com/852888 [ Nexus_5X ] system_health.memory_mobile/background:news:nytimes [ Skip ]
 crbug.com/859500 [ Nexus_5X ] system_health.memory_mobile/browse:social:tumblr_infinite_scroll [ Skip ]
-crbug.com/871708 [ Android_Go Android_Webview Android_One ] system_health.memory_mobile/browse:social:facebook_infinite_scroll [ Skip ]
+crbug.com/871708 [ Android_Go Android_Webview ] system_health.memory_mobile/browse:social:facebook_infinite_scroll [ Skip ]
+crbug.com/871708 [ Android_One Android_Webview ] system_health.memory_mobile/browse:social:facebook_infinite_scroll [ Skip ]
 
 # Benchmark: tab_switching.typical_25
 crbug.com/747026 [ Mac ] tab_switching.typical_25/multitab:misc:typical24 [ Skip ]
diff --git a/tools/perf/page_sets/system_health/background_stories.py b/tools/perf/page_sets/system_health/background_stories.py
index 16a823c..8a0faca 100644
--- a/tools/perf/page_sets/system_health/background_stories.py
+++ b/tools/perf/page_sets/system_health/background_stories.py
@@ -30,6 +30,7 @@
 class BackgroundGoogleStory(_BackgroundStory):
   NAME = 'background:search:google'
   URL = 'https://www.google.co.uk/#q=tom+cruise+movies'
+  TAGS = [story_tags.HEALTH_CHECK]
 
   def _DidLoadDocument(self, action_runner):
     # Activte the immersive movie browsing experience
@@ -79,11 +80,13 @@
   NAME = 'background:media:imgur'
   URL = 'http://imgur.com/gallery/hUita'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 class BackgroundGmailMobileStory(LoadGmailMobileStory):
   NAME = 'background:tools:gmail'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  TAGS = [story_tags.HEALTH_CHECK]
 
   def _Measure(self, action_runner):
     action_runner.tab.browser.Background()
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index 2a90447..793f9e7 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -111,7 +111,7 @@
   URL = 'http://edition.cnn.com/'
   ITEM_SELECTOR = '.cd__content > h3 > a'
   ITEMS_TO_VISIT = 2
-  TAGS = [story_tags.JAVASCRIPT_HEAVY]
+  TAGS = [story_tags.JAVASCRIPT_HEAVY, story_tags.HEALTH_CHECK]
 
 
 class FacebookMobileStory(_ArticleBrowsingStory):
@@ -182,7 +182,7 @@
   URL = 'http://news.qq.com'
   ITEM_SELECTOR = '.list .full a'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.INTERNATIONAL]
+  TAGS = [story_tags.INTERNATIONAL, story_tags.HEALTH_CHECK]
 
 
 class RedditDesktopStory(_ArticleBrowsingStory):
@@ -403,8 +403,8 @@
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
   IS_SINGLE_PAGE_APP = True
   ITEM_SELECTOR_INDEX = 3
-  TAGS = [story_tags.JAVASCRIPT_HEAVY]
-  TAGS = [story_tags.EMERGING_MARKET]
+  TAGS = [story_tags.JAVASCRIPT_HEAVY, story_tags.EMERGING_MARKET,
+          story_tags.HEALTH_CHECK]
 
 
 class YouTubeDesktopStory(_MediaBrowsingStory):
@@ -552,7 +552,7 @@
   NAME = 'browse:shopping:avito'
   URL = 'https://www.avito.ru/rossiya'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.EMERGING_MARKET]
+  TAGS = [story_tags.EMERGING_MARKET, story_tags.HEALTH_CHECK]
 
   ITEM_SELECTOR = '.item-link'
   ITEMS_TO_VISIT = 4
diff --git a/tools/perf/page_sets/system_health/chrome_stories.py b/tools/perf/page_sets/system_health/chrome_stories.py
index 5d29e7d7..a128420 100644
--- a/tools/perf/page_sets/system_health/chrome_stories.py
+++ b/tools/perf/page_sets/system_health/chrome_stories.py
@@ -13,6 +13,7 @@
   """Story that loads the about:blank page."""
   NAME = 'load:chrome:blank'
   URL = 'about:blank'
+  TAGS = [story_tags.HEALTH_CHECK]
 
   def _DidLoadDocument(self, action_runner):
     # Request a RAF and wait for it to be processed to ensure that the metric
diff --git a/tools/perf/page_sets/system_health/loading_stories.py b/tools/perf/page_sets/system_health/loading_stories.py
index bc48606..39cd941 100644
--- a/tools/perf/page_sets/system_health/loading_stories.py
+++ b/tools/perf/page_sets/system_health/loading_stories.py
@@ -33,7 +33,7 @@
 class LoadBaiduStory(_LoadingStory):
   NAME = 'load:search:baidu'
   URL = 'https://www.baidu.com/s?word=google'
-  TAGS = [story_tags.INTERNATIONAL]
+  TAGS = [story_tags.INTERNATIONAL, story_tags.HEALTH_CHECK]
 
 
 class LoadYahooStory(_LoadingStory):
@@ -59,7 +59,7 @@
   # "ali_trackid" in the URL suppresses "Download app" interstitial.
   URL = 'http://m.intl.taobao.com/?ali_trackid'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.INTERNATIONAL]
+  TAGS = [story_tags.INTERNATIONAL, story_tags.HEALTH_CHECK]
 
 
 class LoadYandexStory(_LoadingStory):
@@ -72,6 +72,7 @@
   NAME = 'load:search:ebay'
   # Redirects to the "http://" version.
   URL = 'https://www.ebay.com/sch/i.html?_nkw=headphones'
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 ################################################################################
@@ -155,6 +156,7 @@
   NAME = 'load:news:nytimes'
   URL = 'http://mobile.nytimes.com'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 class LoadQqMobileStory(_LoadingStory):
@@ -174,12 +176,14 @@
   NAME = 'load:news:reddit'
   URL = 'https://www.reddit.com/r/news/top/?sort=top&t=week'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 class LoadWashingtonPostMobileStory(_LoadingStory):
   NAME = 'load:news:washingtonpost'
   URL = 'https://www.washingtonpost.com/pwa'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  TAGS = [story_tags.HEALTH_CHECK]
   _CLOSE_BUTTON_SELECTOR = '.close'
 
   def _DidLoadDocument(self, action_runner):
@@ -204,7 +208,7 @@
   NAME = 'load:news:irctc'
   URL = 'https://www.irctc.co.in'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
-  TAGS = [story_tags.EMERGING_MARKET]
+  TAGS = [story_tags.EMERGING_MARKET, story_tags.HEALTH_CHECK]
 
 
 ################################################################################
@@ -217,7 +221,7 @@
   NAME = 'load:media:youtube'
   URL = 'https://www.youtube.com/watch?v=QGfhS1hfTWw&autoplay=false'
   PLATFORM_SPECIFIC = True
-  TAGS = [story_tags.EMERGING_MARKET]
+  TAGS = [story_tags.EMERGING_MARKET, story_tags.HEALTH_CHECK]
 
 
 class LoadDailymotionStory(_LoadingStory):
@@ -231,6 +235,7 @@
 class LoadGoogleImagesStory(_LoadingStory):
   NAME = 'load:media:google_images'
   URL = 'https://www.google.co.uk/search?tbm=isch&q=love'
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 class LoadSoundCloudStory(_LoadingStory):
@@ -249,6 +254,7 @@
 class LoadImgurStory(_LoadingStory):
   NAME = 'load:media:imgur'
   URL = 'http://imgur.com/gallery/5UlBN'
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 class LoadFacebookPhotosMobileStory(_LoadingStory):
@@ -280,6 +286,7 @@
   NAME = 'load:tools:docs'
   URL = (
       'https://docs.google.com/document/d/1GvzDP-tTLmJ0myRhUAfTYWs3ZUFilUICg8psNHyccwQ/edit?usp=sharing')
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 class _LoadGmailBaseStory(_LoadingStory):
@@ -327,6 +334,7 @@
   NAME = 'load:tools:stackoverflow'
   URL = (
       'https://stackoverflow.com/questions/36827659/compiling-an-application-for-use-in-highly-radioactive-environments')
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 class LoadDropboxStory(_LoadingStory):
@@ -362,6 +370,7 @@
   NAME = 'load:games:bubbles'
   URL = (
       'https://games.cdn.famobi.com/html5games/s/smarty-bubbles/v010/?fg_domain=play.famobi.com&fg_uid=d8f24956-dc91-4902-9096-a46cb1353b6f&fg_pid=4638e320-4444-4514-81c4-d80a8c662371&fg_beat=620')
+  TAGS = [story_tags.HEALTH_CHECK]
 
   def _DidLoadDocument(self, action_runner):
     # The #logo element is removed right before the main menu is displayed.
@@ -373,12 +382,14 @@
   NAME = 'load:games:lazors'
   # Using "https://" hangs and shows "This site can't be reached".
   URL = 'http://www8.games.mobi/games/html5/lazors/lazors.html'
+  TAGS = [story_tags.HEALTH_CHECK]
 
 
 class LoadSpyChaseStory(_LoadingStory):
   NAME = 'load:games:spychase'
   # Using "https://" shows "Your connection is not private".
   URL = 'http://playstar.mobi/games/spychase/index.php'
+  TAGS = [story_tags.HEALTH_CHECK]
 
   def _DidLoadDocument(self, action_runner):
     # The background of the game canvas is set when the "Tap screen to play"
diff --git a/tools/perf/page_sets/system_health/story_tags.py b/tools/perf/page_sets/system_health/story_tags.py
index 6f43e42f..2200b44 100644
--- a/tools/perf/page_sets/system_health/story_tags.py
+++ b/tools/perf/page_sets/system_health/story_tags.py
@@ -22,6 +22,9 @@
     'css_animation', 'Story has animations that are implemented using CSS.')
 EXTENSION = Tag(
     'extension', 'Story has browser with extension installed.')
+HEALTH_CHECK = Tag(  # See go/health_check_stories for details.
+    'health_check', 'Story belongs to a collection chosen to have a wide '
+    'coverage but low running time.')
 IMAGES = Tag(
     'images', 'Story has sites with heavy uses of images.')
 INFINITE_SCROLL = Tag('infinite_scroll', 'Story has infinite scroll action.')
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index 45e4384..0ce3f1d8 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -348,7 +348,9 @@
       # It was only written to one shard, use that shards data
       results_filename = join(directories[0], 'perf_results.json')
 
-    print 'Uploading perf results from %s benchmark' % benchmark_name
+    results_size_in_mib = os.path.getsize(results_filename) / (2 ** 20)
+    print 'Uploading perf results from %s benchmark (size %s Mib)' % (
+        benchmark_name, results_size_in_mib)
     with open(output_json_file, 'w') as oj:
       upload_fail = _upload_perf_results(
         results_filename,
diff --git a/ui/base/ime/mock_ime_input_context_handler.cc b/ui/base/ime/mock_ime_input_context_handler.cc
index b6a3960..e01c87e 100644
--- a/ui/base/ime/mock_ime_input_context_handler.cc
+++ b/ui/base/ime/mock_ime_input_context_handler.cc
@@ -12,7 +12,8 @@
 MockIMEInputContextHandler::MockIMEInputContextHandler()
     : commit_text_call_count_(0),
       update_preedit_text_call_count_(0),
-      delete_surrounding_text_call_count_(0) {}
+      delete_surrounding_text_call_count_(0),
+      last_sent_key_event_(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0) {}
 
 MockIMEInputContextHandler::~MockIMEInputContextHandler() {}
 
@@ -47,9 +48,12 @@
   update_preedit_text_call_count_ = 0;
   delete_surrounding_text_call_count_ = 0;
   last_commit_text_.clear();
+  last_sent_key_event_ = ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0);
 }
 
-void MockIMEInputContextHandler::SendKeyEvent(KeyEvent* event) {}
+void MockIMEInputContextHandler::SendKeyEvent(KeyEvent* event) {
+  last_sent_key_event_ = *event;
+}
 
 InputMethod* MockIMEInputContextHandler::GetInputMethod() {
   return nullptr;
diff --git a/ui/base/ime/mock_ime_input_context_handler.h b/ui/base/ime/mock_ime_input_context_handler.h
index 66ed795d..e1bcb1f6 100644
--- a/ui/base/ime/mock_ime_input_context_handler.h
+++ b/ui/base/ime/mock_ime_input_context_handler.h
@@ -10,6 +10,7 @@
 #include "ui/base/ime/composition_text.h"
 #include "ui/base/ime/ime_input_context_handler_interface.h"
 #include "ui/base/ime/ui_base_ime_export.h"
+#include "ui/events/event.h"
 
 namespace ui {
 class InputMethod;
@@ -60,6 +61,10 @@
     return last_delete_surrounding_text_arg_;
   }
 
+  const ui::KeyEvent& last_sent_key_event() const {
+    return last_sent_key_event_;
+  }
+
   // Resets all call count.
   void Reset();
 
@@ -68,6 +73,7 @@
   int update_preedit_text_call_count_;
   int delete_surrounding_text_call_count_;
   std::string last_commit_text_;
+  ui::KeyEvent last_sent_key_event_;
   UpdateCompositionTextArg last_update_composition_arg_;
   DeleteSurroundingTextArg last_delete_surrounding_text_arg_;
 };
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index f130b42..056fce23 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -51,7 +51,7 @@
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kTouchableAppContextMenu = {
-    "EnableTouchableAppContextMenu", base::FEATURE_ENABLED_BY_DEFAULT};
+    "EnableTouchableAppContextMenu", base::FEATURE_DISABLED_BY_DEFAULT};
 
 bool IsTouchableAppContextMenuEnabled() {
   return base::FeatureList::IsEnabled(kTouchableAppContextMenu) ||
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index f92d3e3f..420df48 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -589,7 +589,7 @@
   }
 
   blink::WebFloatPoint position_in_widget = wheel_event.PositionInWidget();
-  if (input_handler_->HasWheelEventHandlerAt(
+  if (input_handler_->HasBlockingWheelEventHandlerAt(
           gfx::Point(position_in_widget.x, position_in_widget.y))) {
     result = DID_NOT_HANDLE;
   } else {
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index 0b54ce2..9f5bd515 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -152,7 +152,7 @@
                cc::InputHandler::TouchStartOrMoveEventListenerType(
                    const gfx::Point& point,
                    cc::TouchAction* touch_action));
-  MOCK_CONST_METHOD1(HasWheelEventHandlerAt, bool(const gfx::Point&));
+  MOCK_CONST_METHOD1(HasBlockingWheelEventHandlerAt, bool(const gfx::Point&));
 
   MOCK_METHOD0(RequestUpdateForSynchronousInputHandler, void());
   MOCK_METHOD1(SetSynchronousInputHandlerRootScrollOffset,
@@ -457,7 +457,7 @@
 
 TEST_P(InputHandlerProxyTest, MouseWheelNoListener) {
   expected_disposition_ = InputHandlerProxy::DROP_EVENT;
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::_))
+  EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_))
       .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
@@ -472,7 +472,7 @@
 
 TEST_P(InputHandlerProxyTest, MouseWheelPassiveListener) {
   expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING;
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::_))
+  EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_))
       .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
@@ -487,7 +487,7 @@
 
 TEST_P(InputHandlerProxyTest, MouseWheelBlockingListener) {
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::_))
+  EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_))
       .WillRepeatedly(testing::Return(true));
 
   WebMouseWheelEvent wheel(WebInputEvent::kMouseWheel,
@@ -499,7 +499,7 @@
 
 TEST_P(InputHandlerProxyTest, MouseWheelBlockingAndPassiveListener) {
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::_))
+  EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_))
       .WillRepeatedly(testing::Return(true));
   // We will not call GetEventListenerProperties because we early out when we
   // hit blocking region.
@@ -512,11 +512,13 @@
 
 TEST_P(InputHandlerProxyTest, MouseWheelEventOutsideBlockingListener) {
   expected_disposition_ = InputHandlerProxy::DROP_EVENT;
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::Property(
-                                       &gfx::Point::y, testing::Gt(10))))
+  EXPECT_CALL(mock_input_handler_,
+              HasBlockingWheelEventHandlerAt(
+                  testing::Property(&gfx::Point::y, testing::Gt(10))))
       .WillRepeatedly(testing::Return(true));
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::Property(
-                                       &gfx::Point::y, testing::Le(10))))
+  EXPECT_CALL(mock_input_handler_,
+              HasBlockingWheelEventHandlerAt(
+                  testing::Property(&gfx::Point::y, testing::Le(10))))
       .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
@@ -534,11 +536,13 @@
 TEST_P(InputHandlerProxyTest,
        MouseWheelEventOutsideBlockingListenerWithPassiveListener) {
   expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING;
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::Property(
-                                       &gfx::Point::y, testing::Gt(10))))
+  EXPECT_CALL(mock_input_handler_,
+              HasBlockingWheelEventHandlerAt(
+                  testing::Property(&gfx::Point::y, testing::Gt(10))))
       .WillRepeatedly(testing::Return(true));
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::Property(
-                                       &gfx::Point::y, testing::Le(10))))
+  EXPECT_CALL(mock_input_handler_,
+              HasBlockingWheelEventHandlerAt(
+                  testing::Property(&gfx::Point::y, testing::Le(10))))
       .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
@@ -1527,7 +1531,7 @@
       blink::kWebGestureDeviceTouchpad);
 
   // Wheel event with passive event listener.
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::_))
+  EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_))
       .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
@@ -1555,7 +1559,7 @@
   // Wheel event with blocking event listener. If there is a wheel event handler
   // at the point, we do not need to call GetEventListenerProperties since it
   // indicates kBlocking.
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::_))
+  EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_))
       .WillRepeatedly(testing::Return(true));
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel));
@@ -1578,7 +1582,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   // Wheel scrolling on main thread.
-  EXPECT_CALL(mock_input_handler_, HasWheelEventHandlerAt(testing::_))
+  EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_))
       .WillRepeatedly(testing::Return(true));
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kMainThreadScrollState));
diff --git a/ui/file_manager/file_manager/background/js/test_util_base.js b/ui/file_manager/file_manager/background/js/test_util_base.js
index 9f6f68b7..e37f0ff 100644
--- a/ui/file_manager/file_manager/background/js/test_util_base.js
+++ b/ui/file_manager/file_manager/background/js/test_util_base.js
@@ -12,42 +12,49 @@
  * @param {Element} element Element to be extracted.
  * @param {Window} contentWindow Window to be tested.
  * @param {Array<string>=} opt_styleNames List of CSS property name to be
- *     obtained.
+ *     obtained. NOTE: Causes element style re-calculation.
  * @return {{attributes:Object<string>, text:string,
- *           styles:Object<string>, hidden:boolean}} Element
+ *           styles:(Object<string>|undefined), hidden:boolean}} Element
  *     information that contains contentText, attribute names and
  *     values, hidden attribute, and style names and values.
  */
 function extractElementInfo(element, contentWindow, opt_styleNames) {
-  var attributes = {};
-  for (var i = 0; i < element.attributes.length; i++) {
+  const attributes = {};
+  for (let i = 0; i < element.attributes.length; i++) {
     attributes[element.attributes[i].nodeName] =
         element.attributes[i].nodeValue;
   }
-  var styles = {};
-  var styleNames = opt_styleNames || [];
-  assert(Array.isArray(styleNames));
-  var computedStyles = contentWindow.getComputedStyle(element);
-  for (var i = 0; i < styleNames.length; i++) {
-    styles[styleNames[i]] = computedStyles[styleNames[i]];
-  }
-  var text = element.textContent;
-  var size = element.getBoundingClientRect();
-  return {
+
+  const result = {
     attributes: attributes,
-    text: text,
+    text: element.textContent,
     value: element.value,
-    styles: styles,
     // The hidden attribute is not in the element.attributes even if
     // element.hasAttribute('hidden') is true.
     hidden: !!element.hidden,
-    // These attributes are set when element is img or canvas.
-    imageWidth: Number(element.width),
-    imageHeight: Number(element.height),
-    // These attributes are set in any element.
-    renderedWidth: size.width,
-    renderedHeight: size.height
   };
+
+  const styleNames = opt_styleNames || [];
+  assert(Array.isArray(styleNames));
+  if (!styleNames.length)
+    return result;
+
+  const styles = {};
+  const size = element.getBoundingClientRect();
+  const computedStyles = contentWindow.getComputedStyle(element);
+  for (let i = 0; i < styleNames.length; i++) {
+    styles[styleNames[i]] = computedStyles[styleNames[i]];
+  }
+
+  result.styles = styles;
+  // These attributes are set when element is img or canvas.
+  result.imageWidth = Number(element.width);
+  result.imageHeight = Number(element.height);
+
+  // These attributes are set in any element.
+  result.renderedWidth = size.width;
+  result.renderedHeight = size.height;
+  return result;
 }
 
 /**
@@ -257,7 +264,7 @@
  * @param {Array<string>=} opt_styleNames List of CSS property name to be
  *     obtained.
  * @return {?{attributes:Object<string>, text:string,
- *                  styles:Object<string>, hidden:boolean}} Element
+ *                  styles:(Object<string>|undefined), hidden:boolean}} Element
  *     information that contains contentText, attribute names and
  *     values, hidden attribute, and style names and values. If there is no
  *     active element, returns null.
diff --git a/ui/file_manager/file_manager/foreground/css/common.css b/ui/file_manager/file_manager/foreground/css/common.css
index e3201689..7c68a1d6 100644
--- a/ui/file_manager/file_manager/foreground/css/common.css
+++ b/ui/file_manager/file_manager/foreground/css/common.css
@@ -195,6 +195,7 @@
 /* Pop-up dialogs. */
 
 .cr-dialog-container {
+  background-color: rgba(0, 0, 0, 0.6);
   display: flex;
   height: 100%;
   left: 0;
diff --git a/ui/file_manager/integration_tests/gallery/open_image_files.js b/ui/file_manager/integration_tests/gallery/open_image_files.js
index 589185e..013fc3b1 100644
--- a/ui/file_manager/integration_tests/gallery/open_image_files.js
+++ b/ui/file_manager/integration_tests/gallery/open_image_files.js
@@ -43,8 +43,8 @@
     return resizedWindowPromise.then(function() {
       var rootElementPromise =
           gallery.waitForElement(appId, '.gallery[mode="slide"]');
-      var fullImagePromsie = gallery.waitForElement(
-          appId, '.gallery .image-container > .image');
+      var fullImagePromsie = gallery.waitForElementStyles(
+          appId, '.gallery .image-container > .image', ['any']);
       return Promise.all([rootElementPromise, fullImagePromsie]).
           then(function(args) {
             chrome.test.assertEq(760, args[1].renderedWidth);
diff --git a/ui/file_manager/integration_tests/remote_call.js b/ui/file_manager/integration_tests/remote_call.js
index 8e91774d..aa8b80f 100644
--- a/ui/file_manager/integration_tests/remote_call.js
+++ b/ui/file_manager/integration_tests/remote_call.js
@@ -179,15 +179,29 @@
  * @return {Promise} Promise to be fulfilled when the element appears.
  */
 RemoteCall.prototype.waitForElement = function(windowId, query) {
+  return this.waitForElementStyles(windowId, query, []);
+};
+
+/**
+ * Waits for the specified element appearing in the DOM.
+ * @param {string} windowId Target window ID.
+ * @param {string} query Query string for the element.
+ * @param {!Array<string>} styleNames List of CSS property name to be
+ *     obtained. NOTE: Causes element style re-calculation.
+ * @return {Promise} Promise to be fulfilled when the element appears.
+ */
+RemoteCall.prototype.waitForElementStyles = function(
+    windowId, query, styleNames) {
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.callRemoteTestUtil('queryAllElements', windowId, [query])
+  return repeatUntil(() => {
+    return this
+        .callRemoteTestUtil('queryAllElements', windowId, [query, styleNames])
         .then(function(elements) {
           if (elements.length > 0)
             return elements[0];
           return pending(caller, 'Element %s is not found.', query);
         });
-  }.bind(this));
+  });
 };
 
 /**
@@ -460,7 +474,7 @@
     return Promise
         .all([
           this.waitForElement(windowId, '#rename-input'),
-          this.waitForElement(windowId, query)
+          this.waitForElementStyles(windowId, query, ['any'])
         ])
         .then(function(args) {
           var nameBox = args[0];
diff --git a/ui/ozone/common/linux/BUILD.gn b/ui/ozone/common/linux/BUILD.gn
index de979e68..0d62943e 100644
--- a/ui/ozone/common/linux/BUILD.gn
+++ b/ui/ozone/common/linux/BUILD.gn
@@ -5,7 +5,7 @@
 import("//build/config/ui.gni")
 import("//ui/ozone/ozone.gni")
 
-assert(ozone_platform_gbm)
+assert(ozone_platform_gbm || ozone_platform_wayland)
 
 source_set("linux") {
   sources = [
diff --git a/ui/ozone/common/linux/drm_util_linux.cc b/ui/ozone/common/linux/drm_util_linux.cc
index eaa8d58..b9a26887 100644
--- a/ui/ozone/common/linux/drm_util_linux.cc
+++ b/ui/ozone/common/linux/drm_util_linux.cc
@@ -74,4 +74,25 @@
   }
 }
 
+bool IsValidBufferFormat(uint32_t current_format) {
+  switch (GetBufferFormatFromFourCCFormat(current_format)) {
+    case gfx::BufferFormat::R_8:
+    case gfx::BufferFormat::RG_88:
+    case gfx::BufferFormat::RGBA_8888:
+    case gfx::BufferFormat::RGBX_8888:
+    case gfx::BufferFormat::BGRA_8888:
+    case gfx::BufferFormat::BGRX_8888:
+    case gfx::BufferFormat::BGRX_1010102:
+    case gfx::BufferFormat::RGBX_1010102:
+    case gfx::BufferFormat::BGR_565:
+    case gfx::BufferFormat::UYVY_422:
+    case gfx::BufferFormat::YUV_420_BIPLANAR:
+    case gfx::BufferFormat::YVU_420:
+      return true;
+    default:
+      return false;
+  }
+  return false;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/common/linux/drm_util_linux.h b/ui/ozone/common/linux/drm_util_linux.h
index f430a39..4e836bc9 100644
--- a/ui/ozone/common/linux/drm_util_linux.h
+++ b/ui/ozone/common/linux/drm_util_linux.h
@@ -13,6 +13,9 @@
 int GetFourCCFormatFromBufferFormat(gfx::BufferFormat format);
 gfx::BufferFormat GetBufferFormatFromFourCCFormat(int format);
 
+// Returns true if the fourcc format is known.
+bool IsValidBufferFormat(uint32_t current_format);
+
 }  // namespace ui
 
 #endif  // UI_OZONE_COMMON_LINUX_DRM_UTIL_LINUX_H__
diff --git a/ui/ozone/platform/drm/host/drm_device_connector.cc b/ui/ozone/platform/drm/host/drm_device_connector.cc
index ed0bb34..dfdd37c 100644
--- a/ui/ozone/platform/drm/host/drm_device_connector.cc
+++ b/ui/ozone/platform/drm/host/drm_device_connector.cc
@@ -61,7 +61,8 @@
 void DrmDeviceConnector::OnGpuServiceLaunched(
     scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_runner,
-    GpuHostBindInterfaceCallback binder) {
+    GpuHostBindInterfaceCallback binder,
+    GpuHostTerminateCallback terminate_callback) {
   // We need to preserve |binder| to let us bind interfaces later.
   binder_callback_ = std::move(binder);
   if (am_running_in_ws_mode()) {
diff --git a/ui/ozone/platform/drm/host/drm_device_connector.h b/ui/ozone/platform/drm/host/drm_device_connector.h
index d77183c..8de058eb 100644
--- a/ui/ozone/platform/drm/host/drm_device_connector.h
+++ b/ui/ozone/platform/drm/host/drm_device_connector.h
@@ -39,7 +39,8 @@
   void OnGpuServiceLaunched(
       scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
       scoped_refptr<base::SingleThreadTaskRunner> io_runner,
-      GpuHostBindInterfaceCallback binder) override;
+      GpuHostBindInterfaceCallback binder,
+      GpuHostTerminateCallback terminate_callback) override;
 
   // BindInterface arranges for the drm_device_ptr to be connected.
   void BindInterfaceDrmDevice(
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
index 12332387..5daea0c 100644
--- a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
@@ -121,7 +121,8 @@
 void DrmGpuPlatformSupportHost::OnGpuServiceLaunched(
     scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_runner,
-    GpuHostBindInterfaceCallback binder) {
+    GpuHostBindInterfaceCallback binder,
+    GpuHostTerminateCallback terminate_callback) {
   NOTREACHED() << "DrmGpuPlatformSupportHost::OnGpuServiceLaunched shouldn't "
                   "be used with pre-mojo IPC";
 }
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h
index c2d1d65..9c8cf6f 100644
--- a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h
@@ -44,7 +44,8 @@
   void OnGpuServiceLaunched(
       scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
       scoped_refptr<base::SingleThreadTaskRunner> io_runner,
-      GpuHostBindInterfaceCallback binder) override;
+      GpuHostBindInterfaceCallback binder,
+      GpuHostTerminateCallback terminate_callback) override;
 
   void OnMessageReceived(const IPC::Message& message) override;
 
diff --git a/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/ui/ozone/platform/scenic/ozone_platform_scenic.cc
index 5bb85d465..6100a7f 100644
--- a/ui/ozone/platform/scenic/ozone_platform_scenic.cc
+++ b/ui/ozone/platform/scenic/ozone_platform_scenic.cc
@@ -29,9 +29,11 @@
 
 namespace {
 
-constexpr OzonePlatform::PlatformProperties kScenicPlatformProperties = {
+const OzonePlatform::PlatformProperties kScenicPlatformProperties(
     /*needs_view_owner_request=*/true,
-};
+    /*custom_frame_pref_default=*/false,
+    /*use_system_title_bar=*/false,
+    std::vector<gfx::BufferFormat>());
 
 class ScenicPlatformEventSource : public ui::PlatformEventSource {
  public:
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 97eae521..d07a1bd 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -5,6 +5,7 @@
 visibility = [ "//ui/ozone/*" ]
 
 import("//build/config/linux/pkg_config.gni")
+import("//ui/ozone/platform/wayland/wayland.gni")
 
 pkg_config("wayland-egl") {
   packages = [ "wayland-egl" ]
@@ -16,10 +17,14 @@
     "client_native_pixmap_factory_wayland.h",
     "gl_surface_wayland.cc",
     "gl_surface_wayland.h",
+    "gpu/wayland_connection_proxy.cc",
+    "gpu/wayland_connection_proxy.h",
     "ozone_platform_wayland.cc",
     "ozone_platform_wayland.h",
     "wayland_connection.cc",
     "wayland_connection.h",
+    "wayland_connection_connector.cc",
+    "wayland_connection_connector.h",
     "wayland_cursor.cc",
     "wayland_cursor.h",
     "wayland_data_device.cc",
@@ -70,8 +75,12 @@
 
   deps = [
     "//base",
+    "//mojo/public/cpp/bindings",
     "//skia",
+    "//third_party/libdrm",
+    "//third_party/minigbm",
     "//third_party/wayland:wayland_client",
+    "//third_party/wayland-protocols:linux_dmabuf_protocol",
     "//third_party/wayland-protocols:xdg_shell_protocol",
     "//ui/base",
     "//ui/base:ui_features",
@@ -79,17 +88,42 @@
     "//ui/events",
     "//ui/events:dom_keycode_converter",
     "//ui/events/ozone:events_ozone",
+    "//ui/events/ozone:events_ozone_evdev",
     "//ui/events/ozone:events_ozone_layout",
     "//ui/events/platform",
     "//ui/gfx",
+    "//ui/gfx:memory_buffer",
     "//ui/gfx/geometry",
     "//ui/ozone:ozone_base",
     "//ui/ozone/common",
+    "//ui/ozone/common/linux",
+    "//ui/ozone/public/interfaces/wayland:wayland_interfaces",
     "//ui/platform_window",
   ]
 
   defines = [ "OZONE_IMPLEMENTATION" ]
 
+  if (use_wayland_gbm) {
+    defines += [ "WAYLAND_GBM" ]
+    sources += [
+      "gpu/drm_render_node_handle.cc",
+      "gpu/drm_render_node_handle.h",
+      "gpu/drm_render_node_path_finder.cc",
+      "gpu/drm_render_node_path_finder.h",
+      "gpu/gbm_pixmap_wayland.cc",
+      "gpu/gbm_pixmap_wayland.h",
+      "gpu/gbm_surfaceless_wayland.cc",
+      "gpu/gbm_surfaceless_wayland.h",
+    ]
+
+    deps += [
+      "//third_party/libdrm",
+      "//third_party/minigbm",
+      "//ui/gfx:memory_buffer",
+      "//ui/ozone/common/linux",
+    ]
+  }
+
   configs += [
     ":wayland-egl",
     "//third_party/khronos:khronos_headers",
@@ -131,4 +165,7 @@
   }
 
   defines = [ "WL_HIDE_DEPRECATED" ]
+  if (use_wayland_gbm) {
+    defines += [ "WAYLAND_GBM" ]
+  }
 }
diff --git a/ui/ozone/platform/wayland/DEPS b/ui/ozone/platform/wayland/DEPS
index e60ac4f2..fde6dba3 100644
--- a/ui/ozone/platform/wayland/DEPS
+++ b/ui/ozone/platform/wayland/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+ui/base/ui_features.h",  # UI features doesn't bring in all of ui/base.
+  "+mojo/public",
 ]
 
diff --git a/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc b/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc
index 5670324..0515392 100644
--- a/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc
+++ b/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc
@@ -4,12 +4,53 @@
 
 #include "ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.h"
 
-#include "ui/ozone/common/stub_client_native_pixmap_factory.h"
+#include "ui/gfx/linux/client_native_pixmap_dmabuf.h"
+#include "ui/gfx/linux/client_native_pixmap_factory_dmabuf.h"
+#include "ui/ozone/public/ozone_platform.h"
 
 namespace ui {
 
+// Implements ClientNativePixmapFactory to provide a more accurate buffer format
+// support when Wayland dmabuf is used.
+class ClientNativePixmapFactoryWayland : public gfx::ClientNativePixmapFactory {
+ public:
+  ClientNativePixmapFactoryWayland() {
+    dmabuf_factory_.reset(gfx::CreateClientNativePixmapFactoryDmabuf());
+  }
+  ~ClientNativePixmapFactoryWayland() override {}
+
+  // ClientNativePixmapFactory overrides:
+  bool IsConfigurationSupported(gfx::BufferFormat format,
+                                gfx::BufferUsage usage) const override {
+    OzonePlatform::PlatformProperties properties =
+        OzonePlatform::GetInstance()->GetPlatformProperties();
+    if (properties.supported_buffer_formats.empty()) {
+      // If the compositor did not announce supported buffer formats, do our
+      // best and assume those are supported.
+      return dmabuf_factory_->IsConfigurationSupported(format, usage);
+    }
+
+    for (auto buffer_format : properties.supported_buffer_formats) {
+      if (buffer_format == format)
+        return dmabuf_factory_->IsConfigurationSupported(format, usage);
+    }
+    return false;
+  }
+
+  std::unique_ptr<gfx::ClientNativePixmap> ImportFromHandle(
+      const gfx::NativePixmapHandle& handle,
+      const gfx::Size& size,
+      gfx::BufferUsage usage) override {
+    return dmabuf_factory_->ImportFromHandle(handle, size, usage);
+  }
+
+ private:
+  std::unique_ptr<ClientNativePixmapFactory> dmabuf_factory_;
+  DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapFactoryWayland);
+};
+
 gfx::ClientNativePixmapFactory* CreateClientNativePixmapFactoryWayland() {
-  return CreateStubClientNativePixmapFactory();
+  return new ClientNativePixmapFactoryWayland();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc b/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc
new file mode 100644
index 0000000..a049ee5
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc
@@ -0,0 +1,35 @@
+// 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 "ui/ozone/platform/wayland/gpu/drm_render_node_handle.h"
+
+#include <fcntl.h>
+#include <xf86drm.h>
+
+namespace ui {
+
+DrmRenderNodeHandle::DrmRenderNodeHandle() = default;
+
+DrmRenderNodeHandle::~DrmRenderNodeHandle() = default;
+
+bool DrmRenderNodeHandle::Initialize(const base::FilePath& path) {
+  base::ScopedFD drm_fd(open(path.value().c_str(), O_RDWR));
+  if (drm_fd.get() < 0)
+    return false;
+
+  drmVersionPtr drm_version = drmGetVersion(drm_fd.get());
+  if (!drm_version) {
+    LOG(FATAL) << "Can't get version for device: '" << path << "'";
+    return false;
+  }
+
+  drm_fd_ = std::move(drm_fd);
+  return true;
+}
+
+base::ScopedFD DrmRenderNodeHandle::PassFD() {
+  return std::move(drm_fd_);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/drm_render_node_handle.h b/ui/ozone/platform/wayland/gpu/drm_render_node_handle.h
new file mode 100644
index 0000000..46632326
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/drm_render_node_handle.h
@@ -0,0 +1,32 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_GPU_DRM_RENDER_NODE_HANDLE_H_
+#define UI_OZONE_PLATFORM_WAYLAND_GPU_DRM_RENDER_NODE_HANDLE_H_
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+
+namespace ui {
+
+// A wrapper around a DRM render node device handle.
+class DrmRenderNodeHandle {
+ public:
+  DrmRenderNodeHandle();
+  ~DrmRenderNodeHandle();
+
+  bool Initialize(const base::FilePath& path);
+
+  base::ScopedFD PassFD();
+
+ private:
+  base::ScopedFD drm_fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmRenderNodeHandle);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_GPU_DRM_RENDER_NODE_HANDLE_H_
diff --git a/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.cc b/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.cc
new file mode 100644
index 0000000..c3367aee
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.cc
@@ -0,0 +1,49 @@
+// 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 "ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.h"
+
+#include <fcntl.h>
+
+#include "base/files/scoped_file.h"
+#include "base/strings/stringprintf.h"
+
+namespace ui {
+
+namespace {
+
+// Drm render node path template.
+constexpr char kDriRenderNodeTemplate[] = "/dev/dri/renderD%u";
+
+// Number of files to look for when discovering DRM devices.
+constexpr uint32_t kDrmMaxMinor = 15;
+constexpr uint32_t kRenderNodeStart = 128;
+constexpr uint32_t kRenderNodeEnd = kRenderNodeStart + kDrmMaxMinor + 1;
+
+}  // namespace
+
+DrmRenderNodePathFinder::DrmRenderNodePathFinder() = default;
+
+DrmRenderNodePathFinder::~DrmRenderNodePathFinder() = default;
+
+base::FilePath DrmRenderNodePathFinder::GetDrmRenderNodePath() {
+  if (drm_render_node_path_.empty())
+    FindDrmRenderNodePath();
+
+  return drm_render_node_path_;
+}
+
+void DrmRenderNodePathFinder::FindDrmRenderNodePath() {
+  for (uint32_t i = kRenderNodeStart; i < kRenderNodeEnd; i++) {
+    std::string dri_render_node(base::StringPrintf(kDriRenderNodeTemplate, i));
+    base::ScopedFD drm_fd(open(dri_render_node.c_str(), O_RDWR));
+    if (drm_fd.get() < 0)
+      continue;
+
+    drm_render_node_path_ = base::FilePath(dri_render_node);
+    break;
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.h b/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.h
new file mode 100644
index 0000000..a6408e7b
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.h
@@ -0,0 +1,33 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_GPU_DRM_RENDER_NODE_PATH_FINDER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_GPU_DRM_RENDER_NODE_PATH_FINDER_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+
+namespace ui {
+
+// A helper class that finds a DRM render node device and returns a path to it.
+class DrmRenderNodePathFinder {
+ public:
+  DrmRenderNodePathFinder();
+  ~DrmRenderNodePathFinder();
+
+  // Returns a path to a drm render node device. If it hasn't been found yet,
+  // triggers FindDrmRenderNodePath and returns the path.
+  base::FilePath GetDrmRenderNodePath();
+
+ private:
+  void FindDrmRenderNodePath();
+
+  base::FilePath drm_render_node_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmRenderNodePathFinder);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_GPU_DRM_RENDER_NODE_PATH_FINDER_H_
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
new file mode 100644
index 0000000..8c3d01b4
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
@@ -0,0 +1,192 @@
+// 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 "ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h"
+
+#include <drm_fourcc.h>
+#include <gbm.h>
+#include <xf86drmMode.h>
+
+#include "base/files/platform_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/native_pixmap_handle.h"
+#include "ui/ozone/common/linux/drm_util_linux.h"
+#include "ui/ozone/common/linux/gbm_device.h"
+#include "ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h"
+#include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
+#include "ui/ozone/platform/wayland/wayland_surface_factory.h"
+#include "ui/ozone/public/overlay_plane.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace ui {
+
+GbmPixmapWayland::GbmPixmapWayland(WaylandSurfaceFactory* surface_manager,
+                                   WaylandConnectionProxy* connection)
+    : surface_manager_(surface_manager), connection_(connection) {}
+
+GbmPixmapWayland::~GbmPixmapWayland() {
+  connection_->DestroyZwpLinuxDmabuf(GetUniqueId());
+}
+
+bool GbmPixmapWayland::InitializeBuffer(gfx::Size size,
+                                        gfx::BufferFormat format,
+                                        gfx::BufferUsage usage) {
+  TRACE_EVENT1("Wayland", "GbmPixmapWayland::InitializeBuffer", "size",
+               size.ToString());
+  uint32_t flags = 0;
+  switch (usage) {
+    case gfx::BufferUsage::GPU_READ:
+      flags = GBM_BO_USE_LINEAR;
+      break;
+    case gfx::BufferUsage::SCANOUT:
+      flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
+      break;
+    case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE:
+      flags = GBM_BO_USE_LINEAR | GBM_BO_USE_WRITE | GBM_BO_USE_SCANOUT;
+      break;
+    case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE:
+      flags = GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT;
+      break;
+    case gfx::BufferUsage::SCANOUT_VDA_WRITE:
+      flags = GBM_BO_USE_SCANOUT;
+      break;
+    case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
+    case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT:
+      // mmap cannot be used with gbm buffers on a different process. That is,
+      // Linux disallows this and "permission denied" is returned. To overcome
+      // this and make software rasterization working, buffers must be created
+      // on the browser process and gbm_bo_map must be used.
+      // TODO(msisov): add support fir these two buffer usage cases.
+      // https://crbug.com/864914
+      LOG(FATAL) << "This scenario is not supported in Wayland now";
+      break;
+    default:
+      NOTREACHED() << "Not supported buffer format";
+      break;
+  }
+
+  const uint32_t fourcc_format = GetFourCCFormatFromBufferFormat(format);
+  gbm_bo_ = connection_->gbm_device()->CreateBuffer(fourcc_format, size, flags);
+  if (!gbm_bo_) {
+    LOG(FATAL) << "Cannot create bo";
+    return false;
+  }
+
+  CreateZwpLinuxDmabuf();
+  return true;
+}
+
+bool GbmPixmapWayland::AreDmaBufFdsValid() const {
+  return gbm_bo_->AreFdsValid();
+}
+
+size_t GbmPixmapWayland::GetDmaBufFdCount() const {
+  return gbm_bo_->GetFdCount();
+}
+
+int GbmPixmapWayland::GetDmaBufFd(size_t plane) const {
+  return gbm_bo_->GetPlaneFd(plane);
+}
+
+int GbmPixmapWayland::GetDmaBufPitch(size_t plane) const {
+  return gbm_bo_->GetPlaneStride(plane);
+}
+
+int GbmPixmapWayland::GetDmaBufOffset(size_t plane) const {
+  return gbm_bo_->GetPlaneOffset(plane);
+}
+
+uint64_t GbmPixmapWayland::GetDmaBufModifier(size_t plane) const {
+  // TODO(msisov): figure out why returning format modifier results in
+  // EGL_BAD_ALLOC.
+  //
+  // return gbm_bo_->get_format_modifier();
+  return 0;
+}
+
+gfx::BufferFormat GbmPixmapWayland::GetBufferFormat() const {
+  return gbm_bo_->GetBufferFormat();
+}
+
+gfx::Size GbmPixmapWayland::GetBufferSize() const {
+  return gbm_bo_->GetSize();
+}
+
+uint32_t GbmPixmapWayland::GetUniqueId() const {
+  return gbm_bo_->GetHandle();
+}
+
+bool GbmPixmapWayland::ScheduleOverlayPlane(
+    gfx::AcceleratedWidget widget,
+    int plane_z_order,
+    gfx::OverlayTransform plane_transform,
+    const gfx::Rect& display_bounds,
+    const gfx::RectF& crop_rect,
+    bool enable_blend,
+    std::unique_ptr<gfx::GpuFence> gpu_fence) {
+  GbmSurfacelessWayland* surfaceless = surface_manager_->GetSurface(widget);
+  DCHECK(surfaceless);
+  surfaceless->QueueOverlayPlane(
+      OverlayPlane(this, std::move(gpu_fence), plane_z_order, plane_transform,
+                   display_bounds, crop_rect, enable_blend));
+  return true;
+}
+
+gfx::NativePixmapHandle GbmPixmapWayland::ExportHandle() {
+  gfx::NativePixmapHandle handle;
+  gfx::BufferFormat format = GetBufferFormat();
+
+  // TODO(dcastagna): Use gbm_bo_get_num_planes once all the formats we use are
+  // supported by gbm.
+  for (size_t i = 0; i < gfx::NumberOfPlanesForBufferFormat(format); ++i) {
+    // Some formats (e.g: YVU_420) might have less than one fd per plane.
+    if (i < GetDmaBufFdCount()) {
+      base::ScopedFD scoped_fd(HANDLE_EINTR(dup(GetDmaBufFd(i))));
+      if (!scoped_fd.is_valid()) {
+        PLOG(ERROR) << "dup";
+        return gfx::NativePixmapHandle();
+      }
+      handle.fds.emplace_back(
+          base::FileDescriptor(scoped_fd.release(), true /* auto_close */));
+    }
+    handle.planes.emplace_back(GetDmaBufPitch(i), GetDmaBufOffset(i),
+                               gbm_bo_->GetPlaneSize(i), GetDmaBufModifier(i));
+  }
+  return handle;
+}
+
+void GbmPixmapWayland::CreateZwpLinuxDmabuf() {
+  uint64_t modifier = gbm_bo_->GetFormatModifier();
+
+  std::vector<uint32_t> strides;
+  std::vector<uint32_t> offsets;
+  std::vector<uint64_t> modifiers;
+
+  size_t plane_count = gbm_bo_->GetNumPlanes();
+  for (size_t i = 0; i < plane_count; ++i) {
+    strides.push_back(GetDmaBufPitch(i));
+    offsets.push_back(GetDmaBufOffset(i));
+    if (modifier != DRM_FORMAT_MOD_INVALID)
+      modifiers.push_back(modifier);
+  }
+
+  base::ScopedFD fd(HANDLE_EINTR(dup(GetDmaBufFd(0))));
+  if (!fd.is_valid()) {
+    PLOG(FATAL) << "dup";
+    return;
+  }
+  base::File file(fd.release());
+
+  // Asks Wayland to create a wl_buffer based on the |file| fd.
+  connection_->CreateZwpLinuxDmabuf(std::move(file), GetBufferSize(), strides,
+                                    offsets, modifiers, gbm_bo_->GetFormat(),
+                                    plane_count, GetUniqueId());
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h
new file mode 100644
index 0000000..6998041
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h
@@ -0,0 +1,70 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_GPU_GBM_PIXMAP_WAYLAND_H_
+#define UI_OZONE_PLATFORM_WAYLAND_GPU_GBM_PIXMAP_WAYLAND_H_
+
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_pixmap.h"
+#include "ui/ozone/common/linux/gbm_buffer.h"
+
+namespace ui {
+
+class WaylandSurfaceFactory;
+class WaylandConnectionProxy;
+
+class GbmPixmapWayland : public gfx::NativePixmap {
+ public:
+  GbmPixmapWayland(WaylandSurfaceFactory* surface_manager,
+                   WaylandConnectionProxy* connection);
+
+  // Creates a buffer object and initializes the pixmap buffer.
+  bool InitializeBuffer(gfx::Size size,
+                        gfx::BufferFormat format,
+                        gfx::BufferUsage usage);
+
+  // gfx::NativePixmap overrides:
+  bool AreDmaBufFdsValid() const override;
+  size_t GetDmaBufFdCount() const override;
+  int GetDmaBufFd(size_t plane) const override;
+  int GetDmaBufPitch(size_t plane) const override;
+  int GetDmaBufOffset(size_t plane) const override;
+  uint64_t GetDmaBufModifier(size_t plane) const override;
+  gfx::BufferFormat GetBufferFormat() const override;
+  gfx::Size GetBufferSize() const override;
+  uint32_t GetUniqueId() const override;
+  bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
+                            int plane_z_order,
+                            gfx::OverlayTransform plane_transform,
+                            const gfx::Rect& display_bounds,
+                            const gfx::RectF& crop_rect,
+                            bool enable_blend,
+                            std::unique_ptr<gfx::GpuFence> gpu_fence) override;
+  gfx::NativePixmapHandle ExportHandle() override;
+
+ private:
+  ~GbmPixmapWayland() override;
+
+  // Asks Wayland to create a dmabuf based wl_buffer.
+  void CreateZwpLinuxDmabuf();
+
+  // gbm_bo wrapper for struct gbm_bo.
+  std::unique_ptr<GbmBuffer> gbm_bo_;
+
+  WaylandSurfaceFactory* surface_manager_ = nullptr;
+
+  // Represents a connection to Wayland.
+  WaylandConnectionProxy* connection_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmPixmapWayland);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_GPU_GBM_PIXMAP_WAYLAND_H_
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
new file mode 100644
index 0000000..5403739
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -0,0 +1,251 @@
+// 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 "ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h"
+
+#include "base/bind.h"
+#include "base/task/post_task.h"
+#include "base/trace_event/trace_event.h"
+#include "ui/gfx/gpu_fence.h"
+#include "ui/ozone/common/egl_util.h"
+#include "ui/ozone/platform/wayland/wayland_surface_factory.h"
+
+namespace ui {
+
+namespace {
+
+void WaitForFence(EGLDisplay display, EGLSyncKHR fence) {
+  eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+                       EGL_FOREVER_KHR);
+  eglDestroySyncKHR(display, fence);
+}
+
+}  // namespace
+
+GbmSurfacelessWayland::GbmSurfacelessWayland(
+    WaylandSurfaceFactory* surface_factory,
+    gfx::AcceleratedWidget widget)
+    : SurfacelessEGL(gfx::Size()),
+      surface_factory_(surface_factory),
+      widget_(widget),
+      has_implicit_external_sync_(
+          HasEGLExtension("EGL_ARM_implicit_external_sync")),
+      weak_factory_(this) {
+  surface_factory_->RegisterSurface(widget_, this);
+  unsubmitted_frames_.push_back(std::make_unique<PendingFrame>());
+}
+
+void GbmSurfacelessWayland::QueueOverlayPlane(OverlayPlane plane) {
+  planes_.push_back(std::move(plane));
+}
+
+bool GbmSurfacelessWayland::ScheduleOverlayPlane(
+    int z_order,
+    gfx::OverlayTransform transform,
+    gl::GLImage* image,
+    const gfx::Rect& bounds_rect,
+    const gfx::RectF& crop_rect,
+    bool enable_blend,
+    std::unique_ptr<gfx::GpuFence> gpu_fence) {
+  unsubmitted_frames_.back()->overlays.push_back(
+      gl::GLSurfaceOverlay(z_order, transform, image, bounds_rect, crop_rect,
+                           enable_blend, std::move(gpu_fence)));
+  return true;
+}
+
+bool GbmSurfacelessWayland::IsOffscreen() {
+  return false;
+}
+
+bool GbmSurfacelessWayland::SupportsPresentationCallback() {
+  // TODO(msisov): enable a real presentation callback for wayland. For now, we
+  // just blindly say it was successful. https://crbug.com/859012.
+  return true;
+}
+
+bool GbmSurfacelessWayland::SupportsAsyncSwap() {
+  return true;
+}
+
+bool GbmSurfacelessWayland::SupportsPostSubBuffer() {
+  // TODO(msisov): figure out how to enable subbuffers with wayland/dmabuf.
+  return false;
+}
+
+gfx::SwapResult GbmSurfacelessWayland::PostSubBuffer(
+    int x,
+    int y,
+    int width,
+    int height,
+    const PresentationCallback& callback) {
+  // The actual sub buffer handling is handled at higher layers.
+  NOTREACHED();
+  return gfx::SwapResult::SWAP_FAILED;
+}
+
+void GbmSurfacelessWayland::SwapBuffersAsync(
+    const SwapCompletionCallback& completion_callback,
+    const PresentationCallback& presentation_callback) {
+  TRACE_EVENT0("wayland", "GbmSurfacelessWayland::SwapBuffersAsync");
+  // If last swap failed, don't try to schedule new ones.
+  if (!last_swap_buffers_result_) {
+    completion_callback.Run(gfx::SwapResult::SWAP_FAILED, nullptr);
+    // Notify the caller, the buffer is never presented on a screen.
+    presentation_callback.Run(gfx::PresentationFeedback::Failure());
+    return;
+  }
+
+  // TODO(dcastagna): remove glFlush since eglImageFlushExternalEXT called on
+  // the image should be enough (https://crbug.com/720045).
+  glFlush();
+  unsubmitted_frames_.back()->Flush();
+
+  PendingFrame* frame = unsubmitted_frames_.back().get();
+  frame->completion_callback = completion_callback;
+  frame->presentation_callback = presentation_callback;
+  unsubmitted_frames_.push_back(std::make_unique<PendingFrame>());
+
+  // TODO: the following should be replaced by a per surface flush as it gets
+  // implemented in GL drivers.
+  EGLSyncKHR fence = InsertFence(has_implicit_external_sync_);
+  if (!fence) {
+    completion_callback.Run(gfx::SwapResult::SWAP_FAILED, nullptr);
+    // Notify the caller, the buffer is never presented on a screen.
+    presentation_callback.Run(gfx::PresentationFeedback::Failure());
+    return;
+  }
+
+  base::OnceClosure fence_wait_task =
+      base::BindOnce(&WaitForFence, GetDisplay(), fence);
+
+  base::OnceClosure fence_retired_callback = base::BindOnce(
+      &GbmSurfacelessWayland::FenceRetired, weak_factory_.GetWeakPtr(), frame);
+
+  base::PostTaskWithTraitsAndReply(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      std::move(fence_wait_task), std::move(fence_retired_callback));
+}
+
+void GbmSurfacelessWayland::PostSubBufferAsync(
+    int x,
+    int y,
+    int width,
+    int height,
+    const SwapCompletionCallback& completion_callback,
+    const PresentationCallback& presentation_callback) {
+  // See the comment in SupportsPostSubBuffer.
+  NOTREACHED();
+}
+
+EGLConfig GbmSurfacelessWayland::GetConfig() {
+  if (!config_) {
+    EGLint config_attribs[] = {EGL_BUFFER_SIZE,
+                               32,
+                               EGL_ALPHA_SIZE,
+                               8,
+                               EGL_BLUE_SIZE,
+                               8,
+                               EGL_GREEN_SIZE,
+                               8,
+                               EGL_RED_SIZE,
+                               8,
+                               EGL_RENDERABLE_TYPE,
+                               EGL_OPENGL_ES2_BIT,
+                               EGL_SURFACE_TYPE,
+                               EGL_DONT_CARE,
+                               EGL_NONE};
+    config_ = ChooseEGLConfig(GetDisplay(), config_attribs);
+  }
+  return config_;
+}
+
+GbmSurfacelessWayland::~GbmSurfacelessWayland() {
+  surface_factory_->UnregisterSurface(widget_);
+}
+
+GbmSurfacelessWayland::PendingFrame::PendingFrame() {}
+
+GbmSurfacelessWayland::PendingFrame::~PendingFrame() {}
+
+bool GbmSurfacelessWayland::PendingFrame::ScheduleOverlayPlanes(
+    gfx::AcceleratedWidget widget) {
+  for (auto& overlay : overlays)
+    if (!overlay.ScheduleOverlayPlane(widget))
+      return false;
+  return true;
+}
+
+void GbmSurfacelessWayland::PendingFrame::Flush() {
+  for (const auto& overlay : overlays)
+    overlay.Flush();
+}
+
+void GbmSurfacelessWayland::SubmitFrame() {
+  DCHECK(!unsubmitted_frames_.empty());
+
+  if (unsubmitted_frames_.front()->ready && !submitted_frame_) {
+    submitted_frame_ = std::move(unsubmitted_frames_.front());
+    unsubmitted_frames_.erase(unsubmitted_frames_.begin());
+
+    bool schedule_planes_succeeded =
+        submitted_frame_->ScheduleOverlayPlanes(widget_);
+
+    if (!schedule_planes_succeeded) {
+      OnSubmission(gfx::SwapResult::SWAP_FAILED, nullptr);
+      OnPresentation(gfx::PresentationFeedback::Failure());
+      return;
+    }
+
+    uint32_t buffer_id = planes_.back().pixmap->GetUniqueId();
+    surface_factory_->ScheduleBufferSwap(widget_, buffer_id);
+
+    // Check comment in ::SupportsPresentationCallback.
+    OnSubmission(gfx::SwapResult::SWAP_ACK, nullptr);
+    OnPresentation(
+        gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(),
+                                  gfx::PresentationFeedback::kZeroCopy));
+
+    planes_.clear();
+  }
+}
+
+EGLSyncKHR GbmSurfacelessWayland::InsertFence(bool implicit) {
+  const EGLint attrib_list[] = {EGL_SYNC_CONDITION_KHR,
+                                EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM,
+                                EGL_NONE};
+  return eglCreateSyncKHR(GetDisplay(), EGL_SYNC_FENCE_KHR,
+                          implicit ? attrib_list : NULL);
+}
+
+void GbmSurfacelessWayland::FenceRetired(PendingFrame* frame) {
+  frame->ready = true;
+  SubmitFrame();
+}
+
+void GbmSurfacelessWayland::OnSubmission(
+    gfx::SwapResult result,
+    std::unique_ptr<gfx::GpuFence> out_fence) {
+  submitted_frame_->swap_result = result;
+}
+
+void GbmSurfacelessWayland::OnPresentation(
+    const gfx::PresentationFeedback& feedback) {
+  // Explicitly destroy overlays to free resources (e.g., fences) early.
+  submitted_frame_->overlays.clear();
+
+  gfx::SwapResult result = submitted_frame_->swap_result;
+  std::move(submitted_frame_->completion_callback).Run(result, nullptr);
+  std::move(submitted_frame_->presentation_callback).Run(feedback);
+  submitted_frame_.reset();
+
+  if (result == gfx::SwapResult::SWAP_FAILED) {
+    last_swap_buffers_result_ = false;
+    return;
+  }
+
+  SubmitFrame();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
new file mode 100644
index 0000000..cc660f3
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
@@ -0,0 +1,102 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_GPU_GBM_SURFACELESS_WAYLAND_H_
+#define UI_OZONE_PLATFORM_WAYLAND_GPU_GBM_SURFACELESS_WAYLAND_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gl/gl_surface_egl.h"
+#include "ui/ozone/public/overlay_plane.h"
+#include "ui/ozone/public/swap_completion_callback.h"
+
+namespace ui {
+
+class WaylandSurfaceFactory;
+
+// A GLSurface for Wayland Ozone platform that uses surfaceless drawing. Drawing
+// and displaying happens directly through NativePixmap buffers. CC would call
+// into SurfaceFactoryOzone to allocate the buffers and then call
+// ScheduleOverlayPlane(..) to schedule the buffer for presentation.
+class GbmSurfacelessWayland : public gl::SurfacelessEGL {
+ public:
+  GbmSurfacelessWayland(WaylandSurfaceFactory* surface_factory,
+                        gfx::AcceleratedWidget widget);
+
+  void QueueOverlayPlane(OverlayPlane plane);
+
+  // gl::GLSurface:
+  bool ScheduleOverlayPlane(int z_order,
+                            gfx::OverlayTransform transform,
+                            gl::GLImage* image,
+                            const gfx::Rect& bounds_rect,
+                            const gfx::RectF& crop_rect,
+                            bool enable_blend,
+                            std::unique_ptr<gfx::GpuFence> gpu_fence) override;
+  bool IsOffscreen() override;
+  bool SupportsPresentationCallback() override;
+  bool SupportsAsyncSwap() override;
+  bool SupportsPostSubBuffer() override;
+  gfx::SwapResult PostSubBuffer(int x,
+                                int y,
+                                int width,
+                                int height,
+                                const PresentationCallback& callback) override;
+  void SwapBuffersAsync(
+      const SwapCompletionCallback& completion_callback,
+      const PresentationCallback& presentation_callback) override;
+  void PostSubBufferAsync(
+      int x,
+      int y,
+      int width,
+      int height,
+      const SwapCompletionCallback& completion_callback,
+      const PresentationCallback& presentation_callback) override;
+  EGLConfig GetConfig() override;
+
+ private:
+  ~GbmSurfacelessWayland() override;
+
+  struct PendingFrame {
+    PendingFrame();
+    ~PendingFrame();
+
+    bool ScheduleOverlayPlanes(gfx::AcceleratedWidget widget);
+    void Flush();
+
+    bool ready = false;
+    gfx::SwapResult swap_result = gfx::SwapResult::SWAP_FAILED;
+    std::vector<gl::GLSurfaceOverlay> overlays;
+    SwapCompletionCallback completion_callback;
+    PresentationCallback presentation_callback;
+  };
+
+  void SubmitFrame();
+
+  EGLSyncKHR InsertFence(bool implicit);
+  void FenceRetired(PendingFrame* frame);
+
+  void OnSubmission(gfx::SwapResult result,
+                    std::unique_ptr<gfx::GpuFence> out_fence);
+  void OnPresentation(const gfx::PresentationFeedback& feedback);
+
+  WaylandSurfaceFactory* surface_factory_;
+  std::vector<OverlayPlane> planes_;
+
+  // The native surface. Deleting this is allowed to free the EGLNativeWindow.
+  gfx::AcceleratedWidget widget_;
+  std::vector<std::unique_ptr<PendingFrame>> unsubmitted_frames_;
+  std::unique_ptr<PendingFrame> submitted_frame_;
+  bool has_implicit_external_sync_;
+  bool last_swap_buffers_result_ = true;
+
+  base::WeakPtrFactory<GbmSurfacelessWayland> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurfacelessWayland);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_GPU_GBM_SURFACELESS_WAYLAND_H_
diff --git a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc
new file mode 100644
index 0000000..88b9515
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc
@@ -0,0 +1,136 @@
+// 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 "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
+
+#include "base/process/process.h"
+#include "third_party/khronos/EGL/egl.h"
+#include "ui/ozone/common/linux/drm_util_linux.h"
+#include "ui/ozone/platform/wayland/wayland_connection.h"
+
+namespace ui {
+
+WaylandConnectionProxy::WaylandConnectionProxy(WaylandConnection* connection)
+    : connection_(connection),
+      gpu_thread_runner_(connection_ ? nullptr
+                                     : base::ThreadTaskRunnerHandle::Get()) {}
+
+WaylandConnectionProxy::~WaylandConnectionProxy() = default;
+
+void WaylandConnectionProxy::SetWaylandConnection(
+    ozone::mojom::WaylandConnectionPtr wc_ptr) {
+  // This is an IO child thread. To satisfy our needs, we pass interface here
+  // and bind it again on a gpu main thread, where buffer swaps happen.
+  wc_ptr_info_ = wc_ptr.PassInterface();
+}
+
+void WaylandConnectionProxy::CreateZwpLinuxDmabuf(
+    base::File file,
+    gfx::Size size,
+    const std::vector<uint32_t>& strides,
+    const std::vector<uint32_t>& offsets,
+    const std::vector<uint64_t>& modifiers,
+    uint32_t current_format,
+    uint32_t planes_count,
+    uint32_t buffer_id) {
+  DCHECK(gpu_thread_runner_);
+  // Do a mojo call on the GpuMainThread instead of the io child thread to
+  // ensure proper functionality.
+  gpu_thread_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WaylandConnectionProxy::CreateZwpLinuxDmabufInternal,
+                     base::Unretained(this), std::move(file), std::move(size),
+                     std::move(strides), std::move(offsets),
+                     std::move(modifiers), current_format, planes_count,
+                     buffer_id));
+}
+
+void WaylandConnectionProxy::CreateZwpLinuxDmabufInternal(
+    base::File file,
+    gfx::Size size,
+    const std::vector<uint32_t>& strides,
+    const std::vector<uint32_t>& offsets,
+    const std::vector<uint64_t>& modifiers,
+    uint32_t current_format,
+    uint32_t planes_count,
+    uint32_t buffer_id) {
+  // The interface pointer is passed on an IO child thread, which is different
+  // from the thread, which is used to call these methods. Thus, rebind the
+  // interface on a first call to ensure mojo calls will always happen on a
+  // sequence we want.
+  if (!bound_) {
+    wc_ptr_.Bind(std::move(wc_ptr_info_));
+    bound_ = true;
+  }
+  DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
+  DCHECK(wc_ptr_);
+  wc_ptr_->CreateZwpLinuxDmabuf(std::move(file), size.width(), size.height(),
+                                strides, offsets, current_format, modifiers,
+                                planes_count, buffer_id);
+}
+
+void WaylandConnectionProxy::DestroyZwpLinuxDmabuf(uint32_t buffer_id) {
+  DCHECK(gpu_thread_runner_);
+  // Do a mojo call on the GpuMainThread instead of the io child thread to
+  // ensure proper functionality.
+  gpu_thread_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WaylandConnectionProxy::DestroyZwpLinuxDmabufInternal,
+                     base::Unretained(this), buffer_id));
+}
+
+void WaylandConnectionProxy::DestroyZwpLinuxDmabufInternal(uint32_t buffer_id) {
+  DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
+  DCHECK(wc_ptr_);
+  wc_ptr_->DestroyZwpLinuxDmabuf(buffer_id);
+}
+
+void WaylandConnectionProxy::ScheduleBufferSwap(gfx::AcceleratedWidget widget,
+                                                uint32_t buffer_id) {
+  DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
+  DCHECK(wc_ptr_);
+  wc_ptr_->ScheduleBufferSwap(widget, buffer_id);
+}
+
+WaylandWindow* WaylandConnectionProxy::GetWindow(
+    gfx::AcceleratedWidget widget) {
+  if (connection_)
+    return connection_->GetWindow(widget);
+  return nullptr;
+}
+
+void WaylandConnectionProxy::ScheduleFlush() {
+  if (connection_)
+    return connection_->ScheduleFlush();
+
+  LOG(FATAL) << "Flush mustn't be called directly on the WaylandConnection, "
+                "when multi-process moe is used";
+}
+
+wl_shm* WaylandConnectionProxy::shm() {
+  wl_shm* shm = nullptr;
+  if (connection_)
+    shm = connection_->shm();
+  return shm;
+}
+
+intptr_t WaylandConnectionProxy::Display() {
+  if (connection_)
+    return reinterpret_cast<intptr_t>(connection_->display());
+
+#if defined(WAYLAND_GBM)
+  // It must not be a single process mode. Thus, shared dmabuf approach is used,
+  // which requires |gbm_device_|.
+  DCHECK(gbm_device_);
+  return EGL_DEFAULT_DISPLAY;
+#endif
+  return 0;
+}
+
+void WaylandConnectionProxy::AddBindingWaylandConnectionClient(
+    ozone::mojom::WaylandConnectionClientRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h
new file mode 100644
index 0000000..9126716
--- /dev/null
+++ b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h
@@ -0,0 +1,135 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_CONNECTION_PROXY_H_
+#define UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_CONNECTION_PROXY_H_
+
+#include "base/macros.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h"
+
+#if defined(WAYLAND_GBM)
+#include "ui/ozone/common/linux/gbm_device.h"
+#endif
+
+struct wl_shm;
+
+namespace ui {
+
+class WaylandConnection;
+class WaylandWindow;
+
+// Provides a proxy connection to a WaylandConnection object on
+// browser process side. When in multi-process mode, this is used to create
+// Wayland dmabufs and ask it to do commits. When in single process mode,
+// this class just forwards calls directly to WaylandConnection.
+//
+// It's guaranteed that WaylandConnectionProxy makes mojo calls on the right
+// sequence.
+class WaylandConnectionProxy : public ozone::mojom::WaylandConnectionClient {
+ public:
+  explicit WaylandConnectionProxy(WaylandConnection* connection);
+  ~WaylandConnectionProxy() override;
+
+  // WaylandConnectionProxy overrides:
+  void SetWaylandConnection(ozone::mojom::WaylandConnectionPtr wc_ptr) override;
+
+  // Methods, which must be used when GPU is hosted on a different process
+  // aka gpu process.
+  //
+  // Asks Wayland to create a wl_buffer based on a shared buffer file
+  // descriptor backed (gbm_bo).
+  void CreateZwpLinuxDmabuf(base::File file,
+                            gfx::Size size,
+                            const std::vector<uint32_t>& strides,
+                            const std::vector<uint32_t>& offsets,
+                            const std::vector<uint64_t>& modifiers,
+                            uint32_t current_format,
+                            uint32_t planes_count,
+                            uint32_t buffer_id);
+
+  // Asks Wayland to destroy a wl_buffer.
+  void DestroyZwpLinuxDmabuf(uint32_t buffer_id);
+
+  // Asks Wayland to find a wl_buffer with the |buffer_id| and schedule a
+  // buffer swap for a WaylandWindow, which backs the following |widget|.
+  void ScheduleBufferSwap(gfx::AcceleratedWidget widget, uint32_t buffer_id);
+
+#if defined(WAYLAND_GBM)
+  // Returns a gbm_device based on a DRM render node.
+  GbmDevice* gbm_device() const { return gbm_device_.get(); }
+  void set_gbm_device(std::unique_ptr<GbmDevice> gbm_device) {
+    gbm_device_ = std::move(gbm_device);
+  }
+#endif
+
+  // Methods, which must be used when a single process mode is used (GPU is
+  // hosted in the browser process).
+  //
+  // Return a WaylandWindow based on the |widget|.
+  WaylandWindow* GetWindow(gfx::AcceleratedWidget widget);
+  // Schedule flush in the Wayland message loop.
+  void ScheduleFlush();
+  // Returns an object for a shared memory support. Used for software fallback.
+  wl_shm* shm();
+
+  // Methods, which can be used with both single- and multi-process modes.
+  //
+  // Returns a pointer to native display. When used in single process mode,
+  // a wl_display pointer is returned. For the the mode, when there are GPU
+  // and browser processes, EGL_DEFAULT_DISPLAY is returned.
+  intptr_t Display();
+
+  // Adds a WaylandConnectionClient binding.
+  void AddBindingWaylandConnectionClient(
+      ozone::mojom::WaylandConnectionClientRequest request);
+
+  WaylandConnection* connection() { return connection_; }
+
+ private:
+  void CreateZwpLinuxDmabufInternal(base::File file,
+                                    gfx::Size size,
+                                    const std::vector<uint32_t>& strides,
+                                    const std::vector<uint32_t>& offsets,
+                                    const std::vector<uint64_t>& modifiers,
+                                    uint32_t current_format,
+                                    uint32_t planes_count,
+                                    uint32_t buffer_id);
+  void DestroyZwpLinuxDmabufInternal(uint32_t buffer_id);
+
+  // Non-owned pointer to a WaylandConnection. It is only used in a single
+  // process mode, when a shared dmabuf approach is not used.
+  WaylandConnection* connection_ = nullptr;
+
+#if defined(WAYLAND_GBM)
+  // A DRM render node based gbm device.
+  std::unique_ptr<GbmDevice> gbm_device_;
+#endif
+
+  mojo::BindingSet<ozone::mojom::WaylandConnectionClient> bindings_;
+
+  // A pointer to a WaylandConnection object, which always lives on a browser
+  // process side. It's used for a multi-process mode.
+  ozone::mojom::WaylandConnectionPtr wc_ptr_;
+  ozone::mojom::WaylandConnectionPtrInfo wc_ptr_info_;
+  bool bound_ = false;
+
+  // A task runner, which is initialized in a multi-process mode. It is used to
+  // ensure all the methods of this class are run on GpuMainThread. This is
+  // needed to ensure mojo calls happen on a right sequence. What is more, it
+  // makes it possible to use a frame callback (when it is implemented) in the
+  // browser process, which calls back to a right sequence after a
+  // ScheduleBufferSwap call.
+  scoped_refptr<base::SingleThreadTaskRunner> gpu_thread_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandConnectionProxy);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_CONNECTION_PROXY_H_
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 1aa07027..4476357 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -4,13 +4,16 @@
 
 #include "ui/ozone/platform/wayland/ozone_platform_wayland.h"
 
+#include "base/memory/ptr_util.h"
 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
 #include "ui/base/ui_features.h"
 #include "ui/display/manager/fake_display_delegate.h"
 #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
 #include "ui/events/system_input_injector.h"
 #include "ui/ozone/common/stub_overlay_manager.h"
+#include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/platform/wayland/wayland_connection_connector.h"
 #include "ui/ozone/platform/wayland/wayland_native_display_delegate.h"
 #include "ui/ozone/platform/wayland/wayland_surface_factory.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
@@ -26,6 +29,12 @@
 #include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
 #endif
 
+#if defined(WAYLAND_GBM)
+#include "ui/ozone/common/linux/gbm_wrapper.h"
+#include "ui/ozone/platform/wayland/gpu/drm_render_node_handle.h"
+#include "ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.h"
+#endif
+
 namespace ui {
 
 namespace {
@@ -61,7 +70,7 @@
   }
 
   GpuPlatformSupportHost* GetGpuPlatformSupportHost() override {
-    return gpu_platform_support_host_.get();
+    return connector_ ? connector_.get() : gpu_platform_support_host_.get();
   }
 
   std::unique_ptr<SystemInputInjector> CreateSystemInputInjector() override {
@@ -95,33 +104,63 @@
     if (!connection_->Initialize())
       LOG(FATAL) << "Failed to initialize Wayland platform";
 
+#if defined(WAYLAND_GBM)
+    if (!args.single_process)
+      connector_.reset(new WaylandConnectionConnector(connection_.get()));
+#endif
+
     cursor_factory_.reset(new BitmapCursorFactoryOzone);
     overlay_manager_.reset(new StubOverlayManager);
     input_controller_ = CreateStubInputController();
-    surface_factory_.reset(new WaylandSurfaceFactory(connection_.get()));
     gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
   }
 
   void InitializeGPU(const InitParams& args) override {
-    // TODO(fwang): args.single_process parameter should be checked here; make
-    // sure callers pass in the proper value. Once it happens, the check whether
-    // surface factory was set in the same process by a previous InitializeUI
-    // call becomes unneeded.
-    if (!surface_factory_) {
-      // TODO(fwang): Separate processes can not share a Wayland connection
-      // and so the current implementations of GLOzoneEGLWayland and
-      // WaylandCanvasSurface may only work when UI and GPU live in the same
-      // process. GetSurfaceFactoryOzone() must be non-null so a dummy instance
-      // of WaylandSurfaceFactory is needed to make the GPU initialization
-      // gracefully fail.
-      surface_factory_.reset(new WaylandSurfaceFactory(nullptr));
+    if (!args.single_process) {
+      proxy_.reset(new WaylandConnectionProxy(nullptr));
+#if defined(WAYLAND_GBM)
+      DrmRenderNodePathFinder path_finder;
+      const base::FilePath drm_node_path = path_finder.GetDrmRenderNodePath();
+      if (drm_node_path.empty())
+        LOG(FATAL) << "Failed to find drm render node path.";
+
+      DrmRenderNodeHandle handle;
+      if (!handle.Initialize(drm_node_path))
+        LOG(FATAL) << "Failed to initialize drm render node handle.";
+
+      auto gbm = CreateGbmDevice(handle.PassFD().release());
+      if (!gbm)
+        LOG(FATAL) << "Failed to initialize gbm device.";
+
+      proxy_->set_gbm_device(std::move(gbm));
+#endif
+    } else {
+      proxy_.reset(new WaylandConnectionProxy(connection_.get()));
     }
+    surface_factory_.reset(new WaylandSurfaceFactory(proxy_.get()));
   }
 
   const PlatformProperties& GetPlatformProperties() override {
+    DCHECK(connection_.get());
+    if (properties_.supported_buffer_formats.empty()) {
+      properties_.supported_buffer_formats =
+          connection_->GetSupportedBufferFormats();
+    }
     return properties_;
   }
 
+  void AddInterfaces(service_manager::BinderRegistry* registry) override {
+    registry->AddInterface<ozone::mojom::WaylandConnectionClient>(
+        base::BindRepeating(
+            &OzonePlatformWayland::CreateWaylandConnectionClientBinding,
+            base::Unretained(this)));
+  }
+
+  void CreateWaylandConnectionClientBinding(
+      ozone::mojom::WaylandConnectionClientRequest request) {
+    proxy_->AddBindingWaylandConnectionClient(std::move(request));
+  }
+
  private:
   std::unique_ptr<WaylandConnection> connection_;
   std::unique_ptr<WaylandSurfaceFactory> surface_factory_;
@@ -134,6 +173,9 @@
   XkbEvdevCodes xkb_evdev_code_converter_;
 #endif
 
+  std::unique_ptr<WaylandConnectionProxy> proxy_;
+  std::unique_ptr<WaylandConnectionConnector> connector_;
+
   PlatformProperties properties_;
 
   DISALLOW_COPY_AND_ASSIGN(OzonePlatformWayland);
diff --git a/ui/ozone/platform/wayland/wayland.gni b/ui/ozone/platform/wayland/wayland.gni
new file mode 100644
index 0000000..8cfdc4c
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland.gni
@@ -0,0 +1,12 @@
+# 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.
+
+import("//build/config/ui.gni")
+import("//ui/ozone/ozone.gni")
+
+declare_args() {
+  # Checks if Wayland must be compiled with dmabuf/gbm feature, which allows a
+  # multi-process hardware accelerated mode.
+  use_wayland_gbm = true
+}
diff --git a/ui/ozone/platform/wayland/wayland_connection.cc b/ui/ozone/platform/wayland/wayland_connection.cc
index b31e91e..4718cf2 100644
--- a/ui/ozone/platform/wayland/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/wayland_connection.cc
@@ -4,6 +4,9 @@
 
 #include "ui/ozone/platform/wayland/wayland_connection.h"
 
+#include <drm_fourcc.h>
+
+#include <linux-dmabuf-unstable-v1-client-protocol.h>
 #include <xdg-shell-unstable-v5-client-protocol.h>
 #include <xdg-shell-unstable-v6-client-protocol.h>
 
@@ -14,6 +17,8 @@
 #include "base/message_loop/message_loop_current.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "ui/ozone/common/linux/drm_util_linux.h"
 #include "ui/ozone/platform/wayland/wayland_object.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 
@@ -22,15 +27,18 @@
 namespace ui {
 namespace {
 const uint32_t kMaxCompositorVersion = 4;
+const uint32_t kMaxLinuxDmabufVersion = 1;
 const uint32_t kMaxSeatVersion = 4;
 const uint32_t kMaxShmVersion = 1;
 const uint32_t kMaxXdgShellVersion = 1;
 }  // namespace
 
-WaylandConnection::WaylandConnection() : controller_(FROM_HERE) {}
+WaylandConnection::WaylandConnection()
+    : controller_(FROM_HERE), binding_(this) {}
 
 WaylandConnection::~WaylandConnection() {
-  DCHECK(window_map_.empty());
+  DCHECK(pending_buffer_map_.empty() && params_to_id_map_.empty() &&
+         buffers_.empty());
 }
 
 bool WaylandConnection::Initialize() {
@@ -147,10 +155,154 @@
   return modifiers;
 }
 
+// TODO(msisov): handle buffer swap failure or success.
+void WaylandConnection::ScheduleBufferSwap(gfx::AcceleratedWidget widget,
+                                           uint32_t buffer_id) {
+  DCHECK(base::MessageLoopForUI::IsCurrent());
+  if (!ValidateDataFromGpu(widget, buffer_id))
+    return;
+
+  auto it = buffers_.find(buffer_id);
+  // A buffer might not exist by this time. So, store the request and process
+  // it once it is created.
+  if (it == buffers_.end()) {
+    auto pending_buffers_it = pending_buffer_map_.find(buffer_id);
+    if (pending_buffers_it != pending_buffer_map_.end()) {
+      // If a buffer didn't exist and second call for a swap comes, buffer must
+      // be associated with the same widget.
+      DCHECK_EQ(pending_buffers_it->second, widget);
+    } else {
+      pending_buffer_map_.insert(
+          std::pair<uint32_t, gfx::AcceleratedWidget>(buffer_id, widget));
+    }
+    return;
+  }
+  struct wl_buffer* buffer = it->second.get();
+
+  WaylandWindow* window = GetWindow(widget);
+  if (!window)
+    return;
+
+  // TODO(msisov): it would be beneficial to use real damage regions to improve
+  // performance.
+  //
+  // TODO(msisov): also start using wl_surface_frame callbacks for better
+  // performance.
+  wl_surface_damage(window->surface(), 0, 0, window->GetBounds().width(),
+                    window->GetBounds().height());
+  wl_surface_attach(window->surface(), buffer, 0, 0);
+  wl_surface_commit(window->surface());
+
+  ScheduleFlush();
+}
+
+void WaylandConnection::CreateZwpLinuxDmabuf(
+    base::File file,
+    uint32_t width,
+    uint32_t height,
+    const std::vector<uint32_t>& strides,
+    const std::vector<uint32_t>& offsets,
+    uint32_t format,
+    const std::vector<uint64_t>& modifiers,
+    uint32_t planes_count,
+    uint32_t buffer_id) {
+  TRACE_EVENT2("Wayland", "WaylandConnection::CreateZwpLinuxDmabuf", "Format",
+               format, "Buffer id", buffer_id);
+
+  static const struct zwp_linux_buffer_params_v1_listener params_listener = {
+      WaylandConnection::CreateSucceeded, WaylandConnection::CreateFailed};
+
+  DCHECK(base::MessageLoopForUI::IsCurrent());
+  if (!ValidateDataFromGpu(file, width, height, strides, offsets, format,
+                           modifiers, planes_count, buffer_id)) {
+    // base::File::Close() has an assertion that checks if blocking operations
+    // are allowed. Thus, manually close the fd here.
+    base::ScopedFD fd(file.TakePlatformFile());
+    fd.reset();
+    return;
+  }
+
+  // Store |params| connected to |buffer_id| to track buffer creation and
+  // identify, which buffer a client wants to use.
+  struct zwp_linux_buffer_params_v1* params =
+      zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf());
+  params_to_id_map_.insert(
+      std::pair<struct zwp_linux_buffer_params_v1*, uint32_t>(params,
+                                                              buffer_id));
+  uint32_t fd = file.TakePlatformFile();
+  for (size_t i = 0; i < planes_count; i++) {
+    zwp_linux_buffer_params_v1_add(params, fd, i /* plane id */, offsets[i],
+                                   strides[i], 0 /* modifier hi */,
+                                   0 /* modifier lo */);
+  }
+  zwp_linux_buffer_params_v1_add_listener(params, &params_listener, this);
+  zwp_linux_buffer_params_v1_create(params, width, height, format, 0);
+
+  ScheduleFlush();
+}
+
+void WaylandConnection::DestroyZwpLinuxDmabuf(uint32_t buffer_id) {
+  TRACE_EVENT1("Wayland", "WaylandConnection::DestroyZwpLinuxDmabuf",
+               "Buffer id", buffer_id);
+
+  DCHECK(base::MessageLoopForUI::IsCurrent());
+
+  auto it = buffers_.find(buffer_id);
+  if (it == buffers_.end()) {
+    TerminateGpuProcess("Trying to destroy non-existing buffer");
+    return;
+  }
+  buffers_.erase(it);
+
+  ScheduleFlush();
+}
+
 ClipboardDelegate* WaylandConnection::GetClipboardDelegate() {
   return this;
 }
 
+// static
+void WaylandConnection::CreateSucceeded(
+    void* data,
+    struct zwp_linux_buffer_params_v1* params,
+    struct wl_buffer* new_buffer) {
+  DCHECK(base::MessageLoopForUI::IsCurrent());
+
+  WaylandConnection* connection = static_cast<WaylandConnection*>(data);
+  DCHECK(connection);
+
+  // Find which buffer id |params| belong to and store wl_buffer
+  // with that id.
+  auto it = connection->params_to_id_map_.find(params);
+  CHECK(it != connection->params_to_id_map_.end());
+  uint32_t buffer_id = it->second;
+  connection->params_to_id_map_.erase(params);
+  zwp_linux_buffer_params_v1_destroy(params);
+
+  connection->buffers_.insert(std::pair<uint32_t, wl::Object<wl_buffer>>(
+      buffer_id, wl::Object<wl_buffer>(new_buffer)));
+
+  TRACE_EVENT1("Wayland", "WaylandConnection::CreateSucceeded", "Buffer id",
+               buffer_id);
+
+  auto pending_buffers_it = connection->pending_buffer_map_.find(buffer_id);
+  if (pending_buffers_it != connection->pending_buffer_map_.end()) {
+    gfx::AcceleratedWidget widget = pending_buffers_it->second;
+    connection->pending_buffer_map_.erase(pending_buffers_it);
+    connection->ScheduleBufferSwap(widget, buffer_id);
+  }
+}
+
+// static
+void WaylandConnection::CreateFailed(
+    void* data,
+    struct zwp_linux_buffer_params_v1* params) {
+  DCHECK(base::MessageLoopForUI::IsCurrent());
+
+  zwp_linux_buffer_params_v1_destroy(params);
+  LOG(FATAL) << "zwp_linux_buffer_params.create failed";
+}
+
 void WaylandConnection::OfferClipboardData(
     const ClipboardDelegate::DataMap& data_map,
     ClipboardDelegate::OfferDataClosure callback) {
@@ -179,6 +331,24 @@
   return !!data_source_;
 }
 
+ozone::mojom::WaylandConnectionPtr WaylandConnection::BindInterface() {
+  // This mustn't be called twice or when the zwp_linux_dmabuf interface is not
+  // available.
+  DCHECK(!binding_.is_bound() || zwp_linux_dmabuf_);
+  ozone::mojom::WaylandConnectionPtr ptr;
+  binding_.Bind(MakeRequest(&ptr));
+  return ptr;
+}
+
+std::vector<gfx::BufferFormat> WaylandConnection::GetSupportedBufferFormats() {
+  return buffer_formats_;
+}
+
+void WaylandConnection::SetTerminateGpuCallback(
+    base::OnceCallback<void(std::string)> terminate_callback) {
+  terminate_gpu_cb_ = std::move(terminate_callback);
+}
+
 void WaylandConnection::GetAvailableMimeTypes(
     ClipboardDelegate::GetMimeTypesClosure callback) {
   std::move(callback).Run(data_device_->GetAvailableMimeTypes());
@@ -226,6 +396,81 @@
 
 void WaylandConnection::OnFileCanWriteWithoutBlocking(int fd) {}
 
+bool WaylandConnection::ValidateDataFromGpu(
+    const base::File& file,
+    uint32_t width,
+    uint32_t height,
+    const std::vector<uint32_t>& strides,
+    const std::vector<uint32_t>& offsets,
+    uint32_t format,
+    const std::vector<uint64_t>& modifiers,
+    uint32_t planes_count,
+    uint32_t buffer_id) {
+  std::string reason;
+  if (!file.IsValid())
+    reason = "Buffer fd is invalid";
+
+  if (width == 0 || height == 0)
+    reason = "Buffer size is invalid";
+
+  if (planes_count < 1)
+    reason = "Planes count cannot be less than 1";
+
+  if (planes_count != strides.size() || planes_count != offsets.size() ||
+      planes_count != modifiers.size()) {
+    reason = "Number of strides(" + std::to_string(strides.size()) +
+             ")/offsets(" + std::to_string(offsets.size()) + ")/modifiers(" +
+             std::to_string(modifiers.size()) +
+             ") does not correspond to the number of planes(" +
+             std::to_string(planes_count) + ")";
+  }
+
+  for (auto stride : strides) {
+    if (stride == 0)
+      reason = "Strides are invalid";
+  }
+
+  if (!IsValidBufferFormat(format))
+    reason = "Buffer format is invalid";
+
+  if (buffer_id < 1)
+    reason = "Invalid buffer id: " + std::to_string(buffer_id);
+
+  if (!reason.empty()) {
+    TerminateGpuProcess(reason);
+    return false;
+  }
+  return true;
+}
+
+bool WaylandConnection::ValidateDataFromGpu(
+    const gfx::AcceleratedWidget& widget,
+    uint32_t buffer_id) {
+  std::string reason;
+
+  if (widget == gfx::kNullAcceleratedWidget)
+    reason = "Invalid accelerated widget";
+
+  if (buffer_id < 1)
+    reason = "Invalid buffer id: " + std::to_string(buffer_id);
+
+  if (!reason.empty()) {
+    TerminateGpuProcess(reason);
+    return false;
+  }
+
+  return true;
+}
+
+void WaylandConnection::TerminateGpuProcess(std::string reason) {
+  std::move(terminate_gpu_cb_).Run(std::move(reason));
+  binding_.Unbind();
+
+  buffers_.clear();
+  params_to_id_map_.clear();
+  pending_buffer_map_.clear();
+}
+
 const std::vector<std::unique_ptr<WaylandOutput>>&
 WaylandConnection::GetOutputList() const {
   return output_list_;
@@ -246,6 +491,9 @@
   static const zxdg_shell_v6_listener shell_v6_listener = {
       &WaylandConnection::PingV6,
   };
+  static const zwp_linux_dmabuf_v1_listener dmabuf_listener = {
+      &WaylandConnection::Format, &WaylandConnection::Modifiers,
+  };
 
   WaylandConnection* connection = static_cast<WaylandConnection*>(data);
   if (!connection->compositor_ && strcmp(interface, "wl_compositor") == 0) {
@@ -330,6 +578,15 @@
     connection->data_device_manager_.reset(
         new WaylandDataDeviceManager(data_device_manager.release()));
     connection->data_device_manager_->set_connection(connection);
+  } else if (!connection->zwp_linux_dmabuf_ &&
+             (strcmp(interface, "zwp_linux_dmabuf_v1") == 0)) {
+    connection->zwp_linux_dmabuf_ = wl::Bind<zwp_linux_dmabuf_v1>(
+        registry, name, std::min(version, kMaxLinuxDmabufVersion));
+    zwp_linux_dmabuf_v1_add_listener(connection->zwp_linux_dmabuf(),
+                                     &dmabuf_listener, connection);
+    // A roundtrip after binding guarantees that the client has received all
+    // supported formats.
+    wl_display_roundtrip(connection->display_.get());
   }
 
   connection->ScheduleFlush();
@@ -414,4 +671,25 @@
   connection->ScheduleFlush();
 }
 
+// static
+void WaylandConnection::Modifiers(void* data,
+                                  struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
+                                  uint32_t format,
+                                  uint32_t modifier_hi,
+                                  uint32_t modifier_lo) {
+  NOTIMPLEMENTED();
+}
+
+// static
+void WaylandConnection::Format(void* data,
+                               struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
+                               uint32_t format) {
+  WaylandConnection* connection = static_cast<WaylandConnection*>(data);
+  // Return on not the supported ARGB format.
+  if (format == DRM_FORMAT_ARGB2101010)
+    return;
+  connection->buffer_formats_.push_back(
+      GetBufferFormatFromFourCCFormat(format));
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_connection.h b/ui/ozone/platform/wayland/wayland_connection.h
index e617c86d..ad3543d 100644
--- a/ui/ozone/platform/wayland/wayland_connection.h
+++ b/ui/ozone/platform/wayland/wayland_connection.h
@@ -7,7 +7,11 @@
 
 #include <map>
 
+#include "ui/gfx/buffer_types.h"
+
+#include "base/files/file.h"
 #include "base/message_loop/message_pump_libevent.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "ui/events/platform/platform_event_source.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/platform/wayland/wayland_data_device.h"
@@ -19,6 +23,11 @@
 #include "ui/ozone/platform/wayland/wayland_pointer.h"
 #include "ui/ozone/platform/wayland/wayland_touch.h"
 #include "ui/ozone/public/clipboard_delegate.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h"
+
+struct zwp_linux_dmabuf_v1;
+struct zwp_linux_buffer_params_v1;
 
 namespace ui {
 
@@ -26,6 +35,7 @@
 
 class WaylandConnection : public PlatformEventSource,
                           public ClipboardDelegate,
+                          public ozone::mojom::WaylandConnection,
                           public base::MessagePumpLibevent::FdWatcher {
  public:
   WaylandConnection();
@@ -34,6 +44,28 @@
   bool Initialize();
   bool StartProcessingEvents();
 
+  // ozone::mojom::WaylandConnection overrides:
+  //
+  // The overridden methods below are invoked by GPU.
+  //
+  // Called by the GPU and asks to import a wl_buffer based on a gbm file
+  // descriptor.
+  void CreateZwpLinuxDmabuf(base::File file,
+                            uint32_t width,
+                            uint32_t height,
+                            const std::vector<uint32_t>& strides,
+                            const std::vector<uint32_t>& offsets,
+                            uint32_t format,
+                            const std::vector<uint64_t>& modifiers,
+                            uint32_t planes_count,
+                            uint32_t buffer_id) override;
+  // Called by the GPU to destroy the imported wl_buffer with a |buffer_id|.
+  void DestroyZwpLinuxDmabuf(uint32_t buffer_id) override;
+  // Called by the GPU and asks to attach a wl_buffer with a |buffer_id| to a
+  // WaylandWindow with the specified |widget|.
+  void ScheduleBufferSwap(gfx::AcceleratedWidget widget,
+                          uint32_t buffer_id) override;
+
   // Schedules a flush of the Wayland connection.
   void ScheduleFlush();
 
@@ -45,6 +77,7 @@
   zxdg_shell_v6* shell_v6() { return shell_v6_.get(); }
   wl_seat* seat() { return seat_.get(); }
   wl_data_device* data_device() { return data_device_->data_device(); }
+  zwp_linux_dmabuf_v1* zwp_linux_dmabuf() { return zwp_linux_dmabuf_.get(); }
 
   WaylandWindow* GetWindow(gfx::AcceleratedWidget widget);
   WaylandWindow* GetCurrentFocusedWindow();
@@ -84,6 +117,14 @@
       ClipboardDelegate::GetMimeTypesClosure callback) override;
   bool IsSelectionOwner() override;
 
+  // Returns bound pointer to own mojo interface.
+  ozone::mojom::WaylandConnectionPtr BindInterface();
+
+  std::vector<gfx::BufferFormat> GetSupportedBufferFormats();
+
+  void SetTerminateGpuCallback(
+      base::OnceCallback<void(std::string)> terminate_gpu_cb);
+
  private:
   void Flush();
   void DispatchUiEvent(Event* event);
@@ -95,6 +136,22 @@
   void OnFileCanReadWithoutBlocking(int fd) override;
   void OnFileCanWriteWithoutBlocking(int fd) override;
 
+  // Validates data sent by the GPU. If anything, terminates the gpu process.
+  bool ValidateDataFromGpu(const base::File& file,
+                           uint32_t width,
+                           uint32_t height,
+                           const std::vector<uint32_t>& strides,
+                           const std::vector<uint32_t>& offsets,
+                           uint32_t format,
+                           const std::vector<uint64_t>& modifiers,
+                           uint32_t planes_count,
+                           uint32_t buffer_id);
+  bool ValidateDataFromGpu(const gfx::AcceleratedWidget& widget,
+                           uint32_t buffer_id);
+
+  // Terminates the GPU process on invalid data received
+  void TerminateGpuProcess(std::string reason);
+
   // wl_registry_listener
   static void Global(void* data,
                      wl_registry* registry,
@@ -113,6 +170,22 @@
   // xdg_shell_listener
   static void Ping(void* data, xdg_shell* shell, uint32_t serial);
 
+  // zwp_linux_dmabuf_v1_listener
+  static void Modifiers(void* data,
+                        struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
+                        uint32_t format,
+                        uint32_t modifier_hi,
+                        uint32_t modifier_lo);
+  static void Format(void* data,
+                     struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
+                     uint32_t format);
+
+  static void CreateSucceeded(void* data,
+                              struct zwp_linux_buffer_params_v1* params,
+                              struct wl_buffer* new_buffer);
+  static void CreateFailed(void* data,
+                           struct zwp_linux_buffer_params_v1* params);
+
   std::map<gfx::AcceleratedWidget, WaylandWindow*> window_map_;
 
   wl::Object<wl_display> display_;
@@ -122,8 +195,23 @@
   wl::Object<wl_seat> seat_;
   wl::Object<wl_shm> shm_;
   wl::Object<xdg_shell> shell_;
+  wl::Object<zwp_linux_dmabuf_v1> zwp_linux_dmabuf_;
   wl::Object<zxdg_shell_v6> shell_v6_;
 
+  // Stores a wl_buffer and it's id provided by the GbmBuffer object on the
+  // GPU process side.
+  base::flat_map<uint32_t, wl::Object<wl_buffer>> buffers_;
+  // A temporary params-to-buffer id map, which is used to identify which
+  // id wl_buffer should be assigned when storing it in the |buffers_| map
+  // during CreateSucceeded call.
+  base::flat_map<struct zwp_linux_buffer_params_v1*, uint32_t>
+      params_to_id_map_;
+  // It might happen that GPU asks to swap buffers, when a wl_buffer hasn't
+  // been created yet. Thus, store the request in a pending map. Once buffer
+  // is created, it will be attached to requested WaylandWindow based on the
+  // gfx::AcceleratedWidget.
+  base::flat_map<uint32_t, gfx::AcceleratedWidget> pending_buffer_map_;
+
   std::unique_ptr<WaylandDataDeviceManager> data_device_manager_;
   std::unique_ptr<WaylandDataDevice> data_device_;
   std::unique_ptr<WaylandDataSource> data_source_;
@@ -147,6 +235,14 @@
   // Stores the callback to be invoked upon data reading from clipboard.
   RequestDataClosure read_clipboard_closure_;
 
+  mojo::Binding<ozone::mojom::WaylandConnection> binding_;
+
+  std::vector<gfx::BufferFormat> buffer_formats_;
+
+  // A callback, which is used to terminate a GPU process in case of invalid
+  // data sent by the GPU to the browser process.
+  base::OnceCallback<void(std::string)> terminate_gpu_cb_;
+
   DISALLOW_COPY_AND_ASSIGN(WaylandConnection);
 };
 
diff --git a/ui/ozone/platform/wayland/wayland_connection_connector.cc b/ui/ozone/platform/wayland/wayland_connection_connector.cc
new file mode 100644
index 0000000..cee7bb4
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland_connection_connector.cc
@@ -0,0 +1,92 @@
+// 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 "ui/ozone/platform/wayland/wayland_connection_connector.h"
+
+#include "base/task_runner_util.h"
+#include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h"
+
+namespace ui {
+
+namespace {
+
+// TODO(msisov): In the future when GpuProcessHost is moved to vizhost, remove
+// this utility code.
+using BinderCallback = ui::GpuPlatformSupportHost::GpuHostBindInterfaceCallback;
+
+void BindInterfaceInGpuProcess(const std::string& interface_name,
+                               mojo::ScopedMessagePipeHandle interface_pipe,
+                               const BinderCallback& binder_callback) {
+  return binder_callback.Run(interface_name, std::move(interface_pipe));
+}
+
+template <typename Interface>
+void BindInterfaceInGpuProcess(mojo::InterfaceRequest<Interface> request,
+                               const BinderCallback& binder_callback) {
+  BindInterfaceInGpuProcess(
+      Interface::Name_, std::move(request.PassMessagePipe()), binder_callback);
+}
+
+}  // namespace
+
+WaylandConnectionConnector::WaylandConnectionConnector(
+    WaylandConnection* connection)
+    : connection_(connection) {}
+
+WaylandConnectionConnector::~WaylandConnectionConnector() = default;
+
+void WaylandConnectionConnector::OnGpuProcessLaunched(
+    int host_id,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> send_runner,
+    const base::RepeatingCallback<void(IPC::Message*)>& send_callback) {}
+
+void WaylandConnectionConnector::OnChannelDestroyed(int host_id) {
+  // TODO(msisov): Handle restarting.
+  NOTIMPLEMENTED();
+}
+
+void WaylandConnectionConnector::OnMessageReceived(
+    const IPC::Message& message) {
+  NOTREACHED() << "This class should only be used with mojo transport but here "
+                  "we're wrongly getting invoked to handle IPC communication.";
+}
+
+void WaylandConnectionConnector::OnGpuServiceLaunched(
+    scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> io_runner,
+    GpuHostBindInterfaceCallback binder,
+    GpuHostTerminateCallback terminate_callback) {
+  terminate_callback_ = std::move(terminate_callback);
+  binder_ = std::move(binder);
+
+  io_runner_ = io_runner;
+  auto on_terminate_gpu_cb =
+      base::BindOnce(&WaylandConnectionConnector::OnTerminateGpuProcess,
+                     base::Unretained(this));
+  connection_->SetTerminateGpuCallback(std::move(on_terminate_gpu_cb));
+
+  base::PostTaskAndReplyWithResult(
+      ui_runner.get(), FROM_HERE,
+      base::BindOnce(&WaylandConnection::BindInterface,
+                     base::Unretained(connection_)),
+      base::BindOnce(&WaylandConnectionConnector::OnWaylandConnectionPtrBinded,
+                     base::Unretained(this)));
+}
+
+void WaylandConnectionConnector::OnWaylandConnectionPtrBinded(
+    ozone::mojom::WaylandConnectionPtr wc_ptr) const {
+  ozone::mojom::WaylandConnectionClientPtr wcp_ptr;
+  auto request = mojo::MakeRequest(&wcp_ptr);
+  BindInterfaceInGpuProcess(std::move(request), binder_);
+  wcp_ptr->SetWaylandConnection(std::move(wc_ptr));
+}
+
+void WaylandConnectionConnector::OnTerminateGpuProcess(std::string message) {
+  io_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(terminate_callback_),
+                                                 std::move(message)));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_connection_connector.h b/ui/ozone/platform/wayland/wayland_connection_connector.h
new file mode 100644
index 0000000..c8941ed
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland_connection_connector.h
@@ -0,0 +1,59 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_WAYLAND_CONNECTION_CONNECTOR_H_
+#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_CONNECTION_CONNECTOR_H_
+
+#include "ui/ozone/public/gpu_platform_support_host.h"
+
+#include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h"
+
+namespace ui {
+
+class WaylandConnection;
+
+// A connector class, which instantiates a connection between
+// WaylandConnectionProxy on the GPU side and the WaylandConnection object on
+// the browser process side.
+class WaylandConnectionConnector : public GpuPlatformSupportHost {
+ public:
+  WaylandConnectionConnector(WaylandConnection* connection);
+  ~WaylandConnectionConnector() override;
+
+  // GpuPlatformSupportHost:
+  void OnGpuProcessLaunched(
+      int host_id,
+      scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> send_runner,
+      const base::RepeatingCallback<void(IPC::Message*)>& send_callback)
+      override;
+  void OnChannelDestroyed(int host_id) override;
+  void OnMessageReceived(const IPC::Message& message) override;
+  void OnGpuServiceLaunched(
+      scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> io_runner,
+      GpuHostBindInterfaceCallback binder,
+      GpuHostTerminateCallback terminate_callback) override;
+
+ private:
+  void OnWaylandConnectionPtrBinded(
+      ozone::mojom::WaylandConnectionPtr wc_ptr) const;
+
+  void OnTerminateGpuProcess(std::string message);
+
+  // Non-owning pointer, which is used to bind a mojo pointer to the
+  // WaylandConnection.
+  WaylandConnection* connection_ = nullptr;
+
+  GpuHostBindInterfaceCallback binder_;
+  GpuHostTerminateCallback terminate_callback_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandConnectionConnector);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_CONNECTION_CONNECTOR_H_
diff --git a/ui/ozone/platform/wayland/wayland_object.cc b/ui/ozone/platform/wayland/wayland_object.cc
index c9ec781..ec41d32 100644
--- a/ui/ozone/platform/wayland/wayland_object.cc
+++ b/ui/ozone/platform/wayland/wayland_object.cc
@@ -4,6 +4,7 @@
 
 #include "ui/ozone/platform/wayland/wayland_object.h"
 
+#include <linux-dmabuf-unstable-v1-client-protocol.h>
 #include <wayland-client.h>
 #include <xdg-shell-unstable-v5-client-protocol.h>
 #include <xdg-shell-unstable-v6-client-protocol.h>
@@ -126,6 +127,11 @@
 const wl_interface* ObjectTraits<xdg_popup>::interface = &xdg_popup_interface;
 void (*ObjectTraits<xdg_popup>::deleter)(xdg_popup*) = &xdg_popup_destroy;
 
+const wl_interface* ObjectTraits<zwp_linux_dmabuf_v1>::interface =
+    &zwp_linux_dmabuf_v1_interface;
+void (*ObjectTraits<zwp_linux_dmabuf_v1>::deleter)(zwp_linux_dmabuf_v1*) =
+    &zwp_linux_dmabuf_v1_destroy;
+
 const wl_interface* ObjectTraits<zxdg_shell_v6>::interface =
     &zxdg_shell_v6_interface;
 void (*ObjectTraits<zxdg_shell_v6>::deleter)(zxdg_shell_v6*) =
diff --git a/ui/ozone/platform/wayland/wayland_object.h b/ui/ozone/platform/wayland/wayland_object.h
index 6924c4a..df3f16e 100644
--- a/ui/ozone/platform/wayland/wayland_object.h
+++ b/ui/ozone/platform/wayland/wayland_object.h
@@ -30,6 +30,7 @@
 struct xdg_shell;
 struct xdg_surface;
 struct xdg_popup;
+struct zwp_linux_dmabuf_v1;
 struct zxdg_shell_v6;
 struct zxdg_surface_v6;
 struct zxdg_toplevel_v6;
@@ -174,6 +175,12 @@
 };
 
 template <>
+struct ObjectTraits<zwp_linux_dmabuf_v1> {
+  static const wl_interface* interface;
+  static void (*deleter)(zwp_linux_dmabuf_v1*);
+};
+
+template <>
 struct ObjectTraits<zxdg_shell_v6> {
   static const wl_interface* interface;
   static void (*deleter)(zxdg_shell_v6*);
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory.cc b/ui/ozone/platform/wayland/wayland_surface_factory.cc
index 6e9834e..805d4608 100644
--- a/ui/ozone/platform/wayland/wayland_surface_factory.cc
+++ b/ui/ozone/platform/wayland/wayland_surface_factory.cc
@@ -15,11 +15,16 @@
 #include "ui/ozone/common/egl_util.h"
 #include "ui/ozone/common/gl_ozone_egl.h"
 #include "ui/ozone/platform/wayland/gl_surface_wayland.h"
-#include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
 #include "ui/ozone/platform/wayland/wayland_object.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
 
+#if defined(WAYLAND_GBM)
+#include "ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h"
+#include "ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h"
+#endif
+
 namespace ui {
 
 static void DeleteSharedMemory(void* pixels, void* context) {
@@ -28,7 +33,8 @@
 
 class WaylandCanvasSurface : public SurfaceOzoneCanvas {
  public:
-  WaylandCanvasSurface(WaylandConnection* connection, WaylandWindow* window_);
+  WaylandCanvasSurface(WaylandConnectionProxy* connection,
+                       WaylandWindow* window_);
   ~WaylandCanvasSurface() override;
 
   // SurfaceOzoneCanvas
@@ -38,7 +44,7 @@
   std::unique_ptr<gfx::VSyncProvider> CreateVSyncProvider() override;
 
  private:
-  WaylandConnection* connection_;
+  WaylandConnectionProxy* connection_;
   WaylandWindow* window_;
 
   gfx::Size size_;
@@ -49,7 +55,7 @@
   DISALLOW_COPY_AND_ASSIGN(WaylandCanvasSurface);
 };
 
-WaylandCanvasSurface::WaylandCanvasSurface(WaylandConnection* connection,
+WaylandCanvasSurface::WaylandCanvasSurface(WaylandConnectionProxy* connection,
                                            WaylandWindow* window)
     : connection_(connection),
       window_(window),
@@ -128,13 +134,27 @@
 
 class GLOzoneEGLWayland : public GLOzoneEGL {
  public:
-  explicit GLOzoneEGLWayland(WaylandConnection* connection)
-      : connection_(connection) {}
+  GLOzoneEGLWayland(WaylandConnectionProxy* connection,
+                    WaylandSurfaceFactory* surface_factory)
+      : connection_(connection), surface_factory_(surface_factory) {}
   ~GLOzoneEGLWayland() override {}
 
   scoped_refptr<gl::GLSurface> CreateViewGLSurface(
       gfx::AcceleratedWidget widget) override;
 
+  scoped_refptr<gl::GLSurface> CreateSurfacelessViewGLSurface(
+      gfx::AcceleratedWidget window) override {
+#if defined(WAYLAND_GBM)
+    // If there is a gbm device available, use surfaceless gl surface.
+    if (!connection_->gbm_device())
+      return nullptr;
+    return gl::InitializeGLSurface(
+        new GbmSurfacelessWayland(surface_factory_, window));
+#else
+    return nullptr;
+#endif
+  }
+
   scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(
       const gfx::Size& size) override;
 
@@ -143,7 +163,8 @@
   bool LoadGLES2Bindings(gl::GLImplementation impl) override;
 
  private:
-  WaylandConnection* connection_;
+  WaylandConnectionProxy* connection_ = nullptr;
+  WaylandSurfaceFactory* surface_factory_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(GLOzoneEGLWayland);
 };
@@ -152,7 +173,8 @@
     gfx::AcceleratedWidget widget) {
   DCHECK(connection_);
   WaylandWindow* window = connection_->GetWindow(widget);
-  DCHECK(window);
+  if (!window)
+    return nullptr;
   // The wl_egl_window needs to be created before the GLSurface so it can be
   // used in the GLSurface constructor.
   auto egl_window = CreateWaylandEglWindow(window);
@@ -172,7 +194,7 @@
 }
 
 intptr_t GLOzoneEGLWayland::GetNativeDisplay() {
-  return reinterpret_cast<intptr_t>(connection_->display());
+  return connection_->Display();
 }
 
 bool GLOzoneEGLWayland::LoadGLES2Bindings(gl::GLImplementation impl) {
@@ -184,14 +206,36 @@
 
 }  // namespace
 
-WaylandSurfaceFactory::WaylandSurfaceFactory(WaylandConnection* connection)
+WaylandSurfaceFactory::WaylandSurfaceFactory(WaylandConnectionProxy* connection)
     : connection_(connection) {
   if (connection_)
-    egl_implementation_ = std::make_unique<GLOzoneEGLWayland>(connection_);
+    egl_implementation_ =
+        std::make_unique<GLOzoneEGLWayland>(connection_, this);
 }
 
 WaylandSurfaceFactory::~WaylandSurfaceFactory() {}
 
+void WaylandSurfaceFactory::RegisterSurface(gfx::AcceleratedWidget widget,
+                                            GbmSurfacelessWayland* surface) {
+  widget_to_surface_map_.insert(std::make_pair(widget, surface));
+}
+
+void WaylandSurfaceFactory::UnregisterSurface(gfx::AcceleratedWidget widget) {
+  widget_to_surface_map_.erase(widget);
+}
+
+GbmSurfacelessWayland* WaylandSurfaceFactory::GetSurface(
+    gfx::AcceleratedWidget widget) const {
+  auto it = widget_to_surface_map_.find(widget);
+  DCHECK(it != widget_to_surface_map_.end());
+  return it->second;
+}
+
+void WaylandSurfaceFactory::ScheduleBufferSwap(gfx::AcceleratedWidget widget,
+                                               uint32_t buffer_id) {
+  connection_->ScheduleBufferSwap(widget, buffer_id);
+}
+
 std::unique_ptr<SurfaceOzoneCanvas>
 WaylandSurfaceFactory::CreateCanvasForWidget(gfx::AcceleratedWidget widget) {
   if (!connection_)
@@ -227,8 +271,15 @@
     gfx::Size size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage) {
-  NOTIMPLEMENTED();
+#if defined(WAYLAND_GBM)
+  scoped_refptr<GbmPixmapWayland> pixmap =
+      base::MakeRefCounted<GbmPixmapWayland>(this, connection_);
+  if (!pixmap->InitializeBuffer(size, format, usage))
+    return nullptr;
+  return pixmap;
+#else
   return nullptr;
+#endif
 }
 
 scoped_refptr<gfx::NativePixmap>
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory.h b/ui/ozone/platform/wayland/wayland_surface_factory.h
index f320bc5..3b569890 100644
--- a/ui/ozone/platform/wayland/wayland_surface_factory.h
+++ b/ui/ozone/platform/wayland/wayland_surface_factory.h
@@ -10,16 +10,28 @@
 #include "ui/gl/gl_surface.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 
+#include "base/posix/eintr_wrapper.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
 namespace ui {
 
-class WaylandConnection;
+class WaylandConnectionProxy;
+class GbmSurfacelessWayland;
 
 class WaylandSurfaceFactory : public SurfaceFactoryOzone {
  public:
-  explicit WaylandSurfaceFactory(WaylandConnection* connection);
+  explicit WaylandSurfaceFactory(WaylandConnectionProxy* connection);
   ~WaylandSurfaceFactory() override;
 
-  // SurfaceFactoryOzone:
+  // These methods are used, when a dmabuf based approach is used.
+  void ScheduleBufferSwap(gfx::AcceleratedWidget widget, uint32_t buffer_id);
+  void RegisterSurface(gfx::AcceleratedWidget widget,
+                       GbmSurfacelessWayland* surface);
+  void UnregisterSurface(gfx::AcceleratedWidget widget);
+  GbmSurfacelessWayland* GetSurface(gfx::AcceleratedWidget widget) const;
+
+  // SurfaceFactoryOzone overrides:
   std::vector<gl::GLImplementation> GetAllowedGLImplementations() override;
   GLOzone* GetGLOzone(gl::GLImplementation implementation) override;
   std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
@@ -36,9 +48,12 @@
       const gfx::NativePixmapHandle& handle) override;
 
  private:
-  WaylandConnection* connection_;
+  WaylandConnectionProxy* connection_ = nullptr;
   std::unique_ptr<GLOzone> egl_implementation_;
 
+  std::map<gfx::AcceleratedWidget, GbmSurfacelessWayland*>
+      widget_to_surface_map_;
+
   DISALLOW_COPY_AND_ASSIGN(WaylandSurfaceFactory);
 };
 
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc b/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
index ac53749..e1365e7d 100644
--- a/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
@@ -19,7 +19,7 @@
 
 class WaylandSurfaceFactoryTest : public WaylandTest {
  public:
-  WaylandSurfaceFactoryTest() : surface_factory(connection_.get()) {}
+  WaylandSurfaceFactoryTest() : surface_factory(connection_proxy_.get()) {}
 
   ~WaylandSurfaceFactoryTest() override {}
 
diff --git a/ui/ozone/platform/wayland/wayland_test.cc b/ui/ozone/platform/wayland/wayland_test.cc
index ff8d382..8db8147 100644
--- a/ui/ozone/platform/wayland/wayland_test.cc
+++ b/ui/ozone/platform/wayland/wayland_test.cc
@@ -29,6 +29,7 @@
       std::make_unique<StubKeyboardLayoutEngine>());
 #endif
   connection_.reset(new WaylandConnection);
+  connection_proxy_.reset(new WaylandConnectionProxy(connection_.get()));
   window_ = std::make_unique<WaylandWindow>(&delegate_, connection_.get());
 }
 
diff --git a/ui/ozone/platform/wayland/wayland_test.h b/ui/ozone/platform/wayland/wayland_test.h
index 20f7dfa4..26f44a5 100644
--- a/ui/ozone/platform/wayland/wayland_test.h
+++ b/ui/ozone/platform/wayland/wayland_test.h
@@ -9,6 +9,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ui_features.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
@@ -43,6 +44,7 @@
   wl::MockSurface* surface_;
 
   MockPlatformWindowDelegate delegate_;
+  std::unique_ptr<WaylandConnectionProxy> connection_proxy_;
   std::unique_ptr<WaylandConnection> connection_;
   std::unique_ptr<WaylandWindow> window_;
   gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget;
diff --git a/ui/ozone/public/gpu_platform_support_host.cc b/ui/ozone/public/gpu_platform_support_host.cc
index 586186c..0881946 100644
--- a/ui/ozone/public/gpu_platform_support_host.cc
+++ b/ui/ozone/public/gpu_platform_support_host.cc
@@ -26,7 +26,8 @@
   void OnGpuServiceLaunched(
       scoped_refptr<base::SingleThreadTaskRunner> ui_runner,
       scoped_refptr<base::SingleThreadTaskRunner> io_runner,
-      GpuHostBindInterfaceCallback binder) override {}
+      GpuHostBindInterfaceCallback binder,
+      GpuHostTerminateCallback terminate_callback) override {}
 };
 
 }  // namespace
diff --git a/ui/ozone/public/gpu_platform_support_host.h b/ui/ozone/public/gpu_platform_support_host.h
index f1120e74..2f81d97 100644
--- a/ui/ozone/public/gpu_platform_support_host.h
+++ b/ui/ozone/public/gpu_platform_support_host.h
@@ -30,6 +30,8 @@
   using GpuHostBindInterfaceCallback =
       base::RepeatingCallback<void(const std::string&,
                                    mojo::ScopedMessagePipeHandle)>;
+  using GpuHostTerminateCallback =
+      base::OnceCallback<void(const std::string& message)>;
 
   GpuPlatformSupportHost();
   virtual ~GpuPlatformSupportHost();
@@ -55,7 +57,8 @@
   virtual void OnGpuServiceLaunched(
       scoped_refptr<base::SingleThreadTaskRunner> host_runner,
       scoped_refptr<base::SingleThreadTaskRunner> io_runner,
-      GpuHostBindInterfaceCallback binder) = 0;
+      GpuHostBindInterfaceCallback binder,
+      GpuHostTerminateCallback terminate_callback) = 0;
 };
 
 // create a stub implementation.
diff --git a/ui/ozone/public/interfaces/wayland/BUILD.gn b/ui/ozone/public/interfaces/wayland/BUILD.gn
new file mode 100644
index 0000000..c6f2360d
--- /dev/null
+++ b/ui/ozone/public/interfaces/wayland/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("wayland_interfaces") {
+  sources = [
+    "wayland_connection.mojom",
+  ]
+
+  public_deps = [
+    "//mojo/public/mojom/base",
+    "//ui/gfx/mojo",
+  ]
+}
diff --git a/ui/ozone/public/interfaces/wayland/OWNERS b/ui/ozone/public/interfaces/wayland/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/ui/ozone/public/interfaces/wayland/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ui/ozone/public/interfaces/wayland/wayland_connection.mojom b/ui/ozone/public/interfaces/wayland/wayland_connection.mojom
new file mode 100644
index 0000000..fde5b12
--- /dev/null
+++ b/ui/ozone/public/interfaces/wayland/wayland_connection.mojom
@@ -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.
+
+module ui.ozone.mojom;
+
+import "mojo/public/mojom/base/file.mojom";
+import "mojo/public/mojom/base/file_path.mojom";
+import "ui/gfx/mojo/accelerated_widget.mojom";
+
+// Used by the GPU for communication with a WaylandConnection on the browser
+// process.
+interface WaylandConnection {
+  // Asks Wayland to create a wl_buffer based on the dmabuf |file| descriptor.
+  CreateZwpLinuxDmabuf(mojo_base.mojom.File file,
+                            uint32 width,
+                            uint32 height,
+                            array<uint32> strides,
+                            array<uint32> offsets,
+                            uint32 format,
+                            array<uint64> modifiers,
+                            uint32 planes_count,
+                            uint32 buffer_id);
+
+  // Destroys a wl_buffer created by WaylandConnection based on the |buffer_id|.
+  DestroyZwpLinuxDmabuf(uint32 buffer_id);
+
+  // Swaps wl_buffers for a WaylandWindow with the following |widget|.
+  ScheduleBufferSwap(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id);
+};
+
+// Used by the browser process to provide the GPU process with a mojo ptr to a
+// WaylandConnection, which lives on the browser process.
+interface WaylandConnectionClient {
+  SetWaylandConnection(WaylandConnection wc_ptr);
+};
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index b496a02..b924a7e8 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -22,7 +22,7 @@
 base::LazyInstance<base::OnceCallback<void(OzonePlatform*)>>::Leaky
     instance_callback = LAZY_INSTANCE_INITIALIZER;
 
-constexpr OzonePlatform::PlatformProperties kDefaultPlatformProperties;
+const OzonePlatform::PlatformProperties kDefaultPlatformProperties;
 
 base::Lock& GetOzoneInstanceLock() {
   static base::Lock lock;
@@ -31,6 +31,23 @@
 
 }  // namespace
 
+OzonePlatform::PlatformProperties::PlatformProperties() = default;
+
+OzonePlatform::PlatformProperties::PlatformProperties(
+    bool needs_request,
+    bool custom_frame_default,
+    bool can_use_system_title_bar,
+    std::vector<gfx::BufferFormat> buffer_formats)
+    : needs_view_owner_request(needs_request),
+      custom_frame_pref_default(custom_frame_default),
+      use_system_title_bar(can_use_system_title_bar),
+      supported_buffer_formats(buffer_formats) {}
+
+OzonePlatform::PlatformProperties::~PlatformProperties() = default;
+
+OzonePlatform::PlatformProperties::PlatformProperties(
+    const PlatformProperties& other) = default;
+
 OzonePlatform::OzonePlatform() {
   GetOzoneInstanceLock().AssertAcquired();
   DCHECK(!instance_) << "There should only be a single OzonePlatform.";
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index ecf1fd85..8d776c83 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -13,6 +13,7 @@
 #include "services/service_manager/public/cpp/bind_source_info.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/events/system_input_injector.h"
+#include "ui/gfx/buffer_types.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/ozone_export.h"
 
@@ -86,6 +87,14 @@
 
   // Struct used to indicate platform properties.
   struct PlatformProperties {
+    PlatformProperties();
+    PlatformProperties(bool needs_request,
+                       bool custom_frame_default,
+                       bool can_use_system_title_bar,
+                       std::vector<gfx::BufferFormat> buffer_formats);
+    ~PlatformProperties();
+    PlatformProperties(const PlatformProperties& other);
+
     // Fuchsia only: set to true when the platforms requires
     // |view_owner_request| field in PlatformWindowInitProperties when creating
     // a window.
@@ -98,6 +107,9 @@
     // Determine whether switching between system and custom frames is
     // supported.
     bool use_system_title_bar = false;
+
+    // Wayland only: carries buffer formats supported by a Wayland server.
+    std::vector<gfx::BufferFormat> supported_buffer_formats;
   };
 
   // Ensures the OzonePlatform instance without doing any initialization.